Version 4.5.0

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

No files matched your search

@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Blog;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Blog Converter
*/
class _Vbulletin extends \IPS\convert\Software
{
/**
@@ -33,8 +36,8 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -61,7 +64,7 @@ class _Vbulletin extends \IPS\convert\Software
/* If this is vB4, what is the content type ID for posts? */
if ( static::$entryContentType === NULL AND ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) AND $needDB )
if ( static::$entryContentType === NULL AND ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) AND $needDB )
{
static::$entryContentType = $this->db->select( 'contenttypeid', 'contenttype', array( "class=?", 'BlogEntry' ) )->first();
}
@@ -102,7 +105,7 @@ class _Vbulletin extends \IPS\convert\Software
{
$attachmentTable = 'blog_attachment';
$attachmentWhere = NULL;
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
$attachmentTable = 'attachment';
$attachmentWhere = array( "contenttypeid=?", static::$entryContentType );
@@ -113,6 +116,10 @@ class _Vbulletin extends \IPS\convert\Software
'table' => 'blog_user',
'where' => NULL,
),
'convertBlogEntryCategories' => array(
'table' => 'blog_category',
'where' => NULL
),
'convertBlogEntries' => array(
'table' => 'blog',
'where' => NULL
@@ -146,7 +153,7 @@ class _Vbulletin extends \IPS\convert\Software
break;
case 'blog_text':
return parent::countRows( 'blog_text' ) - parent::countRows( 'blog' );
return $this->db->select( 'COUNT(*)', 'blog_text', array( 'blogtextid NOT IN (' . (string) $this->db->select( 'firstblogtextid', 'blog' ) . ')' ) )->first();
break;
}
}
@@ -179,6 +186,7 @@ class _Vbulletin extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\blog\Entry\Category', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'blog_entries', 'class' => 'IPS\blog\Entry' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'blog_comments', 'class' => 'IPS\blog\Entry\Comment' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'blog_entries', 'class' => 'IPS\blog\Entry' ), 3, array( 'app', 'link', 'class' ) );
@@ -189,7 +197,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @param string $post Post
* @return string Fixed Posts
*/
public static function fixPostData( $post )
@@ -226,8 +234,8 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -314,7 +322,7 @@ class _Vbulletin extends \IPS\convert\Software
'follow_rel_id_type' => 'blog_blogs',
'follow_member_id' => $follow['userid'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_added' => $follow['dateline'] ?: time(),
'follow_notify_do' => 1,
'follow_notify_meta' => '',
'follow_notify_freq' => $frequency,
@@ -327,6 +335,30 @@ class _Vbulletin extends \IPS\convert\Software
}
}
/**
* Convert blog entries
*
* @return void
*/
public function convertBlogEntryCategories()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'blogcategoryid' );
foreach( $this->fetch( 'blog_category', 'blogcategoryid' ) AS $category )
{
$libraryClass->convertBlogEntryCategory( array(
'entry_category_id' => $category['blogcategoryid'],
'entry_category_blog_id' => $category['userid'],
'entry_category_name' => $category['title'],
'entry_category_position' => $category['displayorder'],
'entry_category_count' => $category['entrycount']
) );
$libraryClass->setLastKeyValue( $category['blogcategoryid'] );
}
}
/**
* Convert blog entries
*
@@ -364,14 +396,18 @@ class _Vbulletin extends \IPS\convert\Software
$status = 'draft';
}
$edittime = $editname = NULL;
$edittime = $editname = $editReason = NULL;
try
{
$editlog = $this->db->select( 'dateline, username', 'blog_editlog', array( "blogtextid=?", $entry['firstblogtextid'] ), "dateline DESC" )->first();
$editlog = $this->db->select( 'dateline, username, reason', 'blog_editlog', array( "blogtextid=?", $entry['firstblogtextid'] ), "dateline DESC" )->first();
$edittime = $editlog['dateline'];
$editname = $editlog['username'];
$editReason = $editlog['reason'];
}
catch( \UnderflowException $e ) {}
$categories = explode( ',', $entry['categories'] );
$mainCategory = array_pop( $categories );
$info = array(
'entry_id' => $entry['blogid'],
@@ -389,7 +425,10 @@ class _Vbulletin extends \IPS\convert\Software
'entry_last_comment_mid' => $lastcommenterid,
'entry_edit_time' => $edittime,
'entry_edit_name' => $editname,
'entry_ip_address' => $text['ipaddress']
'entry_edit_reason' => $editReason,
'entry_append_edit' => $edittime ? TRUE : FALSE,
'entry_ip_address' => long2ip( (int) $text['ipaddress'] ),
'entry_category_id' => $mainCategory
);
$libraryClass->convertBlogEntry( $info );
@@ -407,8 +446,7 @@ class _Vbulletin extends \IPS\convert\Software
) );
}
$categories = explode( ',', $entry['categories'] );
if ( count( $categories ) )
if ( \count( $categories ) )
{
foreach( $categories as $category )
{
@@ -448,7 +486,7 @@ class _Vbulletin extends \IPS\convert\Software
'follow_rel_id_type' => 'blog_entries',
'follow_member_id' => $follow['userid'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_added' => $follow['dateline'] ?: time(),
'follow_notify_do' => 1,
'follow_notify_meta' => '',
'follow_notify_freq' => $frequency,
@@ -471,20 +509,10 @@ class _Vbulletin extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'blogtextid' );
foreach( $this->fetch( 'blog_text', 'blogtextid' ) AS $comment )
// Exclude text of blog entry from iterator
foreach( $this->fetch( 'blog_text', 'blogtextid', array( 'blogtextid NOT IN (' . (string) $this->db->select( 'firstblogtextid', 'blog' ) . ')' ) ) AS $comment )
{
/* Is this actually the entry? */
try
{
$entry = $this->db->select( '*', 'blog', array( "firstblogtextid=?", $comment['blogtextid'] ) )->first();
/* If that didn't throw an exception, then yes this is - skip */
$libraryClass->setLastKeyValue( $comment['blogtextid'] );
continue;
}
catch( \UnderflowException $e ) {} # nope, carry on
$edittime = $editname = NULL;
try
{
@@ -516,7 +544,7 @@ class _Vbulletin extends \IPS\convert\Software
'comment_text' => $comment['pagetext'],
'comment_member_id' => $comment['userid'],
'comment_member_name' => $comment['username'],
'comment_ip_address' => $comment['ipaddress'],
'comment_ip_address' => long2ip( (int) $comment['ipaddress'] ),
'comment_date' => $comment['dateline'],
'comment_edit_time' => $edittime,
'comment_edit_member_name' => $editname,
@@ -542,7 +570,7 @@ class _Vbulletin extends \IPS\convert\Software
$where = NULL;
$column = NULL;
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
$where = array( "contenttypeid=?", static::$entryContentType );
$column = 'contentid';
@@ -556,7 +584,7 @@ class _Vbulletin extends \IPS\convert\Software
foreach( $this->fetch( $table, 'attachmentid', $where ) as $attachment )
{
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
try
{
@@ -601,7 +629,15 @@ class _Vbulletin extends \IPS\convert\Software
{
$data = NULL;
$path = implode( '/', preg_split( '//', $filedata['userid'], -1, PREG_SPLIT_NO_EMPTY ) );
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['file_location'], '/' ) . '/' . $path . '/' . $attachment['filedataid'] . '.attach';
if ( static::$isLegacy === FALSE )
{
$file = $attachment['filedataid'];
}
else
{
$file = $attachment['attachmentid'];
}
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['file_location'], '/' ) . '/' . $path . '/' . $file . '.attach';
}
$attach_id = $libraryClass->convertAttachment( $info, $map, $path, $data );
@@ -615,9 +651,9 @@ class _Vbulletin extends \IPS\convert\Software
$post = \IPS\Db::i()->select( 'entry_content', 'blog_entries', array( "entry_id=?", $entry_id ) )->first();
if ( preg_match( "/\[ATTACH(.+?)?\]".$attachment['attachmentid']."\[\/ATTACH\]/i", $post ) )
if ( preg_match( "/\[ATTACH([^\]]+?)?\]".$attachment['attachmentid']."\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH=(.+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
\IPS\Db::i()->update( 'blog_entries', array( 'entry_content' => $post ), array( "entry_id=?", $entry_id ) );
}
}
@@ -628,4 +664,31 @@ class _Vbulletin extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $attachment['attachmentid'] );
}
}
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
if( preg_match( '#/entries/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
try
{
$data = (string) $this->app->getLink( (int) $matches[1], array( 'blog_entries' ) );
$item = \IPS\blog\Entry::load( $data );
if( $item->can( 'view' ) )
{
return $item->url();
}
}
catch( \Exception $e ) {}
}
return NULL;
}
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Calendar;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MyBB Calendar Converter
*/
class _Mybb extends \IPS\convert\Software
{
/**
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Calendar;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Calendar Converter
*/
class _Vbulletin extends \IPS\convert\Software
{
/**
@@ -98,7 +101,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @param string $post Post
* @return string Fixed Posts
*/
public static function fixPostData( $post )
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Joomla Pages Converter
*/
class _Joomla extends \IPS\convert\Software
{
/**
@@ -125,11 +128,18 @@ class _Joomla extends \IPS\convert\Software
*/
public function finish()
{
$database = $this->app->getLink( 1, 'cms_databases' );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 2, array( 'app', 'link', 'class' ) );
try
{
$database = $this->app->getLink( 1, 'cms_databases' );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 2, array( 'app', 'link', 'class' ) );
return array( "f_recount_cms_categories" );
return array( "f_recount_cms_categories" );
}
catch( \OutOfRangeException $e )
{
return array();
}
}
/**
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Pages Converter
*/
class _Vbulletin extends \IPS\convert\Software
{
/**
@@ -79,7 +82,7 @@ class _Vbulletin extends \IPS\convert\Software
}
/* If this is vB4, what is the content type ID for the cms? */
if ( static::$_articleContentType === NULL AND ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) AND $needDB )
if ( static::$_articleContentType === NULL AND ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) AND $needDB )
{
try
{
@@ -149,12 +152,19 @@ class _Vbulletin extends \IPS\convert\Software
switch( $table )
{
case 'cms_widget':
$blocksWeCanConvert = array();
foreach( $this->db->select( 'widgettypeid', 'cms_widgettype', array( \IPS\Db::i()->in( 'class', array( 'Rss', 'Static' ) ) ) ) AS $typeid )
try
{
$blocksWeCanConvert[] = $typeid;
$blocksWeCanConvert = array();
foreach( $this->db->select( 'widgettypeid', 'cms_widgettype', array( \IPS\Db::i()->in( 'class', array( 'Rss', 'Static' ) ) ) ) AS $typeid )
{
$blocksWeCanConvert[] = $typeid;
}
return $this->db->select( 'COUNT(*)', 'cms_widget', array( $this->db->in( 'widgettypeid', $blocksWeCanConvert ) ) )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
return $this->db->select( 'COUNT(*)', 'cms_widget', array( $this->db->in( 'widgettypeid', $blocksWeCanConvert ) ) )->first();
break;
case 'page':
@@ -190,23 +200,13 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @param string $post Post
* @return string Fixed Posts
*/
public static function fixPostData( $post )
{
return \IPS\convert\Software\Core\Vbulletin::fixPostData( $post );
}
/**
* Returns a block of text, or a language string, that explains what the admin must do to start this conversion
*
* @return string
*/
public static function getPreConversionInformation()
{
return 'convert_vb_preconvert';
}
/**
* Get Setting Value - useful for global settings that need to be translated to group or member settings
@@ -268,7 +268,12 @@ class _Vbulletin extends \IPS\convert\Software
foreach( $this->fetch( 'cms_widget', 'widgetid', array( \IPS\Db::i()->in( 'widgettypeid', $blocksWeCanConvert ) ) ) AS $block )
{
$config = $this->db->select( 'name, value', 'cms_widgetconfig', array( "widgetid=?", $block['widgetid'] ) )->setKeyField( 'name' )->setValueField( 'value' )->first();
$config = array();
foreach( $this->db->select( 'name, value', 'cms_widgetconfig', array( "widgetid=?", $block['widgetid'] ) ) as $_c )
{
$config[ $_c['name'] ] = $_c['value'];
}
$info = array(
'block_id' => $block['widgetid'],
@@ -288,7 +293,7 @@ class _Vbulletin extends \IPS\convert\Software
'type' => 'plugin',
'plugin_app' => 'cms',
) : NULL,
'block_content' => ( $block['widgettypeid'] != $rssTypeId ) ? $config['statichtml'] : NULL,
'block_content' => ( $block['widgettypeid'] != $rssTypeId AND !empty( $config['statichtml'] ) ) ? $config['statichtml'] : NULL,
);
$libraryClass->convertCmsBlock( $info );
@@ -407,9 +412,10 @@ class _Vbulletin extends \IPS\convert\Software
public function convertCmsDatabaseRecords()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'contentid' );
$cmsCategories = iterator_to_array( $this->db->select( 'category, categoryid', 'cms_category' )->setKeyField( 'categoryid' )->setValueField( 'category' ) );
foreach( $this->fetch( 'cms_article', 'contentid' ) AS $row )
{
try
@@ -423,29 +429,27 @@ class _Vbulletin extends \IPS\convert\Software
continue;
}
try
{
$category = $this->db->select( 'categoryid', 'cms_nodecategory', array( "nodeid=?", $node['nodeid'] ) )->first();
}
catch( \UnderflowException $e )
$categories = iterator_to_array( $this->db->select( 'categoryid', 'cms_nodecategory', array( "nodeid=?", $node['nodeid'] ) ) );
if( !\count( $categories ) )
{
/* Create one */
try
{
$this->app->getLink( '__orphan__', 'cms_database_categories' );
$category = '__orphan__';
$categories = array( '__orphan__' );
}
catch( \OutOfRangeException $e )
catch ( \OutOfRangeException $e )
{
$libraryClass->convertCmsDatabaseCategory( array(
'category_id' => '__orphan__',
'category_database_id' => 1,
'category_name' => "vBulletin Articles",
'category_fields' => array( 'article_title', 'article_content' )
'category_id' => '__orphan__',
'category_database_id' => 1,
'category_name' => "vBulletin Articles",
'category_fields' => array( 'article_title', 'article_content' )
) );
$category = '__orphan__';
$categories = array( '__orphan__' );
}
}
@@ -454,8 +458,11 @@ class _Vbulletin extends \IPS\convert\Software
{
$keywords[] = trim( $word );
}
// First category
$category = array_shift( $categories );
$libraryClass->convertCmsDatabaseRecord( array(
$id = $libraryClass->convertCmsDatabaseRecord( array(
'record_id' => $row['contentid'],
'record_database_id' => 1,
'member_id' => $node['userid'],
@@ -478,6 +485,76 @@ class _Vbulletin extends \IPS\convert\Software
1 => $nodeinfo['title'],
2 => $row['pagetext']
) );
/* Need to know database for tag conversion */
try
{
$database = $this->app->getLink( 1, 'cms_databases' );
}
catch( \OutOfRangeException $e )
{
/* Cannot find it, we can't convert tags */
$libraryClass->setLastKeyValue( $row['contentid'] );
}
/* Convert extra unassigned categories as tags */
$convertedTags = array();
if( \count( $categories ) )
{
foreach( $categories as $key )
{
if( isset( $convertedTags[ $cmsCategories[ $key ] ] ) )
{
continue;
}
$libraryClass->convertTag( array(
'tag_meta_app' => 'cms',
'tag_meta_area' => "records{$database}",
'tag_meta_parent_id' => $category,
'tag_meta_id' => $row['contentid'],
'tag_text' => $cmsCategories[ $key ],
'tag_member_id' => $node['userid'],
'tag_prefix' => 0,
'tag_meta_link' => 'cms_custom_database_' . $database,
'tag_meta_parent_link' => 'cms_database_categories',
) );
if( $id )
{
$convertedTags[ $cmsCategories[ $key ] ] = $cmsCategories[ $key ];
}
}
}
/* Convert normal article tags */
$tags = $this->db->select( '*', 'tagcontent', array( "contenttypeid=? AND contentid=?", static::$_articleContentType, $row['contentid'] ) )
->join( 'tag', 'tagcontent.tagid=tag.tagid');
foreach( $tags AS $tag )
{
if( isset( $convertedTags[ $tag['tagtext'] ] ) OR ( $tag['canonicaltagid'] > 0 AND $tag['canonicaltagid'] != $tag['tagid'] ) )
{
continue;
}
$id = $libraryClass->convertTag( array(
'tag_meta_app' => 'cms',
'tag_meta_area' => "records{$database}",
'tag_meta_parent_id' => $category,
'tag_meta_id' => $tag['contentid'],
'tag_text' => $tag['tagtext'],
'tag_member_id' => $tag['userid'],
'tag_prefix' => 0,
'tag_meta_link' => 'cms_custom_database_' . $database,
'tag_meta_parent_link' => 'cms_database_categories',
) );
if( $id )
{
$convertedTags[ $tag['tagtext'] ] = $tag['tagtext'];
}
}
$libraryClass->setLastKeyValue( $row['contentid'] );
}
@@ -494,9 +571,7 @@ class _Vbulletin extends \IPS\convert\Software
{
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\cms\Records'. $database ), 3, array( 'class' ) );
// Uncomment this once Tags are implemented for the cms;
# \IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 3, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 3, array( 'app', 'link', 'class' ) );
try
{
@@ -522,7 +597,7 @@ class _Vbulletin extends \IPS\convert\Software
$where = NULL;
$column = NULL;
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
$where = array( "contenttypeid=?", static::$_articleContentType );
$column = 'contentid';
@@ -533,7 +608,7 @@ class _Vbulletin extends \IPS\convert\Software
{
try
{
$vbRecordId = $this->db->select( 'contentid', 'cms_article', array( "contentid=?", $attachment[ $column ] ) )->first();
$vbRecordId = $this->db->select( 'contentid', 'cms_node', array( "nodeid=? AND contenttypeid=?", $attachment[ $column ], static::$_articleContentType ) )->first();
}
catch( \UnderflowException $e )
{
@@ -545,7 +620,7 @@ class _Vbulletin extends \IPS\convert\Software
continue;
}
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
$filedata = $this->db->select( '*', 'filedata', array( "filedataid=?", $attachment['filedataid'] ) )->first();
}
@@ -555,12 +630,6 @@ class _Vbulletin extends \IPS\convert\Software
$filedata['filedataid'] = $attachment['attachmentid'];
}
$map = array(
'id1' => $vbRecordId,
'id2' => NULL,
'id3' => 1
);
$info = array(
'attach_id' => $attachment['attachmentid'],
'attach_file' => $attachment['filename'],
@@ -584,26 +653,33 @@ class _Vbulletin extends \IPS\convert\Software
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['file_location'], '/' ) . '/' . $path . '/' . $attachment['filedataid'] . '.attach';
}
$attach_id = $libraryClass->convertAttachment( $info, $map, $path, $data );
/* Do some re-jiggery on the post itself to make sure attachment displays */
/* The database is hardcoded to 1 while the conversion, so we have to use 1 here */
$dbId = $this->app->getLink( 1, 'cms_databases' );
$dbName = "cms_custom_database_" . $dbId;
/* Get the database object */
$ipsDb = \IPS\cms\Databases::load( $dbId );
$map = array(
'id1' => $vbRecordId,
'id2' => 2,
'id2_type' => 'cms_database_fields',
'id3' => 1
);
$attach_id = $libraryClass->convertAttachment( $info, $map, $path, $data );
try
{
$recordId = $this->app->getLink( $attachment[ $column ], 'cms_custom_database_' . $dbId );
$recordId = $this->app->getLink( $vbRecordId, 'cms_custom_database_' . $dbId );
/* Get the database object */
$ipsDb = \IPS\cms\Databases::load( $dbId);
$post = \IPS\Db::i()->select( 'field_' . $ipsDb->field_content, $dbName, array( "primary_id_field=?", $recordId ) )->first();
$post = \IPS\Db::i()->select( $ipsDb->field_content, $dbName, array( "primary_id_field=?", $recordId ) )->first();
if ( preg_match( "/\[ATTACH(.+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", $post ) )
if ( preg_match( "/\[ATTACH([^\]]+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH=(.+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
\IPS\Db::i()->update( $dbName, array( 'field_2' => $post ), array( "primary_id_field=?", $recordId ) );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
\IPS\Db::i()->update( $dbName, array( 'field_' . $ipsDb->field_content => $post ), array( "primary_id_field=?", $recordId ) );
}
}
catch( \OutOfRangeException $e ) { }
@@ -641,8 +717,8 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Wordpress Pages Converter
*/
class _Wordpress extends \IPS\convert\Software
{
/**
@@ -178,15 +181,22 @@ class _Wordpress extends \IPS\convert\Software
*/
public function finish()
{
$database = $this->app->getLink( 1, 'cms_databases' );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\cms\Records'. $database ), 3, array( 'class' ) );
try
{
$database = $this->app->getLink( 1, 'cms_databases' );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\cms\Records' . $database ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'cms_records', 'class' => 'IPS\cms\Records' . $database ), 3, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_database_comments', 'class' => 'IPS\cms\Records\Comment' . $database ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'cms_records', 'class' => 'IPS\cms\Records' . $database ), 3, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_database_comments', 'class' => 'IPS\cms\Records\Comment' . $database ), 2, array( 'app', 'link', 'class' ) );
return array( "f_recount_cms_categories", "f_rebuild_cms_tags" );
return array( "f_recount_cms_categories", "f_rebuild_cms_tags" );
}
catch( \OutOfRangeException $e )
{
return array();
}
}
/**
@@ -334,13 +344,14 @@ class _Wordpress extends \IPS\convert\Software
}
/* Record Image */
$image = NULL;
$imagePath = $filename = NULL;
if ( isset( $meta['_thumbnail_id'] ) )
{
try
{
$location = $this->db->select( 'meta_value', 'postmeta', array( "post_id=? AND meta_key=?", $meta['_thumbnail_id'], '_wp_attached_file' ) )->first();
$image = rtrim( $this->app->_session['more_info']['convertCmsDatabaseRecords']['file_location'], '/' ) . '/' . $location;
$imagePath = rtrim( $this->app->_session['more_info']['convertCmsDatabaseRecords']['file_location'], '/' ) . '/' . $location;
$filename = basename( $imagePath );
}
catch( \UnderflowException $e ) {}
}
@@ -353,13 +364,13 @@ class _Wordpress extends \IPS\convert\Software
'record_comments' => $row['comment_count'],
'record_allow_comments' => 1,
'record_saved' => new \IPS\DateTime( $row['post_date'] ),
'record_updated' => new \IPS\DateTime( $row['post_modified'] ),
'record_updated' => $row['post_modified'] !== '0000-00-00 00:00:00' ? new \IPS\DateTime( $row['post_modified'] ) : NULL, # Older WordPress installs may be missing a modified date
'category_id' => $category,
'record_approved' => $approved,
'record_dynamic_furl' => $row['post_name'],
'record_static_furl' => $row['post_name'],
'record_publish_date' => new \IPS\DateTime( $row['post_date'] ),
'record_image' => $image,
'record_image' => $filename,
);
$fields = array(
@@ -367,7 +378,7 @@ class _Wordpress extends \IPS\convert\Software
2 => $row['post_content']
);
$libraryClass->convertCmsDatabaseRecord( $info, $fields );
$libraryClass->convertCmsDatabaseRecord( $info, $fields, $imagePath );
/* Tags */
$tags = array();
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo Pages Converter
*/
class _Xenforo extends \IPS\convert\Software
{
/**
@@ -28,7 +31,7 @@ class _Xenforo extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "XenForo Page Nodes (1.5.x/2.0.x)";
return "XenForo Page Nodes (1.5.x/2.0.x/2.1.x)";
}
/**
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo (resource manager) Pages Converter
*/
class _Xenfororm extends \IPS\convert\Software
{
/**
@@ -38,14 +41,14 @@ class _Xenfororm extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "XenForo Resource Manager Articles (1.5.x/2.0.x)";
return "XenForo Resource Manager Articles (1.5.x/2.0.x/2.1.x)";
}
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -60,7 +63,7 @@ class _Xenfororm extends \IPS\convert\Software
/* Is this XF1 or XF2 */
if ( static::$isLegacy === NULL )
{
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( "addon_id=?", 'XF' ) )->first();
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( \IPS\Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
if ( $version < 2000010 )
{
@@ -310,10 +313,17 @@ class _Xenfororm extends \IPS\convert\Software
*/
public function finish()
{
$database = $this->app->getLink( 1, 'cms_databases' );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 2, array( 'app', 'link', 'class' ) );
try
{
$database = $this->app->getLink( 1, 'cms_databases' );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\cms\Categories' . $database, 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'cms_custom_database_' . $database, 'class' => 'IPS\cms\Records' . $database ), 2, array( 'app', 'link', 'class' ) );
return array( "f_recount_cms_categories", "f_rebuild_cms_tags" );
return array( "f_recount_cms_categories", "f_rebuild_cms_tags" );
}
catch( \OutOfRangeException $e )
{
return array();
}
}
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 14 June 2016
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ExpressionEngine Core Converter
*/
class _Expressionengine extends \IPS\convert\Software
{
/**
@@ -87,23 +90,20 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
if( isset( $rows['convertPrivateMessageReplies'] ) )
{
$rows['convertPmAttachments'] = array(
'step_method' => 'convertPmAttachments',
'step_title' => 'convert_attachments',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments' ),
'source_rows' => array( 'table' => static::canConvert()['convertPmAttachments']['table'], 'where' => static::canConvert()['convertPmAttachments']['where'] ),
'per_cycle' => 100,
'dependencies' => array( 'convertPrivateMessageReplies' ),
'link_type' => 'core_attachments',
);
}
$rows = array();
$rows['convertPmAttachments'] = array(
'step_method' => 'convertPmAttachments',
'step_title' => 'convert_attachments',
'ips_rows' => \IPS\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' ),
'link_type' => 'core_attachments',
);
return $rows;
}
@@ -147,8 +147,8 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -290,7 +290,7 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Finish
*
* @return array
* @return array Messages to display
*/
public function finish()
{
@@ -391,11 +391,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' ? 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;
@@ -437,7 +437,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 )
{
@@ -526,11 +526,10 @@ class _Expressionengine extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 500,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 12 July 2017
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invision Core Converter
*/
class _Invisioncommunity extends \IPS\convert\Software
{
/**
@@ -30,6 +33,14 @@ class _Invisioncommunity extends \IPS\convert\Software
*/
public static $dbNeeded = FALSE;
/**
* 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 )
{
/* Set filename obscuring flag */
@@ -188,6 +199,10 @@ class _Invisioncommunity extends \IPS\convert\Software
'table' => 'core_clubs_memberships',
'where' => NULL
),
'convertClubPages' => array(
'table' => 'core_club_pages',
'where' => NULL
),
'convertAttachments' => array(
'table' => 'core_attachments',
'where' => NULL
@@ -491,7 +506,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 )
{
@@ -592,15 +607,17 @@ 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', '' ) ) )
{
$profilePhotoName = array_pop( explode( '/', $row['pp_main_photo'] ) );
$bits = explode( '/', $row['pp_main_photo'] );
$profilePhotoName = array_pop( $bits );
}
$coverPhotoName = NULL;
if( $row['pp_cover_photo'] )
{
$coverPhotoName = array_pop( explode( '/', $row['pp_cover_photo'] ) );
$coverBits = explode( '/', $row['pp_cover_photo'] );
$coverPhotoName = array_pop( $coverBits );
}
/* Profile Fields */
@@ -620,6 +637,11 @@ class _Invisioncommunity extends \IPS\convert\Software
{
$profileFields = array();
}
/* Rename bit options so we can pass them through the conversion method */
$row['ips_members_bitoptions'] = $row['members_bitoptions'];
$row['ips_members_bitoptions2'] = $row['members_bitoptions2'];
unset( $row['members_bitoptions'], $row['members_bitoptions2'] );
$path = $this->app->_session['more_info']['convertMembers']['upload_path'] . '/profile';
$libraryClass->convertMember( $row, $profileFields, $profilePhotoName, $path, NULL, $coverPhotoName, $path );
@@ -972,7 +994,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$coverFile = $row['cover_photo'] ? $this->app->_session['more_info']['convertClubs']['upload_path'] . '/' . $row['cover_photo'] : NULL;
$libraryClass->convertClub( $row, $iconFile, NULL, $coverFile );
$libraryClass->setLastKeyValue( $row['id'] );
$libraryClass->setLastKeyValue( $row['club_id'] );
}
}
@@ -996,6 +1018,26 @@ class _Invisioncommunity extends \IPS\convert\Software
}
}
/**
* Convert club pages
*
* @return void
*/
public function convertClubPages()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'page_id' );
foreach( $this->fetch( 'core_club_pages', 'page_id' ) AS $row )
{
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_club_pages' );
$libraryClass->convertClubPage( $row );
$libraryClass->setLastKeyValue( $row['page_id'] );
}
}
/**
* Convert attachments
*
@@ -1027,7 +1069,39 @@ class _Invisioncommunity extends \IPS\convert\Software
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_attachments' );
$this->unsetNonStandardColumns( $attachmentMap, 'core_attachments_map' );
/* Attach Types */
switch( $attachmentMap['location_key'] )
{
case 'core_Messaging':
$attachmentMap['id1_type'] = 'core_message_topics';
$attachmentMap['id2_type'] = 'core_message_posts';
$attachmentMap['id2_from_parent'] = FALSE;
break;
case 'core_Signatures':
$attachmentMap['id1_type'] = 'core_members';
break;
case 'core_CustomField':
$attachmentMap['id1_type'] = 'core_members';
$attachmentMap['id2_type'] = 'core_pfields_data';
$attachmentMap['id2_from_parent'] = FALSE;
break;
case 'core_ClubPage':
$attachmentMap['id1_type'] = 'core_club_page';
break;
case 'core_Announcement':
$attachmentMap['id1_type'] = 'core_announcement';
break;
default:
// Can't convert this type
$libraryClass->setLastKeyValue( $row['attach_id'] );
continue 2;
break;
}
/* This applies to all options */
$attachmentMap['id1_from_parent'] = FALSE;
/* Remap rows */
$name = explode( '/', $row['attach_location'] );
$row['attach_container'] = isset( $name[1] ) ? $name[0] : NULL;
@@ -1066,7 +1140,7 @@ class _Invisioncommunity extends \IPS\convert\Software
}
/**
* @brief default language
* @brief Default language
*/
protected $_defaultLanguage = NULL;
@@ -1086,14 +1160,14 @@ class _Invisioncommunity extends \IPS\convert\Software
}
/**
* @brief word storage
* @brief Word storage
*/
protected $_words = [];
/**
* Get word from language system
*
* @param string $key word key
* @param string $key Word key
* @return string
*/
public function getWord( $key )
@@ -1123,17 +1197,17 @@ class _Invisioncommunity extends \IPS\convert\Software
}
/**
* @brief table schema storage
* @brief Table schema storage
*/
protected $_schemas = array();
/**
* Remove non-standard columns from row data
*
* @param array $row database result row
* @param string $table table name
* @param string $app app to check
* @return string
* @param array $row Database result row
* @param string $table Table name
* @param string $app App to check
* @return void
*/
public function unsetNonStandardColumns( &$row, $table, $app=NULL )
{
@@ -1165,14 +1239,20 @@ 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
* @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();
if( preg_match( '#/ips4merge/profile/([0-9]+)-(.+?)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( !\stristr( $url->data[ \IPS\Http\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 ];
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' ) ) )
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Joomla Core Converter
*/
class _Joomla extends \IPS\convert\Software
{
/**
@@ -171,7 +174,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 )
{
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MyBB Core Converter
*/
class _Mybb extends \IPS\convert\Software
{
/**
@@ -88,11 +91,11 @@ class _Mybb extends \IPS\convert\Software
),
'convertPrivateMessages' => array(
'table' => 'privatemessages',
'where' => NULL
'where' => array( 'fromid<>uid' )
),
'convertPrivateMessageReplies' => array(
'table' => 'privatemessages',
'where' => NULL,
'where' => array( 'fromid<>uid' ),
),
'convertProfanityFilters' => array(
'table' => 'badwords',
@@ -293,17 +296,20 @@ class _Mybb extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
/* Replace align tags with left/right/center/justify */
$post = preg_replace( '/\[align=(justify|left|right)\](.+?)\[\/align\]/i', '[$1]$2[/$1]', $post );
$post = preg_replace( '/\[align=(justify|left|center|right)\](.+?)\[\/align\]/i', '[$1]$2[/$1]', $post );
/* remove justify, since we don't support it */
$post = str_replace( array( '[justify]', '[/justify]' ), '', $post );
/* Quotes */
$post = preg_replace("#\[quote=('|\")(.+?)('|\") pid='(\d+)' dateline='(\d+)'\]#i", "[quote name=\"$2\" timestamp=\"$5\"]", $post);
return $post;
}
@@ -544,12 +550,12 @@ class _Mybb extends \IPS\convert\Software
'pf_desc' => $row['description'],
'pf_content' => $options,
'pf_not_null' => $row['required'],
'pf_member_hide' => $row['profile'],
'pf_member_hide' => $row['profile'] ? 'all' : 'hide',
'pf_max_input' => $row['maxlength'],
'pf_position' => $row['disporder'],
'pf_show_on_reg' => $row['registration'],
'pf_input_format' => '/' . $row['regex'] . '/i',
'pf_admin_only' => ( $row['editableby'] == -1 ) ? 0 : 1, # MyBB allows you to define which groups can edit, so if this value is anything other than -1, assume it's admin only
'pf_member_edit' => ( $row['editableby'] == -1 ) ? 1 : 0, # MyBB allows you to define which groups can edit, so if this value is anything other than -1, assume it's admin only
'pf_multiple' => ( $theirType == 'multiselect' ) ? 1 : 0
);
@@ -564,7 +570,7 @@ class _Mybb extends \IPS\convert\Software
/**
* Maps a MyBB Profile Field type to IPS
*
* @param string MyBB Type
* @param string $type MyBB Type
* @return string IPS Type
*/
protected function _fieldMap( $type )
@@ -621,7 +627,7 @@ 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;
@@ -631,7 +637,7 @@ class _Mybb extends \IPS\convert\Software
break;
case 'timeregistered':
if ( !in_array( $promotion['registeredtype'], array( '>', '>=', '=' ) ) )
if ( !\in_array( $promotion['registeredtype'], array( '>', '>=', '=' ) ) )
{
throw new \UnderflowException;
}
@@ -685,7 +691,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 )
{
@@ -794,7 +800,7 @@ class _Mybb extends \IPS\convert\Software
$info = array(
'member_id' => $row['uid'],
'name' => $row['username'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'password' => $row['password'],
'password_extra' => $row['salt'],
@@ -806,7 +812,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'],
@@ -833,11 +839,20 @@ class _Mybb extends \IPS\convert\Software
);
$pfields = array();
foreach( $this->db->select( '*', 'userfields', array( "ufid=?", $row['uid'] ) ) AS $key => $field )
try
{
$key = str_replace( 'fid', '', $key );
$pfields[ $key ] = $field;
foreach ( $this->db->select( '*', 'userfields', array( "ufid=?", $row['uid'] ) )->first() AS $key => $field )
{
if ( $key == 'ufid' )
{
continue;
}
$key = str_replace( 'fid', '', $key );
$pfields[ $key ] = $field;
}
}
catch( \UnderflowException $e ) {}
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google' ) AS $pseudo )
{
@@ -875,21 +890,14 @@ class _Mybb extends \IPS\convert\Software
/* Photo */
$filename = NULL;
$filepath = NULL;
switch( $row['avatartype'] )
if( $row['avatartype'] == 'upload' )
{
case 'gravatar':
$info['pp_gravatar'] = $row['email'];
$info['pp_photo_type'] = 'gravatar';
break;
case 'upload':
$info['pp_photo_type'] = 'custom';
$avatar = trim( '.', $row['avatar'] );
$filepath = explode( '/', $avatar );
$filename = array_pop( $filepath );
$filepath = rtrim( $this->app->_session['more_info']['convertMembers']['photo_location'], '/' ) . implode( '/', $filepath );
break;
$info['pp_photo_type'] = 'custom';
$avatar = trim( $row['avatar'], '.' );
$filepath = explode( '/', $avatar );
$filename = parse_url( array_pop( $filepath ), PHP_URL_PATH );
$filepath = rtrim( $this->app->_session['more_info']['convertMembers']['photo_location'], '/' ) . implode( '/', $filepath );
}
$libraryClass->convertMember( $info, $pfields, $filename, $filepath );
@@ -899,7 +907,7 @@ class _Mybb extends \IPS\convert\Software
{
$libraryClass->convertFollow( array(
'follow_app' => 'core',
'follow_area' => 'members',
'follow_area' => 'member',
'follow_rel_id' => $buddy,
'follow_rel_id_type' => 'core_members',
'follow_member_id' => $row['uid'],
@@ -981,24 +989,29 @@ class _Mybb extends \IPS\convert\Software
$libraryClass::setKey( 'pmid' );
foreach( $this->fetch( 'privatemessages', 'pmid' ) AS $row )
foreach( $this->fetch( 'privatemessages', 'pmid', array( 'fromid<>uid' ) ) AS $row )
{
/* My hands are kind of tied here... every individual message is stored separately, rather than grouped into conversations. In this instance, we'll bring over each individual message as it's own topic for archival purposes. */
$recipients = \unserialize( $row['recipients'] );
/* In MyBB, replies to messages are stored as full conversations, and there is a row for both the sender and the recipient. We'll bring over every reply for archival purposes, but if the fromid == uid, skip because otherwise we end up with duplicates. */
$topic = array(
'mt_id' => $row['pmid'],
'mt_date' => $row['dateline'],
'mt_title' => $row['subject'],
'mt_starter_id' => $row['uid'],
'mt_starter_id' => $row['fromid'],
'mt_start_time' => $row['dateline'],
'mt_last_post_time' => $row['dateline'],
'mt_to_count' => 1,
);
$maps = array();
$maps[$row['uid']] = array(
'map_user_id' => $row['uid'],
$maps[$row['fromid']] = array(
'map_user_id' => $row['fromid'],
'map_read_time' => $row['readtime'],
'map_is_starter' => true
);
$maps[$row['toid']] = array(
'map_user_id' => $row['toid'],
'map_read_time' => $row['readtime'],
);
@@ -1019,14 +1032,14 @@ class _Mybb extends \IPS\convert\Software
$libraryClass::setKey( 'pmid' );
foreach( $this->fetch( 'privatemessages', 'pmid' ) AS $row )
foreach( $this->fetch( 'privatemessages', 'pmid', array( 'fromid<>uid' ) ) AS $row )
{
$libraryClass->convertPrivateMessageReply( array(
'msg_id' => $row['pmid'],
'msg_topic_id' => $row['pmid'],
'msg_date' => $row['dateline'],
'msg_post' => $row['message'],
'msg_author_id' => $row['uid'],
'msg_author_id' => $row['fromid'],
'msg_ip_address' => $row['ipaddress'],
) );
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 6 December 2016
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Photopost Core Converter
*/
class _Photopost extends \IPS\convert\Software
{
/**
@@ -152,7 +155,7 @@ class _Photopost extends \IPS\convert\Software
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return array Message to display
* @return array Messages to display
*/
public function finish()
{
@@ -197,7 +200,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 )
{
@@ -267,11 +270,10 @@ class _Photopost extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PhpBB Core Converter
*/
class _Phpbb extends \IPS\convert\Software
{
/**
@@ -101,17 +104,20 @@ class _Phpbb extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
if( isset( $rows['convertBanfilters'] ) )
{
$rows['convertBanfilters2'] = $rows['convertBanfilters'];
$rows['convertBanfilters2']['step_method'] = 'convertBanfilters2';
$rows['convertBanfilters2']['source_rows'] = array( 'table' => static::canConvert()['convertBanfilters2']['table'], 'where' => static::canConvert()['convertBanfilters2']['where'] );
}
$rows = array();
$rows['convertBanfilters2'] = array(
'step_title' => 'convert_banfilters',
'step_method' => 'convertBanfilters2',
'ips_rows' => \IPS\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' ),
'link_type' => 'core_banfilters'
);
return $rows;
}
@@ -131,9 +137,16 @@ class _Phpbb extends \IPS\convert\Software
{
case 'banfilters':
case 'disallow':
$count = 0;
$count += $this->db->select( 'COUNT(*)', 'banlist', array( "ban_userid=?", 0 ) )->first();
$count += $this->db->select( 'COUNT(*)', 'disallow' )->first();
try
{
$count = 0;
$count += $this->db->select( 'COUNT(*)', 'banlist', array( "ban_userid=?", 0 ) )->first();
$count += $this->db->select( 'COUNT(*)', 'disallow' )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
return $count;
break;
@@ -261,15 +274,28 @@ class _Phpbb extends \IPS\convert\Software
case 'convertMembers':
$return['convertMembers'] = array();
/* We can only retain one type of photo */
$return['convertMembers']['photo_hash'] = 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_randstring'),
);
try
{
/* Try to auto detect the salt */
$salt = $this->db->select( 'config_value', 'config', array( 'config_name=?', 'avatar_salt' ) )->first();
if( empty( $salt ) )
{
throw new \UnderflowException();
}
}
catch( \UnderflowException $e )
{
/* We can only retain one type of photo */
$return['convertMembers']['photo_hash'] = 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_randstring'),
);
}
/* Find out where the photos live */
\IPS\Member::loggedIn()->language()->words['photo_location_desc'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location_nodb_desc' );
@@ -353,8 +379,8 @@ class _Phpbb extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -524,7 +550,7 @@ class _Phpbb extends \IPS\convert\Software
'pf_type' => $type,
'pf_content' => $default,
'pf_not_null' => $row['field_required'],
'pf_member_hide' => $row['field_hide'],
'pf_member_hide' => $row['field_hide'] ? 'hide' : 'all',
'pf_max_input' => ( $field_type == 'dropdown' ) ? NULL : $row['field_maxlen'], # ignore this for selects
'pf_member_edit' => $row['field_active'],
'pf_position' => $row['field_order'],
@@ -579,7 +605,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 )
{
@@ -596,8 +622,22 @@ class _Phpbb extends \IPS\convert\Software
public function convertMembers()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'user_id' );
try
{
/* Try to auto detect the salt */
$avatarSalt = $this->db->select( 'config_value', 'config', array( 'config_name=?', 'avatar_salt' ) )->first();
if( empty( $avatarSalt ) )
{
throw new \UnderflowException();
}
}
catch( \UnderflowException $e )
{
$avatarSalt = rtrim( $this->app->_session['more_info']['convertMembers']['photo_hash'], '_' );
}
foreach( $this->fetch( 'users', 'user_id', array( "user_type<>?", 2 ) ) AS $row )
{
@@ -694,7 +734,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'] )
{
@@ -718,12 +758,7 @@ class _Phpbb extends \IPS\convert\Software
$fileext = explode( '.', $row['user_avatar'] );
$fileext = array_pop( $fileext );
$filepath = $this->app->_session['more_info']['convertMembers']['photo_location'];
$filename = $this->app->_session['more_info']['convertMembers']['photo_hash'] . '_' . $row['user_id'] . '.' . $fileext;
break;
case 'avatar.driver.gravatar':
$info['pp_photo_type'] = 'gravatar';
$info['pp_gravatar'] = $row['user_avatar'];
$filename = $avatarSalt . '_' . $row['user_id'] . '.' . $fileext;
break;
case 'avatar.driver.remote':
@@ -772,7 +807,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 );
@@ -782,7 +817,7 @@ class _Phpbb extends \IPS\convert\Software
{
$libraryClass->convertFollow( array(
'follow_app' => 'core',
'follow_area' => 'members',
'follow_area' => 'member',
'follow_rel_id' => $follower['zebra_id'],
'follow_rel_id_type' => 'core_members',
'follow_member_id' => $follower['user_id'],
@@ -930,7 +965,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;
@@ -5,7 +5,7 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 6 December 2016
* @note Only redirect scripts are supported right now
*/
@@ -13,12 +13,15 @@
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PhpMyForum Core Converter
*/
class _Phpmyforum extends \IPS\convert\Software
{
/**
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PunBB Core Converter
*/
class _Punbb extends \IPS\convert\Software
{
/**
@@ -189,8 +192,8 @@ class _Punbb extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -239,7 +242,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 )
{
@@ -304,11 +307,10 @@ class _Punbb extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -4,20 +4,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package IPS Social Suite
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
* @version
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* SMF Core Converter
*/
class _Smf extends \IPS\convert\Software
{
/**
@@ -132,6 +134,10 @@ class _Smf extends \IPS\convert\Software
{
return 0;
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
@@ -276,7 +282,7 @@ class _Smf extends \IPS\convert\Software
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return array Message to display
* @return array Messages to display
*/
public function finish()
{
@@ -306,8 +312,8 @@ class _Smf extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -321,7 +327,7 @@ class _Smf extends \IPS\convert\Software
$post = preg_replace("#\[img width=(\d+)(?: height=(\d+))?\]([a-zA-Z0-9.:\-/%\?&_=~\#\(\);:]+)\[\/img\]#i", "[img]$3[/img]", $post);
//Quotes
$post = preg_replace("#\[quote author=(.+?) link=(.+?)=(\d+)\.msg(\d+)(.+?)date=(\d+)\](.+)\[\/quote\]#i", "[quote name=\"$1\" post=\"$4\" timestamp=\"$6\"]$7[/quote]", $post);
$post = preg_replace("#\[quote author=(.+?) link=(.+?)=(\d+)\.msg(\d+)(.+?)date=(\d+)\](.+)\[\/quote\]#i", "[quote name=\"$1\" timestamp=\"$6\"]$7[/quote]", $post);
return $post;
}
@@ -524,15 +530,14 @@ 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 ) ? 1 : 0,
'pf_max_input' => ( !in_array( $row['field_type'], array( 'select', 'radio', 'check' ) ) ) ? $row['field_length'] : NULL,
'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_member_edit' => ( $row['private'] < 4 ) ? 1 : 0,
'pf_position' => $row['placement'],
'pf_show_on_reg' => ( $row['show_reg'] >= 1 ) ? 1 : 0,
'pf_input_format' => ( mb_substr( $row['mask'], 0, 5 ) == 'regex' ) ? str_replace( 'regex', '', $row['mask'] ) : NULL,
'pf_admin_only' => ( $row['private'] == 4 ) ? 1 : 0,
);
$merge = ( $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$row['id_field']}"] != 'none' ) ? $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$row['id_field']}"] : NULL;
@@ -581,7 +586,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 )
{
@@ -4,20 +4,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package IPS Social Suite
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
* @version
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* UBBThreads Core Converter
*/
class _UBBthreads extends \IPS\convert\Software
{
/**
@@ -78,7 +80,7 @@ class _UBBthreads extends \IPS\convert\Software
public static function softwareKey()
{
/* Child classes must override this method */
return "UBBthreads";
return "ubbthreads";
}
/**
@@ -136,11 +138,11 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
$rows = array();
$count = $this->countRows( static::canConvert()['convertMembersFollowers']['table'], static::canConvert()['convertMembersFollowers']['where'] );
if( $count )
@@ -150,7 +152,7 @@ class _UBBthreads extends \IPS\convert\Software
'step_title' => 'convert_follows',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_app=? and follow_area=?', 'core', 'member' ) ),
'source_rows' => $count,
'per_cycle' => 500,
'per_cycle' => 200,
'dependencies' => array( 'convertMembers' ),
'link_type' => 'core_follow',
);
@@ -208,7 +210,7 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Attempt to convert a textual date(time) representation to a DateTime instance
*
* @param string $date
* @param string $date Date to try to convert
* @return \IPS\DateTime|null
*/
protected function stringToDateTime( $date )
@@ -358,12 +360,13 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
$post = preg_replace( "#\[quote=(.+?)\]#i", "[quote name=\"$1\"]", $post );
$post = preg_replace( '#\[img:(left|center|right)\](.+?)\[\/img\]#i', '[$1][img]$2[/img][/$1]', $post );
$post = str_ireplace( [ "[image]", '[/image]', '[size:', '[color:' ], [ '[img]', '[/img]', '[size=', '[color=' ], $post );
return $post;
@@ -457,7 +460,7 @@ class _UBBthreads extends \IPS\convert\Software
foreach ( $this->fetch( 'GROUPS', 'GROUP_ID', static::$groupsWhere ) as $row )
{
/* @TODO: Can we enable custom titles per group? */
/* @todo: Can we enable custom titles per group? */
$info = array(
'g_id' => $row['GROUP_ID'],
'g_name' => $row['GROUP_NAME'],
@@ -470,7 +473,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 )
{
@@ -604,11 +607,10 @@ class _UBBthreads extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -678,7 +680,7 @@ class _UBBthreads extends \IPS\convert\Software
foreach ( $this->fetch( 'USER_TITLES', 'USER_TITLE_ID' ) as $row )
{
/**
* @TODO: We should probably prompt to keep or overwrite existing titles, since this can currently result...
* @todo: We should probably prompt to keep or overwrite existing titles, since this can currently result...
* ...in duplicate entries for the same post counts if the local rows are not dropped
*/
$libraryClass->convertRank( array(
@@ -4,19 +4,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package IPS Social Suite
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Vanilla Core Converter
*/
class _Vanilla extends \IPS\convert\Software
{
/**
@@ -49,7 +52,9 @@ class _Vanilla extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @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 )
@@ -173,7 +178,7 @@ class _Vanilla extends \IPS\convert\Software
* Attempt to convert a MySQL datetime string into a unix timestamp
*
* @param string $date Date(Time) string
* @return int|null Timestamp on successful conversion, otherwise NULL
* @return \IPS\DateTime Timestamp on successful conversion, otherwise NULL
*/
public static function mysqlToDateTime( $date )
{
@@ -300,7 +305,7 @@ class _Vanilla extends \IPS\convert\Software
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return string Message to display
* @return array Messages to display
*/
public function finish()
{
@@ -333,8 +338,8 @@ class _Vanilla extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -371,7 +376,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 )
{
@@ -459,7 +464,7 @@ class _Vanilla extends \IPS\convert\Software
}
/* Finally */
$libraryClass->convertMember( $info, NULL, $filename, $filepath );
$libraryClass->convertMember( $info, array(), $filename, $filepath );
$libraryClass->setLastKeyValue( $row['UserID'] );
}
}
@@ -517,7 +522,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'],
);
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Core Converter
*/
class _Vbulletin extends \IPS\convert\Software
{
/**
@@ -38,8 +41,8 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -65,10 +68,9 @@ class _Vbulletin extends \IPS\convert\Software
static::$isLegacy = FALSE;
}
}
/* 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();
}
@@ -155,7 +157,7 @@ class _Vbulletin extends \IPS\convert\Software
),
'convertPrivateMessageReplies' => array(
'table' => 'pm',
'where' => array( 'folderid!=?', -1 )
'where' => array( 'NOT (folderid= -1 AND parentpmid > 0)' )
),
'convertRanks' => array(
'table' => 'usertitle',
@@ -177,7 +179,7 @@ class _Vbulletin extends \IPS\convert\Software
*
* @param string $table The table containing the rows to count.
* @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)
* @param bool $recache Skip cache and pull directly (updating cache)
* @return integer
* @throws \IPS\convert\Exception
*/
@@ -186,18 +188,32 @@ class _Vbulletin extends \IPS\convert\Software
switch( $table )
{
case 'userchangelog':
return $this->db->select( 'count(changeid)', 'userchangelog', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) )->first();
try
{
return $this->db->select( 'count(changeid)', 'userchangelog', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
case 'pm':
/* vB doesn't put an index on a column that makes this converison much quicker, so we'll do it. */
if( !$this->db->checkForIndex( 'pm', 'parent_id' ) )
try
{
$this->db->addIndex( 'pm', array(
'type' => 'key',
'name' => 'parent_id',
'columns' => array( 'parentpmid', 'folderid' )
) );
/* vB doesn't put an index on a column that makes this converison much quicker, so we'll do it. */
if( !$this->db->checkForIndex( 'pm', 'parent_id' ) )
{
$this->db->addIndex( 'pm', array(
'type' => 'key',
'name' => 'parent_id',
'columns' => array( 'parentpmid', 'folderid' )
) );
}
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
return parent::countRows( $table, $where, $recache );
@@ -445,14 +461,24 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\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' ); } },
);
/* Find out where the avatar gallery photos live */
$return['convertMembers']['avatar_gallery_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_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' ) AS $field )
@@ -476,15 +502,17 @@ class _Vbulletin extends \IPS\convert\Software
break;
case 'convertClubs':
\IPS\Member::loggedIn()->language()->words['club_photo_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location' );
$return['convertClubs'] = array();
$return['convertClubs']['photo_location'] = array(
$return['convertClubs']['club_photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'database',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -521,6 +549,7 @@ class _Vbulletin extends \IPS\convert\Software
/* 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' ) );
/* Clubs */
\IPS\Task::queue( 'convert', 'RecountClubMembers', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
@@ -537,30 +566,39 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
$post = preg_replace( "#\[sigpic\](.+?)\[\/sigpic\]#i", "", $post );
if( \preg_match( "#\[sigpic]#i", $post ) )
{
$post = preg_replace( "#\[sigpic\](.+?)\[\/sigpic\]#i", "", $post );
}
$post = str_replace( "<", "&lt;", $post );
$post = str_replace( ">", "&gt;", $post );
$post = str_replace( "'", "&#39;", $post );
$post = preg_replace( "#\[quote=([^\];]+?)\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);\d+\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);n\d+\]#i", "[quote name='$1']", $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 );
$post = preg_replace( "#\[quote=([^\];]+?);n\d+\]#i", "[quote name='$1']", $post );
}
/* Remove video tags and allow our parser to handle the embeds it supports */
$post = preg_replace( "#\[video=[a-z]+;[a-z0-9_]+\](.*?)\[\/video\]#i", '$1', $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( mb_stristr( $post, '[mention=' ) )
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] ) )
@@ -574,22 +612,17 @@ class _Vbulletin extends \IPS\convert\Software
}
}
$maps = array();
$urls = array();
$cardUrls = array();
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array(\IPS\Db::i()->in( 'name', array_keys( $mentions ) )) ), 'IPS\Member' ) AS $member )
{
$maps[ $member->name ] = $member->member_id;
$urls[ $member->name ] = $member->url();
$cardUrls[$member->name] = $member->url()->setQueryString( 'do', 'hovercard' );
}
$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' ) );
foreach ( $mentions AS $member_name => $member_id )
foreach ( $mentions AS $memberName => $memberId )
{
$maps[ $member_name ] = preg_quote( $maps[ $member_name ], '#' );
$member_name = preg_quote( $member_name, '#' );
if( !isset( $maps[ $memberName ] ) )
{
continue;
}
$post = preg_replace( "#\[MENTION={$member_id}\]{$member_name}\[\/MENTION\]#i", "<a contenteditable=\"false\" rel=\"\" href=\"{$urls[ $member_name ]}\" data-mentionid=\"{$maps[ $member_name ]}\" data-ipshover-target=\"{$cardUrls[ $member_name ]}\" data-ipshover=\"\">@{$member_name}</a>", $post );
$memberNameQuoted = preg_quote( $memberName, '#' );
$post = preg_replace( "#\[MENTION={$memberId}\]{$memberNameQuoted}\[\/MENTION\]#i", "[mention={$maps[ $memberName ]}]{$memberName}[/mention]", $post );
}
}
}
@@ -711,10 +744,31 @@ class _Vbulletin extends \IPS\convert\Software
}
$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'] ) );
$options = @\unserialize( $field['data'] );
if ( $options )
{
$content = json_encode( $options );
}
else
{
/* 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] . '";';
}, $field['data'] );
if( $options = @\unserialize( $data ) )
{
$content = json_encode( $options );
}
else
{
/* Nothing else we can really do here */
$content = json_encode( array() );
}
}
}
else
{
@@ -735,14 +789,13 @@ 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_member_hide' => $field['hidden'],
'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,
'pf_position' => $field['displayorder'],
'pf_show_on_reg' => ( $field['required'] == 2 ) ? 1 : 0,
'pf_input_format' => $field['regex'],
'pf_admin_only' => ( $field['editable'] == 0 ) ? 1 : 0,
'pf_group_id' => $field['profilefieldcategoryid'],
'pf_multiple' => $multiple
);
@@ -756,7 +809,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Maps vBulletin Profile Field type to the IPS Equivalent
*
* @param string The vB Field Type
* @param string $type The vB Field Type
* @return string The IPS Field Type
*/
protected static function _pfieldMap( $type )
@@ -819,7 +872,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'] )
{
@@ -917,7 +970,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 )
{
@@ -929,7 +982,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* @brief Member history type map
*/
protected static $changeLogTypes = array( 'username' => 'display_name', 'email' => 'email_change', 'usergroupid' => 'member_group_id', 'membergroupids' => 'mgroup_others' );
protected static $changeLogTypes = array( 'username' => 'display_name', 'email' => 'email_change', 'usergroupid' => 'group', 'membergroupids' => 'group' );
/**
* Convert member history
@@ -943,16 +996,23 @@ class _Vbulletin extends \IPS\convert\Software
foreach( $this->fetch( 'userchangelog', 'changeid', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) ) AS $change )
{
$libraryClass->convertMemberHistory( array(
'log_id' => $change['changeid'],
'log_member' => $change['userid'],
'log_by' => $change['adminid'],
'log_type' => static::$changeLogTypes[ $change['fieldname'] ],
'log_data' => array( 'old' => $change['oldvalue'], 'new' => $change['newvalue'] ),
'log_date' => $change['change_time']
)
$info = array(
'log_id' => $change['changeid'],
'log_member' => $change['userid'],
'log_by' => $change['adminid'],
'log_type' => static::$changeLogTypes[ $change['fieldname'] ],
'log_data' => array( 'old' => $change['oldvalue'], 'new' => $change['newvalue'] ),
'log_date' => $change['change_time']
);
/* Add extra data for group change */
if( static::$changeLogTypes[ $change['fieldname'] ] == 'group' )
{
$info['log_data']['type'] = $change['fieldname'] == 'usergroupid' ? 'primary' : 'secondary';
$info['log_data']['by'] = 'manual';
}
$libraryClass->convertMemberHistory( $info );
$libraryClass->setLastKeyValue( $change['changeid'] );
}
}
@@ -968,6 +1028,9 @@ class _Vbulletin extends \IPS\convert\Software
$libraryClass::setKey( 'user.userid' );
/* User titles */
$userTitles = iterator_to_array( $this->db->select( 'title', 'usertitle' ) );
foreach( $this->fetch( 'user', 'user.userid', NULL, 'user.*, usertextfield.signature' )->join( 'usertextfield', 'user.userid = usertextfield.userid' ) AS $user )
{
/* <3 Closures */
@@ -1054,13 +1117,13 @@ class _Vbulletin extends \IPS\convert\Software
'member_id' => $user['userid'],
'member_group_id' => $user['usergroupid'],
'mgroup_others' => $user['membergroupids'],
'name' => $user['username'],
'name' => html_entity_decode( $user['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $user['email'],
'member_title' => $user['usertitle'],
'member_title' => \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'],
'joined' => $user['joindate'],
'ip_address' => $user['ipaddress'],
'warn_level' => ( $user['ipoints'] > 2147483647 ) ? 2147483647 : $user['ipoints'],
'warn_lastwarn' => $lastWarning['dateline'],
'warn_lastwarn' => ( \is_array( $lastWarning ) ) ? $lastWarning['dateline'] : $lastWarning,
'bday_day' => $bday_day,
'bday_month' => $bday_month,
'bday_year' => $bday_year,
@@ -1074,10 +1137,10 @@ class _Vbulletin extends \IPS\convert\Software
'members_bitoptions' => array(
'view_sigs' => $checkpermission( 'useroptions', 'showsignatures' ), // View signatures?
'coppa_user' => $checkpermission( 'useroptions', 'coppauser' ), // Was the member validated using coppa?
'timezone_override' => !$checkpermission( 'useroptions', 'dstauto' ), // If TRUE, user's timezone will not be detected automatically
),
'members_bitoptions2' => array(
'show_pm_popup' => $user['pmpopup'], // "Show pop-up when I have a new message"
'is_anon' => $checkpermission( 'useroptions', 'invisible' ), // Anonymous status
),
'pp_setting_count_comments' => $checkpermission( 'useroptions', 'vm_enable' ),
'pp_reputation_points' => $user['reputation'],
@@ -1086,6 +1149,18 @@ class _Vbulletin extends \IPS\convert\Software
'members_disable_pm' => !$checkpermission( 'useroptions', 'receivepm' ),
'member_posts' => $user['posts'],
);
/* If there is a validation record for this user, treat them as validating */
try
{
$activation = $this->db->select( '*', 'useractivation', array( "userid=?", $user['userid'] ) )->first();
$info['members_bitoptions']['validating'] = TRUE;
}
catch( \UnderflowException $e )
{
$activation = NULL;
}
/* Profile Fields */
try
@@ -1129,11 +1204,10 @@ class _Vbulletin extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -1257,15 +1331,42 @@ class _Vbulletin extends \IPS\convert\Software
list( $filedata, $filename, $filepath ) = array( NULL, NULL, NULL );
}
}
/* We'll try the avatar gallery */
if( empty( $filename ) AND empty( $filedata ) AND (int) $user['avatarid'] )
{
$avatar = $this->_getFromAvatarGallery( (int) $user['avatarid'] );
if( \is_array( $avatar ) )
{
$filename = $avatar['name'];
$filedata = $avatar['data'];
}
}
$libraryClass->convertMember( $info, $profileFields, $filename, $filepath, $filedata );
$memberId = $libraryClass->convertMember( $info, $profileFields, $filename, $filepath, $filedata );
/* If we are validating, store our validation row entry */
if( $activation !== NULL AND $memberId )
{
\IPS\Db::i()->replace( 'core_validating', array(
'vid' => md5( $activation['activationid'] . $activation['useractivationid'] ),
'member_id' => $memberId,
'entry_date' => $activation['dateline'],
'new_reg' => ( $activation['emailchange'] == 0 ? TRUE : FALSE ),
'email_chg' => ( $activation['emailchange'] == 1 ? TRUE : FALSE ),
'user_verified' => FALSE,
'ip_address' => $user['ipaddress'],
'email_sent' => $activation['dateline']
) );
}
/* Any friends need converting to followers? */
foreach( $this->db->select( '*', 'userlist', array( "type=? AND friend=? AND userid=?", 'buddy', 'yes', $user['userid'] ) ) AS $follower )
{
$libraryClass->convertFollow( array(
'follow_app' => 'core',
'follow_area' => 'members',
'follow_area' => 'member',
'follow_rel_id' => $follower['relationid'],
'follow_rel_id_type' => 'core_members',
'follow_member_id' => $follower['userid'],
@@ -1482,78 +1583,10 @@ class _Vbulletin extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $pm['pmid'] );
continue;
}
try
{
$replies = $this->db->select( 'COUNT(*)', 'pm', array( "parentpmid=? AND folderid!=?", $pm['pmid'], -1 ) )->first();
$replies += 1;
}
catch( \UnderflowException $e )
{
$replies = 1;
}
$topic = array(
'mt_id' => $pm['pmid'],
'mt_date' => $pm['dateline'],
'mt_title' => $pm['title'],
'mt_starter_id' => $pm['fromuserid'],
'mt_start_time' => $pm['dateline'],
'mt_last_post_time' => $pm['dateline'],
'mt_to_count' => count( @\unserialize( $pm['touserarray'] ) ),
'mt_to_member_id' => $pm['userid'],
'mt_replies' => $replies,
'mt_first_msg_id' => $pm['pmid'],
);
$authors = array();
$authors[ $pm['fromuserid'] ] = $pm['fromuserid'];
try
{
foreach( $this->db->select( '*', 'pm', array( "parentpmid=?", $pm['pmid'] ) )->join( 'pmtext', 'pm.pmtextid = pmtext.pmtextid' ) AS $post )
{
if ( $post['folderid'] != -1 )
{
$authors[ $post['fromuserid'] ] = $post['fromuserid'];
}
}
}
catch( \UnderflowException $e ) {}
/* Use stored author ids */
$toUsers = \unserialize( $pm['touserarray'] );
if( isset( $toUsers['cc'] ) )
{
foreach( array_keys( $toUsers['cc'] ) as $id )
{
$authors[ $id ] = $id;
}
}
elseif( \count( $toUsers ) AND isset( $toUsers['cc'] ) AND isset( $toUsers['bcc'] ) )
{
foreach( array_keys( $toUsers ) as $id )
{
$authors[ $id ] = $id;
}
}
$maps = array();
foreach( $authors AS $mapAuthor )
{
$maps[ $mapAuthor ] = array(
'map_user_id' => $mapAuthor,
'map_topic_id' => $pm['pmid'],
'map_user_active' => 1,
'map_user_banned' => 0,
'map_has_unread' => 0,
'map_is_starter' => ( $mapAuthor == $pm['fromuserid'] ) ? 1 : 0,
);
}
$libraryClass->convertPrivateMessage( $topic, $maps );
/* Convert PM */
$this->_convertPm( $libraryClass, $pm );
$libraryClass->setLastKeyValue( $pm['pmid'] );
}
}
@@ -1569,11 +1602,22 @@ class _Vbulletin extends \IPS\convert\Software
$libraryClass::setKey( 'pm.pmid' );
foreach( $this->fetch( 'pm', 'pm.pmid', array( 'pm.folderid!=?', -1 ), "pm.*, pmtext.*" )->join( 'pmtext', 'pm.pmtextid = pmtext.pmtextid' ) AS $pm )
foreach( $this->fetch( 'pm', 'pm.pmid', array( 'NOT (pm.folderid= -1 AND pm.parentpmid > 0)' ), "pm.*, pmtext.*, test.pmid as parent_test" )
->join( 'pmtext', 'pm.pmtextid = pmtext.pmtextid' )
->join( array( 'pm', 'test' ), 'pm.parentpmid=test.pmid' )
AS $pm )
{
/* See if the parent exists, or whether it's already been deleted in vBulletin. */
$parentId = FALSE;
if( $pm['parentpmid'] AND !$pm['parent_test'] )
{
/* It has previously been deleted, so this message can become it's own conversation */
$parentId = $this->_convertPm( $libraryClass, $pm );
}
$libraryClass->convertPrivateMessageReply( array(
'msg_id' => $pm['pmid'],
'msg_topic_id' => !$pm['parentpmid'] ? $pm['pmid'] : $pm['parentpmid'],
'msg_topic_id' => (!$pm['parentpmid'] OR $parentId ) ? $pm['pmid'] : $pm['parentpmid'],
'msg_date' => $pm['dateline'],
'msg_post' => $pm['message'],
'msg_author_id' => $pm['fromuserid'],
@@ -1658,13 +1702,13 @@ class _Vbulletin extends \IPS\convert\Software
$icondata = NULL;
if ( $icon )
{
if ( $this->app->_session['more_info']['convertClubs']['photo_location'] == 'database' )
if ( $this->app->_session['more_info']['convertClubs']['club_photo_location'] == 'database' )
{
$icondata = $icon['filedata'];
}
else
{
$iconpath = $this->app->_session['more_info']['convertClubs']['photo_location'] . "/socialgroupicon_{$icon['groupid']}_{$icon['dateline']}.gif";
$iconpath = $this->app->_session['more_info']['convertClubs']['club_photo_location'] . "/socialgroupicon_{$icon['groupid']}_{$icon['dateline']}.gif";
}
}
@@ -1907,7 +1951,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Get Setting Value - useful for global settings that need to be translated to group or member settings
*
* @param $key The setting key
* @param string $key The setting key
* @return mixed
*/
protected function _setting( $key )
@@ -1951,22 +1995,49 @@ class _Vbulletin extends \IPS\convert\Software
/* 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 )
{
$id = NULL;
try
{
$data = (string) $this->app->getLink( \IPS\Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
return \IPS\Http\Url::external( \IPS\Settings::i()->base_url . 'applications/core/interface/file/attachment.php' )->setQueryString( 'id', $data );
$id = (string) $this->app->getLink( \IPS\Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
}
/* Try any child conversions */
catch( \Exception $e )
{
foreach( $this->app->children() as $child )
{
try
{
$id = (string) $child->getLink( \IPS\Request::i()->attachmentid, array( 'attachments', 'core_attachments' ) );
break;
}
catch( \Exception $e ) {}
}
}
if( !$id )
{
return NULL;
}
return \IPS\Http\Url::external( \IPS\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 ) )
{
return \IPS\Http\Url::internal( "app=core&module=search&controller=search&tags=" . \IPS\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 ) )
{
try
{
return \IPS\Member\Club::loadAndCheckPerms( (string) $this->app->getLink( \IPS\Request::i()->groupid, array( 'core_clubs' ) ) )->url();
}
catch( \Exception $e )
{
return NULL;
}
}
else
{
/* If we can't access profiles, don't bother trying to redirect */
@@ -2038,4 +2109,149 @@ class _Vbulletin extends \IPS\convert\Software
return FALSE;
}
}
/**
* Centralised PM conversation conversion
*
* @param $libraryClass \IPS\convert\Library
* @param $pm array
* @return mixed
*/
protected function _convertPm( \IPS\convert\Library $libraryClass, array $pm )
{
try
{
$replies = $this->db->select( 'COUNT(*)', 'pm', array( "parentpmid=? AND folderid!=?", $pm['pmid'], -1 ) )->first();
$replies += 1;
}
catch( \UnderflowException $e )
{
$replies = 1;
}
$toUserArray = @\unserialize( $pm['touserarray'] );
$topic = array(
'mt_id' => $pm['pmid'],
'mt_date' => $pm['dateline'],
'mt_title' => $pm['title'],
'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_member_id' => $pm['userid'],
'mt_replies' => $replies,
'mt_first_msg_id' => $pm['pmid'],
);
$authors = array();
$authors[ $pm['fromuserid'] ] = $pm['fromuserid'];
try
{
foreach( $this->db->select( '*', 'pm', array( "parentpmid=?", $pm['pmid'] ) )->join( 'pmtext', 'pm.pmtextid = pmtext.pmtextid' ) AS $post )
{
if ( $post['folderid'] != -1 )
{
$authors[ $post['fromuserid'] ] = $post['fromuserid'];
}
}
}
catch( \UnderflowException $e ) {}
/* Use stored author ids */
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 ) )
{
foreach( array_keys( $toUserArray ) as $id )
{
$authors[ $id ] = $id;
}
}
$maps = array();
foreach( $authors AS $mapAuthor )
{
$maps[ $mapAuthor ] = array(
'map_user_id' => $mapAuthor,
'map_topic_id' => $pm['pmid'],
'map_user_active' => 1,
'map_user_banned' => 0,
'map_has_unread' => 0,
'map_is_starter' => ( $mapAuthor == $pm['fromuserid'] ) ? 1 : 0,
);
}
return $libraryClass->convertPrivateMessage( $topic, $maps );
}
protected $_avatars = array();
/**
* Fetch Avatar from Avatar Gallery
*
* @param int $id Avatar ID
* @return array|bool Array of data/name or FALSE
*/
protected function _getFromAvatarGallery( int $id )
{
if( isset( $this->_avatars[ $id ] ) )
{
return $this->_avatars[ $id ];
}
try
{
$avatar = $this->db->select( 'avatarpath', 'avatar', array( 'avatarid=?', $id ) )->first();
}
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://' )
{
try
{
$profilePhotoName = pathinfo( parse_url( $avatar, PHP_URL_PATH ), PATHINFO_BASENAME );
$profilePhotoData = \IPS\Http\Url::external( $avatar )->request()->get();
/* Check it's really an image */
try
{
\IPS\Image::create( $profilePhotoData );
}
catch( \InvalidArgumentException $e )
{
return $this->_avatars[ $id ] = FALSE;
}
return $this->_avatars[ $id ] = array( 'data' => $profilePhotoData, 'name' => $profilePhotoName );
}
catch ( \IPS\Http\Request\Exception $e )
{
return $this->_avatars[ $id ] = FALSE;
}
}
/* nope, it's a file */
$dirName = pathinfo( $avatar, PATHINFO_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 );
return $this->_avatars[ $id ] = array( 'data' => $profilePhotoData, 'name' => $profilePhotoName );
}
return $this->_avatars[ $id ] = FALSE;
}
}
@@ -5,21 +5,58 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin 5 Core Converter
*/
class _Vbulletin5 extends \IPS\convert\Software
{
/**
* @brief is vB 5.5.3 or newer
*/
protected static $skipCustomProfilePic = 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 )
{
try
{
/* Is this vB 5.5.3 or newer? */
if ( static::$skipCustomProfilePic === NULL )
{
$version = $this->db->select( 'value', 'setting', array( "varname=?", 'templateversion' ) )->first();
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
}
return $return;
}
/**
* Software Name
*
@@ -135,12 +172,16 @@ class _Vbulletin5 extends \IPS\convert\Software
{
return 0;
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
case 'node':
if ( $where === 'clubs' )
{
return count( $this->fetchClubs() );
return \count( $this->fetchClubs() );
}
else
{
@@ -160,7 +201,14 @@ class _Vbulletin5 extends \IPS\convert\Software
break;
case 'userchangelog':
return $this->db->select( 'count(changeid)', 'userchangelog', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) )->first();
try
{
return $this->db->select( 'count(changeid)', 'userchangelog', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
@@ -395,8 +443,8 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -426,15 +474,17 @@ class _Vbulletin5 extends \IPS\convert\Software
break;
case 'convertClubs':
\IPS\Member::loggedIn()->language()->words['club_photo_location'] = \IPS\Member::loggedIn()->language()->addToStack( 'photo_location' );
$return['convertClubs'] = array();
$return['convertClubs']['photo_location'] = array(
$return['convertClubs']['club_photo_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'database',
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -483,24 +533,16 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
/* Fix Quotes */
$post = preg_replace( "#\[quote=([^\];]+?)\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);\d+\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);n\d+\]#i", "[quote name='$1']", $post );
/* Remove video tags and allow our parser to handle the embeds it supports */
$post = preg_replace( "#\[video=[a-z]+;[a-z0-9_]+\](.*?)\[\/video\]#i", '$1', $post );
/* IMG2 BBcode */
/* 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 )
{
@@ -533,40 +575,46 @@ 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 ) )
{
if ( isset( $matches[1] ) )
/* Make sure we actually have mention data */
if ( isset( $matches[1] ) AND \count( $matches[1] ) )
{
$mentions = array();
foreach( $matches[1] AS $k => $v )
foreach ( $matches[1] AS $k => $v )
{
if ( isset( $matches[3][$k] ) )
if ( isset( $matches[3][ $k ] ) )
{
$name = trim( $matches[3][ $k ], '@' );
$mentions[ $name ] = $v;
$mentions[ $matches[3][ $k ] ] = $v;
}
}
$maps = array();
$urls = array();
$cardUrls = array();
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array( \IPS\Db::i()->in( 'name', array_keys( $mentions ) ) ) ), 'IPS\Member' ) AS $member )
{
$maps[ $member->name ] = $member->member_id;
$urls[ $member->name ] = $member->url();
$cardUrls[ $member->name ] = $member->url()->setQueryString( 'do', 'hovercard' );
}
$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' ) );
foreach( $mentions AS $member_name => $member_id )
foreach ( $mentions AS $memberName => $memberId )
{
$maps[ $member_name ] = preg_quote( $maps[ $member_name ], '#' );
$memberNameEscaped = preg_quote( $member_name, '#' );
if ( !isset( $maps[ $memberName ] ) )
{
continue;
}
$post = preg_replace( "#\[user(=\"?(\d+)\"?)?\]@?{$memberNameEscaped}\[\/user\]#i", "<a contenteditable=\"false\" rel=\"\" href=\"{$urls[ $member_name ]}\" data-mentionid=\"{$maps[ $member_name ]}\" data-ipshover-target=\"{$cardUrls[ $member_name ]}\" data-ipshover=\"\">@{$member_name}</a>", $post );
$memberNameQuoted = preg_quote( $memberName, '#' );
$post = preg_replace( "#\[user(=\"?(\d+)\"?)?\]@?{$memberNameQuoted}\[\/user\]#i", "[mention={$maps[ $memberName ]}]{$memberName}[/mention]", $post );
}
}
}
// run everything through htmlspecialchars to prevent XSS
$post = htmlspecialchars( $post, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE );
/* Fix Quotes */
$post = preg_replace( "#\[quote=([^\];]+?)\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);\d+\]#i", "[quote name='$1']", $post );
$post = preg_replace( "#\[quote=([^\];]+?);n\d+\]#i", "[quote name='$1']", $post );
/* Remove video tags and allow our parser to handle the embeds it supports */
$post = preg_replace( "#\[video=[a-z_]+;[a-z0-9_]+\](.*?)\[\/video\]#i", '$1', $post );
return $post;
}
@@ -684,7 +732,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$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'] ) );
}
@@ -707,14 +755,13 @@ 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_member_hide' => $field['hidden'],
'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,
'pf_position' => $field['displayorder'],
'pf_show_on_reg' => ( $field['required'] == 2 ) ? 1 : 0,
'pf_input_format' => $field['regex'],
'pf_admin_only' => ( $field['editable'] == 0 ) ? 1 : 0,
'pf_group_id' => $field['profilefieldcategoryid'],
'pf_multiple' => $multiple
);
@@ -728,7 +775,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Maps vBulletin Profile Field type to the IPS Equivalent
*
* @param string The vB Field Type
* @param string $type The vB Field Type
* @return string The IPS Field Type
*/
protected static function _pfieldMap( $type )
@@ -791,7 +838,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'] )
{
@@ -889,7 +936,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 )
{
@@ -901,7 +948,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* @brief Member history type map
*/
protected static $changeLogTypes = array( 'username' => 'display_name', 'email' => 'email_change', 'usergroupid' => 'member_group_id', 'membergroupids' => 'mgroup_others' );
protected static $changeLogTypes = array( 'username' => 'display_name', 'email' => 'email_change', 'usergroupid' => 'group', 'membergroupids' => 'group' );
/**
* Convert member history
@@ -915,17 +962,24 @@ class _Vbulletin5 extends \IPS\convert\Software
foreach( $this->fetch( 'userchangelog', 'changeid', array( $this->db->in( 'fieldname', array_keys( static::$changeLogTypes ) ) ) ) AS $change )
{
$libraryClass->convertMemberHistory( array(
'log_id' => $change['changeid'],
'log_member' => $change['userid'],
'log_by' => $change['adminid'],
'log_type' => static::$changeLogTypes[ $change['fieldname'] ],
'log_data' => array( 'old' => $change['oldvalue'], 'new' => $change['newvalue'] ),
'log_date' => $change['change_time'],
'log_ip_address' => long2ip( (int) $change['ipaddress'] )
)
$info = array(
'log_id' => $change['changeid'],
'log_member' => $change['userid'],
'log_by' => $change['adminid'],
'log_type' => static::$changeLogTypes[ $change['fieldname'] ],
'log_data' => array( 'old' => $change['oldvalue'], 'new' => $change['newvalue'] ),
'log_date' => $change['change_time'],
'log_ip_address' => long2ip( (int) $change['ipaddress'] )
);
/* Add extra data for group change */
if( static::$changeLogTypes[ $change['fieldname'] ] == 'group' )
{
$info['log_data']['type'] = $change['fieldname'] == 'usergroupid' ? 'primary' : 'secondary';
$info['log_data']['by'] = 'manual';
}
$libraryClass->convertMemberHistory( $info );
$libraryClass->setLastKeyValue( $change['changeid'] );
}
}
@@ -941,6 +995,9 @@ class _Vbulletin5 extends \IPS\convert\Software
$libraryClass::setKey( 'user.userid' );
/* User titles */
$userTitles = iterator_to_array( $this->db->select( 'title', 'usertitle' ) );
foreach( $this->fetch( 'user', 'user.userid', NULL, 'user.*, usertextfield.signature' )->join( 'usertextfield', 'user.userid = usertextfield.userid' ) AS $user )
{
/* <3 Closures */
@@ -1036,9 +1093,9 @@ class _Vbulletin5 extends \IPS\convert\Software
'member_id' => $user['userid'],
'member_group_id' => $user['usergroupid'],
'mgroup_others' => $user['membergroupids'],
'name' => $user['username'],
'name' => html_entity_decode( $user['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $user['email'],
'member_title' => $user['usertitle'],
'member_title' => \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'],
'joined' => $user['joindate'],
'ip_address' => $user['ipaddress'],
'warn_level' => $user['ipoints'],
@@ -1056,10 +1113,10 @@ class _Vbulletin5 extends \IPS\convert\Software
'members_bitoptions' => array(
'view_sigs' => $checkpermission( 'useroptions', 'showsignatures' ), // View signatures?
'coppa_user' => $checkpermission( 'useroptions', 'coppauser' ), // Was the member validated using coppa?
'timezone_override' => !$checkpermission( 'useroptions', 'dstauto' ), // If TRUE, user's timezone will not be detected automatically
),
'members_bitoptions2' => array(
'show_pm_popup' => $user['pmpopup'], // "Show pop-up when I have a new message"
'is_anon' => $checkpermission( 'useroptions', 'invisible' ), // Anonymous status
),
'pp_setting_count_comments' => $checkpermission( 'useroptions', 'vm_enable' ),
'pp_reputation_points' => $user['reputation'],
@@ -1070,6 +1127,18 @@ class _Vbulletin5 extends \IPS\convert\Software
'fb_uid' => $user['fbuserid'],
'fb_token' => $user['fbaccesstoken']
);
/* If there is a validation record for this user, treat them as validating */
try
{
$activation = $this->db->select( '*', 'useractivation', array( "userid=?", $user['userid'] ) )->first();
$info['members_bitoptions']['validating'] = TRUE;
}
catch( \UnderflowException $e )
{
$activation = NULL;
}
/* Profile Fields */
try
@@ -1113,11 +1182,10 @@ class _Vbulletin5 extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -1126,16 +1194,21 @@ class _Vbulletin5 extends \IPS\convert\Software
/* Profile Photos */
$firstTable = 'customavatar';
$secondTable = 'customprofilepic';
$secondTable = !static::$skipCustomProfilePic ? 'customprofilepic' : FALSE; # vB 5.5.3 removes the customprofilepic table
if ( $this->app->_session['more_info']['convertMembers']['photo_type'] == 'profile_photos' )
{
$firstTable = 'customprofilepic';
$firstTable = !static::$skipCustomProfilePic ? 'customprofilepic' : FALSE; # vB 5.5.3 removes the customprofilepic table
$secondTable = 'customavatar';
}
if ( $this->app->_session['more_info']['convertMembers']['photo_location'] == 'database' )
{
try
{
if( $firstTable === FALSE )
{
throw new \UnderflowException();
}
foreach( $this->db->select( 'filedata, filename', $firstTable, array( "userid=?", $user['userid'] ) )->first() AS $key => $value )
{
if ( $key == 'filedata' )
@@ -1153,6 +1226,11 @@ class _Vbulletin5 extends \IPS\convert\Software
{
try
{
if( $secondTable === FALSE )
{
throw new \UnderflowException();
}
foreach( $this->db->select( 'filedata, filename', $secondTable, array( "userid=?", $user['userid'] ) )->first() AS $key => $value )
{
if ( $key == 'filedata' )
@@ -1188,6 +1266,11 @@ class _Vbulletin5 extends \IPS\convert\Software
{
try
{
if( $firstTable === FALSE )
{
throw new \UnderflowException();
}
$ext = $this->db->select( 'filename', $firstTable, array( "userid=?", $user['userid'] ) )->first();
$ext = explode( '.', $ext );
$ext = array_pop( $ext );
@@ -1204,6 +1287,11 @@ class _Vbulletin5 extends \IPS\convert\Software
}
catch( \UnderflowException $e )
{
if( $secondTable === FALSE )
{
throw new \UnderflowException();
}
$ext = $this->db->select( 'filename', $secondTable, array( "userid=?", $user['userid'] ) )->first();
$ext = explode( '.', $ext );
$ext = array_pop( $ext );
@@ -1225,14 +1313,29 @@ class _Vbulletin5 extends \IPS\convert\Software
}
}
$libraryClass->convertMember( $info, $profileFields, $filename, $filepath, $filedata );
$memberId = $libraryClass->convertMember( $info, $profileFields, $filename, $filepath, $filedata );
/* If we are validating, store our validation row entry */
if( $activation !== NULL AND $memberId )
{
\IPS\Db::i()->replace( 'core_validating', array(
'vid' => md5( $activation['activationid'] . $activation['useractivationid'] ),
'member_id' => $memberId,
'entry_date' => $activation['dateline'],
'new_reg' => ( $activation['emailchange'] == 0 ? TRUE : FALSE ),
'email_chg' => ( $activation['emailchange'] == 1 ? TRUE : FALSE ),
'user_verified' => FALSE,
'ip_address' => $user['ipaddress'],
'email_sent' => $activation['dateline']
) );
}
/* Any friends need converting to followers? */
foreach( $this->db->select( '*', 'userlist', array( "type=? AND friend=? AND userid=?", 'buddy', 'yes', $user['userid'] ) ) AS $follower )
{
$libraryClass->convertFollow( array(
'follow_app' => 'core',
'follow_area' => 'members',
'follow_area' => 'member',
'follow_rel_id' => $follower['relationid'],
'follow_rel_id_type' => 'core_members',
'follow_member_id' => $follower['userid'],
@@ -1360,7 +1463,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;
}
@@ -1373,7 +1476,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'],
@@ -1549,7 +1652,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$iconpath = NULL;
if ( $filedata )
{
if ( $this->app->_session['more_info']['convertClubs']['photo_location'] == 'database' )
if ( $this->app->_session['more_info']['convertClubs']['club_photo_location'] == 'database' )
{
$icondata = $filedata['filedata'];
}
@@ -1557,7 +1660,7 @@ class _Vbulletin5 extends \IPS\convert\Software
{
$folder = preg_split( '//', $filedata['userid'], NULL, PREG_SPLIT_NO_EMPTY );
$folder = implode( '/', $folder );
$iconpath = $this->app->_session['more_info']['convertClubs']['photo_location'] . '/' . $folder . '/' . $filedata['filedataid'] . '.attach';
$iconpath = $this->app->_session['more_info']['convertClubs']['club_photo_location'] . '/' . $folder . '/' . $filedata['filedataid'] . '.attach';
}
}
@@ -1779,7 +1882,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Get Setting Value - useful for global settings that need to be translated to group or member settings
*
* @param $key The setting key
* @param string $key The setting key
* @return mixed
*/
protected function _setting( $key )
@@ -1876,7 +1979,8 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Helper method to retrieve content type ids
*
* @return void
* @param string $type Content type
* @return string
*/
protected function fetchType( $type )
{
@@ -0,0 +1,812 @@
<?php
/**
* @brief Converter Woltlab Suite Core Class
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage convert
* @since 26 Mar 2020
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Woltlab Suite Core Converter
*/
class _Woltlab extends \IPS\convert\Software
{
/**
* @brief The WCF table prefix can change depending on the number of installs
*/
public static $installId = 1;
/**
* Software Name
*
* @return string
*/
public static function softwareName()
{
/* Child classes must override this method */
return "WoltLab Suite (3.1)";
}
/**
* Software Key
*
* @return string
*/
public static function softwareKey()
{
/* Child classes must override this method */
return "woltlab";
}
/**
* Content we can convert from this software.
*
* @return array
*/
public static function canConvert()
{
return array(
'convertProfileFieldGroups' => array(
'table' => 'wcf' . static::$installId . '_user_option_category',
'where' => array( 'parentCategoryName=?', 'profile' )
),
'convertProfileFields' => array(
'table' => 'wcf' . static::$installId . '_user_option',
'where' => NULL
),
'convertEmoticons' => array(
'table' => 'wcf' . static::$installId . '_smiley',
'where' => NULL
),
'convertIgnoredUsers' => array(
'table' => 'wcf' . static::$installId . '_user_ignore',
'where' => NULL
),
'convertGroups' => array(
'table' => 'wcf' . static::$installId . '_user_group',
'where' => NULL
),
'convertMembers' => array(
'table' => 'wcf' . static::$installId . '_user',
'where' => NULL
),
'convertRanks' => array(
'table' => 'wcf' . static::$installId . '_user_rank',
'where' => array( 'requiredPoints>0' )
),
'convertPrivateMessages' => array(
'table' => 'wcf' . static::$installId . '_conversation',
'where' => NULL
),
'convertPrivateMessageReplies' => array(
'table' => 'wcf' . static::$installId . '_conversation_message',
'where' => NULL,
)
);
}
/**
* Count Source Rows for a specific step
*
* @param string $table The table containing the rows to count.
* @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
*/
public function countRows( $table, $where=NULL, $recache=FALSE )
{
switch( $table )
{
case 'wcf' . static::$installId . '_user_option':
try
{
$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'] ) ) ) );
return $this->db->select( 'count(optionID)', 'wcf' . static::$installId . '_user_option', $where )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
return parent::countRows( $table, $where, $recache );
break;
}
}
/**
* Can we convert passwords from this software.
*
* @return boolean
*/
public static function loginEnabled()
{
return TRUE;
}
/**
* List of conversion methods that require additional information
*
* @return array
*/
public static function checkConf()
{
return array(
'convertProfileFieldGroups',
'convertProfileFields',
'convertEmoticons',
'convertGroups',
'convertMembers'
);
}
/**
* Get More Information
*
* @param string $method Conversion method
* @return array
*/
public function getMoreInfo( $method )
{
$return = array();
switch( $method )
{
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[ $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' );
$return['convertProfileFieldGroups']["map_pfgroup_{$group['categoryID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
'field_default' => NULL,
'field_required' => FALSE,
'field_extra' => array( 'options' => $options ),
'field_hint' => NULL,
);
}
break;
case 'convertProfileFields':
$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[ $field->_id ] = $field->_title;
}
$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' );
$return['convertProfileFields']["map_pfield_{$field['optionID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
'field_default' => NULL,
'field_required' => FALSE,
'field_extra' => array( 'options' => $options ),
'field_hint' => NULL,
);
}
break;
case 'convertEmoticons':
\IPS\Member::loggedIn()->language()->words['emoticon_path'] = \IPS\Member::loggedIn()->language()->addToStack( 'source_path', FALSE, array( 'sprintf' => array( 'Woltlab' ) ) );
$return['convertEmoticons'] = array(
'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' ); } },
),
'keep_existing_emoticons' => array(
'field_class' => 'IPS\\Helpers\\Form\\Checkbox',
'field_default' => TRUE,
'field_required' => FALSE,
'field_extra' => array(),
'field_hint' => NULL,
)
);
break;
case 'convertGroups':
$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[ $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' );
$return['convertGroups']["map_group_{$group['groupID']}"] = array(
'field_class' => 'IPS\\Helpers\\Form\\Select',
'field_default' => NULL,
'field_required' => FALSE,
'field_extra' => array( 'options' => $options ),
'field_hint' => NULL
);
}
break;
case 'convertMembers':
$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' );
$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' ); } },
);
\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' );
$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' ); } },
);
break;
}
return ( isset( $return[ $method ] ) ) ? $return[ $method ] : array();
}
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return array Messages to display
*/
public function finish()
{
/* Search Index Rebuild */
\IPS\Content\Search\Index::i()->rebuild();
/* Clear Cache and Store */
\IPS\Data\Store::i()->clearAll();
\IPS\Data\Cache::i()->clearAll();
/* Non-Content Rebuilds */
\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' ) );
/* 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' ) );
/* First Post Data */
\IPS\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' ) );
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" );
}
/**
* Fix Post Data
*
* @param string $post Post
* @return string Fixed Post
*/
public static function fixPostData( $post )
{
return $post;
}
/**
* Convert profile field groups
*
* @return void
*/
public function convertProfileFieldGroups()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'categoryID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_user_option_category', 'categoryID', array( 'parentCategoryName=?', 'profile' ) ) AS $group )
{
$name = $this->getLanguage( $group['categoryName'], 'wcf.user.option.category' );
$merge = ( $this->app->_session['more_info']['convertProfileFieldGroups']["map_pfgroup_{$group['categoryID']}"] != 'none' ) ? $this->app->_session['more_info']['convertProfileFieldGroups']["map_pfgroup_{$group['categoryID']}"] : NULL;
$libraryClass->convertProfileFieldGroup( array(
'pf_group_id' => $group['categoryID'],
'pf_group_name' => $name,
'pf_group_order' => $group['showOrder']
), $merge );
$libraryClass->setLastKeyValue( $group['categoryID'] );
}
}
/**
* Convert profile fields
*
* @return void
*/
public function convertProfileFields()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'optionID' );
$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->fetch( 'wcf' . static::$installId . '_user_option', 'optionID', $where ) AS $field )
{
/* Birthday is special */
if( \in_array( $field['optionName'], ['birthday', 'birthdayShowYear'] ) )
{
$libraryClass->setLastKeyValue( $field['optionID'] );
continue;
}
$content = [];
switch( $field['optionType'] )
{
case 'aboutMe':
$type = 'Editor';
break;
case 'boolean':
$type = 'YesNo';
break;
case 'text':
case 'URL':
$type = ucwords( mb_strtolower( $field['optionType'] ) );
break;
case 'textarea':
$type = 'TextArea';
break;
case 'select':
$type = 'Select';
foreach( explode( "\n", $field['selectOptions'] ) as $opt )
{
list( $id, $lang ) = explode( ':', $opt );
$content[ $id ] = $this->getLanguage( $lang );
}
break;
default:
$type = 'Text';
break;
}
$category = $this->db->select( 'categoryID', 'wcf' . static::$installId . '_user_option_category', array( 'categoryName=?', $field['categoryName'] ) )->first();
$info = array(
'pf_id' => $field['optionID'],
'pf_name' => $this->getLanguage( $field['optionName'], 'wcf.user.option' ),
'pf_desc' => $this->getLanguage( $field['optionName'], 'wcf.user.option.' . $field['optionName'] . '.description' ),
'pf_type' => $type,
'pf_content' => json_encode( $content ),
'pf_not_null' => $field['required'],
'pf_member_hide' => $field['visible'] == 15 ? 'all' : 'hide',
'pf_member_edit' => ( $field['editable'] >= 1 ) ? 1 : 0,
'pf_position' => $field['showOrder'],
'pf_show_on_reg' => $field['askDuringRegistration'],
'pf_input_format' => $field['validationPattern'],
'pf_group_id' => $category
);
$merge = ( $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$field['optionID']}"] != 'none' ) ? $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$field['optionID']}"] : NULL;
$libraryClass->convertProfileField( $info, $merge );
$libraryClass->setLastKeyValue( $field['optionID'] );
}
}
/**
* Convert emoticons
*
* @return void
*/
public function convertEmoticons()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'smileyID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_smiley', 'smileyID' ) AS $row )
{
/* We need to figure out where our file lives - if it's remote, then we need to use file_get_contents() and pass the raw data. */
$filepathx2 = NULL;
$filenamex2 = NULL;
$filename = basename( $row['smileyPath'] );
$filepath = rtrim( $this->app->_session['more_info']['convertEmoticons']['emoticon_path'], '/' ) . '/' . $row['smileyPath'];
if( !empty( $row['smileyPath2x'] ) )
{
$filenamex2 = basename( $row['smileyPath2x'] );
$filepathx2 = rtrim( $this->app->_session['more_info']['convertEmoticons']['emoticon_path'], '/' ) . '/' . $row['smileyPath2x'];
}
$category = array(
'display_order' => 1,
);
$title = "Converted";
$set = array(
'set' => md5( $title ),
'title' => $title,
'position' => $category['display_order']
);
$info = array(
'id' => $row['smileyID'],
'typed' => $row['smileyCode'],
'filename' => $filename,
'filenamex2' => $filenamex2,
'emo_position' => $row['showOrder'],
);
$libraryClass->convertEmoticon( $info, $set, $this->app->_session['more_info']['convertEmoticons']['keep_existing_emoticons'], $filepath, NULL, $filepathx2, NULL );
$libraryClass->setLastKeyValue( $row['smileyID'] );
}
}
/**
* Convert groups
*
* @return void
*/
public function convertGroups()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'groupID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_user_group', 'groupID' ) AS $row )
{
/* Username Styles */
$style = explode( '%s', $row['userOnlineMarking'] );
$prefix = isset( $style[0] ) ? $style[0] : '';
$suffix = isset( $style[1] ) ? $style[1] : '';
$info = array(
'g_id' => $row['groupID'],
'g_name' => $this->getLanguage( $row['groupName'] ),
'prefix' => $prefix,
'suffix' => $suffix,
);
$merge = $this->app->_session['more_info']['convertGroups']["map_group_{$row['groupID']}"] != 'none' ? $this->app->_session['more_info']['convertGroups']["map_group_{$row['groupID']}"] : NULL;
$libraryClass->convertGroup( $info, $merge );
$libraryClass->setLastKeyValue( $row['groupID'] );
}
/* Now check for group promotions */
if( \count( $libraryClass->groupPromotions ) )
{
foreach( $libraryClass->groupPromotions as $groupPromotion )
{
$libraryClass->convertGroupPromotion( $groupPromotion );
}
}
}
/**
* Convert members
*
* @return void
*/
public function convertMembers()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'userID' );
$options = iterator_to_array( $this->db->select( 'optionID, optionName', 'wcf' . static::$installId . '_user_option' )->setKeyField( 'optionName' )->setValueField( 'optionID' ) );
/* Profile Fields */
$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'] ) ) ) );
$userProfileOptions = iterator_to_array( $this->db->select( 'optionID, optionName', 'wcf' . static::$installId . '_user_option', $where )->setKeyField('optionID')->setValueField('optionName') );
/* User titles */
$userTitles = [];
foreach( $this->db->select( 'rankTitle', 'wcf' . static::$installId . '_user_rank' ) as $rank )
{
$userTitle[] = $this->getLanguage( $rank );
}
foreach ( $this->fetch( 'wcf' . static::$installId . '_user', 'userID' ) AS $row )
{
$fields = $this->db->select( '*', 'wcf' . static::$installId . '_user_option_value', array( 'userID=?', $row['userID'] ) )->first();
$userFields = array();
foreach( $options as $name => $opt )
{
$userFields[ $name ] = $fields[ 'userOption' . $opt ];
}
/* Is there a birthdate? */
$birthdayYear = $birthdayMonth = $birthdayDay = NULL;
if( !empty( $userFields['birthday'] ) )
{
list( $birthdayYear, $birthdayMonth, $birthdayDay ) = explode( '-', $userFields['birthday'] );
}
/* Member Groups */
$memberGroups = iterator_to_array( $this->db->select( 'groupID', 'wcf' . static::$installId . '_user_to_group', array( 'userID=?', $row['userID'] ) )->setKeyField('groupID') );
unset( $memberGroups[ $row['userOnlineGroupID'] ] );
$info = array(
'member_id' => $row['userID'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'password' => $row['password'],
'member_group_id' => $row['userOnlineGroupID'],
'joined' => $row['registrationDate'],
'member_title' => \in_array( $row['userTitle'], $userTitles ) OR empty( $row['userTitle'] ) ? NULL : $row['userTitle'],
'bday_day' => $birthdayDay,
'bday_month' => $birthdayMonth,
'bday_year' => $birthdayYear,
'last_visit' => $row['lastActivityTime'],
'last_activity' => $row['lastActivityTime'],
'temp_ban' => $row['banned'] ? ( $row['banExpires'] ?: -1 ) : NULL,
'mgroup_others' => $memberGroups,
'signature' => $row['signature'],
'allow_admin_mails' => $userFields['adminCanMail'],
'member_posts' => $row['wbbPosts'],
'auto_track' => $userFields['watchThreadOnReply'] ? array( 'content' => 1, 'comments' => 1, 'method' => 'immediate' ) : NULL,
'members_profile_views' => $row['profileHits']
);
/* Avatars */
$filename = $filepath = NULL;
if( !empty( $row['avatarID'] ) )
{
try
{
$avatar = $this->db->select( '*', 'wcf' . static::$installId . '_user_avatar', array( 'avatarID=?', $row['avatarID'] ) )->first();
$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 ) {}
}
/* Cover Photo */
$coverFileName = $coverFilePath = NULL;
if( !empty( $row['coverPhotoHash'] ) )
{
$coverFileName = $row['userID'] . '-' . $row['coverPhotoHash'] . '.' . $row['coverPhotoExtension'];
$coverFilePath = rtrim( $this->app->_session['more_info']['convertMembers']['coverphoto_location'], '/' ) . '/' . mb_substr( $row['coverPhotoHash'], 0, 2 );
}
/* Profile Fields */
$profileFields = array();
foreach( $userProfileOptions as $k => $v )
{
$profileFields[ $k ] = $userFields[ $v ];
}
$libraryClass->convertMember( $info, $profileFields, $filename, $filepath, NULL, $coverFileName, $coverFilePath );
/* Any friends need converting to followers? */
foreach( $this->db->select( '*', 'wcf' . static::$installId . '_user_follow', array( "userID=?", $row['userID'] ) ) AS $follower )
{
$libraryClass->convertFollow( array(
'follow_app' => 'core',
'follow_area' => 'member',
'follow_rel_id' => $follower['followUserID'],
'follow_rel_id_type' => 'core_members',
'follow_member_id' => $follower['userID'],
'follow_added' => $follower['time']
) );
}
$libraryClass->setLastKeyValue( $row['userID'] );
}
}
/**
* Convert ignored users
*
* @return void
*/
public function convertIgnoredUsers()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'ignoreID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_user_ignore', 'ignoreID' ) AS $ignore )
{
$info = array(
'ignore_id' => $ignore['ignoreID'],
'ignore_owner_id' => $ignore['userID'],
'ignore_ignore_id' => $ignore['ignoreUserID'],
'ignore_messages' => 1,
'ignore_topics' => 1
);
$libraryClass->convertIgnoredUser( $info );
$libraryClass->setLastKeyValue( $ignore['ignoreID'] );
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'rankID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_user_rank', 'rankID', array( 'requiredPoints>0' ) ) AS $row )
{
$libraryClass->convertRank( array(
'id' => $row['rankID'],
'title' => $row['rankTitle'],
'posts' => $row['requiredPoints']
) );
$libraryClass->setLastKeyValue( $row['rankID'] );
}
}
/**
* Convert PMs
*
* @return void
*/
public function convertPrivateMessages()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'conversationID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_conversation', 'conversationID' ) AS $row )
{
$participants = iterator_to_array( $this->db->select( '*', 'wcf' . static::$installId . '_conversation_to_user', array( 'conversationID=?', $row['conversationID'] ) )->setKeyField('participantID') );
$topic = array(
'mt_id' => $row['conversationID'],
'mt_date' => $row['time'],
'mt_title' => $row['subject'],
'mt_starter_id' => $row['userID'],
'mt_start_time' => $row['time'],
'mt_last_post_time' => $row['lastPostTime'],
'mt_to_count' => $row['participants'],
);
$maps = array();
$maps[ $row['userID'] ] = array(
'map_user_id' => $row['userID'],
'map_read_time' => $row['time'],
'map_is_starter' => true
);
unset( $participants[ $row['userID'] ] );
foreach( $participants as $participant )
{
$maps[ $participant['participantID'] ] = array(
'map_user_id' => $participant['participantID'],
'map_read_time' => $participant['lastVisitTime'],
'map_user_active' => $participant['hideConversation'] ? 0 : 1,
);
}
$libraryClass->convertPrivateMessage( $topic, $maps );
$libraryClass->setLastKeyValue( $row['conversationID'] );
}
}
/**
* Convert PM replies
*
* @return void
*/
public function convertPrivateMessageReplies()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'messageID' );
foreach( $this->fetch( 'wcf' . static::$installId . '_conversation_message', 'messageID' ) AS $row )
{
$libraryClass->convertPrivateMessageReply( array(
'msg_id' => $row['messageID'],
'msg_topic_id' => $row['conversationID'],
'msg_date' => $row['time'],
'msg_post' => $row['message'],
'msg_author_id' => $row['userID'],
'msg_ip_address' => $row['ipAddress'],
) );
$libraryClass->setLastKeyValue( $row['messageID'] );
}
}
/**
* @brief Cache default language
*/
static $_defaultLanguage = NULL;
/**
* Helper to fetch a WCF lang string
*
* @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
*/
protected function getLanguage( string $itemName, string $itemPrefix=NULL ): string
{
if( static::$_defaultLanguage === NULL )
{
static::$_defaultLanguage = $this->db->select( 'languageID', 'wcf' . static::$installId . '_language', array( 'isDefault=?', 1 ) )->first();
}
try
{
return $this->db->select( 'languageItemValue', 'wcf' . static::$installId . '_language_item', array( "languageID=? AND languageItem=?", static::$_defaultLanguage, $itemName ) )->first();
}
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 ) { }
return $itemName;
}
}
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
/* If it's not blowfish, then we don't have a salt for it. fail. */
if( preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $member->conv_password ) )
{
$salt = mb_substr( $member->conv_password, 0, 29 );
$test = crypt( crypt( $password, $salt ), $salt );
return \IPS\Login::compareHashes( $member->conv_password, $test );
}
return FALSE;
}
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 12 December 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Wordpress Core Converter
*/
class _Wordpress extends \IPS\convert\Software
{
/**
@@ -92,8 +95,8 @@ class _Wordpress extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -166,9 +169,7 @@ class _Wordpress extends \IPS\convert\Software
'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'] ),
'conv_password' => $user['user_pass'],
'pp_photo_type' => 'gravatar',
'pp_gravatar' => $user['user_email'], # WordPress always uses gravatar, as it's their service
'conv_password' => $user['user_pass']
);
$fields = array();
@@ -188,11 +189,10 @@ class _Wordpress extends \IPS\convert\Software
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
) );
}
@@ -5,31 +5,49 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Core;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo Core Converter
*/
class _Xenforo extends \IPS\convert\Software
{
use \IPS\convert\Tools\Xenforo;
/**
* @brief The similarities between XF1 and XF2 are close enough that we can use the same converter
*/
public static $isLegacy = NULL;
/**
* @brief XF2.1 changed serialized data to json decoded
*/
public static $useJson = FALSE;
/**
* @brief XF1&2 have different pseudo fields
*/
public static $pseudoFields = array(
1 => array( 'homepage', 'location', 'occupation', 'about' ),
2 => array( 'website', 'location', 'about' )
);
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -44,7 +62,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( "addon_id=?", 'XF' ) )->first();
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( \IPS\Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
if ( $version < 2000010 )
{
@@ -53,6 +71,12 @@ class _Xenforo extends \IPS\convert\Software
else
{
static::$isLegacy = FALSE;
/* Is this XF 2.1 */
if ( $version > 2010010 )
{
static::$useJson = TRUE;
}
}
}
}
@@ -69,7 +93,7 @@ class _Xenforo extends \IPS\convert\Software
*/
public static function softwareName()
{
return "XenForo (1.5.x/2.0.x)";
return "XenForo (1.5.x/2.0.x/2.1.x)";
}
/**
@@ -109,7 +133,7 @@ class _Xenforo extends \IPS\convert\Software
*/
public static function canConvert()
{
return array(
$return = array(
'convertEmoticons' => array(
'table' => 'xf_smilie',
'where' => NULL
@@ -127,7 +151,7 @@ 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(
@@ -163,6 +187,17 @@ class _Xenforo extends \IPS\convert\Software
'where' => NULL
)
);
/* XF 2.1 */
if( static::$useJson )
{
$return['convertReactions'] = array(
'table' => 'xf_reaction',
'where' => NULL
);
}
return $return;
}
/**
@@ -170,7 +205,7 @@ class _Xenforo extends \IPS\convert\Software
*
* @param string $table The table containing the rows to count.
* @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)
* @param bool $recache Skip cache and pull directly (updating cache)
* @return integer
* @throws \IPS\convert\Exception
*/
@@ -180,11 +215,25 @@ class _Xenforo extends \IPS\convert\Software
{
/* XF1 */
case 'xf_user_change_log':
return $this->db->select( 'count(log_id)', 'xf_user_change_log', array( $this->db->in( 'field', array_keys( static::$changeLogTypes ) ) ) )->first();
try
{
return $this->db->select( 'count(log_id)', 'xf_user_change_log', array( $this->db->in( 'field', array_keys( static::$changeLogTypes ) ) ) )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
/* XF2 */
case 'xf_change_log':
return $this->db->select( 'count(log_id)', 'xf_change_log', array( 'content_type=? AND ' . $this->db->in( 'field', array_keys( static::$changeLogTypes ) ), 'user' ) )->first();
try
{
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 )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
@@ -220,13 +269,21 @@ class _Xenforo extends \IPS\convert\Software
*/
public static function checkConf()
{
return array(
$return = array(
'convertAttachments',
'convertEmoticons',
'convertProfileFields',
'convertGroups',
'convertMembers',
);
/* XF 2.1 */
if( static::$useJson )
{
$return[] = 'convertReactions';
}
return $return;
}
/**
@@ -339,7 +396,7 @@ class _Xenforo extends \IPS\convert\Software
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
);
foreach( array( 'homepage', 'location', 'occupation', 'about', 'gender' ) AS $field )
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' );
@@ -358,6 +415,47 @@ class _Xenforo extends \IPS\convert\Software
);
}
break;
case 'convertReactions':
$return['convertReactions'] = array();
/* XF2.1 */
if ( !static::$isLegacy )
{
$reactions = array();
foreach( iterator_to_array( $this->db->select( '*', 'xf_reaction' ) ) as $reaction )
{
$title = $this->getPhrase( '', 'reaction_title.' . $reaction['reaction_id'] );
if( !empty( $reaction['text_color'] ) AND mb_substr( $reaction['text_color'], 0, 1 ) === '#' )
{
$title = "<span style='color:{$reaction['text_color']}'>{$title}</span>";
}
$reactions[ $reaction['reaction_id'] ] = array(
'title' => $title,
'icon' => FALSE
);
}
$return['convertReactions']['reactions'] = array(
'field_class' => 'IPS\\convert\\Form\\Reactionmapper',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'reactions' => $reactions ),
'field_hint' => NULL,
'field_validation' => NULL,
);
/* 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' ) ) );
$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' ); } },
);
}
break;
}
return ( isset( $return[ $method ] ) ) ? $return[ $method ] : array();
@@ -407,25 +505,6 @@ class _Xenforo extends \IPS\convert\Software
return $settings;
}
/**
* Helper to fetch a xenforo phrase
*
* @param string $xfOneTitle XF1 Phrase title
* @param string $xfTwoTitle XF2 Phrase Title
* @return string|null
*/
protected function getPhrase( $xfOneTitle, $xfTwoTitle )
{
try
{
$title = ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
return $this->db->select( 'phrase_text', 'xf_phrase', array( "title=?", $title ) )->first();
}
catch( \UnderflowException $e )
{
return NULL;
}
}
/**
* Finish
@@ -464,8 +543,8 @@ class _Xenforo extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -473,17 +552,21 @@ class _Xenforo extends \IPS\convert\Software
$post = htmlspecialchars( $post, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE );
// find YouTube ID's and replace.
$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 );
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 );
}
/* Quick check for the presence of user tags */
if( mb_stristr( $post, '[user=' ) )
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] ) )
{
$mentions = array();
@@ -491,34 +574,31 @@ class _Xenforo extends \IPS\convert\Software
{
if ( isset( $matches[2][ $k ] ) )
{
$name = trim( $matches[2][ $k ], '@' );
$mentions[ $name ] = $v;
$mentions[ trim( $matches[2][ $k ], '@' ) ] = $v;
}
}
$maps = array();
$urls = array();
$cardUrls = array();
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members', array( \IPS\Db::i()->in( 'name', array_keys( $mentions ) )) ), 'IPS\Member' ) AS $member )
{
$maps[ $member->name ] = $member->member_id;
$urls[ $member->name ] = $member->url();
$cardUrls[ $member->name ] = $member->url()->setQueryString( 'do', 'hovercard' );
}
$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' ) );
foreach ( $mentions AS $member_name => $member_id )
foreach ( $mentions AS $memberName => $memberId )
{
$maps[ $member_name ] = preg_quote( $maps[ $member_name ], '#' );
$memberNameEscaped = preg_quote( $member_name, '#' );
if ( !isset( $maps[ $memberName ] ) )
{
continue;
}
$post = preg_replace( "#\[user={$member_id}\]@?{$memberNameEscaped}\[\/user\]#i", "<a contenteditable=\"false\" rel=\"\" href=\"{$urls[ $member_name ]}\" data-mentionid=\"{$maps[ $member_name ]}\" data-ipshover-target=\"{$cardUrls[ $member_name ]}\" data-ipshover=\"\">@{$member_name}</a>", $post );
$memberNameQuoted = preg_quote( $memberName, '#' );
$post = preg_replace( "#\[user={$memberId}\]@?{$memberNameQuoted}\[\/user\]#i", "[mention={$maps[ $memberName ]}]{$memberName}[/mention]", $post );
}
}
}
}
/* Quotes */
$post = preg_replace( '#\[quote=&quot;([a-zA-Z0-9]+)(,(.?)+)?&quot;]#i', "[quote name='$1']", $post );
if( \preg_match( "#\[quote=#i", $post ) )
{
$post = preg_replace( '#\[quote=&quot;([a-zA-Z0-9-_ ]+)(,(.?)+)?&quot;]#i', "[quote name='$1']", $post );
}
// finally, give us the post back.
return $post;
@@ -579,9 +659,9 @@ class _Xenforo extends \IPS\convert\Software
$post = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', array( "msg_id=?", $messagePostId ) )->first();
if ( preg_match( "/\[ATTACH(.+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", $post ) )
if ( preg_match( "/\[ATTACH([^\]]+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH(.+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", '[attachment=' . $attachId . ':name]', $post );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", '[attachment=' . $attachId . ':name]', $post );
}
\IPS\Db::i()->update( 'core_message_posts', array( 'msg_post' => $post ), array( "msg_id=?", $messagePostId ) );
@@ -617,12 +697,21 @@ class _Xenforo extends \IPS\convert\Software
/* Sprites */
if( $row['sprite_mode'] )
{
$images = $this->_emoticonsFromSprite( rtrim( $this->app->_session['more_info']['convertEmoticons']['emoticon_path'], '/' ) . '/' . $row['image_url'], \unserialize( $row['sprite_params'] ) );
$filedata = $images['emoticon'];
if( isset( $images['emoticon_x2'] ) )
try
{
$filedatax2 = $images['emoticon_x2'];
$images = $this->_imageFromSprite( rtrim( $this->app->_session['more_info']['convertEmoticons']['emoticon_path'], '/' ) . '/' . $row['image_url'], static::unpack( $row['sprite_params'] ) );
}
catch( \InvalidArgumentException | \OutOfRangeException $e )
{
$this->app->log( 'xf_emo_sprite_convert_invalid_image', __METHOD__, \IPS\convert\App::LOG_WARNING, $row['smilie_id'] );
$libraryClass->setLastKeyValue( $row['smilie_id'] );
continue;
}
$filedata = $images['image'];
if( isset( $images['image_x2'] ) )
{
$filedatax2 = $images['image_x2'];
/* We always save the sprites to PNG */
$filenamex2 = "{$row['title']}.png";
@@ -668,7 +757,7 @@ 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;
@@ -720,14 +809,16 @@ 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' ) ) ) ? \unserialize( $row['field_choices'] ) : 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 ) ? 0 : 1;
$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'] = ( $row['match_type'] == 'regex' AND $row['match_regex'] ) ? '/' . $row['match_regex'] . '/i' : NULL;
$info['pf_input_format'] = static::$isLegacy ?
( ( $row['match_type'] == 'regex' AND $row['match_regex'] ) ? '/' . $row['match_regex'] . '/i' : NULL ) :
( ( $row['match_type'] == 'regex' AND $params = json_decode( $row['match_params'], TRUE ) AND $params['regex'] ) ? '/' . $params['regex'] . '/i' : NULL );
$info['pf_multiple'] = ( $row['field_type'] == 'multiselect' ) ? 1 : 0;
$libraryClass->convertProfileField( $info, $merge );
@@ -737,7 +828,7 @@ class _Xenforo extends \IPS\convert\Software
/**
* Maps the XenForo field ttype to IPS field type
*
* @param string XF Field Type
* @param string $type XF Field Type
* @return string IPS Field Type
*/
protected function _fieldMap( $type )
@@ -835,7 +926,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 )
{
@@ -892,7 +983,7 @@ class _Xenforo extends \IPS\convert\Software
/**
* @brief Member history type map
*/
protected static $changeLogTypes = array( 'username' => 'display_name', 'email' => 'email_change', 'user_group_id' => 'member_group_id', 'secondary_group_ids' => 'mgroup_others' );
protected static $changeLogTypes = array( 'username' => 'display_name', 'email' => 'email_change', 'user_group_id' => 'group', 'secondary_group_ids' => 'group' );
/**
* Convert member history
@@ -908,7 +999,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';
@@ -921,16 +1012,23 @@ class _Xenforo extends \IPS\convert\Software
foreach( $this->fetch( $table, 'log_id', $where ) AS $change )
{
$libraryClass->convertMemberHistory( array(
'log_id' => $change['log_id'],
'log_member' => $change[ $userField ],
'log_by' => ( $change['edit_user_id'] !== $change[ $userField ] ) ? $change['edit_user_id'] : null,
'log_type' => static::$changeLogTypes[ $change['field'] ],
'log_data' => array( 'old' => $change['old_value'], 'new' => $change['new_value'] ),
'log_date' => $change['edit_date']
)
$info = array(
'log_id' => $change['log_id'],
'log_member' => $change[ $userField ],
'log_by' => ( $change['edit_user_id'] !== $change[ $userField ] ) ? $change['edit_user_id'] : null,
'log_type' => static::$changeLogTypes[ $change['field'] ],
'log_data' => array( 'old' => $change['old_value'], 'new' => $change['new_value'] ),
'log_date' => $change['edit_date']
);
/* Add extra data for group change */
if( static::$changeLogTypes[ $change['field'] ] == 'group' )
{
$info['log_data']['type'] = $change['field'] == 'user_group_id' ? 'primary' : 'secondary';
$info['log_data']['by'] = 'manual';
}
$libraryClass->convertMemberHistory( $info );
$libraryClass->setLastKeyValue( $change['log_id'] );
}
}
@@ -1024,7 +1122,7 @@ class _Xenforo extends \IPS\convert\Software
/* Auto Track */
$auto_track = 0;
if ( $row['default_watch_state'] == 'watch_email' )
if ( $row['creation_watch_state'] == 'watch_email' )
{
$auto_track = array(
'content' => 1,
@@ -1061,7 +1159,7 @@ class _Xenforo extends \IPS\convert\Software
/* Main Member Information */
$info = array(
'member_id' => $row['user_id'],
'name' => $row['username'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'conv_password' => $hash,
'conv_password_extra' => $salt,
@@ -1084,7 +1182,7 @@ class _Xenforo extends \IPS\convert\Software
'view_sigs' => $row['content_show_signature']
),
'pp_setting_count_comments' => 1, # always on for XF
'pp_reputation_points' => $row['like_count'],
'pp_reputation_points' => ( static::$useJson ) ? $row['reaction_score'] : $row['like_count'],
'timezone' => $timezone,
'allow_admin_mails' => $row['receive_admin_email'],
'member_title' => $row['custom_title'],
@@ -1102,14 +1200,6 @@ class _Xenforo extends \IPS\convert\Software
$filename = $row['user_id'] . '.jpg';
$filepath = $path . '/l/' . $group;
}
else
{
/* Got a gravatar? */
if ( $row['gravatar'] )
{
$info['pp_gravatar'] = $row['gravatar'];
}
}
/* Profile Fields */
$pfields = array();
@@ -1119,7 +1209,7 @@ class _Xenforo extends \IPS\convert\Software
}
/* Pseudo Fields */
foreach( array( 'homepage', 'location', 'occupation', 'about', 'gender' ) AS $pseudo )
foreach( static::$pseudoFields[ static::$isLegacy ? 1 : 2 ] AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -1149,7 +1239,7 @@ class _Xenforo extends \IPS\convert\Software
) );
}
$pfields[$pseudo] = $row[$pseudo];
$pfields[ $pseudo ] = $row[ $pseudo ];
}
$libraryClass->convertMember( $info, $pfields, $filename, $filepath );
@@ -1159,7 +1249,7 @@ class _Xenforo extends \IPS\convert\Software
{
$libraryClass->convertFollow( array(
'follow_app' => 'core',
'follow_area' => 'members',
'follow_area' => 'member',
'follow_rel_id' => $follower['follow_user_id'],
'follow_rel_id_type' => 'core_members',
'follow_member_id' => $follower['user_id'],
@@ -1246,7 +1336,24 @@ class _Xenforo extends \IPS\convert\Software
'status_approved' => $approved,
);
$libraryClass->convertStatus( $info );
$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'] );
}
@@ -1329,7 +1436,24 @@ class _Xenforo extends \IPS\convert\Software
'reply_ip_address' => $ip,
);
$libraryClass->convertStatusReply( $info );
$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'] );
}
@@ -1377,6 +1501,89 @@ class _Xenforo extends \IPS\convert\Software
}
}
/**
* Convert reactions
*
* @return void
*/
public function convertReactions()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'reaction_id' );
foreach( $this->fetch( 'xf_reaction', 'reaction_id' ) AS $row )
{
/* We need to figure out where our file lives - if it's remote, then we need to use file_get_contents() and pass the raw data. */
$filepath = NULL;
$filedata = NULL;
$filepathx2 = NULL;
$filedatax2 = NULL;
$filenamex2 = NULL;
$title = $this->getPhrase( '', 'reaction_title.' . $row['reaction_id'] );
/* Sprites */
if( $row['sprite_mode'] )
{
try
{
$images = $this->_imageFromSprite( rtrim( $this->app->_session['more_info']['convertReactions']['reaction_path'], '/' ) . '/' . $row['image_url'], static::unpack( $row['sprite_params'] ) );
}
catch( \InvalidArgumentException | \OutOfRangeException $e )
{
$this->app->log( 'xf_sprite_convert_invalid_image', __METHOD__, \IPS\convert\App::LOG_WARNING, $row['reaction_id'] );
$libraryClass->setLastKeyValue( $row['reaction_id'] );
continue;
}
$filedata = $images['image'];
if( isset( $images['image_x2'] ) )
{
$filedatax2 = $images['image_x2'];
}
/* We always save the sprites to PNG */
$filename = \IPS\Http\Url\Friendly::seoTitle( $title ) . ".png";
}
elseif ( mb_substr( $row['image_url'], 0, 4 ) == 'http' )
{
$filedata = file_get_contents( $row['image_url'] );
$fileurl = explode( '/', $row['image_url'] );
$filename = array_pop( $fileurl );
if( !empty( $row['image_url_2x'] ) )
{
$filedata = file_get_contents( $row['image_url_2x'] );
}
}
else
{
$fileurl = explode( '/', $row['image_url'] );
$filename = array_pop( $fileurl );
$filepath = rtrim( $this->app->_session['more_info']['convertReactions']['reaction_path'], '/' ) . '/' . implode( '/', $fileurl );
if( !empty( $row['image_url_2x'] ) )
{
$fileurlx2 = explode( '/', $row['image_url_2x'] );
$filepathx2 = rtrim( $this->app->_session['more_info']['convertReactions']['reaction_path'], '/' ) . '/' . implode( '/', $fileurlx2 );
}
}
$info = array(
'reaction_id' => $row['reaction_id'],
'reaction_title' => $title,
'filename' => $filename,
'reaction_value' => $row['reaction_score'],
'reaction_position' => $row['display_order'] / 100,
'reaction_enabled' => $row['active']
);
$merge = ( $this->app->_session['more_info']['convertReactions']['reactions'][ $row['reaction_id'] ] != 'none' ) ? $this->app->_session['more_info']['convertReactions']['reactions'][ $row['reaction_id'] ] : NULL;
$libraryClass->convertReaction( $info, $filepathx2 ?? $filepath, $filedatax2 ?? $filedata, $merge );
$libraryClass->setLastKeyValue( $row['reaction_id'] );
}
}
/**
* Convert private messages
*
@@ -1476,8 +1683,7 @@ 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
* @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.
* @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()
{
@@ -1503,9 +1709,10 @@ class _Xenforo extends \IPS\convert\Software
}
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'proxy.php' ) !== FALSE AND isset( \IPS\Request::i()->image ) )
{
return \IPS\Http\Url::internal( "applications/core/interface/imageproxy/imageproxy.php", 'none' )->setQueryString( array(
'img' => \IPS\Request::i()->image,
'key' => hash_hmac( "sha256", (string) \IPS\Request::i()->image, \IPS\Settings::i()->site_secret_key )
return \IPS\Http\Url::internal( "app=core&module=system&controller=redirect", 'front' )->setQueryString( array(
'url' => \IPS\Request::i()->image,
'resource' => TRUE,
'key' => hash_hmac( "sha256", (string) \IPS\Request::i()->image, \IPS\Settings::i()->site_secret_key . 'r' )
) );
}
@@ -1533,7 +1740,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 );
}
@@ -1550,64 +1757,4 @@ class _Xenforo extends \IPS\convert\Software
// 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 );
}
/**
* @brief Cache sprite image objects
*/
protected $_spriteImages = array();
/**
* Return image from sprite - We must use GD for this, it's generally available on most servers
* And we don't have any relevant methods in either image handler for doing this completly within
* the image class
*
* @param string $sprite Sprite path
* @param array $spriteParams Sprite parameters
* @return array
*/
protected function _emoticonsFromSprite( $sprite, $spriteParams )
{
$key = md5( $sprite );
/* Set up image canvas */
if( !isset( $this->_spriteImages[ $key ] ) )
{
if( !file_exists( $sprite ) )
{
return array( 'emoticon' => NULL );
}
$this->_spriteImages[ $key ] = new \IPS\Image\Gd( file_get_contents( $sprite ) );
}
/* x2 Emoticon? */
$multiplier = 1;
if( $this->_spriteImages[ $key ]->width > $spriteParams['w'] )
{
$multiplier = 2;
}
$emoticon = \IPS\Image\Gd::newImageCanvas( $spriteParams['w'] * $multiplier, $spriteParams['h'] * $multiplier, array( 0, 0, 0 ) );
/* Set the background to transparent */
imagefill( $emoticon->image, 0, 0, imagecolorallocatealpha( $emoticon->image, 0, 0, 0, 127 ) );
/* Extract sprite */
imagecopy( $emoticon->image, $this->_spriteImages[ $key ]->image, 0, 0, abs( $spriteParams['x'] ) * $multiplier, abs( $spriteParams['y'] ) * $multiplier, $spriteParams['w'] * $multiplier, $spriteParams['h'] * $multiplier );
/* x2 Emoticon? */
$return = array();
if( $multiplier == 2 )
{
$return['emoticon_x2'] = (string) $emoticon;
/* Resize */
$emoticon->resize( $spriteParams['w'], $spriteParams['h'] );
}
$return['emoticon'] = (string) $emoticon;
unset( $emoticon );
return $return;
}
}
@@ -5,21 +5,26 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Downloads;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo Downloads Converter
*/
class _Xenforo extends \IPS\convert\Software
{
use \IPS\convert\Tools\Xenforo;
/**
* @brief The similarities between XF1 and XF2 are close enough that we can use the same converter
*/
@@ -30,11 +35,16 @@ class _Xenforo extends \IPS\convert\Software
*/
public static $tablePrefix = '';
/**
* @brief XF2.1 changed serialized data to json decoded
*/
public static $useJson = FALSE;
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -49,7 +59,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( "addon_id=?", 'XF' ) )->first();
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( \IPS\Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
if ( $version < 2000010 )
{
@@ -59,6 +69,12 @@ class _Xenforo extends \IPS\convert\Software
{
static::$tablePrefix = 'rm_';
static::$isLegacy = FALSE;
/* Is this XF 2.1 */
if ( $version > 2010010 )
{
static::$useJson = TRUE;
}
}
}
}
@@ -76,7 +92,7 @@ class _Xenforo extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "XenForo Resource Manager (1.5.x/2.0.x)";
return "XenForo Resource Manager (1.5.x/2.0.x/2.1.x)";
}
/**
@@ -241,7 +257,7 @@ class _Xenforo extends \IPS\convert\Software
{
try
{
$title = ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
$title = ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
return $this->db->select( 'phrase_text', 'xf_phrase', array( "title=?", $title ) )->first();
}
catch( \UnderflowException $e )
@@ -275,12 +291,12 @@ class _Xenforo extends \IPS\convert\Software
$info = array(
'cf_id' => $row['field_id'],
'cf_type' => $this->_mapFieldType( $row['field_type'], $row['match_type'] ),
'cf_content' => static::$isLegacy ? \unserialize( $row['field_choices'] ) : json_decode( $row['field_choices'], TRUE ),
'cf_content' => static::unpack( $row['field_choices'] ),
'cf_not_null' => $row['required'],
'cf_max_input' => $row['max_length'],
'cf_input_format' => $inputFormat,
'cf_position' => $row['display_order'],
'cf_multiple' => ( in_array( $row['field_type'], array( 'checkbox', 'multiselect' ) ) ) ? 1 : 0
'cf_multiple' => ( \in_array( $row['field_type'], array( 'checkbox', 'multiselect' ) ) ) ? 1 : 0
);
$libraryClass->convertDownloadsCfield( $info );
@@ -290,6 +306,8 @@ class _Xenforo extends \IPS\convert\Software
/**
* Map the field types
*
* @param string $type Custom field type
* @param string $match Match type
* @return void
*/
protected function _mapFieldType( $type, $match )
@@ -363,7 +381,7 @@ class _Xenforo extends \IPS\convert\Software
{
$prefix = $this->getPhrase( "thread_prefix_{$row['thread_prefix_id']}", "thread_prefix.{$row['thread_prefix_id']}" );
if ( is_null( $prefix ) )
if ( \is_null( $prefix ) )
{
$prefix = '';
}
@@ -516,7 +534,7 @@ class _Xenforo extends \IPS\convert\Software
{
$field_type = $this->db->select( 'field_type', 'xf_' . static::$tablePrefix . 'resource_field', array( "field_id=?", $key ) )->first();
if ( in_array( $field_type, array( 'checkbox', 'multiselect' ) ) )
if ( \in_array( $field_type, array( 'checkbox', 'multiselect' ) ) )
{
$cfields[$key] = static::$isLegacy ? json_encode( \unserialize( $value ) ) : $value;
}
@@ -526,7 +544,7 @@ class _Xenforo extends \IPS\convert\Software
}
}
$libraryClass->convertDownloadsFile( $info, $records, $cfields );
$id = $libraryClass->convertDownloadsFile( $info, $records, $cfields );
/* Follows */
foreach( $this->db->select( '*', 'xf_' . static::$tablePrefix . 'resource_watch', array( "resource_id=?", $row['resource_id'] ) ) AS $follow )
@@ -546,6 +564,23 @@ class _Xenforo extends \IPS\convert\Software
'follow_visible' => 1
) );
}
if( static::$useJson AND $id )
{
foreach( $this->db->select( '*', 'xf_reaction_content', array( "content_type=? AND content_id=?", 'resource_update', $row['resource_id'] ) ) as $reaction )
{
$libraryClass->convertReputation( array(
'id' => $reaction['reaction_content_id'],
'app' => 'downloads',
'type' => 'file_id',
'type_id' => $row['resource_id'],
'member_id' => $reaction['reaction_user_id'],
'member_received' => $row['user_id'],
'reaction' => $reaction['reaction_id'],
'rep_date' => $reaction['reaction_date']
) );
}
}
$libraryClass->setLastKeyValue( $row['resource_id'] );
}
@@ -5,18 +5,21 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Converter software exception
*/
class _Exception extends \OutOfRangeException
{}
@@ -1,23 +1,26 @@
<?php
/**
* @brief Converter BBPress Class
* @brief Converter bbPress Class
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Nov 2016
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* BBPress Forums Converter
*/
class _Bbpress extends \IPS\convert\Software
{
/**
@@ -28,7 +31,7 @@ class _Bbpress extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "BBPress (for WordPress)";
return "bbPress (for WordPress)";
}
/**
@@ -93,11 +96,11 @@ class _Bbpress extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics" );
}
@@ -129,9 +132,7 @@ class _Bbpress extends \IPS\convert\Software
}
/**
* User cache to minimise repeated DB lookups
*
* @var array $userCache
* @brief User cache to minimise repeated DB lookups
*/
protected static $userCache = array();
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 17 June 2016
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* ExpressionEngine Forums Converter
*/
class _Expressionengine extends \IPS\convert\Software
{
/**
@@ -77,14 +80,21 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
$rows['convertOtherPosts'] = $rows['convertForumsPosts'];
$rows['convertOtherPosts']['step_method'] = 'convertOtherPosts';
$rows['convertOtherPosts']['source_rows'] = array( 'table' => static::canConvert()['convertOtherPosts']['table'], 'where' => static::canConvert()['convertOtherPosts']['where'] );
$rows = array();
$rows['convertOtherPosts'] = array(
'step_title' => 'convert_forums_posts',
'step_method' => 'convertOtherPosts',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'forums_posts' ),
'source_rows' => array( 'table' => static::canConvert()['convertOtherPosts']['table'], 'where' => static::canConvert()['convertOtherPosts']['where'] ),
'per_cycle' => 200,
'dependencies' => array( 'convertForumsPosts' ),
'link_type' => 'forums_posts',
'requires_rebuild' => TRUE
);
return $rows;
}
@@ -152,16 +162,16 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return string Message to display
* @return array Messages to display
*/
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics" );
}
@@ -169,8 +179,8 @@ class _Expressionengine extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 26 July 2017
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invision Forums Converter
*/
class _Invisioncommunity extends \IPS\convert\Software
{
/**
@@ -30,6 +33,14 @@ class _Invisioncommunity extends \IPS\convert\Software
*/
public static $dbNeeded = FALSE;
/**
* 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 )
{
/* Set filename obscuring flag */
@@ -50,7 +61,7 @@ class _Invisioncommunity extends \IPS\convert\Software
}
/* Get parent sauce */
$this->parent = $this->app->get__parent()->getSource();
$this->parent = $this->app->_parent->getSource();
}
return $return;
@@ -108,11 +119,11 @@ class _Invisioncommunity extends \IPS\convert\Software
'where' => NULL,
),
'convertForumsRssImports' => array(
'table' => 'forums_rss_import',
'where' => NULL,
'table' => 'core_rss_import',
'where' => array( 'rss_import_class=?', 'IPS\\forums\\Topic' ),
),
'convertForumsRssImported' => array(
'table' => 'forums_rss_imported',
'table' => 'core_rss_imported',
'where' => NULL,
),
'convertForumsQuestionRatings' => array(
@@ -130,6 +141,36 @@ class _Invisioncommunity extends \IPS\convert\Software
);
}
/**
* Count Source Rows for a specific step
*
* @param string $table The table containing the rows to count.
* @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
*/
public function countRows( $table, $where=NULL, $recache=FALSE )
{
switch( $table )
{
case 'core_rss_imported':
try
{
return $this->db->select( 'COUNT(*)', 'core_rss_imported', array( 'rss_imported_import_id IN(' . (string) \IPS\Db::i()->select( 'rss_import_id', 'core_rss_import', array( "rss_import_class='IPS\\forums\\Topic'" ) ) . ')' ) )->first() ;
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
return parent::countRows( $table, $where, $recache );
break;
}
}
/**
* Requires Parent
*
@@ -201,10 +242,10 @@ class _Invisioncommunity extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
\IPS\Task::queue( 'convert', 'InvisionCommunityRebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
/* Caches */
@@ -217,6 +258,26 @@ class _Invisioncommunity extends \IPS\convert\Software
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics", "f_topic_tags_recount" );
}
/**
* Convert question ratings
*
* @return void
*/
public function convertForumsAnswerRatings()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'id' );
foreach( $this->fetch( 'forums_answer_ratings', 'id' ) AS $row )
{
/* Remove non-standard columns */
$this->parent->unsetNonStandardColumns( $row, 'forums_answer_ratings', 'forums' );
$libraryClass->convertForumsAnswerRating( $row );
$libraryClass->setLastKeyValue( $row['id'] );
}
}
/**
* Convert forums
*
@@ -236,6 +297,10 @@ class _Invisioncommunity extends \IPS\convert\Software
$row['name'] = $this->parent->getWord( 'forums_forum_' . $row['id'] );
$row['description'] = $this->parent->getWord( 'forums_forum_' . $row['id'] . '_desc' );
/* Rename bit options so we can pass them through the conversion method */
$row['ips_forums_bitoptions'] = $row['forums_bitoptions'];
unset( $row['forums_bitoptions'] );
$libraryClass->convertForumsForum( $row );
/* Convert Follows */
@@ -323,6 +388,10 @@ class _Invisioncommunity extends \IPS\convert\Software
/* Remove non-standard columns */
$this->parent->unsetNonStandardColumns( $row, 'forums_posts', 'forums' );
/* Rename bit options so we can pass them through the conversion method */
$row['ips_post_bwoptions'] = $row['post_bwoptions'];
unset( $row['post_bwoptions'] );
$libraryClass->convertForumsPost( $row );
/* Convert Edit History */
@@ -347,6 +416,26 @@ class _Invisioncommunity extends \IPS\convert\Software
}
}
/**
* Convert question ratings
*
* @return void
*/
public function convertForumsQuestionRatings()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'id' );
foreach( $this->fetch( 'forums_question_ratings', 'id' ) AS $row )
{
/* Remove non-standard columns */
$this->parent->unsetNonStandardColumns( $row, 'forums_question_ratings', 'forums' );
$libraryClass->convertForumsQuestionRating( $row );
$libraryClass->setLastKeyValue( $row['id'] );
}
}
/**
* Convert topics mmod
*
@@ -380,10 +469,13 @@ class _Invisioncommunity extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'rss_import_id' );
foreach( $this->fetch( 'forums_rss_import', 'rss_import_id' ) AS $row )
foreach( $this->fetch( 'core_rss_import', 'rss_import_id', array( 'rss_import_class=?', 'IPS\\forums\\Topic' ) ) AS $row )
{
/* Remove non-standard columns */
$this->parent->unsetNonStandardColumns( $row, 'forums_rss_import', 'forums' );
$this->parent->unsetNonStandardColumns( $row, 'core_rss_import', 'core' );
/* JSON Decode settings (we expect an array) */
$row['rss_import_settings'] = json_decode( $row['rss_import_settings'], TRUE );
$libraryClass->convertForumsRssImport( $row );
$libraryClass->setLastKeyValue( $row['rss_import_id'] );
@@ -399,10 +491,10 @@ class _Invisioncommunity extends \IPS\convert\Software
{
$libraryClass = $this->getLibrary();
foreach( $this->fetch( 'forums_rss_imported', 'rss_imported_tid' ) AS $row )
foreach( $this->fetch( 'core_rss_imported', 'rss_imported_content_id', array( 'rss_imported_id IN(' . (string) \IPS\Db::i()->select( 'rss_import_id', 'core_rss_import', array( "rss_import_class='IPS\\forums\\Topic'" ) ) . ')' ) ) AS $row )
{
/* Remove non-standard columns */
$this->parent->unsetNonStandardColumns( $row, 'forums_rss_imported', 'forums' );
$this->parent->unsetNonStandardColumns( $row, 'core_rss_imported', 'core' );
$libraryClass->convertForumsRssImported( $row );
}
@@ -459,7 +551,15 @@ class _Invisioncommunity extends \IPS\convert\Software
{
$url = \IPS\Request::i()->url();
if( preg_match( '#/ips4merge/(forum|topic)/([0-9]+)-(.+?)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
if( !\stristr( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'ic-merge-' . $this->app->_parent->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 ];
if( preg_match( '#/(forum|topic)/([0-9]+)-(.+?)#i', $searchOn, $matches ) )
{
$oldId = (int) $matches[2];
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* MyBB Forums Converter
*/
class _Mybb extends \IPS\convert\Software
{
/**
@@ -129,7 +132,7 @@ class _Mybb extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
@@ -137,7 +140,7 @@ class _Mybb extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
@@ -169,11 +172,11 @@ class _Mybb extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
/* Rebuild Leaderboard */
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
@@ -188,8 +191,8 @@ class _Mybb extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @return string Fixed Posts
* @param string $post Post
* @return string Fixed Post
*/
public static function fixPostData( $post )
{
@@ -574,7 +577,26 @@ class _Mybb extends \IPS\convert\Software
'attach_filesize' => $row['filesize'],
);
$libraryClass->convertAttachment( $info, $map, rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $row['attachname'] );
$attachId = $libraryClass->convertAttachment( $info, $map, rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $row['attachname'] );
/* Update Post if we can */
try
{
if ( $attachId !== FALSE )
{
$pid = $this->app->getLink( $row['pid'], 'forums_posts' );
$post = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
if( \preg_match( '#\[attachment=' . $row['aid'] . ']#i', $post ) )
{
$post = str_ireplace( '[attachment=' . $row['aid'] . ']', '[attachment=' . $attachId . ':name]', $post );
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $post ), array( "pid=?", $pid ) );
}
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
$libraryClass->setLastKeyValue( $row['aid'] );
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PhpBB Forums Converter
*/
class _Phpbb extends \IPS\convert\Software
{
/**
@@ -159,11 +162,11 @@ class _Phpbb extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics" );
}
@@ -171,8 +174,8 @@ class _Phpbb extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -5,7 +5,7 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 6 December 2016
* @note Only redirect scripts are supported right now
*/
@@ -13,12 +13,15 @@
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PhpMyForum Forums Converter
*/
class _Phpmyforum extends \IPS\convert\Software
{
/**
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* PunBB Forums Converter
*/
class _Punbb extends \IPS\convert\Software
{
/**
@@ -93,11 +96,11 @@ class _Punbb extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics" );
}
@@ -105,8 +108,8 @@ class _Punbb extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -229,7 +232,7 @@ class _Punbb extends \IPS\convert\Software
'last_poster_name' => $row['last_poster'],
'pinned' => ( $row['sticky'] OR $row['announcement'] ) ? 1 : 0,
'moved_to' => $moved_to,
'moved_on' => ( !is_null( $moved_to ) ) ? $row['posted'] : 0,
'moved_on' => ( !\is_null( $moved_to ) ) ? $row['posted'] : 0,
);
$libraryClass->convertForumsTopic( $info );
@@ -5,7 +5,7 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
* @todo SMF supports karma which we should be able to convert to reactions (reputation), but we need a sample database that used karma
*/
@@ -13,12 +13,15 @@
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* SMF Forums Converter
*/
class _Smf extends \IPS\convert\Software
{
/**
@@ -78,14 +81,20 @@ class _Smf extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
$rows['convertForumsBoards'] = $rows['convertForumsForums'];
$rows['convertForumsBoards']['step_method'] = 'convertForumsBoards';
$rows['convertForumsBoards']['source_rows'] = array( 'table' => static::canConvert()['convertForumsBoards']['table'], 'where' => static::canConvert()['convertForumsBoards']['where'] );
$rows = array();
$rows['convertForumsBoards'] = array(
'step_title' => 'convert_forums_forums',
'step_method' => 'convertForumsBoards',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'forums_forums' ),
'source_rows' => array( 'table' => static::canConvert()['convertForumsBoards']['table'], 'where' => static::canConvert()['convertForumsBoards']['where'] ),
'per_cycle' => 200,
'dependencies' => array( 'convertForumsForums' ),
'link_type' => 'forums_forums',
);
return $rows;
}
@@ -104,7 +113,14 @@ class _Smf extends \IPS\convert\Software
switch( $table )
{
case 'forums':
return $this->db->select( 'COUNT(*)', 'categories' )->first() + $this->db->select( 'COUNT(*)', 'boards' )->first();
try
{
return $this->db->select( 'COUNT(*)', 'categories' )->first() + $this->db->select( 'COUNT(*)', 'boards' )->first();
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
@@ -182,11 +198,11 @@ class _Smf extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics" );
}
@@ -194,8 +210,8 @@ class _Smf extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -267,6 +283,7 @@ class _Smf extends \IPS\convert\Software
'redirect_url' => $row['redirect'],
'queued_topics' => $row['unapproved_topics'],
'queued_posts' => $row['unapproved_posts'],
'sub_can_post' => 1
);
$libraryClass->convertForumsForum( $info );
@@ -334,7 +351,12 @@ class _Smf extends \IPS\convert\Software
$poll = array(
'poll_data' => array(
'pid' => $poll_data['id_poll'],
'choices' => $choices,
'choices' => array( 1 => array(
'question' => $poll_data['question'],
'multi' => 0,
'choice' => $choices,
'votes' => $votes,
) ),
'poll_question' => $poll_data['question'],
'start_date' => $firstPost['poster_time'],
'starter_id' => $poll_data['id_member'],
@@ -431,14 +453,14 @@ class _Smf extends \IPS\convert\Software
*/
public function convertAttachments()
{
if ( is_null( static::$paths ) )
if ( \is_null( static::$paths ) )
{
try
{
static::$paths = @\unserialize( $this->db->select( 'value', 'settings', array( "variable=?", 'attachmentUploadDir' ) )->first() );
/* Unserialize failed. Set to an array so we don't try again */
if ( !is_array( static::$paths ) )
if ( !\is_array( static::$paths ) )
{
static::$paths = array();
}
@@ -497,8 +519,8 @@ class _Smf extends \IPS\convert\Software
$row['filename'] = str_replace(']', '', $row['filename']);
$row['filename'] = str_replace('&', '', $row['filename']);
$row['filename'] = str_replace('\'', '', $row['filename']);
$row['filename'] = str_replace( chr(195) . chr(182) , 'A', $row['filename']);
$row['filename'] = str_replace( chr(195) . chr(164) , 'A', $row['filename']);
$row['filename'] = str_replace( \chr(195) . \chr(182) , 'A', $row['filename']);
$row['filename'] = str_replace( \chr(195) . \chr(164) , 'A', $row['filename']);
/* General Information */
$info = array(
@@ -526,6 +548,16 @@ class _Smf extends \IPS\convert\Software
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $location;
}
}
// in 2.1 attachments are stored with a .dat extension, so we need to look for it if this path doesn't exist.
if( !file_exists( $path ) )
{
/* Check the 2.1 file */
if( file_exists( $path . '.dat' ) )
{
$path .= '.dat';
}
}
$libraryClass->convertAttachment( $info, $map, $path );
@@ -4,7 +4,7 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package IPS Social Suite
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
* @version
*/
@@ -12,12 +12,15 @@
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* UBBThreads Forums Converter
*/
class _UBBthreads extends \IPS\convert\Software
{
/**
@@ -39,7 +42,7 @@ class _UBBthreads extends \IPS\convert\Software
public static function softwareKey()
{
/* Child classes must override this method */
return "UBBthreads";
return "ubbthreads";
}
/**
@@ -86,11 +89,11 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
$rows = array();
$count = $this->countRows( static::canConvert()['convertForumsTopicsRatings']['table'], static::canConvert()['convertForumsTopicsRatings']['where'] );
if( $count )
@@ -100,7 +103,7 @@ class _UBBthreads extends \IPS\convert\Software
'step_title' => 'convert_ratings',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_ratings' ),
'source_rows' => $count,
'per_cycle' => 1500,
'per_cycle' => 200,
'dependencies' => array( 'convertForumsTopics' ),
'link_type' => 'topic_ratings',
);
@@ -115,7 +118,7 @@ class _UBBthreads extends \IPS\convert\Software
'step_title' => 'convert_follows',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_follow' ),
'source_rows' => $count,
'per_cycle' => 1000,
'per_cycle' => 200,
'dependencies' => array( 'convertForumsTopics' ),
'link_type' => 'core_follows',
);
@@ -161,7 +164,7 @@ class _UBBthreads extends \IPS\convert\Software
*/
public static function parents()
{
return array( 'core' => array( 'UBBthreads' ) );
return array( 'core' => array( 'ubbthreads' ) );
}
/**
@@ -212,11 +215,11 @@ class _UBBthreads extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics" );
}
@@ -224,8 +227,8 @@ class _UBBthreads extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -385,7 +388,7 @@ class _UBBthreads extends \IPS\convert\Software
'title' => mb_substr( $row['TOPIC_SUBJECT'], 0, 250 ),
'forum_id' => $row['FORUM_ID'],
'state' => ( $row['TOPIC_STATUS'] == 'C' ) ? 'closed' : 'open',
'posts' => $row['TOPIC_TOTAL_REPLIES'],
'posts' => $row['TOPIC_REPLIES'],
'starter_id' => $row['USER_ID'],
'start_date' => \IPS\DateTime::create()->setTimestamp( $row['TOPIC_CREATED_TIME'] ),
'last_poster_id' => $row['TOPIC_LAST_POSTER_ID'],
@@ -4,7 +4,7 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package IPS Social Suite
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
@@ -12,12 +12,15 @@ namespace IPS\convert\Software\Forums;
use \IPS\convert\Software\Core\Vanilla as VanillaCore;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Vanilla Forums Converter
*/
class _Vanilla extends \IPS\convert\Software
{
/**
@@ -28,8 +31,8 @@ class _Vanilla extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -122,13 +125,21 @@ class _Vanilla extends \IPS\convert\Software
/**
* Allows software to add additional menu row options
*
* @param array $rows Existing rows
* @return array
*/
public function extraMenuRows( $rows )
public function extraMenuRows()
{
$rows['convertForumsPosts2'] = $rows['convertForumsPosts'];
$rows['convertForumsPosts2']['step_method'] = 'convertForumsPosts2';
$rows = array();
$rows['convertForumsPosts2'] = array(
'step_title' => 'convert_forums_posts',
'step_method' => 'convertForumsPosts2',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'forums_posts' ),
'source_rows' => array( 'table' => static::canConvert()['convertForumsPosts2']['table'], 'where' => static::canConvert()['convertForumsPosts2']['where'] ),
'per_cycle' => 200,
'dependencies' => array( 'convertForumsPosts' ),
'link_type' => 'forums_posts',
'requires_rebuild' => TRUE
);
return $rows;
}
@@ -147,10 +158,17 @@ class _Vanilla extends \IPS\convert\Software
switch( $table )
{
case 'Comment':
$count = 0;
$count += $this->db->select( 'COUNT(*)', 'Discussion' )->first();
$count += $this->db->select( 'COUNT(*)', 'Comment' )->first();
return $count;
try
{
$count = 0;
$count += $this->db->select( 'COUNT(*)', 'Discussion' )->first();
$count += $this->db->select( 'COUNT(*)', 'Comment' )->first();
return $count;
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
@@ -263,11 +281,11 @@ class _Vanilla extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
/* Rebuild Leaderboard */
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
@@ -282,8 +300,8 @@ class _Vanilla extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -463,7 +481,7 @@ class _Vanilla extends \IPS\convert\Software
{
$tags = explode( ',', $row['Tags'] );
if ( count( $tags ) )
if ( \count( $tags ) )
{
foreach( $tags AS $tag )
{
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Forums Converter
*/
class _Vbulletin extends \IPS\convert\Software
{
/**
@@ -38,8 +41,8 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -68,7 +71,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();
}
@@ -115,7 +118,12 @@ class _Vbulletin extends \IPS\convert\Software
),
'convertForumsTopics' => array(
'table' => 'thread',
'where' => NULL
'where' => NULL,
'extra_steps' => array( 'convertForumsTopicsFollowers' )
),
'convertForumsTopicsFollowers' => array(
'table' => 'subscribethread',
'where' => NULL,
),
'convertForumsPosts' => array(
'table' => 'post',
@@ -135,11 +143,33 @@ class _Vbulletin extends \IPS\convert\Software
),
'convertAttachments' => array(
'table' => 'attachment',
'where' => ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? array( "contenttypeid=?", static::$postContentType ) : NULL
'where' => ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) ? array( "contenttypeid=?", static::$postContentType ) : NULL
)
);
}
/**
* Allows software to add additional menu row options
*
* @return array
*/
public function extraMenuRows()
{
$rows = array();
$rows['convertForumsTopicsFollowers'] = array(
'step_title' => 'convert_forums_topics_followers',
'step_method' => 'convertForumsTopicsFollowers',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_app=? AND follow_area=?', 'forums', 'topics' ) ),
'source_rows' => array( 'table' => static::canConvert()['convertForumsTopicsFollowers']['table'], 'where' => static::canConvert()['convertForumsTopicsFollowers']['where'] ),
'per_cycle' => 2000,
'dependencies' => array( 'convertForumsTopics' ),
'link_type' => 'core_follows',
'requires_rebuild' => FALSE
);
return $rows;
}
/**
* Requires Parent
*
@@ -168,11 +198,11 @@ class _Vbulletin extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 2, array( 'class' ) ); // This must run before the CMS item count task runs.
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
/* Rebuild Leaderboard */
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
@@ -187,7 +217,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @param string $post Post
* @return string Fixed Posts
*/
public static function fixPostData( $post )
@@ -234,7 +264,7 @@ class _Vbulletin extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
@@ -242,7 +272,7 @@ class _Vbulletin extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
@@ -250,7 +280,7 @@ class _Vbulletin extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
@@ -265,8 +295,8 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -317,7 +347,7 @@ class _Vbulletin extends \IPS\convert\Software
'topics' => $forum['threadcount'],
'posts' => $forum['replycount'],
'last_post' => $forum['lastpost'],
'last_poster_id' => ( static::$isLegacy === FALSE or is_null( static::$isLegacy ) ) ? $forum['lastposterid'] : 0,
'last_poster_id' => ( static::$isLegacy === FALSE or \is_null( static::$isLegacy ) ) ? $forum['lastposterid'] : 0,
'last_poster_name' => $forum['lastposter'],
'parent_id' => $forum['parentid'],
'position' => $forum['displayorder'],
@@ -380,10 +410,11 @@ class _Vbulletin extends \IPS\convert\Software
public function convertForumsTopics()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'threadid' );
foreach( $this->fetch( 'thread', 'threadid' ) AS $topic )
$data = iterator_to_array( $this->fetch( 'thread', 'threadid' ) );
$this->app->preCacheLinks( $data, [ 'forums_forums' => 'forumid', 'core_members' => [ 'postuserid', 'lastposterid' ] ] );
foreach( $data AS $topic )
{
/* Pesky Polls */
$poll = NULL;
@@ -434,7 +465,9 @@ class _Vbulletin extends \IPS\convert\Software
$poll['vote_data'] = array();
$ourVotes = array();
foreach( $this->db->select( '*', 'pollvote', array( "pollid=?", $pollData['pollid'] ) ) AS $vote )
$voteData = iterator_to_array( $this->db->select( '*', 'pollvote', array( "pollid=?", $pollData['pollid'] ) ) );
$this->app->preCacheLinks( $voteData, [ 'core_members' => 'userid' ] );
foreach( $voteData AS $vote )
{
if ( !isset( $ourVotes[$vote['userid']] ) )
{
@@ -464,6 +497,21 @@ class _Vbulletin extends \IPS\convert\Software
catch( \UnderflowException $e ) {} # if the poll is missing, don't bother
}
switch( $topic['visible'] )
{
case 0: // hidden awaiting approval.
$approved = 0;
break;
case 1: // Visible
$approved = 1;
break;
case 2: // Soft Deleted in vBulletin is equivalent to hidden by a moderator in IPS.
$approved = -1;
break;
}
$info = array(
'tid' => $topic['threadid'],
'title' => $topic['title'],
@@ -472,59 +520,26 @@ class _Vbulletin extends \IPS\convert\Software
'posts' => $topic['replycount'],
'starter_id' => $topic['postuserid'],
'start_date' => $topic['dateline'],
'last_poster_id' => ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? $topic['lastposterid'] : NULL,
'last_poster_id' => ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) ? $topic['lastposterid'] : NULL,
'last_post' => $topic['lastpost'],
'starter_name' => $topic['postusername'],
'last_poster_name' => $topic['lastposter'],
'poll_state' => $poll,
'last_vote' => $lastVote,
'views' => $topic['views'],
'approved' => $topic['visible'],
'approved' => $approved,
'pinned' => $topic['sticky'],
'topic_hiddenposts' => $topic['hiddencount'] + $topic['deletedcount']
);
unset( $poll );
$topic_id = $libraryClass->convertForumsTopic( $info );
/* Follows */
foreach( $this->db->select( '*', 'subscribethread', array( "threadid=?", $topic['threadid'] ) ) AS $follow )
{
$frequency = 'none';
switch( $follow['emailupdate'] )
{
case 1:
$frequency = 'immediate';
break;
case 2:
$frequency = 'daily';
break;
case 3:
$frequency = 'weekly';
break;
}
$libraryClass->convertFollow( array(
'follow_app' => 'forums',
'follow_area' => 'topic',
'follow_rel_id' => $topic['threadid'],
'follow_rel_id_type' => 'forums_topics',
'follow_member_id' => $follow['userid'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_notify_do' => 1,
'follow_notify_meta' => '',
'follow_notify_freq' => $frequency,
'follow_notify_sent' => 0,
'follow_visible' => 1,
) );
}
$libraryClass->convertForumsTopic( $info );
/* Ratings */
foreach( $this->db->select( '*', 'threadrate', array( "threadid=?", $topic['threadid'] ) ) AS $rating )
$ratingData = iterator_to_array( $this->db->select( '*', 'threadrate', array( "threadid=?", $topic['threadid'] ) ) );
$this->app->preCacheLinks( $ratingData, [ 'core_members' => 'userid' ] );
foreach( $ratingData AS $rating )
{
$libraryClass->convertRating( array(
'id' => $rating['threadrateid'],
@@ -559,7 +574,7 @@ class _Vbulletin extends \IPS\convert\Software
if( $topic['taglist'] !== NULL AND !empty( $topic['taglist'] ) )
{
$tags = explode( ',', $topic['taglist'] );
if ( count( $tags ) )
if ( \count( $tags ) )
{
foreach( $tags AS $tag )
{
@@ -580,6 +595,56 @@ class _Vbulletin extends \IPS\convert\Software
}
}
/**
* Convert topic follows
*
* @return void
*/
public function convertForumsTopicsFollowers()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'subscribethreadid' );
/* Follows */
$followData = iterator_to_array( $this->fetch( 'subscribethread', 'subscribethreadid' ) );
$this->app->preCacheLinks( $followData, [ 'forums_topics' => 'threadid', 'core_members' => 'userid' ] );
foreach( $followData AS $follow )
{
$frequency = 'none';
switch( $follow['emailupdate'] )
{
case 1:
$frequency = 'immediate';
break;
case 2:
$frequency = 'daily';
break;
case 3:
$frequency = 'weekly';
break;
}
$libraryClass->convertFollow( array(
'follow_app' => 'forums',
'follow_area' => 'topic',
'follow_rel_id' => $follow['threadid'],
'follow_rel_id_type' => 'forums_topics',
'follow_member_id' => $follow['userid'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_notify_do' => 1,
'follow_notify_meta' => '',
'follow_notify_freq' => $frequency,
'follow_notify_sent' => 0,
'follow_visible' => 1,
) );
$libraryClass->setLastKeyValue( $follow['subscribethreadid'] );
}
}
/**
* Convert posts
*
@@ -588,11 +653,40 @@ class _Vbulletin extends \IPS\convert\Software
public function convertForumsPosts()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'postid' );
foreach( $this->fetch( 'post', 'postid' ) AS $post )
$data = iterator_to_array( $this->fetch( array( 'post', 'p' ), 'p.postid', NULL, 'p.*, t.visible AS topicvisible' )->join( array( 'thread', 't' ), 'p.threadid=t.threadid' ) );
$this->app->preCacheLinks( $data, [ 'forums_topics' => 'threadid', 'core_members' => 'userid' ] );
foreach( $data AS $post )
{
$queued = NULL;
/* If the topic is hidden, mark this post as hidden by the parent topic */
if ( $post['topicvisible'] == 2 )
{
$queued = 2;
}
/* Still here? Topic is fine, so check the post */
if ( $queued === NULL )
{
switch( $post['visible'] )
{
case 0: // hidden pending moderator approval
$queued = 1;
break;
case 1: // Visible
$queued = 0;
break;
case 2: // Hidden by moderator
$queued = -1;
break;
}
}
$info = array(
'pid' => $post['postid'],
'topic_id' => $post['threadid'],
@@ -601,14 +695,27 @@ class _Vbulletin extends \IPS\convert\Software
'author_name' => $post['username'],
'ip_address' => $post['ipaddress'],
'post_date' => $post['dateline'],
'queued' => ( $post['visible'] >= 2 ) ? -1 : ( ( $post['visible'] == 0 ) ? 1 : 0 ),
'post_htmlstate' => ( static::$isLegacy === FALSE AND in_array( $post['htmlstate'], array( 'on', 'on_nl2br' ) ) ) ? 1 : 0,
'queued' => $queued,
'post_htmlstate' => ( static::$isLegacy === FALSE AND \in_array( $post['htmlstate'], array( 'on', 'on_nl2br' ) ) ) ? 1 : 0,
);
/* Edited? */
try
{
$lastEdit = $this->db->select( '*', 'postedithistory', array( 'postid=?', $post['postid'] ), 'postedithistoryid DESC', 1 )->first();
$info['append_edit'] = 1;
$info['edit_time'] = $lastEdit['dateline'];
$info['edit_name'] = $lastEdit['username'];
$info['post_edit_reason'] = $lastEdit['reason'] ?? null;
}
catch( \UnderflowException $e ) {}
$post_id = $libraryClass->convertForumsPost( $info );
$libraryClass->convertForumsPost( $info );
/* Reputation */
foreach( $this->db->select( '*', 'reputation', array( "postid=?", $post['postid'] ) ) AS $rep )
$reputation = iterator_to_array( $this->db->select( '*', 'reputation', array( "postid=?", $post['postid'] ) ) );
$this->app->preCacheLinks( $reputation, [ 'core_members' => [ 'userid', 'whoadded' ] ] );
foreach( $reputation AS $rep )
{
$reaction = ( $rep['reputation'] > 0 ) ?
$this->app->_session['more_info']['convertForumsPosts']['rep_positive'] :
@@ -627,36 +734,32 @@ class _Vbulletin extends \IPS\convert\Software
) );
}
/* Edit History */
$latestedit = 0;
$reason = NULL;
$name = NULL;
$newText = static::fixPostData( $post['pagetext'] );
foreach( $this->db->select( '*', 'postedithistory', array( "postid=?", $post['postid'] ) ) AS $edit )
/* Process edit history if some exists */
if( isset( $info['append_edit'] ) )
{
$libraryClass->convertEditHistory( array(
'id' => $edit['postedithistoryid'],
'class' => 'IPS\\forums\\Topic\\Post',
'comment_id' => $post['postid'],
'member' => $edit['userid'],
'time' => $edit['dateline'],
'old' => static::fixPostData( $edit['pagetext'] ),
'new' => $newText
) );
$newText = static::fixPostData( $edit['pagetext'] );
if ( $edit['dateline'] > $latestedit )
$newText = $info['post'];
foreach ( $this->db->select( '*', 'postedithistory', array( "postid=?", $post['postid'] ), 'postedithistoryid ASC' ) as $edit )
{
$latestedit = $edit['dateline'];
$reason = $edit['reason'];
$name = $edit['username'];
$editHistory = array(
'id' => $edit['postedithistoryid'],
'class' => 'IPS\\forums\\Topic\\Post',
'comment_id' => $post['postid'],
'member' => $edit['userid'],
'time' => $edit['dateline'],
'old' => static::fixPostData( $edit['pagetext'] ),
'new' => $newText
);
$libraryClass->convertEditHistory( $editHistory );
$newText = $editHistory['old'];
}
}
/* Warnings */
foreach( $this->db->select( '*', 'infraction', array( "postid=?", $post['postid'] ) ) AS $warn )
$warnings = iterator_to_array( $this->db->select( '*', 'infraction', array( "postid=?", $post['postid'] ) ) );
$this->app->preCacheLinks( $warnings, [ 'core_members' => 'whoadded' ] );
foreach( $warnings AS $warn )
{
$warnId = $libraryClass->convertWarnLog( array(
'wl_id' => $warn['infractionid'],
@@ -680,12 +783,6 @@ class _Vbulletin extends \IPS\convert\Software
);
}
/* If we have a latest edit, then update the main post - this should really be in the library, as the converters should not be altering data */
if ( $latestedit )
{
\IPS\Db::i()->update( 'forums_posts', array( 'append_edit' => 1, 'edit_time' => $latestedit, 'edit_name' => $name, 'post_edit_reason' => $reason ), array( "pid=?", $post_id ) );
}
$libraryClass->setLastKeyValue( $post['postid'] );
}
}
@@ -704,7 +801,7 @@ class _Vbulletin extends \IPS\convert\Software
$where = NULL;
$column = NULL;
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
$where = array( "contenttypeid=?", static::$postContentType );
$column = 'contentid';
@@ -728,7 +825,7 @@ class _Vbulletin extends \IPS\convert\Software
continue;
}
if ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) )
if ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) )
{
try
{
@@ -787,9 +884,9 @@ class _Vbulletin extends \IPS\convert\Software
$post = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
if ( preg_match( "/\[ATTACH(.+?)?\]".$attachment['attachmentid']."\[\/ATTACH\]/i", $post ) )
if ( preg_match( "/\[ATTACH([^\]]+?)?\]".$attachment['attachmentid']."\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH(.+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $attachment['attachmentid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $post ), array( "pid=?", $pid ) );
}
@@ -1058,19 +1155,20 @@ class _Vbulletin extends \IPS\convert\Software
mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'showpost.php' ) !== FALSE OR
preg_match( '#/threads/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ] )
)
AND isset( \IPS\Request::i()->p )
AND ( isset( \IPS\Request::i()->p ) OR isset( \IPS\Request::i()->postid ) )
)
{
try
{
$postId = \IPS\Request::i()->postid ?? \IPS\Request::i()->p;
try
{
$data = (string) $this->app->getLink( (int) \IPS\Request::i()->p, array( 'posts', 'forums_posts' ) );
$data = (string) $this->app->getLink( (int) $postId, array( 'posts', 'forums_posts' ) );
}
catch( \OutOfRangeException $e )
{
/* Try the main table */
$data = (string) $this->app->getLink( (int) \IPS\Request::i()->p, array( 'posts', 'forums_posts' ), FALSE, TRUE );
$data = (string) $this->app->getLink( (int) $postId, array( 'posts', 'forums_posts' ), FALSE, TRUE );
}
$item = \IPS\forums\Topic\Post::load( $data );
@@ -1085,21 +1183,28 @@ class _Vbulletin extends \IPS\convert\Software
}
}
/* And then topic URLs, same idea */
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'showthread.php' ) !== FALSE )
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'showthread.php' ) !== FALSE OR
mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'printthread.php' ) !== FALSE )
{
/* Topic URLs can be in one of 4 formats really...
* /showthread.php/1-name
* /threads/1-name
* /showthread.php?t=1
* /showthread.php?1-name
* /printthread.php?t=1
* /showthread.php?threadid=?
*/
$path = $url->data[ \IPS\Http\Url::COMPONENT_PATH ];
if( mb_strpos( $path, 'showthread.php' ) !== FALSE )
if( mb_strpos( $path, 'showthread.php' ) !== FALSE OR mb_strpos( $path, 'printthread.php' ) !== FALSE )
{
if( isset( \IPS\Request::i()->t ) )
{
$oldId = \IPS\Request::i()->t;
}
elseif( isset( \IPS\Request::i()->threadid ) )
{
$oldId = \IPS\Request::i()->threadid;
}
elseif( preg_match( '#^(\d+)-[^/]+#i', $url->data[ \IPS\Http\Url::COMPONENT_QUERY ], $matches ) )
{
$oldId = $matches[1];
@@ -1169,8 +1274,8 @@ class _Vbulletin extends \IPS\convert\Software
/* And finally, archives */
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'archive/index.php' ) !== FALSE )
{
$request = str_replace( '/archive/index.php/', '', $_SERVER['REQUEST_URI'] );
$parameters = explode( '-', $request );
$request = explode( '/', $url->data[ \IPS\Http\Url::COMPONENT_PATH ] );
$parameters = explode( '-', array_pop( $request ) );
$parameters[1] = str_replace( '.html', '', $parameters[1] );
try
@@ -1180,11 +1285,11 @@ class _Vbulletin extends \IPS\convert\Software
case 't':
try
{
$data = $this->getLink( (string) $parameters[1], array( 'topics', 'forums_topics' ) );
$data = $this->app->getLink( (string) $parameters[1], array( 'topics', 'forums_topics' ) );
}
catch( \OutOfRangeException $e )
{
$data = $this->getLink( (string) $parameters[1], array( 'topics', 'forums_topics' ), FALSE, TRUE );
$data = $this->app->getLink( (string) $parameters[1], array( 'topics', 'forums_topics' ), FALSE, TRUE );
}
$item = \IPS\forums\Topic::load( $data );
@@ -1210,7 +1315,28 @@ class _Vbulletin extends \IPS\convert\Software
return NULL;
}
}
/* Club Discussions */
elseif( ( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'group.php' ) !== FALSE AND isset( \IPS\Request::i()->discussionid ) ) OR
preg_match( '#/social-groups/(.*)/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
$id = \IPS\Request::i()->discussionid ?? $matches[2];
try
{
$data = (string) $this->app->getLink( 'clubtopic' . $id, array( 'topics', 'forums_topics' ) );
}
catch( \OutOfRangeException $e )
{
return NULL;
}
$item = \IPS\forums\Topic::load( $data );
if( $item->canView() )
{
return $item->url();
}
}
return NULL;
}
}
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin 5 Forums Converter
*/
class _Vbulletin5 extends \IPS\convert\Software
{
/**
@@ -63,12 +66,38 @@ class _Vbulletin5 extends \IPS\convert\Software
'where' => NULL,
),
'convertAttachments' => array(
'table' => 'attachment',
'where' => NULL,
'extra_steps' => array( 'convertAttachments2' )
),
'convertAttachments2' => array(
'table' => 'attachment',
'where' => NULL
)
),
);
}
/**
* Allows software to add additional menu row options
*
* @return array
*/
public function extraMenuRows()
{
$rows = array();
$rows['convertAttachments2'] = array(
'step_method' => 'convertAttachments2',
'step_title' => 'convert_attachments',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments_map', array( "location_key=?", 'gallery_Images' ) ),
'source_rows' => array( 'table' => static::canConvert()['convertAttachments2']['table'], 'where' => static::canConvert()['convertAttachments2']['where'] ),
'per_cycle' => 10,
'dependencies' => array( 'convertAttachments' ),
'link_type' => 'core_attachments',
);
return $rows;
}
/**
* Count Source Rows for a specific step
*
@@ -83,19 +112,33 @@ class _Vbulletin5 extends \IPS\convert\Software
switch( $table )
{
case 'forum':
return count( $this->fetchForums() );
return \count( $this->fetchForums() );
break;
case 'thread':
return parent::countRows( 'node', array( \IPS\Db::i()->in( 'contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Poll' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ) ) ) . " AND " . \IPS\Db::i()->in( 'parentid', array_keys( $this->fetchForums() ) ), $this->fetchType( 'Text' ), $this->fetchType( 'Poll' ) ) );
try
{
return parent::countRows( 'node', array( \IPS\Db::i()->in( 'contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Poll' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ) ) ) . " AND " . \IPS\Db::i()->in( 'parentid', array_keys( $this->fetchForums() ) ), $this->fetchType( 'Text' ), $this->fetchType( 'Poll' ) ) );
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
case 'post':
return parent::countRows( 'node', array( \IPS\Db::i()->in( 'contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ), $this->fetchType( 'Poll' ) ) ) ) );
try
{
return parent::countRows( 'node', array( \IPS\Db::i()->in( 'contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ), $this->fetchType( 'Poll' ) ) ) ) );
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
case 'attachment':
return parent::countRows( 'node', array( "contenttypeid=?", $this->fetchType( 'Attach' ) ) );
return parent::countRows( 'node', array( "contenttypeid IN (" . $this->fetchType( 'Attach' ) . ',' . $this->fetchType( 'Photo' ) . ")" ) );
break;
default:
@@ -132,10 +175,10 @@ class _Vbulletin5 extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
/* Rebuild Leaderboard */
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
@@ -147,7 +190,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @param string $post Post
* @return string Fixed Posts
*/
public static function fixPostData( $post )
@@ -194,7 +237,7 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
@@ -209,8 +252,8 @@ class _Vbulletin5 extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -301,7 +344,7 @@ class _Vbulletin5 extends \IPS\convert\Software
$libraryClass::setKey( 'node.nodeid' );
$clubs = $this->fetchClubs();
foreach( $this->fetch( 'node', 'node.nodeid', array( "( " . \IPS\Db::i()->in( 'node.parentid', array_keys( $this->fetchForums() ) ) . " OR " . $this->db->in( 'node.parentid', array_keys( $clubs ) ) . " ) AND " . \IPS\Db::i()->in( 'node.contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Poll' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ) ) ) ) )->join( 'text', 'text.nodeid = node.nodeid' ) AS $topic )
foreach( $this->fetch( 'node', 'node.nodeid', array( "( " . \IPS\Db::i()->in( 'node.parentid', array_keys( $this->fetchForums() ) ) . " OR " . $this->db->in( 'node.parentid', array_keys( $clubs ) ) . " ) AND " . \IPS\Db::i()->in( 'node.contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Poll' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ) ) ) ) ) AS $topic )
{
/* Pesky Polls */
$poll = NULL;
@@ -414,8 +457,8 @@ class _Vbulletin5 extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'node.nodeid' );
foreach( $this->fetch( 'node', 'node.nodeid', array( \IPS\Db::i()->in( 'node.contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ), $this->fetchType( 'Poll' ) ) ) ) )->join( 'text', 'text.nodeid = node.nodeid' ) AS $post )
foreach( $this->fetch( 'node', 'node.nodeid', array( \IPS\Db::i()->in( 'node.contenttypeid', array( $this->fetchType( 'Text' ), $this->fetchType( 'Gallery' ), $this->fetchType( 'Video' ), $this->fetchType( 'Link' ), $this->fetchType( 'Poll' ) ) ) ), 'node.*, text.rawtext, text.htmlstate' )->join( 'text', 'text.nodeid = node.nodeid' ) AS $post )
{
/* Comments of comments of comments of comments */
$isSubComment = FALSE;
@@ -488,21 +531,56 @@ class _Vbulletin5 extends \IPS\convert\Software
'ip_address' => $post['ipaddress'],
'post_date' => $post['publishdate'],
'queued' => $queued,
'post_htmlstate' => ( in_array( $post['htmlstate'], array( 'on', 'on_nl2br' ) ) ) ? 1 : 0,
'post_htmlstate' => ( \in_array( $post['htmlstate'], array( 'on', 'on_nl2br' ) ) ) ? 1 : 0,
);
/* Any vB5 Links? */
if( $post['contenttypeid'] == $this->fetchType( 'Link' ) )
{
try
{
$link = $this->db->select( 'url, url_title', 'link', array('nodeid=?', $post['nodeid']) )->first();
if ( !empty( $link['url_title'] ) )
{
$info['post'] .= <<<LINK
[url={$link['url']}]{$link['url_title']}[/url]
LINK;
}
else
{
$info['post'] .= <<<LINK
[url]{$link['url']}[/url]
LINK;
}
}
catch ( \UnderflowException $e ) {}
}
$post_id = $libraryClass->convertForumsPost( $info );
/* Need to insert into the SDL? */
if ( $softDeleted )
{
try
{
$softDeleteId = $this->app->getLink( $post['deleteuserid'], 'core_members', TRUE );
}
/* User link doesn't exist */
catch( \OutOfRangeException $e )
{
$softDeleteId = 0;
}
\IPS\Db::i()->insert( 'core_soft_delete_log', array(
'sdl_obj_id' => $post_id,
'sdl_obj_key' => 'post',
'sdl_obj_member_id' => $this->app->getLink( $post['deleteuserid'], 'core_members', TRUE ),
'sdl_obj_date' => $post['unpublishdate'] ?: time(),
'sdl_obj_reason' => $post['deletereason'] ?: NULL,
'sdl_locked' => 0
'sdl_obj_id' => $post_id,
'sdl_obj_key' => 'post',
'sdl_obj_member_id' => $softDeleteId,
'sdl_obj_date' => $post['unpublishdate'] ?: time(),
'sdl_obj_reason' => $post['deletereason'] ?: NULL,
'sdl_locked' => 0
) );
}
@@ -693,9 +771,9 @@ class _Vbulletin5 extends \IPS\convert\Software
continue;
}
if ( preg_match( "/\[ATTACH=(.+?)?\]n" . $attachment['nodeid'] . "\[\/ATTACH\]/i", $post ) )
if ( preg_match( "/\[ATTACH([^\]]+?)?\]n" . $attachment['nodeid'] . "\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH(.+?)?\]" . $attachment['nodeid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]n" . $attachment['nodeid'] . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $post ), array( "pid=?", $pid ) );
}
@@ -712,10 +790,10 @@ class _Vbulletin5 extends \IPS\convert\Software
/* Don't fail if this table/value doesn't exist */
catch( \Exception $e ) { }
if ( $oldAttachId !== NULL and preg_match( "/\[ATTACH=(.+?)?\]" . $oldAttachId . "\[\/ATTACH\]/i", $post ) )
if ( $oldAttachId !== NULL and preg_match( "/\[ATTACH([^\]]+?)?\]" . $oldAttachId . "\[\/ATTACH\]/i", $post ) )
{
$update = TRUE;
$post = preg_replace( "/\[ATTACH(.+?)?\]" . $oldAttachId . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $oldAttachId . "\[\/ATTACH\]/i", '[attachment=' . $attach_id . ':name]', $post );
}
elseif( preg_match( "/\[ATTACH=JSON](.+?)\"data-attachmentid\":\"?{$attachment['nodeid']}\"?(.+?)\[\/ATTACH\]/i", $post ) )
{
@@ -733,6 +811,103 @@ class _Vbulletin5 extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $attachment['nodeid'] );
}
}
/**
* Convert attachments (photo posts)
*
* @return void
*/
public function convertAttachments2()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'node.nodeid' );
$main = $this->fetch( 'node', 'node.nodeid', array( "node.contenttypeid=?", $this->fetchType( 'Photo' ) ) )
->join( 'photo', 'photo.nodeid = node.nodeid' )
->join( 'filedata', 'filedata.filedataid = photo.filedataid' );
foreach( $main AS $attachment )
{
/* This is the first post in the topic */
try
{
$this->app->getLink( $attachment['parentid'], 'forums_topics' );
$post_id = $attachment['parentid'];
}
/* It isn't the first post in the topic */
catch( \OutOfRangeException $e )
{
try
{
$this->app->getLink( $attachment['parentid'], 'forums_posts' );
$parent = $this->db->select( 'nodeid, parentid', 'node', array( "nodeid=?", $attachment['parentid'] ) )->first();
$attachment['parentid'] = $parent['parentid'];
$post_id = $parent['nodeid'];
}
catch( \OutOfRangeException $e )
{
/* No valid converter link for the parent post */
$libraryClass->setLastKeyValue( $attachment['nodeid'] );
$this->app->log( 'vb5_attach_missing_post_link', __METHOD__, \IPS\convert\App::LOG_WARNING, $attachment['nodeid'] );
continue;
}
catch( \UnderflowException $e )
{
/* Parent post does not exist in vB database */
$libraryClass->setLastKeyValue( $attachment['nodeid'] );
$this->app->log( 'vb5_attach_missing_post', __METHOD__, \IPS\convert\App::LOG_WARNING, $attachment['nodeid'] );
continue;
}
}
$map = array(
'id1' => $attachment['parentid'],
'id2' => $post_id
);
$info = array(
'attach_id' => $attachment['nodeid'],
'attach_file' => $attachment['filehash'] . '.' . $attachment['extension'],
'attach_date' => $attachment['dateline'],
'attach_member_id' => $attachment['userid'],
'attach_hits' => 0,
'attach_ext' => $attachment['extension'],
'attach_filesize' => $attachment['filesize'],
);
if ( $this->app->_session['more_info']['convertAttachments']['file_location'] == 'database' )
{
/* Simples! */
$data = $attachment['filedata'];
$path = NULL;
}
else
{
$data = NULL;
$path = implode( '/', preg_split( '//', $attachment['userid'], -1, PREG_SPLIT_NO_EMPTY ) );
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['file_location'], '/' ) . '/' . $path . '/' . $attachment['filedataid'] . '.attach';
/* Apparently vBulletin had a bug between 5.0.0 and 5.1.3 where attachments were incorrectly stored in /path/userid/file.attach rather than in the user ID split location */
if ( !file_exists( $path ) )
{
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['file_location'], '/' ) . '/' . $attachment['userid'] . '/' . $attachment['filedataid'] . '.attach';
}
/* Still not there? It might be in the database still */
if ( !file_exists( $path ) AND $attachment['filedata'] )
{
$data = $attachment['filedata'];
$path = NULL;
}
}
$attach_id = $libraryClass->convertAttachment( $info, $map, $path, $data );
$libraryClass->setLastKeyValue( $attachment['nodeid'] );
}
}
/* !vBulletin Stuff */
@@ -769,7 +944,7 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Helper method to retrieve forums from nodes
*
* @return void
* @return array
*/
protected function fetchForums()
{
@@ -822,11 +997,12 @@ class _Vbulletin5 extends \IPS\convert\Software
/**
* Helper method to retrieve content type ids
*
* @param int|string $type The content type
* @return void
*/
protected function fetchType( $type )
{
if ( count( $this->typesCache ) )
if ( \count( $this->typesCache ) )
{
return $this->typesCache[ $type ];
}
@@ -0,0 +1,546 @@
<?php
/**
* @brief Converter WoltLab Forum Class
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage convert
* @since 01 Apr 2020
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Woltlab Forums Converter
*/
class _Woltlab extends \IPS\convert\Software
{
/**
* @brief The WBB table prefix can change depending on the number of installs
*/
public static $installId = 1;
/**
* Software Name
*
* @return string
*/
public static function softwareName()
{
/* Child classes must override this method */
return "WoltLab Suite Forum";
}
/**
* Software Key
*
* @return string
*/
public static function softwareKey()
{
/* Child classes must override this method */
return "woltlab";
}
/**
* Content we can convert from this software.
*
* @return array
*/
public static function canConvert()
{
return array(
'convertForumsForums' => array(
'table' => 'wbb' . static::$installId . '_board',
'where' => NULL
),
'convertForumsTopics' => array(
'table' => 'wbb' . static::$installId . '_thread',
'where' => array( 'movedThreadID IS NULL' )
),
'convertForumsPosts' => array(
'table' => 'wbb' . static::$installId . '_post',
'where' => NULL
),
'convertAttachments' => array(
'table' => 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_attachment',
'where' => NULL
)
);
}
/**
* Requires Parent
*
* @return boolean
*/
public static function requiresParent()
{
return TRUE;
}
/**
* Possible Parent Conversions
*
* @return array
*/
public static function parents()
{
return array( 'core' => array( 'woltlab' ) );
}
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return array Messages to display
*/
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
/* Rebuild Leaderboard */
\IPS\Task::queue( 'core', 'RebuildReputationLeaderboard', array(), 4 );
\IPS\Db::i()->delete('core_reputation_leaderboard_history');
/* Caches */
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'forums_topics', 'class' => 'IPS\forums\Topic' ), 3, array( 'app', 'link', 'class' ) );
return array( "f_forum_last_post_data", "f_rebuild_posts", "f_recounting_forums", "f_recounting_topics", "f_topic_tags_recount" );
}
/**
* Count Source Rows for a specific step
*
* @param string $table The table containing the rows to count.
* @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
*/
public function countRows( $table, $where=NULL, $recache=FALSE )
{
switch( $table )
{
case 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_attachment':
try
{
$postObjects = iterator_to_array( $this->db->select( 'objectTypeID', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_object_type', "objectType='com.woltlab.wbb.post'" ) );
return $this->db->select( 'count(attachmentID)', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_attachment', array( $this->db->in( 'objectTypeID', $postObjects ) ) )->first();
}
catch( \UnderflowException $e )
{
return 0;
}
catch( \Exception $e )
{
throw new \IPS\convert\Exception( sprintf( \IPS\Member::loggedIn()->language()->get( 'could_not_count_rows' ), $table ) );
}
break;
default:
return parent::countRows( $table, $where, $recache );
break;
}
}
/**
* Fix Post Data
*
* @param string $post Post
* @return string Fixed Post
*/
public static function fixPostData( $post )
{
return \IPS\convert\Software\Core\Woltlab::fixPostData( $post );
}
/**
* List of conversion methods that require additional information
*
* @return array
*/
public static function checkConf()
{
return array(
'convertAttachments',
'convertForumsPosts'
);
}
/**
* Get More Information
*
* @param string $method Conversion method
* @return array
*/
public function getMoreInfo( $method )
{
$return = array();
switch( $method )
{
case 'convertForumsPosts':
/* Get our reactions to let the admin map them */
$options = array();
$descriptions = array();
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_reactions' ), 'IPS\Content\Reaction' ) AS $reaction )
{
$options[ $reaction->id ] = $reaction->_icon->url;
$descriptions[ $reaction->id ] = \IPS\Member::loggedIn()->language()->addToStack('reaction_title_' . $reaction->id ) . '<br>' . $reaction->_description;
}
$return['convertForumsPosts'] = array(
'rep_positive' => array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
'rep_negative' => array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
);
break;
case 'convertAttachments':
$return['convertAttachments'] = array(
'file_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_woltlab_attach_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
)
);
break;
}
return ( isset( $return[ $method ] ) ) ? $return[ $method ] : array();
}
/**
* Convert forums
*
* @return void
*/
public function convertForumsForums()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'boardID' );
$boardObjects = iterator_to_array( $this->db->select( 'objectTypeID', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_object_type', "objectType='com.woltlab.wbb.board'" ) );
foreach( $this->fetch( 'wbb' . static::$installId . '_board', 'boardID' ) AS $row )
{
$info = array(
'id' => $row['boardID'],
'name' => $row['title'],
'description' => $row['description'],
'topics' => $row['threads'],
'posts' => $row['posts'],
'parent_id' => !$row['parentID'] ? -1 : $row['parentID'],
'position' => $row['position'],
'redirect_url' => $row['externalURL'],
'redirect_hits' => $row['clicks'],
'inc_postcount' => $row['countUserPosts'],
'sub_can_post' => 1,
);
$libraryClass->convertForumsForum( $info );
/* Followers */
foreach( $this->db->select( 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_user_object_watch.*', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_user_object_watch', array( "objectID=? AND " . $this->db->in('objectTypeID', $boardObjects ), $row['boardID'] ) ) AS $follow )
{
$libraryClass->convertFollow( array(
'follow_app' => 'forums',
'follow_area' => 'forum',
'follow_rel_id' => $row['boardID'],
'follow_rel_id_type' => 'forums_forums',
'follow_member_id' => $follow['userID'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_notify_do' => $follow['notification'],
'follow_notify_meta' => '',
'follow_notify_freq' => 'immediate',
'follow_notify_sent' => 0,
'follow_visible' => 1,
) );
}
$libraryClass->setLastKeyValue( $row['boardID'] );
}
}
/**
* Convert topics
*
* @return void
*/
public function convertForumsTopics()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'threadID' );
$threadObjects = iterator_to_array( $this->db->select( 'objectTypeID', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_object_type', "objectType='com.woltlab.wbb.thread'" ) );
foreach( $this->fetch( 'wbb' . static::$installId . '_thread', 'threadID', array( 'movedThreadID IS NULL' ) ) AS $row )
{
$info = array(
'tid' => $row['threadID'],
'title' => $row['topic'],
'forum_id' => $row['boardID'],
'state' => ( $row['isClosed'] == 1 ) ? 'closed' : 'open',
'starter_id' => $row['userID'],
'start_date' => $row['time'],
'last_poster_id' => $row['lastPosterID'],
'last_post' => $row['lastPostID'],
'starter_name' => $row['username'],
'last_poster_name' => $row['lastPoster'],
'views' => $row['views'],
'approved' => $row['isDeleted'] ? 0 : 1,
'pinned' => ( $row['isSticky'] OR $row['isAnnouncement'] ) ? 1 : 0,
);
$libraryClass->convertForumsTopic( $info );
/* Tags */
$tags = $this->db->select( 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_tag_to_object.tagID', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_tag_to_object', array( "objectID=? AND " . $this->db->in('objectTypeID', $threadObjects ), $row['threadID'] ) );
$tagText = $this->db->select( 'name', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_tag', array( $this->db->in( 'tagID', iterator_to_array( $tags ) ) ) );
foreach( $tagText as $text )
{
$libraryClass->convertTag( array(
'tag_meta_app' => 'forums',
'tag_meta_area' => 'forums',
'tag_meta_parent_id' => $row['boardID'],
'tag_meta_id' => $row['threadID'],
'tag_text' => $text,
'tag_member_id' => $row['userID'],
) );
}
/* Follows */
foreach( $this->db->select( 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_user_object_watch.*', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_user_object_watch', array( "objectID=? AND " . $this->db->in('objectTypeID', $threadObjects ), $row['threadID'] ) ) AS $follow )
{
$libraryClass->convertFollow( array(
'follow_app' => 'forums',
'follow_area' => 'topic',
'follow_rel_id' => $row['threadID'],
'follow_rel_id_type' => 'forums_topics',
'follow_member_id' => $follow['userID'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_notify_do' => $follow['notification'],
'follow_notify_meta' => '',
'follow_notify_freq' => 'immediate',
'follow_notify_sent' => 0,
'follow_visible' => 1,
) );
}
$libraryClass->setLastKeyValue( $row['threadID'] );
}
}
/**
* Convert posts
*
* @return void
*/
public function convertForumsPosts()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'postID' );
$postLikeObjects = iterator_to_array( $this->db->select( 'objectTypeID', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_object_type', "objectType='com.woltlab.wbb.likeablePost'" ) );
foreach( $this->fetch( 'wbb' . static::$installId . '_post', 'postID' ) AS $row )
{
$info = array(
'pid' => $row['postID'],
'topic_id' => $row['threadID'],
'post' => $row['message'],
'edit_time' => $row['lastEditTime'],
'author_id' => $row['userID'],
'author_name' => $row['username'],
'ip_address' => $row['ipAddress'],
'post_date' => $row['time'],
'queued' => ( $row['isDeleted'] OR $row['isClosed'] ) ? 1 : 0,
'post_edit_reason' => $row['editReason'],
);
$libraryClass->convertForumsPost( $info );
/* Reputation */
foreach( $this->db->select( '*', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_like', array( "objectID=? AND " . $this->db->in('objectTypeID', $postLikeObjects ), $row['postID'] ) ) AS $rep )
{
$reaction = ( $rep['likeValue'] > 0 ) ? $this->app->_session['more_info']['convertForumsPosts']['rep_positive'] : $this->app->_session['more_info']['convertForumsPosts']['rep_negative'];
$libraryClass->convertReputation( array(
'id' => $rep['likeID'],
'app' => 'forums',
'type' => 'pid',
'type_id' => $row['postID'],
'member_id' => $rep['userID'],
'member_received' => $rep['objectUserID'],
'rep_date' => $rep['time'],
'reaction' => $reaction
) );
}
$libraryClass->setLastKeyValue( $row['postID'] );
}
}
/**
* Convert attachments
*
* @return void
*/
public function convertAttachments()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'attachmentID' );
$postObjects = iterator_to_array( $this->db->select( 'objectTypeID', 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_object_type', "objectType='com.woltlab.wbb.post'" ) );
foreach( $this->fetch( 'wcf' . \IPS\convert\Software\Core\Woltlab::$installId . '_attachment', 'attachmentID', array( $this->db->in( 'objectTypeID', $postObjects ) ) ) AS $row )
{
try
{
$topicId = $this->db->select( 'threadID', 'wbb' . static::$installId . '_post', array( "postID=?", $row['objectID'] ) )->first();
}
catch( \UnderflowException $e )
{
/* Post is orphaned */
$libraryClass->setLastKeyValue( $row['attachmentID'] );
continue;
}
$map = array(
'id1' => $topicId,
'id2' => $row['objectID'],
);
$info = array(
'attach_id' => $row['attachmentID'],
'attach_file' => $row['filename'],
'attach_date' => $row['uploadTime'],
'attach_member_id' => $row['userID'],
'attach_hits' => $row['downloads'],
'attach_filesize' => $row['filesize'],
);
$attachId = $libraryClass->convertAttachment( $info, $map, rtrim( $this->app->_session['more_info']['convertAttachments']['file_location'], '/' ) . '/' . mb_substr( $row['fileHash'], 0, 2 ) . '/' . $row['attachmentID'] . '-' . $row['fileHash'] );
/* Update Post if we can */
try
{
if ( $attachId !== FALSE )
{
$pid = $this->app->getLink( $row['objectID'], 'forums_posts' );
$post = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
if( \preg_match( '#\[attach]#i', $post ) )
{
$post = str_ireplace( '[attach]' . $row['attachmentID'] . '[/attach]', '[attachment=' . $attachId . ':name]', $post );
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $post ), array( "pid=?", $pid ) );
}
}
}
catch( \UnderflowException | \OutOfRangeException $e ) {}
$libraryClass->setLastKeyValue( $row['attachmentID'] );
}
}
/**
* Check if we can redirect the legacy URLs from this software to the new locations
*
* @return NULL|\IPS\Http\Url
*/
public function checkRedirects()
{
$url = \IPS\Request::i()->url();
if( preg_match( '#/thread/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
if( !empty( \IPS\Request::i()->postID ) )
{
$class = '\IPS\forums\Topic\Post';
$types = array( 'posts', 'forums_posts' );
$oldId = (int) \IPS\Request::i()->postID;
}
else
{
$class = '\IPS\forums\Topic';
$types = array( 'topics', 'forums_topics' );
$oldId = (int) $matches[1];
}
}
elseif( preg_match( '#/board/([0-9]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
$class = '\IPS\forums\Forum';
$types = array( 'forums', 'forums_forums' );
$oldId = (int) $matches[1];
}
if( isset( $class ) )
{
try
{
try
{
$data = (string) $this->app->getLink( $oldId, $types );
}
catch( \OutOfRangeException $e )
{
$data = (string) $this->app->getLink( $oldId, $types, FALSE, TRUE );
}
$item = $class::load( $data );
if( $item instanceof \IPS\Content )
{
if( $item->canView() )
{
return $item->url();
}
}
elseif( $item instanceof \IPS\Node\Model )
{
if( $item->can( 'view' ) )
{
return $item->url();
}
}
}
catch( \Exception $e )
{
return NULL;
}
}
return NULL;
}
}
@@ -5,21 +5,26 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Forums;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo Forums Converter
*/
class _Xenforo extends \IPS\convert\Software
{
use \IPS\convert\Tools\Xenforo;
/**
* @brief The similarities between XF1 and XF2 are close enough that we can use the same converter
*/
@@ -30,11 +35,16 @@ class _Xenforo extends \IPS\convert\Software
*/
public static $contentFixed = TRUE;
/**
* @brief XF2.1 changed serialized data to json decoded
*/
public static $useJson = FALSE;
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -49,16 +59,24 @@ 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( "addon_id=?", 'XF' ) )->first();
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( \IPS\Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
if ( $version < 2000010 )
{
/* XF1 */
static::$isLegacy = TRUE;
}
else
{
/* XF2 */
static::$isLegacy = FALSE;
}
/* Is this XF 2.1 */
if ( $version > 2010010 )
{
static::$useJson = TRUE;
}
}
}
catch( \Exception $e ) {} # If we can't query, we won't be able to do anything anyway
@@ -75,7 +93,7 @@ class _Xenforo extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "XenForo (1.5.x/2.0.x)";
return "XenForo (1.5.x/2.0.x/2.1.x)";
}
/**
@@ -143,10 +161,16 @@ class _Xenforo extends \IPS\convert\Software
*/
public static function checkConf()
{
return array(
'convertAttachments',
'convertForumsPosts'
$return = array(
'convertAttachments'
);
if( !static::$useJson )
{
$return[] = 'convertForumsPosts';
}
return $return;
}
/**
@@ -161,25 +185,31 @@ class _Xenforo extends \IPS\convert\Software
switch( $method )
{
case 'convertForumsPosts':
/* Get our reactions to let the admin map them */
$options = array();
$descriptions = array();
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_reactions' ), 'IPS\Content\Reaction' ) AS $reaction )
/* XF 1 & 2 */
if( !static::$useJson )
{
$options[ $reaction->id ] = $reaction->_icon->url;
$descriptions[ $reaction->id ] = \IPS\Member::loggedIn()->language()->addToStack('reaction_title_' . $reaction->id ) . '<br>' . $reaction->_description;
/* Get our reactions to let the admin map them */
$options = array();
$descriptions = array();
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_reactions' ), 'IPS\Content\Reaction' ) AS $reaction )
{
$options[ $reaction->id ] = $reaction->_icon->url;
$descriptions[ $reaction->id ] = \IPS\Member::loggedIn()->language()->addToStack('reaction_title_' . $reaction->id );
}
$return['convertForumsPosts'] = array(
'rep_like' => array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
),
);
}
$return['convertForumsPosts'] = array(
'rep_like' => array(
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_hint' => NULL,
'field_validation' => NULL,
),
);
break;
case 'convertAttachments':
@@ -207,11 +237,11 @@ class _Xenforo extends \IPS\convert\Software
public function finish()
{
/* Content Rebuilds */
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\forums\Forum', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildContent', array( 'app' => $this->app->app_id, 'link' => 'forums_posts', 'class' => 'IPS\forums\Topic\Post' ), 2, array( 'app', 'link', 'class' ) );
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\forums\Topic' ), 3, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildFirstPostIds', array( 'app' => $this->app->app_id ), 2, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 4, array( 'app' ) );
\IPS\Task::queue( 'convert', 'DeleteEmptyTopics', array( 'app' => $this->app->app_id ), 5, array( 'app' ) );
/* Caches */
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'forums_topics', 'class' => 'IPS\forums\Topic' ), 3, array( 'app', 'link', 'class' ) );
@@ -222,8 +252,8 @@ class _Xenforo extends \IPS\convert\Software
/**
* Fix post data
*
* @param string raw post data
* @return string parsed post data
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
@@ -241,7 +271,7 @@ class _Xenforo extends \IPS\convert\Software
{
try
{
$title = ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
$title = ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
return $this->db->select( 'phrase_text', 'xf_phrase', array( "title=?", $title ) )->first();
}
catch( \UnderflowException $e )
@@ -332,10 +362,11 @@ class _Xenforo extends \IPS\convert\Software
public function convertForumsTopics()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'thread_id' );
foreach( $this->fetch( 'xf_thread', 'thread_id' ) AS $row )
$data = iterator_to_array( $this->fetch( 'xf_thread', 'thread_id' ) );
$this->app->preCacheLinks( $data, [ 'forums_forums' => 'node_id', 'core_members' => [ 'user_id', 'last_post_user_id' ] ] );
foreach( $data AS $row )
{
/* Poll */
$poll = NULL;
@@ -463,13 +494,13 @@ class _Xenforo extends \IPS\convert\Software
'follow_visible' => 1,
) );
}
/* Tag Prefix */
if ( $row['prefix_id'] )
{
$prefix = $this->getPhrase( "thread_prefix_{$row['prefix_id']}", "thread_prefix.{$row['prefix_id']}" );
if ( !is_null( $prefix ) )
if ( !\is_null( $prefix ) )
{
$libraryClass->convertTag( array(
'tag_meta_app' => 'forums',
@@ -486,8 +517,8 @@ class _Xenforo extends \IPS\convert\Software
/* Other tags */
if ( $row['tags'] )
{
$tags = \unserialize( $row['tags'] );
if ( count( $tags ) )
$tags = static::unpack( $row['tags'] );
if ( \count( $tags ) )
{
foreach( $tags AS $key => $tag )
{
@@ -518,30 +549,17 @@ class _Xenforo extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'post_id' );
foreach( $this->fetch( 'xf_post', 'post_id' ) AS $row )
$posts = iterator_to_array( $this->fetch( 'xf_post', 'post_id', null, 'xf_post.*, xf_ip.ip, xf_thread.discussion_state' )
->join( 'xf_ip', 'xf_post.ip_id=xf_ip.ip_id' )
->join( 'xf_thread', 'xf_post.thread_id=xf_thread.thread_id' ) );
$this->app->preCacheLinks( $posts, [ 'forums_topics' => 'thread_id', 'core_members' => 'user_id' ] );
foreach( $posts AS $row )
{
/* Query for the IP */
try
{
$ip = $this->db->select( 'ip', 'xf_ip', array( "ip_id=?", $row['ip_id'] ) )->first();
}
catch( \UnderflowException $e )
{
$ip = '127.0.0.1';
}
/* Figure out queued stuff */
try
if ( $row['discussion_state'] == 'deleted' )
{
$topic_state = $this->db->select( 'discussion_state', 'xf_thread', array( "thread_id=?", $row['thread_id'] ) )->first();
if ( $topic_state == 'deleted' )
{
$row['message_state'] = 'topic_hidden';
}
$row['message_state'] = 'topic_hidden';
}
catch( \UnderflowException $e ) {}
switch( $row['message_state'] )
{
@@ -578,29 +596,51 @@ class _Xenforo extends \IPS\convert\Software
'post' => static::fixPostData( $row['message'] ),
'edit_time' => $row['last_edit_date'],
'edit_name' => $editName,
'append_edit' => $editName ? 1 : NULL,
'author_id' => $row['user_id'],
'author_name' => $row['username'],
'ip_address' => $ip,
'ip_address' => $row['ip'] ?? '127.0.0.1',
'post_date' => $row['post_date'],
'queued' => $queued,
);
$post_id = $libraryClass->convertForumsPost( $info );
$libraryClass->convertForumsPost( $info );
/* Reputation */
$likes = \unserialize( $row['like_users'] );
if ( count( $likes ) )
if ( !static::$useJson )
{
foreach( $likes AS $like )
$likes = \unserialize( $row['like_users'] );
if ( \is_array( $likes ) AND \count( $likes ) )
{
foreach( $likes AS $like )
{
$libraryClass->convertReputation( array(
'app' => 'forums',
'type' => 'pid',
'type_id' => $row['post_id'],
'member_id' => $like['user_id'],
'member_received' => $row['user_id'],
'reaction' => $this->app->_session['more_info']['convertForumsPosts']['rep_like'],
'rep_date' => $row['post_date']
) );
}
}
}
else
{
$reputation = iterator_to_array( $this->db->select( '*', 'xf_reaction_content', array( "content_type=? AND content_id=?", 'post', $row['post_id'] ) ) );
$this->app->preCacheLinks( $reputation, [ 'core_members' => 'reaction_user_id' ] );
foreach( $reputation as $reaction )
{
$libraryClass->convertReputation( array(
'id' => $reaction['reaction_content_id'],
'app' => 'forums',
'type' => 'pid',
'type_id' => $row['post_id'],
'member_id' => $like['user_id'],
'member_id' => $reaction['reaction_user_id'],
'member_received' => $row['user_id'],
'reaction' => $this->app->_session['more_info']['convertForumsPosts']['rep_like'],
'rep_date' => $row['post_date']
'reaction' => $reaction['reaction_id'],
'rep_date' => $reaction['reaction_date']
) );
}
}
@@ -631,14 +671,13 @@ class _Xenforo extends \IPS\convert\Software
}
/* Edit History */
$latestedit = 0;
$reason = NULL;
$name = NULL;
$newText = static::fixPostData( $row['message'] );
$newText = $info['post'];
foreach( $this->db->select( '*', 'xf_edit_history', array( "content_type=? AND content_id=?", 'post', $row['post_id'] ) ) AS $edit )
{
$libraryClass->convertEditHistory( array(
$editHistory = array(
'id' => $edit['edit_history_id'],
'class' => 'IPS\\forums\\Topic\\Post',
'comment_id' => $row['post_id'],
@@ -646,20 +685,9 @@ class _Xenforo extends \IPS\convert\Software
'time' => $edit['edit_date'],
'old' => static::fixPostData( $edit['old_text'] ),
'new' => $newText
) );
$newText = static::fixPostData( $edit['old_text'] );
if ( $edit['edit_date'] > $latestedit )
{
$latestedit = $edit['edit_date'];
}
}
/* If we have a latest edit, then update the main post - this should really be in the library, as the converters should not be altering data */
if ( $latestedit )
{
\IPS\Db::i()->update( 'forums_posts', array( 'append_edit' => 1, 'edit_time' => $latestedit ), array( "pid=?", $post_id ) );
);
$libraryClass->convertEditHistory( $editHistory );
$newText = $editHistory['old'];
}
$libraryClass->setLastKeyValue( $row['post_id'] );
@@ -716,9 +744,9 @@ class _Xenforo extends \IPS\convert\Software
$post = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
if ( preg_match( "/\[ATTACH(.+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", $post ) )
if ( preg_match( "/\[ATTACH([^\]]+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", $post ) )
{
$post = preg_replace( "/\[ATTACH(.+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", '[attachment=' . $attachId . ':name]', $post );
$post = preg_replace( "/\[ATTACH([^\]]+?)?\]" . $row['attachment_id'] . "\[\/ATTACH\]/i", '[attachment=' . $attachId . ':name]', $post );
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $post ), array( "pid=?", $pid ) );
}
}
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Gallery;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Coppermine Gallery Converter
*/
class _Coppermine extends \IPS\convert\Software
{
/**
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 02 October 2016
*/
namespace IPS\convert\Software\Gallery;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Photoplog Gallery Converter
*/
class _Photoplog extends \IPS\convert\Software
{
/**
@@ -105,7 +108,7 @@ class _Photoplog extends \IPS\convert\Software
/**
* Finish - Adds everything it needs to the queues and clears data store
*
* @return string Message to display
* @return array Messages to display
*/
public function finish()
{
@@ -235,7 +238,7 @@ class _Photoplog extends \IPS\convert\Software
$album = \unserialize( $row['albumids'] );
$albumId = NULL;
if( is_array( $album ) )
if( \is_array( $album ) )
{
$albumId = $album[0];
}
@@ -250,7 +253,7 @@ class _Photoplog extends \IPS\convert\Software
'image_comments' => $row['num_comments0'] + $row['num_comments1'],
'image_ratings_total' => $row['sum_ratings1'],
'image_ratings_count' => $row['num_ratings1'],
'image_rating' => $row['num_ratings1'] > 0 ? intval( $row['sum_ratings1'] / $row['num_ratings1'] ) : 0,
'image_rating' => $row['num_ratings1'] > 0 ? \intval( $row['sum_ratings1'] / $row['num_ratings1'] ) : 0,
'image_date' => $row['dateline'],
'image_metadata' => json_encode( \unserialize( $row['exifinfo'] ) ),
'image_file_name' => $row['filename'],
@@ -5,7 +5,7 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 6 December 2016
* @note Only redirect scripts are supported right now
*/
@@ -13,12 +13,15 @@
namespace IPS\convert\Software\Gallery;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Photopost Gallery Converter
*/
class _Photopost extends \IPS\convert\Software
{
/**
@@ -66,7 +69,7 @@ class _Photopost extends \IPS\convert\Software
/**
* Content we can convert from this software.
*
* @return NULL
* @return array
*/
public static function canConvert()
{
@@ -324,7 +327,8 @@ class _Photopost extends \IPS\convert\Software
return NULL;
}
}
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'showphoto.php' ) !== FALSE )
elseif( mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'showphoto.php' ) !== FALSE OR
mb_strpos( $url->data[ \IPS\Http\Url::COMPONENT_PATH ], 'showfull.php' ) !== FALSE)
{
if( !$id = \IPS\Request::i()->photo )
{
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Gallery;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* vBulletin Gallery Converter
*/
class _Vbulletin extends \IPS\convert\Software
{
/**
@@ -33,8 +36,8 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -63,7 +66,7 @@ class _Vbulletin extends \IPS\convert\Software
/* If this is vB4, what is the content type ID for posts? */
if ( static::$imageContentType === NULL AND ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) )
if ( static::$imageContentType === NULL AND ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) )
{
static::$imageContentType = $this->db->select( 'contenttypeid', 'contenttype', array( "class=?", 'Album' ) )->first();
}
@@ -161,6 +164,8 @@ class _Vbulletin extends \IPS\convert\Software
\IPS\Task::queue( 'core', 'RebuildItemCounts', array( 'class' => 'IPS\gallery\Image' ), 3, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\gallery\Album', 'count' => 0 ), 4, array( 'class' ) );
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\gallery\Category', 'count' => 0 ), 5, array( 'class' ) );
\IPS\Task::queue( 'convert', 'RebuildNonContent', array( 'app' => $this->app->app_id, 'link' => 'gallery_albums', 'extension' => 'gallery_Albums' ), 2, array( 'app', 'link', 'extension' ) );
/* Caches */
\IPS\Task::queue( 'convert', 'RebuildTagCache', array( 'app' => $this->app->app_id, 'link' => 'gallery_images', 'class' => 'IPS\gallery\Image' ), 3, array( 'app', 'link', 'class' ) );
@@ -171,7 +176,7 @@ class _Vbulletin extends \IPS\convert\Software
/**
* Fix Post Data
*
* @param string Post
* @param string $post Post
* @return string Fixed Posts
*/
public static function fixPostData( $post )
@@ -198,8 +203,8 @@ class _Vbulletin extends \IPS\convert\Software
'field_required' => TRUE,
'field_extra' => array(
'options' => array(
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'file_system' ),
'database' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_database' ),
'file_system' => \IPS\Member::loggedIn()->language()->addToStack( 'conv_store_file_system' ),
),
'userSuppliedInput' => 'file_system',
),
@@ -5,19 +5,22 @@
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @package Invision Community
* @subpackage Converter
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\Software\Gallery;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Xenforo Gallery Converter
*/
class _Xenforo extends \IPS\convert\Software
{
/**
@@ -30,11 +33,16 @@ class _Xenforo extends \IPS\convert\Software
*/
public static $tablePrefix = 'xengallery_';
/**
* @brief XF2.1 changed serialized data to json decoded
*/
public static $useJson = FALSE;
/**
* Constructor
*
* @param \IPS\convert\App The application to reference for database and other information.
* @param bool Establish a DB connection
* @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
*/
@@ -49,7 +57,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( "addon_id=?", 'XF' ) )->first();
$version = $this->db->select( 'MAX(version_id)', 'xf_template', array( \IPS\Db::i()->in( 'addon_id', array( 'XF', 'XenForo' ) ) ) )->first();
if ( $version < 2000010 )
{
@@ -60,6 +68,11 @@ class _Xenforo extends \IPS\convert\Software
static::$tablePrefix = 'xf_mg_';
static::$isLegacy = FALSE;
}
/* Is this XF 2.1 */
if ( $version > 2010010 )
{
static::$useJson = TRUE;
}
}
}
catch( \Exception $e ) {} # If we can't query, we won't be able to do anything anyway
@@ -76,7 +89,7 @@ class _Xenforo extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "XenForo Media Gallery (1.5.x/2.0.x)";
return "XenForo Media Gallery (1.5.x/2.0.x/2.1.x)";
}
/**
@@ -251,7 +264,7 @@ class _Xenforo extends \IPS\convert\Software
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions ),
'field_extra' => array( 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ),
'field_hint' => NULL,
'field_validation' => NULL,
);
@@ -292,7 +305,7 @@ class _Xenforo extends \IPS\convert\Software
{
try
{
$title = ( static::$isLegacy === FALSE OR is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
$title = ( static::$isLegacy === FALSE OR \is_null( static::$isLegacy ) ) ? $xfTwoTitle : $xfOneTitle;
return $this->db->select( 'phrase_text', 'xf_phrase', array( "title=?", $title ) )->first();
}
catch( \UnderflowException $e )
@@ -319,7 +332,7 @@ class _Xenforo extends \IPS\convert\Software
'category_name' => isset( $row['category_title'] ) ? $row['category_title'] : $row['title'],
'category_desc' => isset( $row['category_description'] ) ? $row['category_description'] : $row['description'],
'category_parent_id' => $row['parent_category_id'],
'category_count_imgs' => isset( $row['category_media_count'] ) ? $row['category_media_count'] : $row['category_media_count'],
'category_count_imgs' => isset( $row['category_media_count'] ) ? $row['category_media_count'] : $row['media_count'],
'category_position' => $row['display_order']
) );
@@ -394,7 +407,7 @@ class _Xenforo extends \IPS\convert\Software
$users = $perm['share_users'];
$socialgroup['members'] = array();
if ( count( $users ) )
if ( \count( $users ) )
{
foreach( $users AS $key => $user )
{
@@ -433,7 +446,7 @@ class _Xenforo extends \IPS\convert\Software
$info = array(
'album_id' => $row['album_id'],
'album_owner_id' => $row['album_user_id'],
'album_owner_id' => isset( $row['album_user_id'] ) ? $row['album_user_id'] : $row['user_id'],
'album_description' => isset( $row['album_description'] ) ? $row['album_description'] : $row['description'],
'album_name' => isset( $row['album_title'] ) ? $row['album_title'] : $row['title'],
'album_type' => $type,
@@ -441,7 +454,7 @@ class _Xenforo extends \IPS\convert\Software
'album_count_comments' => isset( $row['album_comment_count'] ) ? $row['album_comment_count'] : $row['comment_count'],
'album_rating_aggregate' => isset( $row['album_rating_avg'] ) ? $row['album_rating_avg'] : $row['rating_avg'],
'album_rating_count' => isset( $row['album_rating_count'] ) ? $row['album_rating_count'] : $row['rating_count'],
'album_rating_total' => isset( $row['album_rating_sum'] ) ? $row['album_rating_sum'] : $row['ating_sum'],
'album_rating_total' => isset( $row['album_rating_sum'] ) ? $row['album_rating_sum'] : $row['rating_sum'],
);
$category = $this->app->_session['more_info']['convertGalleryAlbums']['members_gallery_category'];
@@ -486,7 +499,7 @@ class _Xenforo extends \IPS\convert\Software
$libraryClass::setKey( 'media_id' );
foreach( $this->fetch( static::$isLegacy ? 'xf_gallery_media' : 'xf_mg_media_item', 'media_id' ) AS $row )
foreach( $this->fetch( static::$isLegacy ? 'xengallery_media' : 'xf_mg_media_item', 'media_id' ) AS $row )
{
/* Exif */
$exif = array();
@@ -500,11 +513,16 @@ class _Xenforo extends \IPS\convert\Software
/* XF2 */
else
{
foreach( json_decode( $row['exif_data'], TRUE ) AS $key => $value )
$exifData = json_decode( $row['exif_data'], TRUE );
if( $exifData )
{
foreach( $value as $subKey => $subValue )
foreach( $exifData AS $key => $value )
{
$exif[ $key . '.' . $subKey ] = $subValue;
foreach( $value as $subKey => $subValue )
{
$exif[ $key . '.' . $subKey ] = $subValue;
}
}
}
}
@@ -561,7 +579,9 @@ class _Xenforo extends \IPS\convert\Software
try
{
$em = \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( $row['media_embed_url'] ) );
$memberId = $this->app->getLink( $row['user_id'], 'core_members', TRUE );
$em = \IPS\Text\Parser::embeddableMedia( \IPS\Http\Url::createFromString( $row['media_embed_url'] ), FALSE, \IPS\Member::load( $memberId ) );
}
catch( \Exception $e )
{
@@ -624,8 +644,15 @@ class _Xenforo extends \IPS\convert\Software
}
/* Reputation */
$likes = \unserialize( $row['like_users'] );
if ( is_array( $likes ) AND count( $likes ) )
if ( !static::$useJson )
{
$likes = \unserialize( $row['like_users'] );
}
else
{
$likes = \json_decode( $row['reaction_score'], TRUE );
}
if ( \is_array( $likes ) AND \count( $likes ) )
{
foreach( $likes AS $like )
{
@@ -672,7 +699,7 @@ class _Xenforo extends \IPS\convert\Software
$tags = \unserialize( $row['tags'] );
}
if ( is_array( $tags ) and count( $tags ) )
if ( \is_array( $tags ) and \count( $tags ) )
{
foreach( $tags AS $k => $tag )
{
@@ -743,8 +770,15 @@ class _Xenforo extends \IPS\convert\Software
) );
/* Reputation */
$likes = \unserialize( $row['like_users'] );
if ( is_array( $likes ) AND count( $likes ) )
if ( !static::$useJson )
{
$likes = \unserialize( $row['like_users'] );
}
else
{
$likes = \json_decode( $row['reaction_score'], TRUE );
}
if ( \is_array( $likes ) AND \count( $likes ) )
{
foreach( $likes AS $like )
{