Version 4.5.0

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

No files matched your search

@@ -1,20 +1,33 @@
<?php
/**
* @brief Converter: Perform conversion
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\modules\admin\manage;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* convert
* Run conversion
*/
class _convert extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -22,6 +35,11 @@ class _convert extends \IPS\Dispatcher\Controller
*/
public function execute()
{
if ( \IPS\DEMO_MODE === TRUE )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '2V368/2', 403, '' );
}
\IPS\Output::i()->responsive = FALSE;
\IPS\Dispatcher::i()->checkAcpPermission( 'convert_manage' );
parent::execute();
@@ -34,11 +52,13 @@ class _convert extends \IPS\Dispatcher\Controller
*/
protected function manage()
{
/* Do we have an id? */
if ( ! isset( \IPS\Request::i()->id ) )
{
\IPS\Output::i()->error( 'no_conversion_app', '2V101/1' );
}
/* Load the app */
try
{
$app = \IPS\convert\App::load( \IPS\Request::i()->id );
@@ -48,45 +68,57 @@ class _convert extends \IPS\Dispatcher\Controller
\IPS\Output::i()->error( 'conversion_app_not_found', '2V101/2', 404 );
}
if ( $app->finished )
{
\IPS\Output::i()->error( 'conversion_app_finished', '2V368/1', 403, '' );
}
$softwareClass = $app->getSource();
$libraryClass = $softwareClass->getLibrary();
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'converting_x_to_x', FALSE, array( 'sprintf' => array( $softwareClass::softwareName(), \IPS\Application::load( $app->sw )->_title ) ) );
/* Build our table. If I can do this using only the Table helper, I'll be impressed */
try
{
$menuRows = $libraryClass->menuRows( TRUE );
/* Do a quick parent storage check */
\IPS\convert\Application::checkConvParent( $app->getSource()->getLibrary()->app );
/* "Extra steps" should not show in the list of things to convert, but still need to be defined in menu rows */
$extraSteps = array();
/* Are we using the legacy UI? If not, then start converting as we already have everything we need. */
if( !\IPS\CONVERTERS_DEV_UI )
{
$configurationNeeded = FALSE;
foreach( $softwareClass::canConvert() as $row )
/* If we have not yet configured the app, go there first */
if( !\count( $app->_session['more_info'] ) )
{
if( isset( $row['extra_steps'] ) )
$configurationNeeded = TRUE;
}
/* Check child apps also have more_info */
foreach( $app->children() as $childApp )
{
if( !\count( $childApp->_session['more_info'] ) )
{
$extraSteps = array_merge( $extraSteps, $row['extra_steps'] );
$configurationNeeded = TRUE;
break;
}
}
$menuRows = array_filter( $menuRows, function( $row ) use ( $extraSteps ) {
if( in_array( $row['step_method'], $extraSteps ) )
{
return FALSE;
}
/* Do we need to configure? */
if( $configurationNeeded )
{
$url = \IPS\Http\Url::internal( "app=convert&module=manage&controller=create&_moveToStep=convert_start_conversion_details&id=" . ( $app->parent ?: $app->app_id ) );
}
/* Otherwise start converting! */
else
{
$url = \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=runStep&id=" . $app->app_id )->csrf();
return TRUE;
});
}
catch( \IPS\convert\Exception $e )
{
\IPS\Output::i()->error( $e->getMessage(), '1V101/3' );
if( isset( \IPS\Request::i()->continue ) )
{
$url = $url->setQueryString( 'continue', \IPS\Request::i()->continue );
}
}
\IPS\Output::i()->redirect( $url );
}
/* Get our details */
$softwareClass = $app->getSource();
$libraryClass = $softwareClass->getLibrary();
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'converting_x_to_x', FALSE, array( 'sprintf' => array( $softwareClass::softwareName(), \IPS\Application::load( $app->sw )->_title ), 'striptags' => true ) );
/* Build our table. If I can do this using only the Table helper, I'll be impressed */
$menuRows = static::getMenuRows( $softwareClass );
$table = new \IPS\Helpers\Table\Custom( $menuRows, \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert" ) );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'table' ), 'convertMenuRow' );
$table->extra = array( 'sessionData' => $app->_session, 'appClass' => $app, 'softwareClass' => $softwareClass, 'libraryClass' => $libraryClass, 'menuRows' => $menuRows );
@@ -115,6 +147,194 @@ class _convert extends \IPS\Dispatcher\Controller
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'table' )->postConversionInformation( $libraryClass->getPostConversionInformation() );
}
}
/**
* Get the appropriate menu rows for the library
*
* @param \IPS\convert\Software $softwareClass Library class
* @param bool $filter Filter out extra steps
* @param bool $return Let exception bubble instead of outputting it
* @param bool $count Count database rows
* @return array
*/
public static function getMenuRows( $softwareClass, $filter=TRUE, $return=FALSE, $count=TRUE )
{
$libraryClass = $softwareClass->getLibrary();
try
{
$menuRows = $libraryClass->menuRows( $count );
/* "Extra steps" should not show in the list of things to convert, but still need to be defined in menu rows */
$extraSteps = array();
foreach( $softwareClass::canConvert() as $row )
{
if( isset( $row['extra_steps'] ) )
{
$extraSteps = array_merge( $extraSteps, $row['extra_steps'] );
}
}
if( $filter === TRUE )
{
$menuRows = array_filter( $menuRows, function( $row ) use ( $extraSteps ) {
if( \in_array( $row['step_method'], $extraSteps ) )
{
return FALSE;
}
return TRUE;
});
}
}
catch( \IPS\convert\Exception $e )
{
if( $return )
{
throw $e;
}
\IPS\Output::i()->error( $e->getMessage(), '1V101/3', 500 );
}
return $menuRows;
}
/**
* Figure out the next steps for converting
*
* @param \IPS\convert\App $app Application we are converting
* @return array
*/
protected function _getNextStep( $app )
{
/* Set our variables */
$appId = $app->app_id;
$method = null;
if ( !isset( $_SESSION['convertCountRows'] ) )
{
$_SESSION['convertCountRows'] = array();
}
$applicationsToCheck = array();
if( $app->parent )
{
$masterAppId = $app->_parent->app_id;
$applicationsToCheck[] = $app->_parent;
$applicationsToCheck = array_merge( $applicationsToCheck, iterator_to_array( $app->_parent->children() ) );
}
else
{
$masterAppId = $app->app_id;
$applicationsToCheck[] = $app;
$applicationsToCheck = array_merge( $applicationsToCheck, iterator_to_array( $app->children() ) );
}
/* Set up cache array */
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ] = array();
}
/* Loop over all applications */
if( \count( $applicationsToCheck ) )
{
foreach( $applicationsToCheck as $appToCheck )
{
/* If this is the parent app, and we can convert settings, and we elected to convert them, and no steps are done, go there first */
$softwareClass = $appToCheck->getSource();
/* Set up cache array */
if( !isset( $_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ] ) )
{
$_SESSION['convertCountRows'][ $masterAppId ][ $appToCheck->sw ] = 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;
}
/* First, get all of the menu rows for this application to see if there are any (more) to run.
Do NOT filter out extra steps as we need to run those too. */
$menuRows = static::getMenuRows( $softwareClass, FALSE, FALSE, FALSE );
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'] ] ) )
{
/* 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'];
}
if( $method !== NULL )
{
continue;
}
$appId = $appToCheck->app_id;
/* If the row is marked working, this is the one we're on */
if( array_key_exists( $row['step_method'], $appToCheck->_session['working'] ) )
{
$method = $row['step_method'];
continue;
}
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'] ) )
{
unset( $session['completed'][ $key ] );
$app->_session = $session;
}
}
/* Check whether the dependency was set to be converted, or whether it actually converted (it may not have any source data) */
foreach( $row['dependencies'] as $k => $v )
{
if( empty( $menuRows[ $v ] ) OR empty( $appToCheck->_session['more_info'][ $v ][ $menuRows[ $v ]['step_title'] ] ) )
{
/* It wasn't, so we can continue without this dependency */
unset( $row['dependencies'][ $k ] );
}
}
/* If this step has any dependencies not yet converted, skip for now */
if( \count( array_filter( $row['dependencies'], array( $appToCheck, 'dependencies' ) ) ) )
{
continue;
}
/* If this step is already completed, also skip */
if( \in_array( $row['step_method'], $appToCheck->_session['completed'] ) )
{
continue;
}
/* If we don't want this converted, also skip */
if( empty( $appToCheck->_session['more_info'][ $row['step_method'] ][ $row['step_title'] ] ) AND !\in_array( $row['step_method'], array_keys( $softwareClass->extraMenuRows() ) ) )
{
continue;
}
/* Otherwise, this is our next step */
$method = $row['step_method'];
}
}
}
return array( 'id' => $appId, 'method' => $method );
}
/**
* Remove Converted Data
@@ -123,6 +343,8 @@ class _convert extends \IPS\Dispatcher\Controller
*/
protected function emptyData()
{
\IPS\Session::i()->csrfCheck();
if ( ! isset( \IPS\Request::i()->id ) )
{
\IPS\Output::i()->error( 'no_conversion_app', '2V101/4' );
@@ -163,11 +385,15 @@ class _convert extends \IPS\Dispatcher\Controller
*/
protected function runStep()
{
\IPS\Session::i()->csrfCheck();
/* Make sure we have our app id */
if ( ! isset( \IPS\Request::i()->id ) )
{
\IPS\Output::i()->error( 'no_conversion_app', '2V101/6' );
}
/* Load our app */
try
{
$app = \IPS\convert\App::load( \IPS\Request::i()->id );
@@ -178,7 +404,7 @@ class _convert extends \IPS\Dispatcher\Controller
}
/* If we decided to truncate, we need to do so before getMoreInfo() is generated */
if ( isset( \IPS\Request::i()->empty_local_data ) AND \IPS\Request::i()->empty_local_data == 1 )
if ( \IPS\CONVERTERS_DEV_UI AND isset( \IPS\Request::i()->empty_local_data ) AND \IPS\Request::i()->empty_local_data == 1 )
{
$app->getSource()->getLibrary()->emptyLocalData( \IPS\Request::i()->method );
@@ -187,7 +413,6 @@ class _convert extends \IPS\Dispatcher\Controller
$session = $app->_session;
if ( isset( $canConvert[ \IPS\Request::i()->method ]['extra_steps'] ) )
{
$next = NULL;
foreach( $canConvert[ \IPS\Request::i()->method ]['extra_steps'] as $next )
{
if( $key = array_search( $next, $session['completed'] ) )
@@ -220,47 +445,11 @@ class _convert extends \IPS\Dispatcher\Controller
}
/* Do we need more information? */
$softwareClass = $app->getSource();
if ( ! isset( $app->_session['more_info'][ \IPS\Request::i()->method ] ) OR ( isset( \IPS\Request::i()->reconfigure ) AND \IPS\Request::i()->reconfigure == 1 ) )
if ( \IPS\CONVERTERS_DEV_UI AND ! isset( $app->_session['more_info'][ \IPS\Request::i()->method ] ) OR ( isset( \IPS\Request::i()->reconfigure ) AND \IPS\Request::i()->reconfigure == 1 ) )
{
if ( in_array( \IPS\Request::i()->method, $softwareClass::checkConf() ) )
if( $this->_checkMethodConfiguration( $app ) )
{
$getMoreInfo = $softwareClass->getMoreInfo( \IPS\Request::i()->method );
if ( is_array( $getMoreInfo ) AND count( $getMoreInfo ) )
{
$form = new \IPS\Helpers\Form( \IPS\Request::i()->method . '_more_info', 'continue' );
$form->hiddenValues['per_cycle'] = \IPS\Request::i()->per_cycle;
if ( isset( \IPS\Request::i()->reconfigure ) )
{
$form->hiddenValues['reconfigure'] = 1;
}
/* Show the 'more info' fields */
foreach( $getMoreInfo as $key => $input )
{
$fieldClass = $input['field_class'];
$form->add( new $fieldClass( $key, $input['field_default'], $input['field_required'], $input['field_extra'], isset( $input['field_validation'] ) ? $input['field_validation'] : NULL ) );
if ( $input['field_hint'] !== NULL )
{
$form->addMessage( $input['field_hint'], 'ipsMessage ipsMessage_info' );
}
}
if ( $values = $form->values() )
{
$per_cycle = $values['per_cycle'];
unset( $values['per_cycle'] );
$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()->title = \IPS\Member::loggedIn()->language()->addToStack( 'more_info_needed' );
\IPS\Output::i()->output = $form;
return;
}
return;
}
}
@@ -274,45 +463,154 @@ class _convert extends \IPS\Dispatcher\Controller
}
}
/* Generate our multiredirect URL */
$redirectUrl = \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=runStep&id={$app->app_id}" )->csrf();
/* Legacy URL parameters we won't need now, but will if using the developer UI */
if( isset( \IPS\Request::i()->per_cycle ) )
{
$redirectUrl = $redirectUrl->setQueryString( 'per_cycle', \IPS\Request::i()->per_cycle );
}
if( isset( \IPS\Request::i()->method ) )
{
$redirectUrl = $redirectUrl->setQueryString( 'method', \IPS\Request::i()->method );
}
/* Output multiredirector */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'converting' );
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=runStep&id={$app->app_id}&per_cycle=" . \IPS\Request::i()->per_cycle . "&method=" . \IPS\Request::i()->method ),
function( $data ) use ( $app )
{
try
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect(
/* The multiredirect URL */
$redirectUrl,
/* The function we are running on each step */
function( $data ) use ( $app )
{
return $app->getSource()->getLibrary()->process( $data, \IPS\Request::i()->method, \IPS\Request::i()->per_cycle );
}
catch( \IPS\convert\Exception $e )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=error&id={$app->app_id}" ) );
}
},
function() use ( $app )
{
/* Do we have more steps to process? If so, go to those first. */
$canConvert = $app->getSource()->getLibrary()->getConvertableItems();
if ( isset( $canConvert[ \IPS\Request::i()->method ]['extra_steps'] ) )
{
/* See which one we need to run next. */
$next = NULL;
foreach( $canConvert[ \IPS\Request::i()->method ]['extra_steps'] as $next )
/* Determine which method we are converting */
$convertData = $this->_getNextStep( $app );
/* If there is no method returned, we are done */
if( $convertData['method'] === NULL )
{
if ( in_array( $next, $app->_session['completed'] ) )
return NULL;
}
/* If we are moving on to a different app, load it now */
if( $convertData['id'] != $app->app_id )
{
$app = \IPS\convert\App::load( $convertData['id'] );
}
/* Convert settings if that's what we're doing */
if( $convertData['method'] == 'convertSettings' )
{
$app->getSource()->convertSettings( $app->_session['more_info']['convertSettings'] );
\IPS\Settings::i()->clearCache();
/* A Software Exception indicates we are done */
$completed = $app->_session['completed'];
$more_info = $app->_session['more_info'];
if ( !\in_array( $convertData['method'], $completed ) )
{
$next = NULL;
continue;
$completed[] = $convertData['method'];
}
/* Manually set running flag to save write queries */
$running = (isset( $app->_session['running'] ) ) ? $app->_session['running'] : array();
$running[ $convertData['method'] ] = FALSE;
$app->_session = array( 'working' => array(), 'more_info' => $more_info, 'completed' => $completed, 'running' => $running );
$percentage = 100 / \IPS\convert\Library::getTotalCachedRows( $app->getMasterConversionId() );
if ( $percentage > 100 )
{
$percentage = 100;
}
return array( 0, sprintf( \IPS\Member::loggedIn()->language()->get( 'converted_x_of_x' ), 1, 1, \IPS\Member::loggedIn()->language()->addToStack( '_convert_settings' ) ), $percentage );
}
try
{
/* If the current method has been running for 45 seconds, set the flag to 'continue' */
if( $app->getRunningFlag( $convertData['method'] ) AND $app->getRunningFlag( $convertData['method'] ) < ( time() - 45 ) )
{
$_SESSION['convertContinue'] = TRUE;
}
return $app->getSource()->getLibrary()->process( $data, $convertData['method'], $app->getSource()->getLibrary()->getMethodFromMenuRows( $convertData['method'] )['per_cycle'] );
}
catch( \IPS\convert\Exception $e )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=error&id={$app->app_id}" ) );
}
},
/* The final function to run */
function() use ( $app )
{
if( \IPS\CONVERTERS_DEV_UI AND isset( \IPS\Request::i()->method ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=manage&id=" . ( $app->parent ?: $app->app_id ) ) );
}
else
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=finish&wasConfirmed=1&id=" . ( $app->parent ?: $app->app_id ) )->csrf() );
}
}
);
}
/**
* Check if we need to configure this conversion step
*
* @param \IPS\convert\App $app App we are converting
* @return bool
*/
protected function _checkMethodConfiguration( $app )
{
$softwareClass = $app->getSource();
if ( \in_array( \IPS\Request::i()->method, $softwareClass::checkConf() ) )
{
$getMoreInfo = $softwareClass->getMoreInfo( \IPS\Request::i()->method );
if ( \is_array( $getMoreInfo ) AND \count( $getMoreInfo ) )
{
$form = new \IPS\Helpers\Form( \IPS\Request::i()->method . '_more_info', 'continue' );
$form->hiddenValues['per_cycle'] = \IPS\Request::i()->per_cycle;
if ( isset( \IPS\Request::i()->reconfigure ) )
{
$form->hiddenValues['reconfigure'] = 1;
}
/* Show the 'more info' fields */
foreach( $getMoreInfo as $key => $input )
{
$fieldClass = $input['field_class'];
$form->add( new $fieldClass( $key, $input['field_default'], $input['field_required'], $input['field_extra'], isset( $input['field_validation'] ) ? $input['field_validation'] : NULL ) );
if ( $input['field_hint'] !== NULL )
{
$form->addMessage( $input['field_hint'], 'ipsMessage ipsMessage_info' );
}
}
/* Found one? */
if ( !is_null( $next ) )
if ( $values = $form->values() )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=runStep&id={$app->app_id}&per_cycle=" . \IPS\Request::i()->per_cycle . "&method={$next}" ) );
$per_cycle = $values['per_cycle'];
unset( $values['per_cycle'] );
$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()->title = \IPS\Member::loggedIn()->language()->addToStack( 'more_info_needed' );
\IPS\Output::i()->output = $form;
return TRUE;
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&id={$app->app_id}" ) );
} );
}
return FALSE;
}
/**
@@ -375,7 +673,7 @@ class _convert extends \IPS\Dispatcher\Controller
foreach( $softwareClass->settingsMapList() as $key => $setting )
{
$value = $setting['value'];
if ( is_bool( ( $setting['value'] ) ) )
if ( \is_bool( ( $setting['value'] ) ) )
{
$value = $setting['value'] === TRUE ? 'On' : 'Off';
}
@@ -437,7 +735,7 @@ class _convert extends \IPS\Dispatcher\Controller
$parent->finished = TRUE;
$parent->save();
if ( is_array( $return ) )
if ( \is_array( $return ) )
{
$messages = array_merge( $messages, $return );
}
@@ -457,7 +755,7 @@ class _convert extends \IPS\Dispatcher\Controller
$sibling->finished = TRUE;
$sibling->save();
if ( is_array( $return ) )
if ( \is_array( $return ) )
{
$messages = array_merge( $messages, $return );
}
@@ -477,7 +775,7 @@ class _convert extends \IPS\Dispatcher\Controller
catch( \OutOfRangeException $e )
{
/* This is a parent - run it's children */
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'convert_apps', array( "parent=?", $app->app_id ) ), 'IPS\convert\App' ) AS $child )
foreach( $app->children() AS $child )
{
$childSoftwareClass = $child->getSource();
@@ -488,7 +786,7 @@ class _convert extends \IPS\Dispatcher\Controller
$child->finished = TRUE;
$child->save();
if ( is_array( $return ) )
if ( \is_array( $return ) )
{
$messages = array_merge( $messages, $return );
}
@@ -508,7 +806,7 @@ class _convert extends \IPS\Dispatcher\Controller
$app->finished = TRUE;
$app->save();
if ( is_array( $return ) )
if ( \is_array( $return ) )
{
$messages = array_merge( $messages, $return );
}
@@ -519,13 +817,15 @@ class _convert extends \IPS\Dispatcher\Controller
}
/* Any Messages? */
if( !count( $messages ) )
if( !\count( $messages ) )
{
$messages = array( 'nothing_to_finish' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=manage" ), implode( ', ', array_map( function( $key ) {
$messages = array_map( function( $key ) {
return \IPS\Member::loggedIn()->language()->addToStack( $key );
}, $messages ) ) );
}, $messages );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=manage" ), \IPS\Member::loggedIn()->language()->formatList( $messages ) );
}
}
@@ -1,20 +1,33 @@
<?php
/**
* @brief Converter: Start a new conversion
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\modules\admin\manage;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* create
* Create new conversion session
*/
class _create extends \IPS\Dispatcher\Controller
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -22,8 +35,15 @@ class _create extends \IPS\Dispatcher\Controller
*/
public function execute()
{
if ( \IPS\DEMO_MODE === TRUE )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '2V391/3', 403, '' );
}
\IPS\Output::i()->responsive = FALSE;
\IPS\Dispatcher::i()->checkAcpPermission( 'create_manage' );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'convert.css' ) );
parent::execute();
}
@@ -38,87 +58,34 @@ class _create extends \IPS\Dispatcher\Controller
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'convert_start' );
/* Let's call on the wizard to make some magic */
\IPS\Output::i()->output = new \IPS\Helpers\Wizard( array(
'convert_start_application' => array( $this, '_stepOne' ),
'convert_start_source_software' => array( $this, '_stepTwo' ),
'convert_start_database_details' => array( $this, '_stepThree' ),
\IPS\Output::i()->output = (string) new \IPS\Helpers\Wizard( array(
'convert_start_source_software' => array( $this, '_chooseSoftware' ),
'convert_start_application' => array( $this, '_chooseApplications' ),
'convert_start_database_details' => array( $this, '_setDatabaseDetails' ),
'convert_start_conversion_details' => array( $this, '_setConversionDetails' ),
), \IPS\Http\Url::internal( 'app=convert&module=manage&controller=create' ) );
}
/**
* First step of conversion
* Choose what software you want to convert from
*
* @return void
* @param array $data Wizard data from previous steps
* @return \IPS\Helpers\Form|array
*/
public function _stepOne( $data )
{
foreach( \IPS\Application::applications() AS $app )
{
if ( \IPS\Member::loggedIn()->language()->checkKeyExists( "{$app->directory}_node_select" ) )
{
\IPS\member::loggedIn()->language()->words["__app_{$app->directory}"] = \IPS\Member::loggedIn()->language()->addToStack( "{$app->directory}_node_select" );
}
}
/* Have we done any conversions yet? */
$appCount = \IPS\Db::i()->select( 'COUNT(*)', 'convert_apps', array( "parent=?", 0 ) )->first();
$form = new \IPS\Helpers\Form( 'convert_application', 'select_source' );
$form->add( new \IPS\Helpers\Form\Node( 'convert_start_application', NULL, TRUE, array( 'class' => 'IPS\\Application', 'subnodes' => FALSE, 'permissionCheck' => function( $node ) use ( $appCount )
{
foreach( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/convert/sources/Library' ) AS $file )
{
if ( ucwords( $node->directory ) === str_replace( '.php', '', $file->getFilename() ) )
{
$hasFile = FALSE;
/* Only show the node if there is a converter available */
foreach( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/convert/sources/Software/' . ucwords( $node->directory ) ) AS $softwareFile )
{
if( mb_substr( $softwareFile->getFilename(), -3 ) === 'php' )
{
$hasFile = TRUE;
break;
}
}
if ( ( $node->directory === 'core' OR $appCount > 0 ) and $hasFile )
{
return TRUE;
}
}
}
return NULL;
} ) ) );
if ( $values = $form->values() )
{
return array( 'convert_start_application' => $values['convert_start_application']->directory );
}
return $form;
}
/**
* Second step of conversion
*
* @return void
*/
public function _stepTwo( $data )
public function _chooseSoftware( $data )
{
$options = array();
foreach( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/convert/sources/Software/' . ucwords( $data['convert_start_application'] ) ) AS $file )
foreach( \IPS\convert\Software::software()['core'] AS $key => $classname )
{
$classname = 'IPS\\convert\\Software\\' . ucwords( $data['convert_start_application'] ) . '\\' . ucwords( str_replace( '.php', '', $file->getFilename() ) );
if ( class_exists( $classname ) AND $classname::canConvert() !== NULL )
{
$options[ $classname::softwareKey() ] = $classname::softwareName();
}
}
$form = new \IPS\Helpers\Form( 'convert_source_software', 'enter_database_details' );
$form = new \IPS\Helpers\Form( 'convert_source_software', 'choose_what_to_convert' );
$form->add( new \IPS\Helpers\Form\Select( 'convert_start_source_software', NULL, TRUE, array( 'options' => $options ) ) );
if ( $values = $form->values() )
{
return $values;
@@ -128,56 +95,100 @@ class _create extends \IPS\Dispatcher\Controller
}
/**
* Third step of conversion
* Choose which applications to convert
*
* @return void
* @param array $data Wizard data from previous steps
* @return \IPS\Helpers\Form|array
*/
public function _stepThree( $data )
public function _chooseApplications( $data )
{
$form = new \IPS\Helpers\Form( 'convert_database_details', 'start_conversion' );
$classname = 'IPS\\convert\\Software\\' . ucwords( $data['convert_start_application'] ) . '\\' . ucwords( $data['convert_start_source_software'] );
/* Figure out what converter libraries we have for the selected application */
$software = $data['convert_start_source_software'];
$applications = array( 'core' => \IPS\Member::loggedIn()->language()->addToStack( 'core_node_select' ) );
if ( !is_null( $classname::parents() ) AND is_array( $classname::parents() ) )
foreach( \IPS\convert\Library::libraries() AS $key => $class )
{
/* Do we have any available parents? */
$where = array();
$options = array();
foreach( $classname::parents() AS $app => $sw )
/* Check app is installed and enabled */
if( !\IPS\Application::appIsEnabled( $key ) )
{
foreach( $sw AS $key )
{
$where[] = array( 'sw=? AND app_key=? AND finished=?', $app, $key, 0 );
}
}
try
{
foreach( \IPS\Db::i()->select( '*', 'convert_apps', $where ) AS $row )
{
$name = explode( '_', $row['name'] );
$parentClassname = 'IPS\\convert\\Software\\' . ucwords( $name[0] ) . '\\' . ucwords( $name[1] );
$options[$row['app_id']] = $parentClassname::softwareName();
}
}
catch( \Exception $e ) {}
/* If we have any possible parents, lets add a select menu */
if ( count( $options ) > 0 )
{
$form->addHeader( 'convert_start_select_parent' );
$form->add( new \IPS\Helpers\Form\Select( 'convert_start_parent', NULL, FALSE, array( 'options' => $options ) ) );
$form->addHeader( 'convert_start_or_database' );
continue;
}
/* This software class requires a parent, but we don't have any that have been converted */
elseif( $classname::requiresParent() )
foreach( \IPS\convert\Software::software()[ $key ] AS $softwareKey => $softwareClass )
{
\IPS\Output::i()->error( 'convert_no_applicable_parent', '4V346/1' );
if( $softwareKey == $software )
{
$applications[ $key ] = \IPS\Member::loggedIn()->language()->addToStack( $key . '_node_select' );
}
elseif( isset( $softwareClass::parents()['core'] ) )
{
if( \in_array( $software, $softwareClass::parents()['core'] ) )
{
$applications[ $key . '_' . $softwareKey ] = \IPS\Member::loggedIn()->language()->get( $key . '_node_select' ) . ' - ' . $softwareClass::softwareName();
}
}
}
}
if ( $classname::getPreConversionInformation() !== NULL )
/* Don't select the 'extra' converters */
$preSelected = $applications;
array_walk( $preSelected, function( $value, $key ) use( &$preSelected ) {
if( mb_stristr( $key, '_' ) )
{
unset( $preSelected[ $key ] );
}
});
$form = new \IPS\Helpers\Form( 'convert_application', 'enter_database_details' );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'choose_what_to_convert', array_keys( $preSelected ), FALSE, array( 'options' => $applications, 'disabled' => array( 'core' ) ) ) );
if ( $values = $form->values() )
{
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( $classname::getPreConversionInformation() ), '', FALSE );
/* Core is required, and it was disabled on the form, so make sure it's in the array just to prevent people breaking stuff intentionally */
if( !\in_array( 'core', $values['choose_what_to_convert'] ) )
{
$values['choose_what_to_convert'][] = 'core';
}
return $values;
}
return $form;
}
/**
* Allow the admin to supply the needed details
*
* @param array $data Wizard data from previous steps
* @return \IPS\Helpers\Form|array
*/
public function _setDatabaseDetails( $data )
{
$form = new \IPS\Helpers\Form( 'convert_database_details', 'enter_conversion_details' );
$usePrefix = NULL;
foreach( $data['choose_what_to_convert'] as $application )
{
if( mb_stristr( $application, '_' ) )
{
$converter = explode( '_', $application );
$classname = \IPS\convert\Software::software()[ $converter[0] ] [ $converter[1] ];
}
else
{
$classname = \IPS\convert\Software::software()[ $application ] [ mb_strtolower( $data['convert_start_source_software'] ) ];
}
if ( $classname::getPreConversionInformation() !== NULL )
{
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack( $classname::getPreConversionInformation() ), '', FALSE );
}
if( $application == 'core' )
{
$usePrefix = $classname::usesPrefix();
}
}
$form->add( new \IPS\Helpers\Form\Text( 'convert_start_database_host', NULL, FALSE ) );
@@ -187,7 +198,7 @@ class _create extends \IPS\Dispatcher\Controller
$form->add( new \IPS\Helpers\Form\Password( 'convert_start_database_pass', NULL, FALSE ) );
/* If the source software doesn't use a prefix, then we should not ask for one to avoid confusion. */
if ( $classname::usesPrefix() )
if ( $usePrefix )
{
$form->add( new \IPS\Helpers\Form\Text( 'convert_start_database_prefix', NULL, FALSE ) );
}
@@ -196,32 +207,11 @@ class _create extends \IPS\Dispatcher\Controller
if ( $values = $form->values() )
{
/* Did we select a parent but no DB details? */
if (
isset( $values['convert_start_parent'] ) AND
( !isset( $values['convert_start_database_host'] ) || empty($values['convert_start_database_host']) ) AND
( !isset( $values['convert_start_database_name'] ) || empty($values['convert_start_database_name']) ) AND
( !isset( $values['convert_start_database_user'] ) || empty($values['convert_start_database_user']) ) AND
( !isset( $values['convert_start_database_pass'] ) || empty($values['convert_start_database_pass']) ) AND
( !isset( $values['convert_start_database_prefix'] ) || empty($values['convert_start_database_prefix']) ) AND
( !isset( $values['convert_start_database_charset'] ) || empty($values['convert_start_database_charset']) )
)
{
/* We did - load the parent app and store the DB details for that */
$parentApp = \IPS\convert\App::load( $values['convert_start_parent'] );
$values['convert_start_database_host'] = $parentApp->db_host;
$values['convert_start_database_port'] = $parentApp->db_port;
$values['convert_start_database_name'] = $parentApp->db_db;
$values['convert_start_database_user'] = $parentApp->db_user;
$values['convert_start_database_pass'] = $parentApp->db_pass;
$values['convert_start_database_prefix'] = $parentApp->db_prefix;
$values['convert_start_database_charset'] = $parentApp->db_charset;
}
/* Start the app object */
/* Start the (core) app object */
$app = new \IPS\convert\App;
$app->sw = $data['convert_start_application'];
$app->sw = 'core';
$app->app_key = $data['convert_start_source_software'];
$app->parent = 0;
try
{
@@ -246,14 +236,15 @@ class _create extends \IPS\Dispatcher\Controller
}
$db = \IPS\Db::i( 'convertertest', $connectionSettings );
$db->checkConnection();
/* Now test the charset */
if ( $values['convert_start_database_charset'] AND !in_array( $values['convert_start_database_charset'], array( 'utf8', 'utf8mb4' ) ) )
if ( $values['convert_start_database_charset'] AND !\in_array( $values['convert_start_database_charset'], array( 'utf8', 'utf8mb4' ) ) )
{
/* Get all db charsets and make sure the one we entered is valid */
$charsets = \IPS\convert\Software::getDatabaseCharsets( $db );
if ( !in_array( mb_strtolower( $values['convert_start_database_charset'] ), $charsets ) )
if ( !\in_array( mb_strtolower( $values['convert_start_database_charset'] ), $charsets ) )
{
throw new \InvalidArgumentException( 'invalid_charset' );
}
@@ -261,30 +252,54 @@ class _create extends \IPS\Dispatcher\Controller
$db->set_charset( $values['convert_start_database_charset'] );
}
/* Try to verify that the db prefix is correct */
$appClassName = $app->getSource( FALSE, FALSE );
$canConvert = $appClassName::canConvert();
$appClass = new $appClassName( $app, FALSE );
$appClass->db = $db;
$appsToCheck = array( $app );
$testAgainst = array_shift( $canConvert );
foreach( $data['choose_what_to_convert'] as $application )
{
$childApp = new \IPS\convert\App;
/* We specifically use CountRows to test this, canConvert() may contain references to tables that do not exist..
* ..This is for the row count look up.
*/
try
{
$appClass->countRows( $testAgainst['table'] );
if( mb_stristr( $application, '_' ) )
{
$converter = explode( '_', $application );
$childApp->sw = $converter[0];
$childApp->app_key = $converter[1];
}
else
{
$childApp->sw = $application;
$childApp->app_key = $data['convert_start_source_software'];
}
$appsToCheck[] = $childApp;
}
catch( \UnderflowException $e )
foreach( $appsToCheck as $appToCheck )
{
throw new \InvalidArgumentException( 'invalid_prefix' );
}
/* Can't find the table */
catch( \IPS\convert\Exception $e )
{
$form->error = $e->getMessage();
return $form;
/* Try to verify that the db prefix is correct */
$appClassName = $appToCheck->getSource( FALSE, FALSE );
$canConvert = $appClassName::canConvert();
$appClass = new $appClassName( $appToCheck, FALSE );
$appClass->db = $db;
$testAgainst = array_shift( $canConvert );
/* We specifically use CountRows to test this, canConvert() may contain references to tables that do not exist..
* ..This is for the row count look up.
*/
try
{
$appClass->countRows( $testAgainst['table'] );
}
catch( \UnderflowException $e )
{
throw new \InvalidArgumentException( 'invalid_prefix' );
}
/* Can't find the table */
catch( \IPS\convert\Exception $e )
{
$form->error = $e->getMessage();
return $form;
}
}
}
catch( \InvalidArgumentException $e )
@@ -309,26 +324,7 @@ class _create extends \IPS\Dispatcher\Controller
$form->error = \IPS\Member::loggedIn()->language()->addToStack('convert_cant_connect_db');
return $form;
}
try
{
$appExists = \IPS\convert\App::load( $data['convert_start_application'] . '_' . $data['convert_start_source_software'], 'name' );
$app->name = $data['convert_start_application'] . '_' . $data['convert_start_source_software'] . '_' . time();
}
catch( \OutOfRangeException $e )
{
$app->name = $data['convert_start_application'] . '_' . $data['convert_start_source_software'];
}
if ( isset( $values['convert_start_parent'] ) )
{
$app->parent = $values['convert_start_parent'];
}
else
{
$app->parent = 0;
}
$app->db_host = $values['convert_start_database_host'];
$app->db_port = $values['convert_start_database_port'];
$app->db_db = $values['convert_start_database_name'];
@@ -357,9 +353,203 @@ class _create extends \IPS\Dispatcher\Controller
$app->save();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&id={$app->app_id}" ) );
/* Now save the child applications */
foreach( $data['choose_what_to_convert'] as $application )
{
if( $application == 'core' )
{
continue;
}
$childApp = clone $app;
$childApp->parent = $app->app_id;
if( mb_stristr( $application, '_' ) )
{
$converter = explode( '_', $application );
$childApp->sw = $converter[0];
$childApp->app_key = $converter[1];
}
else
{
$childApp->sw = $application;
}
$childApp->save();
}
$values['_app_parent'] = $app->app_id;
return $values;
}
return $form;
}
/**
* Allow the admin to supply the needed details
*
* @param array $data Wizard data from previous steps
* @return \IPS\Helpers\Form
*/
public function _setConversionDetails( $data )
{
/* We may be coming here from a previously incomplete conversion setup...detect and set the parent app ID appropriately */
if( ( !isset( $data['_app_parent'] ) OR !$data['_app_parent'] ) AND isset( \IPS\Request::i()->id ) )
{
$data['_app_parent'] = \IPS\Request::i()->id;
}
$form = new \IPS\Helpers\Form( 'convert_other_details', 'start_conversion' );
try
{
$parentApplication = \IPS\convert\App::load( $data['_app_parent'] );
}
catch( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'conversion_app_not_found', '3V391/1', 404, '' );
}
$this->_addConversionApplication( $parentApplication, $form );
foreach( $parentApplication->children() as $childApplication )
{
$this->_addConversionApplication( $childApplication, $form );
}
if( $values = $form->values() )
{
/* We know the core/parent app is fine, but any child apps that don't have 'more_info' should be deleted as they cannot be converted */
foreach( $parentApplication->children() as $childApplication )
{
if( !\count( $childApplication->_session['more_info'] ) )
{
$childApplication->delete();
}
}
/* We have already saved our custom configuration, so now we can head off to the races */
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&id={$data['_app_parent']}" ) );
}
return $form;
}
/**
* Add form elements for the specified application to the form
*
* @param \IPS\convert\App $app Conversion application
* @param \IPS\Helpers\Form $form Form object
* @return void
*/
protected function _addConversionApplication( $app, &$form )
{
$softwareClass = $app->getSource();
$libraryClass = $softwareClass->getLibrary();
/* Add a nice little header to the form */
$form->addHeader( \IPS\Member::loggedIn()->language()->addToStack( 'converting_x_to_x', FALSE, array( 'sprintf' => array( $softwareClass::softwareName(), \IPS\Application::load( $app->sw )->_title ) ) ) );
/* Now fetch the details about this application we are converting so we can build the appropriate form elements */
try
{
$menuRows = \IPS\convert\modules\admin\manage\convert::getMenuRows( $softwareClass, TRUE, TRUE );
}
catch( \IPS\Convert\Exception $e )
{
$form->addMessage( 'nothing_convertable_found', 'ipsMessage ipsMessage_warning', TRUE );
return;
}
/* Does this converter have settings that can be converted? */
if ( $softwareClass::canConvertSettings() !== FALSE )
{
$settings = array();
foreach( $softwareClass->settingsMapList() as $key => $setting )
{
$value = $setting['value'];
if ( \is_bool( ( $setting['value'] ) ) )
{
$value = $setting['value'] === TRUE ? 'On' : 'Off';
}
$settings[ $setting['our_key'] ] = new \IPS\Helpers\Form\Checkbox( $setting['our_key'], TRUE, FALSE, array( 'label' => $value ), NULL, NULL, NULL, $setting['our_key'] );
}
/* Add a nice little header to the form */
$form->addHeader( 'convert_settings' );
$form->add( new \IPS\Helpers\Form\YesNo( 'convert_settings', TRUE, TRUE, array( 'togglesOn' => array_keys( $settings ) ) ) );
foreach( $settings as $settingFormField )
{
$form->add( $settingFormField );
}
/* Did we just submit? If so save our configuration */
if( $values = $form->values() )
{
$values = array_filter( $values, function( $key ) use ( $settings ) { return $key == 'convert_settings' OR \in_array( $key, array_keys( $settings ) ); }, ARRAY_FILTER_USE_KEY );
$app->saveMoreInfo( 'convertSettings', $values );
}
}
/* Now build the menu rows... */
foreach( $menuRows as $row )
{
/* Determine if this step has any extra configuration questions */
$formElements = array();
if ( \in_array( $row['step_method'], $softwareClass::checkConf() ) )
{
$getMoreInfo = $softwareClass->getMoreInfo( $row['step_method'] );
if ( \is_array( $getMoreInfo ) AND \count( $getMoreInfo ) )
{
/* Show the 'more info' fields */
foreach( $getMoreInfo as $key => $input )
{
$fieldClass = $input['field_class'];
$formElements[ $key ] = new $fieldClass( $key, $input['field_default'], $input['field_required'], $input['field_extra'], isset( $input['field_validation'] ) ? $input['field_validation'] : NULL, NULL, NULL, $key );
if ( $input['field_hint'] !== NULL )
{
\IPS\Member::loggedIn()->language()->words[ $key . '_desc' ] = $input['field_hint'];
}
}
}
}
$form->addHeader( $row['step_title'] );
$form->add( new \IPS\Helpers\Form\YesNo( $row['step_title'], TRUE, TRUE, array( 'togglesOn' => array_keys( $formElements ) ) ) );
if( \count( $formElements ) )
{
foreach( $formElements as $key => $element )
{
$form->add( $element );
}
}
/* Did we just submit? If so save our configuration */
if( $values = $form->values() )
{
$values = array_filter( $values, function( $key ) use ( $formElements, $row ) { return $key == $row['step_title'] OR \in_array( $key, array_keys( $formElements ) ); }, ARRAY_FILTER_USE_KEY );
$app->saveMoreInfo( $row['step_method'], $values );
}
}
/* If there was nothing to convert, show an appropriate message */
if( $softwareClass::canConvertSettings() === FALSE AND !\count( $menuRows ) )
{
$form->addMessage( 'nothing_convertable_found', 'ipsMessage ipsMessage_warning', TRUE );
}
/* If there is a message to show, go ahead and show it */
if( $postConversionMessage = $libraryClass->getPostConversionInformation() )
{
$form->addMessage( $postConversionMessage, 'ipsMessage ipsMessage_info', FALSE );
}
}
}
Whitespace-only changes.
@@ -1,20 +1,33 @@
<?php
/**
* @brief Converter: Manage conversions
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage convert
* @since 21 Jan 2015
*/
namespace IPS\convert\modules\admin\manage;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* manage
* Converter overview
*/
class _manage extends \IPS\Dispatcher\Controller
{
{
/**
* @brief Has been CSRF-protected
*/
public static $csrfProtected = TRUE;
/**
* Execute
*
@@ -22,6 +35,11 @@ class _manage extends \IPS\Dispatcher\Controller
*/
public function execute()
{
if ( \IPS\DEMO_MODE === TRUE )
{
\IPS\Output::i()->error( 'demo_mode_function_blocked', '2V407/1', 403, '' );
}
\IPS\Output::i()->responsive = FALSE;
\IPS\Dispatcher::i()->checkAcpPermission( 'manage_manage' );
parent::execute();
@@ -41,66 +59,75 @@ class _manage extends \IPS\Dispatcher\Controller
}
/* Remind the user that they must configure permissions after the conversion is complete */
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( 'convert_configure_permissions', 'warning' );
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( 'convert_configure_permissions', 'info' );
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'convert_apps', \IPS\Http\Url::internal( 'app=convert&module=manage&controller=manage' ) );
$table = new \IPS\Helpers\Table\Db( 'convert_apps', \IPS\Http\Url::internal( 'app=convert&module=manage&controller=manage' ), array( 'parent=?', 0 ) );
$table->langPrefix = 'convert_';
$table->include = array( 'sw', 'app_key', 'name', 'parent', 'finished' );
$table->include = array( 'app_key', 'sw', 'start_date', 'finished' );
$table->parsers = array(
'sw' => function( $val, $row )
{
switch( $val )
$translate = function( $app )
{
case 'board':
$val = 'forums';
break;
case 'ccs':
$val = 'cms';
break;
switch( $app )
{
case 'board':
$app = 'forums';
break;
case 'ccs':
$app = 'cms';
break;
}
return $app;
};
/* The main row will always be the core application (except for legacy conversions) */
$applications = array( \IPS\Application::load( $translate( $val ) )->_title );
foreach( \IPS\Db::i()->select( '*', 'convert_apps', array( 'parent=?', $row['app_id'] ) ) as $software )
{
/* Translate the software key, if required */
$software['sw'] = $translate( $software['sw'] );
try
{
if ( \IPS\CONVERTERS_DEV_UI === TRUE )
{
$continueUrl = \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&id={$software['app_id']}&continue=1" )->csrf();
$applications[] = "<a href='{$continueUrl}'>" . \IPS\Application::load( $software['sw'] )->_title . "</a>";
continue;
}
$applications[] = \IPS\Application::load( $software['sw'] )->_title;
}
catch( \OutOfRangeException $e )
{
$applications[] = mb_ucfirst( $software );
}
}
try
{
return \IPS\Application::load( $val )->_title;
}
catch( \OutOfRangeException $e )
{
return ucwords( $val );
}
return \IPS\Member::loggedIn()->language()->formatList( $applications );
},
'app_key' => function( $val, $row )
{
$app = \IPS\convert\App::constructFromData( $row );
try
{
$classname = get_class( $app->getSource() );
$classname = \get_class( $app->getSource() );
return $classname::softwareName();
}
catch( \Exception $ex )
{
return $app->name;
return $val;
}
},
'parent' => function( $val, $row )
'start_date' => function( $val )
{
try
{
if ( ! $val )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'convert_no_parent' );
}
else
{
return \IPS\convert\App::load( $val )->name;
}
}
catch( \OutOfRangeException $e )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'convert_cannot_parent' );
}
return (string) \IPS\DateTime::ts( $val );
},
'finished' => function( $val )
{
@@ -146,43 +173,35 @@ class _manage extends \IPS\Dispatcher\Controller
\IPS\Application::load( $row['sw'] );
/* Try to load the app class - if exception, the converter no longer exists */
get_class( \IPS\convert\App::constructFromData( $row )->getSource() );
\IPS\convert\App::constructFromData( $row )->getSource();
$return = array();
if ( !$row['finished'] )
if ( !$row['finished'] OR \IPS\CONVERTERS_DEV_UI === TRUE )
{
$return[] = array(
'icon' => 'chevron-circle-right',
'title' => 'continue',
'link' => \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&id={$row['app_id']}" )
'link' => \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&id={$row['app_id']}&continue=1" )->csrf()
);
if ( $row['parent'] == 0 )
if( \IPS\CONVERTERS_DEV_UI === TRUE AND !$row['finished'] )
{
$return[] = array(
'icon' => 'check',
'title' => 'finish',
'link' => \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=finish&id={$row['app_id']}" ),
'link' => \IPS\Http\Url::internal( "app=convert&module=manage&controller=convert&do=finish&id={$row['app_id']}" )->csrf(),
'data' => array(
'confirm' => '', 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->get( 'convert_finish_confirm' )
)
);
}
$return[] = array(
'icon' => 'pencil',
'title' => 'edit',
'link' => \IPS\Http\Url::internal( "app=convert&module=manage&controller=manage&do=edit&id={$row['app_id']}" ),
);
$return[] = array(
'icon' => 'times-circle',
'title' => 'delete',
'link' => \IPS\Http\Url::internal( "app=convert&module=manage&controller=manage&do=delete&id={$row['app_id']}" ),
'data' => array( 'delete' => '' ),
);
}
return $return;
@@ -217,23 +236,45 @@ class _manage extends \IPS\Dispatcher\Controller
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'classname', NULL, TRUE ) );
$apps = array();
foreach( new \DirectoryIterator( \IPS\ROOT_PATH . '/applications/convert/sources/Library' ) as $file )
{
if ( !$file->isDir() AND !$file->isDot() AND ( \substr( $file->getFilename(), -4 ) == '.php' ) AND $file->getFilename() != 'Board.php' AND $file->getFilename() != 'Ccs.php' )
{
$apps[ str_replace( '.php', '', $file->getFilename() ) ] = str_replace( '.php', '', $file->getFilename() );
}
}
$form->add( new \IPS\Helpers\Form\Select( 'library', NULL, FALSE, array( 'options' => $apps ) ) );
if ( $values = $form->values() )
{
$default = file_get_contents( \IPS\ROOT_PATH . '/applications/convert/data/defaults/Software.txt' );
$code = str_replace( array( '<#CLASS#>', '<#APP#>', '<#CLASS_LOWER#>' ), array( $values['classname'], $values['library'], mb_strtolower( $values['classname'] ) ), $default );
\file_put_contents( \IPS\ROOT_PATH . '/applications/convert/sources/Software/' . $values['library'] . '/' . $values['classname'] . '.php', $code );
/* Get the default code */
$default = file_get_contents( \IPS\ROOT_PATH . '/applications/convert/data/defaults/Software.txt' );
/* Explode the entered class name to figure out the file path and namespace */
$exploded = explode( '\\', ltrim( $values['classname'], '\\' ) );
/* Copy the exploded array to generate the path */
$copied = $exploded;
/* Shift off IPS from the namespace */
array_shift( $copied );
/* Get our application key */
$application = array_shift( $copied );
/* Generate our path */
$filepath = \IPS\ROOT_PATH . "/applications/{$application}/sources/" . implode( '/', $copied ) . ".php";
/* Now figure out our namespace, class, and default code with replacements */
$classname = array_pop( $exploded );
$namespace = implode( '\\', $exploded );
$code = str_replace( array( '<#NAMESPACE#>', '<#CLASS#>', '<#CLASS_LOWER#>' ), array( $namespace, $classname, mb_strtolower( $classname ) ), $default );
/* Check if we need to create a folder */
if( \count( $copied ) > 1 )
{
$folder = \IPS\ROOT_PATH . "/applications/{$application}/sources/" . $copied[0];
if( !file_exists( $folder ) )
{
@mkdir( $folder );
@chmod( $folder, \IPS\FOLDER_PERMISSION_NO_WRITE );
}
}
/* Generate the file */
\file_put_contents( $filepath, $code );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=manage" ), 'saved' );
}
@@ -259,9 +300,42 @@ class _manage extends \IPS\Dispatcher\Controller
if ( $values = $form->values() )
{
$default = file_get_contents( \IPS\ROOT_PATH . '/applications/convert/data/defaults/Library.txt' );
$code = str_replace( array( '<#CLASS#>', '<#CLASS_LOWER#>' ), array( $values['classname'], mb_strtolower( $values['classname'] ) ), $default );
\file_put_contents( \IPS\ROOT_PATH . '/applications/convert/sources/Library/' . $values['classname'] . '.php', $code );
/* Get the default code */
$default = file_get_contents( \IPS\ROOT_PATH . '/applications/convert/data/defaults/Library.txt' );
/* Explode the entered class name to figure out the file path and namespace */
$exploded = explode( '\\', ltrim( $values['classname'], '\\' ) );
/* Copy the exploded array to generate the path */
$copied = $exploded;
/* Shift off IPS from the namespace */
array_shift( $copied );
/* Get our application key */
$application = array_shift( $copied );
/* Generate our path */
$filepath = \IPS\ROOT_PATH . "/applications/{$application}/sources/" . implode( '/', $copied ) . ".php";
/* Now figure out our namespace, class, and default code with replacements */
$classname = array_pop( $exploded );
$namespace = implode( '\\', $exploded );
$code = str_replace( array( '<#NAMESPACE#>', '<#CLASS#>', '<#CLASS_LOWER#>' ), array( $namespace, $classname, mb_strtolower( $classname ) ), $default );
/* Check if we need to create a folder */
if( \count( $copied ) > 1 )
{
$folder = \IPS\ROOT_PATH . "/applications/{$application}/sources/" . $copied[0];
if( !file_exists( $folder ) )
{
@mkdir( $folder );
@chmod( $folder, \IPS\FOLDER_PERMISSION_NO_WRITE );
}
}
/* Generate the file */
\file_put_contents( $filepath, $code );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=convert&module=manage&controller=manage' ), 'saved' );
}
@@ -332,12 +406,12 @@ class _manage extends \IPS\Dispatcher\Controller
$db = \IPS\Db::i( 'convertertest', $connectionSettings );
/* Now test the charset */
if ( $values['db_charset'] AND !in_array( $values['db_charset'], array( 'utf8', 'utf8mb4' ) ) )
if ( $values['db_charset'] AND !\in_array( $values['db_charset'], array( 'utf8', 'utf8mb4' ) ) )
{
/* Get all db charsets and make sure the one we entered is valid */
$charsets = \IPS\convert\Software::getDatabaseCharsets( $db );
if ( !in_array( mb_strtolower( $values['db_charset'] ), $charsets ) )
if ( !\in_array( mb_strtolower( $values['db_charset'] ), $charsets ) )
{
throw new \InvalidArgumentException( 'invalid_charset' );
}
@@ -383,50 +457,29 @@ class _manage extends \IPS\Dispatcher\Controller
}
$app->db_host = $values['db_host'];
$app->db_port = $values['db_port'];
$app->db_port = $values['db_port'];
$app->db_db = $values['db_db'];
$app->db_user = $values['db_user'];
$app->db_pass = $values['db_pass'];
$app->db_prefix = $values['db_prefix'];
$app->db_charset = $values['db_charset'];
$app->save();
foreach( $app->children() as $child )
{
$child->db_host = $values['db_host'];
$child->db_port = $values['db_port'];
$child->db_db = $values['db_db'];
$child->db_user = $values['db_user'];
$child->db_pass = $values['db_pass'];
$child->db_prefix = $values['db_prefix'];
$child->db_charset = $values['db_charset'];
$child->save();
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=convert&module=manage&controller=manage" ), 'saved' );
}
\IPS\Output::i()->output = $form;
}
/**
* Delete a Conversion
*
* @return void
*/
public function delete()
{
if ( !isset( \IPS\Request::i()->id ) )
{
\IPS\Output::i()->error( 'no_conversion_app', '2V100/5' );
}
try
{
$app = \IPS\convert\App::load( \IPS\Request::i()->id );
/* Check for children */
try
{
\IPS\DB::i()->select( '*', 'convert_apps', array( 'parent=?', $app->app_id ) )->first();
\IPS\Output::i()->error( 'conversion_app_has_children', '2V100/6' );
}
catch( \UnderflowException $ex ) { }
$app->delete();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=convert&module=manage&controller=manage' ), 'conversion_deleted' );
}
catch( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'conversion_app_not_found', '2V100/6', 404 );
}
}
}