Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

@@ -12,16 +12,39 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function strlen;
use function strtolower;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ExpressionEngine Core Converter
*/
class _Expressionengine extends \IPS\convert\Software
class Expressionengine extends Software
{
/**
* Software Name
@@ -94,7 +117,7 @@ class _Expressionengine extends \IPS\convert\Software
$rows['convertPmAttachments'] = array(
'step_method' => 'convertPmAttachments',
'step_title' => 'convert_attachments',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments' ),
'ips_rows' => Db::i()->select( 'COUNT(*)', 'core_attachments' ),
'source_rows' => array( 'table' => static::canConvert()['convertPmAttachments']['table'], 'where' => static::canConvert()['convertPmAttachments']['where'] ),
'per_cycle' => 10,
'dependencies' => array( 'convertPrivateMessageReplies' ),
@@ -141,12 +164,14 @@ class _Expressionengine extends \IPS\convert\Software
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
return nl2br( $post );
}
@@ -170,8 +195,8 @@ class _Expressionengine extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_ee_sig_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_ee_sig_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
)
);
break;
@@ -179,16 +204,16 @@ class _Expressionengine extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
foreach( $this->db->select( '*', 'exp_member_fields' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['m_field_id']}"] = $field['m_field_label'];
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['m_field_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['m_field_id']}"] = $field['m_field_label'];
Member::loggedIn()->language()->words["map_pfield_{$field['m_field_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['m_field_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -205,15 +230,15 @@ class _Expressionengine extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'exp_member_groups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['group_id']}"] = $group['group_title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['group_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['group_id']}"] = $group['group_title'];
Member::loggedIn()->language()->words["map_group_{$group['group_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['group_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -232,33 +257,33 @@ class _Expressionengine extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'avatars',
'field_required' => TRUE,
'field_extra' => array( 'options' => array( 'avatars' => \IPS\Member::loggedIn()->language()->addToStack( 'avatars' ), 'profile_photos' => \IPS\Member::loggedIn()->language()->addToStack( 'profile_photos' ) ) ),
'field_extra' => array( 'options' => array( 'avatars' => Member::loggedIn()->language()->addToStack( 'avatars' ), 'profile_photos' => Member::loggedIn()->language()->addToStack( 'profile_photos' ) ) ),
'field_hint' => NULL,
);
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
Member::loggedIn()->language()->words['photo_location_desc'] = Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_ee_avatar_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_ee_avatar_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
foreach( array( 'url', 'location', 'occupation', 'bio' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -273,8 +298,8 @@ class _Expressionengine extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_ee_pm_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_ee_pm_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
)
);
break;
@@ -291,25 +316,25 @@ class _Expressionengine extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_rebuild_attachments" );
}
@@ -357,15 +382,15 @@ class _Expressionengine extends \IPS\convert\Software
{
$memberId = $this->app->getLink( $row['member_id'], 'core_members' );
$signature = \IPS\Db::i()->select( 'signature', 'core_members', array( "member_id=?", $memberId ) )->first();
$signature = Db::i()->select( 'signature', 'core_members', array( "member_id=?", $memberId ) )->first();
$signature .= '[attachment=' . $attachId . ':name]';
\IPS\Db::i()->update( 'core_members', array( 'signature' => $signature ), array( "member_id=?", $memberId ) );
Db::i()->update( 'core_members', array( 'signature' => $signature ), array( "member_id=?", $memberId ) );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
catch( UnderflowException $e ) {}
catch( OutOfRangeException $e ) {}
$libraryClass->setLastKeyValue( $row['member_id'] );
}
@@ -388,11 +413,11 @@ class _Expressionengine extends \IPS\convert\Software
$info['pf_type'] = ucwords( $row['m_field_type'] );
$info['pf_name'] = $row['m_field_label'];
$info['pf_desc'] = $row['m_field_description'];
$info['pf_content'] = ( !\in_array( $row['m_field_type'], array( 'textbox', 'textarea' ) ) ) ? explode( "\r", $row['field_choices'] ) : NULL;
$info['pf_content'] = ( !in_array( $row['m_field_type'], array( 'textbox', 'textarea' ) ) ) ? explode( "\r", $row['field_choices'] ) : NULL;
$info['pf_not_null'] = $row['m_field_required'] == 'y' ? 1 : 0;
$info['pf_member_hide'] = $row['m_field_public'] == 'n' ? 'hide' : 'all';
$info['pf_max_input'] = $row['m_field_maxl'];
$info['pf_member_edit'] = ( \in_array( $row['user_editable'], array( 'yes', 'once' ) ) ) ? 0 : 1;
$info['pf_member_edit'] = ( in_array( $row['user_editable'], array( 'yes', 'once' ) ) ) ? 0 : 1;
$info['pf_position'] = $row['m_field_order'];
$info['pf_show_on_reg'] = $row['m_field_reg'] == 'y' ? 1 : 0;
$info['pf_input_format'] = NULL;
@@ -434,7 +459,7 @@ class _Expressionengine extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -495,7 +520,7 @@ class _Expressionengine extends \IPS\convert\Software
$profileFields[ str_replace( 'm_field_id_', '', $key ) ] = $value;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$profileFields = array();
}
@@ -515,7 +540,7 @@ class _Expressionengine extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -661,7 +686,7 @@ class _Expressionengine extends \IPS\convert\Software
'attach_file' => $row['attachment_name'],
'attach_date' => $row['attachment_date'],
'attach_member_id' => $row['sender_id'],
'attach_ext' => \strtolower( trim( $row['attachment_extension'], '.' ) ),
'attach_ext' => strtolower( trim( $row['attachment_extension'], '.' ) ),
'attach_filesize' => ( $row['attachment_size'] * 1000 ), //convert to kb
);
@@ -676,15 +701,15 @@ class _Expressionengine extends \IPS\convert\Software
{
$pmId = $this->app->getLink( $row['message_id'], 'core_message_posts' );
$message = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', array( "msg_id=?", $pmId ) )->first();
$message = Db::i()->select( 'msg_post', 'core_message_posts', array( "msg_id=?", $pmId ) )->first();
$message .= '[attachment=' . $attachId . ':name]';
\IPS\Db::i()->update( 'core_message_posts', array( 'msg_post' => $message ), array( "msg_id=?", $pmId ) );
Db::i()->update( 'core_message_posts', array( 'msg_post' => $message ), array( "msg_id=?", $pmId ) );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
catch( UnderflowException $e ) {}
catch( OutOfRangeException $e ) {}
$libraryClass->setLastKeyValue( $row['attachment_id'] );
}
@@ -693,26 +718,26 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( preg_match( '#/member/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( preg_match( '#/member/([0-9]+)#i', $url->data[ Url::COMPONENT_PATH ], $matches ) )
{
try
{
$data = (string) $this->app->getLink( (int) $matches[1], array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -724,13 +749,13 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
$length = \strlen( $member->conv_password );
$length = strlen( $member->conv_password );
$providedHash = FALSE;
switch( $length )
@@ -753,6 +778,6 @@ class _Expressionengine extends \IPS\convert\Software
break;
}
return ( \IPS\Login::compareHashes( $member->conv_password, $providedHash ) ) ? TRUE : FALSE;
return ( Login::compareHashes( $member->conv_password, $providedHash ) ) ? TRUE : FALSE;
}
}
@@ -12,16 +12,42 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateTimeZone;
use DomainException;
use Exception;
use InvalidArgumentException;
use IPS\Application;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\App;
use IPS\convert\Library;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\File;
use IPS\Http\Url;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function stristr;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invision Core Converter
*/
class _Invisioncommunity extends \IPS\convert\Software
class Invisioncommunity extends Software
{
/**
* @brief Whether the versions of IPS4 match
@@ -36,15 +62,15 @@ class _Invisioncommunity extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App $app The application to reference for database and other information.
* @param App $app The application to reference for database and other information.
* @param bool $needDB Establish a DB connection
* @return void
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public function __construct( \IPS\convert\App $app, $needDB=TRUE )
public function __construct( App $app, $needDB=TRUE )
{
/* Set filename obscuring flag */
\IPS\convert\Library::$obscureFilenames = FALSE;
Library::$obscureFilenames = FALSE;
$return = parent::__construct( $app, $needDB );
@@ -57,7 +83,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$version = $this->db->select( 'app_version', 'core_applications', array( 'app_directory=?', 'core' ) )->first();
/* We're matching against the human version since the long version can change with patches */
if ( $version == \IPS\Application::load( 'core' )->version )
if ( $version == Application::load( 'core' )->version )
{
static::$versionMatch = TRUE;
}
@@ -76,7 +102,7 @@ class _Invisioncommunity extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return 'Invision Community (' . \IPS\Application::load( 'core' )->version . ')';
return 'Invision Community (' . Application::load( 'core' )->version . ')';
}
/**
@@ -171,14 +197,6 @@ class _Invisioncommunity extends \IPS\convert\Software
'table' => 'core_member_history',
'where' => array( 'log_app=? AND log_type=?', 'core', 'display_name' )
),
'convertStatuses' => array(
'table' => 'core_member_status_updates',
'where' => NULL
),
'convertStatusReplies' => array(
'table' => 'core_member_status_replies',
'where' => NULL
),
'convertMemberHistory' => array(
'table' => 'core_member_history',
'where' => array( 'log_app=? AND log_type!=?', 'core', 'display_name' )
@@ -265,16 +283,16 @@ class _Invisioncommunity extends \IPS\convert\Software
case 'convertClubs':
case 'convertAttachments':
case 'convertReactions':
\IPS\Member::loggedIn()->language()->words["upload_path"] = \IPS\Member::loggedIn()->language()->addToStack( 'convert_invision_upload_input' );
\IPS\Member::loggedIn()->language()->words["upload_path_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'convert_invision_upload_input_desc' );
Member::loggedIn()->language()->words["upload_path"] = Member::loggedIn()->language()->addToStack( 'convert_invision_upload_input' );
Member::loggedIn()->language()->words["upload_path_desc"] = Member::loggedIn()->language()->addToStack( 'convert_invision_upload_input_desc' );
$return[ $method ] = array(
'upload_path' => array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => isset( $this->app->_session['more_info']['convertEmoticons']['upload_path'] ) ? $this->app->_session['more_info']['convertEmoticons']['upload_path'] : NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_invision_upload_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_invision_upload_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
)
);
@@ -294,16 +312,16 @@ class _Invisioncommunity extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[ $field->_id ] = $field->_title;
}
foreach( $this->db->select( '*', 'core_pfields_data' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['pf_id']}"] = $this->getWord( 'core_pfield_' . $field['pf_id'] );
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['pf_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['pf_id']}"] = $this->getWord( 'core_pfield_' . $field['pf_id'] );
Member::loggedIn()->language()->words["map_pfield_{$field['pf_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['pf_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -320,15 +338,15 @@ class _Invisioncommunity extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'core_groups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['g_id']}"] = $this->getWord( 'core_group_' . $group['g_id'] );
\IPS\Member::loggedIn()->language()->words["map_group_{$group['g_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['g_id']}"] = $this->getWord( 'core_group_' . $group['g_id'] );
Member::loggedIn()->language()->words["map_group_{$group['g_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['g_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -352,24 +370,24 @@ class _Invisioncommunity extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Clubs */
\IPS\Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches" );
}
@@ -496,22 +514,22 @@ class _Invisioncommunity extends \IPS\convert\Software
{
try
{
\IPS\File::get( 'core_Emoticons', $row['filename'] );
File::get( 'core_Emoticons', $row['filename'] );
}
catch( \Exception $ex )
catch( Exception $ex )
{
\IPS\File::create( 'core_Emoticons', $row['filename'], file_get_contents( $path . '/' . $row['filename'] ), 'emoticons', FALSE, NULL, FALSE );
File::create( 'core_Emoticons', $row['filename'], file_get_contents( $path . '/' . $row['filename'] ), 'emoticons', FALSE, NULL, FALSE );
}
if( isset( $path2 ) )
{
try
{
\IPS\File::get( 'core_Emoticons', $row['filenamex2'] );
File::get( 'core_Emoticons', $row['filenamex2'] );
}
catch( \Exception $ex )
catch( Exception $ex )
{
\IPS\File::create( 'core_Emoticons', $row['filenamex2'], file_get_contents( $path2 . '/' . $row['filenamex2'] ), 'emoticons', FALSE, NULL, FALSE );
File::create( 'core_Emoticons', $row['filenamex2'], file_get_contents( $path2 . '/' . $row['filenamex2'] ), 'emoticons', FALSE, NULL, FALSE );
}
}
}
@@ -566,7 +584,7 @@ class _Invisioncommunity extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -641,7 +659,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$this->unsetNonStandardColumns( $row, 'core_members' );
$profilePhotoName = NULL;
if( !\in_array( $row['pp_photo_type'], array( 'letter', 'none', 'twitter', 'facebook', 'microsoft', '' ) ) )
if( !in_array( $row['pp_photo_type'], array( 'letter', 'none', 'twitter', 'facebook', 'microsoft', '' ) ) )
{
$bits = explode( '/', $row['pp_main_photo'] );
$profilePhotoName = array_pop( $bits );
@@ -667,7 +685,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$profileFields[ str_replace( 'field_', '', $key ) ] = $value;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$profileFields = array();
}
@@ -675,9 +693,9 @@ class _Invisioncommunity extends \IPS\convert\Software
/* Load TZ into an object and validate */
try
{
$row['timezone'] = new \DateTimeZone( $row['timezone'] );
$row['timezone'] = new DateTimeZone( $row['timezone'] );
}
catch( \Exception $e )
catch( Exception $e )
{
unset( $row['timezone'] );
}
@@ -892,46 +910,6 @@ class _Invisioncommunity extends \IPS\convert\Software
}
}
/**
* Convert status updates
*
* @return void
*/
public function convertStatuses()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'status_id' );
foreach( $this->fetch( 'core_member_status_updates', 'status_id' ) AS $row )
{
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_member_status_updates' );
$libraryClass->convertStatus( $row );
$libraryClass->setLastKeyValue( $row['status_id'] );
}
}
/**
* Convert status updates
*
* @return void
*/
public function convertStatusReplies()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'reply_id' );
foreach( $this->fetch( 'core_member_status_replies', 'status_id' ) AS $row )
{
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_member_status_replies' );
$libraryClass->convertStatusReply( $row );
$libraryClass->setLastKeyValue( $row['reply_id'] );
}
}
/**
* Convert ignored users
*
@@ -1092,7 +1070,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'attach_id' );
$mapTypes = array_keys( \IPS\Application::load('core')->extensions( 'core', 'EditorLocations', FALSE, FALSE ) );
$mapTypes = array_keys( Application::load('core')->extensions( 'core', 'EditorLocations', FALSE, FALSE ) );
array_walk( $mapTypes, function( &$value )
{
$value = 'core_' . $value;
@@ -1102,9 +1080,9 @@ class _Invisioncommunity extends \IPS\convert\Software
{
try
{
$attachmentMap = $this->db->select( '*', 'core_attachments_map', array( 'attachment_id=? AND ' . \IPS\Db::i()->in( 'location_key', $mapTypes ), $row['attach_id'] ) )->first();
$attachmentMap = $this->db->select( '*', 'core_attachments_map', array( 'attachment_id=? AND ' . Db::i()->in( 'location_key', $mapTypes ), $row['attach_id'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$libraryClass->setLastKeyValue( $row['attach_id'] );
continue;
@@ -1227,7 +1205,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$result = $this->_words[ $key ] = $this->db->select( 'word_custom, word_default', 'core_sys_lang_words', array( 'word_key=? AND lang_id=?', $key, $this->_defaultLanguage() ) )->first();
}
/* Can't find the word, so return the original string */
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
return $this->_words[ $key ] = $key;
}
@@ -1283,24 +1261,24 @@ class _Invisioncommunity extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( !\stristr( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'ic-merge-' . $this->app->app_id ) )
if( !stristr( $url->data[ Url::COMPONENT_PATH ], 'ic-merge-' . $this->app->app_id ) )
{
return NULL;
}
/* account for non-mod_rewrite links */
$searchOn = \stristr( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'index.php' ) ? $url->data[ \IPS\Http\Url::COMPONENT_QUERY ] : $url->data[ \IPS\Http\Url::COMPONENT_PATH ];
$searchOn = stristr( $url->data[ Url::COMPONENT_PATH ], 'index.php' ) ? $url->data[ Url::COMPONENT_QUERY ] : $url->data[ Url::COMPONENT_PATH ];
if( preg_match( '#/profile/([0-9]+)-(.+?)#i', $searchOn, $matches ) )
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
@@ -1308,9 +1286,9 @@ class _Invisioncommunity extends \IPS\convert\Software
try
{
$data = (string) $this->app->getLink( (int) $matches[1], array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -12,16 +12,30 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Task;
use PasswordHash;
use function count;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Joomla Core Converter
*/
class _Joomla extends \IPS\convert\Software
class Joomla extends Software
{
/**
* Software Name
@@ -103,15 +117,15 @@ class _Joomla extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'usergroups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['id']}"] = $group['title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['id']}"] = $group['title'];
Member::loggedIn()->language()->words["map_group_{$group['id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -135,14 +149,14 @@ class _Joomla extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches" );
}
@@ -174,7 +188,7 @@ class _Joomla extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -236,7 +250,7 @@ class _Joomla extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -246,12 +260,12 @@ class _Joomla extends \IPS\convert\Software
if( preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $member->conv_password ) )
{
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( 8, TRUE );
$ph = new PasswordHash( 8, TRUE );
return $ph->CheckPassword( $password, $member->conv_password ) ? TRUE : FALSE;
}
/* Joomla 2 */
if ( \IPS\Login::compareHashes( $member->conv_password, md5( $password . $member->misc ) ) )
if ( Login::compareHashes( $member->conv_password, md5( $password . $member->misc ) ) )
{
return TRUE;
}
@@ -12,16 +12,41 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function intval;
use function unserialize;
use const ENT_HTML5;
use const ENT_QUOTES;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MyBB Core Converter
*/
class _Mybb extends \IPS\convert\Software
class Mybb extends Software
{
/**
* Software Name
@@ -141,14 +166,14 @@ class _Mybb extends \IPS\convert\Software
switch( $method )
{
case 'convertEmoticons':
\IPS\Member::loggedIn()->language()->words['emoticon_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'MyBB' ) ) );
Member::loggedIn()->language()->words['emoticon_path'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'MyBB' ) ) );
$return['convertEmoticons']['emoticon_path'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
$return['convertEmoticons']['keep_existing_emoticons'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -163,16 +188,16 @@ class _Mybb extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
foreach( $this->db->select( '*', 'profilefields' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['fid']}"] = $field['name'];
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['fid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['fid']}"] = $field['name'];
Member::loggedIn()->language()->words["map_pfield_{$field['fid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['fid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -188,16 +213,16 @@ class _Mybb extends \IPS\convert\Software
$return['convertGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'usergroups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['gid']}"] = $group['title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['gid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['gid']}"] = $group['title'];
Member::loggedIn()->language()->words["map_group_{$group['gid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['gid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -208,14 +233,14 @@ class _Mybb extends \IPS\convert\Software
);
}
\IPS\Member::loggedIn()->language()->words['icon_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'MyBB' ) ) );
Member::loggedIn()->language()->words['icon_path'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'MyBB' ) ) );
$return['convertGroups']['icon_path'] = array(
'field_class' => "IPS\\Helpers\\Form\\Text",
'field_default' => NULL,
'field_required' => FALSE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_groupicon_hint'),
'field_validation' => function( $value ) { if ( !empty( $value ) AND !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_groupicon_hint'),
'field_validation' => function( $value ) { if ( !empty( $value ) AND !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
break;
@@ -223,29 +248,29 @@ class _Mybb extends \IPS\convert\Software
$return['convertMembers'] = array();
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['photo_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'MyBB' ) ) );
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = '';
Member::loggedIn()->language()->words['photo_location'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'MyBB' ) ) );
Member::loggedIn()->language()->words['photo_location_desc'] = '';
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google', 'usertitle' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -266,37 +291,39 @@ class _Mybb extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_announcements', 'extension' => 'core_Announcement' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_announcements', 'extension' => 'core_Announcement' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_announce_rebuild" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
/* Replace align tags with left/right/center/justify */
$post = preg_replace( '/\[align=(justify|left|center|right)\](.+?)\[\/align\]/i', '[$1]$2[/$1]', $post );
@@ -429,7 +456,7 @@ class _Mybb extends \IPS\convert\Software
foreach( $this->fetch( 'warninglevels', 'lid' ) AS $row )
{
$action = \unserialize( $row['action'] );
$action = unserialize( $row['action'] );
$mq = 0;
$mq_unit = 'h';
@@ -624,26 +651,26 @@ class _Mybb extends \IPS\convert\Software
switch( $promotion['requirements'] )
{
case 'postcount':
if ( !\in_array( $promotion['posttype'], array( '>', '>=', '=' ) ) )
if ( !in_array( $promotion['posttype'], array( '>', '>=', '=' ) ) )
{
/* Bubble up */
throw new \UnderflowException;
throw new UnderflowException;
}
$promotion_unit = 0;
$promotion_data = array( $promotion['newusergroup'], $promotion['posts'] );
break;
case 'timeregistered':
if ( !\in_array( $promotion['registeredtype'], array( '>', '>=', '=' ) ) )
if ( !in_array( $promotion['registeredtype'], array( '>', '>=', '=' ) ) )
{
throw new \UnderflowException;
throw new UnderflowException;
}
$promotion_unit = 0;
$promotion_data = array( $promotion['newusergroup'], $promotion['registered'] );
break;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Username Styles */
$style = explode( '{username}', $row['namestyle'] );
@@ -688,7 +715,7 @@ class _Mybb extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -716,7 +743,7 @@ class _Mybb extends \IPS\convert\Software
{
$last_warn = $this->db->select( 'dateline', 'warnings', array( "uid=?", $row['uid'] ), "dateline DESC" )->first();
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Restrict posting */
$restrict_post = 0;
@@ -793,11 +820,11 @@ class _Mybb extends \IPS\convert\Software
$temp_ban = -1;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
$info = array(
'member_id' => $row['uid'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'name' => html_entity_decode( $row['username'], ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'password' => $row['password'],
'password_extra' => $row['salt'],
@@ -809,7 +836,7 @@ class _Mybb extends \IPS\convert\Software
'restrict_post' => $restrict_post,
'bday_day' => str_pad( $bday_day, 2, '0', STR_PAD_LEFT ),
'bday_month' => str_pad( $bday_month, 2, '0', STR_PAD_LEFT ),
'bday_year' => $bday_year ? \intval( $bday_year ) : NULL,
'bday_year' => $bday_year ? intval( $bday_year ) : NULL,
'msg_count_new' => $row['unreadpms'],
'msg_count_total' => $row['totalpms'],
'msg_show_notification' => $row['pmnotify'],
@@ -848,7 +875,7 @@ class _Mybb extends \IPS\convert\Software
$pfields[ $key ] = $field;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google', 'usertitle' ) AS $pseudo )
{
@@ -864,7 +891,7 @@ class _Mybb extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -1067,26 +1094,26 @@ class _Mybb extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'member.php' ) !== FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'member.php' ) !== FALSE )
{
try
{
$data = (string) $this->app->getLink( \IPS\Request::i()->uid, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
$data = (string) $this->app->getLink( Request::i()->uid, array( 'members', 'core_members' ) );
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -1098,13 +1125,13 @@ class _Mybb extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( $member->misc ) . md5( $password ) ) ) )
if ( Login::compareHashes( $member->conv_password, md5( md5( $member->misc ) . md5( $password ) ) ) )
{
return TRUE;
}
@@ -12,16 +12,34 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use function count;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Photopost Core Converter
*/
class _Photopost extends \IPS\convert\Software
class Photopost extends Software
{
/**
* Software Name
@@ -94,15 +112,15 @@ class _Photopost extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'usergroups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupid']}"] = $group['groupname'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['groupid']}"] = $group['groupname'];
Member::loggedIn()->language()->words["map_group_{$group['groupid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['groupid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -117,29 +135,29 @@ class _Photopost extends \IPS\convert\Software
case 'convertMembers':
$return['convertMembers'] = array();
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
Member::loggedIn()->language()->words['photo_location_desc'] = Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => "The path to the folder where avatars are saved (no trailing slash - usually /path_to_photopost/data/avatars):",
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
/* And decide what to do about these... */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'location', 'interests', 'occupation', 'bio' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -160,18 +178,18 @@ class _Photopost extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_signatures_rebuild" );
}
@@ -201,7 +219,7 @@ class _Photopost extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -263,7 +281,7 @@ class _Photopost extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -289,26 +307,26 @@ class _Photopost extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'member.php' ) !== FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'member.php' ) !== FALSE )
{
try
{
$data = (string) $this->app->getLink( \IPS\Request::i()->cat, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
$data = (string) $this->app->getLink( Request::i()->cat, array( 'members', 'core_members' ) );
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -12,16 +12,44 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateTimeZone;
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Login\HashCryptPrivate;
use IPS\convert\Software;
use IPS\core\Ignore;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use PasswordHash;
use UnderflowException;
use function count;
use function defined;
use function is_null;
use function is_numeric;
use function strlen;
use function unserialize;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PhpBB Core Converter
*/
class _Phpbb extends \IPS\convert\Software
class Phpbb extends Software
{
/**
* Software Name
@@ -108,7 +136,7 @@ class _Phpbb extends \IPS\convert\Software
$rows['convertBanfilters2'] = array(
'step_title' => 'convert_banfilters',
'step_method' => 'convertBanfilters2',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_banfilters' ),
'ips_rows' => Db::i()->select( 'COUNT(*)', 'core_banfilters' ),
'source_rows' => array( 'table' => static::canConvert()['convertBanfilters2']['table'], 'where' => static::canConvert()['convertBanfilters2']['where'] ),
'per_cycle' => 200,
'dependencies' => array( 'convertBanfilters' ),
@@ -139,9 +167,9 @@ class _Phpbb extends \IPS\convert\Software
$count += $this->db->select( 'COUNT(*)', 'banlist', array( "ban_userid=?", 0 ) )->first();
$count += $this->db->select( 'COUNT(*)', 'disallow' )->first();
}
catch( \Exception $e )
catch( Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
return $count;
break;
@@ -204,8 +232,8 @@ class _Phpbb extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_phpbb_emoticons'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_phpbb_emoticons'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
),
'keep_existing_emoticons' => array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -221,16 +249,16 @@ class _Phpbb extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
foreach( $this->db->select( '*', 'profile_fields' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}"] = $this->db->select( 'lang_name', 'profile_lang', array( "field_id=?", $field['field_id'] ) )->first();
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}"] = $this->db->select( 'lang_name', 'profile_lang', array( "field_id=?", $field['field_id'] ) )->first();
Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['field_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -247,15 +275,15 @@ class _Phpbb extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'groups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['group_id']}"] = $group['group_name'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['group_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['group_id']}"] = $group['group_name'];
Member::loggedIn()->language()->words["map_group_{$group['group_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['group_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -277,10 +305,10 @@ class _Phpbb extends \IPS\convert\Software
if( empty( $salt ) )
{
throw new \UnderflowException();
throw new UnderflowException();
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
/* We can only retain one type of photo */
$return['convertMembers']['photo_hash'] = array(
@@ -288,19 +316,19 @@ class _Phpbb extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_phpbb_randstring'),
'field_hint' => Member::loggedIn()->language()->addToStack('convert_phpbb_randstring'),
);
}
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
Member::loggedIn()->language()->words['photo_location_desc'] = Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_phpbb_avatars'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_phpbb_avatars'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
$return['convertMembers']['gallery_location'] = array(
@@ -308,8 +336,8 @@ class _Phpbb extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => FALSE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_phpbb_avatargallery'),
'field_validation' => function( $value ) { if ( $value AND !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_phpbb_avatargallery'),
'field_validation' => function( $value ) { if ( $value AND !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
break;
@@ -326,24 +354,24 @@ class _Phpbb extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" );
}
@@ -360,14 +388,16 @@ class _Phpbb extends \IPS\convert\Software
{
return str_replace( ":" . $uid . "]", "]", $post );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
/* Convert newlines to <br> tags */
$post = nl2br( $post );
@@ -378,7 +408,7 @@ class _Phpbb extends \IPS\convert\Software
foreach( $matches[0] as $k => $m )
{
$c = \count( $codeboxes );
$c = count( $codeboxes );
$codeboxes[ $c ] = $m;
$replacement = '__CODE_BOX_' . $c . '__';
@@ -475,7 +505,7 @@ class _Phpbb extends \IPS\convert\Software
{
$name = $this->db->select( 'lang_name', 'profile_lang', array( "field_id=?", $row['field_id'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$name = $row['field_name'];
}
@@ -590,7 +620,7 @@ class _Phpbb extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -616,10 +646,10 @@ class _Phpbb extends \IPS\convert\Software
if( empty( $avatarSalt ) )
{
throw new \UnderflowException();
throw new UnderflowException();
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$avatarSalt = rtrim( $this->app->_session['more_info']['convertMembers']['photo_hash'], '_' );
}
@@ -661,9 +691,9 @@ class _Phpbb extends \IPS\convert\Software
/* Work out timezone - we don't really need to create an instance of \DateTimeZone here, however it helps check for invalid ones */
try
{
$timezone = new \DateTimeZone( $row['user_timezone'] );
$timezone = new DateTimeZone( $row['user_timezone'] );
}
catch( \Exception $e )
catch( Exception $e )
{
$timezone = 'UTC';
}
@@ -683,7 +713,7 @@ class _Phpbb extends \IPS\convert\Software
$temp_ban = $ban['ban_end'];
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Array of basic data */
$info = array(
@@ -720,7 +750,7 @@ class _Phpbb extends \IPS\convert\Software
if ( $row['user_avatar_type'] )
{
/* Is it numeric? Apparently phpBB doesn't clean up after itself. */
if ( \is_numeric( $row['user_avatar_type'] ) )
if ( is_numeric( $row['user_avatar_type'] ) )
{
switch( $row['user_avatar_type'] )
{
@@ -769,7 +799,7 @@ class _Phpbb extends \IPS\convert\Software
{
$userfields = $this->db->select( '*', 'profile_fields_data', array( "user_id=?", $row['user_id'] ) )->first();
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
foreach( $this->db->select( '*', 'profile_fields' ) AS $field )
{
@@ -786,7 +816,7 @@ class _Phpbb extends \IPS\convert\Software
/* phpBB stores the option incremented from 1 - however the options are stored incremented from 0. So if you select the first option, 1 is stored but profile_fields_lang actually has it as 0. I don't get it either. */
$pfields[ $field['field_id'] ] = $this->db->select( 'lang_value', 'profile_fields_lang', array( "field_id=? AND option_id=?", $field['field_id'], $userfields['pf_' . $field['field_name'] ] - 1 ) )->first();
}
catch( \UnderflowException $e ) { }
catch( UnderflowException $e ) { }
}
else
{
@@ -797,7 +827,7 @@ class _Phpbb extends \IPS\convert\Software
/* PM Folders */
$pmFolders = iterator_to_array( $this->db->select( 'folder_name', 'privmsgs_folder', array( 'user_id=?', $row['user_id'] ) ) );
$info['pconversation_filters'] = \count( $pmFolders ) ? $pmFolders : NULL;
$info['pconversation_filters'] = count( $pmFolders ) ? $pmFolders : NULL;
/* Finally */
$libraryClass->convertMember( $info, $pfields, $filename, $filepath );
@@ -820,9 +850,9 @@ class _Phpbb extends \IPS\convert\Software
try
{
$log = $this->db->select( '*', 'log', array( "log_id=?", $warn['log_id'] ) )->first();
$data = @\unserialize( $log['log_data'] );
$data = @unserialize( $log['log_data'] );
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$log = array( 'user_id' => 0 );
$data = array( 0 => NULL );
@@ -870,7 +900,7 @@ class _Phpbb extends \IPS\convert\Software
'ignore_ignore_id' => $row['zebra_id'],
);
foreach( \IPS\core\Ignore::types() AS $type )
foreach( Ignore::types() AS $type )
{
$info['ignore_' . $type] = 1;
}
@@ -927,7 +957,7 @@ class _Phpbb extends \IPS\convert\Software
}
/* If Type is null - skip */
if ( \is_null( $type ) )
if ( is_null( $type ) )
{
$libraryClass->setLastKeyValue( $row['ban_id'] );
continue;
@@ -1073,26 +1103,26 @@ class _Phpbb extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'memberlist.php' ) !== FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'memberlist.php' ) !== FALSE )
{
try
{
$data = (string) $this->app->getLink( \IPS\Request::i()->u, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
$data = (string) $this->app->getLink( Request::i()->u, array( 'members', 'core_members' ) );
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -1137,19 +1167,19 @@ class _Phpbb extends \IPS\convert\Software
/* Does the referenced folder exist in PhpBB? */
if( !isset( static::$phpBbFolders[ $folderId ] ) )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
$member = \IPS\Member::load( $this->app->getLink( (int) $user, 'core_members' ) );
$member = Member::load( $this->app->getLink( (int) $user, 'core_members' ) );
$filters = json_decode( $member->pconversation_filters, TRUE );
/* No PM Folders or it's a guest */
if( $filters === NULL OR !$member->member_id )
{
throw new \OutOfRangeException;
throw new OutOfRangeException;
}
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
return static::$pmFolders[ $user ][ $folderId ] = FALSE;
}
@@ -1161,7 +1191,7 @@ class _Phpbb extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -1175,31 +1205,31 @@ class _Phpbb extends \IPS\convert\Software
if( preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $hash ) )
{
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( 8, TRUE );
$ph = new PasswordHash( 8, TRUE );
$success = $ph->CheckPassword( $password, $member->conv_password ) ? TRUE : FALSE;
}
$hashLibrary = new \IPS\convert\Login\HashCryptPrivate;
$hashLibrary = new HashCryptPrivate;
if ( $success === FALSE )
{
/* phpBB3 */
$single_md5_pass = md5( $password );
$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ( \strlen( $hash ) == 34 )
if ( strlen( $hash ) == 34 )
{
$success = ( \IPS\Login::compareHashes( $hash, $hashLibrary->hashCryptPrivate( $password, $hash, $itoa64 ) ) ) ? TRUE : FALSE;
$success = ( Login::compareHashes( $hash, $hashLibrary->hashCryptPrivate( $password, $hash, $itoa64 ) ) ) ? TRUE : FALSE;
}
else
{
$success = ( \IPS\Login::compareHashes( $hash, $single_md5_pass ) ) ? TRUE : FALSE;
$success = ( Login::compareHashes( $hash, $single_md5_pass ) ) ? TRUE : FALSE;
}
}
/* phpBB2 */
if ( !$success )
{
$success = ( \IPS\Login::compareHashes( $hash, $hashLibrary->hashCryptPrivate( $single_md5_pass, $hash, $itoa64 ) ) ) ? TRUE : FALSE ;
$success = ( Login::compareHashes( $hash, $hashLibrary->hashCryptPrivate( $single_md5_pass, $hash, $itoa64 ) ) ) ? TRUE : FALSE ;
}
/* Fallback to password_verify(), this applies to PhpBB 3.3+ where Argon may be used */
@@ -13,16 +13,25 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use Exception;
use IPS\Application\Module;
use IPS\convert\Software;
use IPS\Http\Url;
use IPS\Member;
use IPS\Request;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PhpMyForum Core Converter
*/
class _Phpmyforum extends \IPS\convert\Software
class Phpmyforum extends Software
{
/**
* Software Name
@@ -59,26 +68,26 @@ class _Phpmyforum extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'profile.php' ) !== FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'profile.php' ) !== FALSE )
{
try
{
$data = (string) $this->app->getLink( \IPS\Request::i()->id, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
$data = (string) $this->app->getLink( Request::i()->id, array( 'members', 'core_members' ) );
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -12,16 +12,34 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use function count;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PunBB Core Converter
*/
class _Punbb extends \IPS\convert\Software
class Punbb extends Software
{
/**
* Software Name
@@ -112,15 +130,15 @@ class _Punbb extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'groups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['g_id']}"] = $group['g_title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['g_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['g_id']}"] = $group['g_title'];
Member::loggedIn()->language()->words["map_group_{$group['g_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['g_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -136,16 +154,16 @@ class _Punbb extends \IPS\convert\Software
/* Pseudo Profile Fields */
foreach( [ 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location', 'title' ] AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -166,32 +184,34 @@ class _Punbb extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
$post = nl2br( $post );
@@ -238,7 +258,7 @@ class _Punbb extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -294,7 +314,7 @@ class _Punbb extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -399,26 +419,26 @@ class _Punbb extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'profile.php' ) !== FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'profile.php' ) !== FALSE )
{
try
{
$data = (string) $this->app->getLink( \IPS\Request::i()->u, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
$data = (string) $this->app->getLink( Request::i()->u, array( 'members', 'core_members' ) );
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -430,7 +450,7 @@ class _Punbb extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -441,18 +461,18 @@ class _Punbb extends \IPS\convert\Software
if ( mb_strlen( $member->conv_password ) == 40 )
{
/* Password with salt */
$success = \IPS\Login::compareHashes( $member->conv_password, sha1( $member->conv_password_extra . sha1( $password ) ) );
$success = Login::compareHashes( $member->conv_password, sha1( $member->conv_password_extra . sha1( $password ) ) );
if ( !$success )
{
/* No salt */
$success = \IPS\Login::compareHashes( $member->conv_password, sha1( $password ) );
$success = Login::compareHashes( $member->conv_password, sha1( $password ) );
}
}
else
{
/* MD5 */
$success = \IPS\Login::compareHashes( $member->conv_password, md5( $password ) );
$success = Login::compareHashes( $member->conv_password, md5( $password ) );
}
return $success;
@@ -11,16 +11,40 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\core\Ignore;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use PasswordHash;
use UnderflowException;
use function count;
use function defined;
use function file_exists;
use function in_array;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* SMF Core Converter
*/
class _Smf extends \IPS\convert\Software
class Smf extends Software
{
/**
* Software Name
@@ -130,13 +154,13 @@ class _Smf extends \IPS\convert\Software
{
return $this->db->select( 'SUM(CHAR_LENGTH(buddy_list) - CHAR_LENGTH(REPLACE(buddy_list, ",", "") + 1))', 'members', array( "pm_ignore_list!=?", '' ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
return 0;
}
catch( \Exception $e )
catch( Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
@@ -164,7 +188,7 @@ class _Smf extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
$return['convertEmoticons']['keep_existing_emoticons'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -179,16 +203,16 @@ class _Smf extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
foreach( $this->db->select( '*', 'custom_fields' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['id_field']}"] = $field['field_name'];
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['id_field']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['id_field']}"] = $field['field_name'];
Member::loggedIn()->language()->words["map_pfield_{$field['id_field']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['id_field']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -204,16 +228,16 @@ class _Smf extends \IPS\convert\Software
$return['convertGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'membergroups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['id_group']}"] = $group['group_name'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['id_group']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['id_group']}"] = $group['group_name'];
Member::loggedIn()->language()->words["map_group_{$group['id_group']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['id_group']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -236,14 +260,14 @@ class _Smf extends \IPS\convert\Software
'field_hint' => NULL,
);
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
Member::loggedIn()->language()->words['photo_location_desc'] = Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => "This is typically: /path/to/smf/attachments/",
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
$return['convertMembers']['gallery_location'] = array(
@@ -252,21 +276,21 @@ class _Smf extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => "This is typically: /path/to/smf/uploads/avatars/",
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
foreach( array( 'gender', 'website_url', 'location', 'icq', 'aim', 'yim', 'msn', 'personal_text', 'usertitle' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -287,36 +311,38 @@ class _Smf extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_rebuild_attachments" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
// Replace BBCode - The table HTML is removed by our parser, but it leaves the posts cleaner than having unparsed bbcode
$search = array( '[li]', '[/li]', '[table]', '[/table]', '[tr]', '[/tr]', '[td]', '[/td]', '[tt]', '[/tt]' );
@@ -531,10 +557,10 @@ class _Smf extends \IPS\convert\Software
'pf_type' => $type,
'pf_name' => $row['field_name'],
'pf_desc' => $row['field_desc'],
'pf_content' => ( \in_array( $row['field_type'], array( 'select', 'radio' ) ) ) ? json_encode( explode( ',', $row['field_options'] ) ) : NULL,
'pf_content' => ( in_array( $row['field_type'], array( 'select', 'radio' ) ) ) ? json_encode( explode( ',', $row['field_options'] ) ) : NULL,
'pf_not_null' => ( $row['show_reg'] == 2 ) ? 1 : 0,
'pf_member_hide' => ( $row['private'] >= 3 ) ? 'hide' : 'all',
'pf_max_input' => ( !\in_array( $row['field_type'], array( 'select', 'radio', 'check' ) ) ) ? $row['field_length'] : NULL,
'pf_max_input' => ( !in_array( $row['field_type'], array( 'select', 'radio', 'check' ) ) ) ? $row['field_length'] : NULL,
'pf_member_edit' => ( $row['private'] < 4 ) ? 1 : 0,
'pf_position' => $row['placement'],
'pf_show_on_reg' => ( $row['show_reg'] >= 1 ) ? 1 : 0,
@@ -587,7 +613,7 @@ class _Smf extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -639,7 +665,7 @@ class _Smf extends \IPS\convert\Software
$rp = $time;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Catch out of range issues */
if( $ban > 2147483647 )
@@ -698,7 +724,7 @@ class _Smf extends \IPS\convert\Software
{
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$type = 'Text';
$content = '[]';
@@ -769,13 +795,13 @@ class _Smf extends \IPS\convert\Software
/* Remote */
try
{
$file = \IPS\Http\Url::external( $row['avatar'] )->request()->get();
$file = Url::external( $row['avatar'] )->request()->get();
$filename = explode( '/', $row['avatar'] );
$filename = array_pop( $filename );
$filedata = (string) $file;
}
catch( \Exception $e ) {}
catch( Exception $e ) {}
}
else
{
@@ -815,18 +841,18 @@ class _Smf extends \IPS\convert\Software
$legacyName = preg_replace( '~\.[\.]+~', '.', $cleanName );
}
if ( \file_exists( $path . '/' . $location ) )
if ( file_exists( $path . '/' . $location ) )
{
$filename = $attach['filename'];
$filepath = $path . '/' . $location;
}
elseif( !empty( $legacyName ) AND \file_exists( $path . '/' . $legacyName ) )
elseif( !empty( $legacyName ) AND file_exists( $path . '/' . $legacyName ) )
{
$filename = $attach['filename'];
$filepath = $path;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
}
$libraryClass->convertMember( $info, $pfields, $filename, $filepath, $filedata );
@@ -860,7 +886,7 @@ class _Smf extends \IPS\convert\Software
$info['ignore_owner_id'] = $row['id_member'];
$info['ignore_ignore_id'] = $id;
foreach( \IPS\core\Ignore::types() AS $type )
foreach( Ignore::types() AS $type )
{
$info['ignore_' . $type] = 1;
}
@@ -874,26 +900,26 @@ class _Smf extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
* @note Current profile URL format: /index.php?action=profile;u=123
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
if( isset( \IPS\Request::i()->action ) AND mb_strpos( \IPS\Request::i()->action, 'profile' ) !== FALSE )
if( isset( Request::i()->action ) AND mb_strpos( Request::i()->action, 'profile' ) !== FALSE )
{
if( isset( \IPS\Request::i()->u ) )
if( isset( Request::i()->u ) )
{
$oldId = \IPS\Request::i()->u;
$oldId = Request::i()->u;
}
else
{
$pieces = explode( ';', \IPS\Request::i()->action );
$pieces = explode( ';', Request::i()->action );
foreach( $pieces as $piece )
{
@@ -910,9 +936,9 @@ class _Smf extends \IPS\convert\Software
try
{
$data = (string) $this->app->getLink( $oldId, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -924,29 +950,29 @@ class _Smf extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->name ) . html_entity_decode( $password ) ) ) )
if ( Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->name ) . html_entity_decode( $password ) ) ) )
{
return TRUE;
}
else if ( \IPS\Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->name ) . $password ) ) )
else if ( Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->name ) . $password ) ) )
{
return TRUE;
}
/* In 4.2.6 we save the original members name as the salt so that we don't have one that has been modified in the conversion process */
else if ( \IPS\Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->conv_password_extra ) . $password ) ) )
else if ( Login::compareHashes( $member->conv_password, sha1( mb_strtolower( $member->conv_password_extra ) . $password ) ) )
{
return TRUE;
}
else
{
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( 8, TRUE );
$ph = new PasswordHash( 8, TRUE );
if( $ph->CheckPassword( mb_strtolower( $member->name ) . $password, $member->conv_password ) )
{
@@ -11,16 +11,38 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\core\Ignore;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Db;
use IPS\Http\Url;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use function count;
use function defined;
use function strtoupper;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* UBBThreads Core Converter
*/
class _UBBthreads extends \IPS\convert\Software
class UBBthreads extends Software
{
/**
* @brief Emoticons WHERE statement
@@ -146,7 +168,7 @@ class _UBBthreads extends \IPS\convert\Software
$rows['convertMembersFollowers'] = array(
'step_method' => 'convertMembersFollowers',
'step_title' => 'convert_follows',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_app=? and follow_area=?', 'core', 'member' ) ),
'ips_rows' => Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_app=? and follow_area=?', 'core', 'member' ) ),
'source_rows' => $count,
'per_cycle' => 200,
'dependencies' => array( 'convertMembers' ),
@@ -207,15 +229,15 @@ class _UBBthreads extends \IPS\convert\Software
* Attempt to convert a textual date(time) representation to a DateTime instance
*
* @param string $date Date to try to convert
* @return \IPS\DateTime|null
* @return DateTime|null
*/
protected function stringToDateTime( $date )
{
try
{
return new \IPS\DateTime( $date );
return new DateTime( $date );
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -235,7 +257,7 @@ class _UBBthreads extends \IPS\convert\Software
case 'convertEmoticons':
$return['convertEmoticons'] = array();
\IPS\Member::loggedIn()->language()->words['emoticon_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'UBBthreads' ) ) );
Member::loggedIn()->language()->words['emoticon_path'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'UBBthreads' ) ) );
$return['convertEmoticons']['emoticon_path'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
@@ -256,16 +278,16 @@ class _UBBthreads extends \IPS\convert\Software
$return['convertGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[ $group->g_id ] = $group->name;
}
foreach( $this->db->select( '*', 'GROUPS' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['GROUP_ID']}"] = $group['GROUP_NAME'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['GROUP_ID']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['GROUP_ID']}"] = $group['GROUP_NAME'];
Member::loggedIn()->language()->words["map_group_{$group['GROUP_ID']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['GROUP_ID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -285,8 +307,8 @@ class _UBBthreads extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'display_name',
'field_required' => TRUE,
'field_extra' => array( 'options' => array( 'username' => \IPS\Member::loggedIn()->language()->addToStack( 'user_name' ), 'display_name' => \IPS\Member::loggedIn()->language()->addToStack( 'display_name' ) ) ),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack( 'username_hint' ),
'field_extra' => array( 'options' => array( 'username' => Member::loggedIn()->language()->addToStack( 'user_name' ), 'display_name' => Member::loggedIn()->language()->addToStack( 'display_name' ) ) ),
'field_hint' => Member::loggedIn()->language()->addToStack( 'username_hint' ),
);
$return['convertMembers']['photo_location'] = array(
@@ -294,22 +316,22 @@ class _UBBthreads extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_ubbthreads_photo_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_ubbthreads_photo_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
foreach( array( 'homepage', 'occupation', 'hobbies', 'location', 'icq', 'yahoo', 'aim', 'msn', 'custom_title' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -330,37 +352,39 @@ class _UBBthreads extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Conversation' ), 3, array( 'class' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Conversation' ), 3, array( 'class' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_rebuild_attachments" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
$post = preg_replace( "#\[quote=(.+?)\]#i", "[quote name=\"$1\"]", $post );
$post = preg_replace( '#\[img:(left|center|right)\](.+?)\[\/img\]#i', '[$1][img]$2[/img][/$1]', $post );
@@ -470,7 +494,7 @@ class _UBBthreads extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -507,7 +531,7 @@ class _UBBthreads extends \IPS\convert\Software
);
/* Assume we want to ignore everything by this member */
foreach ( \IPS\core\Ignore::types() as $type )
foreach ( Ignore::types() as $type )
{
$info[ 'ignore_' . $type ] = 1;
}
@@ -558,24 +582,24 @@ class _UBBthreads extends \IPS\convert\Software
'md5_password' => $row['USER_PASSWORD'],
'member_group_id' => $primaryGroup,
'mgroup_others' => $secondaryGroups,
'joined' => \IPS\DateTime::create()->setTimestamp( $row['USER_REGISTERED_ON'] ),
'joined' => DateTime::create()->setTimestamp( $row['USER_REGISTERED_ON'] ),
'ip_address' => $row['USER_REGISTRATION_IP'],
'bday_day' => $birthday['day'],
'bday_month' => $birthday['month'],
'bday_year' => $birthday['year'],
'msg_count_total' => $row['USER_TOTAL_PM'],
'last_visit' => \IPS\DateTime::create()->setTimestamp( $row['USER_LAST_VISIT_TIME'] ),
'last_activity' => \IPS\DateTime::create()->setTimestamp(
'last_visit' => DateTime::create()->setTimestamp( $row['USER_LAST_VISIT_TIME'] ),
'last_activity' => DateTime::create()->setTimestamp(
max( (int) $row['USER_LAST_POST_TIME'], (int) $row['USER_LAST_SEARCH_TIME'] )
),
'allow_admin_mails' => ( $row['USER_ACCEPT_ADMIN_EMAILS'] != 'Off' ),
'member_posts' => $row['USER_TOTAL_POSTS'],
'signature' => $row['USER_DEFAULT_SIGNATURE'],
'member_last_post' => \IPS\DateTime::create()->setTimestamp( $row['USER_LAST_POST_TIME'] ),
'member_last_post' => DateTime::create()->setTimestamp( $row['USER_LAST_POST_TIME'] ),
'temp_ban' => isset( $row['BAN_EXPIRATION'] )
? ( ( (string) $row['BAN_EXPIRATION'] === '0' )
? -1
: \IPS\DateTime::create()->setTimestamp( $row['BAN_EXPIRATION'] ) )
: DateTime::create()->setTimestamp( $row['BAN_EXPIRATION'] ) )
: NULL,
);
@@ -595,7 +619,7 @@ class _UBBthreads extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -610,7 +634,7 @@ class _UBBthreads extends \IPS\convert\Software
) );
}
$fieldColumn = 'USER_' . \strtoupper( $pseudo );
$fieldColumn = 'USER_' . strtoupper( $pseudo );
$pfields[ $pseudo ] = isset( $row[ $fieldColumn ] ) ? $row[ $fieldColumn ] : NULL;
}
@@ -631,7 +655,7 @@ class _UBBthreads extends \IPS\convert\Software
try
{
$profilePhotoName = pathinfo( parse_url( $row['USER_AVATAR'], PHP_URL_PATH ), PATHINFO_BASENAME );
$profilePhotoData = \IPS\Http\Url::external( $row['USER_AVATAR'] )->request()->get();
$profilePhotoData = Url::external( $row['USER_AVATAR'] )->request()->get();
}
catch( \IPS\Http\Request\Exception $e ) { }
}
@@ -677,11 +701,11 @@ class _UBBthreads extends \IPS\convert\Software
{
$topic = array(
'mt_id' => $topicRow['TOPIC_ID'],
'mt_date' => \IPS\DateTime::create()->setTimestamp( $topicRow['TOPIC_TIME'] ),
'mt_date' => DateTime::create()->setTimestamp( $topicRow['TOPIC_TIME'] ),
'mt_title' => $topicRow['TOPIC_SUBJECT'],
'mt_starter_id' => $topicRow['USER_ID'],
'mt_start_time' => \IPS\DateTime::create()->setTimestamp( $topicRow['TOPIC_TIME'] ),
'mt_last_post_time' => \IPS\DateTime::create()->setTimestamp( $topicRow['TOPIC_LAST_REPLY_TIME'] ),
'mt_start_time' => DateTime::create()->setTimestamp( $topicRow['TOPIC_TIME'] ),
'mt_last_post_time' => DateTime::create()->setTimestamp( $topicRow['TOPIC_LAST_REPLY_TIME'] ),
'mt_replies' => $topicRow['TOPIC_REPLIES'],
);
@@ -690,7 +714,7 @@ class _UBBthreads extends \IPS\convert\Software
/* Make sure the topic starter is in the map */
$maps[ $topicRow['USER_ID'] ] = array(
'map_user_id' => $topicRow['USER_ID'],
'map_read_time' => \IPS\DateTime::create()->setTimestamp( $topicRow['TOPIC_TIME'] )
'map_read_time' => DateTime::create()->setTimestamp( $topicRow['TOPIC_TIME'] )
);
foreach ( $this->db->select( '*', 'PRIVATE_MESSAGE_USERS', array( 'TOPIC_ID=?', $topicRow['TOPIC_ID'] ) ) as $userRow )
@@ -722,7 +746,7 @@ class _UBBthreads extends \IPS\convert\Software
$libraryClass->convertPrivateMessageReply( array(
'msg_id' => $row['POST_ID'],
'msg_topic_id' => $row['TOPIC_ID'],
'msg_date' => \IPS\DateTime::create()->setTimestamp( $row['POST_TIME'] ),
'msg_date' => DateTime::create()->setTimestamp( $row['POST_TIME'] ),
'msg_post' => $row['POST_DEFAULT_BODY'],
'msg_author_id' => $row['USER_ID'],
'msg_ip_address' => '127.0.0.1',
@@ -765,31 +789,31 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
/* Make sure it's a UBBThreads URL */
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'ubbthreads.php' ) === FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'ubbthreads.php' ) === FALSE )
{
return NULL;
}
if( preg_match( '#/ubbthreads.php/users/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( preg_match( '#/ubbthreads.php/users/([0-9]+)#i', $url->data[ Url::COMPONENT_PATH ], $matches ) )
{
try
{
$data = $this->app->getLink( (int) $matches[1], array( 'members', 'core_members' ) );
$item = \IPS\Member::load( $data );
$item = Member::load( $data );
if( \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return $item->url();
}
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -801,7 +825,7 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -810,20 +834,20 @@ class _UBBthreads extends \IPS\convert\Software
$hash = $member->members_pass_hash;
$salt = $member->members_pass_salt;
if ( \IPS\Login::compareHashes( $hash, md5( $password ) ) )
if ( Login::compareHashes( $hash, md5( $password ) ) )
{
return TRUE;
}
// Not using md5, UBB salts the password with the password
// IPB already md5'd it though, *sigh*
if ( \IPS\Login::compareHashes( $hash, md5( md5( $salt ) . crypt( $password, $password ) ) ) )
if ( Login::compareHashes( $hash, md5( md5( $salt ) . crypt( $password, $password ) ) ) )
{
return TRUE;
}
// Now standard IPB check.
if ( \IPS\Login::compareHashes( $hash, md5( md5( $salt ) . md5( $password ) ) ) )
if ( Login::compareHashes( $hash, md5( md5( $salt ) . md5( $password ) ) ) )
{
return TRUE;
}
@@ -11,16 +11,42 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateTimeZone;
use erusev\Parsedown;
use Exception;
use InvalidArgumentException;
use IPS\Content\Search\Index;
use IPS\convert\App;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Db;
use IPS\Http\Url;
use IPS\IPS;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Task;
use PasswordHash;
use function count;
use function defined;
use function is_array;
use function unserialize;
use const PATHINFO_BASENAME;
use const PATHINFO_DIRNAME;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Vanilla Core Converter
*/
class _Vanilla extends \IPS\convert\Software
class Vanilla extends Software
{
/**
* Software Name
@@ -44,41 +70,6 @@ class _Vanilla extends \IPS\convert\Software
return "vanilla";
}
/**
* @brief The activity type ID for status updates
*/
protected static $activityStatusType = NULL;
/**
* Constructor
*
* @param \IPS\convert\App $app The application to reference for database and other information.
* @param bool $needDB Establish a DB connection
* @return void
* @throws \InvalidArgumentException
*/
public function __construct( \IPS\convert\App $app, $needDB=TRUE )
{
$return = parent::__construct( $app, $needDB );
if ( $needDB )
{
/* What is the activity type ID for status updates? */
if ( static::$activityStatusType === NULL )
{
try
{
static::$activityStatusType = $this->db->select( 'ActivityTypeID', 'ActivityType',
array( 'Name=?', 'Status' ), NULL, 1
)->first();
}
catch ( \Exception $e ) {}
}
}
return $return;
}
/**
* Can we convert settings?
*
@@ -97,7 +88,7 @@ class _Vanilla extends \IPS\convert\Software
public function settingsMapList()
{
$settings = array(
'fluid' => array( 'title' => \IPS\Member::loggedIn()->language()->addToStack( 'use_fluid_view_convert' ), 'value' => true, 'our_key' => 'use_fluid_view_convert', 'our_title' => \IPS\Member::loggedIn()->language()->addToStack( 'use_fluid_view_convert' ) )
'fluid' => array( 'title' => Member::loggedIn()->language()->addToStack( 'use_fluid_view_convert' ), 'value' => true, 'our_key' => 'use_fluid_view_convert', 'our_title' => Member::loggedIn()->language()->addToStack( 'use_fluid_view_convert' ) )
);
return $settings;
@@ -116,7 +107,7 @@ class _Vanilla extends \IPS\convert\Software
return;
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'fluid' ), array( "conf_key=?", 'forums_default_view' ) );
Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'fluid' ), array( "conf_key=?", 'forums_default_view' ) );
}
/**
@@ -125,7 +116,7 @@ class _Vanilla extends \IPS\convert\Software
*
* @param string $location File location retrieved from the database
* @param string $uploadsPath Configured uploads path
* @return \IPS\Http\Url|string|void
* @return Url|string|void
*/
public static function parseMediaLocation( $location, $uploadsPath )
{
@@ -134,7 +125,7 @@ class _Vanilla extends \IPS\convert\Software
// URL
if ( preg_match( '`^https?://`', $location ) )
{
return \IPS\Http\Url::external( $location );
return Url::external( $location );
}
// Full filesystem path
elseif ( mb_strpos( $location, $uploadsPath ) === 0 )
@@ -180,11 +171,11 @@ class _Vanilla extends \IPS\convert\Software
* Attempt to convert a MySQL datetime string into a unix timestamp
*
* @param string $date Date(Time) string
* @return \IPS\DateTime Timestamp on successful conversion, otherwise NULL
* @return DateTime Timestamp on successful conversion, otherwise NULL
*/
public static function mysqlToDateTime( $date )
{
return \IPS\DateTime::ts( strtotime( $date ) ?: time() );
return DateTime::ts( strtotime( $date ) ?: time() );
}
/**
@@ -203,10 +194,6 @@ class _Vanilla extends \IPS\convert\Software
'table' => 'User',
'where' => array( "Deleted<>?", 1 ),
),
'convertStatuses' => array(
'table' => 'Activity',
'where' => array( 'ActivityTypeId=?', (int) static::$activityStatusType )
),
'convertPrivateMessages' => array(
'table' => 'Conversation',
'where' => NULL
@@ -266,15 +253,15 @@ class _Vanilla extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[ $group->g_id ] = $group->name;
}
foreach( $this->db->select( '*', 'Role' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['RoleID']}"] = $group['Name'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['RoleID']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['RoleID']}"] = $group['Name'];
Member::loggedIn()->language()->words["map_group_{$group['RoleID']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['RoleID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -290,13 +277,13 @@ class _Vanilla extends \IPS\convert\Software
$return['convertMembers'] = array();
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['attach_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'attach_location' );
Member::loggedIn()->language()->words['attach_location_desc'] = Member::loggedIn()->language()->addToStack( 'attach_location' );
$return['convertMembers']['attach_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_vanilla_photopath'),
'field_hint' => Member::loggedIn()->language()->addToStack('convert_vanilla_photopath'),
);
break;
}
@@ -312,28 +299,25 @@ class _Vanilla extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
/* Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'core_member_status_updates', 'class' => 'IPS\core\Statuses\Status' ), 2, array( 'app', 'link', 'class' ) );
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_rebuild_attachments" );
}
@@ -344,12 +328,14 @@ class _Vanilla extends \IPS\convert\Software
protected static $_parseDown = NULL;
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
/* Is this Markdown? */
if( mb_substr( $post, 0, 15 ) == '<!--Markdown-->' )
@@ -357,8 +343,8 @@ class _Vanilla extends \IPS\convert\Software
/* Load parser */
if( static::$_parseDown === NULL )
{
\IPS\IPS::$PSR0Namespaces['erusev'] = \IPS\ROOT_PATH . "/applications/convert/sources/Tools/Vanilla/Parsedown";
static::$_parseDown = new \erusev\Parsedown();
IPS::$PSR0Namespaces['erusev'] = \IPS\ROOT_PATH . "/applications/convert/sources/Tools/Vanilla/Parsedown";
static::$_parseDown = new Parsedown();
}
$post = str_replace( '<!--Markdown-->', '', $post );
@@ -372,7 +358,7 @@ class _Vanilla extends \IPS\convert\Software
$post = '';
foreach( $postData as $data )
{
if( \is_array( $data['insert'] ) )
if( is_array( $data['insert'] ) )
{
switch ( $data['insert']['embed-external']['data']['embedType'] )
{
@@ -392,11 +378,11 @@ class _Vanilla extends \IPS\convert\Software
$matches = [];
preg_match_all( '/@("|&quot;)([^@"]+)("|&quot;)/i', $post, $matches );
if( \count( $matches ) )
if( count( $matches ) )
{
foreach( $matches[0] as $key => $tag )
{
$member = \IPS\Member::load( $matches[2][ $key ], 'name' );
$member = Member::load( $matches[2][ $key ], 'name' );
if( !$member->member_id )
{
continue;
@@ -454,7 +440,7 @@ class _Vanilla extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -488,8 +474,8 @@ class _Vanilla extends \IPS\convert\Software
$bdayMonth = NULL;
$bdayYear = NULL;
$dateOfBirth = !empty( $row['DateOfBirth'] ) ? @\IPS\DateTime::ts( strtotime( $row['DateOfBirth'] ) ) : NULL;
if ( $dateOfBirth instanceof \IPS\DateTime )
$dateOfBirth = !empty( $row['DateOfBirth'] ) ? @DateTime::ts( strtotime( $row['DateOfBirth'] ) ) : NULL;
if ( $dateOfBirth instanceof DateTime )
{
$bdayYear = $dateOfBirth->format( 'Y' );
$bdayMonth = $dateOfBirth->format( 'n' );
@@ -517,7 +503,7 @@ class _Vanilla extends \IPS\convert\Software
'last_visit' => static::mysqlToDateTime( $row['DateLastActive'] ),
'timezone' => !empty( $row['HourOffset'] )
? (int) $row['HourOffset']
: new \DateTimeZone(
: new DateTimeZone(
'UTC'
),
'member_posts' => (int) $row['CountComments'],
@@ -529,7 +515,7 @@ class _Vanilla extends \IPS\convert\Software
if ( !empty( $row['Photo'] ) AND ( $location = static::parseMediaLocation( $row['Photo'], $uploadsPath ) ) )
{
if ( $location instanceof \IPS\Http\Url )
if ( $location instanceof Url )
{
/* The library uses file_get_contents() so we can just pop the file name off and pass the URL directly */
$filebits = explode( '/', (string) $location );
@@ -539,8 +525,8 @@ class _Vanilla extends \IPS\convert\Software
else
{
// Full-sized profile photos start with "p", small profile photos start with "n"
$filename = 'p' . pathinfo( $location, \PATHINFO_BASENAME );
$filepath = pathinfo( $location, \PATHINFO_DIRNAME );
$filename = 'p' . pathinfo( $location, PATHINFO_BASENAME );
$filepath = pathinfo( $location, PATHINFO_DIRNAME );
}
}
@@ -551,37 +537,6 @@ class _Vanilla extends \IPS\convert\Software
}
}
/**
* Convert statuses
*
* @return void
*/
public function convertStatuses()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'ActivityID' );
$data = iterator_to_array( $this->fetch( 'Activity', 'ActivityID', array( 'ActivityTypeID=?', static::$activityStatusType ) ) );
$this->app->preCacheLinks( $data, [ 'core_members' => 'ActivityUserID' ] );
/* Run conversions */
foreach ( $data as $row )
{
$info = array(
'status_id' => $row['ActivityID'],
'status_member_id' => $row['ActivityUserID'],
'status_date' => \IPS\DateTime::ts( strtotime( $row['DateInserted'] ) ?: time() ),
'status_content' => $row['Story'],
//'status_replies' => $this->countRows( 'ActivityComment', array( 'ActivityID=?', $row['ActivityID'] ) ),
'status_author_id' => $row['ActivityUserID'],
'status_author_ip' => $row['InsertIPAddress'],
);
$libraryClass->convertStatus( $info );
$libraryClass->setLastKeyValue( $row['ActivityID'] );
}
}
/**
* Convert PMs
*
@@ -606,7 +561,7 @@ class _Vanilla extends \IPS\convert\Software
'mt_date' => static::mysqlToDateTime( $row['DateInserted'] ),
'mt_starter_id' => $row['InsertUserID'],
'mt_last_post_time' => static::mysqlToDateTime( $row['DateUpdated'] ),
'mt_to_count' => ( isset( $row['CountParticipants'] ) ) ? $row['CountParticipants'] : \count( \unserialize( $row['Contributors'] ) ),
'mt_to_count' => ( isset( $row['CountParticipants'] ) ) ? $row['CountParticipants'] : count( unserialize( $row['Contributors'] ) ),
'mt_replies' => $row['CountMessages'],
);
@@ -667,7 +622,7 @@ class _Vanilla extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -677,11 +632,11 @@ class _Vanilla extends \IPS\convert\Software
if( preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $member->conv_password ) OR mb_substr( $member->conv_password, 0, 3 ) == '$P$' )
{
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( 8, TRUE );
$ph = new PasswordHash( 8, TRUE );
return $ph->CheckPassword( $password, $member->conv_password ) ? TRUE : FALSE;
}
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( str_replace( '&#39;', "'", html_entity_decode( $password ) ) ) . $member->misc ) ) )
if ( Login::compareHashes( $member->conv_password, md5( md5( str_replace( '&#39;', "'", html_entity_decode( $password ) ) ) . $member->misc ) ) )
{
return TRUE;
}
@@ -12,16 +12,52 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use InvalidArgumentException;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\App;
use IPS\convert\Library;
use IPS\convert\Software;
use IPS\core\Ignore;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Request\Exception;
use IPS\Http\Url;
use IPS\Image;
use IPS\Login;
use IPS\Member;
use IPS\Member\Club;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Settings;
use IPS\Task;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function is_array;
use function is_null;
use function preg_match;
use function strlen;
use function substr;
use function unserialize;
use const ENT_HTML5;
use const ENT_QUOTES;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Core Converter
*/
class _Vbulletin extends \IPS\convert\Software
class Vbulletin extends Software
{
/**
* @brief vBulletin 4 Stores all attachments under one table - this will store the content type for the forums app.
@@ -41,12 +77,12 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App $app The application to reference for database and other information.
* @param App $app The application to reference for database and other information.
* @param bool $needDB Establish a DB connection
* @return void
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public function __construct( \IPS\convert\App $app, $needDB=TRUE )
public function __construct( App $app, $needDB=TRUE )
{
$return = parent::__construct( $app, $needDB );
@@ -70,7 +106,7 @@ class _Vbulletin extends \IPS\convert\Software
}
/* If this is vB4, what is the content type ID for posts? */
if ( static::$postContentType === NULL AND ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) )
if ( static::$postContentType === NULL AND ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) )
{
static::$postContentType = $this->db->select( 'contenttypeid', 'contenttype', array( "class=?", 'Post' ) )->first();
}
@@ -144,10 +180,6 @@ class _Vbulletin extends \IPS\convert\Software
'table' => 'userchangelog',
'where' => NULL
),
'convertStatuses' => array(
'table' => 'visitormessage',
'where' => NULL
),
'convertIgnoredUsers' => array(
'table' => 'userlist',
'where' => array( "type=?", 'ignore' )
@@ -190,7 +222,7 @@ class _Vbulletin extends \IPS\convert\Software
$rows['convertMembersFollowers'] = array(
'step_method' => 'convertMembersFollowers',
'step_title' => 'convert_follows',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_app=? and follow_area=?', 'core', 'member' ) ),
'ips_rows' => Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_app=? and follow_area=?', 'core', 'member' ) ),
'source_rows' => $count,
'per_cycle' => 200,
'dependencies' => array( 'convertMembers' ),
@@ -221,7 +253,7 @@ class _Vbulletin extends \IPS\convert\Software
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
@@ -240,7 +272,7 @@ class _Vbulletin extends \IPS\convert\Software
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
return parent::countRows( $table, $where, $recache );
@@ -298,7 +330,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$setting = $this->db->select( 'varname, value', 'setting', array( "varname=?", $theirs ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
@@ -307,12 +339,12 @@ class _Vbulletin extends \IPS\convert\Software
{
$title = $this->db->select( 'text', 'phrase', array( "varname=?", "setting_{$setting['varname']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$title = $setting['varname'];
}
$settings[$setting['varname']] = array( 'title' => $title, 'value' => $setting['value'], 'our_key' => $ours, 'our_title' => \IPS\Member::loggedIn()->language()->addToStack( $ours ) );
$settings[$setting['varname']] = array( 'title' => $title, 'value' => $setting['value'], 'our_key' => $ours, 'our_title' => Member::loggedIn()->language()->addToStack( $ours ) );
}
return $settings;
@@ -364,8 +396,8 @@ class _Vbulletin extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_vb_smilie_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_vb_smilie_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
),
'keep_existing_emoticons' => array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -381,8 +413,8 @@ class _Vbulletin extends \IPS\convert\Software
$return['convertProfileFieldGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_groups' ), 'IPS\core\ProfileFields\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_groups' ), 'IPS\core\ProfileFields\Group' ) AS $group )
{
$options[$group->_id] = $group->_title;
}
@@ -393,14 +425,14 @@ class _Vbulletin extends \IPS\convert\Software
try
{
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "category{$id}_title" ) )->first();
Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "category{$id}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = "vBulletin Profile Group {$id}";
Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = "vBulletin Profile Group {$id}";
}
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$id}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfgroup_desc' );
Member::loggedIn()->language()->words["map_pfgroup_{$id}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfgroup_desc' );
$return['convertProfileFieldGroups']["map_pfgroup_{$group['profilefieldcategoryid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -416,8 +448,8 @@ class _Vbulletin extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
@@ -426,13 +458,13 @@ class _Vbulletin extends \IPS\convert\Software
{
try
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_title" ) )->first();
Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = "vBulletin Profile Field {$field['profilefieldid']}";
Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = "vBulletin Profile Field {$field['profilefieldid']}";
}
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['profilefieldid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -449,15 +481,15 @@ class _Vbulletin extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'usergroup' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}"] = $group['title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}"] = $group['title'];
Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['usergroupid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -477,7 +509,7 @@ class _Vbulletin extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'avatars',
'field_required' => TRUE,
'field_extra' => array( 'options' => array( 'avatars' => \IPS\Member::loggedIn()->language()->addToStack( 'avatars' ), 'profile_photos' => \IPS\Member::loggedIn()->language()->addToStack( 'profile_photos' ) ) ),
'field_extra' => array( 'options' => array( 'avatars' => Member::loggedIn()->language()->addToStack( 'avatars' ), 'profile_photos' => Member::loggedIn()->language()->addToStack( 'profile_photos' ) ) ),
'field_hint' => NULL,
);
@@ -488,13 +520,13 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
'database' => Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
/* Find out where the avatar gallery photos live */
@@ -503,23 +535,23 @@ class _Vbulletin extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_vb_avatar_gallery_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_vb_avatar_gallery_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
/* And decide what to do about these... */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'user_title' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -529,7 +561,7 @@ class _Vbulletin extends \IPS\convert\Software
break;
case 'convertClubs':
\IPS\Member::loggedIn()->language()->words['club_photo_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location' );
Member::loggedIn()->language()->words['club_photo_location'] = Member::loggedIn()->language()->addToStack( 'photo_location' );
$return['convertClubs'] = array();
$return['convertClubs']['club_photo_location'] = array(
@@ -538,13 +570,13 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
'database' => Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
break;
}
@@ -560,46 +592,45 @@ class _Vbulletin extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
/* Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'core_member_status_updates', 'class' => 'IPS\core\Statuses\Status' ), 2, array( 'app', 'link', 'class' ) );
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_announcements', 'extension' => 'core_Announcement' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_announcements', 'extension' => 'core_Announcement' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Message' ), 3, array( 'class' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Message' ), 3, array( 'class' ) );
/* Clubs */
\IPS\Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_announce_rebuild" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
if( \preg_match( "#\[sigpic]#i", $post ) )
if( preg_match( "#\[sigpic]#i", $post ) )
{
$post = preg_replace( "#\[sigpic\](.+?)\[\/sigpic\]#i", "", $post );
}
@@ -607,7 +638,7 @@ class _Vbulletin extends \IPS\convert\Software
$post = str_replace( ">", "&gt;", $post );
$post = str_replace( "'", "&#39;", $post );
if( \preg_match( "#\[quote=#i", $post ) )
if( preg_match( "#\[quote=#i", $post ) )
{
$post = preg_replace( "#\[quote=([^\];]+?)\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);\d+\]#i", "[quote name='$1']", $post );
@@ -615,21 +646,21 @@ class _Vbulletin extends \IPS\convert\Software
}
/* Remove video tags and allow our parser to handle the embeds it supports */
if( \preg_match( "#\[video=#i", $post ) )
if( preg_match( "#\[video=#i", $post ) )
{
$post = preg_replace( "#\[video=[a-z_]+;[a-z0-9_-]+\](.*?)\[\/video\]#i", '$1', $post );
}
/* This is not a core feature in vBulletin, but is a popular addon and the code was supplied below.
Addon URL: https://www.dragonbyte-tech.com/store/product/20-advanced-user-tagging/ */
if( \preg_match( "#\[mention=#i", $post ) )
if( preg_match( "#\[mention=#i", $post ) )
{
preg_match_all( '#\[MENTION=(\d+)\](.+?)\[\/MENTION\]#i', $post, $matches );
if ( \count( $matches ) )
if ( count( $matches ) )
{
/* Make sure we actually have mention data */
if ( isset( $matches[1] ) AND \count( $matches[1] ) )
if ( isset( $matches[1] ) AND count( $matches[1] ) )
{
$mentions = array();
foreach ( $matches[1] AS $k => $v )
@@ -640,7 +671,7 @@ class _Vbulletin extends \IPS\convert\Software
}
}
$maps = iterator_to_array( \IPS\Db::i()->select( 'name, member_id', 'core_members', array( \IPS\Db::i()->in( 'name', array_keys( $mentions ) ) ) )->setKeyField( 'name' )->setValueField( 'member_id' ) );
$maps = iterator_to_array( Db::i()->select( 'name, member_id', 'core_members', array( Db::i()->in( 'name', array_keys( $mentions ) ) ) )->setKeyField( 'name' )->setValueField( 'member_id' ) );
foreach ( $mentions AS $memberName => $memberId )
{
@@ -686,7 +717,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$imageCategory = $this->db->select( '*', 'imagecategory', array( "imagecategoryid=?", $emoticon['imagecategoryid'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$imageCategory = array(
'title' => "Converted",
@@ -707,7 +738,7 @@ class _Vbulletin extends \IPS\convert\Software
/* Remote */
try
{
$filedata = (string) \IPS\Http\Url::external( $emoticon['smiliepath'] )->request()->get();
$filedata = (string) Url::external( $emoticon['smiliepath'] )->request()->get();
}
catch( \Exception $e ) {}
}
@@ -739,7 +770,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$name = $this->db->select( 'text', 'phrase', array( "varname=?", "category{$group['profilefieldcategoryid']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$name = "vBulletin Profile Group {$group['profilefieldcategoryid']}";
}
@@ -773,7 +804,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$name = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$name = "vBulletin Profile Field {$field['profilefieldid']}";
}
@@ -782,16 +813,16 @@ class _Vbulletin extends \IPS\convert\Software
{
$desc = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_desc" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$desc = "";
}
$merge = ( $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$field['profilefieldid']}"] != 'none' ) ? $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$field['profilefieldid']}"] : NULL;
if ( \in_array( mb_strtolower( $field['type'] ), array( 'select', 'radio', 'checkbox', 'select_multiple' ) ) )
if ( in_array( mb_strtolower( $field['type'] ), array( 'select', 'radio', 'checkbox', 'select_multiple' ) ) )
{
$options = @\unserialize( $field['data'] );
$options = @unserialize( $field['data'] );
if ( $options )
{
$content = json_encode( $options );
@@ -800,10 +831,10 @@ class _Vbulletin extends \IPS\convert\Software
{
/* We can try to restore the serialized value */
$data = preg_replace_callback( '/s:(\d+):"(.*?)";/i', function( $match ) {
return ( $match[1] == \strlen( $match[2] ) ) ? $match[0] : 's:' . \strlen( $match[2] ) . ':"' . $match[2] . '";';
return ( $match[1] == strlen( $match[2] ) ) ? $match[0] : 's:' . strlen( $match[2] ) . ':"' . $match[2] . '";';
}, $field['data'] );
if( $options = @\unserialize( $data ) )
if( $options = @unserialize( $data ) )
{
$content = json_encode( $options );
}
@@ -833,7 +864,7 @@ class _Vbulletin extends \IPS\convert\Software
'pf_desc' => $desc,
'pf_type' => $type,
'pf_content' => $content,
'pf_not_null' => ( \in_array( $field['required'], array( 1, 3 ) ) ) ? 1 : 0,
'pf_not_null' => ( in_array( $field['required'], array( 1, 3 ) ) ) ? 1 : 0,
'pf_member_hide' => $field['hidden'] ? 'hide' : 'all',
'pf_max_input' => $field['maxlength'],
'pf_member_edit' => ( $field['editable'] >= 1 ) ? 1 : 0,
@@ -916,7 +947,7 @@ class _Vbulletin extends \IPS\convert\Software
$promotion = $this->db->select( '*', 'userpromotion', array( "usergroupid=?", $group['usergroupid'] ) )->first();
/* We only support Posts or Join Date */
if ( \in_array( $promotion['strategy'], array( 17, 18 ) ) )
if ( in_array( $promotion['strategy'], array( 17, 18 ) ) )
{
switch( $promotion['strategy'] )
{
@@ -931,7 +962,7 @@ class _Vbulletin extends \IPS\convert\Software
}
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Work out photo vars - vBulletin has a concept of avatars and profile photos, so we are just going to use the larger of the two */
$g_max_photo_vars = array();
@@ -990,10 +1021,8 @@ class _Vbulletin extends \IPS\convert\Software
'g_signature_limits' => $g_signature_limits,
'g_bitoptions' => array(
'gbw_promote_unit_type' => $gbw_promote_unit, // Type of group promotion to use. 1 is days since joining, 0 is content count. Corresponds to g_promotion
'gbw_no_status_update' => !$checkpermission( 'visitormessagepermissions', 'canmessageownprofile' ), // Can NOT post status updates
'gbw_allow_upload_bgimage' => 1, // Can upload a cover photo?
'gbw_view_reps' => $checkpermission( 'genericpermissions2', 'canprofilerep' ), // Can view who gave reputation?
'gbw_no_status_import' => !$checkpermission( 'visitormessagepermissions', 'canmessageownprofile' ), // Can NOT import status updates from Facebook/Twitter
'gbw_disable_tagging' => !$checkpermission( 'genericpermissions', 'cancreatetag' ), // Can NOT use tags
'gbw_disable_prefixes' => !$checkpermission( 'genericpermissions', 'cancreatetag' ), // Can NOT use prefixes
'gbw_pm_override_inbox_full'=> $checkpermission( 'pmpermissions', 'canignorequota' ), // 1 means this group can send other members PMs even when that member's inbox is full
@@ -1002,8 +1031,7 @@ class _Vbulletin extends \IPS\convert\Software
'g_hide_own_posts' => $checkpermission( 'forumpermissions', 'candeletepost' ),
'g_pm_flood_mins' => $this->_setting( 'pmfloodtime' ) / 60,
'g_post_polls' => $checkpermission( 'forumpermissions', 'canpostpoll' ) ? 1 : 0,
'g_vote_polls' => $checkpermission( 'forumpermissions', 'canvote' ) ? 1 : 0,
'g_topic_rate_setting' => $checkpermission( 'forumpermissions', 'canthreadrate' ) ? 1 : 0
'g_vote_polls' => $checkpermission( 'forumpermissions', 'canvote' ) ? 1 : 0
);
$merge = ( $this->app->_session['more_info']['convertGroups']["map_group_{$group['usergroupid']}"] != 'none' ) ? $this->app->_session['more_info']['convertGroups']["map_group_{$group['usergroupid']}"] : NULL;
@@ -1014,7 +1042,7 @@ class _Vbulletin extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -1118,7 +1146,7 @@ class _Vbulletin extends \IPS\convert\Software
$lastWarning = 0;
/* If there are warnings, get the last warning */
if( \count( $warnings ) )
if( count( $warnings ) )
{
$lastWarning = end( $warnings );
reset( $warnings );
@@ -1162,7 +1190,7 @@ class _Vbulletin extends \IPS\convert\Software
{
if( !isset( $bans[ $user['userid'] ] ) )
{
throw new \UnderflowException();
throw new UnderflowException();
}
$temp_ban = $bans[ $user['userid'] ];
@@ -1171,19 +1199,19 @@ class _Vbulletin extends \IPS\convert\Software
$temp_ban = -1;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Main Members Table */
$info = array(
'member_id' => $user['userid'],
'member_group_id' => $user['usergroupid'],
'mgroup_others' => $user['membergroupids'],
'name' => html_entity_decode( $user['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'name' => html_entity_decode( $user['username'], ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
'email' => $user['email'],
'joined' => $user['joindate'],
'ip_address' => $user['ipaddress'],
'warn_level' => ( $user['ipoints'] > 2147483647 ) ? 2147483647 : $user['ipoints'],
'warn_lastwarn' => ( \is_array( $lastWarning ) ) ? $lastWarning['dateline'] : $lastWarning,
'warn_lastwarn' => ( is_array( $lastWarning ) ) ? $lastWarning['dateline'] : $lastWarning,
'bday_day' => $bday_day,
'bday_month' => $bday_month,
'bday_year' => $bday_year,
@@ -1215,13 +1243,13 @@ class _Vbulletin extends \IPS\convert\Software
{
if( !isset( $activation[ $user['userid'] ] ) )
{
throw new \UnderflowException();
throw new UnderflowException();
}
$activation = $activation[ $user['userid'] ];
$info['members_bitoptions']['validating'] = TRUE;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$activation = NULL;
}
@@ -1231,7 +1259,7 @@ class _Vbulletin extends \IPS\convert\Software
{
if( !isset( $fields[ $user['userid'] ] ) )
{
throw new \UnderflowException();
throw new UnderflowException();
}
$profileFields = $fields[ $user['userid'] ];
@@ -1244,7 +1272,7 @@ class _Vbulletin extends \IPS\convert\Software
$profileFields[ str_replace( 'field', '', $key ) ] = $value;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$profileFields = array();
}
@@ -1264,7 +1292,7 @@ class _Vbulletin extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -1281,7 +1309,7 @@ class _Vbulletin extends \IPS\convert\Software
if( $pseudo == 'user_title' )
{
$profileFields[ $pseudo ] = \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'];
$profileFields[ $pseudo ] = in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'];
}
else
{
@@ -1316,7 +1344,7 @@ class _Vbulletin extends \IPS\convert\Software
}
$filepath = NULL;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
try
{
@@ -1334,7 +1362,7 @@ class _Vbulletin extends \IPS\convert\Software
$filepath = NULL;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
list( $filedata, $filename, $filepath ) = array( NULL, NULL, NULL );
}
@@ -1379,11 +1407,11 @@ class _Vbulletin extends \IPS\convert\Software
else
{
/* Throw an exception so we can try the other */
throw new \UnderflowException;
throw new UnderflowException;
}
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$ext = $this->db->select( 'filename', $secondTable, array( "userid=?", $user['userid'] ) )->first();
$ext = explode( '.', $ext );
@@ -1402,12 +1430,12 @@ class _Vbulletin extends \IPS\convert\Software
}
else
{
throw new \UnderflowException;
throw new UnderflowException;
}
}
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
list( $filedata, $filename, $filepath ) = array( NULL, NULL, NULL );
}
@@ -1418,7 +1446,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$avatar = $this->_getFromAvatarGallery( (int) $user['avatarid'] );
if( \is_array( $avatar ) )
if( is_array( $avatar ) )
{
$filename = $avatar['name'];
$filedata = $avatar['data'];
@@ -1430,7 +1458,7 @@ class _Vbulletin extends \IPS\convert\Software
/* If we are validating, store our validation row entry */
if( $activation !== NULL AND $memberId )
{
\IPS\Db::i()->replace( 'core_validating', array(
Db::i()->replace( 'core_validating', array(
'vid' => md5( $activation['activationid'] . $activation['useractivationid'] ),
'member_id' => $memberId,
'entry_date' => $activation['dateline'],
@@ -1443,7 +1471,7 @@ class _Vbulletin extends \IPS\convert\Software
}
/* Skip this if we know that the user doesn't have any warnings */
if( \count( $warnings ) )
if( count( $warnings ) )
{
/* And warn logs made on the profile - we'll do content specific later */
foreach ( $warnings AS $warn )
@@ -1502,52 +1530,6 @@ class _Vbulletin extends \IPS\convert\Software
}
}
/**
* Convert status updates
*
* @return void
*/
public function convertStatuses()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'vmid' );
foreach( $this->fetch( 'visitormessage', 'vmid' ) AS $status )
{
/* Work out approval status */
switch( $status['state'] )
{
case 'moderation':
$approved = 0;
break;
case 'deleted':
$approved = -1;
break;
case 'visible':
default:
$approved = 1;
break;
}
$info = array(
'status_id' => $status['vmid'],
'status_member_id' => $status['userid'],
'status_date' => $status['dateline'],
'status_content' => static::fixPostData( $status['pagetext'] ),
'status_author_id' => $status['postuserid'],
'status_author_ip' => $status['ipaddress'],
'status_approved' => $approved
);
$libraryClass->convertStatus( $info );
$libraryClass->setLastKeyValue( $status['vmid'] );
}
}
/**
* Convert one or more settings
*
@@ -1567,12 +1549,12 @@ class _Vbulletin extends \IPS\convert\Software
{
$setting = $this->db->select( 'value', 'setting', array( "varname=?", $theirs ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $setting ), array( "conf_key=?", $ours ) );
Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $setting ), array( "conf_key=?", $ours ) );
}
}
@@ -1593,7 +1575,7 @@ class _Vbulletin extends \IPS\convert\Software
'ignore_ignore_id' => $ignore['relationid'],
);
foreach( \IPS\core\Ignore::types() AS $type )
foreach( Ignore::types() AS $type )
{
$info['ignore_' . $type] = 1;
}
@@ -1752,7 +1734,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$icon = $this->db->select( '*', 'socialgroupicon', array( "groupid=?", $group['groupid'] ) )->first();
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
$info = array(
'club_id' => $group['groupid'],
@@ -1806,7 +1788,7 @@ class _Vbulletin extends \IPS\convert\Software
$type = 'leader';
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
if ( $type === NULL )
{
@@ -1827,7 +1809,7 @@ class _Vbulletin extends \IPS\convert\Software
* @brief Silly Bit Options for Groups. Typically we would leave out app specific options (such as Forums here) however we need them for some general permissions, like uploading.
* @note This is public simply because I do not want to do this ever again if it ever changes.
*/
public static $bitOptions = array(
public static array $bitOptions = array(
'forumpermissions' => array(
'canview' => 1,
'canviewothers' => 2,
@@ -2042,7 +2024,7 @@ class _Vbulletin extends \IPS\convert\Software
$this->settingsCache[$key] = $setting['defaultvalue'];
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
/* If we failed to find it, we probably will fail again on later attempts */
$this->settingsCache[$key] = NULL;
@@ -2054,19 +2036,19 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
/* Attachment URLs are the same across VB 3.8 and VB 4 */
if( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'attachment.php' ) !== FALSE )
if( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'attachment.php' ) !== FALSE )
{
$id = NULL;
try
{
$id = (string) $this->app->getLink( \IPS\Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
$id = (string) $this->app->getLink( Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
}
/* Try any child conversions */
catch( \Exception $e )
@@ -2075,7 +2057,7 @@ class _Vbulletin extends \IPS\convert\Software
{
try
{
$id = (string) $child->getLink( \IPS\Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
$id = (string) $child->getLink( Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
break;
}
catch( \Exception $e ) {}
@@ -2087,19 +2069,19 @@ class _Vbulletin extends \IPS\convert\Software
return NULL;
}
return \IPS\Http\Url::external( \IPS\Settings::i()->base_url . 'applications/core/interface/file/attachment.php' )->setQueryString( 'id', $id );
return Url::external( Settings::i()->base_url . 'applications/core/interface/file/attachment.php' )->setQueryString( 'id', $id );
}
/* Tag URLs are straightforward */
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'tags.php' ) !== FALSE AND isset( \IPS\Request::i()->tag ) )
elseif( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'tags.php' ) !== FALSE AND isset( Request::i()->tag ) )
{
return \IPS\Http\Url::internal( "app=core&module=search&controller=search&tags=" . \IPS\Request::i()->tag, 'front', 'search' );
return Url::internal( "app=core&module=search&controller=search&tags=" . Request::i()->tag, 'front', 'search' );
}
/* Club Index */
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'group.php' ) !== FALSE AND isset( \IPS\Request::i()->groupid ) )
elseif( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'group.php' ) !== FALSE AND isset( Request::i()->groupid ) )
{
try
{
return \IPS\Member\Club::loadAndCheckPerms( (string) $this->app->getLink( \IPS\Request::i()->groupid, array( 'core_clubs' ) ) )->url();
return Club::loadAndCheckPerms( (string) $this->app->getLink( Request::i()->groupid, array( 'core_clubs' ) ) )->url();
}
catch( \Exception $e )
{
@@ -2109,7 +2091,7 @@ class _Vbulletin extends \IPS\convert\Software
else
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
@@ -2120,14 +2102,14 @@ class _Vbulletin extends \IPS\convert\Software
* /member.php?u=1
* /member.php?1-name
*/
$path = $url->data[ \IPS\Http\Url::COMPONENT_PATH ];
$path = $url->data[ Url::COMPONENT_PATH ];
if( mb_strpos( $path, 'member.php' ) !== FALSE )
{
if( isset( \IPS\Request::i()->u ) )
if( isset( Request::i()->u ) )
{
$oldId = \IPS\Request::i()->u;
$oldId = Request::i()->u;
}
elseif( preg_match( '#^(\d+)-[^/]+#i', $url->data[ \IPS\Http\Url::COMPONENT_QUERY ], $matches ) )
elseif( preg_match( '#^(\d+)-[^/]+#i', $url->data[ Url::COMPONENT_QUERY ], $matches ) )
{
$oldId = $matches[1];
}
@@ -2137,7 +2119,7 @@ class _Vbulletin extends \IPS\convert\Software
$oldId = $queryStringPieces[0];
}
}
elseif( preg_match( '#/members/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
elseif( preg_match( '#/members/([0-9]+)#i', $url->data[ Url::COMPONENT_PATH ], $matches ) )
{
$oldId = (int) $matches[1];
}
@@ -2147,7 +2129,7 @@ class _Vbulletin extends \IPS\convert\Software
try
{
$data = (string) $this->app->getLink( $oldId, array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e )
{
@@ -2162,13 +2144,13 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
if ( \IPS\Login::compareHashes( $member->conv_password, md5( md5( str_replace( '&#39;', "'", html_entity_decode( $password ) ) ) . $member->conv_password_extra ) ) )
if ( Login::compareHashes( $member->conv_password, md5( md5( str_replace( '&#39;', "'", html_entity_decode( $password ) ) ) . $member->conv_password_extra ) ) )
{
return TRUE;
}
@@ -2181,23 +2163,23 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Centralised PM conversation conversion
*
* @param $libraryClass \IPS\convert\Library
* @param $libraryClass Library
* @param $pm array
* @return mixed
*/
protected function _convertPm( \IPS\convert\Library $libraryClass, array $pm )
protected function _convertPm( Library $libraryClass, array $pm )
{
try
{
$replies = $this->db->select( 'COUNT(*)', 'pm', array( "parentpmid=? AND folderid!=?", $pm['pmid'], -1 ) )->first();
$replies += 1;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$replies = 1;
}
$toUserArray = @\unserialize( $pm['touserarray'] );
$toUserArray = @unserialize( $pm['touserarray'] );
$topic = array(
'mt_id' => $pm['pmid'],
@@ -2206,7 +2188,7 @@ class _Vbulletin extends \IPS\convert\Software
'mt_starter_id' => $pm['fromuserid'],
'mt_start_time' => $pm['dateline'],
'mt_last_post_time' => $pm['dateline'],
'mt_to_count' => ( $toUserArray AND \is_array( $toUserArray ) ) ? \count( $toUserArray ) : 1,
'mt_to_count' => ( $toUserArray AND is_array( $toUserArray ) ) ? count( $toUserArray ) : 1,
'mt_to_member_id' => $pm['userid'],
'mt_replies' => $replies,
'mt_first_msg_id' => $pm['pmid'],
@@ -2225,17 +2207,17 @@ class _Vbulletin extends \IPS\convert\Software
}
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Use stored author ids */
if( isset( $toUserArray['cc'] ) AND \is_array( $toUserArray['cc'] ) )
if( isset( $toUserArray['cc'] ) AND is_array( $toUserArray['cc'] ) )
{
foreach( array_keys( $toUserArray['cc'] ) as $id )
{
$authors[ $id ] = $id;
}
}
elseif( $toUserArray AND \is_array( $toUserArray ) AND \count( $toUserArray ) )
elseif( $toUserArray AND is_array( $toUserArray ) AND count( $toUserArray ) )
{
foreach( array_keys( $toUserArray ) as $id )
{
@@ -2279,32 +2261,32 @@ class _Vbulletin extends \IPS\convert\Software
{
$avatar = $this->db->select( 'avatarpath', 'avatar', array( 'avatarid=?', $id ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
return $this->_avatars[ $id ] = FALSE;
}
/* It might be a remote image */
if( \substr( $avatar, 0, 8 ) === 'https://' OR \substr( $avatar, 0, 7 ) === 'http://' )
if( substr( $avatar, 0, 8 ) === 'https://' OR substr( $avatar, 0, 7 ) === 'http://' )
{
try
{
$profilePhotoName = pathinfo( parse_url( $avatar, PHP_URL_PATH ), PATHINFO_BASENAME );
$profilePhotoData = \IPS\Http\Url::external( $avatar )->request()->get();
$profilePhotoData = Url::external( $avatar )->request()->get();
/* Check it's really an image */
try
{
\IPS\Image::create( $profilePhotoData );
Image::create( $profilePhotoData );
}
catch( \InvalidArgumentException $e )
catch( InvalidArgumentException $e )
{
return $this->_avatars[ $id ] = FALSE;
}
return $this->_avatars[ $id ] = array( 'data' => $profilePhotoData, 'name' => $profilePhotoName );
}
catch ( \IPS\Http\Request\Exception $e )
catch ( Exception $e )
{
return $this->_avatars[ $id ] = FALSE;
}
@@ -2312,7 +2294,7 @@ class _Vbulletin extends \IPS\convert\Software
/* nope, it's a file */
$dirName = pathinfo( $avatar, PATHINFO_DIRNAME );
if( \substr( $this->app->_session['more_info']['convertMembers']['avatar_gallery_location'], -\strlen( $dirName ) ) == $dirName )
if( substr( $this->app->_session['more_info']['convertMembers']['avatar_gallery_location'], -strlen( $dirName ) ) == $dirName )
{
$profilePhotoName = pathinfo( $avatar, PATHINFO_BASENAME );
$profilePhotoData = file_get_contents( rtrim( $this->app->_session['more_info']['convertMembers']['avatar_gallery_location'], '/' ) . '/' . $profilePhotoName );
@@ -12,16 +12,45 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use InvalidArgumentException;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\App;
use IPS\convert\Software;
use IPS\core\Ignore;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use PasswordHash;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function is_null;
use function strpos;
use function unserialize;
use const ENT_HTML5;
use const ENT_QUOTES;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin 5 Core Converter
*/
class _Vbulletin5 extends \IPS\convert\Software
class Vbulletin5 extends Software
{
/**
* @brief is vB 5.5.3 or newer
@@ -31,12 +60,12 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App $app The application to reference for database and other information.
* @param App $app The application to reference for database and other information.
* @param bool $needDB Establish a DB connection
* @return void
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public function __construct( \IPS\convert\App $app, $needDB=TRUE )
public function __construct( App $app, $needDB=TRUE )
{
$return = parent::__construct( $app, $needDB );
@@ -51,7 +80,7 @@ class _Vbulletin5 extends \IPS\convert\Software
static::$skipCustomProfilePic = version_compare( $version, '5.5.3', '>=' );
}
}
catch( \Exception $e ) {} # If we can't query, we won't be able to do anything anyway
catch( Exception $e ) {} # If we can't query, we won't be able to do anything anyway
}
return $return;
@@ -164,20 +193,20 @@ class _Vbulletin5 extends \IPS\convert\Software
return $this->db->select( 'COUNT(*)', 'privatemessage', array( "privatemessage.msgtype=? AND node.parentid=?", 'message', $node ) )->join( 'node', 'privatemessage.nodeid = node.nodeid' )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
return 0;
}
catch( \Exception $e )
catch( Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
case 'node':
if ( $where === 'clubs' )
{
return \count( $this->fetchClubs() );
return count( $this->fetchClubs() );
}
else
{
@@ -201,9 +230,9 @@ class _Vbulletin5 extends \IPS\convert\Software
{
return $this->db->select( 'count(changeid)', 'userchangelog', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) )->first();
}
catch( \Exception $e )
catch( Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
@@ -249,7 +278,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$setting = $this->db->select( 'varname, value', 'setting', array( "varname=?", $theirs ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
@@ -258,12 +287,12 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$title = $this->db->select( 'text', 'phrase', array( "varname=?", "setting_{$setting['varname']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$title = $setting['varname'];
}
$settings[$setting['varname']] = array( 'title' => $title, 'value' => $setting['value'], 'our_key' => $ours, 'our_title' => \IPS\Member::loggedIn()->language()->addToStack( $ours ) );
$settings[$setting['varname']] = array( 'title' => $title, 'value' => $setting['value'], 'our_key' => $ours, 'our_title' => Member::loggedIn()->language()->addToStack( $ours ) );
}
return $settings;
@@ -315,8 +344,8 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_vb5_smilie_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_vb5_smilie_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
),
'keep_existing_emoticons' => array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -332,8 +361,8 @@ class _Vbulletin5 extends \IPS\convert\Software
$return['convertProfileFieldGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_groups' ), 'IPS\core\ProfileFields\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_groups' ), 'IPS\core\ProfileFields\Group' ) AS $group )
{
$options[$group->_id] = $group->_title;
}
@@ -344,14 +373,14 @@ class _Vbulletin5 extends \IPS\convert\Software
try
{
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "category{$id}_title" ) )->first();
Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "category{$id}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = "vBulletin Profile Group {$id}";
Member::loggedIn()->language()->words["map_pfgroup_{$id}"] = "vBulletin Profile Group {$id}";
}
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$id}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfgroup_desc' );
Member::loggedIn()->language()->words["map_pfgroup_{$id}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfgroup_desc' );
$return['convertProfileFieldGroups']["map_pfgroup_{$group['profilefieldcategoryid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -367,8 +396,8 @@ class _Vbulletin5 extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
@@ -377,13 +406,13 @@ class _Vbulletin5 extends \IPS\convert\Software
{
try
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_title" ) )->first();
Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = "vBulletin Profile Field {$field['profilefieldid']}";
Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}"] = "vBulletin Profile Field {$field['profilefieldid']}";
}
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['profilefieldid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['profilefieldid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -400,15 +429,15 @@ class _Vbulletin5 extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'usergroup' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}"] = $group['title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}"] = $group['title'];
Member::loggedIn()->language()->words["map_group_{$group['usergroupid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['usergroupid']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -428,7 +457,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'avatars',
'field_required' => TRUE,
'field_extra' => array( 'options' => array( 'avatars' => \IPS\Member::loggedIn()->language()->addToStack( 'avatars' ), 'profile_photos' => \IPS\Member::loggedIn()->language()->addToStack( 'profile_photos' ) ) ),
'field_extra' => array( 'options' => array( 'avatars' => Member::loggedIn()->language()->addToStack( 'avatars' ), 'profile_photos' => Member::loggedIn()->language()->addToStack( 'profile_photos' ) ) ),
'field_hint' => NULL,
);
@@ -439,28 +468,28 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
'database' => Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
/* And decide what to do about these... */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'google', 'user_title' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -470,7 +499,7 @@ class _Vbulletin5 extends \IPS\convert\Software
break;
case 'convertClubs':
\IPS\Member::loggedIn()->language()->words['club_photo_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location' );
Member::loggedIn()->language()->words['club_photo_location'] = Member::loggedIn()->language()->addToStack( 'photo_location' );
$return['convertClubs'] = array();
$return['convertClubs']['club_photo_location'] = array(
@@ -479,13 +508,13 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
'database' => Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( $value != 'database' AND !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
break;
}
@@ -501,45 +530,47 @@ class _Vbulletin5 extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
/* Clubs */
\IPS\Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
/* IMG2 BBcode - Needs to be ran before htmlspecialchars due to it containing JSON */
$matches = array();
preg_match_all( "/\[IMG2=JSON](.+?)\[\/IMG2\]/i", $post, $matches );
if ( \count( $matches ) )
if ( count( $matches ) )
{
foreach( $matches[0] as $k => $v )
{
@@ -548,7 +579,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/* Make sure the JSON value exists */
if( !isset( $matches[1][ $k ] ) )
{
throw new \DomainException;
throw new DomainException;
}
$decoded = json_decode( $matches[1][ $k ], TRUE );
@@ -556,12 +587,12 @@ class _Vbulletin5 extends \IPS\convert\Software
/* Make sure the JSON is valid and contains src */
if( !isset( $decoded['src'] ) OR empty( $decoded['src'] ) )
{
throw new \DomainException;
throw new DomainException;
}
$post = str_replace( $v, "[img]{$decoded['src']}[/img]", $post );
}
catch( \DomainException $e )
catch( DomainException $e )
{
continue;
}
@@ -572,10 +603,10 @@ class _Vbulletin5 extends \IPS\convert\Software
$matches = array();
preg_match_all( '#\[user(=\"?(\d+)\"?)?\](.+?)\[\/user\]#i', $post, $matches ); // this purposefully matches more than the default vB mention.
if ( \count( $matches ) )
if ( count( $matches ) )
{
/* Make sure we actually have mention data */
if ( isset( $matches[1] ) AND \count( $matches[1] ) )
if ( isset( $matches[1] ) AND count( $matches[1] ) )
{
$mentions = array();
foreach ( $matches[1] AS $k => $v )
@@ -586,7 +617,7 @@ class _Vbulletin5 extends \IPS\convert\Software
}
}
$maps = iterator_to_array( \IPS\Db::i()->select( 'name, member_id', 'core_members', array( \IPS\Db::i()->in( 'name', array_keys( $mentions ) ) ) )->setKeyField( 'name' )->setValueField( 'member_id' ) );
$maps = iterator_to_array( Db::i()->select( 'name, member_id', 'core_members', array( Db::i()->in( 'name', array_keys( $mentions ) ) ) )->setKeyField( 'name' )->setValueField( 'member_id' ) );
foreach ( $mentions AS $memberName => $memberId )
{
@@ -642,7 +673,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$imageCategory = $this->db->select( '*', 'imagecategory', array( "imagecategoryid=?", $emoticon['imagecategoryid'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$imageCategory = array(
'title' => "Converted",
@@ -679,7 +710,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$name = $this->db->select( 'text', 'phrase', array( "varname=?", "category{$group['profilefieldcategoryid']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$name = "vBulletin Profile Group {$group['profilefieldcategoryid']}";
}
@@ -713,7 +744,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$name = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_title" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$name = "vBulletin Profile Field {$field['profilefieldid']}";
}
@@ -722,16 +753,16 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$desc = $this->db->select( 'text', 'phrase', array( "varname=?", "field{$field['profilefieldid']}_desc" ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$desc = "";
}
$merge = ( $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$field['profilefieldid']}"] != 'none' ) ? $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$field['profilefieldid']}"] : NULL;
if ( \in_array( mb_strtolower( $field['type'] ), array( 'select', 'radio', 'checkbox', 'select_multiple' ) ) )
if ( in_array( mb_strtolower( $field['type'] ), array( 'select', 'radio', 'checkbox', 'select_multiple' ) ) )
{
$content = json_encode( @\unserialize( $field['data'] ) );
$content = json_encode( @unserialize( $field['data'] ) );
}
else
{
@@ -752,7 +783,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'pf_desc' => $desc,
'pf_type' => $type,
'pf_content' => $content,
'pf_not_null' => ( \in_array( $field['required'], array( 1, 3 ) ) ) ? 1 : 0,
'pf_not_null' => ( in_array( $field['required'], array( 1, 3 ) ) ) ? 1 : 0,
'pf_member_hide' => $field['hidden'] ? 'hide' : 'all',
'pf_max_input' => $field['maxlength'],
'pf_member_edit' => ( $field['editable'] >= 1 ) ? 1 : 0,
@@ -835,7 +866,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$promotion = $this->db->select( '*', 'userpromotion', array( "usergroupid=?", $group['usergroupid'] ) )->first();
/* We only support Posts or Join Date */
if ( \in_array( $promotion['strategy'], array( 17, 18 ) ) )
if ( in_array( $promotion['strategy'], array( 17, 18 ) ) )
{
switch( $promotion['strategy'] )
{
@@ -850,7 +881,7 @@ class _Vbulletin5 extends \IPS\convert\Software
}
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Work out photo vars - vBulletin has a concept of avatars and profile photos, so we are just going to use the larger of the two */
$g_max_photo_vars = array();
@@ -909,10 +940,8 @@ class _Vbulletin5 extends \IPS\convert\Software
'g_signature_limits' => $g_signature_limits,
'g_bitoptions' => array(
'gbw_promote_unit_type' => $gbw_promote_unit, // Type of group promotion to use. 1 is days since joining, 0 is content count. Corresponds to g_promotion
'gbw_no_status_update' => !$checkpermission( 'visitormessagepermissions', 'canmessageownprofile' ), // Can NOT post status updates
'gbw_allow_upload_bgimage' => 1, // Can upload a cover photo?
'gbw_view_reps' => $checkpermission( 'genericpermissions2', 'canprofilerep' ), // Can view who gave reputation?
'gbw_no_status_import' => !$checkpermission( 'visitormessagepermissions', 'canmessageownprofile' ), // Can NOT import status updates from Facebook/Twitter
'gbw_disable_tagging' => !$checkpermission( 'genericpermissions', 'cancreatetag' ), // Can NOT use tags
'gbw_disable_prefixes' => !$checkpermission( 'genericpermissions', 'cancreatetag' ), // Can NOT use prefixes
'gbw_pm_override_inbox_full'=> $checkpermission( 'pmpermissions', 'canignorequota' ), // 1 means this group can send other members PMs even when that member's inbox is full
@@ -921,8 +950,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'g_hide_own_posts' => $checkpermission( 'forumpermissions', 'candeletepost' ),
'g_pm_flood_mins' => $this->_setting( 'pmfloodtime' ) / 60,
'g_post_polls' => $checkpermission( 'forumpermissions', 'canpostpoll' ) ? 1 : 0,
'g_vote_polls' => $checkpermission( 'forumpermissions', 'canvote' ) ? 1 : 0,
'g_topic_rate_setting' => $checkpermission( 'forumpermissions', 'canthreadrate' ) ? 1 : 0
'g_vote_polls' => $checkpermission( 'forumpermissions', 'canvote' ) ? 1 : 0
);
$merge = ( $this->app->_session['more_info']['convertGroups']["map_group_{$group['usergroupid']}"] != 'none' ) ? $this->app->_session['more_info']['convertGroups']["map_group_{$group['usergroupid']}"] : NULL;
@@ -933,7 +961,7 @@ class _Vbulletin5 extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -1026,7 +1054,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$warn_lastwarn = $this->db->select( 'actiondateline', 'infraction', array( "infracteduserid=?", $user['userid'] ), "actiondateline DESC", 1 )->first();
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Birthday */
if ( $user['birthday'] )
@@ -1071,7 +1099,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$temp_ban = -1;
}
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
/* Figure Out Password - they differ between 5.0 and 5.1 */
if ( !isset( $user['password'] ) AND ( isset( $user['token'] ) AND isset( $user['scheme'] ) ) )
@@ -1090,7 +1118,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'member_id' => $user['userid'],
'member_group_id' => $user['usergroupid'],
'mgroup_others' => $user['membergroupids'],
'name' => html_entity_decode( $user['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'name' => html_entity_decode( $user['username'], ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
'email' => $user['email'],
'joined' => $user['joindate'],
'ip_address' => $user['ipaddress'],
@@ -1131,7 +1159,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$info['members_bitoptions']['validating'] = TRUE;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$activation = NULL;
}
@@ -1150,7 +1178,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$profileFields[ str_replace( 'field', '', $key ) ] = $value;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$profileFields = array();
}
@@ -1170,7 +1198,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -1187,7 +1215,7 @@ class _Vbulletin5 extends \IPS\convert\Software
if( $pseudo == 'user_title' )
{
$profileFields[ $pseudo ] = \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'];
$profileFields[ $pseudo ] = in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'];
}
else
{
@@ -1209,7 +1237,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
if( $firstTable === FALSE )
{
throw new \UnderflowException();
throw new UnderflowException();
}
foreach( $this->db->select( 'filedata, filename', $firstTable, array( "userid=?", $user['userid'] ) )->first() AS $key => $value )
@@ -1225,13 +1253,13 @@ class _Vbulletin5 extends \IPS\convert\Software
}
$filepath = NULL;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
try
{
if( $secondTable === FALSE )
{
throw new \UnderflowException();
throw new UnderflowException();
}
foreach( $this->db->select( 'filedata, filename', $secondTable, array( "userid=?", $user['userid'] ) )->first() AS $key => $value )
@@ -1247,7 +1275,7 @@ class _Vbulletin5 extends \IPS\convert\Software
}
$filepath = NULL;
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
list( $filedata, $filename, $filepath ) = array( NULL, NULL, NULL );
}
@@ -1271,7 +1299,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
if( $firstTable === FALSE )
{
throw new \UnderflowException();
throw new UnderflowException();
}
$ext = $this->db->select( 'filename', $firstTable, array( "userid=?", $user['userid'] ) )->first();
@@ -1285,14 +1313,14 @@ class _Vbulletin5 extends \IPS\convert\Software
else
{
/* Throw an exception so we can try the other */
throw new \UnderflowException;
throw new UnderflowException;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
if( $secondTable === FALSE )
{
throw new \UnderflowException();
throw new UnderflowException();
}
$ext = $this->db->select( 'filename', $secondTable, array( "userid=?", $user['userid'] ) )->first();
@@ -1306,11 +1334,11 @@ class _Vbulletin5 extends \IPS\convert\Software
else
{
/* Throw an exception so we can try the other */
throw new \UnderflowException;
throw new UnderflowException;
}
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
list( $filedata, $filename, $filepath ) = array( NULL, NULL, NULL );
}
@@ -1321,7 +1349,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/* If we are validating, store our validation row entry */
if( $activation !== NULL AND $memberId )
{
\IPS\Db::i()->replace( 'core_validating', array(
Db::i()->replace( 'core_validating', array(
'vid' => md5( $activation['activationid'] . $activation['useractivationid'] ),
'member_id' => $memberId,
'entry_date' => $activation['dateline'],
@@ -1352,7 +1380,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$node = $this->db->select( 'userid, description', 'node', array( "nodeid=?", $warn['nodeid'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
@@ -1402,12 +1430,12 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$setting = $this->db->select( 'value', 'setting', array( "varname=?", $theirs ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $setting ), array( "conf_key=?", $ours ) );
Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $setting ), array( "conf_key=?", $ours ) );
}
}
@@ -1428,7 +1456,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'ignore_ignore_id' => $ignore['relationid'],
);
foreach( \IPS\core\Ignore::types() AS $type )
foreach( Ignore::types() AS $type )
{
$info['ignore_' . $type] = 1;
}
@@ -1456,7 +1484,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$replies = $this->db->select( 'COUNT(*)', 'node', array( "parentid=?", $pm['nodeid'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$replies = 1;
}
@@ -1466,7 +1494,7 @@ class _Vbulletin5 extends \IPS\convert\Software
foreach( $this->db->select( 'userid', 'node', array( "parentid=?", $pm['nodeid'] ) ) AS $userid )
{
$to[$userid] = $userid;
if ( \is_null( $toId ) AND $userid != $pm['userid'] )
if ( is_null( $toId ) AND $userid != $pm['userid'] )
{
$toId = $userid;
}
@@ -1479,7 +1507,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'mt_starter_id' => $pm['userid'],
'mt_start_time' => $pm['publishdate'],
'mt_last_post_time' => $pm['lastcontent'],
'mt_to_count' => \count( $to ),
'mt_to_count' => count( $to ),
'mt_to_member_id' => $toId,
'mt_replies' => $replies,
'mt_first_msg_id' => $pm['nodeid'],
@@ -1537,7 +1565,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$pm['rawtext'] = $this->db->select( 'message', 'pmtext', array( "pmtextid=?", $pm['oldid'] ) )->first();
}
catch( \UnderflowException $e ) { }
catch( UnderflowException $e ) { }
}
$libraryClass->convertPrivateMessageReply( array(
@@ -1567,7 +1595,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$mainNodeId = $this->db->select( 'nodeid', 'channel', array( "guid=?", static::$guids['clubs'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
/* shrug */
throw new \IPS\convert\Software\Exception;
@@ -1600,7 +1628,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$channel = $this->db->select( '*', 'channel', array( "nodeid=?", $row['nodeid'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$channel = array( 'filedataid' => 0 );
}
@@ -1612,7 +1640,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$filedata = $this->db->select( '*', 'filedata', array( "filedataid=?", $channel['filedataid'] ) )->first();
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
}
$info = array(
@@ -1677,7 +1705,7 @@ class _Vbulletin5 extends \IPS\convert\Software
* @brief Silly Bit Options for Groups. Typically we would leave out app specific options (such as Forums here) however we need them for some general permissions, like uploading.
* @note This is public simply because I do not want to do this ever again if it ever changes.
*/
public static $bitOptions = array(
public static array $bitOptions = array(
'forumpermissions' => array(
'canview' => 1,
'canviewothers' => 2,
@@ -1885,7 +1913,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$this->settingsCache[$key] = $setting['defaultvalue'];
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
/* If we failed to find it, we probably will fail again on later attempts */
$this->settingsCache[$key] = NULL;
@@ -1946,7 +1974,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$clubs[ $row['nodeid'] ] = $row;
}
}
catch( \UnderflowException $e ) { /* Just in case something goofy happened and there is no main channel */ }
catch( UnderflowException $e ) { /* Just in case something goofy happened and there is no main channel */ }
return $clubs;
}
@@ -1982,26 +2010,26 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( preg_match( '#/member/([0-9]+)\-#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( preg_match( '#/member/([0-9]+)\-#i', $url->data[ Url::COMPONENT_PATH ], $matches ) )
{
try
{
$data = (string) $this->app->getLink( $matches[1], array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
@@ -2013,16 +2041,16 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
/* If no algorithm is defined, use the legacy handler */
if ( \strpos( $member->misc, 'blowfish' ) === FALSE and \strpos( $member->misc, 'legacy' ) === FALSE )
if ( strpos( $member->misc, 'blowfish' ) === FALSE and strpos( $member->misc, 'legacy' ) === FALSE )
{
return \IPS\convert\Software\Core\Vbulletin::login( $member, $password );
return Vbulletin::login( $member, $password );
}
/* Which do we need to use? */
@@ -2034,7 +2062,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/* vBulletin uses PasswordHash, however they md5 once the password prior to checking */
$md5_once_password = md5( $password );
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( $algo[ 1 ], FALSE );
$ph = new PasswordHash( $algo[ 1 ], FALSE );
return $ph->CheckPassword( $md5_once_password, $member->conv_password );
break;
@@ -2043,7 +2071,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$token = explode( ' ', $member->conv_password );
$member->conv_password = $token[0];
$member->conv_password_extra = $token[1];
return \IPS\convert\Software\Core\Vbulletin::login( $member, $password );
return Vbulletin::login( $member, $password );
break;
/** vBulletin 5.6.2 was able to also use the Argon2id password hashing scheme. */
case 'argon2id':
@@ -12,16 +12,36 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use IPS\Content\Search\Index;
use IPS\convert\Exception;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Task;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use const ENT_HTML5;
use const ENT_QUOTES;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Woltlab Suite Core Converter
*/
class _Woltlab extends \IPS\convert\Software
class Woltlab extends Software
{
/**
* @brief The WCF table prefix can change depending on the number of installs
@@ -100,7 +120,7 @@ class _Woltlab extends \IPS\convert\Software
* @param array|NULL $where WHERE clause to only count specific rows, or NULL to count all.
* @param bool $recache Skip cache and pull directly (updating cache)
* @return integer
* @throws \IPS\convert\Exception
* @throws Exception
*/
public function countRows( $table, $where=NULL, $recache=FALSE )
{
@@ -114,7 +134,7 @@ class _Woltlab extends \IPS\convert\Software
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
@@ -164,16 +184,16 @@ class _Woltlab extends \IPS\convert\Software
case 'convertProfileFieldGroups':
$return['convertProfileFieldGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_groups' ), 'IPS\core\ProfileFields\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_groups' ), 'IPS\core\ProfileFields\Group' ) AS $group )
{
$options[ $group->_id ] = $group->_title;
}
foreach( $this->db->select( '*', 'wcf' . static::$installId . '_user_option_category', array( 'parentCategoryName=?', 'profile' ) ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$group['categoryID']}"] = $this->getLanguage( $group['categoryName'], 'wcf.user.option.category' );
\IPS\Member::loggedIn()->language()->words["map_pfgroup_{$group['categoryID']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfgroup_desc' );
Member::loggedIn()->language()->words["map_pfgroup_{$group['categoryID']}"] = $this->getLanguage( $group['categoryName'], 'wcf.user.option.category' );
Member::loggedIn()->language()->words["map_pfgroup_{$group['categoryID']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfgroup_desc' );
$return['convertProfileFieldGroups']["map_pfgroup_{$group['categoryID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -189,8 +209,8 @@ class _Woltlab extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[ $field->_id ] = $field->_title;
}
@@ -198,8 +218,8 @@ class _Woltlab extends \IPS\convert\Software
$where = array( "categoryName LIKE 'profile.%' OR " . $this->db->in( 'categoryName', iterator_to_array( $this->db->select( 'categoryName', static::canConvert()['convertProfileFieldGroups']['table'], static::canConvert()['convertProfileFieldGroups']['where'] ) ) ) );
foreach( $this->db->select( '*', 'wcf' . static::$installId . '_user_option', $where ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['optionID']}"] = $this->getLanguage( $field['optionName'], 'wcf.user.option' );
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['optionID']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['optionID']}"] = $this->getLanguage( $field['optionName'], 'wcf.user.option' );
Member::loggedIn()->language()->words["map_pfield_{$field['optionID']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['optionID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -212,7 +232,7 @@ class _Woltlab extends \IPS\convert\Software
break;
case 'convertEmoticons':
\IPS\Member::loggedIn()->language()->words['emoticon_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'Woltlab' ) ) );
Member::loggedIn()->language()->words['emoticon_path'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'Woltlab' ) ) );
$return['convertEmoticons'] = array(
'emoticon_path' => array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
@@ -220,7 +240,7 @@ class _Woltlab extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
),
'keep_existing_emoticons' => array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -236,16 +256,16 @@ class _Woltlab extends \IPS\convert\Software
$return['convertGroups'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[ $group->g_id ] = $group->name;
}
foreach( $this->db->select( '*', 'wcf' . static::$installId . '_user_group' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupID']}"] = $this->getLanguage( $group['groupName'] );
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupID']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['groupID']}"] = $this->getLanguage( $group['groupName'] );
Member::loggedIn()->language()->words["map_group_{$group['groupID']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['groupID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -261,39 +281,39 @@ class _Woltlab extends \IPS\convert\Software
$return['convertMembers'] = array();
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['photo_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'convert_woltlab_avatar' );
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'convert_woltlab_avatar_desc' );
Member::loggedIn()->language()->words['photo_location'] = Member::loggedIn()->language()->addToStack( 'convert_woltlab_avatar' );
Member::loggedIn()->language()->words['photo_location_desc'] = Member::loggedIn()->language()->addToStack( 'convert_woltlab_avatar_desc' );
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
\IPS\Member::loggedIn()->language()->words['coverphoto_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'convert_woltlab_cover' );
\IPS\Member::loggedIn()->language()->words['coverphoto_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'convert_woltlab_cover_desc' );
Member::loggedIn()->language()->words['coverphoto_location'] = Member::loggedIn()->language()->addToStack( 'convert_woltlab_cover' );
Member::loggedIn()->language()->words['coverphoto_location_desc'] = Member::loggedIn()->language()->addToStack( 'convert_woltlab_cover_desc' );
$return['convertMembers']['coverphoto_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
foreach( [ 'usertitle' ] AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, [ 'sprintf' => $field ] );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, [ 'sprintf' => $field ] );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = [
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => [
'options' => [
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
],
'userSuppliedInput' => 'create_field'
],
@@ -314,38 +334,40 @@ class _Woltlab extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_announcements', 'extension' => 'core_Announcement' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_announcements', 'extension' => 'core_Announcement' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Message' ), 3, array( 'class' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Message' ), 3, array( 'class' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" );
}
/**
* Fix Post Data
* Pre-process content for the Invision Community text parser
*
* @param string $post Post
* @return string Fixed Post
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
return $post;
}
@@ -390,7 +412,7 @@ class _Woltlab extends \IPS\convert\Software
foreach ( $this->fetch( 'wcf' . static::$installId . '_user_option', 'optionID', $where ) AS $field )
{
/* Birthday is special */
if( \in_array( $field['optionName'], ['birthday', 'birthdayShowYear'] ) )
if( in_array( $field['optionName'], ['birthday', 'birthdayShowYear'] ) )
{
$libraryClass->setLastKeyValue( $field['optionID'] );
continue;
@@ -529,7 +551,7 @@ class _Woltlab extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -583,7 +605,7 @@ class _Woltlab extends \IPS\convert\Software
$info = array(
'member_id' => $row['userID'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'name' => html_entity_decode( $row['username'], ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'password' => $row['password'],
'member_group_id' => $row['userOnlineGroupID'],
@@ -612,7 +634,7 @@ class _Woltlab extends \IPS\convert\Software
$filename = $row['avatarID'] . '-' . $avatar['fileHash'] . '.' . $avatar['avatarExtension'];
$filepath = rtrim( $this->app->_session['more_info']['convertMembers']['photo_location'], '/' ) . '/' . mb_substr( $avatar['fileHash'], 0, 2 );
}
catch( \UnderflowException $e ) {}
catch( UnderflowException $e ) {}
}
/* Cover Photo */
@@ -645,7 +667,7 @@ class _Woltlab extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( [
'pf_id' => $pseudo,
@@ -663,7 +685,7 @@ class _Woltlab extends \IPS\convert\Software
if( $pseudo == 'usertitle' )
{
$profileFields[ $pseudo ] = \in_array( $row['userTitle'], $userTitles ) OR empty( $row['userTitle'] ) ? NULL : $row['userTitle'];
$profileFields[ $pseudo ] = in_array( $row['userTitle'], $userTitles ) OR empty( $row['userTitle'] ) ? NULL : $row['userTitle'];
}
else
{
@@ -798,7 +820,7 @@ class _Woltlab extends \IPS\convert\Software
* @param string $itemName WCF Language Name
* @param string $itemPrefix WCF Prefix for end-user input
* @return string Translated string, or original if no customisation exists
* @throws \UnderflowException
* @throws UnderflowException
*/
protected function getLanguage( string $itemName, string $itemPrefix=NULL ): string
{
@@ -811,13 +833,13 @@ class _Woltlab extends \IPS\convert\Software
{
return $this->db->select( 'languageItemValue', 'wcf' . static::$installId . '_language_item', array( "languageID=? AND languageItem=?", static::$_defaultLanguage, $itemName ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
try
{
return $this->db->select( 'languageItemValue', 'wcf' . static::$installId . '_language_item', array( "languageID=? AND languageItem=?", static::$_defaultLanguage, $itemPrefix . '.' . $itemName ) )->first();
}
catch( \UnderflowException $e ) { }
catch( UnderflowException $e ) { }
return $itemName;
}
@@ -826,7 +848,7 @@ class _Woltlab extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -838,7 +860,7 @@ class _Woltlab extends \IPS\convert\Software
$salt = mb_substr( $member->conv_password, 0, 29 );
$test = crypt( crypt( $password, $salt ), $salt );
return \IPS\Login::compareHashes( $member->conv_password, $test );
return Login::compareHashes( $member->conv_password, $test );
}
return FALSE;
@@ -12,16 +12,31 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Login;
use IPS\Member;
use IPS\Settings;
use IPS\Task;
use OutOfRangeException;
use PasswordHash;
use function defined;
use function strlen;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Wordpress Core Converter
*/
class _Wordpress extends \IPS\convert\Software
class Wordpress extends Software
{
/**
* Software Name
@@ -93,12 +108,14 @@ class _Wordpress extends \IPS\convert\Software
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
return nl2br( $post );
}
@@ -120,23 +137,23 @@ class _Wordpress extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'display_name',
'field_required' => TRUE,
'field_extra' => array( 'options' => array( 'username' => \IPS\Member::loggedIn()->language()->addToStack( 'user_name' ), 'display_name' => \IPS\Member::loggedIn()->language()->addToStack( 'display_name' ) ) ),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack( 'username_hint' ),
'field_extra' => array( 'options' => array( 'username' => Member::loggedIn()->language()->addToStack( 'user_name' ), 'display_name' => Member::loggedIn()->language()->addToStack( 'display_name' ) ) ),
'field_hint' => Member::loggedIn()->language()->addToStack( 'username_hint' ),
);
/* Pseudo Fieds */
foreach( array( 'first_name', 'last_name', 'user_url' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( str_replace( '_', ' ', $field ) ) ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( str_replace( '_', ' ', $field ) ) ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -165,10 +182,10 @@ class _Wordpress extends \IPS\convert\Software
/* Main Members Table */
$info = array(
'member_id' => $user['ID'],
'ips_group_id' => $user['user_level'] > 9 ? \IPS\Settings::i()->admin_group : \IPS\Settings::i()->member_group,
'ips_group_id' => $user['user_level'] > 9 ? Settings::i()->admin_group : Settings::i()->member_group,
'name' => $this->app->_session['more_info']['convertMembers']['username'] == 'username' ? $user['user_login'] : $user['display_name'],
'email' => $user['user_email'],
'joined' => new \IPS\DateTime( $user['user_registered'] ),
'joined' => new DateTime( $user['user_registered'] ),
'conv_password' => $user['user_pass']
);
@@ -187,7 +204,7 @@ class _Wordpress extends \IPS\convert\Software
{
$fieldId = $this->app->getLink( $field, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $field,
@@ -231,17 +248,17 @@ class _Wordpress extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
return array( "f_search_index_rebuild", "f_clear_caches" );
}
@@ -249,7 +266,7 @@ class _Wordpress extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -258,16 +275,16 @@ class _Wordpress extends \IPS\convert\Software
$success = FALSE;
// If the hash is still md5...
if ( \strlen( $member->conv_password ) <= 32 )
if ( strlen( $member->conv_password ) <= 32 )
{
$success = ( \IPS\Login::compareHashes( $member->conv_password, md5( $password ) ) ) ? TRUE : FALSE;
$success = ( Login::compareHashes( $member->conv_password, md5( $password ) ) ) ? TRUE : FALSE;
}
// New pass hash check
else
{
// Init the pass class
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( 8, TRUE );
$ph = new PasswordHash( 8, TRUE );
// Check it
$success = $ph->CheckPassword( $password, $member->conv_password ) ? TRUE : FALSE;
@@ -12,16 +12,42 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DomainException;
use Exception;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\Software;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\DateTime;
use IPS\Db;
use IPS\Http\Url;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Task;
use OutOfRangeException;
use UnderflowException;
use function count;
use function defined;
use function in_array;
use function preg_replace;
use function str_replace;
use function stristr;
use function strtolower;
use function strtotime;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* wpForo Core Converter
*/
class _Wpforo extends \IPS\convert\Software
class Wpforo extends Software
{
/**
* Software Name
@@ -110,22 +136,24 @@ class _Wpforo extends \IPS\convert\Software
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
/* Mentions */
$matches = [];
preg_match_all( '/@([^@ ]+)/i', $post, $matches );
if( \count( $matches ) )
if( count( $matches ) )
{
foreach( $matches[0] as $key => $tag )
{
$member = \IPS\Member::load( $matches[1][ $key ], 'name' );
$member = Member::load( $matches[1][ $key ], 'name' );
if( !$member->member_id )
{
continue;
@@ -160,24 +188,24 @@ class _Wpforo extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => [],
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_wp_typical_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_wp_typical_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
]
];
/* Pseudo Fields */
foreach( $this->_profileFields AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, [ 'sprintf' => ucwords( str_replace( '_', ' ', $field ) ) ] );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, [ 'sprintf' => ucwords( str_replace( '_', ' ', $field ) ) ] );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = [
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => [
'options' => [
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
],
'userSuppliedInput' => 'create_field'
],
@@ -190,16 +218,16 @@ class _Wpforo extends \IPS\convert\Software
$return['convertGroups'] = [];
$options = [];
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[ $group->g_id ] = $group->name;
}
foreach( $this->db->select( '*', 'wpforo_usergroups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupid']}"] = $group['name'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['groupid']}"] = $group['name'];
Member::loggedIn()->language()->words["map_group_{$group['groupid']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['groupid']}"] = [
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -232,7 +260,7 @@ class _Wpforo extends \IPS\convert\Software
foreach( $this->fetch( 'wpforo_pms', 'pmid' ) AS $row )
{
if( !\stristr( $row['message'], '[attach]' ) AND !\stristr( $row['message'], 'wpforo-attached-file' ) )
if( !stristr( $row['message'], '[attach]' ) AND !stristr( $row['message'], 'wpforo-attached-file' ) )
{
$libraryClass->setLastKeyValue( $row['pmid'] );
continue;
@@ -252,7 +280,7 @@ class _Wpforo extends \IPS\convert\Software
$matches = [];
preg_match_all( '/\[attach\](\d+)\[\/attach\]/i', $row['message'], $matches );
if( \count( $matches ) )
if( count( $matches ) )
{
foreach( $matches[1] as $key => $id )
{
@@ -263,7 +291,7 @@ class _Wpforo extends \IPS\convert\Software
$info = [
'attach_id' => $row['pmid'],
'attach_file' => $sourceAttachment['filename'],
'attach_date' => \strtotime( $row['date'] ),
'attach_date' => strtotime( $row['date'] ),
'attach_member_id' => $sourceAttachment['userid'],
'attach_filesize' => $sourceAttachment['size'],
];
@@ -282,14 +310,14 @@ class _Wpforo extends \IPS\convert\Software
if( !isset( $this->_postContent[ $messagePostId ] ) )
{
$this->_postContent[ $messagePostId ] = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', [ "msg_id=?", $messagePostId ] )->first();
$this->_postContent[ $messagePostId ] = Db::i()->select( 'msg_post', 'core_message_posts', [ "msg_id=?", $messagePostId ] )->first();
}
$this->_postContent[ $messagePostId ] = str_replace( $matches[0][ $key ], '[attachment=' . $attachId . ':name]', $this->_postContent[ $messagePostId ] );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
catch( UnderflowException $e ) {}
catch( OutOfRangeException $e ) {}
}
}
@@ -297,7 +325,7 @@ class _Wpforo extends \IPS\convert\Software
$matches = [];
preg_match_all( '/\<div id\="wpfa\-[\d]+"(.+?)?>\<a class\="wpforo\-default\-attachment" href\="(.+?)"(.+?)?>\<i class\="(.+?)">\<\/i>(.+?)<\/a><\/div>/i', $row['message'], $matches );
if( \count( $matches ) )
if( count( $matches ) )
{
foreach( $matches[2] as $key => $url )
{
@@ -306,7 +334,7 @@ class _Wpforo extends \IPS\convert\Software
$info = [
'attach_id' => $row['pmid'],
'attach_file' => $filename,
'attach_date' => \strtotime( $row['date'] ),
'attach_date' => strtotime( $row['date'] ),
'attach_member_id' => $row['fromuserid'],
];
@@ -324,14 +352,14 @@ class _Wpforo extends \IPS\convert\Software
if( !isset( $this->_postContent[ $messagePostId ] ) )
{
$this->_postContent[ $messagePostId ] = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', [ "msg_id=?", $messagePostId ] )->first();
$this->_postContent[ $messagePostId ] = Db::i()->select( 'msg_post', 'core_message_posts', [ "msg_id=?", $messagePostId ] )->first();
}
$this->_postContent[ $messagePostId ] = str_replace( $matches[0][ $key ], '[attachment=' . $attachId . ':name]', $this->_postContent[ $messagePostId ] );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
catch( UnderflowException $e ) {}
catch( OutOfRangeException $e ) {}
}
}
@@ -341,7 +369,7 @@ class _Wpforo extends \IPS\convert\Software
/* Do the updates */
foreach( $this->_postContent as $id => $content )
{
\IPS\Db::i()->update( 'core_message_posts', [ 'msg_post' => $content ], [ "msg_id=?", $id ] );
Db::i()->update( 'core_message_posts', [ 'msg_post' => $content ], [ "msg_id=?", $id ] );
}
}
@@ -380,7 +408,7 @@ class _Wpforo extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -408,7 +436,7 @@ class _Wpforo extends \IPS\convert\Software
{
try
{
$joined = ( new \IPS\DateTime( $user['user_registered'] ) )->getTimestamp();
$joined = ( new DateTime( $user['user_registered'] ) )->getTimestamp();
}
catch( \ValueError $e )
{
@@ -438,7 +466,7 @@ class _Wpforo extends \IPS\convert\Software
{
$fieldId = $this->app->getLink( $field, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( [
'pf_id' => $field,
@@ -470,7 +498,7 @@ class _Wpforo extends \IPS\convert\Software
/* Take a guess */
foreach( [ 'jpg', 'png', 'jpeg', 'gif' ] as $ext )
{
$filename = \strtolower( \str_replace( '_', '', \preg_replace( '/\s|\.(?=.*\.)/i', '-',$user['display_name'] ) ) ) . '_' . $user['ID'] . '.' . $ext;
$filename = strtolower( str_replace( '_', '', preg_replace( '/\s|\.(?=.*\.)/i', '-',$user['display_name'] ) ) ) . '_' . $user['ID'] . '.' . $ext;
if( file_exists( $avatarPath . '/' . $filename ) )
{
@@ -506,7 +534,7 @@ class _Wpforo extends \IPS\convert\Software
{
$firstMessage = $this->db->select( '*', 'wpforo_pms', [ 'folderid=?', $row['folderid'] ], 'pmid ASC', 1 )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$libraryClass->setLastKeyValue( $row['folderid'] );
continue;
@@ -516,7 +544,7 @@ class _Wpforo extends \IPS\convert\Software
{
$lastMessage = $this->db->select( '*', 'wpforo_pms', [ 'folderid=?', $row['folderid'] ], 'pmid DESC', 1 )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$libraryClass->setLastKeyValue( $row['folderid'] );
continue;
@@ -524,18 +552,18 @@ class _Wpforo extends \IPS\convert\Software
$topic = [
'mt_id' => $row['folderid'],
'mt_date' => \strtotime( $firstMessage['date'] ),
'mt_date' => strtotime( $firstMessage['date'] ),
'mt_title' => $row['name'] ?: 'Message',
'mt_starter_id' => $firstMessage['fromuserid'],
'mt_start_time' => \strtotime( $firstMessage['date'] ),
'mt_last_post_time' => \strtotime( $lastMessage['date'] ),
'mt_start_time' => strtotime( $firstMessage['date'] ),
'mt_last_post_time' => strtotime( $lastMessage['date'] ),
'mt_to_count' => $row['user_count'],
];
$maps = [];
$maps[ $firstMessage['fromuserid'] ] = [
'map_user_id' => $firstMessage['fromuserid'],
'map_read_time' => \strtotime( $firstMessage['date'] ),
'map_read_time' => strtotime( $firstMessage['date'] ),
'map_is_starter' => true
];
@@ -550,7 +578,7 @@ class _Wpforo extends \IPS\convert\Software
$maps[ $participant ] = [
'map_user_id' => $participant,
'map_read_time' => \in_array( $participant, $readData ) ? time() : 0,
'map_read_time' => in_array( $participant, $readData ) ? time() : 0,
'map_user_active' => 1,
];
}
@@ -575,7 +603,7 @@ class _Wpforo extends \IPS\convert\Software
$libraryClass->convertPrivateMessageReply( [
'msg_id' => $row['pmid'],
'msg_topic_id' => $row['folderid'],
'msg_date' => \strtotime( $row['date'] ),
'msg_date' => strtotime( $row['date'] ),
'msg_post' => $row['message'],
'msg_author_id' => $row['fromuserid'],
] );
@@ -592,27 +620,27 @@ class _Wpforo extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
/* Non-Content Rebuilds */
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ], 2, [ 'app', 'link', 'extension' ] );
\IPS\Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ], 2, [ 'app', 'link', 'extension' ] );
\IPS\Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ], 2, [ 'app', 'link', 'extension' ] );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ], 2, [ 'app', 'link', 'extension' ] );
Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ], 2, [ 'app', 'link', 'extension' ] );
Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ], 2, [ 'app', 'link', 'extension' ] );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', [ 'app' => $this->app->app_id ], 4, [ 'app' ] );
\IPS\Task::queue( 'core', 'RebuildItemCounts', [ 'class' => 'IPS\core\Messenger\Message' ], 3, [ 'class' ] );
Task::queue( 'core', 'RecountMemberContent', [ 'app' => $this->app->app_id ], 4, [ 'app' ] );
Task::queue( 'core', 'RebuildItemCounts', [ 'class' => 'IPS\core\Messenger\Message' ], 3, [ 'class' ] );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', [ 'app' => $this->app->app_id ], 2, [ 'app' ] );
Task::queue( 'convert', 'RebuildConversationFirstIds', [ 'app' => $this->app->app_id ], 2, [ 'app' ] );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', [ 'app' => $this->app->app_id ], 1, [ 'app' ] );
Task::queue( 'core', 'RebuildAttachmentThumbnails', [ 'app' => $this->app->app_id ], 1, [ 'app' ] );
return [ "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" ];
}
@@ -620,30 +648,30 @@ class _Wpforo extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
return \IPS\convert\Software\Core\Wordpress::login( $member, $password );
return Wordpress::login( $member, $password );
}
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
$wpForoSlug = \defined('WPFORO_SLUG') ? WPFOROSLUG : 'community';
$url = Request::i()->url();
$wpForoSlug = defined('WPFORO_SLUG') ? WPFOROSLUG : 'community';
$matches = [];
if( preg_match( '#/' . $wpForoSlug . '/profile/([a-z0-9-]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( preg_match( '#/' . $wpForoSlug . '/profile/([a-z0-9-]+)#i', $url->data[ Url::COMPONENT_PATH ], $matches ) )
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
@@ -651,9 +679,9 @@ class _Wpforo extends \IPS\convert\Software
try
{
$data = (string) $this->app->getLink( $matches[1], [ 'member_furl' ] );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e ) { }
catch( Exception $e ) { }
}
return NULL;
@@ -12,18 +12,53 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateTimeZone;
use DomainException;
use Exception;
use InvalidArgumentException;
use IPS\Application\Module;
use IPS\Content\Search\Index;
use IPS\convert\App;
use IPS\convert\Software;
use IPS\convert\Tools\Xenforo;
use IPS\Data\Cache;
use IPS\Data\Store;
use IPS\Db;
use IPS\Http\Url;
use IPS\Http\Url\Friendly;
use IPS\Login;
use IPS\Member;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Request;
use IPS\Settings;
use IPS\Task;
use OutOfRangeException;
use PasswordHash;
use UnderflowException;
use function count;
use function defined;
use function extension_loaded;
use function in_array;
use function is_null;
use function preg_match;
use function stristr;
use function unserialize;
use const ENT_HTML5;
use const ENT_QUOTES;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo Core Converter
*/
class _Xenforo extends \IPS\convert\Software
class Xenforo extends Software
{
use \IPS\convert\Tools\Xenforo;
use Xenforo;
/**
* @brief The similarities between XF1 and XF2 are close enough that we can use the same converter
@@ -46,12 +81,12 @@ class _Xenforo extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App $app The application to reference for database and other information.
* @param App $app The application to reference for database and other information.
* @param bool $needDB Establish a DB connection
* @return void
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public function __construct( \IPS\convert\App $app, $needDB=TRUE )
public function __construct( App $app, $needDB=TRUE )
{
$return = parent::__construct( $app, $needDB );
@@ -62,7 +97,7 @@ class _Xenforo extends \IPS\convert\Software
/* Is this XF1 or XF2 */
if ( static::$isLegacy === NULL )
{
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( \IPS\Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
if ( $version < 2000010 )
{
@@ -80,7 +115,7 @@ class _Xenforo extends \IPS\convert\Software
}
}
}
catch( \Exception $e ) {} # If we can't query, we won't be able to do anything anyway
catch( Exception $e ) {} # If we can't query, we won't be able to do anything anyway
}
return $return;
@@ -151,21 +186,13 @@ class _Xenforo extends \IPS\convert\Software
'where' => NULL
),
'convertMemberHistory' => array(
'table' => ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) ? 'xf_change_log' : 'xf_user_change_log',
'table' => ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? 'xf_change_log' : 'xf_user_change_log',
'where' => NULL
),
'convertWarnReasons' => array(
'table' => 'xf_warning_definition',
'where' => NULL
),
'convertStatuses' => array(
'table' => 'xf_profile_post',
'where' => NULL
),
'convertStatusReplies' => array(
'table' => 'xf_profile_post_comment',
'where' => NULL
),
'convertIgnoredUsers' => array(
'table' => 'xf_user_ignored',
'where' => NULL
@@ -215,9 +242,9 @@ class _Xenforo extends \IPS\convert\Software
{
return $this->db->select( 'count(log_id)', 'xf_user_change_log', array( $this->db->in( 'field', array_keys( static::$changeLogTypes ) ) ) )->first();
}
catch( \Exception $e )
catch( Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
/* XF2 */
@@ -226,9 +253,9 @@ class _Xenforo extends \IPS\convert\Software
{
return $this->db->select( 'count(log_id)', 'xf_change_log', array( 'content_type=? AND ' . $this->db->in( 'field', array_keys( static::$changeLogTypes ) ), 'user' ) )->first();
}
catch( \Exception $e )
catch( Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
throw new \IPS\convert\Exception( sprintf( Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
@@ -301,15 +328,15 @@ class _Xenforo extends \IPS\convert\Software
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_xf_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_xf_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
)
);
break;
case 'convertEmoticons':
/* XenForo stores emoticons either as a remotely linked image, or relative to the installation path, so we need to change the verbiage here a bit. */
\IPS\Member::loggedIn()->language()->words['emoticon_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'XenForo' ) ) );
Member::loggedIn()->language()->words['emoticon_path'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'XenForo' ) ) );
$return['convertEmoticons'] = array(
'emoticon_path' => array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
@@ -317,7 +344,7 @@ class _Xenforo extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
),
'keep_existing_emoticons' => array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
@@ -333,16 +360,16 @@ class _Xenforo extends \IPS\convert\Software
$return['convertProfileFields'] = array();
$options = array();
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
$options['none'] = Member::loggedIn()->language()->addToStack( 'none' );
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_pfields_data' ), 'IPS\core\ProfileFields\Field' ) AS $field )
{
$options[$field->_id] = $field->_title;
}
foreach( $this->db->select( '*', 'xf_user_field' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}"] = $this->getPhrase( "user_field_{$field['field_id']}", "user_field_title.{$field['field_id']}" );
\IPS\Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}"] = $this->getPhrase( "user_field_{$field['field_id']}", "user_field_title.{$field['field_id']}" );
Member::loggedIn()->language()->words["map_pfield_{$field['field_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_pfield_desc' );
$return['convertProfileFields']["map_pfield_{$field['field_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -359,15 +386,15 @@ class _Xenforo extends \IPS\convert\Software
$options = array();
$options['none'] = 'None';
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
foreach( new ActiveRecordIterator( Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[$group->g_id] = $group->name;
}
foreach( $this->db->select( '*', 'xf_user_group' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['user_group_id']}"] = $group['title'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['user_group_id']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
Member::loggedIn()->language()->words["map_group_{$group['user_group_id']}"] = $group['title'];
Member::loggedIn()->language()->words["map_group_{$group['user_group_id']}_desc"] = Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['user_group_id']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
@@ -382,28 +409,28 @@ class _Xenforo extends \IPS\convert\Software
$return['convertMembers'] = array();
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
Member::loggedIn()->language()->words['photo_location_desc'] = Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
$return['convertMembers']['photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_xf_avatar_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_hint' => Member::loggedIn()->language()->addToStack('convert_xf_avatar_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
foreach( static::$pseudoFields[ static::$isLegacy ? 1 : 2 ] AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
Member::loggedIn()->language()->words["field_{$field}"] = Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
Member::loggedIn()->language()->words["field_{$field}_desc"] = Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
'no_convert' => Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => Member::loggedIn()->language()->addToStack( 'create_field' ),
),
'userSuppliedInput' => 'create_field'
),
@@ -441,14 +468,14 @@ class _Xenforo extends \IPS\convert\Software
);
/* XenForo stores emoticons either as a remotely linked image, or relative to the installation path, so we need to change the verbiage here a bit. */
\IPS\Member::loggedIn()->language()->words['reaction_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'XenForo' ) ) );
Member::loggedIn()->language()->words['reaction_path'] = Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'XenForo' ) ) );
$return['convertReactions']['reaction_path'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new DomainException( 'path_invalid' ); } },
);
}
break;
@@ -483,7 +510,7 @@ class _Xenforo extends \IPS\convert\Software
{
$setting = $this->db->select( 'option_value', 'xf_option', array( "option_id=?", $theirs ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
@@ -495,7 +522,7 @@ class _Xenforo extends \IPS\convert\Software
$title = $setting;
}
$settings[ $theirs ] = array( 'title' => $title, 'value' => $setting, 'our_key' => $ours, 'our_title' => \IPS\Member::loggedIn()->language()->addToStack( $ours ) );
$settings[ $theirs ] = array( 'title' => $title, 'value' => $setting, 'our_key' => $ours, 'our_title' => Member::loggedIn()->language()->addToStack( $ours ) );
}
return $settings;
@@ -510,46 +537,48 @@ class _Xenforo extends \IPS\convert\Software
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
Store::i()->clearAll();
Cache::i()->clearAll();
\IPS\Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildProfilePhotos', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ), 2, array( 'app', 'link', 'extension' ) );
Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ), 2, array( 'app', 'link', 'extension' ) );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Message' ), 3, array( 'class' ) );
Task::queue( 'core', 'RecountMemberContent', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\core\Messenger\Message' ), 3, array( 'class' ) );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
Task::queue( 'convert', 'RebuildConversationFirstIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
Task::queue( 'core', 'RebuildAttachmentThumbnails', array( 'app' => $this->app->app_id ), 1, array( 'app' ) );
/* Rebuild Leaderboard */
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
\IPS\Db::i()->delete('core_reputation_leaderboard_history');
Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
Db::i()->delete('core_reputation_leaderboard_history');
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" );
}
/**
* Fix post data
* Pre-process content for the Invision Community text parser
*
* @param string $post Raw post data
* @return string Parsed post data
* @param string The post
* @param string|null Content Classname passed by post-conversion rebuild
* @param int|null Content ID passed by post-conversion rebuild
* @return string The converted post
*/
public static function fixPostData( $post )
public static function fixPostData( string $post, ?string $className=null, ?int $contentId=null ): string
{
// run everything through htmlspecialchars to prevent XSS
$post = htmlspecialchars( $post, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE );
// find YouTube ID's and replace.
if ( \preg_match( "#\[media=#i", $post ) )
if ( preg_match( "#\[media=#i", $post ) )
{
$post = preg_replace( '#\[media=youtube\](.+?)\[/media\]#i', 'https://www.youtube.com/watch?v=$1', $post );
$post = preg_replace( '#\[media=facebook\](.+?)\[/media\]#i', 'https://www.facebook.com/video.php?v=$1', $post );
@@ -561,25 +590,25 @@ class _Xenforo extends \IPS\convert\Software
}
// Remove extra attributes
if ( \preg_match( "#\[img #i", $post ) )
if ( preg_match( "#\[img #i", $post ) )
{
$post = preg_replace( '/\[img .*\](.*?)\[\/img\]/i', '[img]$1[/img]', $post );
}
if ( \preg_match( "#\[url unfurl#i", $post ) )
if ( preg_match( "#\[url unfurl#i", $post ) )
{
$post = preg_replace( '/\[url unfurl=.*\](.*?)\[\/url\]/i', '[url]$1[/url]', $post );
}
/* Quick check for the presence of user tags */
if ( \preg_match( "#\[user=#i", $post ) )
if ( preg_match( "#\[user=#i", $post ) )
{
/* Mentions */
preg_match_all( '#\[user=(\d+)\](.+?)\[\/user\]#i', $post, $matches );
if ( \count( $matches ) )
if ( count( $matches ) )
{
/* Make sure we actually have mention data */
if ( isset( $matches[1] ) and \count( $matches[1] ) )
if ( isset( $matches[1] ) and count( $matches[1] ) )
{
$mentions = array();
foreach ( $matches[1] as $k => $v )
@@ -590,7 +619,7 @@ class _Xenforo extends \IPS\convert\Software
}
}
$maps = iterator_to_array( \IPS\Db::i()->select( 'name, member_id', 'core_members', array( \IPS\Db::i()->in( 'name', array_keys( $mentions ) ) ) )->setKeyField( 'name' )->setValueField( 'member_id' ) );
$maps = iterator_to_array( Db::i()->select( 'name, member_id', 'core_members', array( Db::i()->in( 'name', array_keys( $mentions ) ) ) )->setKeyField( 'name' )->setValueField( 'member_id' ) );
foreach ( $mentions as $memberName => $memberId )
{
@@ -607,13 +636,13 @@ class _Xenforo extends \IPS\convert\Software
}
/* Quotes */
if ( \preg_match( "#\[quote=#i", $post ) )
if ( preg_match( "#\[quote=#i", $post ) )
{
$post = preg_replace( '#\[quote=&quot;([a-zA-Z0-9-_ ]+)(,([a-z0-9\s:])+)?(,([a-z0-9\s:])+)?&quot;]#i', "[quote name='$1']", $post );
}
/* Tables */
if ( \stristr( $post, '[table]' ) )
if ( stristr( $post, '[table]' ) )
{
$search = array( '[table]', '[/table]', '[th]', '[/th]', '[tr]', '[/tr]', '[td]', '[/td]' );
$replace = array( '<table>', '</table>', '<th>', '</th>', '<tr>', '</tr>', '<td>', '</td>' );
@@ -622,7 +651,7 @@ class _Xenforo extends \IPS\convert\Software
}
/* Spoilers */
if ( \preg_match( "#\[spoiler=#i", $post ) )
if ( preg_match( "#\[spoiler=#i", $post ) )
{
$post = preg_replace( '/\[spoiler=(&quot;|&#039;|)([a-zA-Z0-9-_ ]+)(&quot;|&#039;|)\]/i', "[spoiler]", $post );
}
@@ -649,9 +678,9 @@ class _Xenforo extends \IPS\convert\Software
{
$conversation = $this->db->select( 'conversation_id', 'xf_conversation_message', array( "message_id=?", $row['content_id'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$this->app->log( 'xf_attach_missing_conversation_link', __METHOD__, \IPS\convert\App::LOG_WARNING, $row['attachment_id'] );
$this->app->log( 'xf_attach_missing_conversation_link', __METHOD__, App::LOG_WARNING, $row['attachment_id'] );
$libraryClass->setLastKeyValue( $row['attachment_id'] );
continue;
}
@@ -692,18 +721,18 @@ class _Xenforo extends \IPS\convert\Software
{
$messagePostId = $this->app->getLink( $row['content_id'], 'core_message_posts' );
$post = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', array( "msg_id=?", $messagePostId ) )->first();
$post = Db::i()->select( 'msg_post', 'core_message_posts', array( "msg_id=?", $messagePostId ) )->first();
if ( preg_match( "/\[ATTACH([^\]]+?)?\]" . $row['attachment_id'] . "(\._xfImport)?\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $row['attachment_id'] . "(\._xfImport)?\[\/ATTACH\]/i", '[attachment=' . $attachId . ':name]', $post );
}
\IPS\Db::i()->update( 'core_message_posts', array( 'msg_post' => $post ), array( "msg_id=?", $messagePostId ) );
Db::i()->update( 'core_message_posts', array( 'msg_post' => $post ), array( "msg_id=?", $messagePostId ) );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
catch( UnderflowException $e ) {}
catch( OutOfRangeException $e ) {}
$libraryClass->setLastKeyValue( $row['attachment_id'] );
}
@@ -736,9 +765,9 @@ class _Xenforo extends \IPS\convert\Software
{
$images = $this->_imageFromSprite( rtrim( $this->app->_session['more_info']['convertEmoticons']['emoticon_path'], '/' ) . '/' . $row['image_url'], static::unpack( $row['sprite_params'] ) );
}
catch( \InvalidArgumentException | \OutOfRangeException $e )
catch( InvalidArgumentException | OutOfRangeException $e )
{
$this->app->log( 'xf_emo_sprite_convert_invalid_image', __METHOD__, \IPS\convert\App::LOG_WARNING, $row['smilie_id'] );
$this->app->log( 'xf_emo_sprite_convert_invalid_image', __METHOD__, App::LOG_WARNING, $row['smilie_id'] );
$libraryClass->setLastKeyValue( $row['smilie_id'] );
continue;
}
@@ -792,13 +821,13 @@ class _Xenforo extends \IPS\convert\Software
$category = $this->db->select( '*', 'xf_smilie_category', array( "smilie_category_id=?", $row['smilie_category_id'] ) )->first();
$title = $this->getPhrase( "smilie_category_{$category['smilie_category_id']}_title", "smilie_category_title.{$category['smilie_category_id']}" );
if ( \is_null( $title ) )
if ( is_null( $title ) )
{
/* Bubble Up */
throw new \UnderflowException;
throw new UnderflowException;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$category = array(
'display_order' => 1,
@@ -844,11 +873,11 @@ class _Xenforo extends \IPS\convert\Software
$info['pf_type'] = $this->_fieldMap( $row['field_type'] );
$info['pf_name'] = $this->getPhrase( "user_field_{$row['field_id']}", "user_field_title.{$row['field_id']}" );
$info['pf_desc'] = $this->getPhrase( "user_field_{$row['field_id']}_desc", "user_field_desc.{$row['field_id']}" );
$info['pf_content'] = ( !\in_array( $row['field_type'], array( 'textbox', 'textarea' ) ) ) ? ( static::$isLegacy ? \unserialize( $row['field_choices'] ) : json_decode( $row['field_choices'], TRUE ) ) : NULL;
$info['pf_content'] = ( !in_array( $row['field_type'], array( 'textbox', 'textarea' ) ) ) ? ( static::$isLegacy ? unserialize( $row['field_choices'] ) : json_decode( $row['field_choices'], TRUE ) ) : NULL;
$info['pf_not_null'] = $row['required'];
$info['pf_member_hide'] = ( $row['viewable_profile'] > 0 ) ? 'all' : 'hide';
$info['pf_max_input'] = $row['max_length'];
$info['pf_member_edit'] = ( \in_array( $row['user_editable'], array( 'yes', 'once' ) ) ) ? 0 : 1;
$info['pf_member_edit'] = ( in_array( $row['user_editable'], array( 'yes', 'once' ) ) ) ? 0 : 1;
$info['pf_position'] = $row['display_order'];
$info['pf_show_on_reg'] = ( $row['show_registration'] > 0 ) ? 1 : 0;
$info['pf_input_format'] = static::$isLegacy ?
@@ -967,7 +996,7 @@ class _Xenforo extends \IPS\convert\Software
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
if( count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
@@ -1040,7 +1069,7 @@ class _Xenforo extends \IPS\convert\Software
$userField = 'user_id';
$table = 'xf_user_change_log';
if( ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) )
if( ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) )
{
$table = 'xf_change_log';
$userField = 'content_id';
@@ -1108,13 +1137,13 @@ class _Xenforo extends \IPS\convert\Software
{
if( !isset( $passwords[ $row['user_id'] ] ) )
{
throw new \UnderflowException;
throw new UnderflowException;
}
$data = \unserialize( $passwords[ $row['user_id'] ] );
$data = unserialize( $passwords[ $row['user_id'] ] );
if( !isset( $data['hash'] ) )
{
throw new \UnderflowException;
throw new UnderflowException;
}
$hash = $data['hash'];
@@ -1132,7 +1161,7 @@ class _Xenforo extends \IPS\convert\Software
$salt = $data['username'];
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
/* Ut oh... do something random. */
$hash = md5( mt_rand() );
@@ -1144,13 +1173,13 @@ class _Xenforo extends \IPS\convert\Software
{
$ip = $this->db->select( 'ip', 'xf_ip', array( "user_id=? AND action=?", $row['user_id'], 'register' ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
try
{
$ip = $this->db->select( 'ip', 'xf_ip', array( "user_id=? AND action=?", $row['user_id'], 'login' ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$ip = '127.0.0.1';
}
@@ -1161,7 +1190,7 @@ class _Xenforo extends \IPS\convert\Software
{
$last_warn = $this->db->select( 'warning_date', 'xf_warning', array( "user_id=?", $row['user_id'] ), "warning_id DESC" )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$last_warn = 0;
}
@@ -1171,7 +1200,7 @@ class _Xenforo extends \IPS\convert\Software
{
$pm_count = $this->db->select( 'COUNT(*)', 'xf_conversation_master', array( "user_id=?", $row['user_id'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$pm_count = 0;
}
@@ -1192,7 +1221,7 @@ class _Xenforo extends \IPS\convert\Software
{
if( !isset( $bans[ $row['user_id'] ] ) )
{
throw new \UnderflowException;
throw new UnderflowException;
}
$ban = $bans[ $row['user_id'] ];
@@ -1202,7 +1231,7 @@ class _Xenforo extends \IPS\convert\Software
$ban = -1;
}
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$ban = 0;
}
@@ -1210,9 +1239,9 @@ class _Xenforo extends \IPS\convert\Software
/* Timezone Verification */
try
{
$timezone = new \DateTimeZone( $row['timezone'] ); # if invalid, this will throw an exception
$timezone = new DateTimeZone( $row['timezone'] ); # if invalid, this will throw an exception
}
catch( \Exception $e )
catch( Exception $e )
{
$timezone = 'UTC';
}
@@ -1220,7 +1249,7 @@ class _Xenforo extends \IPS\convert\Software
/* Main Member Information */
$info = array(
'member_id' => $row['user_id'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'name' => html_entity_decode( $row['username'], ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'conv_password' => $hash,
'conv_password_extra' => $salt,
@@ -1283,7 +1312,7 @@ class _Xenforo extends \IPS\convert\Software
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException $e )
{
$libraryClass->convertProfileField( array(
'pf_id' => $pseudo,
@@ -1346,79 +1375,6 @@ class _Xenforo extends \IPS\convert\Software
}
}
/**
* Convert statuses
*
* @return void
*/
public function convertStatuses()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'profile_post_id' );
foreach( $this->fetch( 'xf_profile_post', 'profile_post_id' ) AS $row )
{
/* We have to query for the IP Address */
try
{
$ip = $this->db->select( 'ip', 'xf_ip', array( "ip_id=?", $row['ip_id'] ) )->first();
}
catch( \UnderflowException $e )
{
$ip = '127.0.0.1';
}
/* Approval State */
switch( $row['message_state'] )
{
case 'visible':
$approved = 1;
break;
case 'moderated':
$approved = 0;
break;
case 'deleted':
$approved = -1;
break;
}
$info = array(
'status_id' => $row['profile_post_id'],
'status_member_id' => $row['profile_user_id'],
'status_date' => $row['post_date'],
'status_content' => $row['message'],
'status_replies' => $row['comment_count'],
'status_author_id' => $row['user_id'],
'status_author_ip' => $ip,
'status_approved' => $approved,
);
$id = $libraryClass->convertStatus( $info );
if( static::$useJson AND $id )
{
foreach( $this->db->select( '*', 'xf_reaction_content', array( "content_type=? AND content_id=?", 'profile_post', $row['profile_post_id'] ) ) as $reaction )
{
$libraryClass->convertReputation( array(
'id' => $reaction['reaction_content_id'],
'app' => 'core',
'type' => 'status_id',
'type_id' => $row['profile_post_id'],
'member_id' => $reaction['reaction_user_id'],
'member_received' => $row['profile_user_id'],
'reaction' => $reaction['reaction_id'],
'rep_date' => $reaction['reaction_date']
) );
}
}
$libraryClass->setLastKeyValue( $row['profile_post_id'] );
}
}
/**
* Convert one or more settings
*
@@ -1438,87 +1394,15 @@ class _Xenforo extends \IPS\convert\Software
{
$setting = $this->db->select( 'option_value', 'xf_option', array( "option_id=?", $theirs ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
continue;
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $setting ), array( "conf_key=?", $ours ) );
Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $setting ), array( "conf_key=?", $ours ) );
}
}
/**
* Convert status replies
*
* @return void
*/
public function convertStatusReplies()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'profile_post_comment_id' );
foreach( $this->fetch( 'xf_profile_post_comment', 'profile_post_comment_id' ) AS $row )
{
/* We need to query for the IP Address */
try
{
$ip = $this->db->select( 'ip', 'xf_ip', array( "ip_id=?", $row['ip_id'] ) )->first();
}
catch( \UnderflowException $e )
{
$ip = '127.0.0.1';
}
/* Approval State */
switch( $row['message_state'] )
{
case 'visible':
$approved = 1;
break;
case 'moderated':
$approved = 0;
break;
case 'deleted':
$approved = -1;
break;
}
$info = array(
'reply_id' => $row['profile_post_comment_id'],
'reply_status_id' => $row['profile_post_id'],
'reply_member_id' => $row['user_id'],
'reply_date' => $row['comment_date'],
'reply_content' => $row['message'],
'reply_approved' => $approved,
'reply_ip_address' => $ip,
);
$id = $libraryClass->convertStatusReply( $info );
if( static::$useJson AND $id )
{
foreach( $this->db->select( '*', 'xf_reaction_content', array( "content_type=? AND content_id=?", 'profile_post_comment', $row['profile_post_comment_id'] ) ) as $reaction )
{
$libraryClass->convertReputation( array(
'id' => $reaction['reaction_content_id'],
'app' => 'core',
'type' => 'status_reply_id',
'type_id' => $row['profile_post_comment_id'],
'member_id' => $reaction['reaction_user_id'],
'member_received' => $row['user_id'],
'reaction' => $reaction['reaction_id'],
'rep_date' => $reaction['reaction_date']
) );
}
}
$libraryClass->setLastKeyValue( $row['profile_post_comment_id'] );
}
}
/**
* Convert ignored users
*
@@ -1565,9 +1449,9 @@ class _Xenforo extends \IPS\convert\Software
{
$images = $this->_imageFromSprite( rtrim( $this->app->_session['more_info']['convertReactions']['reaction_path'], '/' ) . '/' . $row['image_url'], static::unpack( $row['sprite_params'] ) );
}
catch( \InvalidArgumentException | \OutOfRangeException $e )
catch( InvalidArgumentException | OutOfRangeException $e )
{
$this->app->log( 'xf_sprite_convert_invalid_image', __METHOD__, \IPS\convert\App::LOG_WARNING, $row['reaction_id'] );
$this->app->log( 'xf_sprite_convert_invalid_image', __METHOD__, App::LOG_WARNING, $row['reaction_id'] );
$libraryClass->setLastKeyValue( $row['reaction_id'] );
continue;
}
@@ -1580,7 +1464,7 @@ class _Xenforo extends \IPS\convert\Software
}
/* We always save the sprites to PNG */
$filename = \IPS\Http\Url\Friendly::seoTitle( $title ) . ".png";
$filename = Friendly::seoTitle( $title ) . ".png";
}
elseif ( mb_substr( $row['image_url'], 0, 4 ) == 'http' )
{
@@ -1652,7 +1536,7 @@ class _Xenforo extends \IPS\convert\Software
{
$recip = $this->db->select( '*', 'xf_conversation_recipient', array( "conversation_id=? AND user_id=?", $row['conversation_id'], $map['owner_user_id'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$recip = array(
'conversation_id' => $row['conversation_id'],
@@ -1696,7 +1580,7 @@ class _Xenforo extends \IPS\convert\Software
{
$ips[$row['ip_id']] = $this->db->select( 'ip', 'xf_ip', array( "ip_id=?", $row['ip_id'] ) )->first();
}
catch( \UnderflowException $e )
catch( UnderflowException $e )
{
$ips[$row['ip_id']] = '127.0.0.1';
}
@@ -1718,17 +1602,17 @@ class _Xenforo extends \IPS\convert\Software
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
* @return NULL|Url
* @todo The proxy.php redirection is not validating the hash that XenForo set previously, derived from the globalSalt and the option imageLinkProxyKey. We need to either validate the hash (there is an open proxy concern), or we need to convert all calls to proxy.php in posted content to prevent broken images in posts.
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
$url = Request::i()->url();
if( preg_match( '#/members/(.+?)\.([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( preg_match( '#/members/(.+?)\.([0-9]+)#i', $url->data[ Url::COMPONENT_PATH ], $matches ) )
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
if( !Member::loggedIn()->canAccessModule( Module::get( 'core', 'members' ) ) )
{
return NULL;
}
@@ -1736,19 +1620,19 @@ class _Xenforo extends \IPS\convert\Software
try
{
$data = (string) $this->app->getLink( (int) $matches[2], array( 'members', 'core_members' ) );
return \IPS\Member::load( $data )->url();
return Member::load( $data )->url();
}
catch( \Exception $e )
catch( Exception $e )
{
return NULL;
}
}
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'proxy.php' ) !== FALSE AND isset( \IPS\Request::i()->image ) )
elseif( mb_strpos( $url->data[ Url::COMPONENT_PATH ], 'proxy.php' ) !== FALSE AND isset( Request::i()->image ) )
{
return \IPS\Http\Url::internal( "app=core&module=system&controller=redirect", 'front' )->setQueryString( array(
'url' => \IPS\Request::i()->image,
return Url::internal( "app=core&module=system&controller=redirect", 'front' )->setQueryString( array(
'url' => Request::i()->image,
'resource' => TRUE,
'key' => hash_hmac( "sha256", (string) \IPS\Request::i()->image, \IPS\Settings::i()->site_secret_key . 'r' )
'key' => hash_hmac( "sha256", (string) Request::i()->image, Settings::i()->site_secret_key . 'r' )
) );
}
@@ -1758,7 +1642,7 @@ class _Xenforo extends \IPS\convert\Software
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param Member $member The member
* @param string $password Password from form
* @return bool
*/
@@ -1768,7 +1652,7 @@ class _Xenforo extends \IPS\convert\Software
// XF 1.2
require_once \IPS\ROOT_PATH . "/applications/convert/sources/Login/PasswordHash.php";
$ph = new \PasswordHash( 10, TRUE );
$ph = new PasswordHash( 10, TRUE );
if ( $ph->CheckPassword( $password, $member->conv_password ) )
{
@@ -1776,7 +1660,7 @@ class _Xenforo extends \IPS\convert\Software
}
// XF 1.1
if ( \extension_loaded( 'hash' ) )
if ( extension_loaded( 'hash' ) )
{
$hashedPassword = hash( 'sha256', hash( 'sha256', $password ) . $member->misc );
}
@@ -1785,12 +1669,12 @@ class _Xenforo extends \IPS\convert\Software
$hashedPassword = sha1( sha1( $password ) . $member->misc );
}
if ( \IPS\Login::compareHashes( $member->conv_password, $hashedPassword ) )
if ( Login::compareHashes( $member->conv_password, $hashedPassword ) )
{
return TRUE;
}
// If they converted vB > XF > IPB then passwords may be in vB format still - try that.
return \IPS\convert\Software\Core\Vbulletin::login( $member, $password );
return Vbulletin::login( $member, $password );
}
}