Your IP : 216.73.216.224


Current Path : /var/www/html/administrator/components/com_jdownloads/src/Model/
Upload File :
Current File : /var/www/html/administrator/components/com_jdownloads/src/Model/DownloadModel.php

<?php
/**
 * @package jDownloads
 * @version 4.0  
 * @copyright (C) 2007 - 2022 - Arno Betz - www.jdownloads.com
 * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.txt
 * 
 * jDownloads is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

namespace JDownloads\Component\JDownloads\Administrator\Model;
 
\defined( '_JEXEC' ) or die;

use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\Registry\Registry;
use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Language\Associations;
use Joomla\Filesystem\File;
use Joomla\Filesystem\Folder;
use Joomla\CMS\Helper\TagsHelper;
use Joomla\CMS\Tag\TaggableTableInterface;
use Joomla\CMS\Tag\TaggableTableTrait;
use Joomla\Database\DatabaseDriver;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\Database\ParameterType;
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Form\FormFactoryInterface;
use Joomla\CMS\Table\TableInterface;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Filter\OutputFilter;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\UCM\UCMType;
use Joomla\CMS\Language\LanguageHelper;
use Joomla\Database\DatabaseInterface;
use JLoader;

use JDownloads\Component\JDownloads\Administrator\Helper\JDownloadsHelper;
use JDownloads\Component\JDownloads\Site\Helper\JDHelper;
use JDownloads\Component\JDownloads\Administrator\Field\JDLicenseSelect;
use JDownloads\Component\JDownloads\Administrator\Field\JDSystemSelectField;
use JDownloads\Component\JDownloads\Administrator\Field\JDFileLanguageSelectField;
use JDownloads\Component\JDownloads\Administrator\Table\DownloadTable;
use JDownloads\Component\JDownloads\Site\Helper\AssociationHelper;
use JDownloads\Component\JDownloads\Administrator\Helper\CategoriesHelper;

class DownloadModel extends AdminModel
{
	
    // The prefix to use with controller messages.
    protected $text_prefix = 'COM_JDOWNLOADS';

    // The context used for the associations table
    protected $associationsContext = 'com_jdownloads.item';
    
    // Required for Joomla fields support
    protected $option = 'com_jdownloads';
    protected $name = 'download';
    
    // Allowed batch commands
    protected $jd_batch_commands = array(
        'price' => 'batchPrice',
        'tag'   => 'batchTag',
    );
    
    public function __construct($config = array(), ?MVCFactoryInterface $factory = null, ?FormFactoryInterface $formFactory = null)
    {   
        parent::__construct($config, $factory, $formFactory);
        
    }
    
    /**
     * Method to test whether a record can be deleted.
     *
     * @param    object    A record object.
     * @return    boolean    True if allowed to delete the record. Defaults to the permission set in the component.
     * @since    1.6
     */
    protected function canDelete($record)
    {
        return parent::canDelete($record);
    }
    
    /**
     * Method to test whether a record can have its state changed.
     *
     * @param    object    A record object.
     * @return    boolean    True if allowed to change the state of the record. Defaults to the permission set in the component.
     * @since    1.6
     */
    protected function canEditState($record)
    {
        return parent::canEditState($record);
    }

    /**
     * Method to get a table object, load it if necessary.
     *
     * @param   string  $name     The table name. Optional.
     * @param   string  $prefix   The class prefix. Optional.
     * @param   array   $options  Configuration array for model. Optional.
     *
     * @return  Table  A Table object
     *
     * @since   3.0
     */
    public function getTable($name = 'Download', $prefix = 'JTable', $options = array()) 
    {
        if ($table = $this->_createTable($name, $prefix, $options)) {
            return $table;
        }
    } 
    
    /**
     * Method to get the record form.
     *
     * @param    array    $data        Data for the form.
     * @param    boolean    $loadData    True if the form is to load its own data (default case), false if not.
     * @return    mixed    A JForm object on success, false on failure
     * @since    1.6
     */
    public function getForm($data = array(), $loadData = true) 
    {
        $app  = Factory::getApplication();
        $user = $app->getIdentity();
        
        // Get the form.
        $form = $this->loadForm('com_jdownloads.download', 'download', array('control' => 'jform', 'load_data' => $loadData));
        
        if (empty($form)) {
            return false;
        }
        
        $jinput = Factory::getApplication()->getInput();

        /*
         * The front end calls this model and uses a_id to avoid id clashes so we need to check for that first.
         * The back end uses id so we use that the rest of the time and set it to 0 by default.
         */
        $id = $jinput->get('a_id', $jinput->get('id', 0));

        // Determine correct permissions to check.
        if ($this->getState('download.id')) {
            $id = $this->getState('download.id');

            // Existing record. Can only edit in selected categories.
            $form->setFieldAttribute('catid', 'action', 'core.edit');

            // Existing record. Can only edit own Downloads in selected categories.
            $form->setFieldAttribute('catid', 'action', 'core.edit.own');
        } else {
            // New record. Can only create in selected categories.
            $form->setFieldAttribute('catid', 'action', 'core.create');
        }
        
        // Check for existing Download.
        // Modify the form based on Edit State access controls.
        if ($id != 0 && (!$user->authorise('core.edit.state', 'com_jdownloads.download.' . (int) $id))
            || ($id == 0 && !$user->authorise('core.edit.state', 'com_jdownloads')))
        {
            // Disable fields for display.
            $form->setFieldAttribute('featured', 'disabled', 'true');
            $form->setFieldAttribute('ordering', 'disabled', 'true');
            $form->setFieldAttribute('publish_up', 'disabled', 'true');
            $form->setFieldAttribute('publish_down', 'disabled', 'true');
            $form->setFieldAttribute('state', 'disabled', 'true');

            // Disable fields while saving.
            // The controller has already verified this is an Download you can edit.
            $form->setFieldAttribute('featured', 'filter', 'unset');
            $form->setFieldAttribute('ordering', 'filter', 'unset');
            $form->setFieldAttribute('publish_up', 'filter', 'unset');
            $form->setFieldAttribute('publish_down', 'filter', 'unset');
            $form->setFieldAttribute('state', 'filter', 'unset');
        }

        // Prevent messing with Download language and category when editing existing Download with associations
        $app = Factory::getApplication();
        $assoc = Associations::isEnabled();

        // Check if article is associated
        if ($this->getState('download.id') && $app->isClient('site') && $assoc) {
            $associations = AssociationHelper::getAssociations($id, '#__jdownloads_files', 'com_jdownloads.item');  //Associations::getAssociations('com_jdownloads', '#__jdownloads_files', 'com_jdownloads.item', $id, 'id', 'alias', 'catid');
                            
            // Make fields read only
            if (!empty($associations)) {
                $form->setFieldAttribute('language', 'readonly', 'true');
                $form->setFieldAttribute('catid', 'readonly', 'true');
                $form->setFieldAttribute('language', 'filter', 'unset');
                $form->setFieldAttribute('catid', 'filter', 'unset');
            }
        }
        
        return $form;
    }
    
    /**
     * Method to get the data that should be injected in the form.
     *
     * @return    mixed    The data for the form.
     * @since    1.6
     */
    protected function loadFormData() 
    {
        // Check the session for previously entered form data.
        $app  = Factory::getApplication();
        $data = $app->getUserState('com_jdownloads.edit.download.data', array());
        
        if (empty($data)) {
            
            $data = $this->getItem();

            // Pre-select some filters (Status, Category, Language, Access) in edit form if those have been selected in Downloads Manager
            if ($this->getState('download.id') == 0) {
                $filters = (array) $app->getUserState('com_jdownloads.downloads.filter');
                $data->state = $app->getInput()->getInt(
                    'state',
                    ((isset($filters['published']) && $filters['published'] !== '') ? $filters['published'] : null)
                );
                
                // If multiple categories are filtered, pick the first one to avoid loading all fields
                $filteredCategories = $filters['category_id'] ?? null;
                $selectedCatId      = null;

                if (\is_array($filteredCategories)) {
                    $selectedCatId = (int) reset($filteredCategories);
                } elseif (!empty($filteredCategories)) {
                    $selectedCatId = (int) $filteredCategories;
                }

                $data->catid = $app->getInput()->getInt('catid', $selectedCatId);

                if ($app->isClient('administrator')) {
                    $data->language = $app->getInput()->getString('language', (!empty($filters['language']) ? $filters['language'] : null));
                }

                $data->access = $app->getInput()->getInt('access', (!empty($filters['access']) ? $filters['access'] : $app->get('access')));
            }
        }
        
        // If there are params fieldsets in the form it will fail with a registry object
        if (isset($data->params) && $data->params instanceof Registry){
            $data->params = $data->params->toArray();
        }
        
        $this->preprocessData('com_jdownloads.download', $data);
        
        return $data;
    }
    
    /**
     * Prepare and sanitise the table prior to saving.
     *
     * @since    1.6
     */
    protected function prepareTable($table)
    {
        $app = Factory::getApplication();
        $user = $app->getIdentity();
        $db = $this->getDatabase();
        
        if ((int)$table->publish_up == 0){
            if ($table->published == 1){
                // Set the publish date to now
                $table->publish_up = Factory::getDate()->toSql();
            } else {
                $table->publish_up = null;
            }
        }
        
        if (intval($table->publish_down) == 0) {
            $table->publish_down = null;
        }
        
        $table->title        = htmlspecialchars_decode($table->title, ENT_QUOTES);
        $table->alias        = ApplicationHelper::stringURLSafe($table->alias);

        if (empty($table->alias)) {
            $table->alias = ApplicationHelper::stringURLSafe($table->title);
            if (trim(str_replace('-','',$table->alias)) == '') {
                $table->alias = Factory::getDate()->format("Y-m-d-H-i-s");
        	}  
        }           

        if (!empty($table->password)) {
            $table->password_md5 = hash('sha256', $table->password);
        } else {
            $table->password_md5 = '';
        }
        
        if (!$table->language){
            $table->language = '*';            
        }
                
        // Set the default values for new created download
        if (empty($table->id)) {
            // Reorder the downloads within the category so the new download is first
            $table->reorder('catid = '.(int) $table->catid.' AND published >= 0');

            // Set ordering to the last item if not set
            if (empty($table->ordering)) {
                $db->setQuery('SELECT MAX(ordering) FROM #__jdownloads_files');
                $max = $db->loadResult();
                $table->ordering = $max+1;
            }
        }
    }
    
    /**
     * Method to save the form data.
     *
     * @param    array    $data     The form data.
     * @param    boolean  $auto     true when the data comes from auto monitoring
     * @param    boolean  $import   true when the data comes from 1.9.x import process   
     * @return    boolean    True on success.
     */
    public function save($data, $auto = false, $import = false, $restore_in_progress = false)
    {
        $params = ComponentHelper::getParams('com_jdownloads');
        
        $app    = Factory::getApplication();
        $input  = $app->getInput();
        $filter = InputFilter::getInstance();
        
        // Initialise variables;
        $table      = $this->getTable();
        $context    = $this->option . '.' . $this->name;
        
        $key        = $table->getKeyName();
        $pk         = (!empty($data['id'])) ? (int)$data['id'] : (int)$this->getState($this->getName().'.id');
        $isNew      = true;
        
        // Include the plugins for the save events.
        PluginHelper::importPlugin($this->events_map['save']);
        
        $result = false;
        
        // Cast catid to integer for comparison
        /*  $catid = (int) $data['catid'];
        
        // Check if New Category exists
        if ($catid > 0){
            $catid = CategoriesHelper::validateCategoryId($data['catid'], 'com_jdownloads');
        }
        
        // Save New Category
        if ($catid == 0 && $this->canCreateCategory()){
            $table = array();
            $table['title'] = $data['catid'];
            $table['parent_id'] = 1;
            $table['language'] = $data['language'];
            $table['published'] = 1;
            
            // Create new category and get catid back
            $data['catid'] = CategoriesHelper::createCategory($table);
        }  */
        
        // Include the content plugins for the on save events.
        // PluginHelper::importPlugin('content'); // Commented out as unnecessary here. In conjunction with Event Gallery extension error message and abort.
        
        // Load the row if saving an existing download. Not when auto monitoring is activated (also use for import from old version)
        if ($pk > 0 && !$auto || ($pk > 0 && $restore_in_progress) ) {
            $table->load($pk);
            $isNew = false;
        }

        // Alter the title for save as copy
        if ($input->get('task') == 'save2copy') {
            $origTable = $this->getTable();

            if ($app->isClient('site')) {
                $origTable->load($input->getInt('a_id'));

                if ($origTable->title === $data['title']) {
                    /**
                     * If title of Download is not changed, set alias to original Download alias so that Joomla! will generate
                     * new Title and Alias for the copied Download
                     */
                    $data['alias'] = $origTable->alias;
                } else {
                    $data['alias'] = '';
                }
            } else {
                $origTable->load($input->getInt('id'));
            }

            if ($data['title'] == $origTable->title) {
                list($title, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['title']);
                $data['title'] = $title;
                $data['alias'] = $alias;
            } elseif ($data['alias'] == $origTable->alias) {
                $data['alias'] = '';
            }
        }

        // Automatic handling of alias for empty fields
        if (in_array($input->get('task'), array('apply', 'save', 'save2new')) && (!isset($data['id']) || (int) $data['id'] == 0)) {
            
            if ($data['alias'] == null) {
                
                if ($app->get('unicodeslugs') == 1) {
                    $data['alias'] = OutputFilter::stringUrlUnicodeSlug($data['title']);
                } else {
                    $data['alias'] = OutputFilter::stringURLSafe($data['title']);
                }

                $table = $this->getTable();

                if ($table->load(array('alias' => $data['alias'], 'catid' => $data['catid']))) {
                    $msg = Text::_('COM_JDOWNLOADS_ALIAS_SAVE_WARNING');
                }

                list($title, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['title']);
                $data['alias'] = $alias;

                if (isset($msg)) {
                    $app->enqueueMessage($msg, 'warning');
                }
            }
        }
        
        if (!isset($data['rules'])){
            $data['rules'] = array(
                'core.create' => array(),
                'core.delete' => array(),
                'core.edit' => array(),
                'core.edit.state' => array(),
                'core.edit.own' => array(),
                'download' => array(),
            ); 
        }

       
        if ((!empty($data['tags']) && $data['tags'][0] != '')) {
            $table->newTags = $data['tags'];
        } 

        // Bind the data.
        if (!$table->bind($data)) {
            $this->setError($table->getError());
            return false;
        }

        // Prepare the row for saving
        $this->prepareTable($table);
        
        // Check the data and check the selected files and handle it.
        if (!$table->checkData($isNew, $auto)) {
            $this->setError($table->getError());
            return false;
        }
        
        // Trigger the onContentBeforeSave event.
        if (!$auto){
            $result = Factory::getApplication()->triggerEvent($this->event_before_save, array($context, $table, $isNew, $data));
            if (in_array(false, $result, true)) {
                $this->setError($table->getError());
                return false;
            }
        }

        // Store the data.
        if ($import === true){
            // Set off this 
            $table->set('_autoincrement',false);
        }
        
        if (!$table->store()) {
            $this->setError($table->getError());
            return false;
        } else {
            // Folder handling functionality
            if (!$auto){
                // Update only the log table when we have a new download creation in frontend
                $app  = Factory::getApplication();
                if ($app->isClient('site') && $isNew){
                    $upload_data           = new \stdClass();
                    $upload_data->id       = $table->id;
                    $upload_data->url_download  = $table->url_download;
                    $upload_data->title    = $table->title;
                    $upload_data->size          = $table->size;
                    JDHelper::updateLog($type = 2, '', $upload_data);
                    
                    // A failed notification mail must not invalidate an already stored upload.
                    if ($params->get('send_mailto_option_upload') == '1'){
                        try {
                            if (!JDHelper::sendMailUpload($table)) {
                                $app->enqueueMessage(Text::_('COM_JDOWNLOADS_UPLOAD_MAIL_SEND_FAILED'), 'warning');
                            }
                        } catch (\Throwable $e) {
                            $app->enqueueMessage(Text::_('COM_JDOWNLOADS_UPLOAD_MAIL_SEND_FAILED'), 'warning');
                        }
                    }
                }
            }    
        }    

        // Trigger the onContentAfterSave event - also Custom fields are saved here!!!
        if (!$auto){
            Factory::getApplication()->triggerEvent('onContentAfterSave', array($context, $table, $isNew, $data));
        }
       
        $this->setState($this->getName().'.id', $table->id);
        
        $this->setState($this->getName() . '.new', $isNew);
        
        // We need the 'association job' only when we are not in the monitoring routine 
        if (!$auto){
            
            if ($this->associationsContext && Associations::isEnabled() && !empty($data['associations'])){
                $associations = $data['associations'];

                // Unset any invalid associations
                $associations = ArrayHelper::toInteger($associations);

                // Unset any invalid associations
                foreach ($associations as $tag => $id){
                    if (!$id){
                        unset($associations[$tag]);
                    }
                }

                // Show a warning if the item isn't assigned to a language but we have associations.
                if ($associations && $table->language === '*'){
                    Factory::getApplication()->enqueueMessage(
                        Text::_(strtoupper($this->option) . '_ERROR_ALL_LANGUAGE_ASSOCIATED'),
                        'warning'
                    );
                }

                // Get associationskey for edited item
                $db    = $this->getDatabase();
                $query = $db->getQuery(true)
                    ->select($db->qn('key'))
                    ->from($db->qn('#__associations'))
                    ->where($db->qn('context') . ' = ' . $db->quote($this->associationsContext))
                    ->where($db->qn('id') . ' = ' . (int) $table->$key);
                $db->setQuery($query);
                $old_key = $db->loadResult();

                // Deleting old associations for the associated items
                $query = $db->getQuery(true)
                    ->delete($db->qn('#__associations'))
                    ->where($db->qn('context') . ' = ' . $db->quote($this->associationsContext));

                if ($associations){
                    $query->where('(' . $db->qn('id') . ' IN (' . implode(',', $associations) . ') OR '
                        . $db->qn('key') . ' = ' . $db->q($old_key) . ')');
                } else {
                    $query->where($db->qn('key') . ' = ' . $db->q($old_key));
                }

                $db->setQuery($query);
                $db->execute();

                // Adding self to the association
                if ($table->language !== '*'){
                    $associations[$table->language] = (int) $table->$key;
                }

                if (count($associations) > 1){
                    // Adding new association for these items
                    $key   = md5(json_encode($associations));
                    $query = $db->getQuery(true)
                        ->insert('#__associations');

                    foreach ($associations as $id){
                        $query->values(((int) $id) . ',' . $db->quote($this->associationsContext) . ',' . $db->quote($key));
                    }

                    $db->setQuery($query);
                    $db->execute();
                }
            }
        }
        return true;
    }
    
    /**
     * Method to perform batch operations on an item or a set of items.
     *
     * @param   array  $commands  An array of commands to perform.
     * @param   array  $pks       An array of item ids.
     * @param   array  $contexts  An array of item contexts.
     *
     * @return  boolean  Returns true on success, false on failure.
     *
     */
    public function batch($commands, $pks, $contexts)
    {
        $app = Factory::getApplication();
        
        // Sanitize ids.
        $pks = array_unique($pks);
        $pks = ArrayHelper::toInteger($pks);
        
        $this->batch_commands = array_merge($this->batch_commands, $this->jd_batch_commands);

        // Remove any values of zero.
        if (array_search(0, $pks, true)) {
            unset($pks[array_search(0, $pks, true)]);
        }

        if (empty($pks)) {
            $this->setError(Text::_('JGLOBAL_NO_ITEM_SELECTED'));

            return false;
        }

        $done = false;
        PluginHelper::importPlugin($this->events_map['batch']);
        
        // Initialize re-usable member properties
        $this->initBatch();

        // Set some needed variables.
        $this->user = $app->getIdentity();
        $this->table = $this->getTable();
        $this->tableClassName = get_class($this->table);
        
        $type = new UcmType; 
        $this->type = $type->getTypeByTable($this->tableClassName);
        
        $this->contentType = new UcmType;
        $this->type = $this->contentType->getTypeByTable($this->tableClassName);
         
        $this->batchSet = true;

        if ($this->type == false) {
            $type = new UcmType;
            $this->type = $type->getTypeByAlias($this->typeAlias);
        }

        if ($this->batch_copymove && !empty($commands[$this->batch_copymove])) {
            
            $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c');

            if ($cmd == 'c' || $cmd == 'cc' || $cmd == 'ca') {
                if ($cmd == 'cc'){ 
                    $copy_with_file = true;
                } else {
                    $copy_with_file = false;
                }
                
                if ($cmd == 'ca'){ 
                    $copy_and_assign_file = true;
                } else {
                    $copy_and_assign_file = false;
                }
                
                $result = $this->batchCopy($commands[$this->batch_copymove], $pks, $contexts, $copy_with_file, $copy_and_assign_file);

                if (is_array($result)) {
                    
                    foreach ($result as $old => $new) {
                        $contexts[$new] = $contexts[$old];
                    }
                    $pks = array_values($result);
                } else {
                    // Actualize at last the batch progress setting 
                    $result = JDownloadsHelper::changeParamSetting('downloads_batch_in_progress', '0');
                    return false;
                }
            } elseif ($cmd === 'm' && !$this->batchMove($commands[$this->batch_copymove], $pks, $contexts)) {
                // Actualize at last the batch progress setting 
                $result = JDownloadsHelper::changeParamSetting('downloads_batch_in_progress', '0');
                return false;
            }

            $done = true;
        }

        foreach ($this->batch_commands as $identifier => $command) {
            
            if (!empty($commands[$identifier])) {
                
                if (!$this->$command($commands[$identifier], $pks, $contexts)) {
                    return false;
                }

                $done = true;
            }
        }

        if (!$done) {
            $this->setError(Text::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION'));

            return false;
        }

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

        return true;
    }    
    
    /**
     * Batch copy downloads to a new category
     *
     * @param   integer  $value     The new category.
     * @param   array    $pks       An array of row IDs.
     * @param   array    $contexts  An array of item contexts.
     *
     * @return  mixed  An array of new IDs on success, boolean false on failure.
     *
     */
    protected function batchCopy($value, $pks, $contexts, $copy_with_file = false, $copy_and_assign_file = false)
    {
        $params = ComponentHelper::getParams('com_jdownloads');
        $app = Factory::getApplication();
        $categoryId = (int) $value;

        $db = $this->getDatabase();
        $query = $db->getQuery(true);
        
        // Check at first, that it is not always run a other batch job
        if ($params->get('categories_batch_in_progress') || $params->get('downloads_batch_in_progress')){
            // Generate the warning and return
            Factory::getApplication()->enqueueMessage( Text::_('COM_JDOWNLOADS_BATCH_IS_ALWAYS_STARTED'), 'warning');
            return false;
        } else {
            // Actualize at first the batch progress setting 
            $result = JDownloadsHelper::changeParamSetting('downloads_batch_in_progress', '1');
        }
        
        // Check that the target category exists
        if ($categoryId){
            $targetCategoryTable = $this->getTable('JDCategory');
            if (!$targetCategoryTable->load($categoryId)){
                if ($error = $targetCategoryTable->getError()){
                    // Fatal error
                    $this->setError($error);
                    return false;
                } else {
                    $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
                    return false;
                }
            }
        }

        if (empty($categoryId)){
            $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
            return false;
        }
        
        // Check that the user has create permission for the component
        $extension = Factory::getApplication()->getInput()->get('option', '');
        $user = $app->getIdentity();
        
        if (!$user->authorise('core.create', $extension . '.category.' . $categoryId)){
            $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE'));
            return false;
        }
        
        $newIds = array();

        // Parent exists so we let's proceed
        while (!empty($pks))
        {
            // Pop the first ID off the stack
            $pk = array_shift($pks);

            $this->table->reset();

            // Check that the row actually exists
            if (!$this->table->load($pk)){
                if ($error = $this->table->getError()){
                    // Fatal error
                    $this->setError($error);
                    return false;
                } else {
                    // Not fatal error
                    $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk));
                    continue;
                }
            }
            
            // It is required to copy also the assigned file?
            // ******************************************************************
            // If this is not possible, the download copying will not continue!
            // ******************************************************************
            $source_path = '';
            $target_path = '';
            
            if ($copy_with_file){
                
                // Build the file path from the Downloads target category when required
                if ($targetCategoryTable->cat_dir_parent != ''){
                    $target_folder = $targetCategoryTable->cat_dir_parent.'/'.$targetCategoryTable->cat_dir;
                } else {
                    $target_folder = $targetCategoryTable->cat_dir;
                }
                $target_path = $params->get('files_uploaddir') . '/' . $target_folder . '/' . $this->table->url_download;
                
                if ($categoryId > 1 && $this->table->url_download != ''){
                    $copy_filename       = $this->table->url_download;
                    $source_cat_id       = $this->table->catid;
                    $target_cat_id       = $categoryId;
                    
                    // We must only copy when we have a different cat id.
                    if ($source_cat_id != $target_cat_id){
             
                        // Get the data from the source category
                        $sourceCategoryTable = $this->getTable('JDCategory');
                        if (!$sourceCategoryTable->load($source_cat_id)){
                            if ($error = $sourceCategoryTable->getError()){
                                // Fatal error
                                $this->setError($error);
                                return false;
                            } else {
                                // Category not found
                                $this->setError(Text::_('COM_JDOWNLOADS_BATCH_MOVE_ROW_NOT_FOUND'));
                                return false;
                            }
                        }
                        
                        // Build the file path from the Downloads source category
                        if ($sourceCategoryTable->cat_dir_parent != ''){
                            $source_folder = $sourceCategoryTable->cat_dir_parent.'/'.$sourceCategoryTable->cat_dir.'/'.$copy_filename;
                        } else {
                            $source_folder = $sourceCategoryTable->cat_dir.'/'.$copy_filename;
                        }
                        
                        $source_path = $params->get('files_uploaddir') . '/' . $source_folder;
                        
                        if ($source_path && $target_path){
                            
                            // Exists the file to be copied in the source directory?
                            if (!File::exists($source_path)){
                                Factory::getApplication()->enqueueMessage( Text::sprintf('COM_JDOWNLOADS_BATCH_CANNOT_FIND_COPY_FILE', $source_folder, $this->table->id ), 'warning');
                                continue;
                            }
                            
                            // We can copy the file to the new folder only if it does not already exist there.
                            if (File::exists($target_path)){
                                Factory::getApplication()->enqueueMessage( Text::sprintf('COM_JDOWNLOADS_BATCH_FILES_EXIST_ALREADY', $source_folder, $this->table->id ), 'warning');
                                continue;
                            } else {
                                if (!File::copy($source_path, $target_path)){
                                    // Could not copy the file - error.
                                    Factory::getApplication()->enqueueMessage( Text::sprintf('COM_JDOWNLOADS_BATCH_CANNOT_COPY_FILES', $source_folder, $this->table->id ), 'warning');
                                    continue;
                                }
                            }
                        }
                    }    
                }    
            }
            
            // Build a new title and alias
            list($title,$alias) = $this->table->buildNewTitle($this->table->alias, $this->table->title);
            $this->table->title    = $title;
            $this->table->alias    = $alias;

            if ($copy_and_assign_file){
                $this->table->other_file_id = (int)$this->table->id;
            }

            // Reset the ID because we are making a copy
            $this->table->id = 0;

            // New category ID
            $this->table->catid = $categoryId;
            
            // set correct new ordering
            $this->table->ordering = $this->getNewOrdering($categoryId); 
            
            $this->table->views = 0;
            $this->table->downloads = 0;
            $this->table->modified_by = 0;            
            $this->table->modified = null;

            if (!$copy_with_file || $copy_and_assign_file){
                $this->table->url_download = '';
            }
            
            // We don't want to modify tags - so remove the associated tags helper
            if ($this->table instanceof TaggableTableInterface) {
                // $this->table->clearTagsHelper();
            }
            
            // Check the row.
            if (!$this->table->check(true)){
                $this->setError($this->table->getError());
                return false;
            }
            
            // Store the row.
            if (!$this->table->store()){
                $this->setError($this->table->getError());
                return false;
            }
            
            // Get the new item ID
            $newId = $this->table->get('id');
            
            $this->cleanupPostBatchCopy($this->table, $newId, $pk);

            // Add the new ID to the array
            $newIds[$pk] = $newId;
        }

        // Clean the cache
        $this->cleanCache();

        // Actualize at last the batch progress setting 
        $result = JDownloadsHelper::changeParamSetting('downloads_batch_in_progress', '0');          
        
        return $newIds;
    } 
    
    /**
     * Function that can be overriden to do any data cleanup after batch copying data
     *
     * @param   \TableInterface  $table  The table object containing the newly created item
     * @param   integer           $newId  The id of the new item
     * @param   integer           $oldId  The original item id
     *
     * @return  void
     *
     */
    protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId)
    {
        // Register FieldsHelper
        JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php');

        $oldItem = $this->getTable();
        $oldItem->load($oldId);
        $fields = FieldsHelper::getFields('com_jdownloads.download', $oldItem, true);

        $fieldsData = array();

        if (!empty($fields)) {
            $fieldsData['com_fields'] = array();

            foreach ($fields as $field) {
                $fieldsData['com_fields'][$field->name] = $field->rawvalue;
            }
        }

        Factory::getApplication()->triggerEvent('onContentAfterSave', array('com_jdownloads.download', &$this->table, true, $fieldsData));        
        
    } 
    
    /**
     * Batch move Downloads to a new category
     * If the downloads contain a file we will try to move it. If this is not possible, the download in this case will not be changed!
     *
     * @param   integer  $value  The new target category ID.
     * @param   array    $pks    An array of Downloads row IDs.
     *
     * @return  booelan  True if successful, false otherwise and internal error is set.
     *
     */
    protected function batchMove($value, $pks, $contexts)
    {
        if (empty($this->batchSet)) {
            // Set some needed variables.
            $this->user           = $this->getCurrentUser();
            $this->table          = $this->getTable();
            $this->tableClassName = \get_class($this->table);
            $this->contentType    = new UCMType();
            $this->type           = $this->contentType->getTypeByTable($this->tableClassName);
        }
        
        $app    = Factory::getApplication();
        $jinput = Factory::getApplication()->getInput();
        
        $params = ComponentHelper::getParams('com_jdownloads');
        $this->typeAlias = 'com_jdownloads.download';
        
        $new_category_id = (int) $value;

        // Check at first, that it is not always run a other batch job
        if ($params->get('categories_batch_in_progress') || $params->get('downloads_batch_in_progress')){
            // Generate the warning and return
            Factory::getApplication()->enqueueMessage( Text::_('COM_JDOWNLOADS_BATCH_IS_ALWAYS_STARTED'), 'warning');
            return false;
        } else {
            // Actualize at first the batch progress setting 
            $result = JDownloadsHelper::changeParamSetting('downloads_batch_in_progress', '1');        
        }        

        // Check that the target category exists
        if ($new_category_id) {
            $newCategoryTable = $this->getTable('JDCategory');
            
            if (!$newCategoryTable->load($new_category_id)) {
                if ($error = $newCategoryTable->getError()) {
                    // Fatal error
                    $this->setError($error);
                    return false;
                } else {
                    $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
                    return false;
                }
            }
        }

        if (empty($new_category_id)) {
            $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
            return false;
        }

        // Check that user has create and edit permission for the component
        $extension   = $jinput->get('option');
        $user        = $app->getIdentity();
        
        if (!$user->authorise('core.create', $extension)) {
            $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE'));
            return false;
        }

        if (!$user->authorise('core.edit', $extension)) {
            $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
            return false;
        }
        
        PluginHelper::importPlugin('system');

        // Parent exists so we let's proceed
        foreach ($pks as $pk) {
            
            // Check the edit permissions
            if (!$this->user->authorise('core.edit', $contexts[$pk])){
                $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
                continue;
                //return false;
            }
            
            // Check that the Download actually exists
            if (!$this->table->load($pk)) {
                if ($error = $this->table->getError()) {
                    // Fatal error
                    $this->setError($error);
                    return false;
                } else {
                    // Selected Download not found
                    $this->setError(Text::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk));
                    continue;
                }
            }
            
            $oldCategoryTable = $this->getTable('JDCategory');
            
            if (!$oldCategoryTable->load($this->table->catid)) {
                if ($error = $oldCategoryTable->getError()) {
                    // Fatal error
                    $this->setError($error);
                    return false;
                } else {
                    $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND'));
                    return false;
                }
            }
            
            if ($oldCategoryTable->cat_dir_parent){
                $source_folder = $oldCategoryTable->cat_dir_parent . '/' . $oldCategoryTable->cat_dir;
            } else {
                $source_folder = $oldCategoryTable->cat_dir;
            }

            $source_path = $params->get('files_uploaddir') . '/' . $source_folder . '/' . $this->table->url_download;

            // Set the new category ID
            $this->table->catid = $new_category_id;
            
            // If a file was assigned to this download, we will now try to move it.
            // ******************************************************************
            // If this is not possible, we will not continue moving the download!
            // ******************************************************************
             
            if ($this->table->url_download){
                if ($newCategoryTable->cat_dir_parent){
                    $target_folder = $newCategoryTable->cat_dir_parent . '/' . $newCategoryTable->cat_dir;
                } else {
                    $target_folder = $newCategoryTable->cat_dir;
                }
                $target_path = $params->get('files_uploaddir') . '/' . $target_folder . '/' . $this->table->url_download;
                
                if ($source_path && $target_path){
                            
                    // Exists the file to be moved in the source directory?
                    if (!File::exists($source_path)){
                        Factory::getApplication()->enqueueMessage( Text::sprintf('COM_JDOWNLOADS_BATCH_CANNOT_FIND_COPY_FILE', $source_folder, $this->table->id ), 'warning');
                        continue;
                    }
                    
                    // We can move the file to the new folder only if it does not already exist there.
                    if (File::exists($target_path)){
                        Factory::getApplication()->enqueueMessage( Text::sprintf('COM_JDOWNLOADS_BATCH_FILES_EXIST_ALREADY', $source_folder, $this->table->id ), 'warning');
                        continue;
                    } else {
                        if (!File::move($source_path, $target_path)){
                            // Could not move the file - error.
                            Factory::getApplication()->enqueueMessage( Text::sprintf('COM_JDOWNLOADS_BATCH_CANNOT_COPY_FILES', $source_folder, $this->table->id ), 'warning');
                            continue;
                        }
                    }
                }
            }
            
            // Custom fields
            $fields = FieldsHelper::getFields('com_jdownloads.download', $this->table, true);

            $fieldsData = array();

            if (!empty($fields)) {
                $fieldsData['com_fields'] = array();

                foreach ($fields as $field) {
                    $fieldsData['com_fields'][$field->name] = $field->rawvalue;
                }
            }

            // Set the new category ID
            $this->table->catid = $new_category_id;
            
            // We don't want to modify tags - so remove the associated tags helper
            if ($this->table instanceof TaggableTableInterface) {
                $this->table->clearTagsHelper();
            }

            // Check the row.
            if (!$this->table->check()) {
                $this->setError($this->table->getError());
                return false;
            }
            
            // Store the row.
            if (!$this->table->store()) {
                $this->setError($this->table->getError());
                return false;
            }
            
            // Run event for moved Download
            Factory::getApplication()->triggerEvent('onContentAfterSave', array('com_jdownloads.download', &$this->table, false, $fieldsData));
        }

        // Clean the cache
        $this->cleanCache();

        // Actualize at last the batch progress setting 
        $result = JDownloadsHelper::changeParamSetting('downloads_batch_in_progress', '0');                      
        
        return true;
    }
    
    /**
     * Method implementing the batch setting of price values
     */
    protected function batchPrice($value, $pks, $contexts)
    {
        $app = Factory::getApplication();

        if (isset($value) && ($value != '')) {
            
            if (empty($this->batchSet)) {
                // Set some needed variables.
                $this->user = $app->getIdentity();
                $this->table = $this->getTable();
                $this->tableClassName = get_class($this->table);
                $this->contentType = new UcmType;
                $this->type = $this->contentType->getTypeByTable($this->tableClassName);
            }

            foreach ($pks as $pk) {
                
                if ($this->user->authorise('core.edit', $contexts[$pk])) {
                    $this->table->reset();
                    $this->table->load($pk);

                    if (isset($value)) {
                        $price = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
                        $this->table->price = $price;
                    }

                    if (!$this->table->store()) {
                        $this->setError($this->table->getError());
                        return false;
                    }
                } else {
                    $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
                    return false;
                }
            }
        }
        return true;
    }
    
    // Compute the new ordering for batch copy
    public function getNewOrdering($categoryId) 
    {
        $ordering = 1;
        $this->_db->setQuery('SELECT MAX(ordering) FROM #__jdownloads_files WHERE catid='.(int)$categoryId);
        $max = $this->_db->loadResult();
        $ordering = $max + 1;
        return $ordering;
    }
    
    
    /**
    * Method to create a new download 
    * 
    * @param mixed $name
    * @param mixed $note
    * @param mixed $description
    * @param string $filename
    * @param integer $catid
    * @param integer $filesize
    * 
    * @return CategoryNode
    */
    public function createDownload( $name, $note, $description, $filename, $catid = 1, $filesize = 0 )
    {
        $params = ComponentHelper::getParams('com_jdownloads');
        
        Table::addIncludePath( JPATH_ADMINISTRATOR . '/components/com_jdownloads/src/Table' );

        $data = array (
            'id' => 0,
            'catid' => $catid,
            'title' => $name,
            'alias' => '',
            'notes' => $note,
            'url_download' => $filename,
            'size' => $filesize,
            'description' => $description,
            'file_pic' => $params->get('file_pic_default_filename'),
            'published' => '1',
            'access' => '1',
            'metadesc' => '',
            'metakey' => '',
            'created_user_id' => '0',
            'language' => '*',
            'rules' => array(
                'core.create' => array(),
                'core.delete' => array(),
                'core.edit' => array(),
                'core.edit.state' => array(),
                'core.edit.own' => array(),
                'download' => array(),
            ),
            'params' => array(),
        );

        if( !$this->save( $data ) )
        {
            return NULL;
        }
        
        $download_id = $this->getState('download.id');
        $download = $this->getItem($download_id);
        
        return $download;
    }                      

    /**
    * Method to create a new download from auto monitoring
    * 
    * @param array       $data
    *        boolean     $import    switch which is set true when import process is run   
    * @return boolean
    */

    public function createAutoDownload( $data, $import = false )
    {
        Table::addIncludePath( JPATH_ADMINISTRATOR . '/components/com_jdownloads/src/Table' );

        if (!$this->save( $data, true, $import)) {
            return false;
        }
        
        return true;
    }                      

    
    /**
     * Method to get a single record.
     *
     * @param    integer    The id of the primary key.
     * @return    mixed    Object on success, false on failure.
     */
    public function getItem($pk = null)
    {
        $item = parent::getItem($pk);
        
        if (!empty($item->id)){
            $registry = new Registry;
            
            // Get the tags
            $item->tags = new TagsHelper;
            $item->tags->getTagIds($item->id, 'com_jdownloads.download');         
        } 
        
        // Added to support the Joomla Language Associations
        // Load associated Download items
        $assoc = Associations::isEnabled();

        if ($assoc) {
            $item->associations = array();

            if ($item->id != null) {
                $associations = Associations::getAssociations('com_jdownloads', '#__jdownloads_files', 'com_jdownloads.item', $item->id, 'id', 'alias', '');    

                foreach ($associations as $tag => $association) {
                    $item->associations[$tag] = $association->id;
                }
            }
        }       

        return $item;
    }                           
    
    /**
     * Allows preprocessing of the JForm object.
     *
     * @param   JForm   $form   The form object
     * @param   array   $data   The data to be merged into the form object
     * @param   string  $group  The plugin group to be executed
     *
     * @return  void
     *
     * @since   3.0
     */
    protected function preprocessForm(Form $form, $data, $group = 'jdownload')
    {
        if ($this->canCreateCategory()) {
            $form->setFieldAttribute('catid', 'allowAdd', 'true');
        }

        // Association content items
        if (Associations::isEnabled()) {
            
            $languages = LanguageHelper::getContentLanguages(false, true, null, 'ordering', 'asc');

            if (count($languages) > 1) {
                
                $addform = new \SimpleXMLElement('<form />');
                $fields = $addform->addChild('fields');
                $fields->addAttribute('name', 'associations');
                $fieldset = $fields->addChild('fieldset');
                $fieldset->addAttribute('name', 'item_associations');

                foreach ($languages as $language) {
                    
                    $field = $fieldset->addChild('field');
                    $field->addAttribute('name', $language->lang_code);
                    $field->addAttribute('type', 'modal_download');
                    $field->addAttribute('language', $language->lang_code);
                    $field->addAttribute('label', $language->title);
                    $field->addAttribute('translate_label', 'false');
                    $field->addAttribute('select', 'true');
                    $field->addAttribute('new', 'true');
                    $field->addAttribute('edit', 'true');
                    $field->addAttribute('clear', 'true');
                    $field->addAttribute('propagate', 'true');
                }

                $form->load($addform, false);
            }
        }

        parent::preprocessForm($form, $data, $group);
    }
    
    /**
     * Is the user allowed to create an on the fly category?
     *
     * @return  boolean
     *
     * @since   3.6.1
     */
    private function canCreateCategory()
    {
        $app = Factory::getApplication();
        return $app->getIdentity()->authorise('core.create', 'com_jdownloads');
    }
    
    
    /**
     * Method to remove a download and his file, preview file and images
     *
     * @access    public
     * @return    boolean    True on success
     */
    public function delete(&$pks = array())
    {
        $params = ComponentHelper::getParams('com_jdownloads');
       
        // Initialise variables.
        $pks = (array) $pks;
        $table = $this->getTable('Download');
        
        $app       = Factory::getApplication();
        $db        = Factory::getContainer()->get(DatabaseInterface::class);
        
        $jinput = Factory::getApplication()->getInput();
        
        $total     = count($pks);
        $query     = '';
        $cids      = implode( ',', $pks );
        $del_error = false;
        $del_image_error = false;
        
        $pics_folder   = JPATH_SITE.'/images/jdownloads/screenshots/';
        $thumbs_folder = JPATH_SITE.'/images/jdownloads/screenshots/thumbnails/';
        
        $preview_folder = $params->get('files_uploaddir').'/'.$params->get('preview_files_folder_name').'/';        
        
        // Get selected option value to delete also the file
        $file_delete = $jinput->get('delete_file_option', 1, 'integer');
        
        // Include the content plugins for the on delete events.
        PluginHelper::importPlugin('content');
        
        $can_delete = false;
        
        // Iterate the items to delete each one.
        foreach ($pks as $i => $pk)
        {

            if ($table->load($pk)) {
                
                if ($app->isClient('administrator')) {
                    $can_delete = $this->canDelete($table);
                }    
                
                if ($app->isClient('site') || $can_delete) {

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

                    // Trigger the onContentBeforeDelete event.
                    $result = Factory::getApplication()->triggerEvent($this->event_before_delete, array($context, $table));
                    if (in_array(false, $result, true)) {
                        $this->setError($table->getError());
                        return false;
                    }
                    
                    // Check file delete option - delete it at first when selected
                    if ($file_delete == 1) {            
                          // Only when no extern links are used
                          if ($table->url_download <> ''){
                              
                              // Check at first which other Downloads has still assigned to this Download file (use file from other Download option)
                              $db->setQuery('SELECT id, title FROM #__jdownloads_files WHERE other_file_id = '.$db->quote($table->id));
                              $still_assigned_files = $db->loadObjectList();
                              if (count($still_assigned_files)){
                                  $msg = Text::sprintf('COM_JDOWNLOADS_BE_NO_DEL_FILES_ASSIGNED_ITEMS_EXISTS', $table->title);
                                  $msg .= '<ul>'; 
                                  foreach ($still_assigned_files as $assigned){
                                      $msg .= '<li>ID: <b>'.$assigned->id. '</b> Title: <b>'.$assigned->title.'</b></li>';
                                  }
                                  $msg .= '</ul>'; 
                                  Factory::getApplication()->enqueueMessage($msg, 'warning');
                                  return false;
                              }
                              
                              $db->setQuery("SELECT cat_dir, cat_dir_parent FROM #__jdownloads_categories WHERE id = '$table->catid'");
                              $cat_dirs = $db->loadObject();
                              
                              if ($cat_dirs->cat_dir_parent != ''){
                                  $cat_dir = $cat_dirs->cat_dir_parent.'/'.$cat_dirs->cat_dir;
                              } else {
                                  $cat_dir = $cat_dirs->cat_dir;
                              }

                              if ($cat_dir && @file_exists($params->get('files_uploaddir').'/'.$cat_dir.'/'.$table->url_download)){
                                  // Delete the file now
                                  if (!File::delete($params->get('files_uploaddir').'/'.$cat_dir.'/'.$table->url_download)) {
                                      $del_error = Text::_('COM_JDOWNLOADS_BACKEND_FILESEDIT_DEL_FILES_ERROR');
                                  } 
                              } else {
                                    $del_error = Text::_('COM_JDOWNLOADS_BACKEND_FILESEDIT_DEL_FILES_ERROR');                                
                              }   
                          }

                    }                   

                    // Delete also the assigned images from this download, when this option is activated in config
                    if ($params->get('delete_also_images_from_downloads') == 1){
                          if ($table->images) {
                              $pics = explode('|', $table->images);
                              
                              foreach ($pics as $pic){
                                  if (!File::delete($pics_folder.$pic)) {
                                      $del_image_error = Text::_('COM_JDOWNLOADS_BACKEND_FILESEDIT_DEL_IMAGES_ERROR');
                                  }    
                                  
                                  if (!File::delete($thumbs_folder.$pic)) {
                                      $del_image_error = Text::_('COM_JDOWNLOADS_BACKEND_FILESEDIT_DEL_IMAGES_ERROR');
                                  }    
                              }
                          }
                    }                      
                      
                    // Delete also the assigned preview file from this download, when this option is activated in config
                    if ($params->get('delete_also_preview_files_from_downloads') == 1){
                        if ($table->preview_filename) {
                            if (!File::delete($preview_folder.$table->preview_filename)) {
                                $del_image_error = Text::_('COM_JDOWNLOADS_BACKEND_FILESEDIT_DEL_IMAGES_ERROR');
                            }    
                        }
                    }                   
                    
                    if ($file_delete == 1 && $del_error){
                        // Create error message
                        Factory::getApplication()->enqueueMessage( $del_error, 'warning');
                    }
                    
                    if ($del_image_error){
                        // Create error message
                        Factory::getApplication()->enqueueMessage( $del_image_error, 'warning');
                    }                          
                    
                    // Delete now the row in table
                    if (!$table->delete($pk)) {
                        $this->setError($table->getError());
                        return false;
                    }

                    // Trigger the onContentAfterDelete event.
                    Factory::getApplication()->triggerEvent($this->event_after_delete, array($context, $table));

                } else {

                    // Prune items that you can't change.
                    unset($pks[$i]);
                    $error = $this->getError();
                    if ($error) {
                        Factory::getApplication()->enqueueMessage( $error, 'warning');
                        return false;
                    } else {
                        Factory::getApplication()->enqueueMessage( Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), 'warning');
                        return false;
                    }
                }
            } else {
                $this->setError($table->getError());
                return false;
            }
        }

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

        return true;      
    } 
    
    /**
     * Method to toggle the featured setting of Downloads.
     *
     * @param   array    $pks    The ids of the items to toggle.
     * @param   integer  $value  The value to toggle to.
     *
     * @return  boolean  True on success.
     */
    public function featured($pks, $value = 0)
    {
        // Sanitize the ids.
        $pks = (array) $pks;
        ArrayHelper::toInteger($pks);

        if (empty($pks)){
            $this->setError(Text::_('COM_JDOWNLOADS_NO_ITEM_SELECTED'));
            return false;
        }

        try
        {
            $db = $this->getDatabase();
            $query = $db->getQuery(true)
                        ->update($db->quoteName('#__jdownloads_files'))
                        ->set('featured = ' . (int) $value)
                        ->where('id IN (' . implode(',', $pks) . ')');
            $db->setQuery($query);
            $db->execute();
        }
        catch (Exception $e)
        {
            $this->setError($e->getMessage());
            return false;
        }
        $this->cleanCache();
        return true;
    }
    
}
?>