<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use Joomla\CMS\Access\Rules; use Joomla\CMS\Factory; use Joomla\CMS\Table\Asset; /** * FOF model behavior class to add Joomla! ACL assets support * * @since 2.1 */ class Assets extends Observer { public function onAfterSave(DataModel &$model) { if (!$model->hasField('asset_id') || !$model->isAssetsTracked()) { return true; } $assetFieldAlias = $model->getFieldAlias('asset_id'); $currentAssetId = $model->getFieldValue('asset_id'); unset($model->$assetFieldAlias); // Create the object used for inserting/updating data to the database $fields = $model->getTableFields(); // Let's remove the asset_id field, since we unset the property above and we would get a PHP notice if (isset($fields[$assetFieldAlias])) { unset($fields[$assetFieldAlias]); } // Asset Tracking $parentId = $model->getAssetParentId(); $name = $model->getAssetName(); $title = $model->getAssetTitle(); $asset = new Asset(Factory::getDbo()); $asset->loadByName($name); // Re-inject the asset id. $this->$assetFieldAlias = $asset->id; // Check for an error. $error = $asset->getError(); // Since we are using \Joomla\CMS\Table\Table, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!empty($error)) { throw new \Exception($error); } // @codeCoverageIgnoreEnd // Specify how a new or moved node asset is inserted into the tree. // Since we're unsetting the table field before, this statement is always true... if (empty($model->$assetFieldAlias) || $asset->parent_id !== $parentId) { $asset->setLocation($parentId, 'last-child'); } // Prepare the asset to be stored. $asset->parent_id = $parentId; $asset->name = $name; $asset->title = $title; if ($model->getRules() instanceof Rules) { $asset->rules = (string) $model->getRules(); } // Since we are using \Joomla\CMS\Table\Table, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!$asset->check() || !$asset->store()) { throw new \Exception($asset->getError()); } // @codeCoverageIgnoreEnd // Create an asset_id or heal one that is corrupted. if (empty($model->$assetFieldAlias) || (($currentAssetId != $model->$assetFieldAlias) && !empty($model->$assetFieldAlias))) { // Update the asset_id field in this table. $model->$assetFieldAlias = (int) $asset->id; $k = $model->getKeyName(); $db = $model->getDbo(); $query = $db->getQuery(true) ->update($db->qn($model->getTableName())) ->set($db->qn($assetFieldAlias) . ' = ' . (int) $model->$assetFieldAlias) ->where($db->qn($k) . ' = ' . (int) $model->$k); $db->setQuery($query)->execute(); } return true; } public function onAfterBind(DataModel &$model, &$src) { if (!$model->isAssetsTracked()) { return true; } $rawRules = []; if (is_array($src) && array_key_exists('rules', $src) && is_array($src['rules'])) { $rawRules = $src['rules']; } elseif (is_object($src) && isset($src->rules) && is_array($src->rules)) { $rawRules = $src->rules; } if (empty($rawRules)) { return true; } // Bind the rules. if (isset($rawRules) && is_array($rawRules)) { // We have to manually remove any empty value, since they will be converted to int, // and "Inherited" values will become "Denied". Joomla is doing this manually, too. $rules = []; foreach ($rawRules as $action => $ids) { // Build the rules array. $rules[$action] = []; foreach ($ids as $id => $p) { if ($p !== '') { $rules[$action][$id] = $p == '1' || $p == 'true'; } } } $model->setRules($rules); } return true; } public function onBeforeDelete(DataModel &$model, $oid) { if (!$model->isAssetsTracked()) { return true; } $k = $model->getKeyName(); // If the table is not loaded, let's try to load it with the id if (!$model->$k) { $model->load($oid); } // If I have an invalid assetName I have to stop $name = $model->getAssetName(); // Do NOT touch \Joomla\CMS\Table\Table here -- we are loading the core asset table which is a \Joomla\CMS\Table\Table, not a FOF Table $asset = new Asset(Factory::getDbo()); if ($asset->loadByName($name)) { // Since we are using \Joomla\CMS\Table\Table, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!$asset->delete()) { throw new \Exception($asset->getError()); } // @codeCoverageIgnoreEnd } return true; } }