Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions administrator/components/com_admin/script.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Joomla\CMS\Log\Log;
use Joomla\CMS\Table\Table;
use Joomla\Database\ParameterType;
use Joomla\Component\Fields\Administrator\Model\FieldModel;
use Joomla\Database\UTF8MB4SupportInterface;

/**
Expand Down Expand Up @@ -98,6 +99,7 @@ public function update($installer)
$this->updateAssets($installer);
$this->clearStatsCache();
$this->convertTablesToUtf8mb4(true);
$this->uninstallRepeatableFieldsPlugin();
$this->cleanJoomlaCache();

// VERY IMPORTANT! THIS METHOD SHOULD BE CALLED LAST, SINCE IT COULD
Expand Down Expand Up @@ -223,6 +225,199 @@ protected function updateDatabaseMysql()
}
}

/**
* Uninstalls the plg_fields_repeatable plugin and transforms its custom field instances
* to instances of the plg_fields_subfields plugin.
*
* @return void
*
* @since __DEPLOY_VERSION__
*/
protected function uninstallRepeatableFieldsPlugin()
{
$app = Factory::getApplication();
$db = Factory::getDbo();

// Check if the plg_fields_repeatable plugin is present
$extensionId = $db->setQuery(
$db->getQuery(true)
->select('extension_id')
->from('#__extensions')
->where('name = ' . $db->quote('plg_fields_repeatable'))
)->loadResult();

// Skip uninstalling when it doesn't exist
if (!$extensionId)
{
return;
}

try
{
$db->transactionStart();

// Get the FieldsModelField, we need it in a sec
$fieldModel = $app->bootComponent('com_fields')->getMVCFactory()->createModel('Field', 'Administrator', ['ignore_request' => true]);
/** @var FieldModel $fieldModel */

// Now get a list of all `repeatable` custom field instances
$db->setQuery(
$db->getQuery(true)
->select('*')
->from('#__fields')
->where($db->quoteName('type') . ' = ' . $db->quote('repeatable'))
);

// Execute the query and iterate over the `repeatable` instances
foreach ($db->loadObjectList() as $row)
{
// Skip broken rows - just a security measure, should not happen
if (!isset($row->fieldparams) || !($oldFieldparams = json_decode($row->fieldparams)) || !is_object($oldFieldparams))
{
continue;
}

/**
* We basically want to transform this `repeatable` type into a `subfields` type. While $oldFieldparams
* holds the `fieldparams` of the `repeatable` type, $newFieldparams shall hold the `fieldparams`
* of the `subfields` type.
*/
$newFieldparams = array(
'repeat' => '1',
'options' => array(),
);

// If this repeatable fields actually had child-fields (normally this is always the case)
if (isset($oldFieldparams->fields) && is_object($oldFieldparams->fields))
{
// Small counter for the child-fields (aka sub fields)
$newFieldCount = 0;

// Iterate over the sub fields
foreach (get_object_vars($oldFieldparams->fields) as $oldField)
{
// Used for field name collision prevention
$fieldname_prefix = '';
$fieldname_suffix = 0;

// Try to save the new sub field in a loop because of field name collisions
while (true)
{
/**
* We basically want to create a completely new custom fields instance for every sub field
* of the `repeatable` instance. This is what we use $data for, we create a new custom field
* for each of the sub fields of the `repeatable` instance.
*/
$data = array(
'context' => $row->context,
'group_id' => $row->group_id,
'title' => $oldField->fieldname,
'name' => (
$fieldname_prefix
. $oldField->fieldname
. ($fieldname_suffix > 0 ? ('_' . $fieldname_suffix) : '')
),
'label' => $oldField->fieldname,
'default_value' => $row->default_value,
'type' => $oldField->fieldtype,
'description' => $row->description,
'state' => '1',
'params' => $row->params,
'language' => '*',
'assigned_cat_ids' => [-1],
);

// `number` is not a valid custom field type, so use `text` instead.
if ($data['type'] == 'number')
{
$data['type'] = 'text';
}

// Reset the state because else \Joomla\CMS\MVC\Model\AdminModel will take an already
// existing value (e.g. from previous save) and do an UPDATE instead of INSERT.
$fieldModel->setState('field.id', 0);

// If an error occurred when trying to save this.
if (!$fieldModel->save($data))
{
// If the error is, that the name collided, increase the collision prevention
$error = $fieldModel->getError();

if ($error == 'COM_FIELDS_ERROR_UNIQUE_NAME')
{
// If this is the first time this error occurs, set only the prefix
if ($fieldname_prefix == '')
{
$fieldname_prefix = ($row->name . '_');
}
else
{
// Else increase the suffix
$fieldname_suffix++;
}

// And start again with the while loop.
continue 1;
}

// Else bail out with the error. Something is totally wrong.
throw new \Exception($error);
}

// Break out of the while loop, saving was successful.
break 1;
}

// Get the newly created id
$subfield_id = $fieldModel->getState('field.id');

// Really check that it is valid
if (!is_numeric($subfield_id) || $subfield_id < 1)
{
throw new \Exception('Something went wrong.');
}

// And tell our new `subfields` field about his child
$newFieldparams['options'][('option' . $newFieldCount)] = array(
'customfield' => $subfield_id,
'render_values' => '1',
);

$newFieldCount++;
}
}

// Write back the changed stuff to the database
$db->setQuery(
$db->getQuery(true)
->update('#__fields')
->set($db->quoteName('type') . ' = ' . $db->quote('subfields'))
->set($db->quoteName('fieldparams') . ' = ' . $db->quote(json_encode($newFieldparams)))
->where($db->quoteName('id') . ' = ' . $db->quote($row->id))
)->execute();
}

// Now, unprotect the plugin so we can uninstall it
$db->setQuery(
$db->getQuery(true)
->update('#__extensions')
->set('protected = 0')
->where($db->quoteName('extension_id') . ' = ' . $extensionId)
)->execute();

// And now uninstall the plugin
$installer = new Installer;
$installer->uninstall('plugin', $extensionId);

$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
}

/**
* Update the manifest caches
*
Expand Down Expand Up @@ -4518,6 +4713,10 @@ public function deleteUnexistingFiles()
'/plugins/content/confirmconsent/fields/consentbox.php',
'/plugins/editors/codemirror/layouts/editors/codemirror/init.php',
'/plugins/editors/tinymce/field/skins.php',
'/plugins/fields/repeatable/params/repeatable.xml',
'/plugins/fields/repeatable/repeatable.php',
'/plugins/fields/repeatable/repeatable.xml',
'/plugins/fields/repeatable/tmpl/repeatable.php',
'/plugins/quickicon/joomlaupdate/joomlaupdate.php',
'/plugins/system/languagecode/language/en-GB/en-GB.plg_system_languagecode.ini',
'/plugins/system/languagecode/language/en-GB/en-GB.plg_system_languagecode.sys.ini',
Expand Down Expand Up @@ -5896,6 +6095,9 @@ public function deleteUnexistingFiles()
'/administrator/components/com_actionlogs/libraries',
'/administrator/components/com_actionlogs/helpers',
'/administrator/components/com_actionlogs/controllers',
'/plugins/fields/repeatable/params',
'/plugins/fields/repeatable/tmpl',
'/plugins/fields/repeatable',
);

foreach ($files as $file)
Expand Down
16 changes: 0 additions & 16 deletions administrator/language/en-GB/plg_fields_repeatable.ini

This file was deleted.

7 changes: 0 additions & 7 deletions administrator/language/en-GB/plg_fields_repeatable.sys.ini

This file was deleted.

1 change: 0 additions & 1 deletion installation/sql/mysql/joomla.sql
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,6 @@ INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`,
(0, 'plg_editors-xtd_fields', 'plugin', 'fields', 'editors-xtd', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_sampledata_blog', 'plugin', 'blog', 'sampledata', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_system_sessiongc', 'plugin', 'sessiongc', 'system', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_fields_repeatable', 'plugin', 'repeatable', 'fields', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_content_confirmconsent', 'plugin', 'confirmconsent', 'content', 0, 0, 1, 0, '', '{}', 0, NULL, 0, 0),
(0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, '', '{}', 0, NULL, 0, 0),
(0, 'plg_actionlog_joomla', 'plugin', 'joomla', 'actionlog', 0, 1, 1, 0, '', '{}', 0, NULL, 0, 0),
Expand Down
1 change: 0 additions & 1 deletion installation/sql/postgresql/joomla.sql
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,6 @@ INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder",
(0, 'plg_editors-xtd_fields', 'plugin', 'fields', 'editors-xtd', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_sampledata_blog', 'plugin', 'blog', 'sampledata', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_system_sessiongc', 'plugin', 'sessiongc', 'system', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_fields_repeatable', 'plugin', 'repeatable', 'fields', 0, 1, 1, 0, '', '', 0, NULL, 0, 0),
(0, 'plg_content_confirmconsent', 'plugin', 'confirmconsent', 'content', 0, 0, 1, 0, '', '{}', 0, NULL, 0, 0),
(0, 'plg_system_actionlogs', 'plugin', 'actionlogs', 'system', 0, 1, 1, 0, '', '{}', 0, NULL, 0, 0),
(0, 'plg_actionlog_joomla', 'plugin', 'joomla', 'actionlog', 0, 1, 1, 0, '', '{}', 0, NULL, 0, 0),
Expand Down
67 changes: 0 additions & 67 deletions plugins/fields/repeatable/params/repeatable.xml

This file was deleted.

Loading