Skip to content

Instantly share code, notes, and snippets.

@muskie9
Created October 5, 2018 02:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save muskie9/8321b9730c191d5c22b14ce2f316a1af to your computer and use it in GitHub Desktop.
Save muskie9/8321b9730c191d5c22b14ce2f316a1af to your computer and use it in GitHub Desktop.
<?php
namespace Foo\Bar\Baz\Tasks;
use SilverStripe\Assets\File;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Dev\BuildTask;
use SilverStripe\Dev\Debug;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\Versioned\Versioned;
/**
* Class ContentFileMigrationTask
* @package Foo\Bar\Baz\Tasks
*/
class ContentFileMigrationTask extends BuildTask
{
/**
* @var string
*/
protected $title = 'Content File Migration Task';
/**
* @var string
*/
private static $segment = 'content-file-migration-task';
/**
* Mapping property for field updates
*
* @var array
*/
private $mapping = [];
/**
* @param \SilverStripe\Control\HTTPRequest $request
*/
public function run($request)
{
$this->setMapping();
$this->migrateContentFiles();
}
/**
* Migrate file image references to the new SS4 shortcode
*/
protected function migrateContentFiles()
{
foreach ($this->getMapping() as $class => $tables) {
foreach ($tables as $table => $fields) {
$selectString = 'ID';
foreach ($fields as $field) {
$selectString = "$selectString, \"{$field}\"";
}
$records = DB::query("SELECT {$selectString} FROM \"{$table}\"");
//echo count($records) . " records to be updated in {$table}\n";
foreach ($records as $record) {
//echo count($fields) . " field(s) to be updated for {$class} - {$record['ID']}\n";
foreach ($fields as $field) {
$this->updateFileReference($class, $table, $record, $field);
}
}
}
}
}
/**
* Yield all subclasses of DataObject
*
* @return \Generator
*/
protected function getClasses()
{
$classes = ClassInfo::subclassesFor(DataObject::class);
unset($classes[strtolower(DataObject::class)]);
foreach ($classes as $class) {
yield $class;
}
}
/**
* Set mapping array
*
* [
* 'ClassName' => [
* 'TableName' => [
* 'FieldName',
* 'FieldName2',
* ],
* 'TableName2' => [
* 'FieldName3',
* ],
* ],
* ]
*
* @param $class
*/
protected function setMapping()
{
foreach ($this->getClasses() as $class) {
foreach ($this->getDBFields($class) as $field => $type) {
if ($type == 'HTMLText') {
$table = $this->getFieldTable($class, $field);
if (!isset($this->mapping[$class])) {
$this->mapping[$class] = [];
}
if (!isset($This->mapping[$class][$table])) {
$this->mapping[$class][$table] = [];
}
if (!in_array($field, $this->mapping[$class][$table])) {
$this->mapping[$class][$table][] = $field;
}
}
}
}
return $this;
}
/**
* Get the array of Class, Table and Field mapping for updating
*
* @return array
*/
protected function getMapping()
{
if (empty($this->mapping)) {
$this->setMapping();
}
return $this->mapping;
}
/**
* Get database fields for a given class
*
* @param $class
* @return mixed
*/
protected function getDBFields($class)
{
return $class::singleton()->getSchema()->fieldSpecs($class);
}
/**
* Get the appropriate table to update via SQL as to not publish draft content from the SS3 site
*
* @param $class
* @param $field
* @return mixed
*/
protected function getFieldTable($class, $field)
{
return $class::singleton()->getSchema()->tableForField($class, $field);
}
/**
* Update records of based on ContentFileMigrationTask::mapping
*
* @param $class
* @param $table
* @param $record
* @param $field
*/
protected function updateFileReference($class, $table, $record, $field)
{
if (preg_match('/<img\s*(?:src\s*\=\s*[\'\"](.*?)[\'\"].*?\s*|\s*|\s*)+.*?>/sm', $record[$field], $matches)) {
foreach ($matches as $match) {
preg_match('/src=".*"/', $match, $source);
$path = substr($source[0], 5, strlen($source[0]) - 6);
$parts = explode('/', $path);
$file = File::get()->filter('FileFilename:PartialMatch', $parts[count($parts) - 1])->first();
$newValue = $this->getNewFieldValue($record, $field, $match, $file);
if ($newValue) {
$versioned = $class::singleton()->hasExtension(Versioned::class);
//echo "{$table}\n";
DB::prepared_query("UPDATE \"{$table}\" SET \"{$field}\" = ? WHERE ID = ?", [$newValue, $record['ID']]);
echo "Updated image reference for: {$class} - {$record['ID']}\n";
if ($versioned) {
echo "Versioned and live tables updated\n";
DB::prepared_query("UPDATE \"{$table}_Live\" SET \"{$field}\" = ? WHERE ID = ?", [$newValue, $record['ID']]);
DB::prepared_query("UPDATE \"{$table}_Versions\" SET \"{$field}\" = ? WHERE RecordID = ?", [$newValue, $record['ID']]);
}
} else {
echo "Couldn't update {$class} = {$record['ID']} - {$match}\n";
}
}
}
}
/**
* Replace image instances with new shortcode
*
* @param $record
* @param $field
* @param $match
* @param $file
* @return mixed
*/
protected function getNewFieldValue($record, $field, $match, $file)
{
if ($file instanceof File) {
$newPath = $file->getURL();
$find = [
'/<img/',
'/src=".*"/',
'/>/',
];
$replace = [
'[image',
"src=\"{$newPath}\"",
']',
];
$newReference = preg_replace($find, $replace, $match);
echo "New image referance {$newReference}\n";
$newValue = str_replace($match, $newReference, $record[$field]);
return $newValue;
}
return false;
}
}
@phil-quinn
Copy link

Thanks for creating this! I forked it and made a few tweaks:
https://gist.github.com/phil-quinn/aed842994e85fd79bf7efc6aa1c78455

  1. support for Fluent (I have not tested it WITHOUT Fluent however)
  2. a dry run option (prevents DB updates)
  3. hack to fix weird empty attribute issue where things like title="" become title="""" in the RTE. (so I'm removing empty attributes)
  4. add the id=123 attribute to the shortcode.

Hope this is useful, and thanks again for creating this!

@phil-quinn
Copy link

Update: My tweaks are not quite ready for prime time. I introduced problems with Fluent and Versions. Will add comment when I think I have them fixed.

@phil-quinn
Copy link

Should work better now. Also added file lookup caching for speed.
https://gist.github.com/phil-quinn/aed842994e85fd79bf7efc6aa1c78455

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment