| Current Path : /home/digilove/public_html/administrator/components/com_gsd/helpers/ |
| Current File : /home/digilove/public_html/administrator/components/com_gsd/helpers/migrator.php |
<?php
/**
* @package Google Structured Data
* @version 5.6.5 Pro
*
* @author Tassos Marinos <info@tassos.gr>
* @link http://www.tassos.gr
* @copyright Copyright © 2021 Tassos Marinos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
defined('_JEXEC') or die('Restricted Access');
use Joomla\Registry\Registry;
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseDriver;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
use Joomla\Filesystem\File;
use Joomla\CMS\Table\Table;
/**
* Google Structured Data Migrator
*
* The purpose of this class is to migrate data and offer a non-breaking update from 2.1 to 3.0 version.
* Migration should be run on 3.0 versions and below only.
*/
class GSDMigrator
{
/**
* Log messages array
*
* @var array
*/
public $log;
/**
* If enabled, the main GSD table will be flushed before starting the migration
*
* @var boolean
*/
private $flushTable;
/**
* Override the database object for testing purposes and sharing data between different databases
*
* @var mixed
*/
private $db;
/**
* Supported migration component names
*
* @var array
*/
private $migrateComponents = array(
'content',
'k2'
);
/**
* The name of the System Plugin
*
* @var string
*/
private $systemPluginName = 'plg_system_gsd';
/**
* Class Constructor
*
* @param boolean $flushTable Indicates wether the table will be flushed or not
* @param mixed $db Database options
*/
public function __construct($flushTable = false, $db = null)
{
$this->db = $db;
$this->flushTable = $flushTable;
$this->log = array('Migrator: Started');
}
/**
* Returns a database instance
*
* @return JDatabase object
*/
private function getDB()
{
return is_array($this->db) ? DatabaseDriver::getInstance($this->db) : Factory::getDBo();
}
/**
* Start the migration process
*
* @return void
*/
public function start()
{
// Flush the main GSD table
if ($this->flushTable)
{
$this->flushTable();
}
// Start the migration process
$this->migrateSnippets();
$this->migrateParams();
// Save log to a file
$this->saveLog();
}
/**
* Save log to a file
*
* @return void
*/
private function saveLog()
{
array_unshift(
$this->log,
str_repeat('-', 19),
Factory::getDate(),
str_repeat('-', 19)
);
$log = implode(PHP_EOL, $this->log) . PHP_EOL . PHP_EOL;
try
{
$logFile = __DIR__ . '/migrator/migrator.log';
File::write($logFile, $log);
}
catch (Exception $e)
{
$this->log[] = "Can't write log file" . $e->getMessage();
}
}
/**
* Move parameters from system plugin to component
*
* @return void
*/
private function migrateParams()
{
$this->log[] = 'Params: Migration Started.';
$pluginParams = $this->getPluginParams();
// Make sure we have valid parameters. Otherwise abort.
if (empty($pluginParams) || strpos($pluginParams, 'sitename_enabled') === false)
{
$this->log[] = 'Params: Migration Skipped. Probably already migrated.';
return;
}
// Store parameters to gsd_config
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_gsd/tables');
$table = Table::getInstance('Config', 'GSDTable');
$table->load('config');
$table->params = $pluginParams;
if (!$table->store())
{
$this->log[] = 'Params: Cannot store params to config table.';
return;
}
// Backup parameters to a file
$this->backup('params', $pluginParams);
// Remove parameters from plg_system_gsd
if (!$this->removePluginParams())
{
$this->log[] = 'Params: Unable to remove params from system plugin.';
return;
}
// Success!
$this->log[] = "Params: Migration finished.";
return true;
}
/**
* Move snippet data from each component to com_gsd
*
* Note: Items with the snippet property disabled are skipped.
*
* @return void
*/
private function migrateSnippets()
{
foreach ($this->migrateComponents as $component)
{
// Skip component if it's not installed.
if (!\NRFramework\Functions::extensionInstalled($component))
{
$this->log[] = "Snippets - $component: Not installed. Skipped.";
continue;
}
// Get component's snippets from database
if (!$snippets = $this->getComponentSnippets($component))
{
$this->log[] = "Snippets - $component: No snippets found. Skipped.";
continue;
}
// Backup Snippets
$this->backup($component . '_snippets', json_encode($snippets, JSON_PRETTY_PRINT));
// Loop through each snippet, validate it and save it to gsd table
foreach ($snippets as $snippet)
{
$params = $this->validateSnippetParams($snippet->params);
// Remove parameters from component's item
$this->removeComponentSnippet($component, $snippet->id);
if (is_null($params))
{
$this->log[] = "Snippets - $component: Item #$snippet->id skipped.";
continue;
}
// Prepare data array
$data = array(
'thing' => $snippet->id,
'plugin' => $component,
'params' => json_encode($params),
'state' => 1
);
// Load GSD model and save the data into the database
BaseDatabaseModel::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_gsd/models');
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_gsd/tables');
$model = BaseDatabaseModel::getInstance('Item', 'GSDModel');
$item = $model->validate(null, $data);
$result = $model->save($item);
$this->log[] = "Snippets - $component: Item #$snippet->id transfered.";
}
$this->log[] = "Snippets - $component: Migration finished.";
}
}
/**
* Validates snippets parameters and adjusts them to be valid with the new requirements
*
* Checks:
*
* 1. Removes gsd offset
* 2. Renames snippet property to contenttype
* 3. Alters rating property
* 4. Alters image property
*
* @param object $data The snippet params object
*
* @return array The new snippet params array
*/
private function validateSnippetParams($data)
{
// Prepare Params
$data = new Registry($data);
// Abort if we don't have the required property
if (!$data->offsetExists('gsd'))
{
return;
}
$p = $data->offsetGet('gsd');
// Abort if there is no selected content type
if (!isset($p->snippet) || $p->snippet == '0')
{
return;
}
$contentType = $p->snippet;
//Shorthand for snippet data
$s = $p->$contentType;
/**
* Rating Properties
*/
if ($contentType != 'review' && isset($s->ratingValue) && isset($s->reviewCount))
{
$ratingValue = (float) $s->ratingValue;
$reviewCount = (int) $s->reviewCount;
// If ratingValue and reviewCount are greater than 0 means that we have
// a valid custom rating data. Otherwise we disable the rating.
$ratingOption = ($ratingValue > 0 && $reviewCount > 0) ? 2 : 0;
// Add rating data to object
$s->rating = array(
'option' => $ratingOption,
'ratingValue' => $ratingValue,
'reviewCount' => $reviewCount
);
}
// On Reviews Content Type we have a ratingValue property only
if ($contentType == 'review' && isset($s->ratingValue) && (float) $s->ratingValue > 0)
{
$s->rating = array(
'option' => '2',
'ratingValue' => $s->ratingValue
);
}
// Unset old properties
unset($s->ratingValue);
unset($s->reviewCount);
/**
* Image Property
*
* Since the image option was available only in the Reviews Content Type we are going to
* check only that content type. All the other content types will be set to Inherit.
*/
if ($contentType == 'review' && isset($s->image))
{
$s->image = array(
'option' => $s->image,
'file' => $s->imgupload,
'url' => $s->imgurl
);
// Unset old properties
unset($s->imgupload);
unset($s->imgurl);
}
$data = array(
'contenttype' => $contentType,
$contentType => $s,
'customcode' => $p->customcode
);
return $data;
}
/**
* Get snippets from database
*
* @param string $component The component's alias name
*
* @return object
*/
private function getComponentSnippets($component)
{
$db = $this->getDB();
$query = $db->getQuery(true);
if ($component == 'k2')
{
$query
->select('id, plugins as params')
->from('#__k2_items')
->where($db->quoteName('plugins') . ' LIKE ' . $db->quote('%gsd%'));
}
if ($component == 'content')
{
$query
->select('id, attribs as params')
->from('#__content')
->where($db->quoteName('attribs') . ' LIKE ' . $db->quote('%gsd%'))
->where($db->quoteName('state') . ' IN (0,1,2)');
}
$db->setQuery($query);
return $db->loadObjectList();
}
/**
* Remove snippet parameters from component's item
*
* @param integer $id The item's primary key
*
* @return bool
*/
public function removeComponentSnippet($component, $id)
{
if (!in_array($component, $this->migrateComponents))
{
return;
}
if ($component == 'content')
{
$column = 'attribs';
$table = 'content';
}
if ($component == 'k2')
{
$column = 'plugins';
$table = 'k2_items';
}
$db = $this->getDB();
$params = $db->setQuery(
$db->getQuery(true)
->select($db->quoteName($column))
->from($db->quoteName('#__' . $table))
->where($db->quoteName('id') . ' = ' . $id)
)->loadResult();
$params = json_decode($params);
if (!isset($params->gsd))
{
return;
}
unset($params->gsd);
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__' . $table))
->set($db->quoteName($column) . ' = ' . $db->quote(json_encode($params)))
->where($db->quoteName('id') . ' = ' . $id)
);
return $db->execute();
}
/**
* Get GSD System Plugin Parameters
*
* @return mixed Registry object on success, false on failure
*/
private function getPluginParams()
{
$db = $this->getDB();
return $db->setQuery(
$db->getQuery(true)
->select('params')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('name') . ' = ' . $db->quote($this->systemPluginName))
)->loadResult();
}
/**
* Removes saved parameters from system plugin in order to avoid future re-migration.
*
* @return bool
*/
private function removePluginParams()
{
$db = $this->getDB();
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set(array($db->quoteName('params') . ' = ' . $db->quote('')))
->where(array($db->quoteName('name') . ' = ' . $db->quote($this->systemPluginName)))
);
return $db->execute();
}
/**
* Remove all records from local table
*
* @return void
*/
private function flushTable()
{
$this->log[] = "Migrator: Flushing GSD main table.";
$db = Factory::getDBo();
$db->setQuery('TRUNCATE #__gsd');
$db->execute();
}
/**
* Writes data to a backup file
*
* @param string $filename The filename
* @param string $data The data to be written into the file
*
* @return void
*/
private function backup($filename, $data)
{
try
{
File::write(__DIR__ . '/migrator/backup/' . $filename . '.bak', $data);
}
catch (Exception $e)
{
$this->log[] = "Can't write backup file: $filename" . $e->getMessage();
}
}
}