Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

+1 -1
View File
@@ -115,7 +115,7 @@ class _Application extends \IPS\Application
$handler->classname = 'IPS\\convert\\Login';
$handler->order = $position + 1;
$handler->acp = TRUE;
$handler->settings = array( 'auth_types' => \IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL );
$handler->settings = array( 'auth_types' => \IPS\Login::AUTH_TYPE_EMAIL );
$handler->enabled = TRUE;
$handler->register = FALSE;
$handler->save();
+2 -1
View File
@@ -4,5 +4,6 @@
"app_directory": "convert",
"app_protected": 0,
"app_website": null,
"app_update_check": null
"app_update_check": null,
"app_hide_tab": 0
}
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<javascript app="convert">
<file javascript_app="convert" javascript_location="admin" javascript_path="controllers/convert" javascript_name="ips.convert.menu.js" javascript_type="controller" javascript_version="105100" javascript_position="1000100">/**
<file javascript_app="convert" javascript_location="admin" javascript_path="controllers/convert" javascript_name="ips.convert.menu.js" javascript_type="controller" javascript_version="106107" javascript_position="1000100">/**
* Invision Community
* (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
*
@@ -45,7 +45,7 @@
}
} );
}(jQuery, _));</file>
<file javascript_app="convert" javascript_location="admin" javascript_path="controllers/forms" javascript_name="ips.forms.reactionmapper.js" javascript_type="controller" javascript_version="105100" javascript_position="1000050">/**
<file javascript_app="convert" javascript_location="admin" javascript_path="controllers/forms" javascript_name="ips.forms.reactionmapper.js" javascript_type="controller" javascript_version="106107" javascript_position="1000050">/**
* Invision Community
* (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
*
+5 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<language>
<app key="convert" version="105100">
<app key="convert" version="106107">
<word key="__app_convert" js="0">Converter</word>
<word key="menu__convert_manage" js="0">Conversions</word>
<word key="menu__convert_manage_manage_start" js="0">Start New Conversion</word>
@@ -159,6 +159,10 @@
<word key="waiting_previous_process" js="0">Waiting for a previous conversion cycle to finish</word>
<word key="no_conversion_app" js="0">The conversion you attempted to run could not be found.</word>
<word key="converter_could_not_continue" js="0"><![CDATA[There was a problem proceeding with the conversion, a previous cycle was still running. Please go back to <a href='{internal.app=convert&module=manage&controller=manage}'>the conversion</a> to continue.]]></word>
<word key="convert_achievement_badges" js="0">Convert Badges</word>
<word key="_convert_achievement_badges" js="0">Badges</word>
<word key="convert_achievement_ranks" js="0">Convert Achievement Ranks</word>
<word key="_convert_achievement_ranks" js="0">Achievement Ranks</word>
<word key="convert_custom_bbcode" js="0">Convert Custom BBCode</word>
<word key="_convert_custom_bbcode" js="0">Custom BBCode</word>
<word key="convert_emoticons" js="0">Convert Emoticons</word>
+2 -4
View File
@@ -855,7 +855,7 @@
},
"log_message": {
"name": "log_message",
"type": "VARCHAR",
"type": "TEXT",
"length": 255,
"decimals": null,
"values": [],
@@ -863,9 +863,7 @@
"default": null,
"comment": "Log Message",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
"auto_increment": false
},
"log_app": {
"name": "log_app",
+39 -1
View File
@@ -230,6 +230,7 @@
"104067": "4.4.10",
"104068": "4.4.10",
"104069": "4.4.10",
"104070": "4.4.11",
"105013": "4.5.0 Beta 1",
"105014": "4.5.0 Beta 1",
"105015": "4.5.0 Beta 2",
@@ -250,5 +251,42 @@
"105030": "4.5.0 Beta 11",
"105031": "4.5.0 Beta 11",
"105032": "4.5.0 Beta 12",
"105100": "4.5.0"
"105100": "4.5.0",
"105101": "4.5.0",
"105102": "4.5.1 Beta 1",
"105103": "4.5.1 Beta 1",
"105104": "4.5.1 Beta 2",
"105105": "4.5.1 Beta 3",
"105106": "4.5.1",
"105107": "4.5.2",
"105108": "4.5.2",
"105109": "4.5.2",
"105110": "4.5.2",
"105111": "4.5.2",
"105112": "4.5.3",
"105113": "4.5.3",
"105114": "4.5.3",
"105115": "4.5.3",
"105116": "4.5.4",
"105117": "4.5.4",
"105118": "4.5.4",
"105119": "4.5.4",
"105120": "4.5.4",
"105121": "4.5.4",
"105122": "4.5.4",
"105123": "4.5.4",
"105124": "4.5.4.1",
"105125": "4.5.4.2",
"105126": "4.5.4.2",
"105127": "4.5.4.2",
"105128": "4.5.4.2",
"105129": "4.5.4.2",
"105130": "4.5.4.2",
"106100": "4.6.0 Beta 1",
"106101": "4.6.0 Beta 1.1",
"106103": "4.6.0 Beta 2",
"106104": "4.6.0 Beta 3",
"106105": "4.6.0 Beta 3.1",
"106106": "4.6.0 Beta 4",
"106107": "4.6.0"
}
+1 -1
View File
@@ -18,7 +18,7 @@ class convert_hook_Db extends _HOOK_CLASS_
* @return \IPS\Db\Select
*
*/
public function select( $columns=NULL, $table, $where=NULL, $order=NULL, $limit=NULL, $group=NULL, $having=NULL, $flags=0 )
public function select( $columns, $table, $where=NULL, $order=NULL, $limit=NULL, $group=NULL, $having=NULL, $flags=0 )
{
switch( $table )
{
@@ -71,6 +71,12 @@ class _convert extends \IPS\Dispatcher\Controller
/* Do a quick parent storage check */
\IPS\convert\Application::checkConvParent( $app->getSource()->getLibrary()->app );
/* Check child apps also have temporary columns */
foreach( $app->children() as $child )
{
\IPS\convert\Application::checkConvParent( $child->getSource()->getLibrary()->app );
}
/* Are we using the legacy UI? If not, then start converting as we already have everything we need. */
if( !\IPS\CONVERTERS_DEV_UI )
{
@@ -248,15 +254,15 @@ class _convert extends \IPS\Dispatcher\Controller
$softwareClass = $appToCheck->getSource();
/* Set up cache array */
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ] ) )
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->app_id ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ] = array();
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->app_id ] = array();
}
if( !$appToCheck->parent AND $softwareClass::canConvertSettings() AND isset( $appToCheck->_session['more_info']['convertSettings'] ) AND $appToCheck->_session['more_info']['convertSettings']['convert_settings'] AND !\count( $appToCheck->_session['completed'] ) )
{
$method = 'convertSettings';
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ][ $method ] = 1;
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->app_id ][ $method ] = 1;
}
/* First, get all of the menu rows for this application to see if there are any (more) to run.
@@ -266,11 +272,11 @@ class _convert extends \IPS\Dispatcher\Controller
foreach( $menuRows as $row )
{
/* We need the total counts so we can show the progress bar on the multiredirect */
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ][ $row['step_method'] ] ) )
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->app_id ][ $row['step_method'] ] ) )
{
/* Only count the rows if the cache is missing */
$counts = $softwareClass->getLibrary()->getDatabaseRowCounts( array( $row ), FALSE, TRUE );
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ][ $row['step_method'] ] = $counts[0]['source_rows'];
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->app_id ][ $row['step_method'] ] = $counts[0]['source_rows'];
}
if( $method !== NULL )
@@ -287,16 +293,15 @@ class _convert extends \IPS\Dispatcher\Controller
continue;
}
/* We chose to convert a specific step and it's not complete */
if( isset( \IPS\Request::i()->method ) AND $row['step_method'] == \IPS\Request::i()->method )
{
$method = $row['step_method'];
$session = $app->_session;
if( $key = array_search( $method, $session['completed'] ) )
if( !\in_array( $row['step_method'], $appToCheck->_session['completed'] ) )
{
unset( $session['completed'][ $key ] );
$app->_session = $session;
$method = $row['step_method'];
}
continue;
}
/* Check whether the dependency was set to be converted, or whether it actually converted (it may not have any source data) */
@@ -486,6 +491,20 @@ class _convert extends \IPS\Dispatcher\Controller
/* The function we are running on each step */
function( $data ) use ( $app )
{
/* Are we reconverting using the dev ui and this is the first iteration? */
if( $data === NULL )
{
if( isset( \IPS\Request::i()->method ) )
{
$session = $app->_session;
if( $key = array_search( \IPS\Request::i()->method, $session['completed'] ) )
{
unset( $session['completed'][ $key ] );
$app->_session = $session;
}
}
}
/* Determine which method we are converting */
$convertData = $this->_getNextStep( $app );
@@ -601,7 +620,7 @@ class _convert extends \IPS\Dispatcher\Controller
$app->saveMoreInfo( \IPS\Request::i()->method, $values );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=runStep&id={$app->app_id}&per_cycle={$per_cycle}&method=" . \IPS\Request::i()->method ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=runStep&id={$app->app_id}&per_cycle={$per_cycle}&method=" . \IPS\Request::i()->method )->csrf() );
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'more_info_needed' );
@@ -253,6 +253,7 @@ class _create extends \IPS\Dispatcher\Controller
}
$appsToCheck = array( $app );
$childAppsToRemove = array();
foreach( $data['choose_what_to_convert'] as $application )
{
@@ -297,8 +298,15 @@ class _create extends \IPS\Dispatcher\Controller
/* Can't find the table */
catch( \IPS\convert\Exception $e )
{
$form->error = $e->getMessage();
return $form;
if( $appToCheck->sw != 'core' )
{
$childAppsToRemove[] = $appToCheck->sw;
}
else
{
$form->error = $e->getMessage();
return $form;
}
}
}
}
@@ -361,6 +369,11 @@ class _create extends \IPS\Dispatcher\Controller
continue;
}
if( \in_array( $application, $childAppsToRemove ) )
{
continue;
}
$childApp = clone $app;
$childApp->parent = $app->app_id;
@@ -43,7 +43,7 @@ class _Upgrade
$handler->classname = 'IPS\\convert\\Login';
$handler->order = $position + 1;
$handler->acp = TRUE;
$handler->settings = array( 'auth_types' => \IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL );
$handler->settings = array( 'auth_types' => \IPS\Login::AUTH_TYPE_EMAIL );
$handler->enabled = TRUE;
$handler->register = FALSE;
$handler->save();
@@ -0,0 +1,24 @@
{
"id": 105101,
"name": "4.5.0",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105102,
"name": "4.5.1 Beta 1",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105103,
"name": "4.5.1 Beta 1",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105104,
"name": "4.5.1 Beta 2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105105,
"name": "4.5.1 Beta 3",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105106,
"name": "4.5.1",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105107,
"name": "4.5.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105108,
"name": "4.5.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105109,
"name": "4.5.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105110,
"name": "4.5.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105111,
"name": "4.5.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105112,
"name": "4.5.3",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105113,
"name": "4.5.3",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105114,
"name": "4.5.3",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105115,
"name": "4.5.3",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105116,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105117,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105118,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105119,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105120,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105121,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105122,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105123,
"name": "4.5.4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105124,
"name": "4.5.4.1",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105125,
"name": "4.5.4.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105126,
"name": "4.5.4.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105127,
"name": "4.5.4.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105128,
"name": "4.5.4.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105129,
"name": "4.5.4.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 105130,
"name": "4.5.4.2",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 106100,
"name": "4.6.0 Beta 1",
"steps": {
"queries": true,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,21 @@
{
"1": {
"method": "changeColumn",
"params": [
"convert_logs",
"log_message",
{
"name": "log_message",
"type": "TEXT",
"length": 255,
"decimals": null,
"values": [],
"allow_null": true,
"default": null,
"comment": "Log Message",
"unsigned": false,
"auto_increment": false
}
]
}
}
@@ -0,0 +1,24 @@
{
"id": 106101,
"name": "4.6.0 Beta 1.1",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 106103,
"name": "4.6.0 Beta 2",
"steps": {
"queries": false,
"lang": true,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,17 @@
{
"normal": {
"added": [
"convert_achievement_badges",
"_convert_achievement_badges",
"convert_achievement_ranks",
"_convert_achievement_ranks"
],
"edited": [],
"removed": []
},
"js": {
"added": [],
"edited": [],
"removed": []
}
}
@@ -0,0 +1,24 @@
{
"id": 106104,
"name": "4.6.0 Beta 3",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 106105,
"name": "4.6.0 Beta 3.1",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 106106,
"name": "4.6.0 Beta 4",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
@@ -0,0 +1,24 @@
{
"id": 106107,
"name": "4.6.0",
"steps": {
"queries": false,
"lang": false,
"theme": false,
"themeSettings": false,
"javascript": false,
"emailTemplates": false,
"hooks": false,
"acpSearchKeywords": false,
"settings": false,
"tasks": false,
"modules": false,
"widgets": false,
"whatsnew": false,
"customOptions": false,
"customRoutines": false
},
"forceMainUpgrader": false,
"forceManualDownloadNoCiC": false,
"forceManualDownloadCiC": false
}
+1 -1
View File
@@ -445,7 +445,7 @@ class _App extends \IPS\Patterns\ActiveRecord
* @param array $data
* @param array $map
*/
public function preCacheLinks( array $data, array $map ): void
public function preCacheLinks( array $data, array $map )
{
$cachesToLookup = [];
$tables = [];
+7 -7
View File
@@ -164,17 +164,17 @@ abstract class _Library
$_SESSION['convertCountRows'][ $masterAppId ] = array();
}
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ] ) )
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->app_id ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ] = array();
$_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->app_id ] = array();
}
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ][ $method ] ) )
if ( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->app_id ][ $method ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ][ $method ] = $this->software->countRows( static::$action, $canConvert[ $method ]['where'], true );
$_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->app_id ][ $method ] = $this->software->countRows( static::$action, $canConvert[ $method ]['where'], true );
}
$total = $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->sw ][ $method ];
$total = $_SESSION['convertCountRows'][ $masterAppId ][ $this->software->app->app_id ][ $method ];
if ( $data >= $total )
{
@@ -363,13 +363,13 @@ abstract class _Library
foreach( $applicationsToCheck as $app )
{
/* Check app has cached data */
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $app->sw ] ) )
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $app->app_id ] ) )
{
continue;
}
$totalSoFar += array_sum(
array_filter( $_SESSION['convertCountRows'][ $masterAppId ][ $app->sw ], function( $key ) use( $app ) {
array_filter( $_SESSION['convertCountRows'][ $masterAppId ][ $app->app_id ], function( $key ) use( $app ) {
return ( \in_array( $key, $app->_session['completed'] ) );
}, ARRAY_FILTER_USE_KEY )
);
+15 -3
View File
@@ -1412,7 +1412,6 @@ class _Cms extends Core
switch( $data['type'] )
{
case 'CheckboxSet':
case 'Member':
if ( $data['multiple'] )
{
@@ -1440,6 +1439,7 @@ class _Cms extends Core
case 'Address':
case 'Codemirror':
case 'Select':
case 'CheckboxSet':
case 'Youtube':
case 'Spotify':
case 'Soundcloud':
@@ -1469,7 +1469,19 @@ class _Cms extends Core
if ( isset( $data['max_length'] ) AND $data['max_length'] )
{
$columnDefinition['length'] = $data['max_length'];
if( $data['max_length'] > 255 )
{
$columnDefinition['type'] = 'MEDIUMTEXT';
if( isset( $columnDefinition['length'] ) )
{
unset( $columnDefinition['length'] );
}
}
else
{
$columnDefinition['length'] = $data['max_length'];
}
}
\IPS\Db::i()->addColumn( "cms_custom_database_{$inserted_id}", $columnDefinition );
@@ -1819,7 +1831,7 @@ class _Cms extends Core
$this->software->app->addLink( $inserted_id, $id, 'cms_database_categories' );
\IPS\Db::i()->update( 'cms_database_categories', array( "category_parent_id" => $inserted_id ), array( "category_conv_parent=?", $id ) );
\IPS\Db::i()->insert( 'core_permission_index', array( 'app' => 'cms', 'perm_type' => 'categories', 'perm_type_id' => $inserted_id, 'perm_view' => '' ) );
\IPS\Db::i()->insert( 'core_permission_index', array( 'app' => 'cms', 'perm_type' => 'categories_' . $info['category_database_id'], 'perm_type_id' => $inserted_id, 'perm_view' => '' ) );
\IPS\Lang::saveCustom( 'cms', "content_cat_name_{$inserted_id}", $info['category_name'] );
\IPS\Lang::saveCustom( 'cms', "content_cat_name_{$inserted_id}_desc", $desc );
+177 -95
View File
@@ -43,6 +43,30 @@ class _Core extends \IPS\convert\Library
{
switch( $k )
{
case 'convertAchievementBadges':
$return[ $k ] = array(
'step_title' => 'convert_achievement_badges',
'step_method' => 'convertAchievementBadges',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_badges' ),
'source_rows' => array( 'table' => $v['table'], 'where' => $v['where'] ),
'per_cycle' => 100,
'dependencies' => array(),
'link_type' => 'core_achievement_badges',
);
break;
case 'convertAchievementRanks':
$return[ $k ] = array(
'step_title' => 'convert_achievement_ranks',
'step_method' => 'convertAchievementRanks',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_member_ranks' ),
'source_rows' => array( 'table' => $v['table'], 'where' => $v['where'] ),
'per_cycle' => 200,
'dependencies' => array(),
'link_type' => 'core_achievement_ranks',
);
break;
case 'convertAcronyms':
$return[ $k ] = array(
'step_title' => 'convert_acronyms',
@@ -89,13 +113,22 @@ class _Core extends \IPS\convert\Library
break;
case 'convertAttachments':
$dependencies = array();
if ( array_key_exists( 'convertPrivateMessageReplies', $this->getConvertableItems() ) )
{
$dependencies[] = 'convertPrivateMessageReplies';
}
$dependencies[] = 'convertMembers';
$return[ $k ] = array(
'step_title' => 'convert_attachments',
'step_method' => 'convertAttachments',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments' ),
'source_rows' => array( 'table' => $v['table'], 'where' => $v['where'] ),
'per_cycle' => 10,
'dependencies' => array( 'convertMembers' ),
'dependencies' => $dependencies,
'link_type' => 'core_attachments',
);
break;
@@ -525,6 +558,14 @@ class _Core extends \IPS\convert\Library
switch( $k )
{
/* Should return multiple array members for each table that needs to be truncated. The key should be the table, while the value should be a WHERE clause, or NULL to completely empty the table */
case 'convertAchievementBadges':
$return['convertAchievementBadges'] = array( 'core_badges' => NULL );
break;
case 'convertAchievementRanks':
$return['convertAchievementRanks'] = array( 'core_member_ranks' => NULL );
break;
case 'convertAcronyms':
$return['convertAcronyms'] = array( 'core_acronyms' => NULL );
break;
@@ -719,6 +760,130 @@ class _Core extends \IPS\convert\Library
* If the data is missing and it is required to convert the item, we should log a WARNING and return FALSE.
* If the conversion absolutely cannot proceed at all (filestorage locations not writable, for example), then we should log an ERROR and throw an \IPS\convert\Exception to completely halt the process and redirect to an error screen showing the last logged error.
*/
/**
* Convert a Badge
*
* @param array $info Data to insert
* @param string|NULL $imagePath Path to image file, or NULL.
* @param string|NULL $imageData Binary image data, or NULL.
* @return bool|int The ID of the newly inserted badge, or FALSE on failure
*/
public function convertAchievementBadge( array $info=[], string $imagePath=NULL, string $imageData=NULL )
{
if ( !isset( $info['id'] ) )
{
$this->software->app->log( 'badge_missing_id', __METHOD__, \IPS\convert\App::LOG_WARNING );
return FALSE;
}
if ( !isset( $info['title'] ) )
{
$this->software->app->log( 'badge_missing_title', __METHOD__, \IPS\convert\App::LOG_WARNING );
return FALSE;
}
else
{
$name = $info['title'];
unset( $info['title'] );
}
if( !isset( $info['manually_awarded'] ) )
{
$info['manually_awarded'] = 0;
}
if ( isset( $info['image'] ) AND ( !\is_null( $imagePath ) OR !\is_null( $imageData ) ) )
{
try
{
if ( \is_null( $imageData ) AND !\is_null( $imagePath ) )
{
$imageData = file_get_contents( $imagePath );
}
$file = \IPS\File::create( 'core_Badges', $info['image'], $imageData, NULL, FALSE, NULL, FALSE );
$info['image'] = (string) $file;
}
catch( \ErrorException | \Exception $e )
{
$info['image'] = NULL;
}
}
else
{
$info['image'] = NULL;
}
$oldId = $info['id'];
unset( $info['id'] );
$insertedId = \IPS\Db::i()->insert( 'core_badges', $info );
$this->software->app->addLink( $insertedId, $oldId, 'core_achievement_badges' );
\IPS\Lang::saveCustom( 'core', "core_badges_{$insertedId}", $name );
return $insertedId;
}
/**
* Convert an Achievement Rank
*
* @param array $info Data to insert
* @param string|NULL $imagePath Path to image file, or NULL.
* @param string|NULL $imageData Binary image data, or NULL.
* @return bool|int The ID of the newly inserted badge, or FALSE on failure
*/
public function convertAchievementRank( array $info=[], string $imagePath=NULL, string $imageData=NULL )
{
/* We don't really need an ID for these, so if one isn't specified, then that's okay. */
$hasId = TRUE;
if ( !isset( $info['id'] ) )
{
$hasId = FALSE;
$this->software->app->log( 'rank_missing_ids', __METHOD__, \IPS\convert\App::LOG_NOTICE );
}
/* We do need this, though. */
if ( !isset( $info['title'] ) )
{
$this->software->app->log( 'rank_missing_title', __METHOD__, \IPS\convert\App::LOG_NOTICE );
return FALSE;
}
if ( isset( $info['icon'] ) AND ( !\is_null( $imagePath ) OR !\is_null( $imageData ) ) )
{
try
{
if ( \is_null( $imageData ) AND !\is_null( $imagePath ) )
{
$imageData = file_get_contents( $imagePath );
}
$file = \IPS\File::create( 'core_Badges', $info['icon'], $imageData, NULL, FALSE, NULL, FALSE );
$info['icon'] = (string) $file;
}
catch( \ErrorException | \Exception $e )
{
$info['icon'] = NULL;
}
}
else
{
$info['icon'] = NULL;
}
if ( $hasId )
{
$oldId = $info['id'];
unset( $info['id'] );
}
$insertedId = \IPS\Db::i()->insert( 'core_member_ranks', $info );
if( $hasId )
{
$this->software->app->addLink( $insertedId, $oldId, 'core_achievement_ranks' );
}
\IPS\Lang::saveCustom( 'core', "core_member_rank_{$insertedId}", $info['title'] );
return $insertedId;
}
/**
* Convert an Acronym
@@ -1233,7 +1398,7 @@ class _Core extends \IPS\convert\Library
{
\IPS\Db::i()->delete( 'core_attachments', array( "attach_id=?", $id ) );
$this->software->app->log( 'attachment_missing_parent', __METHOD__, \IPS\convert\App::LOG_WARNING, $id );
return;
return FALSE;
}
if ( isset( $map['id2'] ) )
@@ -2436,6 +2601,9 @@ class _Core extends \IPS\convert\Library
/**
* Convert a Member Rank
*
* @deprecated
* @todo - remove 4.7
*
* @param array $info Data to insert
* @param string|NULL $iconpath Icon File Path, or NULL
* @param string|NULL $icondata Icon File Data, or NULL
@@ -2443,80 +2611,8 @@ class _Core extends \IPS\convert\Library
*/
public function convertRank( $info=array(), $iconpath=NULL, $icondata=NULL )
{
/* We don't really need an ID for these, so if one isn't specified, then that's okay. */
$hasId = TRUE;
if ( !isset( $info['id'] ) )
{
$hasId = FALSE;
$this->software->app->log( 'rank_missing_ids', __METHOD__, \IPS\convert\App::LOG_NOTICE );
}
/* We do need this, though. */
if ( !isset( $info['title'] ) )
{
$this->software->app->log( 'rank_missing_title', __METHOD__, \IPS\convert\App::LOG_NOTICE );
return FALSE;
}
if ( !isset( $info['posts'] ) )
{
$info['posts'] = 0;
}
if ( !isset( $info['pips'] ) )
{
$info['pips'] = 0;
}
else if ( $info['pips'] > 100 )
{
/* set pips to 100, this is the max. value which we support */
$info['pips'] = 100;
}
if ( isset( $info['icon'] ) AND ( !\is_null( $iconpath ) OR !\is_null( $icondata ) ) )
{
try
{
if ( \is_null( $icondata ) AND !\is_null( $iconpath ) )
{
$icondata = file_get_contents( $iconpath );
}
$file = \IPS\File::create( 'core_Theme', $info['icon'], $icondata );
$info['icon'] = (string) $file;
$info['use_icon'] = 1;
}
catch( \Exception $e )
{
$info['icon'] = '';
$info['use_icon'] = 0;
}
catch( \ErrorException $e )
{
$info['icon'] = '';
$info['use_icon'] = 0;
}
}
else
{
$info['icon'] = '';
$info['use_icon'] = 0;
}
if ( $hasId )
{
$id = $info['id'];
unset( $info['id'] );
}
$inserted_id = \IPS\Db::i()->insert( 'core_member_ranks', $info );
\IPS\Lang::saveCustom( 'core', "core_member_rank_{$inserted_id}", $info['title'] );
if ( $hasId )
{
$this->software->app->addLink( $inserted_id, $id, 'core_member_ranks' );
}
return $inserted_id;
$this->software->app->log( 'method_deprecated', __METHOD__, \IPS\convert\App::LOG_WARNING );
return FALSE;
}
/**
@@ -2752,7 +2848,7 @@ class _Core extends \IPS\convert\Library
if ( !isset( $info['email'] ) OR !$info['email'] OR filter_var( $info['email'], FILTER_VALIDATE_EMAIL ) === FALSE )
{
/* No email, or it is invalid */
$newEmail = time() . '@example.com';
$newEmail = microtime( TRUE ) . '@example.com';
$this->software->app->log( sprintf( \IPS\Member::loggedIn()->language()->get( 'member_email_invalid' ), $info['email'], $newEmail ), __METHOD__, \IPS\convert\App::LOG_NOTICE, $info['member_id'] );
$info['email'] = $newEmail;
}
@@ -3384,22 +3480,6 @@ class _Core extends \IPS\convert\Library
$info['marked_site_read'] = time();
$info['acp_language'] = \IPS\Lang::defaultLanguage();
if ( !isset( $info['member_title'] ) )
{
$info['member_title'] = NULL;
}
else
{
$info['member_title'] = strip_tags( $info['member_title'] );
}
/* Check length of supplied title */
if( $info['member_title'] AND ( mb_strlen( $info['member_title'] ) > 64 ) )
{
$this->software->app->log( 'members_title_truncated', __METHOD__, \IPS\convert\App::LOG_NOTICE, $info['member_id'] );
$info['member_title'] = mb_substr( $info['member_title'], 0, 64 );
}
if ( !isset( $info['member_posts'] ) )
{
$info['member_posts'] = 0;
@@ -3505,7 +3585,8 @@ class _Core extends \IPS\convert\Library
break;
case 'secondary':
$groups = array();
foreach ( explode( ',', $info['log_data']['old'] ) as $groupId )
$groupData = \is_array( $info['log_data']['old'] ) ? $info['log_data']['old'] : explode( ',', $info['log_data']['old'] );
foreach ( $groupData as $groupId )
{
try
{
@@ -3516,7 +3597,8 @@ class _Core extends \IPS\convert\Library
$info['log_data']['old'] = implode( ',', array_unique( $groups ) );
$groups = array();
foreach ( explode( ',', $info['log_data']['new'] ) as $groupId )
$newGroupData = \is_array( $info['log_data']['new'] ) ? $info['log_data']['new'] : explode( ',', $info['log_data']['new'] );
foreach ( $newGroupData as $groupId )
{
try
{
@@ -398,57 +398,69 @@ class _Downloads extends Core
switch( $info['cf_type'] )
{
case 'CheckboxSet':
case 'Member':
if ( $info['cf_multiple'] )
{
$columnDefinition['type'] = 'TEXT';
}
else
{
$columnDefinition['type'] = 'INT';
$columnDefinition['length'] = 10;
}
break;
case 'Date':
case 'Poll':
$columnDefinition['type'] = 'INT';
$columnDefinition['length'] = 10;
break;
case 'Member':
if ( $info['cf_multiple'] )
{
$columnDefinition['type'] = 'TEXT';
}
else
{
$columnDefinition['type'] = 'INT';
$columnDefinition['length'] = 10;
}
break;
case 'Editor':
case 'TextArea':
case 'Upload':
case 'Address':
case 'Codemirror':
case 'Select':
$columnDefinition['type'] = 'TEXT';
break;
case 'Email':
case 'Password':
case 'Tel':
case 'Text':
case 'Url':
case 'Color':
case 'Radio':
case 'Number':
$columnDefinition['type'] = 'VARCHAR';
$columnDefinition['length'] = 255;
break;
case 'YesNo':
case 'Checkbox':
case 'Rating':
$columnDefinition['type'] = 'TINYINT';
$columnDefinition['length'] = 1;
break;
case 'Date':
case 'Poll':
$columnDefinition['type'] = 'INT';
$columnDefinition['length'] = 10;
break;
case 'Editor':
case 'TextArea':
case 'Upload':
case 'Address':
case 'Codemirror':
case 'Select':
case 'CheckboxSet':
$columnDefinition['type'] = 'TEXT';
break;
case 'Email':
case 'Password':
case 'Tel':
case 'Text':
case 'Url':
case 'Color':
case 'Radio':
case 'Number':
$columnDefinition['type'] = 'VARCHAR';
$columnDefinition['length'] = 255;
break;
case 'YesNo':
case 'Checkbox':
case 'Rating':
$columnDefinition['type'] = 'TINYINT';
$columnDefinition['length'] = 1;
break;
}
if ( isset( $info['cf_max_input'] ) AND $info['cf_max_input'] )
{
$columnDefinition['length'] = $info['cf_max_input'];
if( $info['cf_max_input'] > 255 )
{
$columnDefinition['type'] = 'MEDIUMTEXT';
if( isset( $columnDefinition['length'] ) )
{
unset( $columnDefinition['length'] );
}
}
else
{
$columnDefinition['length'] = $info['cf_max_input'];
}
}
\IPS\Db::i()->addColumn( 'downloads_ccontent', $columnDefinition );
@@ -110,19 +110,19 @@ class _Forums extends Core
$return[ $k ] = array(
'step_title' => 'convert_forums_rss_imports',
'step_method' => 'convertForumsRssImports',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'forums_rss_import' ),
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'core_rss_import', [ 'rss_import_class=?', 'IPS\\forums\\Topic' ] ),
'source_rows' => array( 'table' => $v['table'], 'where' => $v['where'] ),
'per_cycle' => 200,
'dependencies' => array( 'convertForumsForums' ),
'link_type' => 'forums_rss_import',
);
break;
case 'convertForumsRssImported':
$return[ $k ] = array(
'step_title' => 'convert_forums_rss_imported',
'step_method' => 'convertForumsRssImported',
'ips_rows' => \IPS\Db::i()->select( 'COUNT(*)', 'forums_rss_imported' ),
'ips_rows' => \IPS\Db::i()->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(),
'source_rows' => array( 'table' => $v['table'], 'where' => $v['where'] ),
'per_cycle' => 200,
'dependencies' => array( 'convertForumsTopics', 'convertForumsRssImports' ),
@@ -912,6 +912,11 @@ class _Forums extends Core
$info['title'] = "Untitled Topic {$info['tid']}";
$this->software->app->log( 'topic_missing_title', __METHOD__, \IPS\convert\App::LOG_WARNING );
}
elseif( mb_strlen( $info['title'] ) > 255 )
{
$info['title'] = mb_substr( $info['title'], 0, 255 );
$this->software->app->log( 'topic_title_truncated', __METHOD__, \IPS\convert\App::LOG_NOTICE, $info['tid'] );
}
if ( isset( $info['forum_id'] ) )
{
+19 -3
View File
@@ -65,7 +65,15 @@ class _Login extends \IPS\Login\Handler
/* Make sure we have the username or email */
if( !$usernameOrEmail )
{
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack('login_err_no_account', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $type ) ) ) ), \IPS\Login\Exception::NO_ACCOUNT );
$member = NULL;
if ( $this->authType() & \IPS\Login::AUTH_TYPE_EMAIL )
{
$member = new \IPS\Member;
$member->email = $usernameOrEmail;
}
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack( 'login_err_no_account', FALSE, array( 'pluralize' => array( $this->authType() ) ) ), \IPS\Login\Exception::NO_ACCOUNT, NULL, $member );
}
/* Get member(s) */
@@ -98,7 +106,15 @@ class _Login extends \IPS\Login\Handler
/* If we didn't match any, throw an exception */
if ( !\count( $members ) )
{
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack('login_err_no_account', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $type ) ) ) ), \IPS\Login\Exception::NO_ACCOUNT );
$member = NULL;
if ( $this->authType() & \IPS\Login::AUTH_TYPE_EMAIL )
{
$member = new \IPS\Member;
$member->email = $usernameOrEmail;
}
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack( 'login_err_no_account', FALSE, array( 'pluralize' => array( $this->authType() ) ) ), \IPS\Login\Exception::NO_ACCOUNT, NULL, $member );
}
/* Table switcher for new converters */
@@ -122,7 +138,7 @@ class _Login extends \IPS\Login\Handler
}
/* Still here? Throw a password incorrect exception */
throw new \IPS\Login\Exception( 'login_err_bad_password', \IPS\Login\Exception::BAD_PASSWORD, NULL, isset( $member ) ? $member : NULL );
throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack( 'login_err_bad_password', FALSE, array( 'pluralize' => array( $this->authType() ) ) ), \IPS\Login\Exception::BAD_PASSWORD, NULL, isset( $member ) ? $member : NULL );
}
/**
@@ -165,6 +165,7 @@ abstract class _Software
'vbulletin5' => 'IPS\convert\Software\Core\Vbulletin5',
'woltlab' => 'IPS\convert\Software\Core\Woltlab',
'wordpress' => 'IPS\convert\Software\Core\Wordpress',
'wpforo' => 'IPS\convert\Software\Core\Wpforo',
'xenforo' => 'IPS\convert\Software\Core\Xenforo',
),
'blog' => array(
@@ -198,6 +199,7 @@ abstract class _Software
'vbulletin' => 'IPS\convert\Software\Forums\Vbulletin',
'vbulletin5' => 'IPS\convert\Software\Forums\Vbulletin5',
'woltlab' => 'IPS\convert\Software\Forums\Woltlab',
'wpforo' => 'IPS\convert\Software\Forums\Wpforo',
'xenforo' => 'IPS\convert\Software\Forums\Xenforo',
),
'gallery' => array(
@@ -31,7 +31,7 @@ class _Wordpress extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "Wordpress (4.x)";
return "WordPress (5.x)";
}
/**
@@ -120,7 +120,7 @@ class _Wordpress extends \IPS\convert\Software
*/
public static function parents()
{
return array( 'core' => array( 'wordpress' ) );
return array( 'core' => array( 'wordpress', 'wpforo' ) );
}
/**
@@ -79,10 +79,6 @@ class _Expressionengine extends \IPS\convert\Software
'convertPmAttachments' => array(
'table' => 'exp_message_attachments',
'where' => NULL,
),
'convertRanks' => array(
'table' => 'exp_forum_ranks',
'where' => NULL
)
);
}
@@ -552,30 +548,6 @@ class _Expressionengine extends \IPS\convert\Software
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'rank_id' );
foreach( $this->fetch( 'exp_forum_ranks', 'rank_id' ) AS $row )
{
$libraryClass->convertRank( array(
'id' => $row['rank_id'],
'title' => $row['rank_title'],
'posts' => $row['rank_min_posts'],
'pips' => $row['rank_stars']
) );
$libraryClass->setLastKeyValue( $row['rank_id'] );
}
}
/**
* Convert PMs
*
@@ -99,6 +99,14 @@ class _Invisioncommunity extends \IPS\convert\Software
}
return array(
'convertAchievementBadges' => array(
'table' => 'core_badges',
'where' => NULL
),
'convertAchievementRanks' => array(
'table' => 'core_member_ranks',
'where' => NULL
),
'convertAcronyms' => array(
'table' => 'core_acronyms',
'where' => NULL
@@ -119,10 +127,6 @@ class _Invisioncommunity extends \IPS\convert\Software
'table' => 'core_groups',
'where' => NULL
),
'convertRanks' => array(
'table' => 'core_member_ranks',
'where' => NULL
),
'convertProfileFieldGroups' => array(
'table' => 'core_pfields_groups',
'where' => NULL
@@ -230,7 +234,6 @@ class _Invisioncommunity extends \IPS\convert\Software
return array(
'convertEmoticons',
'convertGroups',
'convertRanks',
'convertMembers',
'convertReputationLevels',
'convertClubs',
@@ -253,7 +256,6 @@ class _Invisioncommunity extends \IPS\convert\Software
switch( $method )
{
case 'convertEmoticons':
case 'convertRanks':
case 'convertMembers':
case 'convertReputationLevels':
case 'convertClubs':
@@ -367,12 +369,65 @@ class _Invisioncommunity extends \IPS\convert\Software
return array( "f_search_index_rebuild", "f_clear_caches" );
}
/**
* Convert Achievement Badges
*
* @return void
*/
public function convertAchievementBadges(): void
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'id' );
foreach( $this->fetch( 'core_badges', 'id' ) AS $row )
{
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_badges' );
/* Add name after clearing other columns */
$row['title'] = $this->getWord( 'core_badges_' . $row['id'] );
/* Path for custom icon */
$imagePath = $row['image'] ? $this->app->_session['more_info']['convertEmoticons']['upload_path'] . '/' . $row['image'] : NULL;
$row['image'] = $row['image'] ? pathinfo( $imagePath, PATHINFO_BASENAME ) : NULL;
$libraryClass->convertAchievementBadge( $row, $imagePath );
$libraryClass->setLastKeyValue( $row['id'] );
}
}
/**
* Convert Achievement Ranks
*
* @return void
*/
public function convertAchievementRanks(): void
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'id' );
foreach( $this->fetch( 'core_member_ranks', 'id' ) AS $row )
{
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_member_ranks' );
/* Add name after clearing other columns */
$row['title'] = $this->getWord( 'core_member_rank_' . $row['id'] );
/* Path for custom icon */
$imagePath = $row['icon'] ? $this->app->_session['more_info']['convertEmoticons']['upload_path'] . '/' . $row['icon'] : NULL;
$libraryClass->convertAchievementRank( $row, $imagePath );
$libraryClass->setLastKeyValue( $row['id'] );
}
}
/**
* Convert acronym
*
* @return void
*/
public function convertAcronym()
public function convertAcronyms()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'a_id' );
@@ -515,32 +570,6 @@ class _Invisioncommunity extends \IPS\convert\Software
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'id' );
foreach( $this->fetch( 'core_member_ranks', 'id' ) AS $row )
{
/* Remove non-standard columns */
$this->unsetNonStandardColumns( $row, 'core_member_ranks' );
/* Add name after clearing other columns */
$row['title'] = $this->getWord( 'core_member_rank_' . $row['id'] );
/* Path for custom icon */
$iconPath = $row['icon'] ? $this->app->_session['more_info']['convertEmoticons']['emoticon_path'] . '/' . $row['icon'] : NULL;
$libraryClass->convertRank( $row, $iconPath );
$libraryClass->setLastKeyValue( $row['id'] );
}
}
/**
* Convert profile field groups
*
@@ -1134,7 +1163,7 @@ class _Invisioncommunity extends \IPS\convert\Software
$this->unsetNonStandardColumns( $row, 'core_member_history' );
/* our content is already formatted for member history, so we'll use that method instead */
$libraryClass->convertMemberHistory( $row );
$libraryClass->convertMemberHistory( $row );
$libraryClass->setLastKeyValue( $row['log_id'] );
}
}
@@ -85,10 +85,6 @@ class _Mybb extends \IPS\convert\Software
'table' => 'announcements',
'where' => NULL
),
'convertRanks' => array(
'table' => 'usertitles',
'where' => NULL
),
'convertPrivateMessages' => array(
'table' => 'privatemessages',
'where' => array( 'fromid<>uid' )
@@ -238,7 +234,7 @@ class _Mybb extends \IPS\convert\Software
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
);
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google' ) AS $field )
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google', 'usertitle' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
@@ -556,7 +552,7 @@ class _Mybb extends \IPS\convert\Software
'pf_show_on_reg' => $row['registration'],
'pf_input_format' => '/' . $row['regex'] . '/i',
'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
'pf_multiple' => ( $theirType == 'multiselect' OR $theirType == 'checkbox' ) ? 1 : 0
);
$merge = $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$row['fid']}"] != 'none' ? $this->app->_session['more_info']['convertProfileFields']["map_pfield_{$row['fid']}"] : NULL;
@@ -833,7 +829,6 @@ class _Mybb extends \IPS\convert\Software
'timezone' => $row['timezone'],
'allow_admin_mails' => $row['allownotices'],
'members_disable_pm' => ( $row['receivepms'] ) ? 0 : 1,
'member_title' => $row['usertitle'],
'member_posts' => $row['postnum'],
'member_last_post' => $row['lastpost'],
);
@@ -854,7 +849,7 @@ class _Mybb extends \IPS\convert\Software
}
catch( \UnderflowException $e ) {}
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google' ) AS $pseudo )
foreach( array( 'website', 'icq', 'aim', 'yahoo', 'skype', 'google', 'usertitle' ) AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -954,30 +949,6 @@ class _Mybb extends \IPS\convert\Software
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'utid' );
foreach( $this->fetch( 'usertitles', 'utid' ) AS $row )
{
$libraryClass->convertRank( array(
'id' => $row['utid'],
'title' => $row['title'],
'posts' => $row['posts'],
'pips' => $row['stars']
) );
$libraryClass->setLastKeyValue( $row['utid'] );
}
}
/**
* Convert PMs
*
@@ -31,7 +31,7 @@ class _Phpbb extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "phpBB (3.1.x/3.2.x)";
return "phpBB (3.1.x/3.2.x/3.3.x)";
}
/**
@@ -81,10 +81,6 @@ class _Phpbb extends \IPS\convert\Software
'table' => 'privmsgs',
'where' => NULL,
),
'convertRanks' => array(
'table' => 'ranks',
'where' => array( "rank_special!=?", 1 ),
),
'convertProfanityFilters' => array(
'table' => 'words',
'where' => NULL
@@ -188,7 +184,6 @@ class _Phpbb extends \IPS\convert\Software
'convertProfileFields',
'convertGroups',
'convertMembers',
'convertRanks',
);
}
@@ -318,17 +313,6 @@ class _Phpbb extends \IPS\convert\Software
);
break;
case 'convertRanks':
$return['convertRanks']['rank_location'] = array(
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => array(),
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_phpbb_ranks_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
);
break;
}
return ( isset( $return[ $method ] ) ) ? $return[ $method ] : array();
@@ -385,7 +369,7 @@ class _Phpbb extends \IPS\convert\Software
public static function fixPostData( $post )
{
/* Convert newlines to <br> tags */
$post = nl2br($post);
$post = nl2br( $post );
/* Preserve code data */
$codeboxes = array();
@@ -402,7 +386,7 @@ class _Phpbb extends \IPS\convert\Software
}
/* Convert HTML entities back into actual entities */
$post = html_entity_decode($post, ENT_COMPAT | ENT_HTML401, "UTF-8");
$post = html_entity_decode( $post, ENT_COMPAT | ENT_HTML401, "UTF-8" );
/* Put codeboxes back */
foreach( $codeboxes as $k => $v )
@@ -410,6 +394,9 @@ class _Phpbb extends \IPS\convert\Software
$post = str_replace( '__CODE_BOX_' . $k . '__', $v, $post );
}
/* Sort out emoticons */
$post = preg_replace( "/<!-- s(\S+?) --><img(?:[^<]+?)<!-- (?:\S+?) -->/", '$1', $post );
/* PhpBB 3.2 */
$search = array( '<r>', '<t>', '<s>', '<e>', '<code>', '</r>', '</t>', '</s>', '</e>', '</img>', '</url>', '</quote>', '</link_text>', '</code>' );
$post = str_ireplace( $search, '', $post );
@@ -431,14 +418,11 @@ class _Phpbb extends \IPS\convert\Software
$post = str_replace( array( "[/*]", "[/*:m]" ), "", $post );
$post = str_replace( array( "[/list:o]", "[/list:u]" ), "[/list]", $post );
/* Sort out emoticons */
$post = preg_replace("/<!-- s(\S+?) --><img(?:[^<]+?)<!-- (?:\S+?) -->/", '$1', $post);
/* Sort out media */
$post = preg_replace("#<!-- m --><a class=\"postlink\" href=\"([^\]]+?)\"([^>]+?)?>([^\]]+?)</a><!-- m -->#i", "$1", $post);
$post = preg_replace( "#<!-- m --><a class=\"postlink\" href=\"([^\]]+?)\"([^>]+?)?>([^\]]+?)</a><!-- m -->#i", "$1", $post );
/* And sort out URLs */
$post = preg_replace("#<a class=\"postlink\" href=\"([^\]]+?)\"([^>]+?)?>([^\]]+?)</a>#i", "[url=$1]$3[/url]", $post);
$post = preg_replace( "#<a class=\"postlink\" href=\"([^\]]+?)\"([^>]+?)?>([^\]]+?)</a>#i", "[url=$1]$3[/url]", $post );
return $post;
}
@@ -725,6 +709,7 @@ class _Phpbb extends \IPS\convert\Software
'member_posts' => $row['user_posts'],
'member_last_post' => $row['user_lastpost_time'],
'signature' => static::stripUid( $row['user_sig'], $row['user_sig_bbcode_uid'] ),
'temp_ban' => $temp_ban,
);
/* Profile Photos */
@@ -795,8 +780,12 @@ class _Phpbb extends \IPS\convert\Software
{
if ( $field_type == 'dropdown' )
{
/* phpBB stores the option incremented from 1 - however the options are stored incremented from 0. So if you select the first option, 1 is stored but profile_fields_lang actually has it as 0. I don't get it either. */
$pfields[ $field['field_id'] ] = $this->db->select( 'lang_value', 'profile_fields_lang', array( "field_id=? AND option_id=?", $field['field_id'], $userfields['pf_' . $field['field_name'] ] - 1 ) )->first();
try
{
/* phpBB stores the option incremented from 1 - however the options are stored incremented from 0. So if you select the first option, 1 is stored but profile_fields_lang actually has it as 0. I don't get it either. */
$pfields[ $field['field_id'] ] = $this->db->select( 'lang_value', 'profile_fields_lang', array( "field_id=? AND option_id=?", $field['field_id'], $userfields['pf_' . $field['field_name'] ] - 1 ) )->first();
}
catch( \UnderflowException $e ) { }
}
else
{
@@ -889,35 +878,6 @@ class _Phpbb extends \IPS\convert\Software
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'rank_id' );
foreach( $this->fetch( 'ranks', 'rank_id', array( "rank_special=?", 0 ) ) AS $row )
{
$filepath = NULL;
if ( $row['rank_image'] )
{
$filepath = rtrim( $this->app->_session['more_info']['convertRanks']['rank_location'], '/' ) . '/' . $row['rank_image'];
}
$libraryClass->convertRank( array(
'id' => $row['rank_id'],
'title' => $row['rank_title'],
'posts' => $row['rank_min'],
), $filepath );
$libraryClass->setLastKeyValue( $row['rank_id'] );
}
}
/**
* Convert profannity filters
*
@@ -934,7 +894,8 @@ class _Phpbb extends \IPS\convert\Software
$libraryClass->convertProfanityFilter( array(
'wid' => $row['word_id'],
'type' => $row['word'],
'swop' => $row['replacement']
'swop' => $row['replacement'],
'm_exact' => TRUE
) );
$libraryClass->setLastKeyValue( $row['word_id'] );
@@ -1207,7 +1168,7 @@ class _Phpbb extends \IPS\convert\Software
{
$password = html_entity_decode( $password );
$success = FALSE;
$hash = $member->conv_password;
$hash = preg_replace('/^\$CP\$/', '', $member->conv_password ); // Remove the prefix.
/* phpBB 3.1 */
if( preg_match( '/^\$2[ay]\$(0[4-9]|[1-2][0-9]|3[0-1])\$[a-zA-Z0-9.\/]{53}/', $hash ) )
@@ -1240,6 +1201,12 @@ class _Phpbb extends \IPS\convert\Software
$success = ( \IPS\Login::compareHashes( $hash, $hashLibrary->hashCryptPrivate( $single_md5_pass, $hash, $itoa64 ) ) ) ? TRUE : FALSE ;
}
/* Fallback to password_verify(), this applies to PhpBB 3.3+ where Argon may be used */
if( !$success )
{
$success = password_verify( $password, $member->conv_password );
}
return $success;
}
}
@@ -61,10 +61,6 @@ class _Punbb extends \IPS\convert\Software
'table' => 'users',
'where' => NULL,
),
'convertRanks' => array(
'table' => 'ranks',
'where' => NULL
),
'convertPrivateMessages' => array(
'table' => 'messages',
'where' => NULL,
@@ -138,7 +134,7 @@ class _Punbb extends \IPS\convert\Software
case 'convertMembers':
/* Pseudo Profile Fields */
foreach( [ 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location' ] AS $field )
foreach( [ 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location', 'title' ] AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
@@ -279,13 +275,12 @@ class _Punbb extends \IPS\convert\Software
'members_bitoptions' => [ 'view_sigs' => $row['show_sig'] ],
'signature' => $row['signature'],
'timezone' => $row['timezone'],
'member_title' => $row['title'],
'member_posts' => $row['num_posts'],
);
/* Pseudo Fields */
$profileFields = array();
foreach( [ 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location' ] AS $pseudo )
foreach( [ 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location', 'title' ] AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -323,31 +318,6 @@ class _Punbb extends \IPS\convert\Software
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'id' );
foreach( $this->fetch( 'ranks', 'id' ) AS $row )
{
$info = array(
'id' => $row['id'],
'title' => $row['rank'],
'posts' => $row['min_posts']
);
$libraryClass->convertRank( $info );
$libraryClass->setLastKeyValue( $row['id'] );
}
}
/**
* Convert private messages
*
@@ -255,7 +255,7 @@ class _Smf extends \IPS\convert\Software
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
);
foreach( array( 'gender', 'website_url', 'location', 'icq', 'aim', 'yim', 'msn', 'personal_text' ) AS $field )
foreach( array( 'gender', 'website_url', 'location', 'icq', 'aim', 'yim', 'msn', 'personal_text', 'usertitle' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
@@ -639,6 +639,12 @@ class _Smf extends \IPS\convert\Software
}
}
catch( \UnderflowException $e ) {}
/* Catch out of range issues */
if( $ban > 2147483647 )
{
$ban = -1;
}
$bday_year = $bday_month = $bday_day = NULL;
@@ -672,14 +678,13 @@ class _Smf extends \IPS\convert\Software
'pp_reputation_points' => $row['karma_good'] - $row['karma_bad'],
'timezone' => $row['time_offset'],
'allow_admin_mails' => $row['notify_announcements'],
'member_title' => $row['usertitle'],
'member_posts' => $row['posts'],
);
$pfields = array();
/* Pseudo Profile Fields */
foreach( array( 'gender', 'website_url', 'location', 'icq', 'aim', 'yim', 'msn', 'personal_text' ) AS $pseudo )
foreach( array( 'gender', 'website_url', 'location', 'icq', 'aim', 'yim', 'msn', 'personal_text', 'usertitle' ) AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -782,14 +787,17 @@ class _Smf extends \IPS\convert\Software
}
}
}
else
/* Nothing yet? try attachments */
if( empty( $filename ) )
{
/* Maybe Uploaded */
try
{
$legacyName = null;
$attach = $this->db->select( '*', 'attachments', array( "id_member=? AND filename LIKE CONCAT( ?, '%' )", $row['id_member'], 'avatar_' ) )->first();
$file = rtrim( $this->app->_session['more_info']['convertMembers']['photo_location'], '/' );
$path = rtrim( $this->app->_session['more_info']['convertMembers']['photo_location'], '/' );
/* We need to figure out where it is */
if ( $attach['file_hash'] )
@@ -803,12 +811,18 @@ class _Smf extends \IPS\convert\Software
$cleanName = strtr( $cleanName, array( 'Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u' ) );
$cleanName = preg_replace( array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $cleanName );
$location = $attach['id_attach'] . '_' . str_replace( '.', '_', $cleanName ) . md5( $cleanName );
$legacyName = preg_replace( '~\.[\.]+~', '.', $cleanName );
}
if ( @file_exists( $file . '/' . $location ) )
if ( \file_exists( $path . '/' . $location ) )
{
$filename = $attach['filename'];
$filedata = file_get_contents( $file . '/' . $location );
$filepath = $path . '/' . $location;
}
elseif( !empty( $legacyName ) AND \file_exists( $path . '/' . $legacyName ) )
{
$filename = $attach['filename'];
$filepath = $path;
}
}
catch( \UnderflowException $e ) {}
@@ -116,10 +116,6 @@ class _UBBthreads extends \IPS\convert\Software
'table' => 'WATCH_LISTS',
'where' => array( 'WATCH_TYPE=?', 'u' )
),
'convertRanks' => array(
'table' => 'USER_TITLES',
'where' => NULL
),
'convertPrivateMessages' => array(
'table' => 'PRIVATE_MESSAGE_TOPICS',
'where' => NULL
@@ -302,7 +298,7 @@ class _UBBthreads extends \IPS\convert\Software
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
);
foreach( array( 'homepage', 'occupation', 'hobbies', 'location', 'icq', 'yahoo', 'aim', 'msn' ) AS $field )
foreach( array( 'homepage', 'occupation', 'hobbies', 'location', 'icq', 'yahoo', 'aim', 'msn', 'custom_title' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
@@ -572,7 +568,6 @@ class _UBBthreads extends \IPS\convert\Software
max( (int) $row['USER_LAST_POST_TIME'], (int) $row['USER_LAST_SEARCH_TIME'] )
),
'allow_admin_mails' => ( $row['USER_ACCEPT_ADMIN_EMAILS'] != 'Off' ),
'member_title' => $row['USER_CUSTOM_TITLE'],
'member_posts' => $row['USER_TOTAL_POSTS'],
'signature' => $row['USER_DEFAULT_SIGNATURE'],
'member_last_post' => \IPS\DateTime::create()->setTimestamp( $row['USER_LAST_POST_TIME'] ),
@@ -585,7 +580,7 @@ class _UBBthreads extends \IPS\convert\Software
/* Profile fields */
$pfields = array();
foreach( array( 'homepage', 'occupation', 'hobbies', 'location', 'icq', 'yahoo', 'aim', 'msn' ) AS $pseudo )
foreach( array( 'homepage', 'occupation', 'hobbies', 'location', 'icq', 'yahoo', 'aim', 'msn', 'custom_title' ) AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -667,32 +662,6 @@ class _UBBthreads extends \IPS\convert\Software
}
}
/**
* Convert member ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'USER_TITLE_ID' );
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...
* ...in duplicate entries for the same post counts if the local rows are not dropped
*/
$libraryClass->convertRank( array(
'id' => $row['USER_TITLE_ID'],
'title' => $row['USER_TITLE_NAME'],
'posts' => $row['USER_TITLE_POST_COUNT']
) );
$libraryClass->setLastKeyValue( $row['USER_TITLE_ID'] );
}
}
/**
* Convert private messages
*
@@ -30,7 +30,7 @@ class _Vanilla extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "Vanilla 2 (2.2.x/2.3.x)";
return "Vanilla (3.x)";
}
/**
@@ -144,14 +144,14 @@ class _Vanilla extends \IPS\convert\Software
// Deprecated "plugin based" path
elseif ( preg_match( '`^~([^/]*)/(.*)$`', $location, $matches ) )
{
return $uploadsPath.'/'.$matches[2];
return rtrim( $uploadsPath, '/' ) . '/' . $matches[2];
}
else
{
$parts = parse_url( $location );
if ( empty( $parts['scheme'] ) )
{
return $uploadsPath.'/'.$location;
return rtrim( $uploadsPath, '/' ) . '/' . $location;
}
else
{
@@ -167,6 +167,8 @@ class _Vanilla extends \IPS\convert\Software
'domain' => $parts['host']
);
return rtrim( $uploadsPath, '/' ) . '/' . ltrim( $parts['path'], '/' );
// @TODO: This is deprecated, and I'm not sure what it was for.
#$format = "{$result['type']}://{$result['domain']}/%s";
#return sprintf( $format, $result['name'] );
@@ -199,7 +201,7 @@ class _Vanilla extends \IPS\convert\Software
),
'convertMembers' => array(
'table' => 'User',
'where' => NULL,
'where' => array( "Deleted<>?", 1 ),
),
'convertStatuses' => array(
'table' => 'Activity',
@@ -335,6 +337,11 @@ class _Vanilla extends \IPS\convert\Software
return array( "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild", "f_rebuild_attachments" );
}
/**
* @brief Store parsedown object
*/
protected static $_parseDown = NULL;
/**
* Fix post data
*
@@ -343,6 +350,80 @@ class _Vanilla extends \IPS\convert\Software
*/
public static function fixPostData( $post )
{
/* Is this Markdown? */
if( mb_substr( $post, 0, 15 ) == '<!--Markdown-->' )
{
/* Load parser */
if( static::$_parseDown === NULL )
{
\IPS\IPS::$PSR0Namespaces['erusev'] = \IPS\ROOT_PATH . "/applications/convert/sources/Tools/Vanilla/Parsedown";
static::$_parseDown = new \erusev\Parsedown();
}
$post = str_replace( '<!--Markdown-->', '', $post );
$post = static::$_parseDown->text( $post );
}
elseif( mb_substr( $post, 0, 3 ) == '[{"' ) // Probably a delta
{
$postData = str_replace( [ "\r\n","\n" ], '', $post );
$postData = json_decode( $postData, TRUE );
$post = '';
foreach( $postData as $data )
{
if( \is_array( $data['insert'] ) )
{
switch ( $data['insert']['embed-external']['data']['embedType'] )
{
case 'quote':
$post .= '[quote name="' . $data['insert']['embed-external']['data']['insertUser']['name'] . '" timestamp="' . strtotime( $data['insert']['embed-external']['data']['dateInserted'] ) . '"]' . $data['insert']['embed-external']['data']['body'] . "[/quote]";
break;
}
}
else
{
$post .= $data['insert'] . "<br>";
}
}
}
/* Mentions */
$matches = [];
preg_match_all( '/@("|&quot;)([^@"]+)("|&quot;)/i', $post, $matches );
if( \count( $matches ) )
{
foreach( $matches[0] as $key => $tag )
{
$member = \IPS\Member::load( $matches[2][ $key ], 'name' );
if( !$member->member_id )
{
continue;
}
$post = str_replace( $tag, "[mention={$member->member_id}]{$member->name}[/mention]", $post );
}
}
/**
* Vanilla has a quotes plugin that seems to have changed formats quite often. - We'll try to match as many formats as we can
*/
$post = preg_replace( '/(?:<blockquote\s+(?:class=\"(?:User)?Quote\")?(?:\s+rel=\"(?:[^\"]+)\")?>)(?:\s+)?<div class=\"QuoteAuthor\">([^\"]+)<\/div>(?:\s+)?<div class=\"QuoteText\">(?:<p>)?(.*?)(?:<\/p>)?<\/div>(?:\s+)?<\/blockquote>/i',
'[quote name="$1"]$2[/quote]' ,
$post );
$post = preg_replace( '/(?:<blockquote\s+(?:class=\"Quote (?:User)?Quote\")?(?:\s+rel=\"(?:[^\"]+)\")?>)(?:\s+)?<div class=\"QuoteText\">(?:<p>)?(.*?)(?:<\/p>)?<\/div>(?:\s+)?<\/blockquote>/i',
'[quote]$1[/quote]' ,
$post );
$post = preg_replace( '/<blockquote\s+rel=\"([^\"]+)\"?>(.*?)<\/blockquote>/i',
'[quote name="$1"]$2[/quote]' ,
$post );
$post = preg_replace( '/<blockquote>(.*?)<\/blockquote>/ims',
'[quote]$1[/quote]' ,
$post );
return $post;
}
@@ -356,12 +437,8 @@ class _Vanilla extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'r.RoleID' );
/* Ready Roles */
$roles = $this->fetch( array( 'Role', 'r' ), 'RoleID' );
//$roles->join( array( 'Permission', 'p' ), 'r.RoleID=p.PermissionID' );
/* Run conversions */
foreach ( $roles as $row )
foreach ( $this->fetch( array( 'Role', 'r' ), 'RoleID' ) as $row )
{
$info = array(
'g_id' => $row['RoleID'],
@@ -397,9 +474,13 @@ class _Vanilla extends \IPS\convert\Software
$uploadsPath = $this->app->_session['more_info']['convertMembers']['attach_location'];
$users = $this->fetch( array( 'User', 'u' ), 'u.UserID', array( "u.Deleted<>?", 1 ) );
$users = $this->fetch( array( 'User', 'u' ), 'u.UserID', array( "u.Deleted<>?", 1 ), 'u.*, ur.RoleID' );
$users->join( array( 'UserRole', 'ur' ), 'ur.UserID=u.UserID' );
foreach( $users AS $row )
$data = iterator_to_array( $users );
$this->app->preCacheLinks( $data, [ 'core_groups' => 'RoleID' ] );
foreach( $data AS $row )
{
/* Work out birthday */
$bdayDay = NULL;
@@ -479,10 +560,11 @@ class _Vanilla extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'ActivityID' );
$statuses = $this->fetch( 'Activity', 'ActivityID', array( 'ActivityTypeID=?', static::$activityStatusType ) );
$data = iterator_to_array( $this->fetch( 'Activity', 'ActivityID', array( 'ActivityTypeID=?', static::$activityStatusType ) ) );
$this->app->preCacheLinks( $data, [ 'core_members' => 'ActivityUserID' ] );
/* Run conversions */
foreach ( $statuses as $row )
foreach ( $data as $row )
{
$info = array(
'status_id' => $row['ActivityID'],
@@ -509,10 +591,11 @@ class _Vanilla extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'ConversationID' );
$messages = $this->fetch( 'Conversation', 'ConversationID' );
$data = iterator_to_array( $this->fetch( 'Conversation', 'ConversationID' ) );
$this->app->preCacheLinks( $data, [ 'core_members' => 'InsertUserID' ] );
/* Run conversions */
foreach ( $messages as $row )
foreach ( $data as $row )
{
/* Message topic information */
//$firstMessageId = $row['FirstMessageID'];
@@ -528,8 +611,8 @@ class _Vanilla extends \IPS\convert\Software
/* Message maps */
$maps = array();
$rows = $this->fetch( 'UserConversation', 'UserID', array( 'ConversationID=?', $row['ConversationID'] ) );
$rows = iterator_to_array( $this->db->select( '*', 'UserConversation', array( 'ConversationID=?', $row['ConversationID'] ) ) );
$this->app->preCacheLinks( $rows, [ 'core_members' => 'UserID' ] );
foreach ( $rows as $row )
{
$maps[ $row['UserID'] ] = array(
@@ -554,11 +637,19 @@ class _Vanilla extends \IPS\convert\Software
public function convertPrivateMessageReplies()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'MessageID' );
foreach( $this->fetch( 'ConversationMessage', 'MessageID' ) AS $row )
$data = iterator_to_array( $this->fetch( 'ConversationMessage', 'MessageID' ) );
$this->app->preCacheLinks( $data, [ 'core_members' => 'InsertUserID', 'core_message_topics' => 'ConversationID' ] );
foreach( $data AS $row )
{
// Add Format Type (for later processing) if Markdown
if( $row['Format'] == 'Markdown' )
{
$row['Body'] = '<!--Markdown-->' . $row['Body'];
}
$libraryClass->convertPrivateMessageReply( array(
'msg_id' => $row['MessageID'],
'msg_topic_id' => $row['ConversationID'],
@@ -159,10 +159,6 @@ class _Vbulletin extends \IPS\convert\Software
'table' => 'pm',
'where' => array( 'NOT (folderid= -1 AND parentpmid > 0)' )
),
'convertRanks' => array(
'table' => 'usertitle',
'where' => NULL
),
'convertClubs' => array(
'table' => 'socialgroup',
'where' => NULL
@@ -481,7 +477,7 @@ class _Vbulletin extends \IPS\convert\Software
);
/* And decide what to do about these... */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype' ) AS $field )
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'user_title' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => ucwords( $field ) ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
@@ -1030,6 +1026,7 @@ class _Vbulletin extends \IPS\convert\Software
/* User titles */
$userTitles = iterator_to_array( $this->db->select( 'title', 'usertitle' ) );
$userTitles = array_merge( $userTitles, iterator_to_array( $this->db->select( 'usertitle', 'usergroup', [ 'usertitle!=?', '' ] ) ) );
foreach( $this->fetch( 'user', 'user.userid', NULL, 'user.*, usertextfield.signature' )->join( 'usertextfield', 'user.userid = usertextfield.userid' ) AS $user )
{
@@ -1119,7 +1116,6 @@ class _Vbulletin extends \IPS\convert\Software
'mgroup_others' => $user['membergroupids'],
'name' => html_entity_decode( $user['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $user['email'],
'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'],
@@ -1182,7 +1178,7 @@ class _Vbulletin extends \IPS\convert\Software
}
/* Pseudo Fields */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype' ) AS $pseudo )
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'user_title' ) AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -1210,8 +1206,15 @@ class _Vbulletin extends \IPS\convert\Software
'pf_show_on_reg' => 0,
) );
}
$profileFields[$pseudo] = $user[$pseudo];
if( $pseudo == 'user_title' )
{
$profileFields[ $pseudo ] = \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'];
}
else
{
$profileFields[ $pseudo ] = $user[ $pseudo ];
}
}
/* Profile Photos */
@@ -1256,6 +1259,7 @@ class _Vbulletin extends \IPS\convert\Software
$filename = $value;
}
}
$filepath = NULL;
}
catch( \UnderflowException $e )
@@ -1263,6 +1267,11 @@ class _Vbulletin extends \IPS\convert\Software
list( $filedata, $filename, $filepath ) = array( NULL, NULL, NULL );
}
}
if( !empty( $filedata ) AND empty( $filename ) )
{
$filename = 'vb-import-' . $info['member_id'] . '.jpg'; // We'll use job here, but image detection will figure it out from magic bytes
}
}
else
{
@@ -1627,29 +1636,6 @@ class _Vbulletin extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $pm['pmid'] );
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'usertitleid' );
foreach( $this->fetch( 'usertitle', 'usertitleid' ) AS $rank )
{
$libraryClass->convertRank( array(
'id' => $rank['usertitleid'],
'posts' => $rank['minposts'],
'title' => $rank['title']
) );
$libraryClass->setLastKeyValue( $rank['usertitleid'] );
}
}
/**
* Convert clubs
@@ -2149,7 +2135,7 @@ class _Vbulletin extends \IPS\convert\Software
try
{
foreach( $this->db->select( '*', 'pm', array( "parentpmid=?", $pm['pmid'] ) )->join( 'pmtext', 'pm.pmtextid = pmtext.pmtextid' ) AS $post )
foreach( $this->db->select( 'folderid, fromuserid', 'pm', array( "parentpmid=?", $pm['pmid'] ) )->join( 'pmtext', 'pm.pmtextid = pmtext.pmtextid' ) AS $post )
{
if ( $post['folderid'] != -1 )
{
@@ -123,10 +123,6 @@ class _Vbulletin5 extends \IPS\convert\Software
'table' => 'privatemessage',
'where' => array( "msgtype=?", 'message' ),
),
'convertRanks' => array(
'table' => 'usertitle',
'where' => NULL
),
'convertClubs' => array(
'table' => 'node',
'where' => 'clubs'
@@ -453,7 +449,7 @@ class _Vbulletin5 extends \IPS\convert\Software
);
/* And decide what to do about these... */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'google' ) AS $field )
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'google', 'user_title' ) AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, array( 'sprintf' => $field ) );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
@@ -1095,7 +1091,6 @@ class _Vbulletin5 extends \IPS\convert\Software
'mgroup_others' => $user['membergroupids'],
'name' => html_entity_decode( $user['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $user['email'],
'member_title' => \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'],
'joined' => $user['joindate'],
'ip_address' => $user['ipaddress'],
'warn_level' => $user['ipoints'],
@@ -1160,7 +1155,7 @@ class _Vbulletin5 extends \IPS\convert\Software
}
/* Pseudo Fields */
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'google' ) AS $pseudo )
foreach( array( 'homepage', 'icq', 'aim', 'yahoo', 'msn', 'skype', 'google', 'user_title' ) AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
@@ -1188,8 +1183,15 @@ class _Vbulletin5 extends \IPS\convert\Software
'pf_show_on_reg' => 0,
) );
}
$profileFields[$pseudo] = $user[$pseudo];
if( $pseudo == 'user_title' )
{
$profileFields[ $pseudo ] = \in_array( $user['usertitle'], $userTitles ) ? NULL : $user['usertitle'];
}
else
{
$profileFields[ $pseudo ] = $user[ $pseudo ];
}
}
/* Profile Photos */
@@ -1549,29 +1551,6 @@ class _Vbulletin5 extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $pm['nodeid'] );
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'usertitleid' );
foreach( $this->fetch( 'usertitle', 'usertitleid' ) AS $rank )
{
$libraryClass->convertRank( array(
'id' => $rank['usertitleid'],
'posts' => $rank['minposts'],
'title' => $rank['title']
) );
$libraryClass->setLastKeyValue( $rank['usertitleid'] );
}
}
/**
* Convert clubs
@@ -82,10 +82,6 @@ class _Woltlab extends \IPS\convert\Software
'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
@@ -285,6 +281,25 @@ class _Woltlab extends \IPS\convert\Software
'field_hint' => NULL,
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
);
foreach( [ 'usertitle' ] AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, [ 'sprintf' => $field ] );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = [
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => [
'options' => [
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
],
'userSuppliedInput' => 'create_field'
],
'field_hint' => NULL
];
}
break;
}
@@ -572,7 +587,6 @@ class _Woltlab extends \IPS\convert\Software
'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,
@@ -615,6 +629,47 @@ class _Woltlab extends \IPS\convert\Software
$profileFields[ $k ] = $userFields[ $v ];
}
/* Pseudo field(s) */
foreach( [ 'usertitle' ] AS $pseudo )
{
/* Are we retaining? */
if ( $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"] == 'no_convert' )
{
/* No, skip */
continue;
}
try
{
/* We don't actually need this, but we need to make sure the field was created */
$fieldId = $this->app->getLink( $pseudo, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
{
$libraryClass->convertProfileField( [
'pf_id' => $pseudo,
'pf_name' => $this->app->_session['more_info']['convertMembers']["field_{$pseudo}"],
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 0,
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
'pf_admin_only' => 0,
] );
}
if( $pseudo == 'usertitle' )
{
$profileFields[ $pseudo ] = \in_array( $row['userTitle'], $userTitles ) OR empty( $row['userTitle'] ) ? NULL : $row['userTitle'];
}
else
{
$profileFields[ $pseudo ] = $row[ $pseudo ];
}
}
$libraryClass->convertMember( $info, $profileFields, $filename, $filepath, NULL, $coverFileName, $coverFilePath );
/* Any friends need converting to followers? */
@@ -659,28 +714,6 @@ class _Woltlab extends \IPS\convert\Software
}
}
/**
* 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
*
@@ -31,7 +31,7 @@ class _Wordpress extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "WordPress (4.x)";
return "WordPress (5.x)";
}
/**
@@ -172,6 +172,12 @@ class _Wordpress extends \IPS\convert\Software
'conv_password' => $user['user_pass']
);
$meta = array();
foreach( $this->db->select( array( 'meta_key', 'meta_value' ), 'usermeta', array( "user_id=?", $user['ID'] ) )->setKeyField( 'meta_key' )->setValueField( 'meta_value' ) AS $key => $value )
{
$meta[$key] = $value;
}
$fields = array();
foreach( array( 'first_name', 'last_name', 'user_url' ) AS $field )
{
@@ -196,9 +202,14 @@ class _Wordpress extends \IPS\convert\Software
) );
}
if ( isset( $user[$field] ) )
$toCheck = $meta;
if ( $field == 'user_url' )
{
$fields[$field] = $user[$field];
$toCheck = $user;
}
if ( isset( $toCheck[$field] ) )
{
$fields[$field] = $toCheck[$field];
}
else
{
@@ -0,0 +1,652 @@
<?php
/**
* @brief Converter wpForo 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 22 Jan 2021
*/
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;
}
/**
* wpForo Core Converter
*/
class _Wpforo extends \IPS\convert\Software
{
/**
* Software Name
*
* @return string
*/
public static function softwareName()
{
/* Child classes must override this method */
return "wpForo (1.9.x)";
}
/**
* Software Key
*
* @return string
*/
public static function softwareKey()
{
/* Child classes must override this method */
return "wpforo";
}
/**
* Content we can convert from this software.
*
* @return array
*/
public static function canConvert()
{
return [
'convertGroups' => [
'table' => 'wpforo_usergroups',
'where' => NULL
],
'convertMembers' => [
'table' => 'users',
'where' => NULL
],
'convertPrivateMessages' => [
'table' => 'wpforo_pmfolders',
'where' => NULL
],
'convertPrivateMessageReplies' => [
'table' => 'wpforo_pms',
'where' => NULL,
],
'convertAttachments' => [
'table' => 'wpforo_pms',
'where' => NULL,
],
];
}
/**
* Can we convert passwords from this software.
*
* @return boolean
*/
public static function loginEnabled()
{
return TRUE;
}
/**
* 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_wpforo_preconvert';
}
/**
* List of conversion methods that require additional information
*
* @return array
*/
public static function checkConf()
{
return [
'convertMembers',
'convertGroups',
];
}
/**
* Fix post data
*
* @param string $post Raw post data
* @return string Parsed post data
*/
public static function fixPostData( $post )
{
/* Mentions */
$matches = [];
preg_match_all( '/@([^@ ]+)/i', $post, $matches );
if( \count( $matches ) )
{
foreach( $matches[0] as $key => $tag )
{
$member = \IPS\Member::load( $matches[1][ $key ], 'name' );
if( !$member->member_id )
{
continue;
}
$post = str_replace( $tag, "[mention={$member->member_id}]{$member->name}[/mention]", $post );
}
}
/* Make quotes look a little nicer */
$post = str_replace( '<blockquote', '<blockquote class="ipsQuote"', $post );
return $post;
}
/**
* Get More Information
*
* @param string $method Conversion method
* @return array
*/
public function getMoreInfo( $method )
{
$return = [];
switch( $method )
{
case 'convertMembers':
$return['convertMembers'] = [
'wpuploads' => [
'field_class' => 'IPS\\Helpers\\Form\\Text',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => [],
'field_hint' => \IPS\Member::loggedIn()->language()->addToStack('convert_wp_typical_path'),
'field_validation' => function( $value ) { if ( !@is_dir( $value ) ) { throw new \DomainException( 'path_invalid' ); } },
]
];
/* Pseudo Fields */
foreach( $this->_profileFields AS $field )
{
\IPS\Member::loggedIn()->language()->words["field_{$field}"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field', FALSE, [ 'sprintf' => ucwords( str_replace( '_', ' ', $field ) ) ] );
\IPS\Member::loggedIn()->language()->words["field_{$field}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'pseudo_field_desc' );
$return['convertMembers']["field_{$field}"] = [
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => 'no_convert',
'field_required' => TRUE,
'field_extra' => [
'options' => [
'no_convert' => \IPS\Member::loggedIn()->language()->addToStack( 'no_convert' ),
'create_field' => \IPS\Member::loggedIn()->language()->addToStack( 'create_field' ),
],
'userSuppliedInput' => 'create_field'
],
'field_hint' => NULL
];
}
break;
case 'convertGroups':
$return['convertGroups'] = [];
$options = [];
$options['none'] = \IPS\Member::loggedIn()->language()->addToStack( 'none' );
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_groups' ), 'IPS\Member\Group' ) AS $group )
{
$options[ $group->g_id ] = $group->name;
}
foreach( $this->db->select( '*', 'wpforo_usergroups' ) AS $group )
{
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupid']}"] = $group['name'];
\IPS\Member::loggedIn()->language()->words["map_group_{$group['groupid']}_desc"] = \IPS\Member::loggedIn()->language()->addToStack( 'map_group_desc' );
$return['convertGroups']["map_group_{$group['groupid']}"] = [
'field_class' => 'IPS\\Helpers\\Form\\Select',
'field_default' => NULL,
'field_required' => FALSE,
'field_extra' => [ 'options' => $options ],
'field_hint' => NULL
];
}
break;
}
return ( isset( $return[ $method ] ) ) ? $return[ $method ] : [];
}
/**
* @brief temporarily store post content
*/
protected $_postContent = [];
/**
* Convert attachments
*
* @return void
*/
public function convertAttachments()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'pmid' );
foreach( $this->fetch( 'wpforo_pms', 'pmid' ) AS $row )
{
if( !\stristr( $row['message'], '[attach]' ) AND !\stristr( $row['message'], 'wpforo-attached-file' ) )
{
$libraryClass->setLastKeyValue( $row['pmid'] );
continue;
}
$map = [
'id1' => $row['folderid'],
'id2' => $row['pmid'],
'id1_type' => 'core_message_topics',
'id1_from_parent' => FALSE,
'id2_type' => 'core_message_posts',
'id2_from_parent' => FALSE,
'location_key' => 'core_Messaging'
];
/* Advanced Attachments */
$matches = [];
preg_match_all( '/\[attach\](\d+)\[\/attach\]/i', $row['message'], $matches );
if( \count( $matches ) )
{
foreach( $matches[1] as $key => $id )
{
$sourceAttachment = $this->db->select( '*', 'wpforo_attachments', [ 'attachid=?', $id ] )->first();
$url = explode( '/', $sourceAttachment['fileurl'] );
$filename = array_pop( $url );
$info = [
'attach_id' => $row['pmid'],
'attach_file' => $sourceAttachment['filename'],
'attach_date' => \strtotime( $row['date'] ),
'attach_member_id' => $sourceAttachment['userid'],
'attach_filesize' => $sourceAttachment['size'],
];
$realFilePath = '/wpforo/attachments/' . $sourceAttachment['userid'] . '/' . $filename;
$path = rtrim( $this->app->_session['more_info']['convertMembers']['wpuploads'], '/' ) . $realFilePath;
$attachId = $libraryClass->convertAttachment( $info, $map, $path );
/* Update Message Post if we can */
try
{
if ( $attachId !== FALSE )
{
$messagePostId = $this->app->getLink( $row['pmid'], 'core_message_posts' );
if( !isset( $this->_postContent[ $messagePostId ] ) )
{
$this->_postContent[ $messagePostId ] = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', [ "msg_id=?", $messagePostId ] )->first();
}
$this->_postContent[ $messagePostId ] = str_replace( $matches[0][ $key ], '[attachment=' . $attachId . ':name]', $this->_postContent[ $messagePostId ] );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
}
}
/* Default Attachments */
$matches = [];
preg_match_all( '/\<div id\="wpfa\-[\d]+"(.+?)?>\<a class\="wpforo\-default\-attachment" href\="(.+?)"(.+?)?>\<i class\="(.+?)">\<\/i>(.+?)<\/a><\/div>/i', $row['message'], $matches );
if( \count( $matches ) )
{
foreach( $matches[2] as $key => $url )
{
$url = explode( '/', $url );
$filename = array_pop( $url );
$info = [
'attach_id' => $row['pmid'],
'attach_file' => $filename,
'attach_date' => \strtotime( $row['date'] ),
'attach_member_id' => $row['fromuserid'],
];
$realFilePath = '/wpforo/default_attachments/' . $filename;
$path = rtrim( $this->app->_session['more_info']['convertMembers']['wpuploads'], '/' ) . $realFilePath;
$attachId = $libraryClass->convertAttachment( $info, $map, $path );
/* Update post if we can */
try
{
if ( $attachId !== FALSE )
{
$messagePostId = $this->app->getLink( $row['pmid'], 'core_message_posts' );
if( !isset( $this->_postContent[ $messagePostId ] ) )
{
$this->_postContent[ $messagePostId ] = \IPS\Db::i()->select( 'msg_post', 'core_message_posts', [ "msg_id=?", $messagePostId ] )->first();
}
$this->_postContent[ $messagePostId ] = str_replace( $matches[0][ $key ], '[attachment=' . $attachId . ':name]', $this->_postContent[ $messagePostId ] );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
}
}
$libraryClass->setLastKeyValue( $row['pmid'] );
}
/* Do the updates */
foreach( $this->_postContent as $id => $content )
{
\IPS\Db::i()->update( 'core_message_posts', [ 'msg_post' => $content ], [ "msg_id=?", $id ] );
}
}
/**
* Convert groups
*
* @return void
*/
public function convertGroups()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'groupid' );
foreach( $this->fetch( 'wpforo_usergroups', 'groupid' ) AS $row )
{
$prefix = NULL;
$suffix = NULL;
if ( $row['color'] )
{
$prefix = "<span style='color:{$row['color']}'>";
$suffix = "</span>";
}
$info = [
'g_id' => $row['groupid'],
'g_name' => $row['name'],
'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 );
}
}
}
/**
* @brief Hardcoded wpForo profile fields
*/
protected $_profileFields = [ 'site', 'icq', 'aim', 'yahoo', 'msn', 'facebook', 'twitter', 'gtalk', 'skype', 'about', 'occupation', 'location' ];
/**
* Convert members
*
* @return void
*/
public function convertMembers()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'users.ID' );
foreach( $this->fetch( 'users', 'users.ID' )->join( 'wpforo_profiles', "users.ID=wpforo_profiles.userid" ) AS $user )
{
/* Main Members Table */
$info = [
'member_id' => $user['ID'],
'member_group_id' => $user['groupid'],
'mgroup_others' => $user['secondary_groups'],
'name' => $user['username'],
'email' => $user['user_email'],
'joined' => new \IPS\DateTime( $user['user_registered'] ),
'conv_password' => $user['user_pass'],
'temp_ban' => $user['status'] == 'blocked' ? -1 : 0,
'signature' => static::fixPostData( $user['signature'] ?: '' ),
'last_visit' => new \IPS\DateTime( $user['last_login'] ),
'last_activity' => new \IPS\DateTime( $user['last_login'] ),
];
$fields = [];
foreach( $this->_profileFields AS $field )
{
if ( $this->app->_session['more_info']['convertMembers']["field_{$field}"] != 'no_convert' )
{
try
{
$fieldId = $this->app->getLink( $field, 'core_pfields_data' );
}
catch( \OutOfRangeException $e )
{
$libraryClass->convertProfileField( [
'pf_id' => $field,
'pf_name' => $this->app->_session['more_info']['convertMembers']["field_{$field}"],
'pf_desc' => '',
'pf_type' => 'Text',
'pf_content' => '[]',
'pf_member_hide' => 'all',
'pf_max_input' => 255,
'pf_member_edit' => 1,
'pf_show_on_reg' => 0,
] );
}
$user[ $field ] = $user[ $field ] ?? '';
}
}
/* Avatars */
$avatarFilename = NULL;
$avatarPath = rtrim( $this->app->_session['more_info']['convertMembers']['wpuploads'], '/' ) . '/wpforo/avatars/';
if( !empty( $user['avatar'] ) )
{
$path = explode( '/', $user['avatar'] );
$avatarFilename = array_pop( $path );
}
else
{
/* Take a guess */
foreach( [ 'jpg', 'png', 'jpeg', 'gif' ] as $ext )
{
$filename = \strtolower( \str_replace( '_', '', \preg_replace( '/\s|\.(?=.*\.)/i', '-',$user['username'] ) ) ) . '_' . $user['ID'] . '.' . $ext;
if( file_exists( $avatarPath . '/' . $filename ) )
{
$avatarFilename = $filename;
}
}
}
$memberId = $libraryClass->convertMember( $info, $fields, $avatarFilename, $avatarPath );
if( $memberId !== FALSE )
{
$this->app->addLink( $memberId, $info['name'], 'member_furl' );
}
$libraryClass->setLastKeyValue( $user['ID'] );
}
}
/**
* Convert PMs
*
* @return void
*/
public function convertPrivateMessages()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'folderid' );
foreach( $this->fetch( 'wpforo_pmfolders', 'folderid' ) AS $row )
{
try
{
$firstMessage = $this->db->select( '*', 'wpforo_pms', [ 'folderid=?', $row['folderid'] ], 'pmid ASC', 1 )->first();
}
catch( \UnderflowException $e )
{
$libraryClass->setLastKeyValue( $row['folderid'] );
continue;
}
try
{
$lastMessage = $this->db->select( '*', 'wpforo_pms', [ 'folderid=?', $row['folderid'] ], 'pmid DESC', 1 )->first();
}
catch( \UnderflowException $e )
{
$libraryClass->setLastKeyValue( $row['folderid'] );
continue;
}
$topic = [
'mt_id' => $row['folderid'],
'mt_date' => \strtotime( $firstMessage['date'] ),
'mt_title' => $row['name'] ?: 'Message',
'mt_starter_id' => $firstMessage['fromuserid'],
'mt_start_time' => \strtotime( $firstMessage['date'] ),
'mt_last_post_time' => \strtotime( $lastMessage['date'] ),
'mt_to_count' => $row['user_count'],
];
$maps = [];
$maps[ $firstMessage['fromuserid'] ] = [
'map_user_id' => $firstMessage['fromuserid'],
'map_read_time' => \strtotime( $firstMessage['date'] ),
'map_is_starter' => true
];
$readData = explode( ',', $lastMessage['read'] );
foreach( explode( ',', $row['userids'] ) as $participant )
{
if( $participant == $firstMessage['fromuserid'] )
{
continue;
}
$maps[ $participant ] = [
'map_user_id' => $participant,
'map_read_time' => \in_array( $participant, $readData ) ? time() : 0,
'map_user_active' => 1,
];
}
$libraryClass->convertPrivateMessage( $topic, $maps );
$libraryClass->setLastKeyValue( $row['folderid'] );
}
}
/**
* Convert PM replies
*
* @return void
*/
public function convertPrivateMessageReplies()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'pmid' );
foreach( $this->fetch( 'wpforo_pms', 'pmid' ) AS $row )
{
$libraryClass->convertPrivateMessageReply( [
'msg_id' => $row['pmid'],
'msg_topic_id' => $row['folderid'],
'msg_date' => \strtotime( $row['date'] ),
'msg_post' => $row['message'],
'msg_author_id' => $row['fromuserid'],
] );
$libraryClass->setLastKeyValue( $row['pmid'] );
}
}
/**
* 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', [ 'app' => $this->app->app_id, 'link' => 'core_message_posts', 'extension' => 'core_Messaging' ], 2, [ 'app', 'link', 'extension' ] );
\IPS\Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ], 2, [ 'app', 'link', 'extension' ] );
\IPS\Task::queue( 'convert', 'RebuildNonContent', [ 'app' => $this->app->app_id, 'link' => 'core_members', 'extension' => 'core_Signatures' ], 2, [ 'app', 'link', 'extension' ] );
/* Content Counts */
\IPS\Task::queue( 'core', 'RecountMemberContent', [ 'app' => $this->app->app_id ], 4, [ 'app' ] );
\IPS\Task::queue( 'core', 'RebuildItemCounts', [ 'class' => 'IPS\core\Messenger\Message' ], 3, [ 'class' ] );
/* First Post Data */
\IPS\Task::queue( 'convert', 'RebuildConversationFirstIds', [ 'app' => $this->app->app_id ], 2, [ 'app' ] );
/* Attachments */
\IPS\Task::queue( 'core', 'RebuildAttachmentThumbnails', [ 'app' => $this->app->app_id ], 1, [ 'app' ] );
return [ "f_search_index_rebuild", "f_clear_caches", "f_rebuild_pms", "f_signatures_rebuild" ];
}
/**
* Process a login
*
* @param \IPS\Member $member The member
* @param string $password Password from form
* @return bool
*/
public static function login( $member, $password )
{
return \IPS\convert\Software\Core\WordPress::login( $member, $password );
}
/**
* 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();
$wpForoSlug = \defined('WPFORO_SLUG') ? WPFOROSLUG : 'community';
$matches = [];
if( preg_match( '#/' . $wpForoSlug . '/profile/([a-z0-9-]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
/* If we can't access profiles, don't bother trying to redirect */
if( !\IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
{
return NULL;
}
try
{
$data = (string) $this->app->getLink( $matches[1], [ 'member_furl' ] );
return \IPS\Member::load( $data )->url();
}
catch( \Exception $e ) { }
}
return NULL;
}
}
@@ -39,8 +39,8 @@ class _Xenforo extends \IPS\convert\Software
* @brief XF1&2 have different pseudo fields
*/
public static $pseudoFields = array(
1 => array( 'homepage', 'location', 'occupation', 'about' ),
2 => array( 'website', 'location', 'about' )
1 => array( 'homepage', 'location', 'occupation', 'about', 'custom_title' ),
2 => array( 'website', 'location', 'about', 'custom_title' )
);
/**
@@ -181,10 +181,6 @@ class _Xenforo extends \IPS\convert\Software
'convertAttachments' => array(
'table' => 'xf_attachment',
'where' => array( "content_type=?", 'conversation_message' )
),
'convertRanks' => array(
'table' => 'xf_user_title_ladder',
'where' => NULL
)
);
@@ -1161,8 +1157,8 @@ class _Xenforo extends \IPS\convert\Software
'member_id' => $row['user_id'],
'name' => html_entity_decode( $row['username'], \ENT_QUOTES | \ENT_HTML5, 'UTF-8' ),
'email' => $row['email'],
'conv_password' => $hash,
'conv_password_extra' => $salt,
'conv_password' => $hash,
'conv_password_extra' => $salt,
'member_group_id' => $row['user_group_id'],
'joined' => $row['register_date'],
'ip_address' => $ip,
@@ -1185,7 +1181,6 @@ class _Xenforo extends \IPS\convert\Software
'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'],
'member_posts' => $row['message_count'],
'signature' => $row['signature']
);
@@ -1478,29 +1473,6 @@ class _Xenforo extends \IPS\convert\Software
}
}
/**
* Convert ranks
*
* @return void
*/
public function convertRanks()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'minimum_level' );
foreach( $this->fetch( 'xf_user_title_ladder', 'minimum_level' ) AS $row )
{
$libraryClass->convertRank( array(
'id' => $row['minimum_level'],
'title' => $row['title'],
'posts' => $row['minimum_level'],
) );
$libraryClass->setLastKeyValue( $row['minimum_level'] );
}
}
/**
* Convert reactions
*
@@ -157,7 +157,7 @@ class _Invisioncommunity extends \IPS\convert\Software
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() ;
return $this->db->select( 'COUNT(*)', 'core_rss_imported', array( 'rss_imported_import_id IN(' . (string) $this->db->select( 'rss_import_id', 'core_rss_import', array( "rss_import_class='IPS\\forums\\Topic'" ) ) . ')' ) )->first();
}
catch( \Exception $e )
{
@@ -31,7 +31,7 @@ class _Phpbb extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "phpBB (3.1.x/3.2.x)";
return "phpBB (3.1.x/3.2.x/3.3.x)";
}
/**
@@ -402,6 +402,16 @@ class _Phpbb extends \IPS\convert\Software
{
$visibility = $row['post_approved'];
}
$editName = null;
if( $row['post_edit_time'] AND $row['post_edit_user'] )
{
try
{
$editName = $this->db->select( 'username', 'users', array( 'user_id=?', $row['post_edit_user'] ) )->first();
}
catch( \UnderflowException $e ) {}
}
$info = array(
'pid' => $row['post_id'],
@@ -409,6 +419,8 @@ class _Phpbb extends \IPS\convert\Software
'post' => \IPS\convert\Software\Core\Phpbb::stripUid( $row['post_text'], $row['bbcode_uid'] ),
'append_edit' => ( $row['post_edit_user'] ) ? 1 : 0,
'edit_time' => $row['post_edit_time'],
'edit_name' => $editName,
'post_edit_reason' => $row['post_edit_reason'] ?? null,
'author_id' => $row['poster_id'],
'ip_address' => $row['poster_ip'],
'post_date' => $row['post_time'],
@@ -456,6 +468,11 @@ class _Phpbb extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $row['post_id'] );
}
}
/**
* @brief temporarily store post content
*/
protected $_postContent = array();
/**
* Convert attachments
@@ -494,28 +511,32 @@ class _Phpbb extends \IPS\convert\Software
{
$pid = $this->app->getLink( $row['post_msg_id'], 'forums_posts' );
$post = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
if( !isset( $this->_postContent[ $pid ] ) )
{
$this->_postContent[ $pid ] = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
}
$attachmentName = preg_quote( $row['real_filename'], '/' );
$regex31 = '/\[attachment=(.+?)?\]\<\!\-\- ia[\d]+ \-\-\>' . $attachmentName . '\<\!\-\- ia[\d]+ \-\-\>\[\/attachment\]/i';
$regex32 = '/\<ATTACHMENT filename\="(.+?)?" index\="(.+?)?"\>\<s\>\[attachment\=(.+?)?\]\<\/s\>' . $attachmentName . '<e\>\[\/attachment\]\<\/e\>\<\/ATTACHMENT\>/i';
$changed = FALSE;
$regex31 = '/\[attachment=(\d+)?\]\<\!\-\- ia[\d]+ \-\-\>' . $attachmentName . '\<\!\-\- ia[\d]+ \-\-\>\[\/attachment\]/i';
$regex32 = '/\<ATTACHMENT filename\="(.+?)?" index\="(.+?)?"\>\<s\>\[attachment\=(\d+)?\]\<\/s\>' . $attachmentName . '<e\>\[\/attachment\]\<\/e\>\<\/ATTACHMENT\>/i';
if( preg_match( $regex31, $post ) )
$replacement = '[attachment=' . $attachId . ':name]';
if( $row['attach_comment'] )
{
$changed = TRUE;
$post = preg_replace( $regex31, '[attachment=' . $attachId . ':name]', $post );
$replacement .= '<p>' . $row['attach_comment'] . '</p>';
}
elseif( preg_match( $regex32, $post ) )
if( preg_match( $regex31, $this->_postContent[ $pid ] ) )
{
$changed = TRUE;
$post = preg_replace( $regex32, '[attachment=' . $attachId . ':name]', $post );
$this->_postContent[ $pid ] = preg_replace( $regex31, $replacement, $this->_postContent[ $pid ] );
}
if( $changed )
elseif( preg_match( $regex32, $this->_postContent[ $pid ] ) )
{
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $post ), array( "pid=?", $pid ) );
$this->_postContent[ $pid ] = preg_replace( $regex32, $replacement, $this->_postContent[ $pid ] );
}
else
{
$this->_postContent[ $pid ] .= '<br>' . $replacement;
}
}
catch( \UnderflowException $e ) {}
@@ -524,6 +545,12 @@ class _Phpbb extends \IPS\convert\Software
$libraryClass->setLastKeyValue( $row['attach_id'] );
}
/* Do the updates */
foreach( $this->_postContent as $pid => $content )
{
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $content ), array( 'pid=?', $pid ) );
}
}
/**
@@ -494,6 +494,7 @@ class _Smf extends \IPS\convert\Software
);
/* We need to figure out where it is */
$legacyName = null;
if ( $row['file_hash'] )
{
$location = $row['id_attach'] . '_' . $row['file_hash'];
@@ -506,6 +507,7 @@ class _Smf extends \IPS\convert\Software
$cleanName = preg_replace( array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $cleanName );
$location = $row['id_attach'] . '_' . str_replace( '.', '_', $cleanName ) . md5( $cleanName );
$legacyName = preg_replace( '~\.[\.]+~', '.', $cleanName);
}
$location = str_replace( ' ', '_', $location );
@@ -536,16 +538,31 @@ class _Smf extends \IPS\convert\Software
if ( $row['id_folder'] AND $row['id_folder'] != 1 )
{
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $location;
if( !file_exists( $path ) AND isset( $legacyName ) )
{
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $legacyName;
}
}
else
{
if ( isset( static::$paths[ $row['id_folder'] ] ) )
{
$path = rtrim( static::$paths[ $row['id_folder'] ], '/' ) . '/' . $location;
if( !file_exists( $path ) AND isset( $legacyName ) )
{
$path = rtrim( static::$paths[ $row['id_folder'] ], '/' ) . '/' . $legacyName;
}
}
else
{
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $location;
if( !file_exists( $path ) AND isset( $legacyName ) )
{
$path = rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . $legacyName;
}
}
}
@@ -56,7 +56,7 @@ class _Vanilla extends \IPS\convert\Software
public static function softwareName()
{
/* Child classes must override this method */
return "Vanilla 2 (2.2.x/2.3.x)";
return "Vanilla (3.x)";
}
/**
@@ -104,10 +104,10 @@ class _Vanilla extends \IPS\convert\Software
),
'convertForumsTopics' => array(
'table' => 'Discussion',
'where' => NULL
'where' => NULL
),
'convertForumsPosts' => array(
'table' => 'Comment',
'table' => 'Discussion',
'where' => NULL,
'extra_steps' => array( 'convertForumsPosts2' )
),
@@ -305,22 +305,7 @@ class _Vanilla extends \IPS\convert\Software
*/
public static function fixPostData( $post )
{
/**
* Vanilla has a quotes plugin that seems to have changed formats quite often. - We'll try to match as many formats as we can
*/
$post = preg_replace( '/(?:<blockquote\s+(?:class=\"(?:User)?Quote\")?(?:\s+rel=\"(?:[^\"]+)\")?>)(?:\s+)?<div class=\"QuoteAuthor\">([^\"]+)<\/div>(?:\s+)?<div class=\"QuoteText\">(?:<p>)?(.*?)(?:<\/p>)?<\/div>(?:\s+)?<\/blockquote>/i',
'[quote name="$1"]$2[/quote]' ,
$post );
$post = preg_replace( '/(?:<blockquote\s+(?:class=\"Quote (?:User)?Quote\")?(?:\s+rel=\"(?:[^\"]+)\")?>)(?:\s+)?<div class=\"QuoteText\">(?:<p>)?(.*?)(?:<\/p>)?<\/div>(?:\s+)?<\/blockquote>/i',
'[quote]$1[/quote]' ,
$post );
$post = preg_replace( '/<blockquote\s+rel=\"([^\"]+)\"?>(.*?)<\/blockquote>/i',
'[quote name="$1"]$2[/quote]' ,
$post );
return $post;
return \IPS\convert\Software\Core\Vanilla::fixPostData( $post );
}
/**
@@ -352,6 +337,7 @@ class _Vanilla extends \IPS\convert\Software
catch( \UnderflowException $ex )
{
$libraryClass->setLastKeyValue( $row['MediaID'] );
continue;
}
$map = array(
@@ -374,7 +360,9 @@ class _Vanilla extends \IPS\convert\Software
'attach_filesize' => $row['Size'],
);
$libraryClass->convertAttachment( $info, $map, rtrim( $this->app->_session['more_info']['convertAttachments']['attach_location'], '/' ) . '/' . trim( $row['Path'], '/' ) );
$uploadPath = \IPS\convert\Software\Core\Vanilla::parseMediaLocation( $row['Path'], $this->app->_session['more_info']['convertAttachments']['attach_location'] );
$libraryClass->convertAttachment( $info, $map, $uploadPath );
$libraryClass->setLastKeyValue( $row['MediaID'] );
}
}
@@ -431,14 +419,37 @@ class _Vanilla extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'd.DiscussionID' );
$discussions = $this->fetch( array( 'Discussion', 'd' ), 'DiscussionID', NULL,
$discussions = $this->fetch( array( 'Discussion', 'd' ), 'DiscussionID', NULL,
'd.*, u.Name as UserName, lcu.UserID as LastCommentUserID, lcu.Name as LastCommentUserName'
);
$discussions->join( array( 'User', 'u' ), 'd.InsertUserID=u.UserID' );
$discussions->join( array( 'User', 'lcu' ), 'd.LastCommentUserID=lcu.UserID' );
foreach( $discussions AS $row )
$data = iterator_to_array( $discussions );
$this->app->preCacheLinks( $data, [ 'core_members' => 'InsertUserID', 'forums_forums' => 'CategoryID' ] );
foreach( $data AS $row )
{
/* Skip reports */
$attributes = [];
if( !empty( $row['attributes'] ) )
{
if( substr( $row['Attributes'], 0, 2 ) == 'a:' ) // Probably serialize
{
$attributes = \unserialize( $row['Attributes'] );
}
else
{
$attributes = json_decode( $row['Attributes'], TRUE );
}
}
if( isset( $attributes['Report'] ) OR $row['Type'] == 'Report' )
{
$libraryClass->setLastKeyValue( $row['DiscussionID'] );
continue;
}
$row['DateLastComment'] = 0;
$row['LastCommentUserID'] = 0;
$row['LastCommentUserName'] = '';
@@ -516,8 +527,31 @@ class _Vanilla extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'DiscussionID' );
foreach( $this->fetch( 'Discussion', 'DiscussionID' ) AS $row )
$data = iterator_to_array( $this->fetch( 'Discussion', 'DiscussionID' ) );
$this->app->preCacheLinks( $data, [ 'core_members' => 'InsertUserID', 'forums_topics' => 'DiscussionID' ] );
foreach( $data AS $row )
{
/* Skip reports */
$attributes = [];
if( !empty( $row['attributes'] ) )
{
if( substr( $row['Attributes'], 0, 2 ) == 'a:' ) // Probably serialize
{
$attributes = \unserialize( $row['Attributes'] );
}
else
{
$attributes = json_decode( $row['Attributes'], TRUE );
}
}
if( isset( $attributes['Report'] ) OR $row['Type'] == 'Report' )
{
$libraryClass->setLastKeyValue( $row['DiscussionID'] );
continue;
}
$editName = NULL;
if( $row['UpdateUserID'] )
@@ -529,6 +563,12 @@ class _Vanilla extends \IPS\convert\Software
catch( \UnderflowException $e ) {}
}
// Add Format Type (for later processing) if Markdown
if( $row['Format'] == 'Markdown' )
{
$row['Body'] = '<!--Markdown-->' . $row['Body'];
}
// First post
$info = array(
'pid' => 'fp-' . $row['DiscussionID'],
@@ -548,7 +588,9 @@ class _Vanilla extends \IPS\convert\Software
/* Reputation - Reactions are only supported if the YAGA addon was used. */
if( static::$_supportsReactions )
{
foreach( $this->db->select( '*', 'Reaction', array( "ParentType=? AND ParentID=?", 'comment', $row['DiscussionID'] ) ) AS $rep )
$reputation = iterator_to_array( $this->db->select( '*', 'Reaction', array( "ParentType=? AND ParentID=?", 'comment', $row['DiscussionID'] ) ) );
$this->app->preCacheLinks( $reputation, [ 'core_members' => [ 'InsertUserID', 'ParentAuthorID' ] ] );
foreach( $reputation AS $rep )
{
$reaction = $this->app->_session['more_info']['convertForumsPosts']['reaction_' . $rep['ActionID'] ];
@@ -577,8 +619,30 @@ class _Vanilla extends \IPS\convert\Software
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'CommentID' );
foreach( $this->fetch( 'Comment', 'CommentID' ) AS $row )
$data = iterator_to_array( $this->fetch( 'Comment', 'CommentID' ) );
$this->app->preCacheLinks( $data, [ 'core_members' => 'InsertUserID', 'forums_topics' => 'DiscussionID' ] );
foreach( $data AS $row )
{
/* Check whether this is a report */
if( !empty( $row['Attributes'] ) )
{
if( substr( $row['Attributes'], 0, 2 ) == 'a:' ) // Probably serialize
{
$attributes = \unserialize( $row['Attributes'] );
}
else
{
$attributes = json_decode( $row['Attributes'], TRUE );
}
if( !empty( $attributes['Type'] ) AND $attributes['Type'] == 'Report' )
{
$libraryClass->setLastKeyValue( $row['CommentID'] );
continue;
}
}
$editName = NULL;
if( $row['UpdateUserID'] )
@@ -590,6 +654,12 @@ class _Vanilla extends \IPS\convert\Software
catch( \UnderflowException $e ) {}
}
// Add Format Type (for later processing) if Markdown
if( $row['Format'] == 'Markdown' )
{
$row['Body'] = '<!--Markdown-->' . $row['Body'];
}
$info = [
'pid' => $row['CommentID'],
'topic_id' => $row['DiscussionID'],
@@ -0,0 +1,530 @@
<?php
/**
* @brief Converter wpForo 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 24 Jan 2021
*/
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;
}
/**
* wpForo Forums Converter
*/
class _Wpforo extends \IPS\convert\Software
{
/**
* Software Name
*
* @return string
*/
public static function softwareName()
{
/* Child classes must override this method */
return "wpForo (1.9.x)";
}
/**
* Software Key
*
* @return string
*/
public static function softwareKey()
{
/* Child classes must override this method */
return "wpforo";
}
/**
* Content we can convert from this software.
*
* @return array
*/
public static function canConvert()
{
return [
'convertForumsForums' => [
'table' => 'wpforo_forums',
'where' => NULL
],
'convertForumsTopics' => [
'table' => 'wpforo_topics',
'where' => NULL
],
'convertForumsPosts' => [
'table' => 'wpforo_posts',
'where' => NULL
],
'convertAttachments' => [
'table' => 'wpforo_posts',
'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( 'wpforo' ) );
}
/**
* 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" );
}
/**
* Fix Post Data
*
* @param string $post Post
* @return string Fixed Post
*/
public static function fixPostData( $post )
{
return \IPS\convert\Software\Core\Wpforo::fixPostData( $post );
}
/**
* List of conversion methods that require additional information
*
* @return array
*/
public static function checkConf()
{
return array(
'convertForumsPosts'
);
}
/**
* Get More Information
*
* @param string $method Conversion method
* @return array
*/
public function getMoreInfo( $method )
{
$return = [];
switch( $method )
{
case 'convertForumsPosts':
/* Get our reactions to let the admin map them */
$options = [];
$descriptions = [];
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'] = [
'rep_positive' => [
'field_class' => 'IPS\\Helpers\\Form\\Radio',
'field_default' => NULL,
'field_required' => TRUE,
'field_extra' => [ 'parse' => 'image', 'options' => $options, 'descriptions' => $descriptions, 'gridspan' => 2 ],
'field_hint' => NULL,
'field_validation' => NULL,
],
];
break;
}
return ( isset( $return[ $method ] ) ) ? $return[ $method ] : [];
}
/**
* Convert forums
*
* @return void
*/
public function convertForumsForums()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'forumid' );
foreach( $this->fetch( 'wpforo_forums', 'forumid' ) AS $row )
{
$info = [
'id' => $row['forumid'],
'name' => $row['title'],
'description' => $row['description'],
'topics' => $row['topics'],
'posts' => $row['posts'],
'parent_id' => ( !$row['parentid'] OR $row['parentid'] == $row['forumid'] ) ? -1 : $row['parentid'],
'position' => $row['order'],
'sub_can_post' => ( !$row['parentid'] OR $row['is_cat'] ) ? 0 : 1,
'feature_color' => $row['color']
];
$forumId = $libraryClass->convertForumsForum( $info );
if( $forumId !== FALSE )
{
$this->app->addLink( $forumId, $row['slug'], 'forum_furl' );
/* Followers */
foreach ( $this->db->select( '*', 'wpforo_subscribes', [ 'itemid=? AND type=?', $row['forumid'], 'forum' ] ) as $follow )
{
$libraryClass->convertFollow( [
'follow_app' => 'forums',
'follow_area' => 'forum',
'follow_rel_id' => $row['forumid'],
'follow_rel_id_type' => 'forums_forums',
'follow_member_id' => $follow['userid'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_notify_do' => $follow['active'],
'follow_notify_meta' => '',
'follow_notify_freq' => 'immediate',
'follow_notify_sent' => 0,
'follow_visible' => 1,
] );
}
}
$libraryClass->setLastKeyValue( $row['forumid'] );
}
}
/**
* Convert topics
*
* @return void
*/
public function convertForumsTopics()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'topicid' );
foreach( $this->fetch( 'wpforo_topics', 'topicid' ) AS $row )
{
$info = [
'tid' => $row['topicid'],
'title' => $row['title'],
'forum_id' => $row['forumid'],
'state' => ( $row['closed'] == 1 ) ? 'closed' : 'open',
'starter_id' => $row['userid'],
'start_date' => \strtotime( $row['created'] ),
'posts' => $row['posts'],
'views' => $row['views'],
'approved' => $row['private'] ? 0 : 1
];
$topicId = $libraryClass->convertForumsTopic( $info );
if( $topicId !== FALSE )
{
$this->app->addLink( $topicId, $row['slug'], 'topic_furl' );
/* Tags */
$tags = explode( ',', $row['tags'] );
foreach( $tags as $text )
{
$libraryClass->convertTag( [
'tag_meta_app' => 'forums',
'tag_meta_area' => 'forums',
'tag_meta_parent_id' => $row['forumid'],
'tag_meta_id' => $row['topicid'],
'tag_text' => $text,
'tag_member_id' => $row['userid'],
] );
}
/* Follows */
foreach( $this->db->select( '*', 'wpforo_subscribes', [ 'itemid=? AND type=?', $row['topicid'], 'topic' ] ) AS $follow )
{
$libraryClass->convertFollow( [
'follow_app' => 'forums',
'follow_area' => 'topic',
'follow_rel_id' => $row['topicid'],
'follow_rel_id_type' => 'forums_topics',
'follow_member_id' => $follow['userid'],
'follow_is_anon' => 0,
'follow_added' => time(),
'follow_notify_do' => $follow['active'],
'follow_notify_meta' => '',
'follow_notify_freq' => 'immediate',
'follow_notify_sent' => 0,
'follow_visible' => 1,
] );
}
}
$libraryClass->setLastKeyValue( $row['topicid'] );
}
}
/**
* Convert posts
*
* @return void
*/
public function convertForumsPosts()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'postid' );
foreach( $this->fetch( 'wpforo_posts', 'postid' ) AS $row )
{
$info = [
'pid' => $row['postid'],
'topic_id' => $row['topicid'],
'post' => $row['body'],
'edit_time' => $row['modified'] != $row['created'] ? \strtotime( $row['modified'] ) : null,
'author_id' => $row['userid'],
'post_date' => \strtotime( $row['created'] ),
'queued' => $row['private'] ? 1 : 0,
];
$libraryClass->convertForumsPost( $info );
/* Reputation */
foreach( $this->db->select( '*', 'wpforo_likes', [ 'postid=?', $row['postid'] ] ) AS $rep )
{
$libraryClass->convertReputation( [
'id' => $rep['likeid'],
'app' => 'forums',
'type' => 'pid',
'type_id' => $row['postid'],
'member_id' => $rep['userid'],
'member_received' => $rep['post_userid'],
'rep_date' => $info['post_date'],
'reaction' => $this->app->_session['more_info']['convertForumsPosts']['rep_positive']
] );
}
$libraryClass->setLastKeyValue( $row['postid'] );
}
}
/**
* @brief temporarily store post content
*/
protected $_postContent = [];
/**
* Convert attachments
*
* @return void
*/
public function convertAttachments()
{
$libraryClass = $this->getLibrary();
$libraryClass::setKey( 'postid' );
foreach( $this->fetch( 'wpforo_posts', 'postid' ) AS $row )
{
if( !\stristr( $row['body'], '[attach]' ) AND !\stristr( $row['body'], 'wpforo-attached-file' ) )
{
$libraryClass->setLastKeyValue( $row['postid'] );
continue;
}
$map = [
'id1' => $row['topicid'],
'id2' => $row['postid']
];
/* Advanced Attachments */
$matches = [];
preg_match_all( '/\[attach\](\d+)\[\/attach\]/i', $row['body'], $matches );
if( \count( $matches ) )
{
foreach( $matches[1] as $key => $id )
{
$sourceAttachment = $this->db->select( '*', 'wpforo_attachments', [ 'attachid=?', $id ] )->first();
$url = explode( '/', $sourceAttachment['fileurl'] );
$filename = array_pop( $url );
$info = [
'attach_id' => $row['postid'],
'attach_file' => $sourceAttachment['filename'],
'attach_date' => \strtotime( $row['created'] ),
'attach_member_id' => $sourceAttachment['userid'],
'attach_filesize' => $sourceAttachment['size'],
];
$realFilePath = '/wpforo/attachments/' . $sourceAttachment['userid'] . '/' . $filename;
$path = rtrim( $this->app->_parent->_session['more_info']['convertMembers']['wpuploads'], '/' ) . $realFilePath;
$attachId = $libraryClass->convertAttachment( $info, $map, $path );
/* Update post if we can */
try
{
if ( $attachId !== FALSE )
{
$pid = $this->app->getLink( $row['postid'], 'forums_posts' );
if( !isset( $this->_postContent[ $pid ] ) )
{
$this->_postContent[ $pid ] = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
}
$this->_postContent[ $pid ] = str_replace( $matches[0][ $key ], '[attachment=' . $attachId . ':name]', $this->_postContent[ $pid ] );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
}
}
/* Default Attachments */
$matches = [];
preg_match_all( '/\<div id\="wpfa\-[\d]+"(.+?)?>\<a class\="wpforo\-default\-attachment" href\="(.+?)"(.+?)?>\<i class\="(.+?)">\<\/i>(.+?)<\/a><\/div>/i', $row['body'], $matches );
if( \count( $matches ) )
{
foreach( $matches[2] as $key => $url )
{
$url = explode( '/', $url );
$filename = array_pop( $url );
$info = [
'attach_id' => $row['postid'],
'attach_file' => $filename,
'attach_date' => \strtotime( $row['created'] ),
'attach_member_id' => $row['userid'],
];
$realFilePath = '/wpforo/default_attachments/' . $filename;
$path = rtrim( $this->app->_parent->_session['more_info']['convertMembers']['wpuploads'], '/' ) . $realFilePath;
$attachId = $libraryClass->convertAttachment( $info, $map, $path );
/* Update post if we can */
try
{
if ( $attachId !== FALSE )
{
$pid = $this->app->getLink( $row['postid'], 'forums_posts' );
if( !isset( $this->_postContent[ $pid ] ) )
{
$this->_postContent[ $pid ] = \IPS\Db::i()->select( 'post', 'forums_posts', array( "pid=?", $pid ) )->first();
}
$this->_postContent[ $pid ] = str_replace( $matches[0][ $key ], '[attachment=' . $attachId . ':name]', $this->_postContent[ $pid ] );
}
}
catch( \UnderflowException $e ) {}
catch( \OutOfRangeException $e ) {}
}
}
$libraryClass->setLastKeyValue( $row['postid'] );
}
/* Do the updates */
foreach( $this->_postContent as $id => $content )
{
\IPS\Db::i()->update( 'forums_posts', array( 'post' => $content ), array( 'pid=?', $id ) );
}
}
/**
* 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();
$wpForoSlug = \defined('WPFORO_SLUG') ? WPFOROSLUG : 'community';
$matches = [];
if( preg_match( '#/' . $wpForoSlug . '/([a-z0-9-]+)/([a-z0-9-]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
$class = '\IPS\forums\Topic';
$types = [ 'topic_furl' ];
$oldId = $matches[2];
}
elseif( preg_match( '#/' . $wpForoSlug . '/([a-z0-9-]+)#i', $url->data[ \IPS\Http\Url::COMPONENT_PATH ], $matches ) )
{
$class = '\IPS\forums\Forum';
$types = [ 'forum_furl' ];
$oldId = $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;
}
}
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013-2018 Emanuil Rusev, erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,86 @@
> I also make [Caret](https://caret.io?ref=parsedown) - a Markdown editor for Mac and PC.
## Parsedown
[![Build Status](https://img.shields.io/travis/erusev/parsedown/master.svg?style=flat-square)](https://travis-ci.org/erusev/parsedown)
<!--[![Total Downloads](http://img.shields.io/packagist/dt/erusev/parsedown.svg?style=flat-square)](https://packagist.org/packages/erusev/parsedown)-->
Better Markdown Parser in PHP
[Demo](http://parsedown.org/demo) |
[Benchmarks](http://parsedown.org/speed) |
[Tests](http://parsedown.org/tests/) |
[Documentation](https://github.com/erusev/parsedown/wiki/)
### Features
* One File
* No Dependencies
* Super Fast
* Extensible
* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown)
* Tested in 5.3 to 7.1 and in HHVM
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra)
### Installation
Include `Parsedown.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown).
### Example
``` php
$Parsedown = new Parsedown();
echo $Parsedown->text('Hello _Parsedown_!'); # prints: <p>Hello <em>Parsedown</em>!</p>
```
More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI).
### Security
Parsedown is capable of escaping user-input within the HTML that it generates. Additionally Parsedown will apply sanitisation to additional scripting vectors (such as scripting link destinations) that are introduced by the markdown syntax itself.
To tell Parsedown that it is processing untrusted user-input, use the following:
```php
$parsedown = new Parsedown;
$parsedown->setSafeMode(true);
```
If instead, you wish to allow HTML within untrusted user-input, but still want output to be free from XSS it is recommended that you make use of a HTML sanitiser that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
In both cases you should strongly consider employing defence-in-depth measures, like [deploying a Content-Security-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/) (a browser security feature) so that your page is likely to be safe even if an attacker finds a vulnerability in one of the first lines of defence above.
#### Security of Parsedown Extensions
Safe mode does not necessarily yield safe results when using extensions to Parsedown. Extensions should be evaluated on their own to determine their specific safety against XSS.
### Escaping HTML
> ⚠️  **WARNING:** This method isn't safe from XSS!
If you wish to escape HTML **in trusted input**, you can use the following:
```php
$parsedown = new Parsedown;
$parsedown->setMarkupEscaped(true);
```
Beware that this still allows users to insert unsafe scripting vectors, such as links like `[xss](javascript:alert%281%29)`.
### Questions
**How does Parsedown work?**
It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line starts with a `-` then perhaps it belongs to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines).
We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages.
**Is it compliant with CommonMark?**
It passes most of the CommonMark tests. Most of the tests that don't pass deal with cases that are quite uncommon. Still, as CommonMark matures, compliance should improve.
**Who uses it?**
[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [Statamic CMS](http://www.statamic.com/), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
**How can I help?**
Use it, star it, share it and if you feel generous, [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2).
@@ -0,0 +1,33 @@
{
"name": "erusev/parsedown",
"description": "Parser for Markdown.",
"keywords": ["markdown", "parser"],
"homepage": "http://parsedown.org",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"require": {
"php": ">=5.3.0",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"autoload": {
"psr-0": {"Parsedown": ""}
},
"autoload-dev": {
"psr-0": {
"TestParsedown": "test/",
"ParsedownTest": "test/",
"CommonMarkTest": "test/",
"CommonMarkTestWeak": "test/"
}
}
}