event_after_delete = $config['event_after_delete']; } elseif (empty($this->event_after_delete)) { $this->event_after_delete = 'onContentAfterDelete'; } if (isset($config['event_after_save'])) { $this->event_after_save = $config['event_after_save']; } elseif (empty($this->event_after_save)) { $this->event_after_save = 'onContentAfterSave'; } if (isset($config['event_before_delete'])) { $this->event_before_delete = $config['event_before_delete']; } elseif (empty($this->event_before_delete)) { $this->event_before_delete = 'onContentBeforeDelete'; } if (isset($config['event_before_save'])) { $this->event_before_save = $config['event_before_save']; } elseif (empty($this->event_before_save)) { $this->event_before_save = 'onContentBeforeSave'; } if (isset($config['event_change_state'])) { $this->event_change_state = $config['event_change_state']; } elseif (empty($this->event_change_state)) { $this->event_change_state = 'onContentChangeState'; } // Guess the JText message prefix. Defaults to the option. if (isset($config['text_prefix'])) { $this->text_prefix = strtoupper($config['text_prefix']); } elseif (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } } /** * 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. * * @since 12.2 */ public function batch($commands, $pks, $contexts) { // Sanitize ids. $pks = array_unique($pks); JArrayHelper::toInteger($pks); // Remove any values of zero. if (array_search(0, $pks, true)) { unset($pks[array_search(0, $pks, true)]); } if (empty($pks)) { $this->setError(JText::_('JGLOBAL_NO_ITEM_SELECTED')); return false; } $done = false; if (!empty($commands['category_id'])) { $cmd = JArrayHelper::getValue($commands, 'move_copy', 'c'); if ($cmd == 'c') { $result = $this->batchCopy($commands['category_id'], $pks, $contexts); if (is_array($result)) { $pks = $result; } else { return false; } } elseif ($cmd == 'm' && !$this->batchMove($commands['category_id'], $pks, $contexts)) { return false; } $done = true; } if (!empty($commands['assetgroup_id'])) { if (!$this->batchAccess($commands['assetgroup_id'], $pks, $contexts)) { return false; } $done = true; } if (!empty($commands['language_id'])) { if (!$this->batchLanguage($commands['language_id'], $pks, $contexts)) { return false; } $done = true; } if (!empty($commands['tag'])) { if (!$this->batchTag($commands['tag'], $pks, $contexts)) { return false; } $done = true; } if (!$done) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_INSUFFICIENT_BATCH_INFORMATION')); return false; } // Clear the cache $this->cleanCache(); return true; } /** * Batch access level changes for a group of rows. * * @param integer $value The new value matching an Asset Group ID. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 12.2 */ protected function batchAccess($value, $pks, $contexts) { // Set the variables $user = JFactory::getUser(); $table = $this->getTable(); foreach ($pks as $pk) { if ($user->authorise('core.edit', $contexts[$pk])) { $table->reset(); $table->load($pk); $table->access = (int) $value; if (!$table->store()) { $this->setError($table->getError()); return false; } } else { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Batch copy items to a new category or current. * * @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. * * @since 12.2 */ protected function batchCopy($value, $pks, $contexts) { $categoryId = (int) $value; $table = $this->getTable(); $i = 0; // Check that the category exists if ($categoryId) { $categoryTable = JTable::getInstance('Category'); if (!$categoryTable->load($categoryId)) { if ($error = $categoryTable->getError()) { // Fatal error $this->setError($error); return false; } else { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND')); return false; } } } if (empty($categoryId)) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND')); return false; } // Check that the user has create permission for the component $extension = JFactory::getApplication()->input->get('option', ''); $user = JFactory::getUser(); if (!$user->authorise('core.create', $extension . '.category.' . $categoryId)) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE')); return false; } // Parent exists so we let's proceed while (!empty($pks)) { // Pop the first ID off the stack $pk = array_shift($pks); $table->reset(); // Check that the row actually exists if (!$table->load($pk)) { if ($error = $table->getError()) { // Fatal error $this->setError($error); return false; } else { // Not fatal error $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); continue; } } // Alter the title & alias $data = $this->generateNewTitle($categoryId, $table->alias, $table->title); $table->title = $data['0']; $table->alias = $data['1']; // Reset the ID because we are making a copy $table->id = 0; // New category ID $table->catid = $categoryId; // TODO: Deal with ordering? // $table->ordering = 1; // Check the row. if (!$table->check()) { $this->setError($table->getError()); return false; } // Store the row. if (!$table->store()) { $this->setError($table->getError()); return false; } // Get the new item ID $newId = $table->get('id'); // Add the new ID to the array $newIds[$i] = $newId; $i++; } // Clean the cache $this->cleanCache(); return $newIds; } /** * Batch language changes for a group of rows. * * @param string $value The new value matching a language. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 11.3 */ protected function batchLanguage($value, $pks, $contexts) { // Set the variables $user = JFactory::getUser(); $table = $this->getTable(); foreach ($pks as $pk) { if ($user->authorise('core.edit', $contexts[$pk])) { $table->reset(); $table->load($pk); $table->language = $value; if (!$table->store()) { $this->setError($table->getError()); return false; } } else { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Batch move items to a new category * * @param integer $value The new category ID. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 12.2 */ protected function batchMove($value, $pks, $contexts) { $categoryId = (int) $value; $table = $this->getTable(); // Check that the category exists if ($categoryId) { $categoryTable = JTable::getInstance('Category'); if (!$categoryTable->load($categoryId)) { if ($error = $categoryTable->getError()) { // Fatal error $this->setError($error); return false; } else { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND')); return false; } } } if (empty($categoryId)) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_MOVE_CATEGORY_NOT_FOUND')); return false; } // Check that user has create and edit permission for the component $extension = JFactory::getApplication()->input->get('option', ''); $user = JFactory::getUser(); if (!$user->authorise('core.create', $extension . '.category.' . $categoryId)) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE')); return false; } // Parent exists so we proceed foreach ($pks as $pk) { if (!$user->authorise('core.edit', $contexts[$pk])) { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } // Check that the row actually exists if (!$table->load($pk)) { if ($error = $table->getError()) { // Fatal error $this->setError($error); return false; } else { // Not fatal error $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_MOVE_ROW_NOT_FOUND', $pk)); continue; } } // Set the new category ID $table->catid = $categoryId; // Check the row. if (!$table->check()) { $this->setError($table->getError()); return false; } // Store the row. if (!$table->store()) { $this->setError($table->getError()); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Batch tag a list of item. * * @param integer $value The value of the new tag. * @param array $pks An array of row IDs. * @param array $contexts An array of item contexts. * * @return void. * * @since 3.1 */ protected function batchTag($value, $pks, $contexts) { // Set the variables $user = JFactory::getUser(); $table = $this->getTable(); foreach ($pks as $pk) { if ($user->authorise('core.edit', $contexts[$pk])) { $table->reset(); $table->load($pk); $tags = array($value); /** * @var JTableObserverTags $tagsObserver */ $tagsObserver = $table->getObserverOfClass('JTableObserverTags'); $result = $tagsObserver->setNewTags($tags, false); if (!$result) { $this->setError($table->getError()); return false; } } else { $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } } // Clean the cache $this->cleanCache(); return true; } /** * Method to test whether a record can be deleted. * * @param object $record A record object. * * @return boolean True if allowed to delete the record. Defaults to the permission for the component. * * @since 12.2 */ protected function canDelete($record) { $user = JFactory::getUser(); return $user->authorise('core.delete', $this->option); } /** * Method to test whether a record can be deleted. * * @param object $record A record object. * * @return boolean True if allowed to change the state of the record. Defaults to the permission for the component. * * @since 12.2 */ protected function canEditState($record) { $user = JFactory::getUser(); return $user->authorise('core.edit.state', $this->option); } /** * Method override to check-in a record or an array of record * * @param mixed $pks The ID of the primary key or an array of IDs * * @return mixed Boolean false if there is an error, otherwise the count of records checked in. * * @since 12.2 */ public function checkin($pks = array()) { $pks = (array) $pks; $table = $this->getTable(); $count = 0; if (empty($pks)) { $pks = array((int) $this->getState($this->getName() . '.id')); } // Check in all items. foreach ($pks as $pk) { if ($table->load($pk)) { if ($table->checked_out > 0) { if (!parent::checkin($pk)) { return false; } $count++; } } else { $this->setError($table->getError()); return false; } } return $count; } /** * Method override to check-out a record. * * @param integer $pk The ID of the primary key. * * @return boolean True if successful, false if an error occurs. * * @since 12.2 */ public function checkout($pk = null) { $pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id'); return parent::checkout($pk); } /** * Method to delete one or more records. * * @param array &$pks An array of record primary keys. * * @return boolean True if successful, false if an error occurs. * * @since 12.2 */ public function delete(&$pks) { $dispatcher = JEventDispatcher::getInstance(); $pks = (array) $pks; $table = $this->getTable(); // Include the content plugins for the on delete events. JPluginHelper::importPlugin('content'); // Iterate the items to delete each one. foreach ($pks as $i => $pk) { if ($table->load($pk)) { if ($this->canDelete($table)) { $context = $this->option . '.' . $this->name; // Trigger the onContentBeforeDelete event. $result = $dispatcher->trigger($this->event_before_delete, array($context, $table)); if (in_array(false, $result, true)) { $this->setError($table->getError()); return false; } if (!$table->delete($pk)) { $this->setError($table->getError()); return false; } // Trigger the onContentAfterDelete event. $dispatcher->trigger($this->event_after_delete, array($context, $table)); } else { // Prune items that you can't change. unset($pks[$i]); $error = $this->getError(); if ($error) { JLog::add($error, JLog::WARNING, 'jerror'); return false; } else { JLog::add(JText::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), JLog::WARNING, 'jerror'); return false; } } } else { $this->setError($table->getError()); return false; } } // Clear the component's cache $this->cleanCache(); return true; } /** * Method to change the title & alias. * * @param integer $category_id The id of the category. * @param string $alias The alias. * @param string $title The title. * * @return array Contains the modified title and alias. * * @since 12.2 */ protected function generateNewTitle($category_id, $alias, $title) { // Alter the title & alias $table = $this->getTable(); while ($table->load(array('alias' => $alias, 'catid' => $category_id))) { $title = JString::increment($title); $alias = JString::increment($alias, 'dash'); } return array($title, $alias); } /** * Method to get a single record. * * @param integer $pk The id of the primary key. * * @return mixed Object on success, false on failure. * * @since 12.2 */ public function getItem($pk = null) { $pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id'); $table = $this->getTable(); if ($pk > 0) { // Attempt to load the row. $return = $table->load($pk); // Check for a table object error. if ($return === false && $table->getError()) { $this->setError($table->getError()); return false; } } // Convert to the JObject before adding other data. $properties = $table->getProperties(1); $item = JArrayHelper::toObject($properties, 'JObject'); if (property_exists($item, 'params')) { $registry = new JRegistry; $registry->loadString($item->params); $item->params = $registry->toArray(); } return $item; } /** * A protected method to get a set of ordering conditions. * * @param JTable $table A JTable object. * * @return array An array of conditions to add to ordering queries. * * @since 12.2 */ protected function getReorderConditions($table) { return array(); } /** * Stock method to auto-populate the model state. * * @return void * * @since 12.2 */ protected function populateState() { $table = $this->getTable(); $key = $table->getKeyName(); // Get the pk of the record from the request. $pk = JFactory::getApplication()->input->getInt($key); $this->setState($this->getName() . '.id', $pk); // Load the parameters. $value = JComponentHelper::getParams($this->option); $this->setState('params', $value); } /** * Prepare and sanitise the table data prior to saving. * * @param JTable $table A reference to a JTable object. * * @return void * * @since 12.2 */ protected function prepareTable($table) { // Derived class will provide its own implementation if required. } /** * Method to change the published state of one or more records. * * @param array &$pks A list of the primary keys to change. * @param integer $value The value of the published state. * * @return boolean True on success. * * @since 12.2 */ public function publish(&$pks, $value = 1) { $dispatcher = JEventDispatcher::getInstance(); $user = JFactory::getUser(); $table = $this->getTable(); $pks = (array) $pks; // Include the content plugins for the change of state event. JPluginHelper::importPlugin('content'); // 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]); JLog::add(JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), JLog::WARNING, 'jerror'); return false; } } } // Attempt to change the state of the records. if (!$table->publish($pks, $value, $user->get('id'))) { $this->setError($table->getError()); return false; } $context = $this->option . '.' . $this->name; // Trigger the onContentChangeState event. $result = $dispatcher->trigger($this->event_change_state, array($context, $pks, $value)); if (in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Clear the component's cache $this->cleanCache(); return true; } /** * Method to adjust the ordering of a row. * * Returns NULL if the user did not have edit * privileges for any of the selected primary keys. * * @param integer $pks The ID of the primary key to move. * @param integer $delta Increment, usually +1 or -1 * * @return mixed False on failure or error, true on success, null if the $pk is empty (no items selected). * * @since 12.2 */ public function reorder($pks, $delta = 0) { $table = $this->getTable(); $pks = (array) $pks; $result = true; $allowed = true; foreach ($pks as $i => $pk) { $table->reset(); if ($table->load($pk) && $this->checkout($pk)) { // Access checks. if (!$this->canEditState($table)) { // Prune items that you can't change. unset($pks[$i]); $this->checkin($pk); JLog::add(JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), JLog::WARNING, 'jerror'); $allowed = false; continue; } $where = $this->getReorderConditions($table); if (!$table->move($delta, $where)) { $this->setError($table->getError()); unset($pks[$i]); $result = false; } $this->checkin($pk); } else { $this->setError($table->getError()); unset($pks[$i]); $result = false; } } if ($allowed === false && empty($pks)) { $result = null; } // Clear the component's cache if ($result == true) { $this->cleanCache(); } return $result; } /** * Method to save the form data. * * @param array $data The form data. * * @return boolean True on success, False on error. * * @since 12.2 */ public function save($data) { $dispatcher = JEventDispatcher::getInstance(); $table = $this->getTable(); if ((!empty($data['tags']) && $data['tags'][0] != '')) { $table->newTags = $data['tags']; } $key = $table->getKeyName(); $pk = (!empty($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id'); $isNew = true; // Include the content plugins for the on save events. JPluginHelper::importPlugin('content'); // Allow an exception to be thrown. try { // Load the row if saving an existing record. if ($pk > 0) { $table->load($pk); $isNew = false; } // Bind the data. if (!$table->bind($data)) { $this->setError($table->getError()); return false; } // Prepare the row for saving $this->prepareTable($table); // Check the data. if (!$table->check()) { $this->setError($table->getError()); return false; } // Trigger the onContentBeforeSave event. $result = $dispatcher->trigger($this->event_before_save, array($this->option . '.' . $this->name, $table, $isNew)); if (in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Store the data. if (!$table->store()) { $this->setError($table->getError()); return false; } // Clean the cache. $this->cleanCache(); // Trigger the onContentAfterSave event. $dispatcher->trigger($this->event_after_save, array($this->option . '.' . $this->name, $table, $isNew)); } catch (Exception $e) { $this->setError($e->getMessage()); return false; } $pkName = $table->getKeyName(); if (isset($table->$pkName)) { $this->setState($this->getName() . '.id', $table->$pkName); } $this->setState($this->getName() . '.new', $isNew); return true; } /** * Saves the manually set order of records. * * @param array $pks An array of primary key ids. * @param integer $order +1 or -1 * * @return mixed * * @since 12.2 */ public function saveorder($pks = null, $order = null) { $table = $this->getTable(); $conditions = array(); if (empty($pks)) { return JError::raiseWarning(500, JText::_($this->text_prefix . '_ERROR_NO_ITEMS_SELECTED')); } // Update ordering values foreach ($pks as $i => $pk) { $table->load((int) $pk); // Access checks. if (!$this->canEditState($table)) { // Prune items that you can't change. unset($pks[$i]); JLog::add(JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), JLog::WARNING, 'jerror'); } elseif ($table->ordering != $order[$i]) { $table->ordering = $order[$i]; if (!$table->store()) { $this->setError($table->getError()); return false; } // Remember to reorder within position and client_id $condition = $this->getReorderConditions($table); $found = false; foreach ($conditions as $cond) { if ($cond[1] == $condition) { $found = true; break; } } if (!$found) { $key = $table->getKeyName(); $conditions[] = array($table->$key, $condition); } } } // Execute reorder for each category. foreach ($conditions as $cond) { $table->load($cond[0]); $table->reorder($cond[1]); } // Clear the component's cache $this->cleanCache(); return true; } }