Файловый менеджер - Редактировать - /home/wuectly/www/03cbe/DataModel.tar
Назад
Relation/BelongsTo.php 0000604 00000004617 15245611445 0010741 0 ustar 00 <?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\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; /** * BelongsTo (reverse 1-to-1 or 1-to-many) relation: this model is a child which belongs to the foreign table * * For example, parentModel is Articles and foreignModel is Users. Each article belongs to one user. One user can have * one or more article. * * Example #2: parentModel is Phones and foreignModel is Users. Each phone belongs to one user. One user can have zero * or one phones. */ class BelongsTo extends HasOne { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable IGNORED * @param string $pivotLocalKey IGNORED * @param string $pivotForeignKey IGNORED */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($localKey)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $this->localKey = $foreignModel->getIdFieldName(); } if (empty($foreignKey)) { if (!isset($foreignModel)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); } $this->foreignKey = $foreignModel->getIdFieldName(); } } /** * This is not supported by the belongsTo relation * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { throw new DataModel\Relation\Exception\NewNotSupported("getNew() is not supported by the belongsTo relation type"); } } Relation/HasMany.php 0000604 00000011631 15245611445 0010377 0 ustar 00 <?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\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Relation; /** * HasMany (1-to-many) relation: this model is a parent which has zero or more children in the foreign table * * For example, parentModel is Users and foreignModel is Articles. Each user has zero or more articles. */ class HasMany extends Relation { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable IGNORED * @param string $pivotLocalKey IGNORED * @param string $pivotForeignKey IGNORED */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($this->localKey)) { $this->localKey = $parentModel->getIdFieldName(); } if (empty($this->foreignKey)) { $this->foreignKey = $this->localKey; } } /** * Returns the count subquery for DataModel's has() and whereHas() methods. * * @param string $tableAlias The alias of the local table in the query. Leave blank to use the table's name. * * @return \JDatabaseQuery */ public function getCountSubquery($tableAlias = null) { // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $db = $foreignModel->getDbo(); if (empty($tableAlias)) { $tableAlias = $this->parentModel->getTableName(); } return $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn($foreignModel->getTableName(), 'reltbl')) ->where($db->qn('reltbl') . '.' . $db->qn($foreignModel->getFieldAlias($this->foreignKey)) . ' = ' . $db->qn($tableAlias) . '.' . $db->qn($this->parentModel->getFieldAlias($this->localKey))); } /** * Returns a new item of the foreignModel type, pre-initialised to fulfil this relation * * @return DataModel * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); // Prime the model $foreignModel->setFieldValue($this->foreignKey, $this->parentModel->getFieldValue($this->localKey)); // Make sure we do have a data list if (!($this->data instanceof DataModel\Collection)) { $this->getData(); } // Add the model to the data list $this->data->add($foreignModel); return $this->data->last(); } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param DataModel\Collection $dataCollection If it's an eager loaded relation, the collection of loaded * parent records * * @return boolean Return false to force an empty data collection */ protected function filterForeignModel(DataModel $foreignModel, DataModel\Collection $dataCollection = null) { // Decide how to proceed, based on eager or lazy loading if (is_object($dataCollection)) { // Eager loaded relation if (!empty($dataCollection)) { // Get a list of local keys from the collection $values = []; /** @var $item DataModel */ foreach ($dataCollection as $item) { $v = $item->getFieldValue($this->localKey, null); if (!is_null($v)) { $values[] = $v; } } // Keep only unique values. This double step is required to re-index the array and avoid issues with // Joomla Registry class. See issue #681 $values = array_values(array_unique($values)); // Apply the filter if (!empty($values)) { $foreignModel->where($this->foreignKey, 'in', $values); } else { return false; } } else { return false; } } else { // Lazy loaded relation; get the single local key $localKey = $this->parentModel->getFieldValue($this->localKey, null); if (is_null($localKey) || ($localKey === '')) { return false; } $foreignModel->where($this->foreignKey, '==', $localKey); } return true; } } Relation/Exception/ForeignModelNotFound.php 0000604 00000000454 15245611445 0015025 0 ustar 00 <?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\Relation\Exception; defined('_JEXEC') || die; class ForeignModelNotFound extends \Exception {} Relation/Exception/SaveNotSupported.php 0000604 00000000450 15245611445 0014257 0 ustar 00 <?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\Relation\Exception; defined('_JEXEC') || die; class SaveNotSupported extends \Exception {} Relation/Exception/RelationNotFound.php 0000604 00000000450 15245611445 0014224 0 ustar 00 <?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\Relation\Exception; defined('_JEXEC') || die; class RelationNotFound extends \Exception {} Relation/Exception/RelationTypeNotFound.php 0000604 00000000454 15245611445 0015072 0 ustar 00 <?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\Relation\Exception; defined('_JEXEC') || die; class RelationTypeNotFound extends \Exception {} Relation/Exception/PivotTableNotFound.php 0000604 00000000452 15245611445 0014522 0 ustar 00 <?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\Relation\Exception; defined('_JEXEC') || die; class PivotTableNotFound extends \Exception {} Relation/Exception/NewNotSupported.php 0000604 00000000450 15245611445 0014112 0 ustar 00 <?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\Relation\Exception; defined('_JEXEC') || die; class NewNotSupported extends \Exception { } Relation/HasOne.php 0000604 00000002551 15245611445 0010215 0 ustar 00 <?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\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Collection; /** * HasOne (straight 1-to-1) relation: this model is a parent which has exactly one child in the foreign table * * For example, parentModel is Users and foreignModel is Phones. Each uses has exactly one Phone. */ class HasOne extends HasMany { /** * Get the relation data. * * If you want to apply additional filtering to the foreign model, use the $callback. It can be any function, * static method, public method or closure with an interface of function(DataModel $foreignModel). You are not * supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do: * $foreignModel->setState('foo', 'bar') * * @param callable $callback The callback to run on the remote model. * @param Collection $dataCollection * * @return Collection|DataModel */ public function getData($callback = null, Collection $dataCollection = null) { if (is_null($dataCollection)) { return parent::getData($callback, $dataCollection)->first(); } else { return parent::getData($callback, $dataCollection); } } } Relation/BelongsToMany.php 0000604 00000027161 15245611445 0011565 0 ustar 00 <?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\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Relation; /** * BelongsToMany (many-to-many) relation: one or more records of this model are related to one or more records in the * foreign model. * * For example, parentModel is Users and foreignModel is Groups. Each user can be assigned to many groups. Each group * can be assigned to many users. */ class BelongsToMany extends Relation { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local * key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign * key * * @throws DataModel\Relation\Exception\PivotTableNotFound */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($localKey)) { $this->localKey = $parentModel->getIdFieldName(); } if (empty($pivotLocalKey)) { $this->pivotLocalKey = $this->localKey; } if (empty($foreignKey)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $this->foreignKey = $foreignModel->getIdFieldName(); } if (empty($pivotForeignKey)) { $this->pivotForeignKey = $this->foreignKey; } if (empty($pivotTable)) { // Get the local model's name (e.g. "users") $localName = $parentModel->getName(); $localName = strtolower($localName); // Get the foreign model's name (e.g. "groups") if (!isset($foreignModel)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); } $foreignName = $foreignModel->getName(); $foreignName = strtolower($foreignName); // Get the local model's app name $parentModelBareComponent = $parentModel->getContainer()->bareComponentName; $foreignModelBareComponent = $foreignModel->getContainer()->bareComponentName; // There are two possibilities for the table name: #__component_local_foreign or #__component_foreign_local. // There are also two possibilities for a component name (local or foreign model's) $db = $parentModel->getDbo(); $prefix = $db->getPrefix(); $tableNames = [ '#__' . strtolower($parentModelBareComponent) . '_' . $localName . '_' . $foreignName, '#__' . strtolower($parentModelBareComponent) . '_' . $foreignName . '_' . $localName, '#__' . strtolower($foreignModelBareComponent) . '_' . $localName . '_' . $foreignName, '#__' . strtolower($foreignModelBareComponent) . '_' . $foreignName . '_' . $localName, ]; $allTables = $db->getTableList(); $this->pivotTable = null; foreach ($tableNames as $tableName) { $checkName = $prefix . substr($tableName, 3); if (in_array($checkName, $allTables)) { $this->pivotTable = $tableName; } } if (empty($this->pivotTable)) { throw new DataModel\Relation\Exception\PivotTableNotFound("Pivot table for many-to-many relation between '$localName and '$foreignName' not found'"); } } } /** * Populates the internal $this->data collection from the contents of the provided collection. This is used by * DataModel to push the eager loaded data into each item's relation. * * @param DataModel\Collection $data The relation data to push into this relation * @param mixed $keyMap Passes around the local to foreign key map * * @return void */ public function setDataFromCollection(DataModel\Collection &$data, $keyMap = null) { $this->data = new DataModel\Collection(); if (!is_array($keyMap)) { return; } if (!empty($data)) { // Get the local key value $localKeyValue = $this->parentModel->getFieldValue($this->localKey); // Make sure this local key exists in the (cached) pivot table if (!isset($keyMap[$localKeyValue])) { return; } /** @var DataModel $item */ foreach ($data as $item) { // Only accept foreign items whose key is associated in the pivot table with our local key if (in_array($item->getFieldValue($this->foreignKey), $keyMap[$localKeyValue])) { $this->data->add($item); } } } } /** * Returns the count subquery for DataModel's has() and whereHas() methods. * * @param string $tableAlias The alias of the local table in the query. Leave blank to use the table's name. * * @return \JDatabaseQuery */ public function getCountSubquery($tableAlias = null) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $db = $foreignModel->getDbo(); if (empty($tableAlias)) { $tableAlias = $this->parentModel->getTableName(); } return $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn($foreignModel->getTableName()) . ' AS ' . $db->qn('reltbl')) ->innerJoin( $db->qn($this->pivotTable) . ' AS ' . $db->qn('pivotTable') . ' ON(' . $db->qn('pivotTable') . '.' . $db->qn($this->pivotForeignKey) . ' = ' . $db->qn('reltbl') . '.' . $db->qn($foreignModel->getFieldAlias($this->foreignKey)) . ')' ) ->where( $db->qn('pivotTable') . '.' . $db->qn($this->pivotLocalKey) . ' =' . $db->qn($tableAlias) . '.' . $db->qn($this->parentModel->getFieldAlias($this->localKey)) ); } /** * Saves all related items. For many-to-many relations there are two things we have to do: * 1. Save all related items; and * 2. Overwrite the pivot table data with the new associations */ public function saveAll() { // Save all related items parent::saveAll(); $this->saveRelations(); } /** * Overwrite the pivot table data with the new associations */ public function saveRelations() { // Get all the new keys $newKeys = []; if ($this->data instanceof DataModel\Collection) { foreach ($this->data as $item) { if ($item instanceof DataModel) { $newKeys[] = $item->getId(); } elseif (!is_object($item)) { $newKeys[] = $item; } } } $newKeys = array_unique($newKeys); $db = $this->parentModel->getDbo(); $localKeyValue = $this->parentModel->getFieldValue($this->localKey); // Kill all existing relations in the pivot table $query = $db->getQuery(true) ->delete($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' = ' . $db->q($localKeyValue)); $db->setQuery($query); $db->execute(); // Write the new relations to the database $protoQuery = $db->getQuery(true) ->insert($db->qn($this->pivotTable)) ->columns([$db->qn($this->pivotLocalKey), $db->qn($this->pivotForeignKey)]); $i = 0; $query = null; foreach ($newKeys as $key) { $i++; if (is_null($query)) { $query = clone $protoQuery; } $query->values($db->q($localKeyValue) . ', ' . $db->q($key)); if (($i % 50) == 0) { $db->setQuery($query); $db->execute(); $query = null; } } if (!is_null($query)) { $db->setQuery($query); $db->execute(); } } /** * This is not supported by the belongsTo relation * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { throw new DataModel\Relation\Exception\NewNotSupported("getNew() is not supported for many-to-may relations. Please add/remove items from the relation data and use push() to effect changes."); } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param DataModel\Collection $dataCollection If it's an eager loaded relation, the collection of loaded * parent records * * @return boolean Return false to force an empty data collection */ protected function filterForeignModel(DataModel $foreignModel, DataModel\Collection $dataCollection = null) { $db = $this->parentModel->getDbo(); // Decide how to proceed, based on eager or lazy loading if (is_object($dataCollection)) { // Eager loaded relation if (!empty($dataCollection)) { // Get a list of local keys from the collection $values = []; /** @var $item DataModel */ foreach ($dataCollection as $item) { $v = $item->getFieldValue($this->localKey, null); if (!is_null($v)) { $values[] = $v; } } // Keep only unique values $values = array_unique($values); $values = array_map(function ($x) use (&$db) { return $db->q($x); }, $values); // Get the foreign keys from the glue table $query = $db->getQuery(true) ->select([$db->qn($this->pivotLocalKey), $db->qn($this->pivotForeignKey)]) ->from($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' IN(' . implode(',', $values) . ')'); $db->setQuery($query); $foreignKeysUnmapped = $db->loadRowList(); $this->foreignKeyMap = []; $foreignKeys = []; foreach ($foreignKeysUnmapped as $unmapped) { $local = $unmapped[0]; $foreign = $unmapped[1]; if (!isset($this->foreignKeyMap[$local])) { $this->foreignKeyMap[$local] = []; } $this->foreignKeyMap[$local][] = $foreign; $foreignKeys[] = $foreign; } // Keep only unique values. However, the array keys are all screwed up. See below. $foreignKeys = array_unique($foreignKeys); // This looks stupid, but it's required to reset the array keys. Without it where() below fails. $foreignKeys = array_merge($foreignKeys); // Apply the filter if (!empty($foreignKeys)) { $foreignModel->where($this->foreignKey, 'in', $foreignKeys); } else { return false; } } else { return false; } } else { // Lazy loaded relation; get the single local key $localKey = $this->parentModel->getFieldValue($this->localKey, null); if (is_null($localKey) || ($localKey === '')) { return false; } $query = $db->getQuery(true) ->select($db->qn($this->pivotForeignKey)) ->from($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' = ' . $db->q($localKey)); $db->setQuery($query); $foreignKeys = $db->loadColumn(); $this->foreignKeyMap[$localKey] = $foreignKeys; // If there are no foreign keys (no foreign items assigned to our item) we return false which then causes // the relation to return null, marking the lack of data. if (empty($foreignKeys)) { return false; } $foreignModel->where($this->foreignKey, 'in', $this->foreignKeyMap[$localKey]); } return true; } } Collection.php 0000604 00000015506 15245611445 0007362 0 ustar 00 <?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; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Utils\Collection as BaseCollection; /** * A collection of data models. You can enumerate it like an array, use it everywhere a collection is expected (e.g. a * foreach loop) and even implements a countable interface. You can also batch-apply DataModel methods on it thanks to * its magic __call() method, hence the type-hinting below. * * @method void setFieldValue(string $name, mixed $value = '') * @method void archive() * @method void save(mixed $data, string $orderingFilter = '', bool $ignore = null) * @method void push(mixed $data, string $orderingFilter = '', bool $ignore = null, array $relations = null) * @method void bind(mixed $data, array $ignore = []) * @method void check() * @method void reorder(string $where = '') * @method void delete(mixed $id = null) * @method void trash(mixed $id) * @method void forceDelete(mixed $id = null) * @method void lock(int $userId = null) * @method void move(int $delta, string $where = '') * @method void publish() * @method void restore(mixed $id) * @method void touch(int $userId = null) * @method void unlock() * @method void unpublish() */ class Collection extends BaseCollection { /** * Find a model in the collection by key. * * @param mixed $key * @param mixed $default * * @return DataModel */ public function find($key, $default = null) { if ($key instanceof DataModel) { $key = $key->getId(); } return array_first($this->items, function ($itemKey, $model) use ($key) { /** @var DataModel $model */ return $model->getId() == $key; }, $default); } /** * Remove an item in the collection by key * * @param mixed $key * * @return void */ public function removeById($key) { if ($key instanceof DataModel) { $key = $key->getId(); } $index = array_search($key, $this->modelKeys()); if ($index !== false) { unset($this->items[$index]); } } /** * Add an item to the collection. * * @param mixed $item * * @return Collection */ public function add($item) { $this->items[] = $item; return $this; } /** * Determine if a key exists in the collection. * * @param mixed $key * * @return bool */ public function contains($key) { return !is_null($this->find($key)); } /** * Fetch a nested element of the collection. * * @param string $key * * @return Collection */ public function fetch(string $key): BaseCollection { return new static(array_fetch($this->toArray(), $key)); } /** * Get the max value of a given key. * * @param string $key * * @return mixed */ public function max($key) { return $this->reduce(function ($result, $item) use ($key) { return (is_null($result) || $item->{$key} > $result) ? $item->{$key} : $result; }); } /** * Get the min value of a given key. * * @param string $key * * @return mixed */ public function min($key) { return $this->reduce(function ($result, $item) use ($key) { return (is_null($result) || $item->{$key} < $result) ? $item->{$key} : $result; }); } /** * Get the array of primary keys * * @return array */ public function modelKeys() { return array_map( function ($m) { /** @var DataModel $m */ return $m->getId(); }, $this->items); } /** * Merge the collection with the given items. * * @param BaseCollection|array $collection * * @return BaseCollection */ public function merge($collection): BaseCollection { $dictionary = $this->getDictionary($this); foreach ($collection as $item) { $dictionary[$item->getId()] = $item; } return new static(array_values($dictionary)); } /** * Diff the collection with the given items. * * @param BaseCollection|array $collection * * @return BaseCollection */ public function diff($collection): BaseCollection { $diff = new static; $dictionary = $this->getDictionary($collection); foreach ($this->items as $item) { /** @var DataModel $item */ if (!isset($dictionary[$item->getId()])) { $diff->add($item); } } return $diff; } /** * Intersect the collection with the given items. * * @param BaseCollection|array $collection * * @return Collection */ public function intersect($collection): BaseCollection { $intersect = new static; $dictionary = $this->getDictionary($collection); foreach ($this->items as $item) { /** @var DataModel $item */ if (isset($dictionary[$item->getId()])) { $intersect->add($item); } } return $intersect; } /** * Return only unique items from the collection. * * @return BaseCollection */ public function unique(): BaseCollection { $dictionary = $this->getDictionary($this); return new static(array_values($dictionary)); } /** * Get a base Support collection instance from this collection. * * @return BaseCollection */ public function toBase() { return new BaseCollection($this->items); } /** * Magic method which allows you to run a DataModel method to all items in the collection. * * For example, you can do $collection->save('foobar' => 1) to update the 'foobar' column to 1 across all items in * the collection. * * IMPORTANT: The return value of the method call is not returned back to you! * * @param string $name The method to call * @param array $arguments The arguments to the method */ public function __call($name, $arguments) { if (count($this) === 0) { return; } $class = get_class($this->first()); if (method_exists($class, $name)) { foreach ($this as $item) { switch (count($arguments)) { case 0: $item->$name(); break; case 1: $item->$name($arguments[0]); break; case 2: $item->$name($arguments[0], $arguments[1]); break; case 3: $item->$name($arguments[0], $arguments[1], $arguments[2]); break; case 4: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; case 6: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); break; default: call_user_func_array([$item, $name], $arguments); break; } } } } /** * Get a dictionary keyed by primary keys. * * @param BaseCollection $collection * * @return array */ protected function getDictionary($collection) { $dictionary = []; foreach ($collection as $value) { $dictionary[$value->getId()] = $value; } return $dictionary; } } Behaviour/RelationFilters.php 0000604 00000004360 15245611445 0012315 0 ustar 00 <?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 JDatabaseQuery; use Joomla\Registry\Registry; class RelationFilters extends Observer { /** * This event runs after we have built the query used to fetch a record list in a model. It is used to apply * automatic query filters based on model relations. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { $relationFilters = $model->getRelationFilters(); foreach ($relationFilters as $filterState) { $relationName = $filterState['relation']; $tableAlias = $model->getBehaviorParam('tableAlias', null); $subQuery = $model->getRelations()->getCountSubquery($relationName, $tableAlias); // Callback method needs different handling if (isset($filterState['method']) && ($filterState['method'] == 'callback')) { call_user_func_array($filterState['value'], array(&$subQuery)); $filterState['method'] = 'search'; $filterState['operator'] = '>='; $filterState['value'] = '1'; } $options = new Registry($filterState); $filter = new DataModel\Filter\Relation($model->getDbo(), $relationName, $subQuery); $methods = $filter->getSearchMethods(); $method = $options->get('method', $filter->getDefaultSearchMethod()); if (!in_array($method, $methods)) { $method = 'exact'; } switch ($method) { case 'between': case 'outside': $sql = $filter->$method($options->get('from', null), $options->get('to')); break; case 'interval': $sql = $filter->$method($options->get('value', null), $options->get('interval')); break; case 'search': $sql = $filter->$method($options->get('value', null), $options->get('operator', '=')); break; default: $sql = $filter->$method($options->get('value', null)); break; } if ($sql) { $query->where($sql); } } } } Behaviour/Enabled.php 0000604 00000003342 15245611445 0010540 0 ustar 00 <?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 JDatabaseQuery; /** * FOF model behavior class to filter access to items based on the enabled status * * @since 2.1 */ class Enabled extends Observer { /** * This event runs before we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onBeforeBuildQuery(DataModel &$model, JDatabaseQuery &$query) { // Make sure the field actually exists if (!$model->hasField('enabled')) { return; } $fieldName = $model->getFieldAlias('enabled'); $db = $model->getDbo(); $model->whereRaw($db->qn($fieldName) . ' = ' . $db->q(1)); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('enabled')) { return; } // Filter by enabled status if (!$model->getFieldValue('enabled', 0)) { $model->reset(true); } } } Behaviour/EmptyNonZero.php 0000604 00000001636 15245611445 0011623 0 ustar 00 <?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 JDatabaseQuery; /** * FOF model behavior class to let the Filters behaviour know that zero value is a valid filter value * * @since 2.1 */ class EmptyNonZero extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { $model->setBehaviorParam('filterZero', 1); } } Behaviour/Language.php 0000604 00000011131 15245611445 0010724 0 ustar 00 <?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 JDatabaseQuery; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Registry\Registry; /** * FOF model behavior class to filter front-end access to items * based on the language. * * @since 2.1 */ class Language extends Observer { /** @var \PlgSystemLanguageFilter */ protected $lang_filter_plugin; /** * This event runs before we have built the query used to fetch a record * list in a model. It is used to blacklist the language filter * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The model which calls this event * * @return void * @noinspection PhpUnusedParameterInspection */ public function onBeforeBuildQuery(DataModel &$model, JDatabaseQuery &$query) { if ($model->getContainer()->platform->isFrontend()) { $model->blacklistFilters('language'); } // Make sure the field actually exists AND we're not in CLI if (!$model->hasField('language') || $model->getContainer()->platform->isCli()) { return; } /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); $hasLanguageFilter = method_exists($app, 'getLanguageFilter'); if ($hasLanguageFilter) { $hasLanguageFilter = $app->getLanguageFilter(); } if (!$hasLanguageFilter) { return; } // Ask Joomla for the plugin only if we don't already have it. Useful for tests if(!$this->lang_filter_plugin) { $this->lang_filter_plugin = PluginHelper::getPlugin('system', 'languagefilter'); } $lang_filter_params = new Registry($this->lang_filter_plugin->params); $languages = array('*'); if ($lang_filter_params->get('remove_default_prefix')) { // Get default site language $platform = $model->getContainer()->platform; $lg = $platform->getLanguage(); $languages[] = $lg->getTag(); } else { // We have to use JoomlaInput since the language fragment is not set in the $_REQUEST, thus we won't have it in our model // TODO Double check the previous assumption $languages[] = JoomlaFactory::getApplication()->input->getCmd('language', '*'); } // Filter out double languages $languages = array_unique($languages); // And filter the query output by these languages $db = $model->getDbo(); $languages = array_map(array($db, 'quote'), $languages); $fieldName = $model->getFieldAlias('language'); $model->whereRaw($db->qn($fieldName) . ' IN(' . implode(', ', $languages) . ')'); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists AND we're not in CLI if (!$model->hasField('language') || $model->getContainer()->platform->isCli()) { return; } // Make sure it is a multilingual site and get a list of languages /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); $hasLanguageFilter = method_exists($app, 'getLanguageFilter'); if ($hasLanguageFilter) { $hasLanguageFilter = $app->getLanguageFilter(); } if (!$hasLanguageFilter) { return; } // Ask Joomla for the plugin only if we don't already have it. Useful for tests if(!$this->lang_filter_plugin) { $this->lang_filter_plugin = PluginHelper::getPlugin('system', 'languagefilter'); } $lang_filter_params = new Registry($this->lang_filter_plugin->params); $languages = array('*'); if ($lang_filter_params->get('remove_default_prefix')) { // Get default site language $lg = $model->getContainer()->platform->getLanguage(); $languages[] = $lg->getTag(); } else { $languages[] = JoomlaFactory::getApplication()->input->getCmd('language', '*'); } // Filter out double languages $languages = array_unique($languages); // Filter by language if (!in_array($model->getFieldValue('language'), $languages)) { $model->reset(); } } } Behaviour/Created.php 0000604 00000004050 15245611445 0010552 0 ustar 00 <?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; /** * FOF model behavior class to updated the created_by and created_on fields on newly created records. * * This behaviour is added to DataModel by default. If you want to remove it you need to do * $this->behavioursDispatcher->removeBehaviour('Created'); * * @since 3.0 */ class Created extends Observer { /** * Add the created_on and created_by fields in the fieldsSkipChecks list of the model. We expect them to be empty * so that we can fill them in through this behaviour. * * @param DataModel $model */ public function onBeforeCheck(DataModel &$model) { $model->addSkipCheckField('created_on'); $model->addSkipCheckField('created_by'); } /** * @param DataModel $model * @param \stdClass $dataObject */ public function onBeforeCreate(DataModel &$model, &$dataObject) { // Handle the created_on field if ($model->hasField('created_on')) { $nullDate = $model->isNullableField('created_on') ? null : $model->getDbo()->getNullDate(); $created_on = $model->getFieldValue('created_on'); if (empty($created_on) || ($created_on == $nullDate)) { $model->setFieldValue('created_on', $model->getContainer()->platform->getDate()->toSql(false, $model->getDbo())); $createdOnField = $model->getFieldAlias('created_on'); $dataObject->$createdOnField = $model->getFieldValue('created_on'); } } // Handle the created_by field if ($model->hasField('created_by')) { $created_by = $model->getFieldValue('created_by'); if (empty($created_by)) { $model->setFieldValue('created_by', $model->getContainer()->platform->getUser()->id); $createdByField = $model->getFieldAlias('created_by'); $dataObject->$createdByField = $model->getFieldValue('created_by'); } } } } Behaviour/Filters.php 0000604 00000006621 15245611445 0010621 0 ustar 00 <?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 JDatabaseQuery; use Joomla\Registry\Registry; class Filters extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { $tableKey = $model->getIdFieldName(); $db = $model->getDbo(); $fields = $model->getTableFields(); $blacklist = $model->getBlacklistFilters(); $filterZero = $model->getBehaviorParam('filterZero', null); $tableAlias = $model->getBehaviorParam('tableAlias', null); foreach ($fields as $fieldname => $fieldmeta) { if (in_array($fieldname, $blacklist)) { continue; } $fieldInfo = (object)array( 'name' => $fieldname, 'type' => $fieldmeta->Type, 'filterZero' => $filterZero, 'tableAlias' => $tableAlias, ); $filterName = $fieldInfo->name; $filterState = $model->getState($filterName, null); // Special primary key handling: if ignore request is set we'll also look for an 'id' state variable if a // state variable by the same name as the key doesn't exist. If ignore request is not set in the model we // do not filter by 'id' since this interferes with going from an edit page to a browse page (the list is // filtered by id without user controls to unset it). if ($fieldInfo->name == $tableKey) { $filterState = $model->getState($filterName, null); if (!$model->getIgnoreRequest()) { continue; } if (empty($filterState)) { $filterState = $model->getState('id', null); } } $field = DataModel\Filter\AbstractFilter::getField($fieldInfo, array('dbo' => $db)); if (!is_object($field) || !($field instanceof DataModel\Filter\AbstractFilter)) { continue; } if ((is_array($filterState) && ( array_key_exists('value', $filterState) || array_key_exists('from', $filterState) || array_key_exists('to', $filterState) )) || is_object($filterState)) { $options = new Registry($filterState); } else { $options = new Registry(); $options->set('value', $filterState); } $methods = $field->getSearchMethods(); $method = $options->get('method', $field->getDefaultSearchMethod()); if (!in_array($method, $methods)) { $method = 'exact'; } switch ($method) { case 'between': case 'outside': case 'range' : $sql = $field->$method($options->get('from', null), $options->get('to', null), $options->get('include', false)); break; case 'interval': case 'modulo': $sql = $field->$method($options->get('value', null), $options->get('interval')); break; case 'search': $sql = $field->$method($options->get('value', null), $options->get('operator', '=')); break; case 'exact': case 'partial': default: $sql = $field->$method($options->get('value', null)); break; } if ($sql) { $query->where($sql); } } } } Behaviour/Own.php 0000604 00000004012 15245611445 0007744 0 ustar 00 <?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 JDatabaseQuery; /** * FOF model behavior class to filter access to items owned by the currently logged in user only * * @since 2.1 */ class Own extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { // Make sure the field actually exists if (!$model->hasField('created_by')) { return; } // Get the current user's id $user_id = $model->getContainer()->platform->getUser()->id; // And filter the query output by the user id $db = $model->getContainer()->platform->getDbo(); $query->where($db->qn($model->getFieldAlias('created_by')) . ' = ' . $db->q($user_id)); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('created_by')) { return; } // Get the user $user_id = $model->getContainer()->platform->getUser()->id; $recordUser = $model->getFieldValue('created_by', null); // Filter by authorised access levels if ($recordUser != $user_id) { $model->reset(true); } } } Behaviour/Modified.php 0000604 00000003623 15245611445 0010730 0 ustar 00 <?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 JDatabaseQuery; /** * FOF model behavior class to updated the modified_by and modified_on fields on newly created records. * * This behaviour is added to DataModel by default. If you want to remove it you need to do * $this->behavioursDispatcher->removeBehaviour('Modified'); * * @since 3.0 */ class Modified extends Observer { /** * Add the modified_on and modified_by fields in the fieldsSkipChecks list of the model. We expect them to be empty * so that we can fill them in through this behaviour. * * @param DataModel $model */ public function onBeforeCheck(DataModel &$model) { $model->addSkipCheckField('modified_on'); $model->addSkipCheckField('modified_by'); } /** * @param DataModel $model * @param \stdClass $dataObject */ public function onBeforeUpdate(DataModel &$model, &$dataObject) { // Make sure we're not modifying a locked record $userId = $model->getContainer()->platform->getUser()->id; $isLocked = $model->isLocked($userId); if ($isLocked) { return; } // Handle the modified_on field if ($model->hasField('modified_on')) { $model->setFieldValue('modified_on', $model->getContainer()->platform->getDate()->toSql(false, $model->getDbo())); $modifiedOnField = $model->getFieldAlias('modified_on'); $dataObject->$modifiedOnField = $model->getFieldValue('modified_on'); } // Handle the modified_by field if ($model->hasField('modified_by')) { $model->setFieldValue('modified_by', $userId); $modifiedByField = $model->getFieldAlias('modified_by'); $dataObject->$modifiedByField = $model->getFieldValue('modified_by'); } } } Behaviour/Assets.php 0000604 00000011362 15245611445 0010451 0 ustar 00 <?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; } } Behaviour/PageParametersToState.php 0000604 00000003324 15245611445 0013412 0 ustar 00 <?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\Application\SiteApplication; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\Registry\Registry; /** * FOF model behavior class to populate the state with the front-end page parameters * * @since 2.1 */ class PageParametersToState extends Observer { public function onAfterConstruct(DataModel &$model) { // This only applies to the front-end if (!$model->getContainer()->platform->isFrontend()) { return; } // Get the page parameters /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); /** @var Registry $params */ $params = $app->getParams(); // Extract the page parameter keys $asArray = []; if (is_object($params) && method_exists($params, 'toArray')) { $asArray = $params->toArray(); } if (empty($asArray)) { // There are no keys; no point in going on. return; } $keys = array_keys($asArray); unset($asArray); // Loop all page parameter keys foreach ($keys as $key) { // This is the current model state $currentState = $model->getState($key); // This is the explicitly requested state in the input $explicitInput = $model->input->get($key, null, 'raw'); // If the current state is empty and there's no explicit input we'll use the page parameters instead if (!is_null($currentState)) { return; } if (!is_null($explicitInput)) { return; } $model->setState($key, $params->get($key)); } } } Behaviour/Tags.php 0000604 00000010100 15245611445 0010072 0 ustar 00 <?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\Observable; use FOF40\Event\Observer; use FOF40\Model\DataModel; use Joomla\CMS\Helper\TagsHelper; /** * FOF model behavior class to add Joomla! Tags support * * @since 2.1 */ class Tags extends Observer { /** @var TagsHelper */ protected $tagsHelper; public function __construct(Observable &$subject) { parent::__construct($subject); $this->tagsHelper = new TagsHelper(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param \stdClass &$dataObject The data to bind to the form * * @return void */ public function onBeforeCreate(DataModel &$model, &$dataObject) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); unset($dataObject->$tagField); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param \stdClass &$dataObject The data to bind to the form * * @return void */ public function onBeforeUpdate(DataModel &$model, &$dataObject) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); unset($dataObject->$tagField); } /** * The event which runs after binding data to the table * * @param DataModel &$model The model which calls this event * * @return void * * @throws \Exception Error message if failed to store tags */ public function onAfterSave(DataModel &$model) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); // Avoid to update on other method (e.g. publish, ...) if (!in_array($model->getContainer()->input->getCmd('task'), ['apply', 'save', 'savenew'])) { return; } $oldTags = $this->tagsHelper->getTagIds($model->getId(), $model->getContentType()); $newTags = $model->$tagField ? implode(',', $model->$tagField) : null; // If no changes, we stop here if ($oldTags == $newTags) { return; } // Check if the content type exists, and create it if it does not $model->checkContentType(); $this->tagsHelper->typeAlias = $model->getContentType(); if (!$this->tagsHelper->postStoreProcess($model, $model->$tagField)) { throw new \Exception('Error storing tags'); } } /** * The event which runs after deleting a record * * @param DataModel &$model The model which calls this event * @param integer $oid The PK value of the record which was deleted * * @return void * * @throws \Exception Error message if failed to detele tags */ public function onAfterDelete(DataModel &$model, $oid) { $this->tagsHelper->typeAlias = $model->getContentType(); if (!$this->tagsHelper->deleteTagData($model, $oid)) { throw new \Exception('Error deleting Tags'); } } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param mixed $data An associative array or object to bind to the DataModel instance. * * @return void * @noinspection PhpUnusedParameterInspection */ public function onAfterBind(DataModel &$model, &$data) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); if ($model->$tagField) { return; } $type = $model->getContentType(); $model->addKnownField($tagField); $model->$tagField = $this->tagsHelper->getTagIds($model->getId(), $type); } /** * This event runs after publishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterPublish(DataModel &$model) { $model->updateUcmContent(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterUnpublish(DataModel &$model) { $model->updateUcmContent(); } } Behaviour/ContentHistory.php 0000604 00000004244 15245611445 0012204 0 ustar 00 <?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 ContenthistoryHelper; use FOF40\Event\Observer; use FOF40\Model\DataModel; /** * FOF model behavior class to add Joomla! content history support * * @since 2.1 */ class ContentHistory extends Observer { /** @var ContentHistoryHelper */ protected $historyHelper; /** * The event which runs after storing (saving) data to the database * * @param DataModel &$model The model which calls this event * * @return boolean True to allow saving without an error */ public function onAfterSave(DataModel &$model) { $model->checkContentType(); $componentParams = $model->getContainer()->params; if ($componentParams->get('save_history', 0)) { if (!$this->historyHelper) { $this->historyHelper = new ContentHistoryHelper($model->getContentType()); } $this->historyHelper->store($model); } return true; } /** * The event which runs before deleting a record * * @param DataModel &$model The model which calls this event * @param integer $oid The PK value of the record to delete * * @return boolean True to allow the deletion */ public function onBeforeDelete(DataModel &$model, $oid) { $componentParams = $model->getContainer()->params; if ($componentParams->get('save_history', 0)) { if (!$this->historyHelper) { $this->historyHelper = new ContentHistoryHelper($model->getContentType()); } $this->historyHelper->deleteHistory($model); } return true; } /** * This event runs after publishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterPublish(DataModel &$model) { $model->updateUcmContent(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterUnpublish(DataModel &$model) { $model->updateUcmContent(); } } Behaviour/Access.php 0000604 00000003447 15245611445 0010415 0 ustar 00 <?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 JDatabaseQuery; /** * FOF model behavior class to filter access to items based on the viewing access levels. * * @since 2.1 */ class Access extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { // Make sure the field actually exists if (!$model->hasField('access')) { return; } $model->applyAccessFiltering(null); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('access')) { return; } // Get the user $user = $model->getContainer()->platform->getUser(); $recordAccessLevel = $model->getFieldValue('access', null); // Filter by authorised access levels if (!in_array($recordAccessLevel, $user->getAuthorisedViewLevels())) { $model->reset(true); } } } RelationManager.php 0000604 00000032762 15245611445 0010342 0 ustar 00 <?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; defined('_JEXEC') || die; use FOF40\Model\DataModel; class RelationManager { /** @var array The known relation types */ protected static $relationTypes = []; /** @var DataModel The data model we are attached to */ protected $parentModel; /** @var Relation[] The relations known to us */ protected $relations = []; /** @var array A list of the names of eager loaded relations */ protected $eager = []; /** * Creates a new relation manager for the defined parent model * * @param DataModel $parentModel The model we are attached to */ public function __construct(DataModel $parentModel) { // Set the parent model $this->parentModel = $parentModel; // Make sure the relation types are initialised static::getRelationTypes(); // @todo Maybe set up a few relations automatically? } /** * Populates the static map of relation type methods and relation handling classes * * @return array Key = method name, Value = relation handling class */ public static function getRelationTypes() { if (empty(static::$relationTypes)) { $relationTypeDirectory = __DIR__ . '/Relation'; $fs = new \DirectoryIterator($relationTypeDirectory); /** @var $file \DirectoryIterator */ foreach ($fs as $file) { if ($file->isDir()) { continue; } if ($file->getExtension() != 'php') { continue; } $baseName = ucfirst($file->getBasename('.php')); $methodName = strtolower($baseName[0]) . substr($baseName, 1); $className = '\\FOF40\\Model\\DataModel\\Relation\\' . $baseName; if (!class_exists($className, true)) { continue; } static::$relationTypes[$methodName] = $className; } } return static::$relationTypes; } /** * Implements deep cloning of the relation object */ function __clone() { $relations = []; /** @var Relation[] $relations */ foreach ($this->relations as $key => $relation) { $relations[$key] = clone($relation); $relations[$key]->reset(); } $this->relations = $relations; } /** * Rebase a relation manager * * @param DataModel $parentModel */ public function rebase(DataModel $parentModel) { $this->parentModel = $parentModel; if (count($this->relations) > 0) { foreach ($this->relations as $relation) { /** @var Relation $relation */ $relation->rebase($parentModel); } } } /** * Populates the internal $this->data collection of a relation from the contents of the provided collection. This is * used by DataModel to push the eager loaded data into each item's relation. * * @param string $name Relation name * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void * * @throws Relation\Exception\RelationNotFound */ public function setDataFromCollection($name, Collection &$data, $keyMap = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->setDataFromCollection($data, $keyMap); } /** * Adds a relation to the relation manager * * @param string $name The name of the relation as known to this relation manager, e.g. 'phone' * @param string $type The relation type, e.g. 'hasOne' * @param string $foreignModelName The name of the foreign key's model in the format "modelName@com_something" * @param string $localKey The local table key for this relation * @param string $foreignKey The foreign key for this relation * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign key * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationTypeNotFound when $type is not known * @throws Relation\Exception\ForeignModelNotFound when $foreignModelClass doesn't exist */ public function addRelation($name, $type, $foreignModelName = null, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { if (!isset(static::$relationTypes[$type])) { throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$type' not found"); } // Guess the foreign model class if necessary if (empty($foreignModelName)) { $foreignModelName = ucfirst($name); } $className = static::$relationTypes[$type]; /** @var Relation $relation */ $relation = new $className($this->parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); $this->relations[$name] = $relation; return $this->parentModel; } /** * Removes a known relation * * @param string $name The name of the relation to remove * * @return DataModel The parent model, for chaining */ public function removeRelation($name) { if (isset($this->relations[$name])) { unset ($this->relations[$name]); } return $this->parentModel; } /** * Removes all known relations */ public function resetRelations() { $this->relations = []; } /** * Resets the data of all relations in this manager. This doesn't remove relations, just their data so that they * get loaded again. * * @param array $relationsToReset The names of the relations to reset. Pass an empty array (default) to reset * all relations. */ public function resetRelationData(array $relationsToReset = []) { /** @var Relation $relation */ foreach ($this->relations as $name => $relation) { if (!empty($relationsToReset) && !in_array($name, $relationsToReset)) { continue; } $relation->reset(); } } /** * Returns a list of all known relations' names * * @return array */ public function getRelationNames() { return array_keys($this->relations); } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * * @return Relation * * @throws Relation\Exception\RelationNotFound */ public function &getRelation($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]; } /** * Get a new related item which satisfies relation $name and adds it to this relation's data list. * * @param string $name The relation based on which a new item is returned * * @return DataModel * * @throws Relation\Exception\RelationNotFound */ public function getNew($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getNew(); } /** * Saves all related items belonging to the specified relation or, if $name is null, all known relations which * support saving. * * @param null|string $name The relation to save, or null to save all known relations * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationNotFound */ public function save($name = null) { if (is_null($name)) { foreach ($this->relations as $relation) { try { $relation->saveAll(); } catch (DataModel\Relation\Exception\SaveNotSupported $e) { // We don't care if a relation doesn't support saving } } } else { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->saveAll(); } return $this->parentModel; } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * @param callable $callback A callback to customise the returned data * @param \FOF40\Utils\Collection $dataCollection Used when fetching the data of an eager loaded relation * * @return Collection|DataModel * * @throws Relation\Exception\RelationNotFound * @see Relation::getData() * */ public function getData($name, $callback = null, \FOF40\Utils\Collection $dataCollection = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getData($callback, $dataCollection); } /** * Gets the foreign key map of a many-to-many relation * * @param string $name The name of the relation to return data for * * @return array * * @throws Relation\Exception\RelationNotFound */ public function &getForeignKeyMap($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getForeignKeyMap(); } /** * Returns the count sub-query for a relation, used for relation filters (whereHas in the DataModel). * * @param string $name The relation to get the sub-query for * @param string $tableAlias The alias to use for the local table * * @return \JDatabaseQuery * @throws Relation\Exception\RelationNotFound */ public function getCountSubquery($name, $tableAlias = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getCountSubquery($tableAlias); } /** * A magic method which allows us to define relations using shorthand notation, e.g. $manager->hasOne('phone') * instead of $manager->addRelation('phone', 'hasOne') * * You can also use it to get data of a relation using shorthand notation, e.g. $manager->getPhone($callback) * instead of $manager->getData('phone', $callback); * * @param string $name The magic method to call * @param array $arguments The arguments to the magic method * * @return DataModel The parent model, for chaining * * @throws \InvalidArgumentException * @throws DataModel\Relation\Exception\RelationTypeNotFound */ function __call($name, $arguments) { $numberOfArguments = count($arguments); if (isset(static::$relationTypes[$name])) { if ($numberOfArguments == 1) { return $this->addRelation($arguments[0], $name); } elseif ($numberOfArguments == 2) { return $this->addRelation($arguments[0], $name, $arguments[1]); } elseif ($numberOfArguments == 3) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2]); } elseif ($numberOfArguments == 4) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3]); } elseif ($numberOfArguments == 5) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4]); } elseif ($numberOfArguments == 6) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); } elseif ($numberOfArguments >= 7) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]); } else { throw new \InvalidArgumentException("You can not create an unnamed '$name' relation"); } } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if ($numberOfArguments == 0) { return $this->getData($relationName); } elseif ($numberOfArguments == 1) { return $this->getData($relationName, $arguments[0]); } elseif ($numberOfArguments == 2) { return $this->getData($relationName, $arguments[0], $arguments[1]); } else { throw new \InvalidArgumentException("Invalid number of arguments getting data for the '$relationName' relation"); } } // Throw an exception otherwise throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$name' not known to relation manager"); } /** * Is $name a magic-callable method? * * @param string $name The name of a potential magic-callable method * * @return bool */ public function isMagicMethod($name) { if (isset(static::$relationTypes[$name])) { return true; } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if (isset($this->relations[$relationName])) { return true; } } return false; } /** * Is $name a magic property? Corollary: returns true if a relation of this name is known to the relation manager. * * @param string $name The name of a potential magic property * * @return bool */ public function isMagicProperty($name) { return isset($this->relations[$name]); } /** * Magic method to get the data of a relation using shorthand notation, e.g. $manager->phone instead of * $manager->getData('phone') * * @param $name * * @return Collection */ function __get($name) { return $this->getData($name); } } Filter/Number.php 0000604 00000015721 15245611445 0007743 0 ustar 00 <?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\Filter; defined('_JEXEC') || die; class Number extends AbstractFilter { /** * The partial match is mapped to an exact match * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function partial($value) { return $this->exact($value); } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function between($from, $to, $include = true) { $from = (float) $from; $to = (float) $to; if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ') AND '; return $sql . ('(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . '))'); } /** * Perform an outside limits match. When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function outside($from, $to, $include = false) { $from = (float) $from; $to = (float) $to; if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' <' . $extra . ' ' . $from . ') OR '; return $sql . ('(' . $this->getFieldName() . ' >' . $extra . ' ' . $to . '))'); } /** * Perform an interval match. It's similar to a 'between' match, but the * from and to values are calculated based on $value and $interval: * $value - $interval < VALUE < $value + $interval * * @param integer|float $value The center value of the search space * @param integer|float $interval The width of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ public function interval($value, $interval, $include = true) { if ($this->isEmpty($value)) { return ''; } // Convert them to float, just to be sure $value = (float) $value; $interval = (float) $interval; $from = $value - $interval; $to = $value + $interval; $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ') AND '; return $sql . ('(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . '))'); } /** * Perform a range limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function range($from, $to, $include = true) { if ($this->isEmpty($from) && $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = []; if ($from) { $sql[] = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ')'; } if ($to) { $sql[] = '(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . ')'; } return '(' . implode(' AND ', $sql) . ')'; } /** * Perform an interval match. It's similar to a 'between' match, but the * from and to values are calculated based on $value and $interval: * $value - $interval < VALUE < $value + $interval * * @param integer|float $value The starting value of the search space * @param integer|float $interval The interval period of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ public function modulo($value, $interval, $include = true) { if ($this->isEmpty($value) || $this->isEmpty($interval)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $value . ' AND '; return $sql . ('(' . $this->getFieldName() . ' - ' . $value . ') % ' . $interval . ' = 0)'); } /** * Overrides the parent to handle floats in locales where the decimal separator is a comma instead of a dot * * @param mixed $value * @param string $operator * * @return string */ public function search($value, $operator = '=') { $value = $this->sanitiseValue($value); return parent::search($value, $operator); } /** * Sanitises float values. Really ugly and desperate workaround. Read below. * * Some locales, such as el-GR, use a comma as the decimal separator. This means that $x = 1.23; echo (string) $x; * will yield 1,23 (with a comma!) instead of 1.23 (with a dot!). This affects the way the SQL WHERE clauses are * generated. All database servers expect a dot as the decimal separator. If they see a decimal with a comma as the * separator they throw a SQL error. * * This method will try to replace commas with dots. I tried working around this with locale switching and the %F * (capital F) format option in sprintf to no avail. I'm pretty sure I was doing something wrong, but I ran out of * time trying to find an academically correct solution. The current implementation of sanitiseValue is a silly * hack around the problem. If you have a proper –and better performing– solution please send in a PR and I'll put * it to the test. * * @param mixed $value A string representing a number, integer, float or array of them. * * @return mixed The sanitised value, or null if the input wasn't numeric. */ public function sanitiseValue($value) { if (!is_numeric($value) && !is_string($value) && !is_array($value)) { $value = null; } if (!is_array($value)) { return str_replace(',', '.', (string) $value); } return array_map([$this, 'sanitiseValue'], $value); } } Filter/Date.php 0000604 00000011677 15245611445 0007376 0 ustar 00 <?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\Filter; defined('_JEXEC') || die; class Date extends Text { /** * Returns the default search method for this field. * * @return string */ public function getDefaultSearchMethod() { return 'exact'; } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function between($from, $to, $include = true) { if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($from) . ') AND '; return $sql . ('(' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($to) . '))'); } /** * Perform an outside limits match. When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function outside($from, $to, $include = false) { if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '((' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($from) . ') AND '; return $sql . ('(' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($to) . '))'); } /** * Interval date search * * @param string $value The value to search * @param string|array|object $interval The interval. Can be (+1 MONTH or array('value' => 1, 'unit' => * 'MONTH', 'sign' => '+')) * @param boolean $include If the borders should be included * * @return string the sql string */ public function interval($value, $interval, $include = true) { if ($this->isEmpty($value) || $this->isEmpty($interval)) { return ''; } $interval = $this->getInterval($interval); // Sanity check on $interval array if (!isset($interval['sign']) || !isset($interval['value']) || !isset($interval['unit'])) { return ''; } $function = $interval['sign'] == '+' ? 'DATE_ADD' : 'DATE_SUB'; $extra = ''; if ($include) { $extra = '='; } $sql = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $function; return $sql . ('(' . $this->getFieldName() . ', INTERVAL ' . $interval['value'] . ' ' . $interval['unit'] . '))'); } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function range($from, $to, $include = true) { if ($this->isEmpty($from) && $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = []; if ($from) { $sql[] = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($from) . ')'; } if ($to) { $sql[] = '(' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($to) . ')'; } return '(' . implode(' AND ', $sql) . ')'; } /** * Parses an interval –which may be given as a string, array or object– into * a standardised hash array that can then be used bu the interval() method. * * @param string|array|object $interval The interval expression to parse * * @return array The parsed, hash array form of the interval */ protected function getInterval($interval) { if (is_string($interval)) { if (strlen($interval) > 2) { $interval = explode(" ", $interval); $sign = ($interval[0] == '-') ? '-' : '+'; $value = (int) substr($interval[0], 1); $interval = [ 'unit' => $interval[1], 'value' => $value, 'sign' => $sign, ]; } else { $interval = [ 'unit' => 'MONTH', 'value' => 1, 'sign' => '+', ]; } } else { $interval = (array) $interval; } return $interval; } } Filter/AbstractFilter.php 0000604 00000023074 15245611445 0011424 0 ustar 00 <?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\Filter; defined('_JEXEC') || die; use FOF40\Model\DataModel\Filter\Exception\InvalidFieldObject; use FOF40\Model\DataModel\Filter\Exception\NoDatabaseObject; abstract class AbstractFilter { /** * The null value for this type * * @var mixed */ public $null_value; protected $db; /** * The column name of the table field * * @var string */ protected $name = ''; /** * The column type of the table field * * @var string */ protected $type = ''; /** * Should I allow filtering against the number 0? * * @var bool */ protected $filterZero = true; /** * Prefix each table name with this table alias. For example, field bar normally creates a WHERE clause: * `bar` = '1' * If tableAlias is set to "foo" then the WHERE clause it generates becomes * `foo`.`bar` = '1' * * @var null */ protected $tableAlias; /** * Constructor * * @param \JDatabaseDriver $db The database object * @param object $field The field information as taken from the db */ public function __construct($db, $field) { $this->db = $db; if (!is_object($field) || !isset($field->name) || !isset($field->type)) { throw new InvalidFieldObject; } $this->name = $field->name; $this->type = $field->type; if (isset ($field->filterZero)) { $this->filterZero = $field->filterZero; } if (isset ($field->tableAlias)) { $this->tableAlias = $field->tableAlias; } } /** * Creates a field Object based on the field column type * * @param object $field The field information * @param array $config The field configuration (like the db object to use) * * @return AbstractFilter The Filter object * * @throws \InvalidArgumentException */ public static function getField($field, $config = []) { if (!is_object($field) || !isset($field->name) || !isset($field->type)) { throw new InvalidFieldObject; } $type = $field->type; $classType = self::getFieldType($type); $className = '\\FOF40\\Model\\DataModel\\Filter\\' . ucfirst($classType); if (($classType !== false) && class_exists($className, true)) { if (!isset($config['dbo'])) { throw new NoDatabaseObject($className); } $db = $config['dbo']; return new $className($db, $field); } return null; } /** * Get the class name based on the field Type * * @param string $type The type of the field * * @return string the class name suffix */ public static function getFieldType($type) { // Remove parentheses, indicating field options / size (they don't matter in type detection) if (!empty($type)) { [$type,] = explode('(', $type); } $detectedType = null; switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'character varying': case 'nvarchar': case 'nchar': $detectedType = 'Text'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'timestamp without time zone': case 'timestamp with time zone': $detectedType = 'Date'; break; case 'tinyint': case 'smallint': $detectedType = 'Boolean'; break; } // Sometimes we have character types followed by a space and some cruft. Let's handle them. if (is_null($detectedType) && !empty($type)) { [$type,] = explode(' ', $type); switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'nvarchar': case 'nchar': $detectedType = 'Text'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': $detectedType = 'Date'; break; case 'tinyint': case 'smallint': $detectedType = 'Boolean'; break; default: $detectedType = 'Number'; break; } } // If all else fails assume it's a Number and hope for the best if (empty($detectedType)) { $detectedType = 'Number'; } return $detectedType; } /** * Is it a null or otherwise empty value? * * @param mixed $value The value to test for emptiness * * @return boolean */ public function isEmpty($value) { return (($value === $this->null_value) || empty($value)) && !($this->filterZero && ($value === "0")); } /** * Returns the default search method for a field. This always returns 'exact' * and you are supposed to override it in specialised classes. The possible * values are exact, partial, between and outside, unless something * different is returned by getSearchMethods(). * * @return string * @see self::getSearchMethods() * */ public function getDefaultSearchMethod() { return 'exact'; } /** * Return the search methods available for this field class, * * @return array */ public function getSearchMethods() { $ignore = [ 'isEmpty', 'getField', 'getFieldType', '__construct', 'getDefaultSearchMethod', 'getSearchMethods', 'getFieldName', ]; $class = new \ReflectionClass(__CLASS__); $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); $tmp = []; foreach ($methods as $method) { $tmp[] = $method->name; } $methods = $tmp; if ($methods = array_diff($methods, $ignore)) { return $methods; } return []; } /** * Perform an exact match (equality matching) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function exact($value) { if ($this->isEmpty($value)) { return ''; } if (is_array($value)) { $db = $this->db; $value = array_map([$db, 'quote'], $value); return '(' . $this->getFieldName() . ' IN (' . implode(',', $value) . '))'; } else { return $this->search($value); } } /** * Perform a partial match (usually: search in string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ abstract public function partial($value); /** * Perform a between limits match (usually: search for a value between * two numbers or a date between two preset dates). When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function between($from, $to, $include = true); /** * Perform an outside limits match (usually: search for a value outside an * area or a date outside a preset period). When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function outside($from, $to, $include = false); /** * Perform an interval search (usually: a date interval check) * * @param string $from The value to search * @param string|array|object $interval The interval * * @return string The SQL where clause for this search */ abstract public function interval($from, $interval); /** * Perform a between limits match (usually: search for a value between * two numbers or a date between two preset dates). When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The higherst value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function range($from, $to, $include = true); /** * Perform an modulo search * * @param integer|float $from The starting value of the search space * @param integer|float $interval The interval period of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ abstract public function modulo($from, $interval, $include = true); /** * Return the SQL where clause for a search * * @param mixed $value The value to search for * @param string $operator The operator to use * * @return string The SQL where clause for this search */ public function search($value, $operator = '=') { if ($this->isEmpty($value)) { return ''; } $prefix = ''; if (substr($operator, 0, 1) == '!') { $prefix = 'NOT '; $operator = substr($operator, 1); } return $prefix . '(' . $this->getFieldName() . ' ' . $operator . ' ' . $this->db->quote($value) . ')'; } /** * Get the field name * * @return string The field name */ public function getFieldName() { $name = $this->db->qn($this->name); if (!empty($this->tableAlias)) { $name = $this->db->qn($this->tableAlias) . '.' . $name; } return $name; } } Filter/Exception/InvalidFieldObject.php 0000604 00000001133 15245611445 0014122 0 ustar 00 <?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\Filter\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class InvalidFieldObject extends \InvalidArgumentException { public function __construct( $message = "", $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_FILTER_INVALIDFIELD'); } parent::__construct( $message, $code, $previous ); } } Filter/Exception/NoDatabaseObject.php 0000604 00000001106 15245611445 0013571 0 ustar 00 <?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\Filter\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoDatabaseObject extends \InvalidArgumentException { public function __construct( $fieldType, $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_FILTER_NODBOBJECT', $fieldType); parent::__construct( $message, $code, $previous ); } } Filter/Text.php 0000604 00000006211 15245611445 0007431 0 ustar 00 <?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\Filter; defined('_JEXEC') || die; class Text extends AbstractFilter { /** * Constructor * * @param \JDatabaseDriver $db The database object * @param object $field The field information as taken from the db */ public function __construct($db, $field) { parent::__construct($db, $field); $this->null_value = ''; } /** * Returns the default search method for this field. * * @return string */ public function getDefaultSearchMethod() { return 'partial'; } /** * Perform a partial match (search in string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function partial($value) { if ($this->isEmpty($value)) { return ''; } return '(' . $this->getFieldName() . ' LIKE ' . $this->db->quote('%' . $value . '%') . ')'; } /** * Perform an exact match (match string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function exact($value) { if ($this->isEmpty($value)) { return ''; } if (is_array($value) || is_object($value)) { $value = (array) $value; $db = $this->db; $value = array_map([$db, 'quote'], $value); return '(' . $this->getFieldName() . ' IN (' . implode(',', $value) . '))'; } return '(' . $this->getFieldName() . ' LIKE ' . $this->db->quote($value) . ')'; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function between($from, $to, $include = true) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function outside($from, $to, $include = false) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $value Ignored * @param mixed $interval Ignored * @param boolean $include Ignored * * @return string Empty string */ public function interval($value, $interval, $include = true) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function range($from, $to, $include = false) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $interval Ignored * @param boolean $include Ignored * * @return string Empty string */ public function modulo($from, $interval, $include = false) { return ''; } } Filter/Boolean.php 0000604 00000000760 15245611445 0010067 0 ustar 00 <?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\Filter; defined('_JEXEC') || die; class Boolean extends Number { /** * Is it a null or otherwise empty value? * * @param mixed $value The value to test for emptiness * * @return bool */ public function isEmpty($value) { return is_null($value) || ($value === ''); } } Filter/Relation.php 0000604 00000001350 15245611445 0010261 0 ustar 00 <?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\Filter; defined('_JEXEC') || die; class Relation extends Number { /** @var \JDatabaseQuery The COUNT subquery to filter by */ protected $subQuery; public function __construct($db, $relationName, $subQuery) { $field = (object)array( 'name' => $relationName, 'type' => 'relation', ); parent::__construct($db, $field); $this->subQuery = $subQuery; } public function callback($value) { return call_user_func($value, $this->subQuery); } public function getFieldName() { return '(' . $this->subQuery . ')'; } } Relation.php 0000604 00000020125 15245611445 0007035 0 ustar 00 <?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; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Model\DataModel; abstract class Relation { /** @var DataModel The data model we are attached to */ protected $parentModel; /** @var string The class name of the foreign key's model */ protected $foreignModelClass; /** @var string The application name of the foreign model */ protected $foreignModelComponent; /** @var string The bade name of the foreign model */ protected $foreignModelName; /** @var string The local table key for this relation */ protected $localKey; /** @var string The foreign table key for this relation */ protected $foreignKey; /** @var null For many-to-many relations, the pivot (glue) table */ protected $pivotTable; /** @var null For many-to-many relations, the pivot table's column storing the local key */ protected $pivotLocalKey; /** @var null For many-to-many relations, the pivot table's column storing the foreign key */ protected $pivotForeignKey; /** @var Collection The data loaded by this relation */ protected $data; /** @var array Maps each local table key to an array of foreign table keys, used in many-to-many relations */ protected $foreignKeyMap = []; /** @var Container The component container for this relation */ protected $container; /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation * @param string $foreignKey The foreign key for this relation * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local * key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign * key */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { $this->parentModel = $parentModel; $this->foreignModelClass = $foreignModelName; $this->localKey = $localKey; $this->foreignKey = $foreignKey; $this->pivotTable = $pivotTable; $this->pivotLocalKey = $pivotLocalKey; $this->pivotForeignKey = $pivotForeignKey; $this->container = $parentModel->getContainer(); $class = $foreignModelName; if (strpos($class, '@') === false) { $this->foreignModelComponent = null; $this->foreignModelName = $class; } else { $foreignParts = explode('@', $class, 2); $this->foreignModelComponent = $foreignParts[1]; $this->foreignModelName = $foreignParts[0]; } } /** * Reset the relation data * * @return $this For chaining */ public function reset() { $this->data = null; $this->foreignKeyMap = []; return $this; } /** * Rebase the relation to a different model * * @param DataModel $model * * @return $this For chaining */ public function rebase(DataModel $model) { $this->parentModel = $model; return $this->reset(); } /** * Get the relation data. * * If you want to apply additional filtering to the foreign model, use the $callback. It can be any function, * static method, public method or closure with an interface of function(DataModel $foreignModel). You are not * supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do: * $foreignModel->setState('foo', 'bar') * * @param callable $callback The callback to run on the remote model. * @param Collection $dataCollection * * @return Collection|DataModel */ public function getData($callback = null, Collection $dataCollection = null) { if (is_null($this->data)) { // Initialise $this->data = new Collection(); // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $filtered = $this->filterForeignModel($foreignModel, $dataCollection); if (!$filtered) { return $this->data; } // Apply the callback, if applicable if (!is_null($callback) && is_callable($callback)) { call_user_func($callback, $foreignModel); } // Get the list of items from the foreign model and cache in $this->data $this->data = $foreignModel->get(true); } return $this->data; } /** * Populates the internal $this->data collection from the contents of the provided collection. This is used by * DataModel to push the eager loaded data into each item's relation. * * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void */ public function setDataFromCollection(Collection &$data, $keyMap = null) { $this->data = new Collection(); if (!empty($data)) { $localKeyValue = $this->parentModel->getFieldValue($this->localKey); /** @var DataModel $item */ foreach ($data as $item) { if ($item->getFieldValue($this->foreignKey) == $localKeyValue) { $this->data->add($item); } } } } /** * Returns the count subquery for DataModel's has() and whereHas() methods. * * @return \JDatabaseQuery */ abstract public function getCountSubquery(); /** * Returns a new item of the foreignModel type, pre-initialised to fulfil this relation * * @return DataModel * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ abstract public function getNew(); /** * Saves all related items. You can use it to touch items as well: every item being saved causes the modified_by and * modified_on fields to be changed automatically, thanks to the DataModel's magic. */ public function saveAll() { if ($this->data instanceof Collection) { foreach ($this->data as $item) { if ($item instanceof DataModel) { $item->save(); } } } } /** * Returns the foreign key map of a many-to-many relation, used for eager loading many-to-many relations * * @return array */ public function &getForeignKeyMap() { return $this->foreignKeyMap; } /** * Gets an object instance of the foreign model * * @param array $config Optional configuration information for the Model * * @return DataModel */ public function &getForeignModel(array $config = []) { // If the model comes from this component go through our Factory if (is_null($this->foreignModelComponent)) { /** @var DataModel $model */ $model = $this->container->factory->model($this->foreignModelName, $config)->tmpInstance(); return $model; } // The model comes from another component. Create a container and go through its factory. $foreignContainer = Container::getInstance($this->foreignModelComponent, ['tempInstance' => true]); /** @var DataModel $model */ $model = $foreignContainer->factory->model($this->foreignModelName, $config)->tmpInstance(); return $model; } /** * Returns the name of the local key of the relation * * @return string */ public function getLocalKey() { return $this->localKey; } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param Collection $dataCollection If it's an eager loaded relation, the collection of loaded parent records * * @return boolean Return false to force an empty data collection */ abstract protected function filterForeignModel(DataModel $foreignModel, Collection $dataCollection = null); } Exception/NoContentType.php 0000604 00000001070 15245611445 0011765 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoContentType extends \UnexpectedValueException { public function __construct( $className, $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_NOCONTENTTYPE', $className); parent::__construct( $message, $code, $previous ); } } Exception/TreeIncompatibleTable.php 0000604 00000001111 15245611445 0013406 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeIncompatibleTable extends \UnexpectedValueException { public function __construct( $tableName, $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_INCOMPATIBLETABLE', $tableName); parent::__construct( $message, $code, $previous ); } } Exception/TreeUnexpectedPrimaryKey.php 0000604 00000001130 15245611445 0014152 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeUnexpectedPrimaryKey extends \UnexpectedValueException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_UNEXPECTEDPK'); } parent::__construct( $message, $code, $previous ); } } Exception/TreeInvalidLftRgt.php 0000604 00000000724 15245611445 0012552 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; abstract class TreeInvalidLftRgt extends \RuntimeException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { parent::__construct( $message, $code, $previous ); } } Exception/TreeInvalidLftRgtSibling.php 0000604 00000001131 15245611445 0014053 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtSibling extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_SIBLING'); } parent::__construct( $message, $code, $previous ); } } Exception/TreeInvalidLftRgtCurrent.php 0000604 00000001131 15245611445 0014106 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtCurrent extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_CURRENT'); } parent::__construct( $message, $code, $previous ); } } Exception/NoTableColumns.php 0000604 00000000442 15245611445 0012103 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; class NoTableColumns extends BaseException { } Exception/CannotLockNotLoadedRecord.php 0000604 00000001125 15245611445 0014201 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class CannotLockNotLoadedRecord extends BaseException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_CANNOTLOCKNOTLOADEDRECORD'); } parent::__construct( $message, $code, $previous ); } } Exception/SpecialColumnMissing.php 0000604 00000000450 15245611445 0013305 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; class SpecialColumnMissing extends BaseException { } Exception/InvalidSearchMethod.php 0000604 00000000447 15245611445 0013100 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; class InvalidSearchMethod extends BaseException { } Exception/TreeInvalidLftRgtOther.php 0000604 00000001125 15245611445 0013550 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtOther extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_OTHER'); } parent::__construct( $message, $code, $previous ); } } Exception/RecordNotLoaded.php 0000604 00000001076 15245611445 0012232 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class RecordNotLoaded extends BaseException { public function __construct( $message = "", $code = 404, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_COULDNOTLOAD'); } parent::__construct( $message, $code, $previous ); } } Exception/TreeRootNotFound.php 0000604 00000001076 15245611445 0012442 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeRootNotFound extends \RuntimeException { public function __construct($tableName, $lft, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_ROOTNOTFOUND', $tableName, $lft); parent::__construct($message, $code, $previous); } } Exception/TreeMethodOnlyAllowedInRoot.php 0000604 00000001077 15245611445 0014570 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeMethodOnlyAllowedInRoot extends \RuntimeException { public function __construct( $method = '', $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_ONLYINROOT', $method); parent::__construct( $message, $code, $previous ); } } Exception/TreeInvalidLftRgtParent.php 0000604 00000001127 15245611445 0013722 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtParent extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_PARENT'); } parent::__construct( $message, $code, $previous ); } } Exception/NoItemsFound.php 0000604 00000001052 15245611445 0011566 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoItemsFound extends BaseException { public function __construct( $className, $code = 404, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_NOITEMSFOUND', $className); parent::__construct( $message, $code, $previous ); } } Exception/TreeUnsupportedMethod.php 0000604 00000001076 15245611445 0013533 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeUnsupportedMethod extends \LogicException { public function __construct( $method = '', $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_UNSUPPORTEDMETHOD', $method); parent::__construct( $message, $code, $previous ); } } Exception/BaseException.php 0000604 00000000445 15245611445 0011752 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; class BaseException extends \RuntimeException { } Exception/NoAssetKey.php 0000604 00000001103 15245611445 0011236 0 ustar 00 <?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\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoAssetKey extends \UnexpectedValueException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_NOASSETKEY'); } parent::__construct( $message, $code, $previous ); } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка