Last active
September 4, 2023 07:42
-
-
Save lukasdoerr/e63b04f5df5e409ebde8e6866b0a3ca4 to your computer and use it in GitHub Desktop.
TYPO3 Flux to Not Flux Migrator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
MIT License with Attribution Clause | |
Copyright (c) 2023 Lukas Dörr | |
Permission is hereby granted to use, modify, and distribute this software, provided that: | |
1. The above copyright notice and this permission notice are included. | |
2. Attribution to Lukas Doerr and link to www.ldoerr.com are prominently displayed. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. | |
*/ | |
if (!ini_get('display_errors')) { | |
ini_set('display_errors', '1'); | |
} | |
$config = array( | |
'DB' => [ | |
'Connections' => [ | |
'Default' => [ | |
'charset' => 'utf8mb4', | |
'dbname' => '', | |
'driver' => 'mysqli', | |
'host' => 'localhost', | |
'password' => '', | |
'socket' => '', | |
'user' => '', | |
], | |
], | |
], | |
); | |
$con = mysqli_connect( | |
$config['DB']['Connections']['Default']['host'], | |
$config['DB']['Connections']['Default']['user'], | |
$config['DB']['Connections']['Default']['password'], | |
$config['DB']['Connections']['Default']['dbname'], | |
); | |
if(!@$con) { | |
die('db con failed'); | |
} | |
else { | |
if(!@isset($_GET['do'])) { | |
?> | |
<html> | |
<h1 style="font-family: Arial, Helvetica, sans-serif;"> | |
<span style="color:red;">F</span> | |
<span style="color:orange;">l</span> | |
<span style="color:yellow;">u</span> | |
<span style="color:green;">x</span> | |
<span style="color:blue;">M</span> | |
<span style="color:indigo;">i</span> | |
<span style="color:purple;">g</span> | |
<span style="color:red;">r</span> | |
<span style="color:orange;">a</span> | |
<span style="color:yellow;">t</span> | |
<span style="color:green;">o</span> | |
<span style="color:blue;">r</span> | |
</h1> | |
<a href="fluxmigrator.php?do=element_migrator">Migrate Content Elements</a> | |
<br> | |
<a href="fluxmigrator.php?do=page_migrator">Migrate Pages</a> | |
</html> | |
<?php | |
} | |
else { | |
switch ($_GET['do']) { | |
case 'page_migrator': | |
//Get Pages and their flux_grid Information | |
$getPagesText = "SELECT * FROM pages WHERE deleted = 0"; | |
$getPagesQuery = mysqli_query($con, $getPagesText); | |
$foundFluxLayouts = []; | |
$foundFluxLayoutsSub = []; | |
while ($pages = mysqli_fetch_array($getPagesQuery)) { | |
if(isset($pages['tx_fed_page_controller_action']) || isset($pages['tx_fed_page_controller_action_sub'])) { | |
//Layouts | |
if(!in_array($pages['tx_fed_page_controller_action'], $foundFluxLayouts) && !empty($pages['tx_fed_page_controller_action'])) { | |
$foundFluxLayouts[] = $pages['tx_fed_page_controller_action']; | |
} | |
//Layouts for subpages | |
if(!in_array($pages['tx_fed_page_controller_action_sub'], $foundFluxLayoutsSub) && !empty($pages['tx_fed_page_controller_action_sub'])) { | |
$foundFluxLayoutsSub[] = $pages['tx_fed_page_controller_action_sub']; | |
} | |
} | |
} | |
if(count($foundFluxLayouts)) { | |
?> | |
<h2>Flux Page Migrator</h2> | |
Remember to add the affected backend_layouts before proceeding<br> | |
And Backup your pages table before running the queries<br> | |
<h3>Found Flux Layouts:</h3> | |
<?php | |
foreach($foundFluxLayouts as $layout) { | |
echo '<br>'.$layout; | |
} | |
echo "<br><br>"; | |
foreach($foundFluxLayouts as $layout) { | |
//echo '<br>'.$layout.'<br>'; | |
$migrLayout = strstr($layout, '>'); | |
$migrLayout = trim($migrLayout,'>'); | |
echo "UPDATE pages SET backend_layout = 'pagets__$migrLayout' WHERE tx_fed_page_controller_action = '$layout' AND deleted = 0;<br>"; | |
} | |
echo "<br><hr><br> | |
<h3>Found Flux Sub-Layouts:</h3>"; | |
foreach($foundFluxLayoutsSub as $layout) { | |
echo '<br>'.$layout; | |
} | |
echo "<br><br>"; | |
foreach($foundFluxLayoutsSub as $layout) { | |
$migrLayout = strstr($layout, '>'); | |
$migrLayout = trim($migrLayout,'>'); | |
echo "UPDATE pages SET backend_layout_next_level = 'pagets__$migrLayout' WHERE tx_fed_page_controller_action_sub = '$layout' AND deleted = 0;<br>"; | |
} | |
?> | |
</form> | |
<?php | |
} | |
else { | |
echo "Pages not found."; | |
} | |
break; | |
case 'element_migrator': | |
?> | |
<h2>Flux Content Element Migrator</h2> | |
<form action="fluxmigrator.php?do=get_element" method="post"> | |
<label for="ctype_flux">CType of Flux Element:</label><br> | |
<input required type="text" name="ctype_flux"/> | |
<br><br> | |
<input type="submit" value="Check"> | |
</form> | |
<?php | |
break; | |
case 'get_element': | |
$cTypeFlux = mysqli_real_escape_string($con, $_POST['ctype_flux']); | |
$textGetFluxElements = "SELECT * FROM tt_content WHERE CType LIKE '%".$cTypeFlux."%' AND deleted = 0"; | |
$foundFluxElements = []; | |
if($queryGetFluxElements = mysqli_query($con, $textGetFluxElements)) { | |
while($faFluxElements = mysqli_fetch_array($queryGetFluxElements)) { | |
$foundFluxElements[] = $faFluxElements; | |
} | |
} | |
//Check if element is in use and not deleted | |
if(@count($foundFluxElements)) { | |
$availableFields = []; | |
$d = false; | |
foreach($foundFluxElements as $element) { | |
if(!$d) { | |
$xml = simplexml_load_string($element['pi_flexform']); | |
$json = json_encode($xml); | |
$flexFormConfiguration = json_decode($json,TRUE); | |
/* | |
echo "flexFormConfiguration:<br><pre>"; | |
print_r($flexFormConfiguration); | |
echo "</pre><br>"; | |
*/ | |
if(@is_array($flexFormConfiguration['data']['sheet']['language']['field'])) | |
{ | |
$fields = $flexFormConfiguration['data']['sheet']['language']['field']; | |
} | |
if(@count($fields)) { | |
foreach ($fields as $field) { | |
if(isset($field['@attributes']['index'])) { | |
if(!in_array($field['@attributes']['index'], $availableFields)) { | |
$availableFields[] = $field['@attributes']['index']; | |
} | |
} | |
else { | |
//Try to find without @attributes | |
if(isset($fields['index'])) { | |
if(!in_array($fields['index'], $availableFields)) { | |
$availableFields[] = $fields['index']; | |
} | |
} | |
else { | |
//die('no fields found'); | |
} | |
} | |
} | |
} | |
} | |
//$d = true; | |
} | |
// echo "available fields:<br><pre>"; | |
// print_r($availableFields); | |
// echo "</pre><br>"; | |
?> | |
<form action="fluxmigrator.php?do=generate_queries" method="post"> | |
<?php | |
foreach($availableFields as $field) { | |
echo ' | |
<div class="form-group"> | |
<label for="'.$field.'">Mask Fieldname to migrate "'.$field.'" to:</label><br> | |
<input id="'.$field.'" type="text" name="fields['.$field.']"> is FAL? <input type="checkbox" name="fields_fal['.$field.']" /> yes | |
<br> | |
</div> | |
'; | |
} | |
?> | |
<div class="form-group"> | |
<br> | |
<label for="from_flux">CType of Flux Element:</label><br> | |
<input id="from_flux" type="text" name="from_flux" value="<?php echo $cTypeFlux; ?>" readonly/> | |
<br><br> | |
</div> | |
<div class="form-group"> | |
<label for="mask_element">Mask Element CType (begins with mask_*, like: "mask_exampleelement"):</label><br> | |
<input required type="text" name="mask_ctype"/> | |
<br><br> | |
</div> | |
<input type="submit" value="Generate Query"> | |
</form> | |
<?php | |
} | |
else { | |
echo $cTypeFlux." not found, maybe already migrated?"; | |
} | |
break; | |
case 'generate_queries': | |
$cTypeFlux = mysqli_real_escape_string($con, $_POST['from_flux']); | |
$cTypeMask = mysqli_real_escape_string($con, $_POST['mask_ctype']); | |
$fluxFields = $_POST['fields']; | |
if(@isset($_POST['fields_fal'])) { | |
$fluxFALFields = $_POST['fields_fal']; | |
} | |
else { | |
$fluxFALFields = NULL; | |
} | |
//Get Flux Elements again: | |
$textGetFluxElements = "SELECT * FROM tt_content WHERE CType LIKE '%".$cTypeFlux."%' AND deleted = 0"; | |
$FALqueries = []; | |
if($queryGetFluxElements = mysqli_query($con, $textGetFluxElements)) { | |
while($faFluxElements = mysqli_fetch_array($queryGetFluxElements)) { | |
//Get Element Flux Settings again: | |
$xml = simplexml_load_string($faFluxElements['pi_flexform']); | |
$json = json_encode($xml); | |
$flexFormConfiguration = json_decode($json,TRUE); | |
if(@is_array($flexFormConfiguration['data']['sheet']['language']['field'])) | |
{ | |
$fields = $flexFormConfiguration['data']['sheet']['language']['field']; | |
} | |
//echo "<br>Fields: <br><pre>".print_r($fields)."</pre><br><br>"; | |
$queryMigration = "UPDATE tt_content SET CType = '".$cTypeMask."'"; | |
$queryMigrationFAL = "UPDATE sys_file_reference SET tablenames = 'tt_content'"; | |
$availableFields = []; | |
$c = 0; | |
if(@count($fields)) { | |
foreach ($fields as $field) { | |
//print_are($field, 'flexFormConfiguration', true); | |
if(isset($field['@attributes']['index'])) { | |
$availableFields[$c]['name'] = $field['@attributes']['index']; | |
if(is_array($field['value'])) { | |
$availableFields[$c]['value'] = 'NULL'; | |
} | |
else { | |
$availableFields[$c]['value'] = htmlspecialchars($field['value']); | |
} | |
} | |
else { | |
//Try to find without @attributes | |
if(isset($field['index'])) { | |
$availableFields[$c]['name'] = $field['index']; | |
} | |
else { | |
//$availableFields[$c]['name'] = $field['index']; | |
if(is_array($fields['value'])) { | |
$availableFields[$c]['value'] = 'NULL'; | |
} | |
else { | |
$availableFields[$c]['value'] = htmlspecialchars($fields['value']); | |
} | |
} | |
} | |
$c++; | |
} | |
} | |
// echo "availableFields:<br><pre>"; | |
// print_r($availableFields); | |
// echo "</pre>"; | |
//Add the available fields: | |
foreach ($availableFields as $avField) { | |
if($avField['value'] == 'NULL') { | |
$queryMigration .= ", ".$fluxFields[$avField['name']]." = ''"; | |
} | |
else { | |
$queryMigration .= ", ".$fluxFields[$avField['name']]." = '".$avField['value']."'"; | |
} | |
if(isset($fluxFALFields)) { | |
if(@$fluxFALFields[$avField['name']] == 'on') { | |
$queryMigrationFAL .= ", fieldname = '".$fluxFields[$avField['name']]."'"; | |
} | |
} | |
} | |
$queryMigrationFAL .= " WHERE uid_foreign = '".$faFluxElements['uid']."' AND deleted = 0;"; | |
$FALqueries[] = $queryMigrationFAL; | |
$queryMigration .= " WHERE uid = '".$faFluxElements['uid']."';<br>"; | |
//Check if there are FAL selected Fields to switch File Reference! | |
echo $queryMigration.'<br>'; | |
unset($availableFields); | |
} | |
echo "<hr><br><br>FAL Queries for selected fields:<br><br>"; | |
foreach ($FALqueries as $query) { | |
echo "<br>".$query; | |
} | |
} | |
break; | |
default: | |
throw new \Exception('Unexpected value'); | |
} | |
} | |
mysqli_close($con); | |
} | |
function print_are($obj, $title = false, $collapse = false) { | |
$template = ' | |
<style> | |
.print_are, | |
.print_are * { | |
box-sizing: border-box; | |
padding: 0; | |
margin: 0; | |
min-height: 0; | |
height: auto; | |
font-family: monospace; | |
line-height: normal; | |
} | |
.print_are { | |
display: block; | |
position: relative; | |
width: 100%; | |
padding: 10px; | |
} | |
.print_are .pre-wrap { | |
background-color: white; | |
color: black; | |
border: 1px solid #333; | |
border-radius: 2px; | |
box-shadow: 0 0 5px #aaa; | |
font-size: 14px; | |
line-height: 16px; | |
position: relative; | |
text-align: left; | |
overflow: auto; | |
display: block; | |
} | |
.print_are .pre-header { | |
padding: 10px 10px 9px; | |
border-bottom: 1px solid #333; | |
} | |
.print_are .pre-obj-type, | |
.print_are .pre-actions a { | |
font-size: 16px; | |
font-weight: bold; | |
} | |
.print_are .pre-actions a { | |
text-decoration: none; | |
font-weight: normal; | |
font-size: 14px; | |
} | |
.print_are .pre-actions a:hover { | |
text-decoration: underline; | |
} | |
.print_are .pre-actions { | |
float: right; | |
} | |
.print_are .pre-content { | |
padding: 0; | |
margin: 0; | |
overflow: auto; | |
background: #fff; | |
border: none; | |
border-radius: 0; | |
box-shadow: none; | |
} | |
.print_are .pre-line, | |
.print_are .pre-lines, | |
.print_are .pre-content { | |
font-size: 12px; | |
line-height: normal; | |
} | |
.print_are .pre-lines { | |
min-height: 20px; | |
} | |
.print_are .pre-lines[data-lines-count="1"] > li:empty::after { | |
content: "(Empty)"; | |
color: #aaa; | |
letter-spacing: 1px; | |
} | |
.print_are .pre-toggle-wrap .unwrap > span { | |
display: none; | |
} | |
.print_are .wrap-lines .pre-toggle-wrap .unwrap > span { | |
display: inline; | |
} | |
.print_are .wrap-lines .pre-content .pre-line { | |
white-space: pre-wrap; | |
} | |
.print_are .pre-lines { | |
display: block; | |
background: #f2f2f2; | |
border-left: 1px solid #333; | |
padding: 2px 0; | |
} | |
.print_are .pre-line { | |
padding: 2px 0 2px 4px; | |
} | |
.print_are .pre-line .key { | |
cursor: pointer; | |
} | |
.print_are .pre-line.selected, | |
.print_are .pre-line:hover { | |
background: #eec; | |
font-weight: bold; | |
} | |
.print_are .pre-line.selected { | |
box-shadow: 0 0 0 1px #ccc inset; | |
margin: 1px; | |
} | |
</style> | |
<div class="print_are"> | |
<div class="pre-wrap"> | |
<div class="pre-header"> | |
<span class="pre-obj-type">{obj_type}</span> | |
<div class="pre-actions"> | |
<a href="#" class="pre-toggle-wrap" onclick="Print_Are.TW(this, event);" title="Wrap/Unwrap Long Lines"><span class="unwrap"><span>UN-</span>WRAP</span></a> | |
/ | |
<a href="#" class="pre-toggle-display" onclick="Print_Are.TD(this, event);" title="Show/Hide {obj_type}">{toggle_display_text}</a> | |
<div style="clear:both"></div> | |
</div> | |
</div> | |
<div style="clear:both"></div> | |
<pre class="pre-content" style="padding-left: {padding_left}px; display: {obj_hide};"><ol class="pre-lines" data-lines-count="{lines_count}"><li class="pre-line" onclick="Print_Are.TB(this)">{obj_content}</li></ol></pre> | |
</div> | |
</div> | |
<script> | |
if (typeof Print_Are !== "object") { | |
var Print_Are = new Object; | |
// Toggle Display | |
Print_Are.TD = function(el, event) { | |
event.preventDefault(); | |
var e = el.parentElement.parentElement.parentElement; | |
var p = e.querySelector(".pre-content"); | |
if (p !== null) { | |
if (p.style.display === "none") { | |
el.innerHTML = "CLOSE"; | |
p.style.display = "block"; | |
} else { | |
el.innerHTML = "OPEN"; | |
p.style.display = "none"; | |
} | |
} | |
} | |
// Toggle Wrap | |
Print_Are.TW = function(el, event) { | |
event.preventDefault(); | |
var e = el.parentElement.parentElement.parentElement; | |
if (e !== null) { | |
var c = e.classList.contains("wrap-lines"); | |
if (c) { | |
e.classList.remove("wrap-lines"); | |
} else { | |
e.classList.add("wrap-lines"); | |
} | |
} | |
} | |
// Toggle Bookmark | |
Print_Are.TB = function(el) { | |
el.classList.toggle("selected"); | |
} | |
// Select Text | |
Print_Are.ST = function(el) { | |
el.parentElement.classList.add("selected"); | |
var range = document.createRange(); | |
var selection = window.getSelection(); | |
selection.removeAllRanges(); | |
range.selectNodeContents(el); | |
selection.addRange(range); | |
} | |
} | |
</script>'; | |
$obj_type = gettype($obj); | |
$obj_type = $title ? "$obj_type - $title" : $obj_type; | |
$obj_str = print_r($obj, true); | |
//$obj_str = str_replace(" ", " ", $obj_str); // tabsize = 2 | |
preg_match_all("/\[[^\]]*\]/", $obj_str, $matches); | |
if( count($matches[0]) > 0 ) { | |
$vars = array(); | |
foreach ($matches[0] as $match) { | |
$val = str_replace(array('[',']'), '', $match); | |
$vars["$match"] = "[<span class='key' title='Click to select text.' onclick='Print_Are.ST(this)'>$val</span>]"; | |
} | |
$search = array_keys($vars); | |
$replace = array_values($vars); | |
$obj_str = str_replace($search, $replace, $obj_str); | |
} | |
$obj_arr = explode(PHP_EOL, $obj_str); | |
$lines_count = count($obj_arr); | |
$obj_content = implode("</li><li class='pre-line' onclick='Print_Are.TB(this)'>", $obj_arr); | |
$obj_hide = $collapse ? 'none' : 'block'; | |
$toggle_display_text = $collapse ? 'OPEN' : 'CLOSE'; | |
$default_padding_left = 15; | |
$padding_offset = 10; | |
$line_count = (string) count($obj_arr); | |
$count_length = (int) strlen($line_count); | |
$padding_left = $default_padding_left + ($count_length * $padding_offset); | |
$vars = array( | |
'{lines_count}' => $lines_count, | |
'{obj_hide}' => $obj_hide, | |
'{obj_type}' => $obj_type, | |
'{obj_content}' => $obj_content, | |
'{padding_left}' => $padding_left, | |
'{toggle_display_text}' => $toggle_display_text, | |
); | |
$search = array_keys($vars); | |
$replace = array_values($vars); | |
$output = str_replace($search, $replace, $template); | |
echo $output; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment