PageRenderTime 61ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_categories/models/category.php

https://github.com/J2MTecnologia/joomla-3.x
PHP | 1142 lines | 682 code | 159 blank | 301 comment | 99 complexity | 916d04898c81f4bfc5a7c56f6b0ce343 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * @package Joomla.Administrator
  4. * @subpackage com_categories
  5. *
  6. * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE.txt
  8. */
  9. defined('_JEXEC') or die;
  10. /**
  11. * Categories Component Category Model
  12. *
  13. * @package Joomla.Administrator
  14. * @subpackage com_categories
  15. * @since 1.6
  16. */
  17. class CategoriesModelCategory extends JModelAdmin
  18. {
  19. /**
  20. * @var string The prefix to use with controller messages.
  21. * @since 1.6
  22. */
  23. protected $text_prefix = 'COM_CATEGORIES';
  24. /**
  25. * The type alias for this content type. Used for content version history.
  26. *
  27. * @var string
  28. * @since 3.2
  29. */
  30. public $typeAlias = null;
  31. /**
  32. * Override parent constructor.
  33. *
  34. * @param array $config An optional associative array of configuration settings.
  35. *
  36. * @see JModelLegacy
  37. * @since 3.2
  38. */
  39. public function __construct($config = array())
  40. {
  41. parent::__construct($config);
  42. $extension = JFactory::getApplication()->input->get('extension', 'com_content');
  43. $this->typeAlias = $extension . '.category';
  44. }
  45. /**
  46. * Method to test whether a record can be deleted.
  47. *
  48. * @param object $record A record object.
  49. *
  50. * @return boolean True if allowed to delete the record. Defaults to the permission set in the component.
  51. *
  52. * @since 1.6
  53. */
  54. protected function canDelete($record)
  55. {
  56. if (!empty($record->id))
  57. {
  58. if ($record->published != -2)
  59. {
  60. return;
  61. }
  62. $user = JFactory::getUser();
  63. return $user->authorise('core.delete', $record->extension . '.category.' . (int) $record->id);
  64. }
  65. }
  66. /**
  67. * Method to test whether a record can have its state changed.
  68. *
  69. * @param object $record A record object.
  70. *
  71. * @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component.
  72. *
  73. * @since 1.6
  74. */
  75. protected function canEditState($record)
  76. {
  77. $user = JFactory::getUser();
  78. // Check for existing category.
  79. if (!empty($record->id))
  80. {
  81. return $user->authorise('core.edit.state', $record->extension . '.category.' . (int) $record->id);
  82. }
  83. // New category, so check against the parent.
  84. elseif (!empty($record->parent_id))
  85. {
  86. return $user->authorise('core.edit.state', $record->extension . '.category.' . (int) $record->parent_id);
  87. }
  88. // Default to component settings if neither category nor parent known.
  89. else
  90. {
  91. return $user->authorise('core.edit.state', $record->extension);
  92. }
  93. }
  94. /**
  95. * Method to get a table object, load it if necessary.
  96. *
  97. * @param string $type The table name. Optional.
  98. * @param string $prefix The class prefix. Optional.
  99. * @param array $config Configuration array for model. Optional.
  100. *
  101. * @return JTable A JTable object
  102. *
  103. * @since 1.6
  104. */
  105. public function getTable($type = 'Category', $prefix = 'CategoriesTable', $config = array())
  106. {
  107. return JTable::getInstance($type, $prefix, $config);
  108. }
  109. /**
  110. * Auto-populate the model state.
  111. *
  112. * Note. Calling getState in this method will result in recursion.
  113. *
  114. * @return void
  115. *
  116. * @since 1.6
  117. */
  118. protected function populateState()
  119. {
  120. $app = JFactory::getApplication('administrator');
  121. $parentId = $app->input->getInt('parent_id');
  122. $this->setState('category.parent_id', $parentId);
  123. // Load the User state.
  124. $pk = $app->input->getInt('id');
  125. $this->setState($this->getName() . '.id', $pk);
  126. $extension = $app->input->get('extension', 'com_content');
  127. $this->setState('category.extension', $extension);
  128. $parts = explode('.', $extension);
  129. // Extract the component name
  130. $this->setState('category.component', $parts[0]);
  131. // Extract the optional section name
  132. $this->setState('category.section', (count($parts) > 1) ? $parts[1] : null);
  133. // Load the parameters.
  134. $params = JComponentHelper::getParams('com_categories');
  135. $this->setState('params', $params);
  136. }
  137. /**
  138. * Method to get a category.
  139. *
  140. * @param integer $pk An optional id of the object to get, otherwise the id from the model state is used.
  141. *
  142. * @return mixed Category data object on success, false on failure.
  143. *
  144. * @since 1.6
  145. */
  146. public function getItem($pk = null)
  147. {
  148. if ($result = parent::getItem($pk))
  149. {
  150. // Prime required properties.
  151. if (empty($result->id))
  152. {
  153. $result->parent_id = $this->getState('category.parent_id');
  154. $result->extension = $this->getState('category.extension');
  155. }
  156. // Convert the metadata field to an array.
  157. $registry = new JRegistry;
  158. $registry->loadString($result->metadata);
  159. $result->metadata = $registry->toArray();
  160. // Convert the created and modified dates to local user time for display in the form.
  161. $tz = new DateTimeZone(JFactory::getApplication()->getCfg('offset'));
  162. if ((int) $result->created_time)
  163. {
  164. $date = new JDate($result->created_time);
  165. $date->setTimezone($tz);
  166. $result->created_time = $date->toSql(true);
  167. }
  168. else
  169. {
  170. $result->created_time = null;
  171. }
  172. if ((int) $result->modified_time)
  173. {
  174. $date = new JDate($result->modified_time);
  175. $date->setTimezone($tz);
  176. $result->modified_time = $date->toSql(true);
  177. }
  178. else
  179. {
  180. $result->modified_time = null;
  181. }
  182. if (!empty($result->id))
  183. {
  184. $result->tags = new JHelperTags;
  185. $result->tags->getTagIds($result->id, $result->extension . '.category');
  186. }
  187. }
  188. $assoc = $this->getAssoc();
  189. if ($assoc)
  190. {
  191. if ($result->id != null)
  192. {
  193. $result->associations = CategoriesHelper::getAssociations($result->id, $result->extension);
  194. JArrayHelper::toInteger($result->associations);
  195. }
  196. else
  197. {
  198. $result->associations = array();
  199. }
  200. }
  201. return $result;
  202. }
  203. /**
  204. * Method to get the row form.
  205. *
  206. * @param array $data Data for the form.
  207. * @param boolean $loadData True if the form is to load its own data (default case), false if not.
  208. *
  209. * @return mixed A JForm object on success, false on failure
  210. *
  211. * @since 1.6
  212. */
  213. public function getForm($data = array(), $loadData = true)
  214. {
  215. $extension = $this->getState('category.extension');
  216. $jinput = JFactory::getApplication()->input;
  217. // A workaround to get the extension into the model for save requests.
  218. if (empty($extension) && isset($data['extension']))
  219. {
  220. $extension = $data['extension'];
  221. $parts = explode('.', $extension);
  222. $this->setState('category.extension', $extension);
  223. $this->setState('category.component', $parts[0]);
  224. $this->setState('category.section', @$parts[1]);
  225. }
  226. // Get the form.
  227. $form = $this->loadForm('com_categories.category' . $extension, 'category', array('control' => 'jform', 'load_data' => $loadData));
  228. if (empty($form))
  229. {
  230. return false;
  231. }
  232. // Modify the form based on Edit State access controls.
  233. if (empty($data['extension']))
  234. {
  235. $data['extension'] = $extension;
  236. }
  237. $user = JFactory::getUser();
  238. if (!$user->authorise('core.edit.state', $extension . '.category.' . $jinput->get('id')))
  239. {
  240. // Disable fields for display.
  241. $form->setFieldAttribute('ordering', 'disabled', 'true');
  242. $form->setFieldAttribute('published', 'disabled', 'true');
  243. // Disable fields while saving.
  244. // The controller has already verified this is a record you can edit.
  245. $form->setFieldAttribute('ordering', 'filter', 'unset');
  246. $form->setFieldAttribute('published', 'filter', 'unset');
  247. }
  248. return $form;
  249. }
  250. /**
  251. * A protected method to get the where clause for the reorder
  252. * This ensures that the row will be moved relative to a row with the same extension
  253. *
  254. * @param JCategoryTable $table Current table instance
  255. *
  256. * @return array An array of conditions to add to add to ordering queries.
  257. *
  258. * @since 1.6
  259. */
  260. protected function getReorderConditions($table)
  261. {
  262. return 'extension = ' . $this->_db->quote($table->extension);
  263. }
  264. /**
  265. * Method to get the data that should be injected in the form.
  266. *
  267. * @return mixed The data for the form.
  268. *
  269. * @since 1.6
  270. */
  271. protected function loadFormData()
  272. {
  273. // Check the session for previously entered form data.
  274. $data = JFactory::getApplication()->getUserState('com_categories.edit.' . $this->getName() . '.data', array());
  275. if (empty($data))
  276. {
  277. $data = $this->getItem();
  278. }
  279. $this->preprocessData('com_categories.category', $data);
  280. return $data;
  281. }
  282. /**
  283. * Method to preprocess the form.
  284. *
  285. * @param JForm $form A JForm object.
  286. * @param mixed $data The data expected for the form.
  287. * @param string $group The name of the plugin group to import.
  288. *
  289. * @return void
  290. *
  291. * @see JFormField
  292. * @since 1.6
  293. * @throws Exception if there is an error in the form event.
  294. */
  295. protected function preprocessForm(JForm $form, $data, $group = 'content')
  296. {
  297. jimport('joomla.filesystem.path');
  298. $lang = JFactory::getLanguage();
  299. $component = $this->getState('category.component');
  300. $section = $this->getState('category.section');
  301. $extension = JFactory::getApplication()->input->get('extension', null);
  302. // Get the component form if it exists
  303. $name = 'category' . ($section ? ('.' . $section) : '');
  304. // Looking first in the component models/forms folder
  305. $path = JPath::clean(JPATH_ADMINISTRATOR . "/components/$component/models/forms/$name.xml");
  306. // Old way: looking in the component folder
  307. if (!file_exists($path))
  308. {
  309. $path = JPath::clean(JPATH_ADMINISTRATOR . "/components/$component/$name.xml");
  310. }
  311. if (file_exists($path))
  312. {
  313. $lang->load($component, JPATH_BASE, null, false, true);
  314. $lang->load($component, JPATH_BASE . '/components/' . $component, null, false, true);
  315. if (!$form->loadFile($path, false))
  316. {
  317. throw new Exception(JText::_('JERROR_LOADFILE_FAILED'));
  318. }
  319. }
  320. // Try to find the component helper.
  321. $eName = str_replace('com_', '', $component);
  322. $path = JPath::clean(JPATH_ADMINISTRATOR . "/components/$component/helpers/category.php");
  323. if (file_exists($path))
  324. {
  325. require_once $path;
  326. $cName = ucfirst($eName) . ucfirst($section) . 'HelperCategory';
  327. if (class_exists($cName) && is_callable(array($cName, 'onPrepareForm')))
  328. {
  329. $lang->load($component, JPATH_BASE, null, false, false) || $lang->load($component, JPATH_BASE . '/components/' . $component, null, false, false) || $lang->load($component, JPATH_BASE, $lang->getDefault(), false, false) || $lang->load($component, JPATH_BASE . '/components/' . $component, $lang->getDefault(), false, false);
  330. call_user_func_array(array($cName, 'onPrepareForm'), array(&$form));
  331. // Check for an error.
  332. if ($form instanceof Exception)
  333. {
  334. $this->setError($form->getMessage());
  335. return false;
  336. }
  337. }
  338. }
  339. // Set the access control rules field component value.
  340. $form->setFieldAttribute('rules', 'component', $component);
  341. $form->setFieldAttribute('rules', 'section', $name);
  342. // Association category items
  343. $assoc = $this->getAssoc();
  344. if ($assoc)
  345. {
  346. $languages = JLanguageHelper::getLanguages('lang_code');
  347. // Force to array (perhaps move to $this->loadFormData())
  348. $data = (array) $data;
  349. $addform = new SimpleXMLElement('<form />');
  350. $fields = $addform->addChild('fields');
  351. $fields->addAttribute('name', 'associations');
  352. $fieldset = $fields->addChild('fieldset');
  353. $fieldset->addAttribute('name', 'item_associations');
  354. $fieldset->addAttribute('description', 'COM_CATEGORIES_ITEM_ASSOCIATIONS_FIELDSET_DESC');
  355. $add = false;
  356. foreach ($languages as $tag => $language)
  357. {
  358. if (empty($data['language']) || $tag != $data['language'])
  359. {
  360. $add = true;
  361. $field = $fieldset->addChild('field');
  362. $field->addAttribute('name', $tag);
  363. $field->addAttribute('type', 'modal_category');
  364. $field->addAttribute('language', $tag);
  365. $field->addAttribute('label', $language->title);
  366. $field->addAttribute('translate_label', 'false');
  367. $field->addAttribute('extension', $extension);
  368. $field->addAttribute('edit', 'true');
  369. $field->addAttribute('clear', 'true');
  370. }
  371. }
  372. if ($add)
  373. {
  374. $form->load($addform, false);
  375. }
  376. }
  377. // Trigger the default form events.
  378. parent::preprocessForm($form, $data, $group);
  379. }
  380. /**
  381. * Method to save the form data.
  382. *
  383. * @param array $data The form data.
  384. *
  385. * @return boolean True on success.
  386. *
  387. * @since 1.6
  388. */
  389. public function save($data)
  390. {
  391. $dispatcher = JEventDispatcher::getInstance();
  392. $table = $this->getTable();
  393. $input = JFactory::getApplication()->input;
  394. $pk = (!empty($data['id'])) ? $data['id'] : (int) $this->getState($this->getName() . '.id');
  395. $isNew = true;
  396. if ((!empty($data['tags']) && $data['tags'][0] != ''))
  397. {
  398. $table->newTags = $data['tags'];
  399. }
  400. // Include the content plugins for the on save events.
  401. JPluginHelper::importPlugin('content');
  402. // Load the row if saving an existing category.
  403. if ($pk > 0)
  404. {
  405. $table->load($pk);
  406. $isNew = false;
  407. }
  408. // Set the new parent id if parent id not matched OR while New/Save as Copy .
  409. if ($table->parent_id != $data['parent_id'] || $data['id'] == 0)
  410. {
  411. $table->setLocation($data['parent_id'], 'last-child');
  412. }
  413. // Alter the title for save as copy
  414. if ($input->get('task') == 'save2copy')
  415. {
  416. list($title, $alias) = $this->generateNewTitle($data['parent_id'], $data['alias'], $data['title']);
  417. $data['title'] = $title;
  418. $data['alias'] = $alias;
  419. $data['published'] = 0;
  420. }
  421. // Bind the data.
  422. if (!$table->bind($data))
  423. {
  424. $this->setError($table->getError());
  425. return false;
  426. }
  427. // Bind the rules.
  428. if (isset($data['rules']))
  429. {
  430. $rules = new JAccessRules($data['rules']);
  431. $table->setRules($rules);
  432. }
  433. // Check the data.
  434. if (!$table->check())
  435. {
  436. $this->setError($table->getError());
  437. return false;
  438. }
  439. // Trigger the onContentBeforeSave event.
  440. $result = $dispatcher->trigger($this->event_before_save, array($this->option . '.' . $this->name, &$table, $isNew));
  441. if (in_array(false, $result, true))
  442. {
  443. $this->setError($table->getError());
  444. return false;
  445. }
  446. // Store the data.
  447. if (!$table->store())
  448. {
  449. $this->setError($table->getError());
  450. return false;
  451. }
  452. $assoc = $this->getAssoc();
  453. if ($assoc)
  454. {
  455. // Adding self to the association
  456. $associations = $data['associations'];
  457. foreach ($associations as $tag => $id)
  458. {
  459. if (empty($id))
  460. {
  461. unset($associations[$tag]);
  462. }
  463. }
  464. // Detecting all item menus
  465. $all_language = $table->language == '*';
  466. if ($all_language && !empty($associations))
  467. {
  468. JError::raiseNotice(403, JText::_('COM_CATEGORIES_ERROR_ALL_LANGUAGE_ASSOCIATED'));
  469. }
  470. $associations[$table->language] = $table->id;
  471. // Deleting old association for these items
  472. $db = JFactory::getDbo();
  473. $query = $db->getQuery(true)
  474. ->delete('#__associations')
  475. ->where($db->quoteName('context') . ' = ' . $db->quote('com_categories.item'))
  476. ->where($db->quoteName('id') . ' IN (' . implode(',', $associations) . ')');
  477. $db->setQuery($query);
  478. $db->execute();
  479. if ($error = $db->getErrorMsg())
  480. {
  481. $this->setError($error);
  482. return false;
  483. }
  484. if (!$all_language && count($associations))
  485. {
  486. // Adding new association for these items
  487. $key = md5(json_encode($associations));
  488. $query->clear()
  489. ->insert('#__associations');
  490. foreach ($associations as $id)
  491. {
  492. $query->values($id . ',' . $db->quote('com_categories.item') . ',' . $db->quote($key));
  493. }
  494. $db->setQuery($query);
  495. $db->execute();
  496. if ($error = $db->getErrorMsg())
  497. {
  498. $this->setError($error);
  499. return false;
  500. }
  501. }
  502. }
  503. // Trigger the onContentAfterSave event.
  504. $dispatcher->trigger($this->event_after_save, array($this->option . '.' . $this->name, &$table, $isNew));
  505. // Rebuild the path for the category:
  506. if (!$table->rebuildPath($table->id))
  507. {
  508. $this->setError($table->getError());
  509. return false;
  510. }
  511. // Rebuild the paths of the category's children:
  512. if (!$table->rebuild($table->id, $table->lft, $table->level, $table->path))
  513. {
  514. $this->setError($table->getError());
  515. return false;
  516. }
  517. $this->setState($this->getName() . '.id', $table->id);
  518. // Clear the cache
  519. $this->cleanCache();
  520. return true;
  521. }
  522. /**
  523. * Method to change the published state of one or more records.
  524. *
  525. * @param array &$pks A list of the primary keys to change.
  526. * @param integer $value The value of the published state.
  527. *
  528. * @return boolean True on success.
  529. *
  530. * @since 2.5
  531. */
  532. public function publish(&$pks, $value = 1)
  533. {
  534. if (parent::publish($pks, $value))
  535. {
  536. $dispatcher = JEventDispatcher::getInstance();
  537. $extension = JFactory::getApplication()->input->get('extension');
  538. // Include the content plugins for the change of category state event.
  539. JPluginHelper::importPlugin('content');
  540. // Trigger the onCategoryChangeState event.
  541. $dispatcher->trigger('onCategoryChangeState', array($extension, $pks, $value));
  542. return true;
  543. }
  544. }
  545. /**
  546. * Method rebuild the entire nested set tree.
  547. *
  548. * @return boolean False on failure or error, true otherwise.
  549. *
  550. * @since 1.6
  551. */
  552. public function rebuild()
  553. {
  554. // Get an instance of the table object.
  555. $table = $this->getTable();
  556. if (!$table->rebuild())
  557. {
  558. $this->setError($table->getError());
  559. return false;
  560. }
  561. // Clear the cache
  562. $this->cleanCache();
  563. return true;
  564. }
  565. /**
  566. * Method to save the reordered nested set tree.
  567. * First we save the new order values in the lft values of the changed ids.
  568. * Then we invoke the table rebuild to implement the new ordering.
  569. *
  570. * @param array $idArray An array of primary key ids.
  571. * @param integer $lft_array The lft value
  572. *
  573. * @return boolean False on failure or error, True otherwise
  574. *
  575. * @since 1.6
  576. */
  577. public function saveorder($idArray = null, $lft_array = null)
  578. {
  579. // Get an instance of the table object.
  580. $table = $this->getTable();
  581. if (!$table->saveorder($idArray, $lft_array))
  582. {
  583. $this->setError($table->getError());
  584. return false;
  585. }
  586. // Clear the cache
  587. $this->cleanCache();
  588. return true;
  589. }
  590. protected function batchTag($value, $pks, $contexts)
  591. {
  592. // Set the variables
  593. $user = JFactory::getUser();
  594. $table = $this->getTable();
  595. foreach ($pks as $pk)
  596. {
  597. if ($user->authorise('core.edit', $contexts[$pk]))
  598. {
  599. $table->reset();
  600. $table->load($pk);
  601. $tags = array($value);
  602. /**
  603. * @var JTableObserverTags $tagsObserver
  604. */
  605. $tagsObserver = $table->getObserverOfClass('JTableObserverTags');
  606. $result = $tagsObserver->setNewTags($tags, false);
  607. if (!$result)
  608. {
  609. $this->setError($table->getError());
  610. return false;
  611. }
  612. }
  613. else
  614. {
  615. $this->setError(JText::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EDIT'));
  616. return false;
  617. }
  618. }
  619. // Clean the cache
  620. $this->cleanCache();
  621. return true;
  622. }
  623. /**
  624. * Batch copy categories to a new category.
  625. *
  626. * @param integer $value The new category.
  627. * @param array $pks An array of row IDs.
  628. * @param array $contexts An array of item contexts.
  629. *
  630. * @return mixed An array of new IDs on success, boolean false on failure.
  631. *
  632. * @since 1.6
  633. */
  634. protected function batchCopy($value, $pks, $contexts)
  635. {
  636. $type = new JUcmType;
  637. $this->type = $type->getTypeByAlias($this->typeAlias);
  638. // $value comes as {parent_id}.{extension}
  639. $parts = explode('.', $value);
  640. $parentId = (int) JArrayHelper::getValue($parts, 0, 1);
  641. $db = $this->getDbo();
  642. $extension = JFactory::getApplication()->input->get('extension', '', 'word');
  643. $i = 0;
  644. // Check that the parent exists
  645. if ($parentId)
  646. {
  647. if (!$this->table->load($parentId))
  648. {
  649. if ($error = $this->table->getError())
  650. {
  651. // Fatal error
  652. $this->setError($error);
  653. return false;
  654. }
  655. else
  656. {
  657. // Non-fatal error
  658. $this->setError(JText::_('JGLOBAL_BATCH_MOVE_PARENT_NOT_FOUND'));
  659. $parentId = 0;
  660. }
  661. }
  662. // Check that user has create permission for parent category
  663. $canCreate = ($parentId == $this->table->getRootId()) ? $this->user->authorise('core.create', $extension) : $this->user->authorise('core.create', $extension . '.category.' . $parentId);
  664. if (!$canCreate)
  665. {
  666. // Error since user cannot create in parent category
  667. $this->setError(JText::_('COM_CATEGORIES_BATCH_CANNOT_CREATE'));
  668. return false;
  669. }
  670. }
  671. // If the parent is 0, set it to the ID of the root item in the tree
  672. if (empty($parentId))
  673. {
  674. if (!$parentId = $this->table->getRootId())
  675. {
  676. $this->setError($db->getErrorMsg());
  677. return false;
  678. }
  679. // Make sure we can create in root
  680. elseif (!$this->user->authorise('core.create', $extension))
  681. {
  682. $this->setError(JText::_('COM_CATEGORIES_BATCH_CANNOT_CREATE'));
  683. return false;
  684. }
  685. }
  686. // We need to log the parent ID
  687. $parents = array();
  688. // Calculate the emergency stop count as a precaution against a runaway loop bug
  689. $query = $db->getQuery(true)
  690. ->select('COUNT(id)')
  691. ->from($db->quoteName('#__categories'));
  692. $db->setQuery($query);
  693. try
  694. {
  695. $count = $db->loadResult();
  696. }
  697. catch (RuntimeException $e)
  698. {
  699. $this->setError($e->getMessage());
  700. return false;
  701. }
  702. // Parent exists so let's proceed
  703. while (!empty($pks) && $count > 0)
  704. {
  705. // Pop the first id off the stack
  706. $pk = array_shift($pks);
  707. $this->table->reset();
  708. // Check that the row actually exists
  709. if (!$this->table->load($pk))
  710. {
  711. if ($error = $this->table->getError())
  712. {
  713. // Fatal error
  714. $this->setError($error);
  715. return false;
  716. }
  717. else
  718. {
  719. // Not fatal error
  720. $this->setError(JText::sprintf('JGLOBAL_BATCH_MOVE_ROW_NOT_FOUND', $pk));
  721. continue;
  722. }
  723. }
  724. // Copy is a bit tricky, because we also need to copy the children
  725. $query->clear()
  726. ->select('id')
  727. ->from($db->quoteName('#__categories'))
  728. ->where('lft > ' . (int) $this->table->lft)
  729. ->where('rgt < ' . (int) $this->table->rgt);
  730. $db->setQuery($query);
  731. $childIds = $db->loadColumn();
  732. // Add child ID's to the array only if they aren't already there.
  733. foreach ($childIds as $childId)
  734. {
  735. if (!in_array($childId, $pks))
  736. {
  737. array_push($pks, $childId);
  738. }
  739. }
  740. // Make a copy of the old ID and Parent ID
  741. $oldId = $this->table->id;
  742. $oldParentId = $this->table->parent_id;
  743. // Reset the id because we are making a copy.
  744. $this->table->id = 0;
  745. // If we a copying children, the Old ID will turn up in the parents list
  746. // otherwise it's a new top level item
  747. $this->table->parent_id = isset($parents[$oldParentId]) ? $parents[$oldParentId] : $parentId;
  748. // Set the new location in the tree for the node.
  749. $this->table->setLocation($this->table->parent_id, 'last-child');
  750. // TODO: Deal with ordering?
  751. // $this->table->ordering = 1;
  752. $this->table->level = null;
  753. $this->table->asset_id = null;
  754. $this->table->lft = null;
  755. $this->table->rgt = null;
  756. // Alter the title & alias
  757. list($title, $alias) = $this->generateNewTitle($this->table->parent_id, $this->table->alias, $this->table->title);
  758. $this->table->title = $title;
  759. $this->table->alias = $alias;
  760. parent::createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table);
  761. // Store the row.
  762. if (!$this->table->store())
  763. {
  764. $this->setError($this->table->getError());
  765. return false;
  766. }
  767. // Get the new item ID
  768. $newId = $this->table->get('id');
  769. // Add the new ID to the array
  770. $newIds[$i] = $newId;
  771. $i++;
  772. // Now we log the old 'parent' to the new 'parent'
  773. $parents[$oldId] = $this->table->id;
  774. $count--;
  775. }
  776. // Rebuild the hierarchy.
  777. if (!$this->table->rebuild())
  778. {
  779. $this->setError($this->table->getError());
  780. return false;
  781. }
  782. // Rebuild the tree path.
  783. if (!$this->table->rebuildPath($this->table->id))
  784. {
  785. $this->setError($this->table->getError());
  786. return false;
  787. }
  788. return $newIds;
  789. }
  790. /**
  791. * Batch move categories to a new category.
  792. *
  793. * @param integer $value The new category ID.
  794. * @param array $pks An array of row IDs.
  795. * @param array $contexts An array of item contexts.
  796. *
  797. * @return boolean True on success.
  798. *
  799. * @since 1.6
  800. */
  801. protected function batchMove($value, $pks, $contexts)
  802. {
  803. $parentId = (int) $value;
  804. $type = new JUcmType;
  805. $this->type = $type->getTypeByAlias($this->typeAlias);
  806. $db = $this->getDbo();
  807. $query = $db->getQuery(true);
  808. $extension = JFactory::getApplication()->input->get('extension', '', 'word');
  809. // Check that the parent exists.
  810. if ($parentId)
  811. {
  812. if (!$this->table->load($parentId))
  813. {
  814. if ($error = $this->table->getError())
  815. {
  816. // Fatal error
  817. $this->setError($error);
  818. return false;
  819. }
  820. else
  821. {
  822. // Non-fatal error
  823. $this->setError(JText::_('JGLOBAL_BATCH_MOVE_PARENT_NOT_FOUND'));
  824. $parentId = 0;
  825. }
  826. }
  827. // Check that user has create permission for parent category
  828. $canCreate = ($parentId == $this->table->getRootId()) ? $this->user->authorise('core.create', $extension) : $this->user->authorise('core.create', $extension . '.category.' . $parentId);
  829. if (!$canCreate)
  830. {
  831. // Error since user cannot create in parent category
  832. $this->setError(JText::_('COM_CATEGORIES_BATCH_CANNOT_CREATE'));
  833. return false;
  834. }
  835. // Check that user has edit permission for every category being moved
  836. // Note that the entire batch operation fails if any category lacks edit permission
  837. foreach ($pks as $pk)
  838. {
  839. if (!$this->user->authorise('core.edit', $extension . '.category.' . $pk))
  840. {
  841. // Error since user cannot edit this category
  842. $this->setError(JText::_('COM_CATEGORIES_BATCH_CANNOT_EDIT'));
  843. return false;
  844. }
  845. }
  846. }
  847. // We are going to store all the children and just move the category
  848. $children = array();
  849. // Parent exists so let's proceed
  850. foreach ($pks as $pk)
  851. {
  852. // Check that the row actually exists
  853. if (!$this->table->load($pk))
  854. {
  855. if ($error = $this->table->getError())
  856. {
  857. // Fatal error
  858. $this->setError($error);
  859. return false;
  860. }
  861. else
  862. {
  863. // Not fatal error
  864. $this->setError(JText::sprintf('JGLOBAL_BATCH_MOVE_ROW_NOT_FOUND', $pk));
  865. continue;
  866. }
  867. }
  868. // Set the new location in the tree for the node.
  869. $this->table->setLocation($parentId, 'last-child');
  870. // Check if we are moving to a different parent
  871. if ($parentId != $this->table->parent_id)
  872. {
  873. // Add the child node ids to the children array.
  874. $query->clear()
  875. ->select('id')
  876. ->from($db->quoteName('#__categories'))
  877. ->where($db->quoteName('lft') . ' BETWEEN ' . (int) $this->table->lft . ' AND ' . (int) $this->table->rgt);
  878. $db->setQuery($query);
  879. try
  880. {
  881. $children = array_merge($children, (array) $db->loadColumn());
  882. }
  883. catch (RuntimeException $e)
  884. {
  885. $this->setError($e->getMessage());
  886. return false;
  887. }
  888. }
  889. parent::createTagsHelper($this->tagsObserver, $this->type, $pk, $this->typeAlias, $this->table);
  890. // Store the row.
  891. if (!$this->table->store())
  892. {
  893. $this->setError($this->table->getError());
  894. return false;
  895. }
  896. // Rebuild the tree path.
  897. if (!$this->table->rebuildPath())
  898. {
  899. $this->setError($this->table->getError());
  900. return false;
  901. }
  902. }
  903. // Process the child rows
  904. if (!empty($children))
  905. {
  906. // Remove any duplicates and sanitize ids.
  907. $children = array_unique($children);
  908. JArrayHelper::toInteger($children);
  909. }
  910. return true;
  911. }
  912. /**
  913. * Custom clean the cache of com_content and content modules
  914. *
  915. * @since 1.6
  916. */
  917. protected function cleanCache($group = null, $client_id = 0)
  918. {
  919. $extension = JFactory::getApplication()->input->get('extension');
  920. switch ($extension)
  921. {
  922. case 'com_content':
  923. parent::cleanCache('com_content');
  924. parent::cleanCache('mod_articles_archive');
  925. parent::cleanCache('mod_articles_categories');
  926. parent::cleanCache('mod_articles_category');
  927. parent::cleanCache('mod_articles_latest');
  928. parent::cleanCache('mod_articles_news');
  929. parent::cleanCache('mod_articles_popular');
  930. break;
  931. default:
  932. parent::cleanCache($extension);
  933. break;
  934. }
  935. }
  936. /**
  937. * Method to change the title & alias.
  938. *
  939. * @param integer $parent_id The id of the parent.
  940. * @param string $alias The alias.
  941. * @param string $title The title.
  942. *
  943. * @return array Contains the modified title and alias.
  944. *
  945. * @since 1.7
  946. */
  947. protected function generateNewTitle($parent_id, $alias, $title)
  948. {
  949. // Alter the title & alias
  950. $table = $this->getTable();
  951. while ($table->load(array('alias' => $alias, 'parent_id' => $parent_id)))
  952. {
  953. $title = JString::increment($title);
  954. $alias = JString::increment($alias, 'dash');
  955. }
  956. return array($title, $alias);
  957. }
  958. public function getAssoc()
  959. {
  960. static $assoc = null;
  961. if (!is_null($assoc))
  962. {
  963. return $assoc;
  964. }
  965. $app = JFactory::getApplication();
  966. $extension = $this->getState('category.extension');
  967. $assoc = JLanguageAssociations::isEnabled();
  968. $extension = explode('.', $extension);
  969. $component = array_shift($extension);
  970. $cname = str_replace('com_', '', $component);
  971. if (!$assoc || !$component || !$cname)
  972. {
  973. $assoc = false;
  974. }
  975. else
  976. {
  977. $hname = $cname . 'HelperAssociation';
  978. JLoader::register($hname, JPATH_SITE . '/components/' . $component . '/helpers/association.php');
  979. $assoc = class_exists($hname) && !empty($hname::$category_association);
  980. }
  981. return $assoc;
  982. }
  983. }