<?php
/**
* @brief Template Class
* @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
* @since 9 Sept 2013
*/
namespace IPSUtf8\Output\Browser;
/**
* Template Class
*/
class Template
{
public static function wrapper( $title="IPS Converter", $content="", $isError=false )
{
$iniTime = @ini_get('max_execution_time');
$execTime = ( is_numeric( $iniTime ) ) ? $iniTime : 30;
$url = \IPSUtf8\Output\Browser::$url;
$title = ( empty( $title ) ) ? 'IPS Converter' : $title;
$active = array( 'convert' => '', 'tools' => '', 'archive' => '' );
$hasArchive = \IPSUtf8\Session::i()->has_archive;
$controller = preg_replace( "/[^a-zA-Z0-9]/", '', empty( \IPSUtf8\Request::i()->controller ) ? 'browser' : \IPSUtf8\Request::i()->controller );
$version = \IPSUtf8\Convert::VERSION_ID;
$copyrightDate = date('Y');
if ( $controller == 'browser' )
{
$active['convert'] = 'active';
}
else if ( $controller === 'archive' )
{
$active['archive'] = 'active';
}
else
{
$active['tools'] = 'active';
}
$html = <<<EOFHTML
<!DOCTYPE html>
<html>
<head>
<title>{$title}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="css/jumbotron.css" rel="stylesheet" media="screen">
</head>
<body data-url="{$url}">
<div class="container">
<div class="header">
EOFHTML;
if ( ! $isError )
{
$html .= <<<EOFHTML
<ul class="nav nav-pills pull-right">
<li class="{$active['convert']}"><a href="{url="?controller=browser"}">Convert</a></li>
EOFHTML;
if ( $hasArchive )
{
$html .= <<<EOFHTML
<li class="{$active['archive']}"><a href="{url="?controller=archive"}">Archive</a></li>
EOFHTML;
}
$html .= <<<EOFHTML
<li class="{$active['tools']}"><a href="{url="?controller=tools"}">Tools</a></li>
</ul>
EOFHTML;
}
$html .= <<<EOFHTML
<h3 class="text-muted">IPS UTF8 Converter</h3>
</div>
{$content}
<div class="footer">
<p>UTF8 Converter v{$version} © {$copyrightDate} Invision Power Services, Inc.</p>
</div>
</div>
<script type="text/javascript">
var ipsSettings = { maxExecTime: {$execTime}, 'controller': '{$controller}' };
</script>
<script src="js/jquery.min.js"></script>
<script src="js/app.js?v=1"></script>
<script src="js/bootstrap.min.js"></script>
</body>
</html>
<div class="modal fade" id="confirmModal" data-url="" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Confirm</h4>
</div>
<div class="modal-body">
<p id="confirmModalText">Please confirm this action</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" data-modal="submit">Confirm</button>
</div>
</div>
</div>
</div>
EOFHTML;
return static::parse( $html );
}
/**
* Tools page
*/
public static function tools( $isUtf8=false, $msg )
{
$html = <<<EOFHTML
<div class="jumbotron">
<h1>Tools</h1>
EOFHTML;
if ( $msg !== null )
{
$html .= <<<EOFHTML
<p>
<div class='alert alert-info'>{$msg}</div>
</p>
<p> </p>
EOFHTML;
}
if ( ! isset( \IPSUtf8\Request::i()->do ) )
{
if ( $isUtf8 === true )
{
$html .= <<<EOFHTML
<p class="text-info">The database is set to UTF-8.</p>
<p><a class="btn btn-lg btn-info" href="{url="?controller=tools&do=collation"}">Check and fix UTF-8 collation</a></p>
EOFHTML;
}
else
{
$html .= <<<EOFHTML
<p class="text-info">Please <a href="{url="?controller=browser"}">convert to UTF-8</a> first.</p>
EOFHTML;
}
}
$html .= <<<EOFHTML
</div>
EOFHTML;
$code = implode( "<br />", \IPSUtf8\Convert::i()->getDebugString() );
$html .= <<<EOFHTML
<div class="jumbotron">
<h1>Info</h1>
<code>
{$code}
</code>
</div>
EOFHTML;
return static::parse( $html );
}
/**
* Error page
*/
public static function error( $msg )
{
$html = <<<EOFHTML
<div class="jumbotron">
<h1>Error</h1>
<p class="lead">{$msg}</p>
</div>
EOFHTML;
return static::parse( $html );
}
/**
* Error page explaining about database being Invision Community already
*/
public static function errorAlreadyIPS4()
{
$html = <<<EOFHTML
<p style="text-align:left">
The target database is already running Invision Community. There is no conversion required and the UTF8 converter is locked.
<br>To override this and unlock the UTF8 converter, please create a file called "constants.php" and upload it to your /admin/convertutf8/ directory with the following contents:
</p>
<p><textarea class="form-control" style="height:90px;"><?php
define( 'BYPASS_SAFETY_LOCK', true );
</textarea>
</p>
<p class="text-danger">Please remember to remove the constants.php file when you are finished!</p>
EOFHTML;
return static::parse( $html );
}
/**
* Welcome page
*/
public static function welcome( $isUtf8=false, $status, $percent=false, $controller='browser' )
{
$convertClass = ( $controller === 'browser' ) ? '\IPSUtf8\Convert' : '\IPSUtf8\Convert\Archive';
$origUtf8Charset = \IPSUtf8\Db::i('utf8')->getCharset();
$canUseMb4 = ( version_compare( \IPSUtf8\Db::i()->server_info, '5.5.3', '>=' ) AND \IPSUtf8\Db::i('utf8')->set_charset('utf8mb4') !== FALSE );
\IPSUtf8\Db::i('utf8')->set_charset( $origUtf8Charset );
$badCollations = $convertClass::i()->getNonUtf8CollationTables();
$badTablesCnt = count( $convertClass::i()->getNonUtf8Tables() );
$path = ROOT_PATH;
$dbCharSetIsCorrect = TRUE;
$isUtf8mb4 = ( $isUtf8 === TRUE AND \IPSUtf8\Convert::i()->database_charset == 'utf8mb4' );
if ( file_exists( ROOT_PATH . '/conf_global.php' ) )
{
require_once( ROOT_PATH . '/conf_global.php' );
if ( !isset( $INFO['sql_charset'] ) OR ! in_array( $INFO['sql_charset'], array( 'utf8', 'utf8mb4' ) ) )
{
$dbCharSetIsCorrect = FALSE;
}
}
if ( isset( \IPSUtf8\Request::i()->convert_anyway ) AND \IPSUtf8\Request::i()->convert_anyway == 1 )
{
$badTablesCnt = \IPSUtf8\Session::i()->json['tableCount'];
}
$html = <<<EOFHTML
<div class="jumbotron">
<h1>Welcome</h1>
EOFHTML;
if ( $controller === 'archive' )
{
$html .= <<<EOFHTML
<p class="text-info">The converter has detected that you have an archive table in a different database.</p>
EOFHTML;
}
if ( $status === 'processing' )
{
$html .= <<<EOFHTML
<p class="text-warning">Conversion in progress ({$percent}% complete).</p>
EOFHTML;
}
else if ( $isUtf8 !== true )
{
$html .= <<<EOFHTML
<p class="lead">You have {$badTablesCnt} table(s) in this database that are not UTF-8.\nYou must convert these tables to UTF-8 before you can proceed with the upgrade.</p>
EOFHTML;
}
else if ( $isUtf8 === true )
{
if ( count( $badCollations ) )
{
$cnt = count( $badCollations );
$html .= <<<EOFHTML
<p class="text-info">
The database is set to UTF-8, however {$cnt} table(s) have incorrect collations and need fixing.
<p><a class="btn btn-lg btn-info" href="{url="?controller=tools&do=collation"}">Fix UTF-8 collations</a></p>
</p>
EOFHTML;
}
else
{
/* If the charset stored for the gb_char_set setting is not utf-8, then we'll hit an endless loop unless we fix it here */
\IPSUtf8\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'utf-8' ), array( 'conf_key=?', 'gb_char_set' ) );
$html .= <<<EOFHTML
<p class="text-info">
The database tables are UTF-8, collations are correct and there is nothing to convert. You can <strong><a href="../upgrade">proceed with the upgrade</strong>.
</p>
EOFHTML;
}
}
if ( ! $isUtf8 and $canUseMb4 )
{
$buttonLabel = ( $isUtf8 ) ? "Convert anyway using 4-Byte UTF-8 (utf8mb4)" : "Start using 4-Byte UTF-8 (utf8mb4)";
$html .= <<<EOFHTML
<p>
<a data-status="Start" class="btn btn-lg btn-info" href="{url="?controller={$controller}&do=process&use_utf8mb4=1"}">{$buttonLabel}</a>
<br />
<div class='small'>
<small>
Some non-common symbols (such as historical scripts, music symbols and Emoji) require more space in the database to be stored. If you choose to convert using 4-Byte UTF-8, these characters will be able to be used, but the database will use more disk space.
</small>
</div>
</p>
EOFHTML;
}
if ( ! $isUtf8 )
{
$label = 'Start using UTF-8 (utf8)';
$selector = 'btn-success';
$more = <<<EOFHTML
<br />
<div class='small'>
<small>
Choose this if you don't wish to store non-common symbols (such as historical scripts, music symbols and Emoji) and wish to use less disk space.
</small>
</div>
<br>
<div class='small'>
<small>
Please note that you should turn your community offline before starting any conversion on a live site.
</small>
</div>
<br>
<div class='small'>
<small>
If you have a large community, or are experiencing browser timeouts during conversion, you may wish to consider running the conversion via the Command Line.<br>
<code>
cd {$path}/admin/convertutf8
php cli.php
</code>
</small>
</div>
EOFHTML;
}
if ( $status === 'processing' )
{
$label = 'Continue Conversion';
$selector = 'btn-warning';
$more = '';
}
else if ( ! $isUtf8 )
{
$label = "Convert as UTF-8 (utf8)";
$selector = 'btn-info';
}
if ( $label and $selector )
{
$html .= <<<EOFHTML
<p>
<a data-status="{$status}" class="btn btn-lg {$selector}" href="{url="?controller={$controller}&do=process"}">{$label}</a>
{$more}
</p>
EOFHTML;
}
if ( $status === 'processing' )
{
$html .= <<<EOFHTML
<p><small><a data-status="reset" data-confirm="true" class='text-danger' href="{url="?controller={$controller}&do=reset"}">Reset conversion and restart</a></small></p>
EOFHTML;
}
$html .= <<<EOFHTML
</div>
EOFHTML;
return static::parse( $html );
}
/**
* Process page
*/
public static function process( $status, $percent, $msg )
{
$utfPrefix = \IPSUtf8\Db::i('utf8')->prefix . \IPSUtf8\Db::i()->prefix;
$html = <<<EOFHTML
<div class="jumbotron" data-init="processInit">
<h1>Converting</h1>
<p> </p>
<p>
<div class="progress progress-striped active">
<div id="progressBar" data-start="{$percent}" class="progress-bar" role="progressbar" aria-valuenow="{$percent}" aria-valuemin="0" aria-valuemax="100" style="width: {$percent}%">
<span class='sr-only'>{$msg}</span>
</div>
</div>
</p>
<p id="message">{$msg}</p>
</div>
EOFHTML;
return static::parse( $html );
}
/**
* Completed page
*/
public static function completed( $timeTaken )
{
$html = <<<EOFHTML
<div class="jumbotron">
<h1>Almost there!</h1>
<p class="lead">The database has now been converted!</p>
<p><small>Conversion took {$timeTaken}</small></p>
<p> </p>
<p><a class="btn btn-lg btn-info" href="{url="?controller=browser&do=finish"}">Click here to finish</a></p>
</div>
EOFHTML;
return static::parse( $html );
}
/**
* Finish page
*/
public static function finished( $updated )
{
$utfPrefix = \IPSUtf8\Db::i('utf8')->prefix;
$normalPrefix = \IPSUtf8\Db::i()->prefix;
$sessionData = \IPSUtf8\Session::i()->json;
$sql_charset = ( array_key_exists( 'utf8mb4', $sessionData['charSets'] ) ) ? 'utf8mb4' : 'utf8';
/* Phil made me give him credit for some changes in this code */
$html = <<<EOFHTML
<div class="jumbotron">
<h1>Finished!</h1>
<p class="lead">The database has now been converted and the tables correctly renamed!</p>
EOFHTML;
if ( \IPSUtf8\Request::i()->controller === 'browser' )
{
if( !$updated )
{
$utf8mb4 = ( $sql_charset == 'utf8mb4' ) ? "<br>\$INFO['sql_utf8mb4'] = true;" : '';
$html .= <<<EOFHTML
<p>You can now <strong>complete the conversion</strong> by editing conf_global.php to add <br><code>\$INFO['sql_charset'] = '{$sql_charset}';{$utf8mb4}</code>.<br>Afterwards, you can <strong><a href="../upgrade">proceed with the upgrade</strong>.</p>
</div>
EOFHTML;
}
else
{
$html .= <<<EOFHTML
<p>You can now <strong><a href="../upgrade">proceed with the upgrade</strong>.</p>
</div>
EOFHTML;
}
}
else if ( \IPSUtf8\Request::i()->controller === 'archive' )
{
$html .= <<<EOFHTML
<p>You can now <strong>complete the conversion</strong> by converting the <a href="{url="?controller=browser"}">main database</a> if you've not already done so.</p>
</div>
EOFHTML;
}
return static::parse( $html );
}
/**
* Basic HTML parsing
*
* @param string $html Raw HTML
* @return string
*/
protected static function parse( $html )
{
/* Parse {plugin="foo"} tags */
$html = preg_replace_callback
(
'/\{([a-z]+?=([\'"]).+?\\2 ?+)}/',
function( $matches )
{
/* Work out the plugin and the values to pass */
preg_match_all( '/(.+?)='.$matches[2].'(.+?)'.$matches[2].'\s?/', $matches[1], $submatches );
$plugin = array_shift( $submatches[1] );
$value = array_shift( $submatches[2] );
$options = array();
foreach ( $submatches[1] as $k => $v )
{
$options[ $v ] = $submatches[2][ $k ];
}
switch( $plugin )
{
case 'url':
return \IPSUtf8\Output\Browser::$url . $value;
break;
}
},
$html
);
return $html;
}
}