
Since Joomla 3.0.0, the Joomla! CMS comes with its own custom Icomoon Font Set. These icon fonts are available by default for use in the "Protostar" (Frontend), "Isis" (Administrator) and "Hathor" (Administrator) Templates.
Step 1:
sql Table Modification or Must be added
`asset_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'FK to the #__assets table.', `ordering` int(11) NOT NULL DEFAULT 0,
Step 2:
In controllerclass CoursesController extends AdminController { public function getModel($name = 'Courses', $prefix = 'Administrator', $config = array('ignore_request' => true)) { return parent::getModel($name, $prefix, $config); } }
Step 3:
In Table
class CoursesTable extends Table { public function __construct(DatabaseDriver $db) { parent::__construct('#__lmst_courses', 'id', $db); } }
Step 4:
In tmpl
use Joomla\CMS\Session\Session; $listOrder = $this->escape($this->state->get('list.ordering')); $listDirn = $this->escape($this->state->get('list.direction')); $saveOrder = $listOrder == 'ordering'; if (strpos($listOrder, 'publish_up') !== false) { $orderingColumn = 'publish_up'; } elseif (strpos($listOrder, 'publish_down') !== false) { $orderingColumn = 'publish_down'; } elseif (strpos($listOrder, 'modified') !== false) { $orderingColumn = 'modified'; } else { $orderingColumn = 'created'; } if ($saveOrder && !empty($this->items)) { $saveOrderingUrl= 'index.php?option=com_lmstarbiats&task=lessons.saveOrderAjax&tmpl=component&' . Session::getFormToken() . '=1'; HTMLHelper::_('draggablelist.draggable'); }<tbody class="js-draggable" data-url=" " data-direction=" " data-nested="true" > >
Step 5:
In Model
use Joomla\CMS\UCM\UCMType; class CoursesModel extends ListModel { public function __construct($config = array()) { if (empty($config['filter_fields'])) { $config['filter_fields'] = array( 'id', 'ordering', 'a.ordering', ); } parent::__construct($config); } /* ADD AdminModel Functions directly in your Model */ /** * 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 1.7 */ public function batch($commands, $pks, $contexts) { // Sanitize ids. $pks = array_unique($pks); $pks = ArrayHelper::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(Text::_('JGLOBAL_NO_ITEM_SELECTED')); return false; } $done = false; // Initialize re-usable member properties $this->initBatch(); if ($this->batch_copymove && !empty($commands[$this->batch_copymove])) { $cmd = ArrayHelper::getValue($commands, 'move_copy', 'c'); if ($cmd === 'c') { $result = $this->batchCopy($commands[$this->batch_copymove], $pks, $contexts); if (\is_array($result)) { foreach ($result as $old => $new) { $contexts[$new] = $contexts[$old]; } $pks = array_values($result); } else { return false; } } elseif ($cmd === 'm' && !$this->batchMove($commands[$this->batch_copymove], $pks, $contexts)) { 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 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 1.7 */ protected function batchAccess($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); foreach ($pks as $pk) { if ($this->user->authorise('core.edit', $contexts[$pk])) { $this->table->reset(); $this->table->load($pk); $this->table->access = (int) $value; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'access'] ); $this->dispatchEvent($event); // Check the row. if (!$this->table->check()) { $this->setError($this->table->getError()); return false; } if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } } else { $this->setError(Text::_('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 array|boolean An array of new IDs on success, boolean false on failure. * * @since 1.7 */ protected function batchCopy($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); $categoryId = $value; if (!$this->checkCategoryId($categoryId)) { return false; } $newIds = []; $db = $this->getDbo(); // Parent exists so 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; } } // Check for asset_id if ($this->table->hasField($this->table->getColumnAlias('asset_id'))) { $oldAssetId = $this->table->asset_id; } $this->generateTitle($categoryId, $this->table); // Reset the ID because we are making a copy $this->table->id = 0; // Unpublish because we are making a copy if (isset($this->table->published)) { $this->table->published = 0; } elseif (isset($this->table->state)) { $this->table->state = 0; } $hitsAlias = $this->table->getColumnAlias('hits'); if (isset($this->table->$hitsAlias)) { $this->table->$hitsAlias = 0; } // New category ID $this->table->catid = $categoryId; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'copy'] ); $this->dispatchEvent($event); // @todo: Deal with ordering? // $this->table->ordering = 1; // 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; } // Get the new item ID $newId = $this->table->get('id'); if (!empty($oldAssetId)) { $dbType = strtolower($db->getServerType()); // Copy rules $query = $db->getQuery(true); $query->clear() ->update($db->quoteName('#__assets', 't')); if ($dbType === 'mysql') { $query->set($db->quoteName('t.rules') . ' = ' . $db->quoteName('s.rules')); } else { $query->set($db->quoteName('rules') . ' = ' . $db->quoteName('s.rules')); } $query->join( 'INNER', $db->quoteName('#__assets', 's'), $db->quoteName('s.id') . ' = :oldassetid' ) ->where($db->quoteName('t.id') . ' = :assetid') ->bind(':oldassetid', $oldAssetId, ParameterType::INTEGER) ->bind(':assetid', $this->table->asset_id, ParameterType::INTEGER); $db->setQuery($query)->execute(); } $this->cleanupPostBatchCopy($this->table, $newId, $pk); // Add the new ID to the array $newIds[$pk] = $newId; } // Clean the cache $this->cleanCache(); return $newIds; } /** * Function that can be overridden 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 * * @since 3.8.12 */ protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId) { } /** * 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 2.5 */ protected function batchLanguage($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); foreach ($pks as $pk) { if ($this->user->authorise('core.edit', $contexts[$pk])) { $this->table->reset(); $this->table->load($pk); $this->table->language = $value; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'language'] ); $this->dispatchEvent($event); // Check the row. if (!$this->table->check()) { $this->setError($this->table->getError()); return false; } if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } } else { $this->setError(Text::_('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 1.7 */ protected function batchMove($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); $categoryId = (int) $value; if (!$this->checkCategoryId($categoryId)) { return false; } // Parent exists so we proceed foreach ($pks as $pk) { if (!$this->user->authorise('core.edit', $contexts[$pk])) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT')); return false; } // 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; } } // Set the new category ID $this->table->catid = $categoryId; $event = new BeforeBatchEvent( $this->event_before_batch, ['src' => $this->table, 'type' => 'move'] ); $this->dispatchEvent($event); // 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; } } // 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 boolean True if successful, false otherwise and internal error is set. * * @since 3.1 */ protected function batchTag($value, $pks, $contexts) { // Initialize re-usable member properties, and re-usable local variables $this->initBatch(); $tags = [$value]; foreach ($pks as $pk) { if ($this->user->authorise('core.edit', $contexts[$pk])) { $this->table->reset(); $this->table->load($pk); $setTagsEvent = \Joomla\CMS\Event\AbstractEvent::create( 'onTableSetNewTags', [ 'subject' => $this->table, 'newTags' => $tags, 'replaceTags' => false, ] ); try { $this->table->getDispatcher()->dispatch('onTableSetNewTags', $setTagsEvent); } catch (\RuntimeException $e) { $this->setError($e->getMessage()); return false; } } else { $this->setError(Text::_('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 1.6 */ protected function canDelete($record) { return Factory::getUser()->authorise('core.delete', $this->option); } /** * Method to test whether a record can have its state changed. * * @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 1.6 */ protected function canEditState($record) { return Factory::getUser()->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 integer|boolean Boolean false if there is an error, otherwise the count of records checked in. * * @since 1.6 */ public function checkin($pks = []) { $pks = (array) $pks; $table = $this->getTable(); $count = 0; if (empty($pks)) { $pks = [(int) $this->getState($this->getName() . '.id')]; } $checkedOutField = $table->getColumnAlias('checked_out'); // Check in all items. foreach ($pks as $pk) { if ($table->load($pk)) { if ($table->{$checkedOutField} > 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 1.6 */ 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 1.6 */ public function delete(&$pks) { $pks = ArrayHelper::toInteger((array) $pks); $table = $this->getTable(); // Include the plugins for the delete events. PluginHelper::importPlugin($this->events_map['delete']); // 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 before delete event. $result = Factory::getApplication()->triggerEvent($this->event_before_delete, [$context, $table]); if (\in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Multilanguage: if associated, delete the item in the _associations table if ($this->associationsContext && Associations::isEnabled()) { $db = $this->getDbo(); $query = $db->getQuery(true) ->select( [ 'COUNT(*) AS ' . $db->quoteName('count'), $db->quoteName('as1.key'), ] ) ->from($db->quoteName('#__associations', 'as1')) ->join('LEFT', $db->quoteName('#__associations', 'as2'), $db->quoteName('as1.key') . ' = ' . $db->quoteName('as2.key')) ->where( [ $db->quoteName('as1.context') . ' = :context', $db->quoteName('as1.id') . ' = :pk', ] ) ->bind(':context', $this->associationsContext) ->bind(':pk', $pk, ParameterType::INTEGER) ->group($db->quoteName('as1.key')); $db->setQuery($query); $row = $db->loadAssoc(); if (!empty($row['count'])) { $query = $db->getQuery(true) ->delete($db->quoteName('#__associations')) ->where( [ $db->quoteName('context') . ' = :context', $db->quoteName('key') . ' = :key', ] ) ->bind(':context', $this->associationsContext) ->bind(':key', $row['key']); if ($row['count'] > 2) { $query->where($db->quoteName('id') . ' = :pk') ->bind(':pk', $pk, ParameterType::INTEGER); } $db->setQuery($query); $db->execute(); } } if (!$table->delete($pk)) { $this->setError($table->getError()); return false; } // Trigger the after event. Factory::getApplication()->triggerEvent($this->event_after_delete, [$context, $table]); } else { // Prune items that you can't change. unset($pks[$i]); $error = $this->getError(); if ($error) { Log::add($error, Log::WARNING, 'jerror'); return false; } else { Log::add(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), Log::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 $categoryId The id of the category. * @param string $alias The alias. * @param string $title The title. * * @return array Contains the modified title and alias. * * @since 1.7 */ protected function generateNewTitle($categoryId, $alias, $title) { // Alter the title & alias $table = $this->getTable(); $aliasField = $table->getColumnAlias('alias'); $catidField = $table->getColumnAlias('catid'); $titleField = $table->getColumnAlias('title'); while ($table->load([$aliasField => $alias, $catidField => $categoryId])) { if ($title === $table->$titleField) { $title = StringHelper::increment($title); } $alias = StringHelper::increment($alias, 'dash'); } return [$title, $alias]; } /** * Method to get a single record. * * @param integer $pk The id of the primary key. * * @return CMSObject|boolean Object on success, false on failure. * * @since 1.6 */ 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) { // If there was no underlying error, then the false means there simply was not a row in the db for this $pk. if (!$table->getError()) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST')); } else { $this->setError($table->getError()); } return false; } } // Convert to the CMSObject before adding other data. $properties = $table->getProperties(1); $item = ArrayHelper::toObject($properties, CMSObject::class); if (property_exists($item, 'params')) { $registry = new Registry($item->params); $item->params = $registry->toArray(); } return $item; } /** * A protected method to get a set of ordering conditions. * * @param Table $table A Table object. * * @return string[] An array of conditions to add to ordering queries. * * @since 1.6 */ protected function getReorderConditions($table) { return []; } /** * Prepare and sanitise the table data prior to saving. * * @param Table $table A reference to a Table object. * * @return void * * @since 1.6 */ 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 1.6 */ public function publish(&$pks, $value = 1) { $user = Factory::getUser(); $table = $this->getTable(); $pks = (array) $pks; $context = $this->option . '.' . $this->name; // Include the plugins for the change of state event. PluginHelper::importPlugin($this->events_map['change_state']); // 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; } // If the table is checked out by another user, drop it and report to the user trying to change its state. if ($table->hasField('checked_out') && $table->checked_out && ($table->checked_out != $user->id)) { Log::add(Text::_('JLIB_APPLICATION_ERROR_CHECKIN_USER_MISMATCH'), Log::WARNING, 'jerror'); // Prune items that you can't change. unset($pks[$i]); return false; } /** * Prune items that are already at the given state. Note: Only models whose table correctly * sets 'published' column alias (if different than published) will benefit from this */ $publishedColumnName = $table->getColumnAlias('published'); if (property_exists($table, $publishedColumnName) && $table->get($publishedColumnName, $value) == $value) { unset($pks[$i]); } } } // Check if there are items to change if (!\count($pks)) { return true; } // Trigger the before change state event. $result = Factory::getApplication()->triggerEvent($this->event_before_change_state, [$context, $pks, $value]); if (\in_array(false, $result, true)) { $this->setError($table->getError()); return false; } // Attempt to change the state of the records. if (!$table->publish($pks, $value, $user->get('id'))) { $this->setError($table->getError()); return false; } // Trigger the change state event. $result = Factory::getApplication()->triggerEvent($this->event_change_state, [$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 boolean|null False on failure or error, true on success, null if the $pk is empty (no items selected). * * @since 1.6 */ 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); Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::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 1.6 */ public function save($data) { $table = $this->getTable(); $context = $this->option . '.' . $this->name; $app = Factory::getApplication(); if (\array_key_exists('tags', $data) && \is_array($data['tags'])) { $table->newTags = $data['tags']; } $key = $table->getKeyName(); $pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id'); $isNew = true; // Include the plugins for the save events. PluginHelper::importPlugin($this->events_map['save']); // 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 before save event. $result = $app->triggerEvent($this->event_before_save, [$context, $table, $isNew, $data]); 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 after save event. $app->triggerEvent($this->event_after_save, [$context, $table, $isNew, $data]); } catch (\Exception $e) { $this->setError($e->getMessage()); return false; } if (isset($table->$key)) { $this->setState($this->getName() . '.id', $table->$key); } $this->setState($this->getName() . '.new', $isNew); 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 === '*') { $app->enqueueMessage( Text::_(strtoupper($this->option) . '_ERROR_ALL_LANGUAGE_ASSOCIATED'), 'warning' ); } // Get associationskey for edited item $db = $this->getDbo(); $id = (int) $table->$key; $query = $db->getQuery(true) ->select($db->quoteName('key')) ->from($db->quoteName('#__associations')) ->where($db->quoteName('context') . ' = :context') ->where($db->quoteName('id') . ' = :id') ->bind(':context', $this->associationsContext) ->bind(':id', $id, ParameterType::INTEGER); $db->setQuery($query); $oldKey = $db->loadResult(); if ($associations || $oldKey !== null) { // Deleting old associations for the associated items $query = $db->getQuery(true) ->delete($db->quoteName('#__associations')) ->where($db->quoteName('context') . ' = :context') ->bind(':context', $this->associationsContext); $where = []; if ($associations) { $where[] = $db->quoteName('id') . ' IN (' . implode(',', $query->bindArray(array_values($associations))) . ')'; } if ($oldKey !== null) { $where[] = $db->quoteName('key') . ' = :oldKey'; $query->bind(':oldKey', $oldKey); } $query->extendWhere('AND', $where, 'OR'); $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($db->quoteName('#__associations')) ->columns( [ $db->quoteName('id'), $db->quoteName('context'), $db->quoteName('key'), ] ); foreach ($associations as $id) { $query->values( implode( ',', $query->bindArray( [$id, $this->associationsContext, $key], [ParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING] ) ) ); } $db->setQuery($query); $db->execute(); } } if ($app->getInput()->get('task') == 'editAssociations') { return $this->redirectToAssociations($data); } 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 boolean Boolean true on success, false on failure * * @since 1.6 */ public function saveorder($pks = [], $order = null) { // Initialize re-usable member properties $this->initBatch(); $conditions = []; if (empty($pks)) { Factory::getApplication()->enqueueMessage(Text::_($this->text_prefix . '_ERROR_NO_ITEMS_SELECTED'), 'error'); return false; } $orderingField = $this->table->getColumnAlias('ordering'); // Update ordering values foreach ($pks as $i => $pk) { $this->table->load((int) $pk); // We don't want to modify tags on reorder, not removing the tagsHelper removes all associated tags if ($this->table instanceof TaggableTableInterface) { $this->table->clearTagsHelper(); } // Access checks. if (!$this->canEditState($this->table)) { // Prune items that you can't change. unset($pks[$i]); Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror'); } elseif ($this->table->$orderingField != $order[$i]) { $this->table->$orderingField = $order[$i]; if (!$this->table->store()) { $this->setError($this->table->getError()); return false; } // Remember to reorder within position and client_id $condition = $this->getReorderConditions($this->table); $found = false; foreach ($conditions as $cond) { if ($cond[1] == $condition) { $found = true; break; } } if (!$found) { $key = $this->table->getKeyName(); $conditions[] = [$this->table->$key, $condition]; } } } // Execute reorder for each category. foreach ($conditions as $cond) { $this->table->load($cond[0]); $this->table->reorder($cond[1]); } // Clear the component's cache $this->cleanCache(); return true; } /** * Method to check the validity of the category ID for batch copy and move * * @param integer $categoryId The category ID to check * * @return boolean * * @since 3.2 */ protected function checkCategoryId($categoryId) { // Check that the category exists if ($categoryId) { $categoryTable = Table::getInstance('Category'); if (!$categoryTable->load($categoryId)) { if ($error = $categoryTable->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 = Factory::getUser(); if (!$user->authorise('core.create', $extension . '.category.' . $categoryId)) { $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_CREATE')); return false; } return true; } /** * A method to preprocess generating a new title in order to allow tables with alternative names * for alias and title to use the batch move and copy methods * * @param integer $categoryId The target category id * @param Table $table The Table within which move or copy is taking place * * @return void * * @since 3.2 */ public function generateTitle($categoryId, $table) { // Alter the title & alias $titleField = $table->getColumnAlias('title'); $aliasField = $table->getColumnAlias('alias'); $data = $this->generateNewTitle($categoryId, $table->$aliasField, $table->$titleField); $table->$titleField = $data['0']; $table->$aliasField = $data['1']; } /** * Method to initialize member variables used by batch methods and other methods like saveorder() * * @return void * * @since 3.8.2 */ public function initBatch() { if ($this->batchSet === null) { $this->batchSet = true; // Get current user $this->user = Factory::getUser(); // Get table $this->table = $this->getTable(); // Get table class name $tc = explode('\\', \get_class($this->table)); $this->tableClassName = end($tc); // Get UCM Type data $this->contentType = new UCMType(); $this->type = $this->contentType->getTypeByTable($this->tableClassName) ?: $this->contentType->getTypeByAlias($this->typeAlias); } } /** * Method to load an item in com_associations. * * @param array $data The form data. * * @return boolean True if successful, false otherwise. * * @since 3.9.0 * * @deprecated 4.3 will be removed in 6.0 * It is handled by regular save method now. */ public function editAssociations($data) { // Save the item return $this->save($data); } /** * Method to load an item in com_associations. * * @param array $data The form data. * * @return boolean True if successful, false otherwise. * * @throws \Exception * @since 3.9.17 */ protected function redirectToAssociations($data) { $app = Factory::getApplication(); $id = $data['id']; // Deal with categories associations if ($this->text_prefix === 'COM_CATEGORIES') { $extension = $app->getInput()->get('extension', 'com_content'); $this->typeAlias = $extension . '.category'; $component = strtolower($this->text_prefix); $view = 'category'; } else { $aliasArray = explode('.', $this->typeAlias); $component = $aliasArray[0]; $view = $aliasArray[1]; $extension = ''; } // Menu item redirect needs admin client $client = $component === 'com_menus' ? '&client_id=0' : ''; if ($id == 0) { $app->enqueueMessage(Text::_('JGLOBAL_ASSOCIATIONS_NEW_ITEM_WARNING'), 'error'); $app->redirect( Route::_('index.php?option=' . $component . '&view=' . $view . $client . '&layout=edit&id=' . $id . $extension, false) ); return false; } if ($data['language'] === '*') { $app->enqueueMessage(Text::_('JGLOBAL_ASSOC_NOT_POSSIBLE'), 'notice'); $app->redirect( Route::_('index.php?option=' . $component . '&view=' . $view . $client . '&layout=edit&id=' . $id . $extension, false) ); return false; } $languages = LanguageHelper::getContentLanguages([0, 1]); $target = ''; /** * If the site contains only 2 languages and an association exists for the item * load directly the associated target item in the side by side view * otherwise select already the target language */ if (count($languages) === 2) { $lang_code = []; foreach ($languages as $language) { $lang_code[] = $language->lang_code; } $refLang = [$data['language']]; $targetLang = array_diff($lang_code, $refLang); $targetLang = implode(',', $targetLang); $targetId = $data['associations'][$targetLang]; if ($targetId) { $target = '&target=' . $targetLang . '%3A' . $targetId . '%3Aedit'; } else { $target = '&target=' . $targetLang . '%3A0%3Aadd'; } } $app->redirect( Route::_( 'index.php?option=com_associations&view=association&layout=edit&itemtype=' . $this->typeAlias . '&task=association.edit&id=' . $id . $target, false ) ); return true; }