PageRenderTime 77ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/com_flexicontent_v2.x/admin/models/parentclassitem.php

http://flexicontent.googlecode.com/
PHP | 4248 lines | 2613 code | 598 blank | 1037 comment | 722 complexity | 33eff71428c84317ac5d14df569ef3b9 MD5 | raw file
Possible License(s): MIT, GPL-2.0, Apache-2.0
  1. <?php
  2. /**
  3. * @version 1.5 stable $Id: item.php 1244 2012-04-12 05:07:35Z ggppdk $
  4. * @package Joomla
  5. * @subpackage FLEXIcontent
  6. * @copyright (C) 2009 Emmanuel Danan - www.vistamedia.fr
  7. * @license GNU/GPL v2
  8. *
  9. * FLEXIcontent is a derivative work of the excellent QuickFAQ component
  10. * @copyright (C) 2008 Christoph Lukes
  11. * see www.schlu.net for more information
  12. *
  13. * FLEXIcontent is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. */
  18. // no direct access
  19. defined( '_JEXEC' ) or die( 'Restricted access' );
  20. jimport( 'joomla.application.component.modeladmin' );
  21. jimport( 'joomla.html.parameter' );
  22. /**
  23. * FLEXIcontent Component Item Model
  24. *
  25. * @package Joomla
  26. * @subpackage FLEXIcontent
  27. * @since 1.0
  28. */
  29. class ParentClassItem extends JModelAdmin
  30. {
  31. var $_name = 'ParentClassItem';
  32. /**
  33. * Component parameters
  34. *
  35. * @var object
  36. */
  37. var $_cparams = null;
  38. /**
  39. * Item data
  40. *
  41. * @var object
  42. */
  43. var $_item = null;
  44. /**
  45. * Item primary key
  46. *
  47. * @var int
  48. */
  49. var $_id = null;
  50. /**
  51. * Item current category id (used for FRONTEND only)
  52. *
  53. * @var int
  54. */
  55. var $_cid = null;
  56. /**
  57. * Item version of loaded data
  58. *
  59. * @var int
  60. */
  61. var $_version = null;
  62. /**
  63. * Associated item translations
  64. *
  65. * @var array
  66. */
  67. var $_translations = null;
  68. /**
  69. * Constructor
  70. *
  71. * @since 1.0
  72. */
  73. public function __construct()
  74. {
  75. parent::__construct();
  76. $app = JFactory::getApplication();
  77. // --. Get component parameters , merge (for frontend) with current menu item parameters
  78. $this->_cparams = clone( JComponentHelper::getParams('com_flexicontent') );
  79. if (!$app->isAdmin()) {
  80. $menu = $app->getMenu()->getActive();
  81. if ($menu) {
  82. $menu_params = FLEXI_J16GE ? $menu->params : new JParameter($menu->params);
  83. $this->_cparams->merge($menu_params);
  84. }
  85. }
  86. // --. Get & Set ITEM's primary key (pk) and (for frontend) the current category
  87. if (!$app->isAdmin()) {
  88. // FRONTEND, use "id" from request
  89. $pk = JRequest::getVar('id', 0, 'default', 'int');
  90. if ( !JRequest::getVar('task') )
  91. $curcatid = JRequest::getVar('cid', 0, $hash='default', 'int');
  92. else
  93. $curcatid = 0;
  94. }
  95. else
  96. {
  97. // BACKEND, use "cid" array from request, but check first for a POST 'id' variable
  98. // this is a correction for problematic name of categories AS cid in item edit form ...
  99. $data = JRequest::get( 'post' );
  100. // Must check if id is SET and if it is non-ZERO !
  101. if ( FLEXI_J16GE ? isset($data['jform']['id']) : isset($data['id']) ) {
  102. $pk = FLEXI_J16GE ? $data['jform']['id'] : $data['id'];
  103. } else {
  104. $cid = JRequest::getVar( 'cid', array(0), $hash='default', 'array' );
  105. JArrayHelper::toInteger($cid, array(0));
  106. $pk = $cid[0];
  107. }
  108. $curcatid = 0;
  109. }
  110. $this->setId($pk, $curcatid); // NOTE: when setting $pk to a new value the $this->_item is cleared
  111. $this->populateState();
  112. }
  113. /**
  114. * Method to set the identifier
  115. *
  116. * @access public
  117. * @param int item identifier
  118. */
  119. function setId($id, $currcatid=0)
  120. {
  121. // Set a new item id and wipe data
  122. if ($this->_id != $id) {
  123. $this->_item = null;
  124. }
  125. $this->_id = (int) $id;
  126. // Set current category, but verify item is assigned to this category, (SECURITY concern)
  127. $this->_cid = (int) $currcatid;
  128. if ($this->_cid) {
  129. $q = "SELECT catid FROM #__flexicontent_cats_item_relations WHERE itemid =". (int)$this->_id ." AND catid = ". (int)$this->_cid;
  130. $this->_db->setQuery($q);
  131. $result = $this->_db->loadResult();
  132. $this->_cid = $result ? $this->_cid : 0; // Clear cid, if category not assigned to the item
  133. }
  134. }
  135. /**
  136. * Method to get the identifier
  137. *
  138. * @access public
  139. * @return int item identifier
  140. */
  141. function getId()
  142. {
  143. return $this->_id;
  144. }
  145. /**
  146. * Overridden get method to get properties from the item
  147. *
  148. * @access public
  149. * @param string $property The name of the property
  150. * @param mixed $value The value of the property to set
  151. * @return mixed The value of the property
  152. * @since 1.5
  153. */
  154. function get($property, $default=null)
  155. {
  156. if ($this->_item || $this->_loadItem()) {
  157. if(isset($this->_item->$property)) {
  158. return $this->_item->$property;
  159. }
  160. }
  161. return $default;
  162. }
  163. /**
  164. * Overridden set method to pass properties on to the item
  165. *
  166. * @access public
  167. * @param string $property The name of the property
  168. * @param mixed $value The value of the property to set
  169. * @return boolean True on success
  170. * @since 1.5
  171. */
  172. function set( $property, $value=null )
  173. {
  174. if ( $this->_loadItem() ) {
  175. $this->_item->$property = $value;
  176. return true;
  177. } else {
  178. return false;
  179. }
  180. }
  181. /**
  182. * Method to get item data
  183. *
  184. * @access public
  185. * @return array
  186. * @since 1.0
  187. */
  188. function &getItem($pk=null, $check_view_access=true, $no_cache=false, $force_version=false)
  189. {
  190. $app = JFactory::getApplication();
  191. $cparams = $this->_cparams;
  192. $preview = JRequest::getVar('preview');
  193. // View access done is meant only for FRONTEND !!! ... force it to false
  194. if ( $app->isAdmin() ) $check_view_access = false;
  195. // Initialise and set primary if it was not given already
  196. $pk = !empty($pk) ? $pk : $this->_id;
  197. if (FLEXI_J16GE) {
  198. $pk = !empty($pk) ? $pk : (int) $this->getState($this->getName().'.id');
  199. }
  200. // Set new item id, clearing item data, ONLY IF DIFFERENT than existing primary key
  201. if ($pk != $this->_id) {
  202. $this->setId($pk);
  203. }
  204. // --. Try to load existing item
  205. if ( $pk && $this->_loadItem($no_cache, $force_version) )
  206. {
  207. // Successfully loaded existing item, do some extra manipulation of the loaded item ...
  208. // Extra Steps for Frontend
  209. if ( !$app->isAdmin() ) {
  210. // Load item parameters with heritage
  211. $this->_loadItemParams();
  212. // Check item viewing access
  213. if ( $check_view_access ) $this->_check_viewing_access();
  214. }
  215. }
  216. // --. Failed to load existing item, or check_view_access indicates not to create a new item object
  217. else if ( $pk || $check_view_access===2 )
  218. {
  219. $msg = JText::sprintf('FLEXI_CONTENT_UNAVAILABLE_ITEM_NOT_FOUND', $pk);
  220. if (FLEXI_J16GE) throw new Exception($msg, 404); else JError::raiseError(404, $msg);
  221. }
  222. // --. Initialize new item, currently this succeeds always
  223. else
  224. {
  225. $this->_initItem();
  226. }
  227. // Extra Steps for Backend
  228. if ( $app->isAdmin() ) {
  229. // Set item type id for existing or new item ('typeid' of the JRequest array) ... verifying that the type exists ...
  230. $this->_item->type_id = $this->getTypesselected()->id;
  231. }
  232. return $this->_item;
  233. }
  234. /**
  235. * Method to load item data
  236. *
  237. * @access private
  238. * @return boolean True on success
  239. * @since 1.0
  240. */
  241. function _loadItem( $no_cache=false, $force_version=false )
  242. {
  243. if(!$this->_id) return false; // Only try to load existing item
  244. // Cache items retrieved, we can retrieve multiple items, for this purpose
  245. // (a) temporarily set JRequest variable -version- to specify loaded version (set to zero to use latest )
  246. // (b1) use member function function setId($id, $currcatid=0) to change primary key and then call getItem()
  247. // (b2) or call getItem($pk, $check_view_access=true) passing the item id and maybe also disabling read access checkings, to avoid unwanted messages/errors
  248. static $items = array();
  249. if ( $no_cache ) {
  250. // Clear item to make sure it is reloaded
  251. $this->_item = null;
  252. }
  253. else if ( isset($items[$this->_id]) ) {
  254. $this->_item = & $items[$this->_id];
  255. return (boolean) $this->_item;
  256. }
  257. static $unapproved_version_notice;
  258. $db = $this->_db;
  259. $app = JFactory::getApplication();
  260. $user = JFactory::getUser();
  261. $cparams = $this->_cparams;
  262. $task = JRequest::getVar('task', false);
  263. $layout = JRequest::getVar('layout', false);
  264. $view = JRequest::getVar('view', false);
  265. $option = JRequest::getVar('option', false);
  266. $use_versioning = $cparams->get('use_versioning', 1);
  267. $allow_current_version = true;
  268. $editjf_translations = $cparams->get('editjf_translations', 0);
  269. // *********************************************************************************************************
  270. // Retrieve item if not already retrieved, null indicates cleared item data, e.g. because of changed item id
  271. // *********************************************************************************************************
  272. if ( $this->_item === null ) {
  273. //*****************************************************
  274. // DECIDE VERSION and GENERATE VERSION RELATED MESSAGES
  275. //*****************************************************
  276. // Variables controlling the version loading logic
  277. $loadcurrent = JRequest::getVar('loadcurrent', false, 'request', 'boolean'); // loadcurrent request flag, ignored if version specified
  278. $preview = JRequest::getVar('preview', false, 'request', 'boolean'); // preview request flag for viewing unapproved version in frontend
  279. $version = JRequest::getVar('version', 0, 'request', 'int' ); // the item version to load
  280. // -- Decide the version to load: (a) the one specified by request or (b) the current one or (c) the latest one
  281. $current_version = FLEXIUtilities::getCurrentVersions($this->_id, true, $force=true); // Get current item version
  282. $last_version = FLEXIUtilities::getLastVersions($this->_id, true, $force=true); // Get last version (=latest one saved, highest version id),
  283. // NOTE: Setting version to zero indicates to load the current version from the normal tables and not the versioning table
  284. if ( !$use_versioning ) {
  285. // Force version to zero (load current version), when not using versioning mode
  286. $version = 0;
  287. } else if ($force_version !== false) {
  288. $version = $force_version==-1 ? $last_version : $force_version;
  289. } else if ($version == 0) {
  290. // version request variable was NOT SET ... We need to decide to load current (version zero) or latest
  291. if ( $app->isAdmin() || ($task=='edit' && $option=='com_flexicontent') ) {
  292. // Catch cases (a) when we enable versioning mode after an item has been saved in unversioning mode, (b) loadcurrent flag is set
  293. // in these case we will load CURRENT version instead of the default for the item edit form which is the LATEST (for backend/fontend)
  294. $version = ($current_version >= $last_version || $loadcurrent) ? 0 : $last_version;
  295. } else {
  296. // In frontend item display the current version must be shown unless preview flag is set
  297. $version = !$preview ? 0 : $last_version;
  298. }
  299. } else if ($version == $current_version) {
  300. // Current version number given, the data from the versioning table should be the same as the data from normal tables
  301. // we do not force $version to ZERO to allow testing the field data of current version from the versioning table
  302. if (!$allow_current_version) $version = 0; // Force zero to retrieve unversioned data
  303. }
  304. // Check if not loading the current version while we are in edit form, and raise a notice to inform the user
  305. if ($version && $version != $current_version && $task=='edit' && $option=='com_flexicontent' && !$unapproved_version_notice) {
  306. $unapproved_version_notice = 1;
  307. if (!$app->isAdmin()) {
  308. JError::raiseNotice(10, JText::_('FLEXI_LOADING_UNAPPROVED_VERSION_NOTICE') );
  309. } else {
  310. JError::raiseNotice(10,
  311. JText::_('FLEXI_LOADING_UNAPPROVED_VERSION_NOTICE') . ' :: ' .
  312. JText::sprintf('FLEXI_LOADED_VERSION_INFO_NOTICE', $version, $current_version)
  313. );
  314. }
  315. }
  316. try
  317. {
  318. if ( $app->isAdmin() )
  319. {
  320. // **********************
  321. // Item Retrieval BACKEND
  322. // **********************
  323. $item = $this->getTable('flexicontent_items', '');
  324. $result = $item->load($this->_id); // try loading existing item data
  325. if ($result===false) return false;
  326. }
  327. else
  328. {
  329. // ***********************
  330. // Item Retrieval FRONTEND
  331. // ***********************
  332. // Tables needed to be joined for calculating access
  333. $joinaccess = '';
  334. // Extra access columns for main category and content type (item access will be added as 'access')
  335. $select_access = 'mc.access as category_access, ty.access as type_access';
  336. // Access Flags for: content type, main category, item
  337. if (FLEXI_J16GE) {
  338. $aid_arr = $user->getAuthorisedViewLevels();
  339. $aid_list = implode(",", $aid_arr);
  340. $select_access .= ', CASE WHEN ty.access IN (0,'.$aid_list.') THEN 1 ELSE 0 END AS has_type_access';
  341. $select_access .= ', CASE WHEN mc.access IN (0,'.$aid_list.') THEN 1 ELSE 0 END AS has_mcat_access';
  342. $select_access .= ', CASE WHEN i.access IN (0,'.$aid_list.') THEN 1 ELSE 0 END AS has_item_access';
  343. } else {
  344. $aid = (int) $user->get('aid');
  345. if (FLEXI_ACCESS) {
  346. $joinaccess .= ' LEFT JOIN #__flexiaccess_acl AS gt ON ty.id = gt.axo AND gt.aco = "read" AND gt.axosection = "type"';
  347. $joinaccess .= ' LEFT JOIN #__flexiaccess_acl AS gc ON mc.id = gc.axo AND gc.aco = "read" AND gc.axosection = "category"';
  348. $joinaccess .= ' LEFT JOIN #__flexiaccess_acl AS gi ON i.id = gi.axo AND gi.aco = "read" AND gi.axosection = "item"';
  349. $select_access .= ', CASE WHEN (gt.aro IN ( '.$user->gmid.' ) OR ty.access <= '. (int) $aid . ') THEN 1 ELSE 0 END AS has_type_access';
  350. $select_access .= ', CASE WHEN (gc.aro IN ( '.$user->gmid.' ) OR mc.access <= '. (int) $aid . ') THEN 1 ELSE 0 END AS has_mcat_access';
  351. $select_access .= ', CASE WHEN (gi.aro IN ( '.$user->gmid.' ) OR i.access <= '. (int) $aid . ') THEN 1 ELSE 0 END AS has_item_access';
  352. } else {
  353. $select_access .= ', CASE WHEN (ty.access <= '. (int) $aid . ') THEN 1 ELSE 0 END AS has_type_access';
  354. $select_access .= ', CASE WHEN (mc.access <= '. (int) $aid . ') THEN 1 ELSE 0 END AS has_mcat_access';
  355. $select_access .= ', CASE WHEN ( i.access <= '. (int) $aid . ') THEN 1 ELSE 0 END AS has_item_access';
  356. }
  357. $select_access .= ', ';
  358. }
  359. // SQL date strings, current date and null date
  360. $nowDate = $db->Quote( FLEXI_J16GE ? JFactory::getDate()->toSql() : JFactory::getDate()->toMySQL() );
  361. $nullDate = $db->Quote($db->getNullDate());
  362. // Decide to limit to CURRENT CATEGORY
  363. $limit_to_cid = $this->_cid ? ' AND rel.catid = '. (int) $this->_cid : ' AND rel.catid = i.catid';
  364. if (FLEXI_J16GE)
  365. {
  366. // Initialize query
  367. $query = $db->getQuery(true);
  368. $query->select('i.*, ie.*'); // Item basic and extended data
  369. $query->select($select_access); // Access Columns and Access Flags for: content type, main category, item
  370. if ($version) $query->select('ver.version_id'); // Versioned item viewing
  371. $query->select('c.id AS catid, i.catid as maincatid'); // Current category id and Main category id
  372. $query->select(
  373. 'c.title AS category_title, c.alias AS category_alias, c.lft,c.rgt'); // Current category data
  374. $query->select('ty.name AS typename, ty.alias as typealias'); // Content Type data, and author data
  375. $query->select('u.name AS author'); // Author data
  376. // Rating count, Rating & Score
  377. $query->select('v.rating_count as rating_count, ROUND( v.rating_sum / v.rating_count ) AS rating, ((v.rating_sum / v.rating_count)*20) as score');
  378. // Item and Current Category slugs (for URL)
  379. $query->select('CASE WHEN CHAR_LENGTH(i.alias) THEN CONCAT_WS(\':\', i.id, i.alias) ELSE i.id END as slug');
  380. $query->select('CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as categoryslug');
  381. // Publication Scheduled / Expired Flags
  382. $query->select('CASE WHEN i.publish_up = '.$nullDate.' OR i.publish_up <= '.$nowDate.' THEN 0 ELSE 1 END as publication_scheduled');
  383. $query->select('CASE WHEN i.publish_down = '.$nullDate.' OR i.publish_down >= '.$nowDate.' THEN 0 ELSE 1 END as publication_expired' );
  384. // From content table, and extended item table, content type table, user table, rating table, categories relation table
  385. $query->from('#__content AS i');
  386. $query->join('LEFT', '#__flexicontent_items_ext AS ie ON ie.item_id = i.id');
  387. $query->join('LEFT', '#__flexicontent_types AS ty ON ie.type_id = ty.id');
  388. $query->join('LEFT', '#__users AS u on u.id = i.created_by');
  389. $query->join('LEFT', '#__content_rating AS v ON i.id = v.content_id');
  390. $query->join('LEFT', '#__flexicontent_cats_item_relations AS rel ON rel.itemid = i.id' . $limit_to_cid);
  391. // Join twice on category table, once for current category and once for item's main category
  392. $query->join('LEFT', '#__categories AS c on c.id = rel.catid'); // All item's categories
  393. $query->join('LEFT', '#__categories AS mc on mc.id = i.catid'); // Item's main category
  394. // HANDLE J1.6+ ancestor category being unpublished, when badcats.id is not null,
  395. // then the item is inside in an unpublished ancestor category, thus inaccessible
  396. $query->select('CASE WHEN badcats.id is null THEN 1 ELSE 0 END AS ancestor_cats_published');
  397. $subquery = ' (SELECT cat.id as id FROM #__categories AS cat JOIN #__categories AS parent ';
  398. $subquery .= 'ON cat.lft BETWEEN parent.lft AND parent.rgt ';
  399. $subquery .= 'WHERE parent.extension = ' . $db->Quote('com_content');
  400. $subquery .= ' AND parent.published <= 0 GROUP BY cat.id)';
  401. $query->join('LEFT', $subquery . ' AS badcats ON badcats.id = c.id');
  402. if ($version) {
  403. // NOTE: version_id is used by field helper file to load the specified version, the reason for left join here is to verify that the version exists
  404. $query->join('LEFT', '#__flexicontent_versions AS ver ON ver.item_id = i.id AND ver.version_id = '. $db->Quote($version) );
  405. }
  406. // Join on contact table, to get contact data of author
  407. //$query = 'SHOW TABLES LIKE "' . JFactory::getApplication()->getCfg('dbprefix') . 'contact_details"';
  408. //$db->setQuery($query);
  409. //$contact_details_tbl_exists = (boolean) count($db->loadObjectList());
  410. //if ( $contact_details_tbl_exists) {
  411. // $query->select('contact.id as contactid' ) ;
  412. // $query->join('LEFT','#__contact_details AS contact on contact.user_id = i.created_by');
  413. //}
  414. // Join over the categories to get parent category titles
  415. //$query->select('parent.title as parent_title, parent.id as parent_id, parent.path as parent_route, parent.alias as parent_alias');
  416. //$query->join('LEFT', '#__categories as parent ON parent.id = c.parent_id');
  417. $query->where('i.id = ' . (int) $this->_id);
  418. //echo $db->replacePrefix($query);
  419. }
  420. else
  421. {
  422. // NOTE: version_id is used by field helper file to load the specified version, the reason for left join here is to verify that the version exists
  423. $version_join = $version ? ' LEFT JOIN #__flexicontent_versions AS ver ON ver.item_id = i.id AND ver.version_id = '. $db->Quote($version) : '';
  424. $where = $this->_buildItemWhere();
  425. $query = 'SELECT i.*, ie.*, ' // Item basic and extended data
  426. . $select_access // Access Columns and Access Flags for: content type, main category, item
  427. . ($version ? 'ver.version_id,' : '') // Versioned item viewing
  428. . ' c.id AS catid, i.catid as maincatid,' // Current category id and Main category id
  429. . ' c.published AS catpublished,' // Current category published (in J1.6+ this includes all ancestor categories)
  430. . ' c.title AS category_title, c.alias AS category_alias,' // Current category data
  431. . ' ty.name as typename, ty.alias as typealias,' // Content Type data
  432. . ' u.name AS author, u.usertype,' // Author data
  433. // Rating count, Rating & Score
  434. . ' v.rating_count as rating_count, ROUND( v.rating_sum / v.rating_count ) AS rating, ((v.rating_sum / v.rating_count)*20) as score,'
  435. // Item and Current Category slugs (for URL)
  436. . ' CASE WHEN CHAR_LENGTH(i.alias) THEN CONCAT_WS(\':\', i.id, i.alias) ELSE i.id END as slug,'
  437. . ' CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as categoryslug,'
  438. // Publication Scheduled / Expired Flags
  439. . ' CASE WHEN i.publish_up = '.$nullDate.' OR i.publish_up <= '.$nowDate.' THEN 0 ELSE 1 END as publication_scheduled,'
  440. . ' CASE WHEN i.publish_down = '.$nullDate.' OR i.publish_down >= '.$nowDate.' THEN 0 ELSE 1 END as publication_expired'
  441. . ' FROM #__content AS i'
  442. . ' LEFT JOIN #__flexicontent_items_ext AS ie ON ie.item_id = i.id'
  443. . ' LEFT JOIN #__flexicontent_types AS ty ON ie.type_id = ty.id'
  444. . ' LEFT JOIN #__flexicontent_cats_item_relations AS rel ON rel.itemid = i.id' . $limit_to_cid
  445. . ' LEFT JOIN #__categories AS c ON c.id = rel.catid'
  446. . ' LEFT JOIN #__categories AS mc ON mc.id = i.catid'
  447. . ' LEFT JOIN #__users AS u ON u.id = i.created_by'
  448. . ' LEFT JOIN #__content_rating AS v ON i.id = v.content_id'
  449. . $joinaccess
  450. . $version_join
  451. . $where
  452. ;
  453. }
  454. $db->setQuery($query);
  455. // Try to execute query directly and load the data as an object
  456. if ( FLEXI_FISH && $task=='edit' && $option=='com_flexicontent' && in_array( $app->getCfg('dbtype') , array('mysqli','mysql') ) ) {
  457. $data = flexicontent_db::directQuery($query);
  458. $data = @ $data[0];
  459. //$data = $db->loadObject(null, false); // do not, translate, this is the JoomFish overridden method of Database extended Class
  460. } else {
  461. $data = $db->loadObject();
  462. }
  463. // Check for SQL error
  464. if ( $db->getErrorNum() ) {
  465. if (FLEXI_J16GE) throw new Exception($db->getErrorMsg(), 500); else JError::raiseError(500, $db->getErrorMsg());
  466. }
  467. //print_r($data); exit;
  468. if(!$data) return false; // item not found, return
  469. if ($version && !$data->version_id) {
  470. JError::raiseNotice(10, JText::sprintf('NOTICE: Requested item version %d was not found', $version) );
  471. }
  472. $item = & $data;
  473. }
  474. // -- Create the description field called 'text' by appending introtext + readmore + fulltext
  475. $item->text = $item->introtext;
  476. $item->text .= JString::strlen( trim($item->fulltext) ) ? '<hr id="system-readmore" />' . $item->fulltext : "";
  477. //echo "<br/>Current version (Frontend Active): " . $item->version;
  478. //echo "<br/>Version to load: ".$version;
  479. //echo "<br/><b> *** db title:</b> ".$item->title;
  480. //echo "<br/><b> *** db text:</b> ".$item->text;
  481. //echo "<pre>*** item data: "; print_r($item); echo "</pre>"; exit;
  482. // Set number of loaded version, IMPORTANT: zero means load unversioned data
  483. JRequest::setVar( 'version', $version );
  484. // *************************************************************************************************
  485. // -- Retrieve all active site languages, and create empty item translation objects for each of them
  486. // *************************************************************************************************
  487. $nn_content_tbl = FLEXI_J16GE ? 'falang_content' : 'jf_content';
  488. if ( FLEXI_FISH /*|| FLEXI_J16GE*/ )
  489. {
  490. $site_languages = FLEXIUtilities::getlanguageslist();
  491. $item_translations = new stdClass();
  492. foreach($site_languages as $lang_id => $lang_data)
  493. {
  494. if ( !$lang_id && $item->language!='*' ) continue;
  495. $lang_data->fields = new stdClass();
  496. $item_translations->{$lang_id} = $lang_data;
  497. }
  498. }
  499. // **********************************
  500. // Retrieve and prepare JoomFish data
  501. // **********************************
  502. if ( (FLEXI_FISH /*|| FLEXI_J16GE*/) && $task=='edit' && $option=='com_flexicontent' && $editjf_translations > 0 )
  503. {
  504. // -- Try to retrieve all joomfish data for the current item
  505. $query = "SELECT jfc.language_id, jfc.reference_field, jfc.value, jfc.published "
  506. ." FROM #__".$nn_content_tbl." as jfc "
  507. ." WHERE jfc.reference_table='content' AND jfc.reference_id = {$this->_id} ";
  508. $db->setQuery($query);
  509. $translated_fields = $db->loadObjectList();
  510. if ( $editjf_translations < 2 && $translated_fields ) {
  511. $app->enqueueMessage("Third party Joom!Fish translations detected for current content, but editting Joom!Fish translations is disabled in global configuration", 'message' );
  512. $app->enqueueMessage("You can enable Joom!Fish translations editting or disable this warning in Global configuration",'message');
  513. } else {
  514. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  515. // -- Parse translation data according to their language
  516. if ( $translated_fields )
  517. {
  518. // Add retrieved translated item properties
  519. foreach ($translated_fields as $field_data)
  520. {
  521. $item_translations ->{$field_data->language_id} ->fields ->{$field_data->reference_field} = new stdClass();
  522. $item_translations ->{$field_data->language_id} ->fields ->{$field_data->reference_field}->value = $field_data->value;
  523. $found_languages[$field_data->language_id] = $item_translations->{$field_data->language_id}->name;
  524. }
  525. //echo "<br/>Joom!Fish translations found for: " . implode(",", $found_languages);
  526. }
  527. foreach ($item_translations as $lang_id => $translation_data)
  528. {
  529. // Default title can be somewhat long, trim it to first word, so that it is more suitable for tabs
  530. list($translation_data->name) = explode(' ', trim($translation_data->name));
  531. // Create text field value for all languages
  532. $translation_data->fields->text = new stdClass();
  533. $translation_data->fields->text->value = @ $translation_data->fields->introtext->value;
  534. if ( JString::strlen( trim(@$translation_data->fields->fulltext->value) ) ) {
  535. $translation_data->fields->text->value .= '<hr id="system-readmore" />' . @ $translation_data->fields->fulltext->value;
  536. }
  537. }
  538. $item->item_translations = & $item_translations;
  539. }
  540. }
  541. //echo "<pre>"; print_r($item->item_translations); exit;
  542. // *****************************************************
  543. // Overwrite item fields with the requested VERSION data
  544. // *****************************************************
  545. $item->current_version = $current_version;
  546. $item->last_version = $last_version;
  547. if ($use_versioning && $version)
  548. {
  549. // Overcome possible group concat limitation
  550. $query="SET SESSION group_concat_max_len = 9999999";
  551. $db->setQuery($query);
  552. $db->query();
  553. $query = "SELECT f.id, f.name, f.field_type, GROUP_CONCAT(iv.value SEPARATOR ',') as value, count(f.id) as valuecount, iv.field_id"
  554. ." FROM #__flexicontent_items_versions as iv "
  555. ." LEFT JOIN #__flexicontent_fields as f on f.id=iv.field_id"
  556. ." WHERE iv.version='".$version."' AND (f.iscore=1 OR iv.field_id=-1 OR iv.field_id=-2) AND iv.item_id='".$this->_id."'"
  557. ." GROUP BY f.id";
  558. $db->setQuery($query);
  559. $fields = $db->loadObjectList();
  560. $fields = $fields ? $fields : array();
  561. //echo "<br/>Overwritting fields with version: $version";
  562. foreach($fields as $f) {
  563. //echo "<br/><b>{$f->field_id} : ". $f->name."</b> : "; print_r($f->value);
  564. // Use versioned data, by overwriting the item data
  565. $fieldname = $f->name;
  566. if ($f->field_type=='hits' || $f->field_type=='state' || $f->field_type=='voting') {
  567. // skip fields that should not have been versioned: hits, state, voting
  568. continue;
  569. } else if ($f->field_type=='version') {
  570. // set version variable to indicate the loaded version
  571. $item->version = $version;
  572. } else if( $fieldname=='categories'|| $fieldname=='tags' ) {
  573. // categories and tags must have been serialized but some earlier versions did not do it,
  574. // we will check before unserializing them, otherwise they were concatenated to a single string and use explode ...
  575. $item->$fieldname = ($array = @unserialize($f->value)) ? $array : explode(",", $f->value);
  576. } else if ($f->field_id==-1) {
  577. if ( FLEXI_FISH ) {
  578. $jfdata = unserialize($f->value);
  579. $item_lang = substr($item->language ,0,2);
  580. foreach ($item_translations as $lang_id => $translation_data) {
  581. //echo "<br/>Adding values for: ".$translation_data->shortcode;
  582. if ( empty($jfdata[$translation_data->shortcode]) ) continue;
  583. foreach ($jfdata[$translation_data->shortcode] as $fieldname => $fieldvalue)
  584. {
  585. //echo "<br/>".$translation_data->shortcode.": $fieldname => $fieldvalue";
  586. if ($translation_data->shortcode != $item_lang) {
  587. $translation_data->fields->$fieldname = new stdClass();
  588. $translation_data->fields->$fieldname->value = $fieldvalue;
  589. } else {
  590. $item->$fieldname = $fieldvalue;
  591. }
  592. }
  593. }
  594. }
  595. } else if ($f->field_id==-2) {
  596. // Other item properties that were versioned, such as alias, catid, meta params, attribs
  597. $item_data = unserialize($f->value);
  598. //$item->bind($item_data);
  599. foreach ($item_data as $k => $v) $item->$k = $v;
  600. } else if ($fieldname) {
  601. // Other fields (maybe serialized or not but we do not unserialized them, this is responsibility of the field itself)
  602. $item->$fieldname = $f->value;
  603. }
  604. }
  605. // The text field is stored in the db as to seperate fields: introtext & fulltext
  606. // So we search for the {readmore} tag and split up the text field accordingly.
  607. $pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i';
  608. $tagPos = preg_match($pattern, $item->text);
  609. if ($tagPos == 0) {
  610. $item->introtext = $item->text;
  611. $item->fulltext = '';
  612. } else {
  613. list($item->introtext, $item->fulltext) = preg_split($pattern, $item->text, 2);
  614. $item->fulltext = JString::strlen( trim($item->fulltext) ) ? $item->fulltext : '';
  615. }
  616. }
  617. // -- Retrieve tags field value (if not using versioning)
  618. if ( $use_versioning && $version ) {
  619. // Check version value was found
  620. if ( !isset($item->tags) || !is_array($item->tags) )
  621. $item->tags = array();
  622. } else {
  623. // Retrieve unversioned value
  624. $query = 'SELECT DISTINCT tid FROM #__flexicontent_tags_item_relations WHERE itemid = ' . (int)$this->_id;
  625. $db->setQuery($query);
  626. $item->tags = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  627. }
  628. // -- Retrieve categories field value (if not using versioning)
  629. if ( $use_versioning && $version ) {
  630. // Check version value was found, and is valid (above code should have produced an array)
  631. if ( !isset($item->categories) || !is_array($item->categories) )
  632. $item->categories = array();
  633. } else {
  634. $query = 'SELECT DISTINCT catid FROM #__flexicontent_cats_item_relations WHERE itemid = ' . (int)$this->_id;
  635. $db->setQuery($query);
  636. $item->categories = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  637. }
  638. // Make sure catid is in categories array
  639. if ( !in_array($item->catid, $item->categories) ) $item->categories[] = $item->catid;
  640. // 'cats' is an alias of categories
  641. $item->cats = & $item->categories;
  642. // *********************************************************
  643. // Retrieve item properties not defined in the model's CLASS
  644. // *********************************************************
  645. // Category access is retrieved here for J1.6+, for J1.5 we use FLEXIaccess
  646. if (FLEXI_J16GE) {
  647. // Get category access for the item's main category, used later to determine viewing of the item
  648. $query = 'SELECT access FROM #__categories WHERE id = '. (int) $item->catid;
  649. $db->setQuery($query);
  650. $item->category_access = $db->loadResult();
  651. }
  652. // Typecast some properties in case LEFT JOIN returned nulls
  653. if ( !isset($item->type_access) ) {
  654. $public_acclevel = !FLEXI_J16GE ? 0 : 1;
  655. $item->type_access = $public_acclevel;
  656. }
  657. $item->typename = (string) @ $item->typename;
  658. $item->typealias = (string) @ $item->typealias;
  659. $item->rating_count = (int) @ $item->rating_count;
  660. $item->score = (int) @ $item->score;
  661. // Retrieve Creator NAME and email (used to display the gravatar)
  662. $query = 'SELECT name, email FROM #__users WHERE id = '. (int) $item->created_by;
  663. $db->setQuery($query);
  664. $creator_data = $db->loadObject();
  665. $item->creator = $creator_data ? $creator_data->name : '';
  666. $item->creatoremail = $creator_data ? $creator_data->email : '';
  667. // Retrieve Modifier NAME
  668. if ($item->created_by == $item->modified_by) {
  669. $item->modifier = $item->creator;
  670. } else {
  671. $query = 'SELECT name, email FROM #__users WHERE id = '. (int) $item->modified_by;
  672. $db->setQuery($query);
  673. $modifier_data = $db->loadObject();
  674. $item->modifier = $modifier_data ? $modifier_data->name : '';
  675. $item->modifieremail = $modifier_data ? $modifier_data->email : '';
  676. }
  677. // Clear modified Date, if it is an invalid "null" date
  678. if ($item->modified == $db->getNulldate()) {
  679. $item->modified = null;
  680. }
  681. // ********************************************************
  682. // Assign to the item data member variable and cache it too
  683. // ********************************************************
  684. $this->_item = & $item;
  685. $items[$this->_id] = & $this->_item;
  686. // ******************************************************************************************************
  687. // Detect if current version doesnot exist in version table and add it !!! e.g. after enabling versioning
  688. // ******************************************************************************************************
  689. if ( $use_versioning && $current_version > $last_version ) {
  690. require_once(JPATH_ADMINISTRATOR.DS.'components'.DS.'com_flexicontent'.DS.'models'.DS.'flexicontent.php');
  691. $fleximodel = new FlexicontentModelFlexicontent();
  692. $fleximodel->addCurrentVersionData($item->id);
  693. }
  694. // return true if item was loaded successfully
  695. return (boolean) $this->_item;
  696. }
  697. catch (JException $e)
  698. {
  699. if ($e->getCode() == 404) {
  700. // Need to go thru the error handler to allow Redirect to work.
  701. $msg = $e->getMessage();
  702. if (FLEXI_J16GE) throw new Exception($msg, 404); else JError::raiseError(404, $msg);
  703. }
  704. else {
  705. $this->setError($e);
  706. $this->_item = false;
  707. }
  708. }
  709. } else {
  710. $items[$this->_id] = & $this->_item;
  711. }
  712. /*$session = JFactory::getSession();
  713. $postdata = $session->get('item_edit_postdata', array(), 'flexicontent');
  714. if (count($postdata)) {
  715. $session->set('item_edit_postdata', null, 'flexicontent');
  716. // ...
  717. }*/
  718. return true;
  719. }
  720. //*************
  721. // BOF of J1.6+
  722. //*************
  723. /**
  724. * Returns a Table object, always creating it
  725. *
  726. * @param type The table type to instantiate
  727. * @param string A prefix for the table class name. Optional.
  728. * @param array Configuration array for model. Optional.
  729. * @return JTable A database object
  730. * @since 1.6
  731. */
  732. public function getTable($type = 'flexicontent_items', $prefix = '', $config = array()) {
  733. return JTable::getInstance($type, $prefix, $config);
  734. }
  735. /**
  736. * Method to get the row form.
  737. *
  738. * @param array $data Data for the form.
  739. * @param boolean $loadData True if the form is to load its own data (default case), false if not.
  740. * @return mixed A JForm object on success, false on failure
  741. * @since 1.6
  742. */
  743. public function getForm($data = array(), $loadData = true)
  744. {
  745. $app = JFactory::getApplication();
  746. $this->getItem();
  747. // *********************************************************
  748. // Prepare item data for being loaded into the form:
  749. // (a) Convert parameters 'images', 'urls,' 'attribs' & 'metadata' to an array
  750. // (b) Set property 'cid' (form field categories)
  751. // *********************************************************
  752. $this->_item->itemparams = FLEXI_J16GE ? new JRegistry() : new JParameter("");
  753. if ($this->_id) {
  754. // Convert the images
  755. $images = $this->_item->images;
  756. $registry = new JRegistry;
  757. $registry->loadString($images);
  758. $this->_item->images = $registry->toArray();
  759. $this->_item->itemparams->merge($registry);
  760. // Convert the urls
  761. $urls = $this->_item->urls;
  762. $registry = new JRegistry;
  763. $registry->loadString($urls);
  764. $this->_item->urls = $registry->toArray();
  765. $this->_item->itemparams->merge($registry);
  766. // Convert the attribs
  767. $attribs = $this->_item->attribs;
  768. $registry = new JRegistry;
  769. $registry->loadString($attribs);
  770. $this->_item->attribs = $registry->toArray();
  771. $this->_item->itemparams->merge($registry);
  772. // Convert the metadata
  773. $metadata = $this->_item->metadata;
  774. $registry = new JRegistry;
  775. $registry->loadString($metadata);
  776. $this->_item->metadata = $registry->toArray();
  777. $this->_item->itemparams->merge($registry);
  778. } else {
  779. $attribs = $metadata = '';
  780. $this->_item->attribs = array();
  781. $this->_item->metadata = array();
  782. $this->_item->images = array();
  783. $this->_item->urls = array();
  784. }
  785. // Set item property 'cid' (form field categories is named cid)
  786. $this->_item->cid = $this->_item->categories;
  787. // ****************************************************************************
  788. // Load item data into the form and restore the changes done above to item data
  789. // ****************************************************************************
  790. $form = $this->loadForm('com_flexicontent.item', 'item', array('control' => 'jform', 'load_data' => $loadData));
  791. if (empty($form)) {
  792. return false;
  793. }
  794. $this->_item->images = $images;
  795. $this->_item->urls = $urls;
  796. $this->_item->attribs = $attribs;
  797. $this->_item->metadata = $metadata;
  798. unset($this->_item->cid);
  799. // Determine correct permissions to check.
  800. $id = @$data['id'] ? $data['id'] : (int) $this->getState($this->getName().'.id');
  801. if ($id) {
  802. // Existing record. Can only edit in selected categories.
  803. $form->setFieldAttribute('catid', 'action', 'core.edit');
  804. // Existing record. Can only edit own articles in selected categories.
  805. $form->setFieldAttribute('catid', 'action', 'core.edit.own');
  806. }
  807. else {
  808. // New record. Can only create in selected categories.
  809. $form->setFieldAttribute('catid', 'action', 'core.create');
  810. }
  811. // Modify the form based on Edit State access controls.
  812. if ( !$this->canEditState( (object)$data ) )
  813. {
  814. $frontend_new = !$id && $app->isSite();
  815. // Disable fields for display.
  816. $form->setFieldAttribute('featured', 'disabled', 'true');
  817. $form->setFieldAttribute('ordering', 'disabled', 'true');
  818. $form->setFieldAttribute('publish_up', 'disabled', 'true');
  819. $form->setFieldAttribute('publish_down', 'disabled', 'true');
  820. $form->setFieldAttribute('created_by', 'disabled', 'true');
  821. $form->setFieldAttribute('created_by_alias', 'disabled', 'true');
  822. if ( !$frontend_new ) {
  823. // skip new items in frontend to allow override via menu (auto-publish), menu override must be check during store
  824. $form->setFieldAttribute('state', 'disabled', 'true'); // only for existing items, not for new to allow menu item override
  825. }
  826. //$form->setFieldAttribute('vstate', 'disabled', 'true'); // DO not -disable- will cause problems
  827. // Disable fields while saving.
  828. // The controller has already verified this is an article you can edit.
  829. $form->setFieldAttribute('featured', 'filter', 'unset');
  830. $form->setFieldAttribute('ordering', 'filter', 'unset');
  831. $form->setFieldAttribute('publish_up', 'filter', 'unset');
  832. $form->setFieldAttribute('publish_down', 'filter', 'unset');
  833. $form->setFieldAttribute('created_by', 'filter', 'unset');
  834. $form->setFieldAttribute('created_by_alias', 'filter', 'unset');
  835. if ( !$frontend_new ) {
  836. // skip new items in frontend to allow override via menu (auto-publish), menu override must be check during store
  837. $form->setFieldAttribute('state', 'filter', 'unset'); // only for existing items, not for new to allow menu item override
  838. }
  839. //$form->setFieldAttribute('vstate', 'filter', 'unset'); // DO not -filter- will cause problems
  840. }
  841. return $form;
  842. }
  843. /**
  844. * Method to get the data that should be injected in the form.
  845. *
  846. * @return mixed The data for the form.
  847. * @since 1.6
  848. */
  849. protected function loadFormData() {
  850. // Check the session for previously entered form data.
  851. $data = JFactory::getApplication()->getUserState('com_flexicontent.edit.'.$this->getName().'.data', array());
  852. if (empty($data)) {
  853. $data = $this->getItem();
  854. }
  855. return $data;
  856. }
  857. /**
  858. * Method to calculate Item Access Permissions
  859. *
  860. * @access private
  861. * @return void
  862. * @since 1.5
  863. */
  864. function getItemAccess($create_cats=array()) {
  865. $iparams_extra = new JRegistry;
  866. $user = JFactory::getUser();
  867. $asset = 'com_content.article.'.$this->_id;
  868. $permission = FlexicontentHelperPerm::getPerm(); // Global component permissions
  869. // NOTE, technically in J1.6+ a guest may edit able to edit/delete an item, so we commented out the guest check bellow,
  870. // this applies for creating item, but flexicontent already allows create to guests via menu item too, so no check there too
  871. // Compute CREATE access permissions.
  872. if ( !$this->_id ) {
  873. // Check if general create permission is missing, NOTE: THIS CAN BE SOFT DENY
  874. // ... so we do need to check category 'create' privilege for all categories !!
  875. /*if ( !$user->authorise('core.create', 'com_flexicontent') ) {
  876. $iparams_extra->set('access-create', false);
  877. return $iparams_extra; // New item, so do not calculate EDIT, DELETE and VIEW access
  878. }*/
  879. // Check that user can create item in at least one category ... this check is not wasted,
  880. // since joomla will cache it and use it later during creation of allowed Category Tree
  881. $canCreate = $user->authorise('core.create', 'com_flexicontent');
  882. if ($canCreate === NULL) {
  883. $allowedcats = FlexicontentHelperPerm::getAllowedCats($user, array('core.create')
  884. , $require_all = true, $check_published = true, $specific_catids = false, $find_first = true
  885. );
  886. $canCreate = count($allowedcats) > 0;
  887. }
  888. $iparams_extra->set('access-create', $canCreate);
  889. return $iparams_extra; // New item, so do not calculate EDIT, DELETE and VIEW access
  890. }
  891. // Not a new item retrieve item if not already done
  892. if ( empty($this->_item) ) {
  893. $this->_item = $this->getItem();
  894. }
  895. // Compute EDIT access permissions.
  896. if ( $this->_id ) {
  897. // first check edit permission on the item
  898. if ($user->authorise('core.edit', $asset)) {
  899. $iparams_extra->set('access-edit', true);
  900. }
  901. // no edit permission, check if edit.own is available for this item
  902. else if ( $user->authorise('core.edit.own', $asset) && $user->get('id') == $this->_item->created_by /* && !$user->get('guest') */ )
  903. {
  904. $iparams_extra->set('access-edit', true);
  905. }
  906. }
  907. // Compute EDIT STATE access permissions.
  908. if ( $this->_id ) {
  909. // first check edit.state permission on the item
  910. if ($user->authorise('core.edit.state', $asset)) {
  911. $iparams_extra->set('access-edit-state', true);
  912. }
  913. // no edit.state permission, check if edit.state.own is available for this item
  914. else if ( $user->authorise('core.edit.state.own', $asset) && $user->get('id') == $this->_item->created_by /* && !$user->get('guest') */ )
  915. {
  916. $iparams_extra->set('access-edit-state', true);
  917. }
  918. }
  919. // Compute DELETE access permissions.
  920. if ( $this->_id ) {
  921. // first check delete permission on the item
  922. if ($user->authorise('core.delete', $asset)) {
  923. $iparams_extra->set('access-delete', true);
  924. }
  925. // no delete permission, chekc delete.own permission if the item is owned by the user
  926. else if ( $user->authorise('core.delete.own', $asset) && $user->get('id') == $this->_item->created_by /* && !$user->get('guest') */ )
  927. {
  928. $iparams_extra->set('access-delete', true);
  929. }
  930. }
  931. // Compute VIEW access permissions.
  932. if ($access = $this->getState('filter.access')) {
  933. // The access filter has been set,
  934. // we already know current user can view this item or we should not check access
  935. $iparams_extra->set('access-view', true);
  936. }
  937. else {
  938. // The access filter has not been set, we will set access flag(s) if not set already
  939. // the layout takes some responsibility for display of limited information,
  940. $groups = $user->getAuthorisedViewLevels();
  941. if ( !isset($this->_item->has_item_access) ) {
  942. $this->_item->has_item_access = in_array($this->_item->access, $groups);
  943. }
  944. if ( !isset($this->_item->has_mcat_access) ) {
  945. $no_mcat_info = $this->_item->catid == 0 || !isset($this->_item->category_access) || $this->_item->category_access === null;
  946. $this->_item->has_mcat_access = $no_mcat_info || in_array($this->_item->category_access, $groups);
  947. }
  948. if ( !isset($this->_item->has_type_access) ) {
  949. $no_type_info = $this->_item->type_id == 0 || !isset($this->_item->type_access) || $this->_item->type_access === null;
  950. $this->_item->has_type_access = $no_type_info || in_array($this->_item->type_access, $groups);
  951. }
  952. $iparams_extra->set('access-view', $this->_item->has_item_access && $this->_item->has_mcat_access && $this->_item->has_type_access);
  953. }
  954. return $iparams_extra;
  955. }
  956. /**
  957. * Method to check if you can assign a (new/existing) item in the specified categories
  958. *
  959. * @param array An array of input data.
  960. *
  961. * @return boolean
  962. * @since 1.6
  963. */
  964. protected function itemAllowedInCats($data = array())
  965. {
  966. // Initialise variables.
  967. $user = JFactory::getUser();
  968. $cats = isset($data['cid']) ? $data['cid'] : array();
  969. if ( !empty($data['catid']) && !in_array($data['catid'], $cats) ) {
  970. $cats[] = $data['catid'];
  971. }
  972. $allow = null;
  973. if (count($cats)) {
  974. $allow = true;
  975. foreach ($cats as $curcatid) {
  976. // If the category has been passed in the data or URL check it.
  977. $cat_allowed = $user->authorise('core.create', 'com_content.category.'.$curcatid);
  978. if (!$cat_allowed) {
  979. return JError::raiseWarning( 500, "No access to add item to category with id ".$curcatid );
  980. }
  981. $allow &= $cat_allowed;
  982. }
  983. }
  984. if ($allow === null) {
  985. // no categories specified, revert to the component permissions.
  986. $allow = $user->authorise('core.create', 'com_flexicontent');
  987. }
  988. return $allow;
  989. }
  990. //*************
  991. // EOF of J1.6+
  992. //*************
  993. //*************
  994. // BOF of J1.5
  995. //*************
  996. /**
  997. * Method (for J1.5) to check if the user can add an item anywhere
  998. *
  999. * @access public
  1000. * @return boolean True on success
  1001. * @since 1.5
  1002. */
  1003. function canAdd()
  1004. {
  1005. $user = JFactory::getUser();
  1006. if (FLEXI_ACCESS && ($user->gid < 25))
  1007. {
  1008. $canSubmit = FAccess::checkComponentAccess('com_content', 'submit', 'users', $user->gmid);
  1009. $canAdd = FAccess::checkAllContentAccess('com_content','add','users',$user->gmid,'content','all');
  1010. if (!$canSubmit && !$canAdd) return false;
  1011. } else {
  1012. $canAdd = $user->authorize('com_content', 'add', 'content', 'all');
  1013. if (!$canAdd) return false;
  1014. }
  1015. return true;
  1016. }
  1017. /**
  1018. * Method (for J1.5) to check if the user can edit the item
  1019. *
  1020. * @access public
  1021. * @return boolean True on success
  1022. * @since 1.5
  1023. */
  1024. function canEdit()
  1025. {
  1026. $user = JFactory::getUser();
  1027. if (!$this->_loadItem() || $user->gid >= 25) {
  1028. return true;
  1029. } else if (FLEXI_ACCESS) {
  1030. // This should not be used, as it bypasses individual item rights
  1031. //$canEditAll = FAccess::checkAllContentAccess('com_content','edit','users',$user->gmid,'content','all');
  1032. //$canEditOwnAll = FAccess::checkAllContentAccess('com_content','editown','users',$user->gmid,'content','all');
  1033. if ($this->_item->id && $this->_item->catid)
  1034. {
  1035. $rights = FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $this->_item->id, $this->_item->catid);
  1036. $canEdit = in_array('edit', $rights) /*|| $canEditAll*/;
  1037. $canEditOwn = ( in_array('editown', $rights) /*|| $canEditOwnAll*/ ) && $this->_item->created_by == $user->get('id');
  1038. if (!$canEdit && !$canEditOwn) return false;
  1039. }
  1040. } else {
  1041. $canEdit= $user->authorize('com_content', 'edit', 'content', 'all');
  1042. if (!$canEdit) return false;
  1043. }
  1044. return true;
  1045. }
  1046. //*************
  1047. // EOF of J1.5
  1048. //*************
  1049. /**
  1050. * Method to check if the user can create items of the given type
  1051. *
  1052. * @access public
  1053. * @return boolean True on success
  1054. * @since 1.5
  1055. */
  1056. function canCreateType($type_ids=false, $any = true, & $types = null)
  1057. {
  1058. $types = $this->getTypeslist ( $type_ids );
  1059. if ( empty($types) ) return false;
  1060. $user = JFactory::getUser();
  1061. $canCreate = $any ? false : true;
  1062. foreach ($types as $type)
  1063. {
  1064. if (FLEXI_J16GE)
  1065. $type->allowed = ! $type->itemscreatable || $user->authorise('core.create', 'com_flexicontent.type.' . $type->id);
  1066. else if (FLEXI_ACCESS && $user->gid < 25)
  1067. $type->allowed = ! $type->itemscreatable || FAccess::checkAllContentAccess('com_content','submit','users', $user->gmid, 'type', $type->id);
  1068. else
  1069. $type->allowed = 1;
  1070. // Require ANY or ALL
  1071. $canCreate = $any ? ($canCreate || $type->allowed) : ($canCreate && $type->allowed);
  1072. if ($canCreate && $any) return true;
  1073. }
  1074. return $canCreate;
  1075. }
  1076. /**
  1077. * Method to check if the user can edit the STATE of the item
  1078. *
  1079. * @access public
  1080. * @return boolean True on success
  1081. * @since 1.5
  1082. */
  1083. function canEditState($item=null, $check_cat_perm=true)
  1084. {
  1085. if ( empty($item) ) $item = & $this->_item;
  1086. $user = JFactory::getUser();
  1087. $isOwner = !empty($item->created_by) && ( $item->created_by == $user->get('id') );
  1088. if (FLEXI_J16GE)
  1089. {
  1090. if ( !empty($item->id) )
  1091. {
  1092. // Existing item, use item specific permissions
  1093. $asset = 'com_content.article.' . $item->id;
  1094. return $user->authorise('core.edit.state', $asset) || ($user->authorise('core.edit.state.own', $asset) && $isOwner);
  1095. }
  1096. elseif ( $check_cat_perm && !empty($item->catid) )
  1097. {
  1098. // *** New item *** with main category set
  1099. $cat_asset = 'com_content.category.' . (int)@ $item->catid;
  1100. return $user->authorise('core.edit.state', $cat_asset) || ($user->authorise('core.edit.state.own', $cat_asset) && $isOwner);
  1101. }
  1102. else
  1103. {
  1104. // *** New item *** get general edit/publish/delete permissions
  1105. return $user->authorise('core.edit.state', 'com_flexicontent') || $user->authorise('core.edit.state.own', 'com_flexicontent');
  1106. }
  1107. }
  1108. else if (FLEXI_ACCESS)
  1109. {
  1110. if ( !empty($item->id) )
  1111. {
  1112. // Existing item, use item specific permissions
  1113. $rights = FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $item->id, $item->catid);
  1114. return ($user->gid < 25) ? ( (in_array('publishown', $rights) && $isOwner) || (in_array('publish', $rights)) ) : 1;
  1115. }
  1116. elseif ( $check_cat_perm && !empty($item->catid) )
  1117. {
  1118. // *** New item *** with main category set
  1119. $rights = FAccess::checkAllCategoryAccess('com_content', 'users', $user->gmid, $item->catid);
  1120. return ($user->gid < 25) ? ( (in_array('publishown', $rights) && $isOwner) || (in_array('publish', $rights)) ) : 1;
  1121. }
  1122. else
  1123. {
  1124. // *** New item *** get general edit/publish/delete permissions
  1125. $canPublishAll = FAccess::checkAllContentAccess('com_content','publish','users',$user->gmid,'content','all');
  1126. $canPublishOwnAll = FAccess::checkAllContentAccess('com_content','publishown','users',$user->gmid,'content','all');
  1127. return ($user->gid < 25) ? $canPublishAll || $canPublishOwnAll : 1;
  1128. }
  1129. }
  1130. else
  1131. {
  1132. // J1.5 permissions with no FLEXIaccess are only general, no item specific permissions
  1133. return ($user->gid >= 21);
  1134. }
  1135. }
  1136. /**
  1137. * Method to initialise the item data
  1138. *
  1139. * @access private
  1140. * @return boolean True on success
  1141. * @since 1.0
  1142. */
  1143. function _initItem()
  1144. {
  1145. // Lets load the item if it doesn't already exist
  1146. if (empty($this->_item))
  1147. {
  1148. // Get some variables
  1149. $app = JFactory::getApplication();
  1150. $user = JFactory::getUser();
  1151. $createdate = JFactory::getDate();
  1152. $nullDate = $this->_db->getNullDate();
  1153. $cparams = $this->_cparams;
  1154. // Load default empty item
  1155. $item = JTable::getInstance('flexicontent_items', '');
  1156. $public_accesslevel = !FLEXI_J16GE ? 0 : 1;
  1157. $default_accesslevel = FLEXI_J16GE ? $app->getCfg( 'access', $public_accesslevel ) : $public_accesslevel;
  1158. // Decide default publication state. NOTE this will only be used if user has publish privilege, otherwise items
  1159. // will be forced to (a) pending_approval state for NEW ITEMS and (b) to item's current state for EXISTING ITEMS
  1160. $pubished_state = 1; $draft_state = -4; $pending_approval_state = -3;
  1161. if ( $app->isAdmin() ) {
  1162. $default_state = $cparams->get('new_item_state', $pubished_state); // Use the configured setting for backend items
  1163. } else {
  1164. $default_state = $cparams->get('new_item_state_fe', $pubished_state); // Use the configured setting for frontend items
  1165. }
  1166. // Override defaults values, we assigned all properties,
  1167. // despite many of them having the correct value already
  1168. $item->id = 0;
  1169. $item->cid = array();
  1170. $item->categories = array();
  1171. $item->catid = null;
  1172. $item->title = null;
  1173. $item->alias = null;
  1174. $item->title_alias = null; // deprecated do not use
  1175. $item->introtext = null;
  1176. $item->fulltext = null;
  1177. $item->author = null;
  1178. $item->text = null;
  1179. $item->sectionid = FLEXI_SECTION;
  1180. $item->type_id = JRequest::getVar('typeid', 0, '', 'int'); // Get default type from HTTP request
  1181. $item->typename = null;
  1182. $item->typealias = null;
  1183. $item->score = 0;
  1184. $item->votecount = 0;
  1185. $item->hits = 0;
  1186. $item->version = 0;
  1187. $item->current_version = 0;
  1188. $item->last_version = 0;
  1189. $item->metadesc = null;
  1190. $item->metakey = null;
  1191. $item->created = $createdate->toUnix();
  1192. $item->created_by = $user->get('id');
  1193. $item->created_by_alias = null;
  1194. $item->creator = null;
  1195. $item->modified = $nullDate;
  1196. $item->modified_by = null;
  1197. $item->modifier = null;
  1198. $item->publish_up = $createdate->toUnix();
  1199. $item->publish_down = null;
  1200. $item->images = null;
  1201. $item->urls = null;
  1202. $item->attribs = null;
  1203. $item->metadata = null;
  1204. $item->access = $default_accesslevel;
  1205. $item->state = $default_state;
  1206. $item->mask = null; // deprecated do not use
  1207. $item->language = FLEXI_J16GE ? '*' : flexicontent_html::getSiteDefaultLang();
  1208. $item->lang_parent_id = 0;
  1209. $item->search_index = null;
  1210. $item->parameters = clone ($cparams); // Assign component parameters, merge with menu item (for frontend)
  1211. $this->_item = $item;
  1212. }
  1213. return true;
  1214. }
  1215. /**
  1216. * Stock method to auto-populate the model state.
  1217. *
  1218. * @return void
  1219. * @since 1.6
  1220. */
  1221. protected function populateState() {
  1222. $app = JFactory::getApplication();
  1223. // Initialise variables.
  1224. $this->setState($this->getName().'.id', $this->_id);
  1225. // Load global parameters
  1226. $this->setState('params', $this->_cparams);
  1227. }
  1228. /**
  1229. * Tests if the item is checked out
  1230. *
  1231. * @access public
  1232. * @param int A user id
  1233. * @return boolean True if checked out
  1234. * @since 1.0
  1235. */
  1236. function isCheckedOut( $uid=0 )
  1237. {
  1238. if ( $this->_loadItem() )
  1239. {
  1240. if ($uid) {
  1241. return ($this->_item->checked_out && $this->_item->checked_out != $uid);
  1242. } else {
  1243. return $this->_item->checked_out;
  1244. }
  1245. } elseif ($this->_id < 1) {
  1246. return false;
  1247. } else {
  1248. JError::raiseWarning( 0, 'Unable to Load Data');
  1249. return false;
  1250. }
  1251. }
  1252. /**
  1253. * Method to checkin/unlock the item
  1254. *
  1255. * @access public
  1256. * @return boolean True on success
  1257. * @since 1.0
  1258. */
  1259. function checkin($pk = NULL)
  1260. {
  1261. if (!$pk) $pk = $this->_id;
  1262. if ($pk) {
  1263. $item = JTable::getInstance('flexicontent_items', '');
  1264. return $item->checkin($pk);
  1265. }
  1266. return false;
  1267. }
  1268. /**
  1269. * Method to checkout/lock the item
  1270. *
  1271. * @access public
  1272. * @param int $uid User ID of the user checking the item out
  1273. * @return boolean True on success
  1274. * @since 1.0
  1275. */
  1276. function checkout($pk = null) // UPDATED to match function signature of J1.6+ models
  1277. {
  1278. // Make sure we have a record id to checkout the record with
  1279. if ( !$pk ) $pk = $this->_id;
  1280. if ( !$pk ) return true;
  1281. // Get current user
  1282. $user = JFactory::getUser();
  1283. $uid = $user->get('id');
  1284. // Lets get table record and checkout the it
  1285. $tbl = JTable::getInstance('flexicontent_items', '');
  1286. if ( $tbl->checkout($uid, $this->_id) ) return true;
  1287. // Reaching this points means checkout failed
  1288. $this->setError( FLEXI_J16GE ? $tbl->getError() : JText::_("FLEXI_ALERT_CHECKOUT_FAILED") );
  1289. return false;
  1290. }
  1291. /**
  1292. * Method to store the item
  1293. *
  1294. * @access public
  1295. * @return boolean True on success
  1296. * @since 1.0
  1297. */
  1298. function store($data)
  1299. {
  1300. // Check for request forgeries
  1301. if ( !JFactory::getApplication()->isAdmin() ) {
  1302. // For flexible usage, e.g. when it is called by the new IMPORT TASK
  1303. JRequest::checkToken() or jexit( 'Invalid Token' );
  1304. }
  1305. // ****************************
  1306. // Initialize various variables
  1307. // ****************************
  1308. $db = $this->_db;
  1309. $app = JFactory::getApplication();
  1310. $user = JFactory::getUser();
  1311. $dispatcher = JDispatcher::getInstance();
  1312. $cparams = $this->_cparams;
  1313. $nullDate = $this->_db->getNullDate();
  1314. $view = JRequest::getVar('view', false);
  1315. JRequest::setVar("isflexicontent", "yes");
  1316. $use_versioning = $cparams->get('use_versioning', 1);
  1317. $print_logging_info = $cparams->get('print_logging_info');
  1318. if ( $print_logging_info ) {
  1319. global $fc_run_times;
  1320. $start_microtime = microtime(true);
  1321. }
  1322. // Dates displayed in the item form, are in user timezone for J2.5, and in site's default timezone for J1.5
  1323. $site_zone = $app->getCfg('offset');
  1324. $user_zone = $user->getParam('timezone', $site_zone);
  1325. $tz_offset = FLEXI_J16GE ? $user_zone : $site_zone ;
  1326. // Sanitize id and approval flag as integers
  1327. $data['vstate'] = (int)$data['vstate'];
  1328. $data['id'] = (int)$data['id'];
  1329. $isnew = ! $data['id'];
  1330. // *****************************************
  1331. // Get an item object and load existing item
  1332. // *****************************************
  1333. // Get an empty item model (with default values)
  1334. $item = $this->getTable('flexicontent_items', '');
  1335. // ... existing items
  1336. if ( !$isnew ) {
  1337. // Load existing item into the empty item model
  1338. $item->load( $data['id'] );
  1339. // Get item's assigned categories
  1340. $query = 'SELECT DISTINCT catid FROM #__flexicontent_cats_item_relations WHERE itemid = ' . (int)$this->_id;
  1341. $db->setQuery($query);
  1342. $item->categories = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  1343. // We need to fake joomla's states ... when triggering the before save content event
  1344. $fc_state = $item->state;
  1345. if ( in_array($fc_state, array(1,-5)) ) $jm_state = 1; // published states
  1346. else if ( in_array($fc_state, array(0,-3,-4)) ) $jm_state = 0; // unpublished states
  1347. else $jm_state = $fc_state; // trashed & archive states
  1348. // Frontend SECURITY concern: ONLY allow to set item type for new items !!!
  1349. if( !$app->isAdmin() )
  1350. unset($data['type_id']);
  1351. } else {
  1352. $item->categories = array();
  1353. }
  1354. // *********************************
  1355. // Check and correct given item DATA
  1356. // *********************************
  1357. // tags and cats will need some manipulation so we retieve them
  1358. $tags = $this->formatToArray( @ $data['tag'] );
  1359. $cats = $this->formatToArray( @ $data['cid'] );
  1360. $featured_cats = $this->formatToArray( @ $data['featured_cid'] );
  1361. unset($data['tag']); unset($data['cid']); unset($data['featured_cid']);
  1362. // Make tags unique
  1363. $tags = array_unique($tags);
  1364. // Auto-assign a not set main category, to be the first out of secondary categories,
  1365. if ( empty($data['catid']) && !empty($cats[0]) ) {
  1366. $data['catid'] = $cats[0];
  1367. }
  1368. $cats_indexed = array_flip($cats);
  1369. // Add the primary cat to the array if it's not already in
  1370. if ( @ $data['catid'] && !isset($cats_indexed[$data['catid']]) ) {
  1371. $cats[] = $data['catid'];
  1372. }
  1373. // Add the primary cat to the array if it's not already in
  1374. if ( !empty($featured_cats) ) foreach ( $featured_cats as $featured_cat ) {
  1375. if (@ $featured_cat && !isset($cats_indexed[$featured_cat]) ) $cats[] = $featured_cat;
  1376. }
  1377. // *****************************
  1378. // Retrieve author configuration
  1379. // *****************************
  1380. $db->setQuery('SELECT author_basicparams FROM #__flexicontent_authors_ext WHERE user_id = ' . $user->id);
  1381. if ( $authorparams = $db->loadResult() )
  1382. $authorparams = FLEXI_J16GE ? new JRegistry($authorparams) : new JParameter($authorparams);
  1383. // At least one category needs to be assigned
  1384. if (!is_array( $cats ) || count( $cats ) < 1) {
  1385. $this->setError(JText::_('FLEXI_OPERATION_FAILED') .", ". JText::_('FLEXI_REASON') .": ". JText::_('FLEXI_SELECT_CATEGORY'));
  1386. return false;
  1387. // Check more than allowed categories
  1388. } else {
  1389. // Get author's maximum allowed categories per item and set js limitation
  1390. $max_cat_assign = !$authorparams ? 0 : intval($authorparams->get('max_cat_assign',0));
  1391. // Verify category limitation for current author
  1392. if ( $max_cat_assign ) {
  1393. if ( count($cats) > $max_cat_assign ) {
  1394. if ( count($cats) <= count($item->categories) ) {
  1395. $existing_only = true;
  1396. // Maximum number of categories is exceeded, but do not abort if only using existing categories
  1397. foreach ($cats as $newcat) {
  1398. $existing_only = $existing_only && in_array($newcat, $item->categories);
  1399. }
  1400. } else {
  1401. $existing_only = false;
  1402. }
  1403. if (!$existing_only) {
  1404. $this->setError(JText::_('FLEXI_OPERATION_FAILED') .", ". JText::_('FLEXI_REASON') .": ". JText::_('FLEXI_TOO_MANY_ITEM_CATEGORIES').$max_cat_assign);
  1405. return false;
  1406. }
  1407. }
  1408. }
  1409. }
  1410. // Set back the altered categories and tags to the form data
  1411. $data['categories'] = $cats; // Set it to real name of field: 'categories' INSTEAD OF 'cid'
  1412. $data['tags'] = $tags; // Set it to real name of field: 'tags' INSTEAD OF 'tag'
  1413. // Reconstruct (main)text field if it has splitted up e.g. to seperate editors per tab
  1414. if (@$data['text'] && is_array($data['text'])) {
  1415. $data['text'][0] .= (preg_match('#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i', $data['text'][0]) == 0) ? ("\n".'<hr id="system-readmore" />') : "" ;
  1416. $tabs_text = '';
  1417. foreach($data['text'] as $tab_text) {
  1418. $tabs_text .= $tab_text;
  1419. }
  1420. $data['text'] = & $tabs_text;
  1421. }
  1422. // The text field is stored in the db as to seperate fields: introtext & fulltext
  1423. // So we search for the {readmore} tag and split up the text field accordingly.
  1424. $pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i';
  1425. $tagPos = preg_match($pattern, @ $data['text']);
  1426. if ($tagPos == 0) {
  1427. $data['introtext'] = @ $data['text'];
  1428. $data['fulltext'] = '';
  1429. } else {
  1430. list($data['introtext'], $data['fulltext']) = preg_split($pattern, $data['text'], 2);
  1431. $data['fulltext'] = JString::strlen( trim($data['fulltext']) ) ? $data['fulltext'] : '';
  1432. }
  1433. // ***************************************************************************************
  1434. // Handle Parameters: attribs & metadata, merging POST values into existing values,
  1435. // IF these were not set at all then there will be no need to merge,
  1436. // BUT part of them may have been displayed, so we use mergeAttributes() instead of bind()
  1437. // Keys that are not set will not be set, thus the previous value is maintained
  1438. // ***************************************************************************************
  1439. // Retrieve (a) item parameters (array PARAMS or ATTRIBS ) and (b) item metadata (array METADATA or META )
  1440. if ( !FLEXI_J16GE ) {
  1441. $params = $this->formatToArray( @ $data['params'] );
  1442. $metadata = $this->formatToArray( @ $data['meta'] );
  1443. unset($data['params']);
  1444. unset($data['meta']);
  1445. } else {
  1446. $params = $this->formatToArray( @ $data['attribs'] );
  1447. $metadata = $this->formatToArray( @ $data['metadata'] );
  1448. unset($data['attribs']);
  1449. unset($data['metadata']);
  1450. }
  1451. // Merge (form posted) item attributes and metadata parameters INTO EXISTING DATA (see above for explanation)
  1452. $this->mergeAttributes($item, $params, $metadata);
  1453. // *******************************************************
  1454. // Retrieve submit configuration for new items in frontend
  1455. // *******************************************************
  1456. if ( $app->isSite() && $isnew && !empty($data['submit_conf']) ) {
  1457. $h = $data['submit_conf'];
  1458. $session = JFactory::getSession();
  1459. $item_submit_conf = $session->get('item_submit_conf', array(),'flexicontent');
  1460. $submit_conf = @ $item_submit_conf[$h] ;
  1461. $autopublished = @ $submit_conf['autopublished'];
  1462. $overridecatperms = @ $submit_conf['overridecatperms'];
  1463. if ( $autopublished) {
  1464. // Dates forced during autopublishing
  1465. if ( @ $submit_conf['autopublished_up_interval'] ) {
  1466. if (FLEXI_J16GE) {
  1467. $publish_up_date = JFactory::getDate(); // Gives editor's timezone by default
  1468. $publish_up_date->modify('+ '.$submit_conf['autopublished_up_interval'].' minutes');
  1469. $publish_up_forced = $publish_up_date->toSql();
  1470. } else {
  1471. $publish_up_date = new DateTime(JHTML::_('date', JFactory::getDate()->toFormat(), '%Y-%m-%d %H:%M:%S'));
  1472. $publish_up_date->modify('+ '.$submit_conf['autopublished_up_interval'].' minutes');
  1473. $publish_up_forced = $publish_up_date->format('Y-m-d H:i:s');
  1474. }
  1475. }
  1476. if ( @ $submit_conf['autopublished_down_interval'] ) {
  1477. if (FLEXI_J16GE) {
  1478. $publish_down_date = JFactory::getDate(); // Gives editor's timezone by default
  1479. $publish_down_date->modify('+ '.$submit_conf['autopublished_down_interval'].' minutes');
  1480. $publish_down_forced = $publish_down_date->toSql();
  1481. } else {
  1482. $publish_down_date = new DateTime(JHTML::_('date', JFactory::getDate()->toFormat(), '%Y-%m-%d %H:%M:%S'));
  1483. $publish_down_date->modify('+ '.$submit_conf['autopublished_down_interval'].' minutes');
  1484. $publish_down_forced = $publish_down_date->format('Y-m-d H:i:s');
  1485. }
  1486. }
  1487. }
  1488. } else {
  1489. $autopublished = 0;
  1490. $overridecatperms = 0;
  1491. }
  1492. // ***********************************************************
  1493. // SECURITY concern: Check form tampering of categories, of:
  1494. // (a) menu overridden categories for frontent item submit
  1495. // (b) or check user has 'create' privilege in item categories
  1496. // ***********************************************************
  1497. if ($overridecatperms)
  1498. {
  1499. $allowed_cid = @ $submit_conf['cids'];
  1500. }
  1501. else
  1502. {
  1503. if (FLEXI_J16GE || FLEXI_ACCESS) {
  1504. $allowed_cid = FlexicontentHelperPerm::getAllowedCats($user, $actions_allowed = array('core.create'), $require_all=true);
  1505. }
  1506. }
  1507. if ( isset($allowed_cid) ) {
  1508. // Add existing item's categories into the user allowed categories
  1509. $allowed_cid = array_merge($allowed_cid, $item->categories);
  1510. // Check main category tampering
  1511. if ( !in_array($data['catid'], $allowed_cid) && $data['catid'] != $item->catid ) {
  1512. $this->setError( 'main category is not in allowed list (form tampered ?)' );
  1513. return false;
  1514. }
  1515. // Check multi category tampering
  1516. $postcats = @ $submit_conf['postcats'];
  1517. if ( !$isnew || !$overridecatperms || $postcats==2 )
  1518. $data['categories'] = array_intersect ($data['categories'], $allowed_cid );
  1519. else if ( $postcats==0 )
  1520. $data['categories'] = $allowed_cid;
  1521. else if ( $postcats==1 )
  1522. $data['categories'] = array($data['catid']);
  1523. }
  1524. // *****************************************************************
  1525. // SECURITY concern: Check form tampering of state related variables
  1526. // *****************************************************************
  1527. // Save old main category & creator (owner)
  1528. $old_created_by = $item->created_by;
  1529. $old_catid = $item->catid;
  1530. // New or Existing item must use the current user + new main category to calculate 'Edit State' privelege
  1531. $item->created_by = $user->get('id');
  1532. $item->catid = $data['catid'];
  1533. $canEditState = $this->canEditState( $item, $check_cat_perm=true );
  1534. // Restore old main category & creator (owner) (in case following code chooses to keep them)
  1535. $item->created_by = $old_created_by;
  1536. $item->catid = $old_catid;
  1537. // If cannot edit state prevent user from changing state related parameters
  1538. if ( !$canEditState )
  1539. {
  1540. $data['vstate'] = 1;
  1541. if (!FLEXI_J16GE) {
  1542. // Behaviour is different in J1.5, it requires edit instead of edit state
  1543. //unset( $data['details']['publish_up'] );
  1544. //unset( $data['details']['publish_down'] );
  1545. //unset( $data['ordering'] );
  1546. } else {
  1547. unset( $data['featured'] );
  1548. unset( $data['publish_up'] );
  1549. unset( $data['publish_down'] );
  1550. unset( $data['ordering'] );
  1551. }
  1552. // Check for publish up/down dates forced during auto-publishing
  1553. if ( @ $publish_up_forced ) $data['publish_up'] = $publish_up_forced;
  1554. if ( @ $publish_down_forced ) $data['publish_down'] = $publish_down_forced;
  1555. $pubished_state = 1; $draft_state = -4; $pending_approval_state = -3;
  1556. if (!$isnew) {
  1557. // Prevent changing state of existing items by users that cannot publish
  1558. $catid_changed = $old_catid != $data['catid'];
  1559. if ($catid_changed && !$use_versioning) {
  1560. $data['state'] = $pending_approval_state;
  1561. $app->enqueueMessage('You have changed category for this content item to be a category in which you cannot publish, you content item is now in "Pending Approval" State, you will have to wait for it to be re-approved', 'warning');
  1562. } else {
  1563. $data['state'] = $item->state;
  1564. }
  1565. }
  1566. else if ($autopublished) {
  1567. // Autopublishing new item via menu configuration
  1568. $data['state'] = $pubished_state;
  1569. }
  1570. else {
  1571. // The preselected forced state of -NEW- items for users that CANNOT publish, and autopublish via menu item is disabled
  1572. if ( $app->isAdmin() ) {
  1573. $data['state'] = $cparams->get('non_publishers_item_state', $draft_state); // Use the configured setting for backend items
  1574. } else {
  1575. $data['state'] = $cparams->get('non_publishers_item_state_fe', $pending_approval_state); // Use the configured setting for frontend items
  1576. }
  1577. }
  1578. }
  1579. $isSuperAdmin = FLEXI_J16GE ? $user->authorise('core.admin', 'root.1') : ($user->gid >= 25);
  1580. // Prevent frontend user from changing the item owner and creation date unless they are super admin
  1581. if ( $app->isSite() && !$isSuperAdmin )
  1582. {
  1583. if (!FLEXI_J16GE) {
  1584. if ($isnew) $data['details']['created_by'] = $user->get('id');
  1585. else unset( $data['details']['created_by'] );
  1586. unset( $data['details']['created'] );
  1587. unset( $data['details']['created_by_alias'] );
  1588. } else {
  1589. if ($isnew) $data['created_by'] = $user->get('id');
  1590. else unset( $data['created_by'] );
  1591. if ( !$user->authorise('flexicontent.editcreationdate', 'com_flexicontent') )
  1592. unset( $data['created'] );
  1593. unset( $data['created_by_alias'] );
  1594. }
  1595. }
  1596. // ***********************************************************
  1597. // SECURITY concern: Check form tampering of allowed languages
  1598. // ***********************************************************
  1599. $allowed_langs = !$authorparams ? null : $authorparams->get('langs_allowed',null);
  1600. $allowed_langs = !$allowed_langs ? null : FLEXIUtilities::paramToArray($allowed_langs);
  1601. if (!$isnew && $allowed_langs) $allowed_langs[] = $item->language;
  1602. if ( $allowed_langs && isset($data['language']) && !in_array($data['language'], $allowed_langs) ) {
  1603. $app->enqueueMessage('You are not allowed to assign language: '.$data['language'].' to Content Items', 'warning');
  1604. unset($data['language']);
  1605. if ($isnew) return false;
  1606. }
  1607. // ************************************************
  1608. // Bind given item DATA and PARAMETERS to the model
  1609. // ************************************************
  1610. // Bind the given data to the items
  1611. if ( !$item->bind($data) ) {
  1612. $this->setError($this->_db->getErrorMsg());
  1613. return false;
  1614. }
  1615. // Bind posted publication details (parameters) to the model for J1.5
  1616. if (!FLEXI_J16GE) {
  1617. $details = $this->formatToArray( @ $data['details'] );
  1618. $item->bind($details);
  1619. }
  1620. // **************************************
  1621. // Check and correct core item properties
  1622. // **************************************
  1623. // -- Modification Date and Modifier, (a) new item gets null modification date and (b) existing item get the current date
  1624. if ($isnew) {
  1625. $item->modified = $nullDate;
  1626. $item->modified_by = 0;
  1627. } else {
  1628. $datenow = JFactory::getDate();
  1629. $item->modified = FLEXI_J16GE ? $datenow->toSql() : $datenow->toMySQL();
  1630. $item->modified_by = $user->get('id');
  1631. }
  1632. // -- Creator, if this is not already set, will be the current user or administrator if current user is not logged
  1633. if ( !$item->created_by ) {
  1634. $item->created_by = $user->get('id') ? $user->get('id') : JFactory::getUser( 'admin' )->get('id');
  1635. }
  1636. // -- Creation Date
  1637. if ($item->created && JString::strlen(trim( $item->created )) <= 10) {
  1638. $item->created .= ' 00:00:00';
  1639. }
  1640. if (FLEXI_J16GE) {
  1641. $date = JFactory::getDate($item->created);
  1642. $date->setTimeZone( new DateTimeZone( $tz_offset ) ); // J2.5: Date from form field is in user's timezone
  1643. } else {
  1644. $date = JFactory::getDate($item->created, $tz_offset); // J1.5: Date from form field is in site's default timezone
  1645. }
  1646. $item->created = FLEXI_J16GE ? $date->toSql() : $date->toMySQL();
  1647. // -- Publish UP Date
  1648. if ($item->publish_up && JString::strlen(trim( $item->publish_up )) <= 10) {
  1649. $item->publish_up .= ' 00:00:00';
  1650. }
  1651. if (FLEXI_J16GE) {
  1652. $date = JFactory::getDate($item->publish_up);
  1653. $date->setTimeZone( new DateTimeZone( $tz_offset ) ); // J2.5: Date from form field is in user's timezone
  1654. } else {
  1655. $date = JFactory::getDate($item->publish_up, $tz_offset); // J1.5: Date from form field is in site's default timezone
  1656. }
  1657. $item->publish_up = FLEXI_J16GE ? $date->toSql() : $date->toMySQL();
  1658. // -- Publish Down Date
  1659. if (trim($item->publish_down) == JText::_('FLEXI_NEVER') || trim( $item->publish_down ) == '')
  1660. {
  1661. $item->publish_down = $nullDate;
  1662. }
  1663. else if ($item->publish_down != $nullDate)
  1664. {
  1665. if ( JString::strlen(trim( $item->publish_down )) <= 10 ) {
  1666. $item->publish_down .= ' 00:00:00';
  1667. }
  1668. if (FLEXI_J16GE) {
  1669. $date = JFactory::getDate($item->publish_down);
  1670. $date->setTimeZone( new DateTimeZone( $tz_offset ) ); // J2.5: Date from form field is in user's timezone
  1671. } else {
  1672. $date = JFactory::getDate($item->publish_down, $tz_offset); // J1.5: Date from form field is in site's default timezone
  1673. }
  1674. $item->publish_down = FLEXI_J16GE ? $date->toSql() : $date->toMySQL();
  1675. }
  1676. // auto assign the section
  1677. if (!FLEXI_J16GE) $item->sectionid = FLEXI_SECTION;
  1678. // For new items get next available ordering number
  1679. if ($isnew) {
  1680. $item->ordering = $item->getNextOrder();
  1681. }
  1682. // auto assign the default language if not set
  1683. $default_language = FLEXI_J16GE ? '*' : flexicontent_html::getSiteDefaultLang() ;
  1684. $item->language = $item->language ? $item->language : $default_language ;
  1685. // Ignore language parent id if item language is site's (content) default language, and for language 'ALL'
  1686. if ( substr($item->language, 0,2) == substr(flexicontent_html::getSiteDefaultLang(), 0,2) || $item->language=='*' ) {
  1687. $lang_parent_id = $item->lang_parent_id;
  1688. $item->lang_parent_id = $isnew ? 0 : $item->id;
  1689. if ( $item->lang_parent_id != $lang_parent_id && $lang_parent_id ) {
  1690. $app->enqueueMessage(JText::_('FLEXI_ORIGINAL_CONTENT_WAS_IGNORED'), 'message' );
  1691. }
  1692. }
  1693. // ****************************************************************************************************************
  1694. // Get version info, force version approval ON is versioning disabled, and decide new item's current version number
  1695. // ****************************************************************************************************************
  1696. $last_version = FLEXIUtilities::getLastVersions($item->id, true);
  1697. $current_version = FLEXIUtilities::getCurrentVersions($item->id, true);
  1698. // (a) Force item approval when versioning disabled
  1699. $data['vstate'] = ( !$use_versioning ) ? 2 : $data['vstate'];
  1700. // (b) Force item approval when item is not yet visible (is in states (a) Draft or (b) Pending Approval)
  1701. $data['vstate'] = ( $item->state==-3 || $item->state==-4 ) ? 2 : $data['vstate'];
  1702. // Decide new current version for the item, this depends if versioning is ON and if versioned is approved
  1703. if ( !$use_versioning ) {
  1704. // not using versioning, increment current version numbering
  1705. $item->version = $isnew ? 1 : $current_version+1;
  1706. } else {
  1707. // using versioning, increment last version numbering, or keep current version number if new version was not approved
  1708. $item->version = $isnew ? 1 : ( $data['vstate']==2 ? $last_version+1 : $current_version);
  1709. }
  1710. // *** Item version should be zero when form was loaded with no type id,
  1711. // *** thus next item form load will load default values of custom fields
  1712. $item->version = ($isnew && !empty($data['type_id_not_set']) ) ? 0 : $item->version;
  1713. if ( $print_logging_info ) @$fc_run_times['item_store_prepare'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1714. // *********************************************************************************************
  1715. // Make sure we import flexicontent AND content plugins since we will be triggering their events
  1716. // *********************************************************************************************
  1717. JPluginHelper::importPlugin('flexicontent');
  1718. JPluginHelper::importPlugin('content');
  1719. // **************************************************************************************************
  1720. // Trigger Event 'onBeforeSaveItem' of FLEXIcontent plugins (such plugin is the 'flexinotify' plugin)
  1721. // **************************************************************************************************
  1722. if ( $print_logging_info ) $start_microtime = microtime(true);
  1723. $result = $dispatcher->trigger('onBeforeSaveItem', array(&$item, $isnew));
  1724. if((count($result)>0) && in_array(false, $result, true)) return false; // cancel item save
  1725. if ( $print_logging_info ) $fc_run_times['onBeforeSaveItem_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1726. // ******************************************************************************************************
  1727. // Trigger Event 'OnBeforeContentSave' (J1.5) or 'onContentBeforeSave' (J2.5) of Joomla's Content plugins
  1728. // ******************************************************************************************************
  1729. // Some compatibility steps
  1730. if (!$isnew) { $db->setQuery( 'UPDATE #__content SET state = '. $jm_state .' WHERE id = '.$item->id ); $db->query(); }
  1731. JRequest::setVar('view', 'article'); JRequest::setVar('option', 'com_content');
  1732. if ( $print_logging_info ) $start_microtime = microtime(true);
  1733. if (FLEXI_J16GE) $result = $dispatcher->trigger($this->event_before_save, array('com_content.article', &$item, $isnew));
  1734. else $result = $dispatcher->trigger('onBeforeContentSave', array(&$item, $isnew));
  1735. if ( $print_logging_info ) $fc_run_times['onContentBeforeSave_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1736. // Reverse compatibility steps
  1737. if (!$isnew) { $db->setQuery( 'UPDATE #__content SET state = '. $fc_state .' WHERE id = '.$item->id ); $db->query(); }
  1738. JRequest::setVar('view', $view); JRequest::setVar('option', 'com_flexicontent');
  1739. if (in_array(false, $result, true)) { $this->setError($item->getError()); return false; } // cancel item save
  1740. // ************************************************************************************************************
  1741. // IF new item, create it before saving the fields (and constructing the search_index out of searchable fields)
  1742. // ************************************************************************************************************
  1743. if ( $print_logging_info ) $start_microtime = microtime(true);
  1744. if( $isnew )
  1745. {
  1746. $this->applyCurrentVersion($item, $data, $createonly=true);
  1747. } else {
  1748. // Make sure the data of the model are correct,
  1749. // e.g. a getForm() used to validate input data may have set an empty item and empty id
  1750. // e.g. type_id of item may have been altered by authorized users
  1751. $this->_id = $item->id;
  1752. $this->_item = & $item;
  1753. }
  1754. if ( $print_logging_info ) $fc_run_times['item_store_core'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1755. // ****************************************************************************
  1756. // Save fields values to appropriate tables (versioning table or normal tables)
  1757. // NOTE: This allow canceling of item save operation, if 'abort' is returned
  1758. // ****************************************************************************
  1759. $files = JRequest::get( 'files', JREQUEST_ALLOWRAW );
  1760. $result = $this->saveFields($isnew, $item, $data, $files);
  1761. $version_approved = $isnew || $data['vstate']==2;
  1762. if( $result==='abort' ) {
  1763. if ($isnew) {
  1764. if (FLEXI_J16GE) {
  1765. $db->setQuery('DELETE FROM #__assets WHERE id = (SELECT asset_id FROM #__content WHERE id='.$item->id.')');
  1766. $db->query();
  1767. } else if (FLEXI_ACCESS) {
  1768. $db->setQuery('DELETE FROM #__flexiaccess_acl WHERE acosection = `com_content` AND axosection = `item` AND axo ='.$item->id);
  1769. $db->query();
  1770. }
  1771. $db->setQuery('DELETE FROM #__content WHERE id ='.$item->id);
  1772. $db->query();
  1773. $db->setQuery('DELETE FROM #__flexicontent_items_ext WHERE item_id='.$item->id);
  1774. $db->query();
  1775. $this->setId(0);
  1776. $this->setError( $this->getError().' '.JText::_('FLEXI_NEW_ITEM_NOT_CREATED') );
  1777. } else {
  1778. $this->setError( $this->getError().' '.JText::_('FLEXI_EXISTING_ITEM_NOT_SAVED') );
  1779. }
  1780. // Set form to reload posted data
  1781. /*$session = JFactory::getSession();
  1782. $session->set('item_edit_postdata', $data, 'flexicontent');*/
  1783. return false;
  1784. }
  1785. // ***************************************************************
  1786. // ITEM DATA SAVED: EITHER new, OR approving current item version
  1787. // ***************************************************************
  1788. if ( $version_approved )
  1789. {
  1790. // *****************************
  1791. // Save item to #__content table
  1792. // *****************************
  1793. if ( $print_logging_info ) $start_microtime = microtime(true);
  1794. if( !$this->applyCurrentVersion($item, $data) ) return false;
  1795. if ( $print_logging_info ) @$fc_run_times['item_store_core'] += round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1796. //echo "<pre>"; var_dump($data); exit();
  1797. // ***************************
  1798. // Update Joomla Featured FLAG
  1799. // ***************************
  1800. if (FLEXI_J16GE) $this->featured(array($item->id), $item->featured);
  1801. // *****************************************************************************************************
  1802. // Trigger Event 'onAfterContentSave' (J1.5) OR 'onContentAfterSave' (J2.5 ) of Joomla's Content plugins
  1803. // *****************************************************************************************************
  1804. if ( $print_logging_info ) $start_microtime = microtime(true);
  1805. // Some compatibility steps
  1806. JRequest::setVar('view', 'article');
  1807. JRequest::setVar('option', 'com_content');
  1808. if (FLEXI_J16GE) $dispatcher->trigger($this->event_after_save, array('com_content.article', &$item, $isnew));
  1809. else $dispatcher->trigger('onAfterContentSave', array(&$item, $isnew));
  1810. // Reverse compatibility steps
  1811. JRequest::setVar('view', $view);
  1812. JRequest::setVar('option', 'com_flexicontent');
  1813. if ( $print_logging_info ) @$fc_run_times['onContentAfterSave_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1814. }
  1815. // *************************************************************************************************
  1816. // Trigger Event 'onAfterSaveItem' of FLEXIcontent plugins (such plugin is the 'flexinotify' plugin)
  1817. // *************************************************************************************************
  1818. if ( $print_logging_info ) $start_microtime = microtime(true);
  1819. $results = $dispatcher->trigger('onAfterSaveItem', array( &$item, &$data ));
  1820. if ( $print_logging_info ) @$fc_run_times['onAfterSaveItem_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1821. // *********************************************************************
  1822. // ITEM DATA NOT SAVED: NEITHER new, NOR approving current item version
  1823. // *********************************************************************
  1824. if ( !$version_approved ) {
  1825. // Warn editor that his/her changes will need approval to before becoming visible
  1826. if ( $canEditState )
  1827. JError::raiseNotice(11, JText::_('FLEXI_SAVED_VERSION_WAS_NOT_APPROVED_NOTICE') );
  1828. else
  1829. JError::raiseNotice(10, JText::_('FLEXI_SAVED_VERSION_MUST_BE_APPROVED_NOTICE') );
  1830. // Set modifier and modification time (as if item has been saved), so that we can use this information for updating the versioning tables
  1831. $datenow = JFactory::getDate();
  1832. $item->modified = FLEXI_J16GE ? $datenow->toSql() : $datenow->toMySQL();
  1833. $item->modified_by = $user->get('id');
  1834. }
  1835. // *********************************************
  1836. // Create and store version METADATA information
  1837. // *********************************************
  1838. if ( $print_logging_info ) $start_microtime = microtime(true);
  1839. if ($use_versioning) {
  1840. $v = new stdClass();
  1841. $v->item_id = (int)$item->id;
  1842. $v->version_id = ($isnew && !empty($data['type_id_not_set']) ) ? 0 : (int)$last_version+1;
  1843. $v->created = $item->created;
  1844. $v->created_by = $item->created_by;
  1845. if ($item->modified != $nullDate) {
  1846. // NOTE: We set modifier as creator of the version, and modication date as creation date of the version
  1847. $v->created = $item->modified;
  1848. $v->created_by = $item->modified_by;
  1849. }
  1850. $v->comment = isset($data['versioncomment']) ? htmlspecialchars($data['versioncomment'], ENT_QUOTES) : '';
  1851. $this->_db->insertObject('#__flexicontent_versions', $v);
  1852. }
  1853. // *************************************************************
  1854. // Delete old versions that are above the limit of kept versions
  1855. // *************************************************************
  1856. $vcount = FLEXIUtilities::getVersionsCount($item->id);
  1857. $vmax = $cparams->get('nr_versions', 10);
  1858. if ($vcount > ($vmax+1)) {
  1859. $deleted_version = FLEXIUtilities::getFirstVersion($item->id, $vmax, $current_version);
  1860. $query = 'DELETE'
  1861. .' FROM #__flexicontent_items_versions'
  1862. .' WHERE item_id = ' . (int)$item->id
  1863. .' AND version <' . $deleted_version
  1864. .' AND version!=' . (int)$current_version
  1865. ;
  1866. $this->_db->setQuery($query);
  1867. $this->_db->query();
  1868. $query = 'DELETE'
  1869. .' FROM #__flexicontent_versions'
  1870. .' WHERE item_id = ' . (int)$item->id
  1871. .' AND version_id <' . $deleted_version
  1872. .' AND version_id!=' . (int)$current_version
  1873. ;
  1874. $this->_db->setQuery($query);
  1875. $this->_db->query();
  1876. }
  1877. if ( $print_logging_info ) @$fc_run_times['ver_cleanup_ver_metadata'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1878. // ****************************************************************************************************
  1879. // Trigger Event 'onCompleteSaveItem' of FLEXIcontent plugins (such plugin is the 'flexinotify' plugin)
  1880. // ****************************************************************************************************
  1881. if ( $print_logging_info ) $start_microtime = microtime(true);
  1882. $results = $dispatcher->trigger('onCompleteSaveItem', array( &$item, &$fields ));
  1883. if ( $print_logging_info ) @$fc_run_times['onCompleteSaveItem_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1884. return true;
  1885. }
  1886. /**
  1887. * Method to save field values of the item in field versioning DB table or in ..._fields_item_relations DB table
  1888. *
  1889. * @access public
  1890. * @return boolean True on success
  1891. * @since 1.0
  1892. */
  1893. function saveFields($isnew, &$item, &$data, &$files)
  1894. {
  1895. $app = JFactory::getApplication();
  1896. $user = JFactory::getUser();
  1897. $dispatcher = JDispatcher::getInstance();
  1898. $cparams = $this->_cparams;
  1899. $use_versioning = $cparams->get('use_versioning', 1);
  1900. $print_logging_info = $cparams->get('print_logging_info');
  1901. $last_version = FLEXIUtilities::getLastVersions($item->id, true);
  1902. $mval_query = true;
  1903. if ( $print_logging_info ) global $fc_run_times;
  1904. if ( $print_logging_info ) $start_microtime = microtime(true);
  1905. //*********************************
  1906. // Checks for untranslatable fields
  1907. //*********************************
  1908. // CASE 1. Check if saving an item that translates an original content in site's default language
  1909. // ... Decide whether to retrieve field values of untranslatable fields from the original content item
  1910. $enable_translation_groups = $cparams->get('enable_translation_groups');
  1911. $is_content_default_lang = substr(flexicontent_html::getSiteDefaultLang(), 0,2) == substr($item->language, 0,2);
  1912. $get_untraslatable_values = $enable_translation_groups && !$is_content_default_lang && $item->lang_parent_id && $item->lang_parent_id!=$item->id;
  1913. // CASE 2. Check if saving an original content item (item's language is site default language)
  1914. // ... Get item ids of translating items
  1915. if ($is_content_default_lang && $this->_id) {
  1916. $query = 'SELECT ie.item_id'
  1917. .' FROM #__flexicontent_items_ext as ie'
  1918. .' WHERE ie.lang_parent_id = ' . (int)$this->_id
  1919. .' AND ie.item_id <> '.(int)$this->_id; // DO NOT include the item itself in associated translations !!
  1920. $this->_db->setQuery($query);
  1921. $assoc_item_ids = FLEXI_J16GE ? $this->_db->loadColumn() : $this->_db->loadResultArray();
  1922. }
  1923. if (empty($assoc_item_ids)) $assoc_item_ids = array();
  1924. // ***************************************************************************************************************************
  1925. // Get item's fields ... and their values (for untranslatable fields the field values from original content item are retrieved
  1926. // ***************************************************************************************************************************
  1927. $fields = $this->getExtrafields($force=true, $get_untraslatable_values ? $item->lang_parent_id : 0);
  1928. // ******************************************************************************************************************
  1929. // Loop through Fields triggering onBeforeSaveField Event handlers, this was seperated from the rest of the process
  1930. // to give chance to ALL fields to check their DATA and cancel item saving process before saving any new field values
  1931. // ******************************************************************************************************************
  1932. $searchindex = array();
  1933. if ($fields)
  1934. {
  1935. foreach($fields as $field)
  1936. {
  1937. // Set vstate property into the field object to allow this to be changed be the before saving field event handler
  1938. $field->item_vstate = $data['vstate'];
  1939. if (FLEXI_J16GE)
  1940. $is_editable = !$field->valueseditable || $user->authorise('flexicontent.editfieldvalues', 'com_flexicontent.field.' . $field->id);
  1941. else if (FLEXI_ACCESS && $user->gid < 25)
  1942. $is_editable = !$field->valueseditable || FAccess::checkAllContentAccess('com_content','submit','users', $user->gmid, 'field', $field->id);
  1943. else
  1944. $is_editable = 1;
  1945. // FORM HIDDEN FIELDS (FRONTEND/BACKEND) AND (ACL) UNEDITABLE FIELDS: maintain their DB value ...
  1946. if (
  1947. ( $app->isSite() && ($field->formhidden==1 || $field->formhidden==3 || $field->parameters->get('frontend_hidden')) ) ||
  1948. ( $app->isAdmin() && ($field->formhidden==2 || $field->formhidden==3 || $field->parameters->get('backend_hidden')) ) ||
  1949. !$is_editable
  1950. ) {
  1951. $postdata[$field->name] = $field->value;
  1952. // UNTRANSLATABLE (CUSTOM) FIELDS: maintain their DB value ...
  1953. } else if ( $get_untraslatable_values && $field->untranslatable ) {
  1954. $postdata[$field->name] = $field->value;
  1955. // CORE FIELDS: if not set maintain their DB value ...
  1956. } else if ($field->iscore) {
  1957. $postdata[$field->name] = @$data[$field->name];
  1958. if ( is_array($postdata[$field->name]) && !count($postdata[$field->name]) || !is_array($postdata[$field->name]) && !strlen(trim($postdata[$field->name])) ) {
  1959. $postdata[$field->name] = $field->value;
  1960. }
  1961. // OTHER CUSTOM FIELDS (not hidden and not untranslatable)
  1962. } else {
  1963. $postdata[$field->name] = !FLEXI_J16GE ? @$data[$field->name] : @$data['custom'][$field->name];
  1964. }
  1965. // Unserialize values already serialized values, e.g. (a) if current values used are from DB or (b) are being imported from CSV file
  1966. $postdata[$field->name] = !is_array($postdata[$field->name]) ? array($postdata[$field->name]) : $postdata[$field->name];
  1967. //echo "<b>{$field->field_type}</b>: <br/> <pre>".print_r($postdata[$field->name], true)."</pre>\n";
  1968. foreach ($postdata[$field->name] as $i => $postdata_val) {
  1969. if ( @unserialize($postdata_val)!== false || $postdata_val === 'b:0;' ) {
  1970. $postdata[$field->name][$i] = unserialize($postdata_val);
  1971. }
  1972. }
  1973. // Trigger plugin Event 'onBeforeSaveField'
  1974. $fieldname = $field->iscore ? 'core' : $field->field_type;
  1975. $result = FLEXIUtilities::call_FC_Field_Func($fieldname, 'onBeforeSaveField', array( &$field, &$postdata[$field->name], &$files[$field->name], &$item ));
  1976. if ($result===false) {
  1977. // Field requested to abort item saving
  1978. $this->setError( JText::sprintf('FLEXI_FIELD_VALUE_IS_INVALID', $field->label) );
  1979. return 'abort';
  1980. }
  1981. // Get vstate property from the field object back to the data array
  1982. $data['vstate'] = $field->item_vstate;
  1983. }
  1984. //echo "<pre>"; print_r($postdata); echo "</pre>"; exit;
  1985. }
  1986. if ( $print_logging_info ) @$fc_run_times['fields_value_preparation'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  1987. // ****************************************************************************************************************************
  1988. // Loop through Fields triggering onIndexAdvSearch, onIndexSearch Event handlers, this was seperated from the before save field
  1989. // event, so that we will update search indexes only if the above has not canceled saving OR has not canceled version approval
  1990. // ****************************************************************************************************************************
  1991. if ( $print_logging_info ) $start_microtime = microtime(true);
  1992. if ($fields)
  1993. {
  1994. $ai_query_vals = array();
  1995. foreach($fields as $field)
  1996. {
  1997. $fieldname = $field->iscore ? 'core' : $field->field_type;
  1998. if ( $data['vstate']==2 || $isnew) // update (regardless of state!!) search indexes if document version is approved OR item is new
  1999. {
  2000. // Trigger plugin Event 'onIndexAdvSearch' to update field-item pair records in advanced search index
  2001. FLEXIUtilities::call_FC_Field_Func($fieldname, 'onIndexAdvSearch', array( &$field, &$postdata[$field->name], &$item ));
  2002. if ( isset($field->ai_query_vals) ) foreach ($field->ai_query_vals as $query_val) $ai_query_vals[] = $query_val;
  2003. //echo $field->name .":".implode(",", @$field->ai_query_vals ? $field->ai_query_vals : array() )."<br/>";
  2004. // Trigger plugin Event 'onIndexSearch' to update item 's (basic) search index record
  2005. FLEXIUtilities::call_FC_Field_Func($fieldname, 'onIndexSearch', array( &$field, &$postdata[$field->name], &$item ));
  2006. if ( strlen(@$field->search[$item->id]) ) $searchindex[] = $field->search[$item->id];
  2007. //echo $field->name .":".@$field->search[$item->id]."<br/>";
  2008. }
  2009. }
  2010. }
  2011. // Remove item's old advanced search index entries
  2012. $query = "DELETE FROM #__flexicontent_advsearch_index WHERE item_id=". $item->id;
  2013. $this->_db->setQuery($query);
  2014. $this->_db->query();
  2015. // Store item's advanced search index entries
  2016. if ( !empty($ai_query_vals) ) {
  2017. $query = "INSERT INTO #__flexicontent_advsearch_index "
  2018. ." (field_id,item_id,extraid,search_index,value_id) VALUES "
  2019. .implode(",", $ai_query_vals);
  2020. $this->_db->setQuery($query);
  2021. $this->_db->query();
  2022. if ($this->_db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($this->_db->getErrorMsg()),'error');
  2023. }
  2024. // Assigned created basic search index into item object
  2025. $item->search_index = implode(' | ', $searchindex);
  2026. // Check if vstate was set to 1 (no approve new version) while versioning is disabled
  2027. if (!$use_versioning && $data['vstate']!=2) {
  2028. $data['vstate'] = 2;
  2029. $app->enqueueMessage('vstate cannot be set to 1 (=no approve new version) when versioning is disabled', 'notice' );
  2030. }
  2031. if ( $print_logging_info ) @$fc_run_times['fields_value_indexing'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2032. if ( $print_logging_info ) $start_microtime = microtime(true);
  2033. // **************************************************************************
  2034. // IF new version is approved, remove old version values from the field table
  2035. // **************************************************************************
  2036. if($data['vstate']==2)
  2037. {
  2038. //echo "delete __flexicontent_fields_item_relations, item_id: " .$item->id;
  2039. $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id = '.$item->id;
  2040. $this->_db->setQuery($query);
  2041. $this->_db->query();
  2042. $query = 'DELETE FROM #__flexicontent_items_versions WHERE item_id='.$item->id.' AND version='.((int)$last_version+1);
  2043. $this->_db->setQuery($query);
  2044. $this->_db->query();
  2045. $untranslatable_fields = array();
  2046. if ($fields) foreach($fields as $field)
  2047. {
  2048. if( $field->iscore ) continue;
  2049. if (count($assoc_item_ids) && $field->untranslatable)
  2050. {
  2051. // Delete field values in all translating items, if current field is untranslatable and current item version is approved
  2052. // NOTE: item itself is not include in associated translations, no need to check for it and skip itit
  2053. if (! $mval_query) {
  2054. $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id IN ('.implode(',',$assoc_item_ids).') AND field_id='.$field->id;
  2055. $this->_db->setQuery($query);
  2056. $this->_db->query();
  2057. } else {
  2058. $untranslatable_fields[] = $field->id;
  2059. }
  2060. }
  2061. }
  2062. if ( count($untranslatable_fields) ) {
  2063. $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id IN ('.implode(',',$assoc_item_ids).') AND field_id IN ('.implode(',',$untranslatable_fields) .')';
  2064. $this->_db->setQuery($query);
  2065. $this->_db->query();
  2066. }
  2067. }
  2068. // *******************************************
  2069. // Loop through Fields saving the field values
  2070. // *******************************************
  2071. if ($fields)
  2072. {
  2073. // Do not save if versioning disabled or item has no type (version 0)
  2074. $record_versioned_data = $use_versioning && $item->version;
  2075. $ver_query_vals = array();
  2076. $rel_query_vals = array();
  2077. foreach($fields as $field)
  2078. {
  2079. // -- Add the new values to the database
  2080. $postvalues = $this->formatToArray( $postdata[$field->name] );
  2081. $i = 1;
  2082. foreach ($postvalues as $postvalue)
  2083. {
  2084. // Create field obj for DB insertion
  2085. $obj = new stdClass();
  2086. $obj->field_id = $field->id;
  2087. $obj->item_id = $item->id;
  2088. $obj->valueorder = $i;
  2089. $obj->version = (int)$last_version+1;
  2090. // Serialize the properties of the value, normally this is redudant, since the field must have had serialized the parameters of each value already
  2091. $obj->value = is_array($postvalue) ? serialize($postvalue) : $postvalue;
  2092. // -- a. Add versioning values, but do not version the 'hits' or 'state' or 'voting' fields
  2093. if ($record_versioned_data && $field->field_type!='hits' && $field->field_type!='state' && $field->field_type!='voting') {
  2094. // Insert only if value non-empty
  2095. if ( isset($obj->value) && JString::strlen(trim($obj->value)) )
  2096. {
  2097. if (! $mval_query) $this->_db->insertObject('#__flexicontent_items_versions', $obj);
  2098. else $ver_query_vals[] = "(".$obj->field_id. "," .$obj->item_id. "," .$obj->valueorder. "," .$obj->version."," .$this->_db->Quote($obj->value).")";
  2099. }
  2100. }
  2101. //echo $field->field_type." - ".$field->name." - ".JString::strlen(trim($obj->value))." ".$field->iscore."<br/>";
  2102. // -- b. If item is new OR version is approved, AND field is not core (aka stored in the content table or in special table), then add field value to field values table
  2103. if( ( $isnew || $data['vstate']==2 ) && !$field->iscore )
  2104. {
  2105. // UNSET version it it used only verion data table, and insert only if value non-empty
  2106. unset($obj->version);
  2107. if ( isset($obj->value) && JString::strlen(trim($obj->value)) )
  2108. {
  2109. if (! $mval_query) $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
  2110. else $rel_query_vals[] = "(".$obj->field_id. "," .$obj->item_id. "," .$obj->valueorder. "," .$this->_db->Quote($obj->value).")";
  2111. // Save field value in all translating items, if current field is untranslatable
  2112. // NOTE: item itself is not include in associated translations, no need to check for it and skip it
  2113. if (count($assoc_item_ids) && $field->untranslatable) {
  2114. foreach($assoc_item_ids as $t_item_id) {
  2115. $obj->item_id = $t_item_id;
  2116. if (! $mval_query) $this->_db->insertObject('#__flexicontent_fields_item_relations', $obj);
  2117. else $rel_query_vals[] = "(".$obj->field_id. "," .$obj->item_id. "," .$obj->valueorder. "," .$this->_db->Quote($obj->value).")";
  2118. }
  2119. }
  2120. }
  2121. }
  2122. $i++;
  2123. }
  2124. }
  2125. // *********************************************
  2126. // Insert values in item fields versioning table
  2127. // *********************************************
  2128. if ( count($ver_query_vals) ) {
  2129. $query = "INSERT INTO #__flexicontent_items_versions "
  2130. ." (field_id,item_id,valueorder,version,value) VALUES "
  2131. ."\n".implode(",\n", $ver_query_vals);
  2132. $this->_db->setQuery($query);
  2133. $this->_db->query();
  2134. if ($this->_db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($this->_db->getErrorMsg()),'error');
  2135. }
  2136. // *******************************************
  2137. // Insert values in item fields relation table
  2138. // *******************************************
  2139. if ( count($rel_query_vals) ) {
  2140. $query = "INSERT INTO #__flexicontent_fields_item_relations "
  2141. ." (field_id,item_id,valueorder,value) VALUES "
  2142. ."\n".implode(",\n", $rel_query_vals);
  2143. $this->_db->setQuery($query);
  2144. $this->_db->query();
  2145. if ($this->_db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($this->_db->getErrorMsg()),'error');
  2146. }
  2147. // **************************************************************
  2148. // Save other versioned item data into the field versioning table
  2149. // **************************************************************
  2150. // a. Save a version of item properties that do not have a corresponding CORE Field
  2151. if ( $record_versioned_data ) {
  2152. $obj = new stdClass();
  2153. $obj->field_id = -2; // ID of Fake Field used to contain item properties not having a corresponding CORE field
  2154. $obj->item_id = $item->id;
  2155. $obj->valueorder = 1;
  2156. $obj->version = (int)$last_version+1;
  2157. $item_data = array();
  2158. $iproperties = array('alias', 'catid', 'metadesc', 'metakey', 'metadata', 'attribs');
  2159. if (FLEXI_J16GE) {
  2160. $j16ge_iproperties = array('urls', 'images');
  2161. $iproperties = array_merge($iproperties, $j16ge_iproperties);
  2162. }
  2163. foreach ( $iproperties as $iproperty) $item_data[$iproperty] = $item->{$iproperty};
  2164. $obj->value = serialize( $item_data );
  2165. $this->_db->insertObject('#__flexicontent_items_versions', $obj);
  2166. }
  2167. // b. Finally save a version of the posted JoomFish translated data for J1.5, if such data are editted inside the item edit form
  2168. if ( FLEXI_FISH && !empty($data['jfdata']) && $record_versioned_data )
  2169. {
  2170. $obj = new stdClass();
  2171. $obj->field_id = -1; // ID of Fake Field used to contain the Joomfish translated item data
  2172. $obj->item_id = $item->id;
  2173. $obj->valueorder = 1;
  2174. $obj->version = (int)$last_version+1;
  2175. $item_lang = substr($item->language ,0,2);
  2176. $data['jfdata'][$item_lang]['alias'] = $item->alias;
  2177. $data['jfdata'][$item_lang]['metadesc'] = $item->metadesc;
  2178. $data['jfdata'][$item_lang]['metakey'] = $item->metakey;
  2179. $obj->value = serialize($data['jfdata']);
  2180. $this->_db->insertObject('#__flexicontent_items_versions', $obj);
  2181. }
  2182. }
  2183. if ( $print_logging_info ) @$fc_run_times['fields_value_saving'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2184. // ******************************
  2185. // Trigger onAfterSaveField Event
  2186. // ******************************
  2187. if ( $fields )
  2188. {
  2189. if ( $print_logging_info ) $start_microtime = microtime(true);
  2190. foreach($fields as $field)
  2191. {
  2192. $fieldname = $field->iscore ? 'core' : $field->field_type;
  2193. $result = FLEXIUtilities::call_FC_Field_Func($fieldname, 'onAfterSaveField', array( &$field, &$postdata[$field->name], &$files[$field->name], &$item ));
  2194. // *** $result is ignored
  2195. }
  2196. if ( $print_logging_info ) @$fc_run_times['onAfterSaveField_event'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2197. }
  2198. return true;
  2199. }
  2200. /**
  2201. * Method to apply a NEW CURRENT version when saving an APPROVED item version
  2202. *
  2203. * @access public
  2204. * @return boolean True on success
  2205. * @since 1.0
  2206. */
  2207. function applyCurrentVersion(&$item, &$data, $createonly=false)
  2208. {
  2209. $app = JFactory::getApplication();
  2210. $user = JFactory::getUser();
  2211. $cparams = $this->_cparams;
  2212. $editjf_translations = $cparams->get('editjf_translations', 0);
  2213. // ******************************
  2214. // Check and store item in the db
  2215. // ******************************
  2216. // Make sure the data is valid
  2217. if (!$item->check()) {
  2218. $this->setError($item->getError());
  2219. return false;
  2220. }
  2221. if (!$item->store()) {
  2222. $this->setError($this->_db->getErrorMsg());
  2223. return false;
  2224. }
  2225. $this->_id = $item->id;
  2226. $this->_item = & $item;
  2227. // ***********************
  2228. // Save access information
  2229. // ***********************
  2230. if (FLEXI_ACCESS) {
  2231. $rights = FAccess::checkAllItemAccess('com_content', 'users', $user->gmid, $item->id, $item->catid);
  2232. $canRight = (in_array('right', $rights) || $user->gid > 24);
  2233. if ($canRight) FAccess::saveaccess( $item, 'item' );
  2234. } else if (FLEXI_J16GE) {
  2235. // Rules for J1.6+ are handled in the JTABLE class of the item with overriden JTable functions: bind() and store()
  2236. }
  2237. // ***************************
  2238. // If creating only return ...
  2239. // ***************************
  2240. if ($createonly) return true;
  2241. // ****************************
  2242. // Save joomfish data in the db
  2243. // ****************************
  2244. if ( (FLEXI_FISH /*|| FLEXI_J16GE*/) && $editjf_translations )
  2245. $this->_saveJFdata( $data['jfdata'], $item );
  2246. // ***********************************************
  2247. // Delete old tag relations and Store the new ones
  2248. // ***********************************************
  2249. $tags = $data['tags'];
  2250. $query = 'DELETE FROM #__flexicontent_tags_item_relations WHERE itemid = '.$item->id;
  2251. $this->_db->setQuery($query);
  2252. $this->_db->query();
  2253. foreach($tags as $tag)
  2254. {
  2255. $query = 'INSERT INTO #__flexicontent_tags_item_relations (`tid`, `itemid`) VALUES(' . $tag . ',' . $item->id . ')';
  2256. $this->_db->setQuery($query);
  2257. $this->_db->query();
  2258. }
  2259. // ***********************************************************************************************************
  2260. // Delete only category relations which are not part of the categories array anymore to avoid loosing ordering
  2261. // ***********************************************************************************************************
  2262. $cats = $data['categories'];
  2263. $query = 'DELETE FROM #__flexicontent_cats_item_relations'
  2264. . ' WHERE itemid = '.$item->id
  2265. . ($cats ? ' AND catid NOT IN (' . implode(', ', $cats) . ')' : '')
  2266. ;
  2267. $this->_db->setQuery($query);
  2268. $this->_db->query();
  2269. // Get an array of the item's used categories (already assigned in DB)
  2270. $query = 'SELECT catid'
  2271. . ' FROM #__flexicontent_cats_item_relations'
  2272. . ' WHERE itemid = '.$item->id
  2273. ;
  2274. $this->_db->setQuery($query);
  2275. $used = FLEXI_J16GE ? $this->_db->loadColumn() : $this->_db->loadResultArray();
  2276. foreach($cats as $cat) {
  2277. // insert only the new records
  2278. if (!in_array($cat, $used)) {
  2279. $query = 'INSERT INTO #__flexicontent_cats_item_relations (`catid`, `itemid`)'
  2280. .' VALUES(' . $cat . ',' . $item->id . ')'
  2281. ;
  2282. $this->_db->setQuery($query);
  2283. $this->_db->query();
  2284. }
  2285. }
  2286. return true;
  2287. }
  2288. /**
  2289. * Method to save Joomfish item translation data
  2290. *
  2291. * @access public
  2292. * @return boolean True on success
  2293. * @since 1.0
  2294. */
  2295. function _saveJFdata( & $jfdata_arr, & $item)
  2296. {
  2297. //$user_currlang = flexicontent_html::getUserCurrentLang(); // user's -current- language
  2298. //$default_sitelang = substr(flexicontent_html::getSiteDefaultLang(),0,2); // site (frontend) -content- language
  2299. //$item_lang = substr($item->language ,0,2); // item language
  2300. $nn_content_tbl = FLEXI_J16GE ? 'falang_content' : 'jf_content';
  2301. $db = $this->_db;
  2302. $app = JFactory::getApplication();
  2303. $dbprefix = $app->getCfg('dbprefix');
  2304. $dbtype = $app->getCfg('dbtype');
  2305. if ( in_array($dbtype, array('mysqli','mysql')) )
  2306. {
  2307. $query = "UPDATE #__content SET title=".$db->Quote($item->title).", alias=".$db->Quote($item->alias).", introtext=".$db->Quote($item->introtext)
  2308. .", `fulltext`=".$db->Quote($item->fulltext).", images=".$db->Quote($item->images).", metadesc=".$db->Quote($item->metadesc).", metakey=".$db->Quote($item->metakey)
  2309. .", publish_up=".$db->Quote($item->publish_up).", publish_down=".$db->Quote($item->publish_down).", attribs=".$db->Quote($item->attribs)." WHERE id=".$db->Quote($item->id);
  2310. //echo $query."<br/>\n";
  2311. if (FLEXI_J16GE) {
  2312. //$query = $db->replacePrefix($query);
  2313. $query = str_replace("#__", $dbprefix, $query);
  2314. $db_connection = $db->getConnection();
  2315. } else {
  2316. $query = str_replace("#__", $dbprefix, $query);
  2317. $db_connection = & $db->_resource;
  2318. }
  2319. //echo "<pre>"; print_r($query); echo "\n\n";
  2320. if ($dbtype == 'mysqli') {
  2321. $result = mysqli_query( $db_connection , $query );
  2322. if ($result===false) {echo mysqli_error($db_connection); return JError::raiseWarning( 500, "error _saveJFdata():: ".mysqli_error($db_connection));}
  2323. } else if ($dbtype == 'mysql') {
  2324. $result = mysql_query( $query, $db_connection );
  2325. if ($result===false) return JError::raiseWarning( 500, "error _saveJFdata():: ".mysql_error($db_connection));
  2326. } else {
  2327. $msg = 'unreachable code in _saveJFdata(): direct db query, unsupported DB TYPE';
  2328. if (FLEXI_J16GE) throw new Exception($msg, 500); else JError::raiseError(500, $msg);
  2329. }
  2330. }
  2331. $modified = $item->modified ? $item->modified : $item->created;
  2332. $modified_by = $item->modified_by ? $item->modified_by : $item->created_by;
  2333. $langs = FLEXIUtilities::getLanguages('shortcode'); // Get Joomfish active languages
  2334. foreach($jfdata_arr as $shortcode => $jfdata)
  2335. {
  2336. //echo $shortcode." : "; print_r($jfdata);
  2337. // Reconstruct (main)text field if it has splitted up e.g. to seperate editors per tab
  2338. if (@$jfdata['text'] && is_array($jfdata['text'])) {
  2339. $jfdata['text'][0] .= (preg_match('#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i', $jfdata['text'][0]) == 0) ? ("\n".'<hr id="system-readmore" />') : "" ;
  2340. $tabs_text = '';
  2341. foreach($jfdata['text'] as $tab_text) {
  2342. $tabs_text .= $tab_text;
  2343. }
  2344. $jfdata['text'] = & $tabs_text;
  2345. } else if ( empty($jfdata['text']) ) {
  2346. $jfdata['text'] = '';
  2347. }
  2348. // Search for the {readmore} tag and split the text up accordingly.
  2349. $pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i';
  2350. $tagPos = preg_match($pattern, $jfdata['text']);
  2351. if ($tagPos == 0) {
  2352. $jfdata['introtext'] = $jfdata['text'];
  2353. $jfdata['fulltext'] = '';
  2354. } else {
  2355. list($jfdata['introtext'], $jfdata['fulltext']) = preg_split($pattern, $jfdata['text'], 2);
  2356. $jfdata['fulltext'] = JString::strlen( trim($jfdata['fulltext']) ) ? $jfdata['fulltext'] : '';
  2357. }
  2358. // Delete existing Joom!Fish translation data for the current item
  2359. $query = "DELETE FROM #__".$nn_content_tbl." WHERE language_id={$langs->$shortcode->id} AND reference_table='content' AND reference_id={$item->id}";
  2360. $db->setQuery($query);
  2361. $db->query();
  2362. // Apply new translation data
  2363. $translated_fields = array('title','alias','introtext','fulltext','metadesc','metakey');
  2364. foreach ($translated_fields as $fieldname) {
  2365. if ( !strlen( @$jfdata[$fieldname] ) ) continue;
  2366. //if ( !JString::strlen(trim(str_replace("&nbsp;", "", strip_tags(@$jfdata[$fieldname])))) ) continue; // skip empty content
  2367. //echo "<br/><b>#__".$nn_content_tbl."($fieldname) :</b><br/>";
  2368. $query = "INSERT INTO #__".$nn_content_tbl." (language_id, reference_id, reference_table, reference_field, value, original_value, original_text, modified, modified_by, published) ".
  2369. "VALUES ( {$langs->$shortcode->id}, {$item->id}, 'content', '$fieldname', ".$db->Quote(@$jfdata[$fieldname]).", '".md5($item->{$fieldname})."', ".$db->Quote($item->{$fieldname}).", '$modified', '$modified_by', 1)";
  2370. //echo $query."<br/>\n";
  2371. $db->setQuery($query);
  2372. $db->query();
  2373. }
  2374. }
  2375. return true;
  2376. }
  2377. /**
  2378. * Method to fetch used tags IDs as an array when performing an edit action
  2379. *
  2380. * @param int id
  2381. * @return array
  2382. * @since 1.0
  2383. */
  2384. function getUsedtagsIds($item_id=0)
  2385. {
  2386. // Allow retrieval of tags of any item
  2387. $item_id = $item_id ? $item_id : $this->_id;
  2388. // *** NOTE: this->_item->tags may already contain a VERSIONED array of values !!!
  2389. if( $this->_id == $item_id && !empty($this->_item->tags) ) {
  2390. // Return existing tags of current item
  2391. return $this->_item->tags;
  2392. }
  2393. else if ($item_id) {
  2394. // Not current item, or current item's tags are not set
  2395. $query = "SELECT tid FROM #__flexicontent_tags_item_relations WHERE itemid ='".$item_id."'";
  2396. $this->_db->setQuery($query);
  2397. $tags = FLEXI_J16GE ? $this->_db->loadColumn() : $this->_db->loadResultArray();
  2398. if ($this->_id == $item_id) {
  2399. // Retrieved tags of current item, set them
  2400. $this->_item->tags = $tags;
  2401. }
  2402. return $tags;
  2403. } else {
  2404. return array();
  2405. }
  2406. }
  2407. /**
  2408. * Method to get the list of the used tags
  2409. *
  2410. * @param array
  2411. * @return array
  2412. * @since 1.5.2
  2413. */
  2414. function getUsedtagsData($tagIds)
  2415. {
  2416. if (empty($tagIds)) {
  2417. return array();
  2418. }
  2419. $query = 'SELECT *,t.id as tid FROM #__flexicontent_tags as t '
  2420. . ' WHERE t.id IN (\'' . implode("','", $tagIds).'\')'
  2421. . ' ORDER BY name ASC'
  2422. ;
  2423. $this->_db->setQuery($query);
  2424. $used = $this->_db->loadObjectList();
  2425. return $used;
  2426. }
  2427. /**
  2428. * Method to get a list of all available tags Data
  2429. *
  2430. * @param array
  2431. * @return array
  2432. * @since 1.5.2
  2433. */
  2434. function getAlltags() {
  2435. $query = 'SELECT * FROM #__flexicontent_tags ORDER BY name';
  2436. $this->_db->setQuery($query);
  2437. $tags = $this->_db->loadObjectlist();
  2438. return $tags;
  2439. }
  2440. /**
  2441. * Method to restore an old version
  2442. *
  2443. * @param int id
  2444. * @param int version
  2445. * @return int
  2446. * @since 1.5
  2447. */
  2448. // !!!!!!!!!!!!!!!!! INCOMPLETE !!!!!!!!!!!!!!!!!!!!!
  2449. function restore($version, $id)
  2450. {
  2451. // delete current field values
  2452. $query = 'DELETE FROM #__flexicontent_fields_item_relations WHERE item_id = '.(int)$id;
  2453. $this->_db->setQuery($query);
  2454. $this->_db->query();
  2455. // load field values from the version to restore
  2456. $query = 'SELECT item_id, field_id, value, valueorder, iscore'
  2457. . ' FROM #__flexicontent_items_versions as iv'
  2458. . ' LEFT JOIN #__flexicontent_fields as f ON iv.field_id=f.id'
  2459. . ' WHERE item_id = '. (int)$id
  2460. . ' AND version = '. (int)$version
  2461. ;
  2462. $this->_db->setQuery($query);
  2463. $versionrecords = $this->_db->loadObjectList();
  2464. // restore the old values
  2465. foreach ($versionrecords as $versionrecord) {
  2466. if(!(int)$versionrecord->iscore)
  2467. $this->_db->insertObject('#__flexicontent_fields_item_relations', $versionrecord);
  2468. }
  2469. $query = "UPDATE #__content SET version='$version' WHERE id='$id';";
  2470. $this->_db->setQuery($query);
  2471. $this->_db->query($query);
  2472. // handle the maintext not very elegant but functions properly
  2473. $row = $this->getTable('flexicontent_items', '');
  2474. $row->load($id);
  2475. if (@$versionrecords[0]->value) {
  2476. // Search for the {readmore} tag and split the text up accordingly.
  2477. $text = $versionrecords[0]->value;
  2478. $pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i';
  2479. $tagPos = preg_match($pattern, $text);
  2480. if ($tagPos == 0) {
  2481. $row->introtext = $text;
  2482. $row->fulltext = '';
  2483. } else {
  2484. list($row->introtext, $row->fulltext) = preg_split($pattern, $text, 2);
  2485. $row->fulltext = JString::strlen( trim($row->fulltext) ) ? $row->fulltext : '';
  2486. }
  2487. }
  2488. //$row->store();
  2489. }
  2490. /**
  2491. * Method to fetch tags according to a given mask
  2492. *
  2493. * @return object
  2494. * @since 1.0
  2495. */
  2496. function gettags($mask="")
  2497. {
  2498. $escaped_mask = FLEXI_J16GE ? $this->_db->escape( $mask, true ) : $this->_db->getEscaped( $mask, true );
  2499. $where = ($mask!="")?" name like ".$this->_db->Quote( '%'.$escaped_mask.'%', false )." AND":"";
  2500. $query = 'SELECT * FROM #__flexicontent_tags WHERE '.$where.' published = 1 ORDER BY name';
  2501. $this->_db->setQuery($query);
  2502. $tags = $this->_db->loadObjectlist();
  2503. return $tags;
  2504. }
  2505. /**
  2506. * Method to reset hits
  2507. *
  2508. * @param int id
  2509. * @return int
  2510. * @since 1.0
  2511. */
  2512. function resetHits($id)
  2513. {
  2514. $row = $this->getTable('flexicontent_items', '');
  2515. $row->load($id);
  2516. $row->hits = 0;
  2517. $row->store();
  2518. return $row->id;
  2519. }
  2520. /**
  2521. * Method to reset votes
  2522. *
  2523. * @param int id
  2524. * @return int
  2525. * @since 1.0
  2526. */
  2527. function resetVotes($id)
  2528. {
  2529. // Delete main vote type
  2530. $query = 'DELETE FROM #__content_rating WHERE content_id = '.$id;
  2531. $this->_db->setQuery($query);
  2532. $this->_db->query();
  2533. // Delete extra vote types
  2534. $query = 'DELETE FROM #__flexicontent_items_extravote WHERE content_id = '.$id;
  2535. $this->_db->setQuery($query);
  2536. $this->_db->query();
  2537. }
  2538. /**
  2539. * Method to get votes
  2540. *
  2541. * @param int id
  2542. * @return object
  2543. * @since 1.0
  2544. */
  2545. function getvotes($id)
  2546. {
  2547. $query = 'SELECT rating_sum, rating_count FROM #__content_rating WHERE content_id = '.(int)$id;
  2548. $this->_db->setQuery($query);
  2549. $votes = $this->_db->loadObjectlist();
  2550. return $votes;
  2551. }
  2552. /**
  2553. * Method to get hits
  2554. *
  2555. * @param int id
  2556. * @return int
  2557. * @since 1.0
  2558. */
  2559. function gethits($id)
  2560. {
  2561. $query = 'SELECT hits FROM #__content WHERE id = '.(int)$id;
  2562. $this->_db->setQuery($query);
  2563. $hits = $this->_db->loadResult();
  2564. return $hits;
  2565. }
  2566. /**
  2567. * Method to get subscriber count
  2568. *
  2569. * @TODO add the notification as an option with a checkbox in the favourites screen
  2570. * @return object
  2571. * @since 1.5
  2572. */
  2573. function getSubscribersCount()
  2574. {
  2575. static $subscribers = array();
  2576. if ( isset($subscribers[$this->_id]) ) return $subscribers[$this->_id];
  2577. $query = 'SELECT COUNT(*)'
  2578. .' FROM #__flexicontent_favourites AS f'
  2579. .' LEFT JOIN #__users AS u'
  2580. .' ON u.id = f.userid'
  2581. .' WHERE f.itemid = ' . (int)$this->_id
  2582. .' AND u.block=0 ' //.' AND f.notify = 1'
  2583. ;
  2584. $this->_db->setQuery($query);
  2585. $subscribers[$this->_id] = $this->_db->loadResult();
  2586. return $subscribers[$this->_id];
  2587. }
  2588. /**
  2589. * Decide item type id for existing or new item ... verifying that the type exists ...
  2590. * NOTE: for new items the value of 'typeid' variable out of the JRequest array is used
  2591. *
  2592. * @return array
  2593. * @since 1.5
  2594. */
  2595. function getTypesselected($force = false)
  2596. {
  2597. static $used = null;
  2598. if (!$used || $force) {
  2599. if ($this->_id) {
  2600. $query = 'SELECT ie.type_id as id,t.name FROM #__flexicontent_items_ext as ie'
  2601. . ' JOIN #__flexicontent_types as t ON ie.type_id=t.id'
  2602. . ' WHERE ie.item_id = ' . (int)$this->_id;
  2603. $this->_db->setQuery($query);
  2604. $used = $this->_db->loadObject();
  2605. } else {
  2606. $typeid = (int)JRequest::getInt('typeid', 0);
  2607. $query = 'SELECT t.id,t.name FROM #__flexicontent_types as t'
  2608. . ' WHERE t.id = ' . (int)$typeid;
  2609. $this->_db->setQuery($query);
  2610. $used = $this->_db->loadObject();
  2611. }
  2612. if (!$used) {
  2613. $used = new stdClass();
  2614. $used->id = 0;
  2615. $used->name = null;
  2616. }
  2617. }
  2618. return $used;
  2619. }
  2620. /**
  2621. * Method to get used categories when performing an edit action
  2622. *
  2623. * @return array
  2624. * @since 1.0
  2625. */
  2626. function getCatsselected($item_id=0)
  2627. {
  2628. // Allow retrieval of categories of any item
  2629. $item_id = $item_id ? $item_id : $this->_id;
  2630. // *** NOTE: this->_item->categories may already contain a VERSIONED array of values !!!
  2631. if( $this->_id == $item_id && !empty($this->_item->categories) ) {
  2632. // Return existing categories of current item
  2633. return $this->_item->categories;
  2634. }
  2635. else if ($item_id) {
  2636. // Not current item, or current item's categories are not set
  2637. $query = "SELECT tid FROM #__flexicontent_cats_item_relations WHERE itemid ='".$item_id."'";
  2638. $this->_db->setQuery($query);
  2639. $categories = FLEXI_J16GE ? $this->_db->loadColumn() : $this->_db->loadResultArray();
  2640. if ($this->_id == $item_id) {
  2641. // Retrieved categories of current item, set them
  2642. $this->_item->categories = & $categories;
  2643. // 'cats' is alias of categories
  2644. $this->_item->cats = & $this->_item->categories; // possibly used by CORE plugin for displaying in frontend
  2645. }
  2646. return $categories;
  2647. } else {
  2648. return array();
  2649. }
  2650. }
  2651. /**
  2652. * Method to get the type parameters of an item
  2653. *
  2654. * @return string
  2655. * @since 1.5
  2656. */
  2657. function getTypeparams ()
  2658. {
  2659. $query = 'SELECT t.attribs'
  2660. . ' FROM #__flexicontent_types AS t';
  2661. if ( !$this->_id ) {
  2662. $type_id = JRequest::getInt('typeid', 0);
  2663. $query .= ' WHERE t.id = ' . (int)$type_id;
  2664. } else {
  2665. $query .= ' JOIN #__flexicontent_items_ext AS ie ON ie.type_id = t.id'
  2666. . ' WHERE ie.item_id = ' . (int)$this->_id
  2667. ;
  2668. }
  2669. $this->_db->setQuery($query);
  2670. $tparams = $this->_db->loadResult();
  2671. return $tparams ? $tparams : '';
  2672. }
  2673. /**
  2674. * Method to get types list when performing an edit action or e.g. checking 'create' ACCESS for the types
  2675. *
  2676. * @return array
  2677. * @since 1.5
  2678. */
  2679. function getTypeslist ( $type_ids=false, $check_perms = false )
  2680. {
  2681. if ( !empty($type_ids) && is_array($type_ids) ) {
  2682. foreach ($type_ids as $i => $type_id)
  2683. $type_ids[$i] = (int) $type_id;
  2684. $type_ids_list = implode(',', $type_ids);
  2685. }
  2686. $query = 'SELECT * '
  2687. . ' FROM #__flexicontent_types'
  2688. . ' WHERE published = 1 '. ( @ $type_ids_list ? ' AND id IN ('. $type_ids_list .' ) ' : '' )
  2689. . ' ORDER BY name ASC'
  2690. ;
  2691. $this->_db->setQuery($query);
  2692. $types = $this->_db->loadObjectList('id');
  2693. if ($check_perms)
  2694. {
  2695. $user = JFactory::getUser();
  2696. $_types = array();
  2697. foreach ($types as $type) {
  2698. if (FLEXI_J16GE)
  2699. $allowed = ! $type->itemscreatable || $user->authorise('core.create', 'com_flexicontent.type.' . $type->id);
  2700. else if (FLEXI_ACCESS && $user->gid < 25)
  2701. $allowed = ! $type->itemscreatable || FAccess::checkAllContentAccess('com_content','submit','users', $user->gmid, 'type', $type->id);
  2702. else
  2703. $allowed = 1;
  2704. if ( $allowed ) $_types[] = $type;
  2705. }
  2706. $types = $_types;
  2707. }
  2708. return $types;
  2709. }
  2710. /**
  2711. * Method to retrieve the value of a core field for a specified item version
  2712. *
  2713. * @return array
  2714. * @since 1.5
  2715. */
  2716. function getCoreFieldValue(&$field, $version = 0)
  2717. {
  2718. if(isset($this->_item)) {
  2719. $item = & $this->_item;
  2720. } else {
  2721. $item = $this->getItem(); // This fuction calls the load item function for existing item and init item function in the case of new item
  2722. }
  2723. switch ($field->field_type) {
  2724. case 'created': // created
  2725. $field_value = array($item->created);
  2726. break;
  2727. case 'createdby': // created by
  2728. $field_value = array($item->created_by);
  2729. break;
  2730. case 'modified': // modified
  2731. $field_value = array($item->modified);
  2732. break;
  2733. case 'modifiedby': // modified by
  2734. $field_value = array($item->modified_by);
  2735. break;
  2736. case 'title': // title
  2737. $field_value = array($item->title);
  2738. break;
  2739. case 'hits': // hits
  2740. $field_value = array($item->hits);
  2741. break;
  2742. case 'type': // document type
  2743. $field_value = array($item->type_id);
  2744. break;
  2745. case 'version': // version
  2746. $field_value = array($item->version);
  2747. break;
  2748. case 'state': // publication state
  2749. $field_value = array($item->state);
  2750. break;
  2751. case 'voting': // voting button // remove dummy value in next version for legacy purposes
  2752. $field_value = array('button'); // dummy value to force display
  2753. break;
  2754. case 'favourites': // favourites button // remove dummy value in next version for legacy purposes
  2755. $field_value = array('button'); // dummy value to force display
  2756. break;
  2757. case 'score': // voting score // remove dummy value in next version for legacy purposes
  2758. $field_value = array('button'); // dummy value to force display
  2759. break;
  2760. case 'categories': // assigned categories
  2761. $field_value = isset($item->categories) ? $item->categories : array();
  2762. break;
  2763. case 'tags': // assigned tags
  2764. $field_value = isset($item->tags) ? $item->tags : array();
  2765. break;
  2766. case 'maintext': // main text
  2767. $value = JString::strlen( trim($item->fulltext) ) ? $item->introtext . "<hr id=\"system-readmore\" />" . $item->fulltext : $item->introtext;
  2768. $field_value = array($value);
  2769. break;
  2770. }
  2771. return $field_value;
  2772. }
  2773. /**
  2774. * Method to get the values of an extrafield
  2775. *
  2776. * @return object
  2777. * @since 1.5
  2778. * @todo move in a specific class and add the parameter $itemid
  2779. */
  2780. function getCustomFieldsValues($item_id=0, $version=0)
  2781. {
  2782. if (!$item_id) $item_id = $this->_id;
  2783. if (!$item_id) return array();
  2784. static $field_values;
  2785. if ( isset($field_values[$item_id][$version] ) )
  2786. return $field_values[$item_id][$version];
  2787. $cparams = $this->_cparams;
  2788. $use_versioning = $cparams->get('use_versioning', 1);
  2789. $query = 'SELECT field_id, value'
  2790. .( ($version<=0 || !$use_versioning) ? ' FROM #__flexicontent_fields_item_relations AS fv' : ' FROM #__flexicontent_items_versions AS fv' )
  2791. .' WHERE fv.item_id = ' . (int)$item_id
  2792. .( ($version>0 && $use_versioning) ? ' AND fv.version='.((int)$version) : '')
  2793. .' ORDER BY field_id, valueorder'
  2794. ;
  2795. $this->_db->setQuery($query);
  2796. $rows = $this->_db->loadObjectList();
  2797. // Add values to cached array
  2798. $field_values[$item_id][$version] = array();
  2799. foreach ($rows as $row) {
  2800. $field_values[$item_id][$version][$row->field_id][] = $row->value;
  2801. }
  2802. return $field_values[$item_id][$version];
  2803. }
  2804. /**
  2805. * Method to get the FIELDs (configuration and values) belonging to the Content Type of:
  2806. * (a) the current item or (b) the one specified in the URL (variable 'typeid')
  2807. *
  2808. * NOTE: Fields are skipped if (a) are not pubished OR (b) no longer belong to the item type
  2809. * NOTE: VERSIONED field values will be retrieved if version is set in the HTTP REQUEST !!!
  2810. *
  2811. * @return object
  2812. * @since 1.5
  2813. */
  2814. function getExtrafields($force = false, $lang_parent_id = 0)
  2815. {
  2816. static $fields;
  2817. if(!$fields || $force) {
  2818. jimport('joomla.html.parameter');
  2819. $use_versioning = $this->_cparams->get('use_versioning', 1);
  2820. $typeid = $this->get('type_id'); // Get item's type_id, loading item if neccessary
  2821. $typeid = $typeid ? $typeid : JRequest::getVar('typeid', 0, '', 'int');
  2822. $type_join = ' JOIN #__flexicontent_fields_type_relations AS ftrel ON ftrel.field_id = fi.id AND ftrel.type_id='.$typeid;
  2823. $version = JRequest::getVar( 'version', 0, 'request', 'int' );
  2824. $query = 'SELECT fi.*'
  2825. .' FROM #__flexicontent_fields AS fi'
  2826. .($typeid ? $type_join : '') // Require field belonging to item type
  2827. .' WHERE fi.published = 1' // Require field published
  2828. .($typeid ? '' : ' AND fi.iscore = 1') // Get CORE fields when typeid not set
  2829. .' ORDER BY '. ($typeid ? 'ftrel.ordering, ' : '') .'fi.ordering, fi.name'
  2830. ;
  2831. $this->_db->setQuery($query);
  2832. $fields = $this->_db->loadObjectList('name');
  2833. if ($this->_db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($this->_db->getErrorMsg()),'error');
  2834. $custom_vals[$this->_id] = $this->getCustomFieldsValues($this->_id, $version);
  2835. if ( $lang_parent_id && !$version) { // Language parent item is used only when loading non-versioned item
  2836. $custom_vals[$lang_parent_id] = $this->getCustomFieldsValues($lang_parent_id, 0);
  2837. }
  2838. foreach ($fields as $field)
  2839. {
  2840. $field->item_id = (int)$this->_id;
  2841. // $version should be ZERO when versioning disabled, or when wanting to load the current version !!!
  2842. if ( (!$version || !$use_versioning) && $field->iscore) {
  2843. // Load CURRENT (non-versioned) core field from properties of item object
  2844. $field->value = $this->getCoreFieldValue($field, $version);
  2845. } else {
  2846. // Load non core field (versioned or non-versioned) OR core field (versioned only)
  2847. // while checking if current field is using untranslatable value from original content item
  2848. $item_id = ($lang_parent_id && @$field->untranslatable && !$version) ? $lang_parent_id : $this->_id;
  2849. $field->value = isset( $custom_vals[$item_id][$field->id] ) ? $custom_vals[$item_id][$field->id] : array();
  2850. if( ( $field->name=='categories') || $field->name=='tags' ) {
  2851. // categories and tags must have been serialized but some early versions did not do it, we will check before unserializing them
  2852. $field->value = ($array = @unserialize($field->value[0]) ) ? $array : $field->value;
  2853. }
  2854. }
  2855. //echo "Got ver($version) id {$field->id}: ". $field->name .": "; print_r($field->value); echo "<br/>";
  2856. $field->parameters = FLEXI_J16GE ? new JRegistry($field->attribs) : new JParameter($field->attribs);
  2857. }
  2858. }
  2859. return $fields;
  2860. }
  2861. /**
  2862. * Method to change the state of an item
  2863. *
  2864. * @access public
  2865. * @return boolean True on success
  2866. * @since 1.0
  2867. */
  2868. function setitemstate($id, $state = 1)
  2869. {
  2870. $app = JFactory::getApplication();
  2871. $user = JFactory::getUser();
  2872. $dispatcher = JDispatcher::getInstance();
  2873. JRequest::setVar("isflexicontent", "yes");
  2874. static $event_failed_notice_added = false;
  2875. if ( $id )
  2876. {
  2877. $v = FLEXIUtilities::getCurrentVersions((int)$id);
  2878. $query = 'UPDATE #__content'
  2879. . ' SET state = ' . (int)$state
  2880. . ' WHERE id = '.(int)$id
  2881. //. ' AND ( checked_out = 0 OR ( checked_out = ' . (int) $user->get('id'). ' ) )'
  2882. ;
  2883. $this->_db->setQuery( $query );
  2884. $this->_db->query();
  2885. if ( $this->_db->getErrorNum() ) if (FLEXI_J16GE) throw new Exception($this->_db->getErrorMsg(), 500); else JError::raiseError(500, $this->_db->getErrorMsg());
  2886. $query = 'UPDATE #__flexicontent_items_tmp'
  2887. . ' SET state = ' . (int)$state
  2888. . ' WHERE id = '.(int)$id
  2889. //. ' AND ( checked_out = 0 OR ( checked_out = ' . (int) $user->get('id'). ' ) )'
  2890. ;
  2891. $this->_db->setQuery( $query );
  2892. $this->_db->query();
  2893. if ( $this->_db->getErrorNum() ) if (FLEXI_J16GE) throw new Exception($this->_db->getErrorMsg(), 500); else JError::raiseError(500, $this->_db->getErrorMsg());
  2894. $query = 'UPDATE #__flexicontent_items_versions'
  2895. . ' SET value = ' . (int)$state
  2896. . ' WHERE item_id = '.(int)$id
  2897. . ' AND valueorder = 1'
  2898. . ' AND field_id = 10'
  2899. . ' AND version = ' .(int)$v['version']
  2900. ;
  2901. $this->_db->setQuery( $query );
  2902. $this->_db->query();
  2903. if ( $this->_db->getErrorNum() ) if (FLEXI_J16GE) throw new Exception($this->_db->getErrorMsg(), 500); else JError::raiseError(500, $this->_db->getErrorMsg());
  2904. }
  2905. // ****************************************************************
  2906. // Trigger Event 'onContentChangeState' of Joomla's Content plugins
  2907. // ****************************************************************
  2908. if (FLEXI_J16GE) {
  2909. // Make sure we import flexicontent AND content plugins since we will be triggering their events
  2910. JPluginHelper::importPlugin('content');
  2911. // PREPARE FOR TRIGGERING content events
  2912. // We need to fake joomla's states ... when triggering events
  2913. $fc_state = $state;
  2914. if ( in_array($fc_state, array(1,-5)) ) $jm_state = 1; // published states
  2915. else if ( in_array($fc_state, array(0,-3,-4)) ) $jm_state = 0; // unpublished states
  2916. else $jm_state = $fc_state; // trashed & archive states
  2917. $fc_itemview = $app->isSite() ? FLEXI_ITEMVIEW : 'item';
  2918. $item = new stdClass();
  2919. // Compatibility steps (including Joomla compatible state),
  2920. // so that 3rd party plugins using the change state event work properly
  2921. JRequest::setVar('view', 'article'); JRequest::setVar('option', 'com_content');
  2922. $item->state = $jm_state;
  2923. $result = $dispatcher->trigger($this->event_change_state, array('com_content.article', (array) $id, $jm_state));
  2924. // Revert compatibilty steps ... the $item->state is not used further regardless if it was changed,
  2925. // besides the event_change_state using plugin should have updated DB state value anyway
  2926. JRequest::setVar('view', $fc_itemview); JRequest::setVar('option', 'com_flexicontent');
  2927. if ($item->state == $jm_state) $item->state = $fc_state; // this check is redundant, item->state is not used further ...
  2928. if (in_array(false, $result, true) && !$event_failed_notice_added) {
  2929. JError::raiseNotice(10, JText::_('One of plugin event handler for onContentChangeState failed') );
  2930. $event_failed_notice_added = true;
  2931. return false;
  2932. }
  2933. }
  2934. return true;
  2935. }
  2936. /**
  2937. * Method to get the versionlist which belongs to the item
  2938. *
  2939. * @return object
  2940. * @since 1.5
  2941. */
  2942. function getVersionList($limitstart=0, $versionsperpage=0)
  2943. {
  2944. $query = 'SELECT v.version_id AS nr, v.created AS date, u.name AS modifier, v.comment AS comment'
  2945. .' FROM #__flexicontent_versions AS v'
  2946. .' LEFT JOIN #__users AS u ON v.created_by = u.id'
  2947. .' WHERE item_id = ' . (int)$this->_id
  2948. .' ORDER BY version_id ASC'
  2949. . ($versionsperpage?' LIMIT '.$limitstart.','.$versionsperpage:'')
  2950. ;
  2951. $this->_db->setQuery($query);
  2952. return $this->_db->loadObjectList();
  2953. }
  2954. /**
  2955. * Method to count the number of versions that the item has
  2956. *
  2957. * @return object
  2958. * @since 1.5
  2959. */
  2960. function getVersionCount()
  2961. {
  2962. $query = 'SELECT count(*) as num'
  2963. .' FROM #__flexicontent_versions AS v'
  2964. .' LEFT JOIN #__users AS u ON v.created_by = u.id'
  2965. .' WHERE item_id = ' . (int)$this->_id
  2966. ;
  2967. $this->_db->setQuery($query);
  2968. return $this->_db->loadResult();
  2969. }
  2970. /**
  2971. * Helper method to format a value as array
  2972. *
  2973. * @return object
  2974. * @since 1.5
  2975. */
  2976. function formatToArray($value)
  2977. {
  2978. $value = $value ? $value : array();
  2979. $value = is_array($value) ? $value : array($value);
  2980. return $value;
  2981. }
  2982. /**
  2983. * Helper method to bind form posted item parameters and and metadata to the item
  2984. *
  2985. * @return object
  2986. * @since 1.5
  2987. */
  2988. function mergeAttributes(&$item, &$params, &$metadata)
  2989. {
  2990. // Build item parameters INI string
  2991. if (is_array($params))
  2992. {
  2993. $item->attribs = FLEXI_J16GE ? new JRegistry($item->attribs) : new JParameter($item->attribs);
  2994. foreach ($params as $k => $v) {
  2995. //$v = is_array($v) ? implode('|', $v) : $v;
  2996. $item->attribs->set($k, $v);
  2997. }
  2998. // Clear any old parameters of all item template layouts, except the currently used one
  2999. $themes = flexicontent_tmpl::getTemplates();
  3000. foreach ($themes->items as $tmpl_name => $tmpl)
  3001. {
  3002. if ( $tmpl_name == @$params['ilayout'] ) continue;
  3003. $tmpl_params = $tmpl->params;
  3004. if (FLEXI_J16GE) {
  3005. $jform = new JForm('com_flexicontent.template.item', array('control' => 'jform', 'load_data' => true));
  3006. $jform->load($tmpl_params);
  3007. foreach ($jform->getGroup('attribs') as $p) {
  3008. if (!empty($p->fieldname))
  3009. $item->attribs->set($p->fieldname, null);
  3010. }
  3011. } else {
  3012. if ( !empty($tmpl_params->_xml['_default']) ) // check if parameters group is empty
  3013. {
  3014. foreach ( $tmpl_params->_xml['_default']->children() as $p ) {
  3015. if (!empty($p->_attributes['name']))
  3016. $item->attribs->set($p->_attributes['name'], null);
  3017. }
  3018. }
  3019. }
  3020. }
  3021. $item->attribs = $item->attribs->toString();
  3022. }
  3023. // Build item metadata INI string
  3024. if (is_array($metadata))
  3025. {
  3026. $item->metadata = FLEXI_J16GE ? new JRegistry($item->metadata) : new JParameter($item->metadata);
  3027. foreach ($metadata as $k => $v) {
  3028. if ( $k == 'description' && !FLEXI_J16GE ) { // is jform field in J1.6+
  3029. $item->metadesc = $v;
  3030. } elseif ( $k == 'keywords' && !FLEXI_J16GE ) { // is jform field in J1.6+
  3031. $item->metakey = $v;
  3032. } else {
  3033. $item->metadata->set($k, $v);
  3034. }
  3035. }
  3036. $item->metadata = $item->metadata->toString();
  3037. }
  3038. }
  3039. /*
  3040. * Method to retrieve the configuration for the Content Submit/Update notifications
  3041. */
  3042. function & getNotificationsConf(&$params)
  3043. {
  3044. static $nConf = null;
  3045. if ($nConf !== null) return $nConf;
  3046. // (a) Check if notifications are not enabled
  3047. if ( !$params->get('enable_notifications', 0) ) {
  3048. $nConf = false;
  3049. return $nConf;
  3050. }
  3051. $db = JFactory::getDBO();
  3052. $nConf = new stdClass();
  3053. // (b) Get Content Type specific notifications (that override global)
  3054. $nConf->userlist_notify_new = FLEXIUtilities::paramToArray( $params->get('userlist_notify_new'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3055. $nConf->usergrps_notify_new = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_new', array()) );
  3056. $nConf->usergrps_notify_new_fa = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_new_fa', array()) );
  3057. $nConf->userlist_notify_new_pending = FLEXIUtilities::paramToArray( $params->get('userlist_notify_new_pending'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3058. $nConf->usergrps_notify_new_pending = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_new_pending', array()) );
  3059. $nConf->usergrps_notify_new_pending_fa = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_new_pending_fa', array()) );
  3060. $nConf->userlist_notify_existing = FLEXIUtilities::paramToArray( $params->get('userlist_notify_existing'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3061. $nConf->usergrps_notify_existing = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_existing', array()) );
  3062. $nConf->usergrps_notify_existing_fa = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_existing_fa', array()) );
  3063. $nConf->userlist_notify_existing_reviewal = FLEXIUtilities::paramToArray( $params->get('userlist_notify_existing_reviewal'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3064. $nConf->usergrps_notify_existing_reviewal = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_existing_reviewal', array()) );
  3065. $nConf->usergrps_notify_existing_reviewal_fa = FLEXIUtilities::paramToArray( $params->get('usergrps_notify_existing_reviewal_fa', array()) );
  3066. // (c) Get category specific notifications
  3067. if ( $params->get('nf_allow_cat_specific') )
  3068. {
  3069. $cats = $this->get('categories');
  3070. $query = "SELECT params FROM #__categories WHERE id IN (".implode(',',$cats).")";
  3071. $db->setQuery( $query );
  3072. $mcats_params = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  3073. foreach ($mcats_params as $cat_params) {
  3074. $cat_params = FLEXI_J16GE ? new JRegistry($cat_params) : new JParameter($cat_params);
  3075. if ( ! $cat_params->get('cats_enable_notifications', 0) ) continue; // Skip this category if category-specific notifications are not enabled for this category
  3076. $cats_userlist_notify_new = FLEXIUtilities::paramToArray( $cat_params->get('cats_userlist_notify_new'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3077. $cats_usergrps_notify_new = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_new', array()) );
  3078. $cats_usergrps_notify_new_fa = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_new_fa', array()) );
  3079. $cats_userlist_notify_new_pending = FLEXIUtilities::paramToArray( $cat_params->get('cats_userlist_notify_new_pending'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3080. $cats_usergrps_notify_new_pending = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_new_pending', array()) );
  3081. $cats_usergrps_notify_new_pending_fa = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_new_pending_fa', array()) );
  3082. $cats_userlist_notify_existing = FLEXIUtilities::paramToArray( $cat_params->get('cats_userlist_notify_existing'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3083. $cats_usergrps_notify_existing = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_existing', array()) );
  3084. $cats_usergrps_notify_existing_fa = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_existing_fa', array()) );
  3085. $cats_userlist_notify_existing_reviewal = FLEXIUtilities::paramToArray( $cat_params->get('cats_userlist_notify_existing_reviewal'), $regex="/[\s]*,[\s]*/", $filterfunc="intval");
  3086. $cats_usergrps_notify_existing_reviewal = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_existing_reviewal', array()) );
  3087. $cats_usergrps_notify_existing_reviewal_fa = FLEXIUtilities::paramToArray( $cat_params->get('cats_usergrps_notify_existing_reviewal_fa', array()) );
  3088. $nConf->userlist_notify_new = array_unique(array_merge($nConf->userlist_notify_new, $cats_userlist_notify_new));
  3089. $nConf->usergrps_notify_new = array_unique(array_merge($nConf->usergrps_notify_new, $cats_usergrps_notify_new));
  3090. $nConf->usergrps_notify_new_fa = array_unique(array_merge($nConf->usergrps_notify_new_fa, $cats_usergrps_notify_new_fa));
  3091. $nConf->userlist_notify_new_pending = array_unique(array_merge($nConf->userlist_notify_new_pending, $cats_userlist_notify_new_pending));
  3092. $nConf->usergrps_notify_new_pending = array_unique(array_merge($nConf->usergrps_notify_new_pending, $cats_usergrps_notify_new_pending));
  3093. $nConf->usergrps_notify_new_pending_fa = array_unique(array_merge($nConf->usergrps_notify_new_pending_fa, $cats_usergrps_notify_new_pending_fa));
  3094. $nConf->userlist_notify_existing = array_unique(array_merge($nConf->userlist_notify_existing, $cats_userlist_notify_existing));
  3095. $nConf->usergrps_notify_existing = array_unique(array_merge($nConf->usergrps_notify_existing, $cats_usergrps_notify_existing));
  3096. $nConf->usergrps_notify_existing_fa = array_unique(array_merge($nConf->usergrps_notify_existing_fa, $cats_usergrps_notify_existing_fa));
  3097. $nConf->userlist_notify_existing_reviewal = array_unique(array_merge($nConf->userlist_notify_existing_reviewal, $cats_userlist_notify_existing_reviewal));
  3098. $nConf->usergrps_notify_existing_reviewal = array_unique(array_merge($nConf->usergrps_notify_existing_reviewal, $cats_usergrps_notify_existing_reviewal));
  3099. $nConf->usergrps_notify_existing_reviewal_fa = array_unique(array_merge($nConf->usergrps_notify_existing_reviewal_fa, $cats_usergrps_notify_existing_reviewal_fa));
  3100. }
  3101. }
  3102. //echo "<pre>"; print_r($nConf); exit;
  3103. // Construct configuation parameter names
  3104. $nConf_emails = new stdClass();
  3105. $notify_types = array('notify_new', 'notify_new_pending', 'notify_existing', 'notify_existing_reviewal');
  3106. foreach ($notify_types as $ntype) {
  3107. $ugrps_fa[$ntype] = 'usergrps_'.$ntype.'_fa';
  3108. $ugrps [$ntype] = 'usergrps_'.$ntype;
  3109. $ulist [$ntype] = 'userlist_'.$ntype;
  3110. }
  3111. // (e) Get emails, but first convert user groups to user ids
  3112. foreach ($notify_types as $ntype)
  3113. {
  3114. $user_emails = array();
  3115. // emails for user ids
  3116. $user_emails_ulist = array();
  3117. $_user_ids = array();
  3118. $_user_names = array();
  3119. foreach ($nConf->{$ulist[$ntype]} as $user_id_name) {
  3120. if ( is_numeric($user_id_name) ) $_user_ids[] = (int) $user_id_name;
  3121. else $_user_names[] = $db->Quote($user_id_name);
  3122. }
  3123. if ( count( $nConf->{$ulist[$ntype]} ) )
  3124. {
  3125. $query = "SELECT DISTINCT email FROM #__users WHERE id IN (".implode(",",$_user_ids).") OR username IN (".implode(",",$_user_names).")";
  3126. $db->setQuery( $query );
  3127. $user_emails_ulist = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  3128. if ( $db->getErrorNum() ) echo $db->getErrorMsg(); // if ($ntype=='notify_new_pending') { echo "<pre>"; print_r($user_emails_ulist); exit; }
  3129. }
  3130. $user_emails_ugrps = array();
  3131. if ( count( $nConf->{$ugrps[$ntype]} ) )
  3132. {
  3133. // emails for user groups
  3134. if (!FLEXI_J16GE) {
  3135. $query = "SELECT DISTINCT email FROM #__users WHERE gid IN (".implode(",",$nConf->{$ugrps[$ntype]}).")";
  3136. } else {
  3137. $query = "SELECT DISTINCT email FROM #__users as u"
  3138. ." JOIN #__user_usergroup_map ugm ON u.id=ugm.user_id AND ugm.group_id IN (".implode(",",$nConf->{$ugrps[$ntype]}).")";
  3139. }
  3140. $db->setQuery( $query );
  3141. $user_emails_ugrps = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  3142. if ( $db->getErrorNum() ) echo $db->getErrorMsg(); // if ($ntype=='notify_new_pending') { print_r($user_emails_ugrps); exit; }
  3143. }
  3144. $user_emails_ugrps_fa = array();
  3145. if ( FLEXI_ACCESS && count( $nConf->{$ugrps_fa[$ntype]} ) )
  3146. {
  3147. $final_groups = array();
  3148. foreach ( $nConf->{$ugrps_fa[$ntype]} as $fagrpid ) {
  3149. $curr_groups = FAccess::mgenfant( $fagrpid );
  3150. $final_groups = array_unique( array_merge ($final_groups,$curr_groups) );
  3151. }
  3152. //print_r($final_groups); exit;
  3153. // emails for flexiaccess user groups
  3154. $query = "SELECT DISTINCT email FROM #__users as u"
  3155. ." JOIN #__flexiaccess_groups ugm ON u.username=ugm.name AND ugm.type=2 AND ugm.id IN (".implode(",",$final_groups).")";
  3156. $db->setQuery( $query );
  3157. $user_emails_ugrps_fa_individual = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  3158. if ( $db->getErrorNum() ) echo $db->getErrorMsg();
  3159. // emails for flexiaccess user groups
  3160. $query = "SELECT DISTINCT email FROM #__users as u"
  3161. ." JOIN #__flexiaccess_members ugm ON u.id=ugm.member_id AND ugm.group_id IN (".implode(",",$final_groups).")";
  3162. $db->setQuery( $query );
  3163. $user_emails_ugrps_fa_collective = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  3164. if ( $db->getErrorNum() ) echo $db->getErrorMsg();
  3165. $user_emails_ugrps_fa = array_unique( array_merge ($user_emails_ugrps_fa_individual, $user_emails_ugrps_fa_collective) );
  3166. // if ($ntype=='notify_new_pending') { print_r($user_emails_ugrps_fa); exit; }
  3167. }
  3168. // merge them
  3169. $user_emails = array_unique( array_merge($user_emails_ulist, $user_emails_ugrps, $user_emails_ugrps_fa) );
  3170. $nConf_emails->{$ntype} = $user_emails;
  3171. }
  3172. $nConf->emails = $nConf_emails;
  3173. //echo "<pre>"; print_r($nConf); exit;
  3174. return $nConf;
  3175. }
  3176. // *****************************************************************************************
  3177. // If there are emails to notify for current saving case, then send the notifications emails
  3178. // *****************************************************************************************
  3179. function sendNotificationEmails(&$notify_vars, &$params, $manual_approval_request=0)
  3180. {
  3181. $needs_version_reviewal = $notify_vars->needs_version_reviewal;
  3182. $needs_publication_approval = $notify_vars->needs_publication_approval;
  3183. $isnew = $notify_vars->isnew;
  3184. $notify_emails = $notify_vars->notify_emails;
  3185. $notify_text = $notify_vars->notify_text;
  3186. $before_cats = $notify_vars->before_cats;
  3187. $after_cats = $notify_vars->after_cats;
  3188. $oitem = $notify_vars->original_item;
  3189. if ( !count($notify_emails) ) return true;
  3190. $app = JFactory::getApplication();
  3191. $db = JFactory::getDBO();
  3192. $user = JFactory::getUser();
  3193. $use_versioning = $this->_cparams->get('use_versioning', 1);
  3194. // Get category titles of categories add / removed from the item
  3195. if ( !$isnew ) {
  3196. $cats_added_ids = array_diff(array_keys($after_cats), array_keys($before_cats));
  3197. foreach($cats_added_ids as $cats_added_id) {
  3198. $cats_added_titles[] = $after_cats[$cats_added_id]->title;
  3199. }
  3200. $cats_removed_ids = array_diff(array_keys($before_cats), array_keys($after_cats));
  3201. foreach($cats_removed_ids as $cats_removed_id) {
  3202. $cats_removed_titles[] = $before_cats[$cats_removed_id]->title;
  3203. }
  3204. $cats_altered = count($cats_added_ids) + count($cats_removed_ids);
  3205. $after_maincat = $this->get('catid');
  3206. }
  3207. // Get category titles in the case of new item or categories unchanged
  3208. if ( $isnew || !$cats_altered) {
  3209. foreach($after_cats as $after_cat) {
  3210. $cats_titles[] = $after_cat->title;
  3211. }
  3212. }
  3213. // **************
  3214. // CREATE SUBJECT
  3215. // **************
  3216. $srvname = preg_replace('#www\.#','', $_SERVER['SERVER_NAME']);
  3217. $url = parse_url($srvname);
  3218. $domain = !empty($url["host"]) ? $url["host"] : $url["path"];
  3219. $subject = '['.$domain.'] - ';
  3220. if ( !$manual_approval_request ) {
  3221. // (a) ADD INFO of being new or updated
  3222. $subject .= JText::_( $isnew? 'FLEXI_NF_NEW_CONTENT_SUBMITTED' : 'FLEXI_NF_EXISTING_CONTENT_UPDATED') . " ";
  3223. // (b) ADD INFO about editor's name and username (or being guest)
  3224. $subject .= !$user->id ? JText::sprintf('FLEXI_NF_BY_GUEST') : JText::sprintf('FLEXI_NF_BY_USER', $user->get('name'), $user->get('username'));
  3225. // (c) (new items) ADD INFO for content needing publication approval
  3226. if ($isnew) {
  3227. $subject .= ": ";
  3228. $subject .= JText::_( $needs_publication_approval ? 'FLEXI_NF_NEEDS_PUBLICATION_APPROVAL' : 'FLEXI_NF_NO_APPROVAL_NEEDED');
  3229. }
  3230. // (d) (existing items with versioning) ADD INFO for content needing version reviewal
  3231. if ( !$isnew && $use_versioning) {
  3232. $subject .= ": ";
  3233. $subject .= JText::_( $needs_version_reviewal ? 'FLEXI_NF_NEEDS_VERSION_REVIEWAL' : 'FLEXI_NF_NO_REVIEWAL_NEEDED');
  3234. }
  3235. } else {
  3236. $subject .= JText::_('FLEXI_APPROVAL_REQUEST');
  3237. }
  3238. // *******************
  3239. // CREATE MESSAGE BODY
  3240. // *******************
  3241. $nf_extra_properties = $params->get('nf_extra_properties', array('creator','modifier','created','modified','viewlink','editlinkfe','editlinkbe','introtext','fulltext'));
  3242. $nf_extra_properties = FLEXIUtilities::paramToArray($nf_extra_properties);
  3243. // ADD INFO for item title
  3244. $body = '<u>'.JText::_( 'FLEXI_NF_CONTENT_TITLE' ) . "</u>: ";
  3245. if ( !$isnew ) {
  3246. $_changed = $oitem->title != $this->get('title');
  3247. $body .= " [ ". JText::_($_changed ? 'FLEXI_NF_MODIFIED' : 'FLEXI_NF_UNCHANGED') . " ] : <br/>\r\n";
  3248. $body .= !$_changed ? "" : $oitem->title . " &nbsp; ==> &nbsp; ";
  3249. }
  3250. $body .= $this->get('title'). "<br/>\r\n<br/>\r\n";
  3251. // ADD INFO about state
  3252. $state_names = array(1=>'FLEXI_PUBLISHED', -5=>'FLEXI_IN_PROGRESS', 0=>'FLEXI_UNPUBLISHED', -3=>'FLEXI_PENDING', -4=>'FLEXI_TO_WRITE', (FLEXI_J16GE ? 2:-1)=>'FLEXI_ARCHIVED', -2=>'FLEXI_TRASHED');
  3253. $body .= '<u>'.JText::_( 'FLEXI_NF_CONTENT_STATE' ) . "</u>: ";
  3254. if ( !$isnew ) {
  3255. $_changed = $oitem->state != $this->get('state');
  3256. $body .= " [ ". JText::_( $_changed ? 'FLEXI_NF_MODIFIED' : 'FLEXI_NF_UNCHANGED') . " ] : <br/>\r\n";
  3257. $body .= !$_changed ? "" : JText::_( $state_names[$oitem->state] ) . " &nbsp; ==> &nbsp; ";
  3258. }
  3259. $body .= JText::_( $state_names[$this->get('state')] ) ."<br/><br/>\r\n";
  3260. // ADD INFO for author / modifier
  3261. if ( in_array('creator',$nf_extra_properties) )
  3262. {
  3263. $body .= '<u>'.JText::_( 'FLEXI_NF_CREATOR_LONG' ) . "</u>: ";
  3264. if ( !$isnew ) {
  3265. $_changed = $oitem->created_by != $this->get('created_by');
  3266. $body .= " [ ". JText::_($_changed ? 'FLEXI_NF_MODIFIED' : 'FLEXI_NF_UNCHANGED') . " ] : <br/>\r\n";
  3267. $body .= !$_changed ? "" : $oitem->creator . " &nbsp; ==> &nbsp; ";
  3268. }
  3269. $body .= $this->get('creator'). "<br/>\r\n";
  3270. }
  3271. if ( in_array('modifier',$nf_extra_properties) && !$isnew )
  3272. {
  3273. $body .= '<u>'.JText::_( 'FLEXI_NF_MODIFIER_LONG' ) . "</u>: ";
  3274. $body .= $this->get('modifier'). "<br/>\r\n";
  3275. }
  3276. $body .= "<br/>\r\n";
  3277. // ADD INFO about creation / modification times. Use site's timezone !! we must
  3278. // (a) set timezone to be site's timezone then
  3279. // (b) call $date_OBJECT->format() with s local flag parameter set to true
  3280. $tz_offset = JFactory::getApplication()->getCfg('offset');
  3281. if (FLEXI_J16GE) $tz = new DateTimeZone($tz_offset);
  3282. $tz_offset_str = FLEXI_J16GE ? ($tz->getOffset(new JDate()) / 3600) : $tz_offset;
  3283. $tz_offset_str = ' &nbsp; (UTC+'.$tz_offset_str.') ';
  3284. if ( in_array('created',$nf_extra_properties) )
  3285. {
  3286. $date_created = JFactory::getDate($this->get('created'));
  3287. if (FLEXI_J16GE) {
  3288. $date_created->setTimezone($tz);
  3289. } else {
  3290. $date_created->setOffset($tz_offset);
  3291. }
  3292. $body .= '<u>'.JText::_( 'FLEXI_NF_CREATION_TIME' ) . "</u>: ";
  3293. $body .= FLEXI_J16GE ?
  3294. $date_created->format($format = 'D, d M Y H:i:s', $local = true) :
  3295. $date_created->toFormat($format = '%Y-%m-%d %H:%M:%S');
  3296. $body .= $tz_offset_str. "<br/>\r\n";
  3297. }
  3298. if ( in_array('modified',$nf_extra_properties) && !$isnew )
  3299. {
  3300. $date_modified = JFactory::getDate($this->get('modified'));
  3301. if (FLEXI_J16GE) {
  3302. $date_modified->setTimezone($tz);
  3303. } else {
  3304. $date_modified->setOffset($tz_offset);
  3305. }
  3306. $body .= '<u>'.JText::_( 'FLEXI_NF_MODIFICATION_TIME' ) . "</u>: ";
  3307. $body .= FLEXI_J16GE ?
  3308. $date_modified->format($format = 'D, d M Y H:i:s', $local = true) :
  3309. $date_modified->toFormat($format = '%Y-%m-%d %H:%M:%S');
  3310. $body .= $tz_offset_str. "<br/>\r\n";
  3311. }
  3312. $body .= "<br/>\r\n";
  3313. // ADD INFO about category assignments
  3314. $body .= '<u>'.JText::_( 'FLEXI_NF_CATEGORIES_ASSIGNMENTS').'</u>';
  3315. if (!$isnew) {
  3316. $body .= " [ ". JText::_( $cats_altered ? 'FLEXI_NF_MODIFIED' : 'FLEXI_NF_UNCHANGED') . " ] : <br/>\r\n";
  3317. } else {
  3318. $body .= " : <br/>\r\n";
  3319. }
  3320. foreach ($cats_titles as $i => $cats_title) {
  3321. $body .= " &nbsp; ". ($i+1) .". ". $cats_title ."<br/>\r\n";
  3322. }
  3323. // ADD INFO for category assignments added or removed
  3324. if ( !empty($cats_added_titles) && count($cats_added_titles) ) {
  3325. $body .= '<u>'.JText::_( 'FLEXI_NF_ITEM_CATEGORIES_ADDED') . "</u> : <br/>\r\n";
  3326. foreach ($cats_added_titles as $i => $cats_title) {
  3327. $body .= " &nbsp; ". ($i+1) .". ". $cats_title ."<br/>\r\n";
  3328. }
  3329. }
  3330. if ( !empty($cats_removed_titles) && count($cats_removed_titles) ) {
  3331. $body .= '<u>'.JText::_( 'FLEXI_NF_ITEM_CATEGORIES_REMOVED') . "</u> : <br/>\r\n";
  3332. foreach ($cats_removed_titles as $i => $cats_title) {
  3333. $body .= " &nbsp; ". ($i+1) .". ". $cats_title ."<br/>\r\n";
  3334. }
  3335. }
  3336. $body .= "<br/>\r\n<br/>\r\n";
  3337. $lang = '&lang='. substr($this->get('language') ,0,2) ;
  3338. // ADD INFO for custom notify text
  3339. $subject .= ' '. JText::_( $notify_text );
  3340. // ADD INFO for view/edit link
  3341. if ( in_array('viewlink',$nf_extra_properties) )
  3342. {
  3343. $body .= '<u>'.JText::_( 'FLEXI_NF_VIEW_IN_FRONTEND' ) . "</u> : <br/>\r\n &nbsp; ";
  3344. $link = JRoute::_( JURI::root(false).FlexicontentHelperRoute::getItemRoute($this->get('id'), $this->get('catid')) . $lang);
  3345. $body .= $link . "<br/>\r\n<br/>\r\n";
  3346. }
  3347. if ( in_array('editlinkfe',$nf_extra_properties) )
  3348. {
  3349. $body .= '<u>'.JText::_( 'FLEXI_NF_EDIT_IN_FRONTEND' ) . "</u> : <br/>\r\n &nbsp; ";
  3350. $link = JRoute::_( JURI::root(false).'index.php?option=com_flexicontent&view='.FLEXI_ITEMVIEW.'&cid='.$this->get('catid').'&id='.$this->get('id').'&task=edit');
  3351. $body .= $link . "<br/>\r\n<br/>\r\n";
  3352. }
  3353. if ( in_array('editlinkbe',$nf_extra_properties) )
  3354. {
  3355. $body .= '<u>'.JText::_( 'FLEXI_NF_EDIT_IN_BACKEND' ) . "</u> : <br/>\r\n &nbsp; ";
  3356. $fc_ctrl_task = FLEXI_J16GE ? 'task=items.edit' : 'controller=items&task=edit';
  3357. $link = JRoute::_( JURI::root(false).'administrator/index.php?option=com_flexicontent&'.$fc_ctrl_task.'&cid='.$this->get('id'));
  3358. $body .= $link . "<br/>\r\n<br/>\r\n";
  3359. }
  3360. // ADD INFO for introtext/fulltext
  3361. if ( $params->get('nf_add_introtext') )
  3362. {
  3363. //echo "<pre>"; print_r($this->_item); exit;
  3364. $body .= "<br/><br/>\r\n";
  3365. $body .= "*************************************************************** <br/>\r\n";
  3366. $body .= JText::_( 'FLEXI_NF_INTROTEXT_LONG' ) . "<br/>\r\n";
  3367. $body .= "*************************************************************** <br/>\r\n";
  3368. $body .= flexicontent_html::striptagsandcut( $this->get('introtext'), 200 );
  3369. }
  3370. if ( $params->get('nf_add_fulltext') )
  3371. {
  3372. $body .= "<br/><br/>\r\n";
  3373. $body .= "*************************************************************** <br/>\r\n";
  3374. $body .= JText::_( 'FLEXI_NF_FULLTEXT_LONG' ) . "<br/>\r\n";
  3375. $body .= "*************************************************************** <br/>\r\n";
  3376. $body .= flexicontent_html::striptagsandcut( $this->get('fulltext'), 200 );
  3377. }
  3378. // **********
  3379. // Send email
  3380. // **********
  3381. $from = $app->getCfg( 'mailfrom' );
  3382. $fromname = $app->getCfg( 'fromname' );
  3383. $recipient = $params->get('nf_send_as_bcc', 0) ? array($from) : $notify_emails;
  3384. $html_mode = true;
  3385. $cc = null;
  3386. $bcc = $params->get('nf_send_as_bcc', 0) ? $notify_emails : null;
  3387. $attachment = null;
  3388. $replyto = null;
  3389. $replytoname = null;
  3390. if (!FLEXI_J16GE) jimport( 'joomla.utilities.utility' );
  3391. $send_result = FLEXI_J16GE ?
  3392. JFactory::getMailer()->sendMail( $from, $fromname, $recipient, $subject, $body, $html_mode, $cc, $bcc, $attachment, $replyto, $replytoname ) :
  3393. JUtility::sendMail( $from, $fromname, $recipient, $subject, $body, $html_mode, $cc, $bcc, $attachment, $replyto, $replytoname );
  3394. $debug_str = ""
  3395. ."<br/>FROM: $from"
  3396. ."<br/>FROMNAME: $fromname <br/>"
  3397. ."<br/>RECIPIENTS: " .implode(",", $recipient)
  3398. ."<br/>BCC: ". ($bcc ? implode(",",$bcc) : '')."<br/>"
  3399. ."<br/>SUBJECT: $subject <br/>"
  3400. ."<br/><br/>**********<br/>BODY<br/>**********<br/> $body <br/>"
  3401. ;
  3402. if ($send_result) {
  3403. // OK
  3404. if ($params->get('nf_enable_debug',0)) {
  3405. $app->enqueueMessage("Sending WORKFLOW notification emails SUCCESS", 'message' );
  3406. $app->enqueueMessage($debug_str, 'message' );
  3407. }
  3408. } else {
  3409. // NOT OK
  3410. if ($params->get('nf_enable_debug',0)) {
  3411. $app->enqueueMessage("Sending WORKFLOW notification emails FAILED", 'warning' );
  3412. $app->enqueueMessage($debug_str, 'message' );
  3413. }
  3414. }
  3415. return $send_result;
  3416. }
  3417. /**
  3418. * Method to build an object with the items submitted to approval
  3419. * it also verifies if the item state are correct (draft state is -4)
  3420. * and if it belongs to the user
  3421. *
  3422. * @access public
  3423. * @params array
  3424. * @return object
  3425. * @since 1.5
  3426. */
  3427. function isUserDraft($cid)
  3428. {
  3429. $user = JFactory::getUser();
  3430. if ($cid)
  3431. {
  3432. $query = 'SELECT c.id, c.catid, c.created_by, c.title, cat.title AS cattitle from #__content AS c'
  3433. . ' LEFT JOIN #__categories AS cat on cat.id = c.catid'
  3434. . ' WHERE c.state = -4'
  3435. . ' AND c.created_by = ' . (int) $user->get('id')
  3436. . (FLEXI_J16GE ? ' AND cat.extension="'.FLEXI_CAT_EXTENSION.'"' : '')
  3437. . ' AND c.id IN ( '. implode(',', $cid).' )'
  3438. . ' AND ( c.checked_out = 0 OR ( c.checked_out = ' . (int) $user->get('id'). ' ) )'
  3439. ;
  3440. $this->_db->setQuery( $query );
  3441. $cids = $this->_db->loadObjectList();
  3442. if (!$this->_db->query()) {
  3443. $this->setError($this->_db->getErrorMsg());
  3444. return false;
  3445. }
  3446. }
  3447. return $cids;
  3448. }
  3449. /**
  3450. * Method to find reviewers of new item
  3451. *
  3452. * @access public
  3453. * @params int the id of the item
  3454. * @params int the catid of the item
  3455. * @return object the validators object
  3456. * @since 1.5
  3457. */
  3458. function getApprovalRequestReceivers($id, $catid)
  3459. {
  3460. // Get component parameters
  3461. $params = FLEXI_J16GE ? new JRegistry() : new JParameter("");
  3462. $cparams = JComponentHelper::getParams('com_flexicontent');
  3463. $params->merge($cparams);
  3464. // Merge into them the type parameters
  3465. $tparams = $this->getTypeparams();
  3466. $tparams = FLEXI_J16GE ? new JRegistry($tparams) : new JParameter($tparams);
  3467. $params->merge($tparams);
  3468. // We will use the email receivers of --new items-- pending approval, as receivers of the manual approval request
  3469. $nConf = $this->getNotificationsConf($params);
  3470. $validators = new stdClass();
  3471. $validators->notify_emails = $nConf->emails->notify_new_pending;
  3472. $validators->notify_text = ''; // clear this ... default is : 'text_notify_new_pending', but it is not used the case of manual approval
  3473. return $validators;
  3474. }
  3475. /**
  3476. * Logic to submit item to approval
  3477. *
  3478. * @access public
  3479. * @return void
  3480. * @since 1.5
  3481. */
  3482. function approval($cid)
  3483. {
  3484. $db = $this->_db;
  3485. $approvables = $this->isUserDraft($cid);
  3486. $submitted = 0;
  3487. $publishable = array();
  3488. foreach ($approvables as $approvable) {
  3489. // Get item setting it into the model, and get publish privilege
  3490. $item = $this->getItem($approvable->id, $check_view_access=false, $no_cache=true);
  3491. $canEditState = $this->canEditState( $item, $check_cat_perm=true );
  3492. if ( $canEditState ) {
  3493. $publishable[] = $item->title;
  3494. continue;
  3495. }
  3496. // Set to pending approval
  3497. $this->setitemstate($approvable->id, -3);
  3498. $validators = $this->getApprovalRequestReceivers($approvable->id, $approvable->catid);
  3499. if ( !count($validators->notify_emails) ) {
  3500. $validators->notify_emails[] = JFactory::getApplication()->getCfg('mailfrom');
  3501. }
  3502. // Get component parameters and them merge into them the type parameters
  3503. $params = FLEXI_J16GE ? new JRegistry() : new JParameter("");
  3504. $cparams = JComponentHelper::getParams('com_flexicontent');
  3505. $params->merge($cparams);
  3506. $tparams = $this->getTypeparams();
  3507. $tparams = FLEXI_J16GE ? new JRegistry($tparams) : new JParameter($tparams);
  3508. $params->merge($tparams);
  3509. $query = 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
  3510. . ' LEFT JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
  3511. . ' WHERE rel.itemid = '.(int) $approvable->id;
  3512. $db->setQuery( $query );
  3513. $after_cats = $db->loadObjectList('id');
  3514. $notify_vars = new stdClass();
  3515. $notify_vars->needs_version_reviewal = 0;
  3516. $notify_vars->needs_publication_approval = 1;
  3517. $notify_vars->isnew = 1;
  3518. $notify_vars->notify_emails = $validators->notify_emails;
  3519. $notify_vars->notify_text = $validators->notify_text;
  3520. $notify_vars->before_cats = array();
  3521. $notify_vars->after_cats = $after_cats;
  3522. $this->sendNotificationEmails($notify_vars, $params, $manual_approval_request=1);
  3523. $submitted++;
  3524. }
  3525. // Number of submitted items
  3526. if ( $submitted) {
  3527. $approve_str = $submitted > 1 ? 'FLEXI_APPROVAL_ITEMS_SUBMITTED' : 'FLEXI_APPROVAL_ITEM_SUBMITTED';
  3528. $msg = ($submitted > 1 ? $submitted : '') . JText::_( $approve_str );
  3529. } else {
  3530. $msg = JText::_( 'FLEXI_APPROVAL_NO_ITEMS_SUBMITTED' );
  3531. }
  3532. // Number of excluded items, and message that items must be owned and in draft state
  3533. $excluded = count($cid) - $submitted;
  3534. $msg .= $excluded ? ' '. $excluded .' '. JText::_( 'FLEXI_APPROVAL_ITEMS_EXCLUDED' ) : '';
  3535. // Message about excluded publishable items, that can be published by the owner
  3536. if ( count($publishable) ) {
  3537. $publishable_str = '"'. implode('" , "', $publishable) .'"';
  3538. $msg .= '<div>'.JText::sprintf('FLEXI_APPROVAL_PUBLISHABLE_EXCLUDED', $publishable_str).'</div>';
  3539. }
  3540. // This may not be needed since the item was already in unpublished stated ??
  3541. if (FLEXI_J16GE) {
  3542. $cache = FLEXIUtilities::getCache();
  3543. $cache->clean('com_flexicontent_items');
  3544. $cache->clean('com_flexicontent_filters');
  3545. } else {
  3546. $itemcache = JFactory::getCache('com_flexicontent_items');
  3547. $itemcache->clean();
  3548. $filtercache = JFactory::getCache('com_flexicontent_filters');
  3549. $filtercache->clean();
  3550. }
  3551. return $msg;
  3552. }
  3553. function getLangAssocs()
  3554. {
  3555. if ($this->_translations!==null) return $this->_translations;
  3556. $this->_translations = array();
  3557. // Make sure we item list is populased and non-empty
  3558. if ( empty($this->_item) ) return $this->_translations;
  3559. // Get associated translations
  3560. if ( empty($this->_item->lang_parent_id) ) return $this->_translations;
  3561. $query = 'SELECT i.id, i.title, i.created, i.modified, ie.lang_parent_id, ie.language as language, ie.language as lang '
  3562. //. ', CASE WHEN CHAR_LENGTH(i.alias) THEN CONCAT_WS(\':\', i.id, i.alias) ELSE i.id END as slug '
  3563. //. ', CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as categoryslug '
  3564. . ' FROM #__content AS i '
  3565. . ' LEFT JOIN #__flexicontent_items_ext AS ie ON ie.item_id = i.id '
  3566. . ' WHERE ie.lang_parent_id IN ('.(int)$this->_item->lang_parent_id.')'
  3567. ;
  3568. $this->_db->setQuery($query);
  3569. $this->_translations = $this->_db->loadObjectList();
  3570. if ($this->_db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($this->_db->getErrorMsg()),'error');
  3571. if ( empty($this->_translations) ) return $this->_translations;
  3572. return $this->_translations;
  3573. }
  3574. /**
  3575. * Method to toggle the featured setting of articles.
  3576. *
  3577. * @param array The ids of the items to toggle.
  3578. * @param int The value to toggle to.
  3579. *
  3580. * @return boolean True on success.
  3581. */
  3582. public function featured($pks, $value = 0)
  3583. {
  3584. // Sanitize the ids.
  3585. $pks = (array) $pks;
  3586. JArrayHelper::toInteger($pks);
  3587. if (empty($pks)) {
  3588. $this->setError(JText::_('NO item selected'));
  3589. return false;
  3590. }
  3591. $table = $this->getTable('flexicontent_content_frontpage', '');
  3592. try {
  3593. $db = $this->getDbo();
  3594. $db->setQuery(
  3595. 'UPDATE #__content' .
  3596. ' SET featured = '.(int) $value.
  3597. ' WHERE id IN ('.implode(',', $pks).')'
  3598. );
  3599. if (!$db->query()) {
  3600. throw new Exception($db->getErrorMsg());
  3601. }
  3602. if ((int)$value == 0) {
  3603. // Adjust the mapping table.
  3604. // Clear the existing features settings.
  3605. $db->setQuery(
  3606. 'DELETE FROM #__content_frontpage' .
  3607. ' WHERE content_id IN ('.implode(',', $pks).')'
  3608. );
  3609. if (!$db->query()) {
  3610. throw new Exception($db->getErrorMsg());
  3611. }
  3612. } else {
  3613. // first, we find out which of our new featured articles are already featured.
  3614. $query = $db->getQuery(true);
  3615. $query->select('f.content_id');
  3616. $query->from('#__content_frontpage AS f');
  3617. $query->where('content_id IN ('.implode(',', $pks).')');
  3618. //echo $query;
  3619. $db->setQuery($query);
  3620. if (!is_array($old_featured = $db->loadColumn())) {
  3621. throw new Exception($db->getErrorMsg());
  3622. }
  3623. // we diff the arrays to get a list of the articles that are newly featured
  3624. $new_featured = array_diff($pks, $old_featured);
  3625. // Featuring.
  3626. $tuples = array();
  3627. foreach ($new_featured as $pk) {
  3628. $tuples[] = '('.$pk.', 0)';
  3629. }
  3630. if (count($tuples)) {
  3631. $db->setQuery(
  3632. 'INSERT INTO #__content_frontpage ('.$db->quoteName('content_id').', '.$db->quoteName('ordering').')' .
  3633. ' VALUES '.implode(',', $tuples)
  3634. );
  3635. if (!$db->query()) {
  3636. $this->setError($db->getErrorMsg());
  3637. return false;
  3638. }
  3639. }
  3640. }
  3641. } catch (Exception $e) {
  3642. $this->setError($e->getMessage());
  3643. return false;
  3644. }
  3645. $table->reorder();
  3646. $this->cleanCache();
  3647. return true;
  3648. }
  3649. }
  3650. ?>