PageRenderTime 73ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/com_flexicontent_v2.x/site/classes/flexicontent.fields.php

http://flexicontent.googlecode.com/
PHP | 3300 lines | 2156 code | 526 blank | 618 comment | 526 complexity | 2069c6a2b4cbc71ed7cea742ffd2753f MD5 | raw file
Possible License(s): MIT, GPL-2.0, Apache-2.0
  1. <?php
  2. /**
  3. * @version 1.5 stable $Id: flexicontent.fields.php 1884 2014-04-13 10:38:52Z 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. defined( '_JEXEC' ) or die( 'Restricted access' );
  19. // Include com_content helper files, these are needed by some content plugins
  20. require_once (JPATH_SITE.DS.'components'.DS.'com_content'.DS.'helpers'.DS.'route.php');
  21. require_once (JPATH_SITE.DS.'components'.DS.'com_content'.DS.'helpers'.DS.'query.php');
  22. //include constants file
  23. require_once (JPATH_ADMINISTRATOR.DS.'components'.DS.'com_flexicontent'.DS.'defineconstants.php');
  24. class FlexicontentFields
  25. {
  26. /**
  27. * Function to render the field display variables for the given items
  28. *
  29. * @param int $item_id
  30. * @return string : the HTML of the item view, also the CSS / JS file would have been loaded
  31. * @since 1.5
  32. */
  33. static function renderFields( $item_per_field=true, $item_ids=array(), $field_names=array(), $view=FLEXI_ITEMVIEW, $methods=array(), $cfparams=array() )
  34. {
  35. require_once (JPATH_ADMINISTRATOR.DS.'components/com_flexicontent/defineconstants.php');
  36. JTable::addIncludePath(JPATH_ADMINISTRATOR.DS.'components'.DS.'com_flexicontent'.DS.'tables');
  37. //require_once("components/com_flexicontent/classes/flexicontent.fields.php");
  38. require_once("components/com_flexicontent/classes/flexicontent.helper.php");
  39. // ***************************
  40. // Check if no data were given
  41. // ***************************
  42. if ( empty($item_ids) || empty($field_names) ) return false;
  43. // Get item data, needed for rendering fields
  44. $db = JFactory::getDBO();
  45. $item_ids = array_unique(array_map('intval', $item_ids));
  46. $item_ids_list = implode("," , $item_ids) ;
  47. $query = 'SELECT i.id, i.*, ie.*, '
  48. . ' CASE WHEN CHAR_LENGTH(i.alias) THEN CONCAT_WS(\':\', i.id, i.alias) ELSE i.id END as slug,'
  49. . ' CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as categoryslug'
  50. . ' FROM #__content AS i'
  51. . ' LEFT JOIN #__flexicontent_items_ext AS ie ON ie.item_id = i.id'
  52. . ' LEFT JOIN #__categories AS c ON c.id = i.catid'
  53. . ' WHERE i.id IN ('. $item_ids_list .')'
  54. . ' GROUP BY i.id';
  55. $db->setQuery($query);
  56. $items = $db->loadObjectList();
  57. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  58. if (!$items) return false;
  59. foreach ($items as $i => $item) $_item_id_map[$item->id] = & $items[$i];
  60. // **************
  61. // Get Field info
  62. // **************
  63. /*if ( $using_ids )
  64. {
  65. $field_ids = array_unique(array_map('intval', $_field_ids));
  66. $field_ids_list = implode("," , $field_ids) ;
  67. $field_where = ' WHERE f.id IN ('. $field_ids_list .')';
  68. }
  69. else {
  70. foreach ($field_names as $i => $field_name) {
  71. $field_names[$i] = preg_replace("/[\"'\\\]/u", "", $field_name);
  72. }
  73. $field_names_list = "'". implode("','" , $field_names) ."'";
  74. $field_where = 'f.name IN ('. $field_names_list .')';
  75. }
  76. $query = 'SELECT f.*'
  77. . ' FROM #__flexicontent_fields AS f'
  78. . ' WHERE 1 '.$field_where
  79. ;
  80. $db->setQuery($query);
  81. $fields = $db->loadObjectList('id');
  82. if (!$fields) return false;*/
  83. // *********************************
  84. // Render Display Variable of Fields
  85. // *********************************
  86. // Get Field values at once to minimized performance impact, null 'params' mean only retrieve values
  87. /*if ($item_per_field && count($items)>1)
  88. // we have at least 2 item and item is per field, this will retrieve all values with single SQL query
  89. FlexicontentFields::getFields($items, $view, $params = null, $aid = false);*/
  90. $return = array();
  91. foreach ($field_names as $i => $field_name)
  92. {
  93. $method = isset( $methods[$i] ) ? $methods[$i] : 'display';
  94. if ( $item_per_field )
  95. {
  96. if ( !isset( $_item_id_map[ $item_ids[$i] ] ) ) { echo "not found item: ".$item_ids[$i] ." <br/>"; continue;}
  97. // Render Display variable of Field for respective item
  98. $_item = & $_item_id_map[$item_ids[$i]];
  99. FlexicontentFields::getFieldDisplay($_item, $field_name, $values=null, $method, $view);
  100. // Add to return array
  101. $return[$_item->id][$field_name] = $_item->fields[$field_name]->$method;
  102. }
  103. else
  104. {
  105. // Render Display variable of Field for all items
  106. FlexicontentFields::getFieldDisplay($items, $field_name, $values=null, $method, $view);
  107. // Add to return array
  108. foreach ($items as $item) {
  109. $return[$item->id][$field_name] = $item->fields[$field_name]->display;
  110. }
  111. }
  112. }
  113. return $return;
  114. }
  115. /**
  116. * Method to bind fields to an items object
  117. *
  118. * @access private
  119. * @return object
  120. * @since 1.5
  121. */
  122. static function &getFields(&$_items, $view = FLEXI_ITEMVIEW, $params = null, $aid = false, $use_tmpl = true)
  123. {
  124. static $apply_cache = null;
  125. static $expired_cleaned = false;
  126. if (!$_items) return $_items;
  127. if (!is_array($_items)) $items = array( & $_items ); else $items = & $_items ;
  128. $user = JFactory::getUser();
  129. $mainframe = JFactory::getApplication();
  130. $cparams = $mainframe->getParams('com_flexicontent');
  131. $print_logging_info = $cparams->get('print_logging_info');
  132. if ( $print_logging_info ) {
  133. global $fc_run_times;
  134. $start_microtime = microtime(true);
  135. }
  136. // Calculate access if it was not providden
  137. if (FLEXI_J16GE) {
  138. $aid = is_array($aid) ? $aid : $user->getAuthorisedViewLevels();
  139. } else {
  140. $aid = $aid!==false ? (int) $aid : (int) $user->get('aid');
  141. }
  142. // Apply cache to public (unlogged) users only
  143. /*if ($apply_cache === null) {
  144. if (FLEXI_J16GE) {
  145. $apply_cache = max($aid) <= 1; // ACCESS LEVEL : PUBLIC 1 , REGISTERED 2
  146. } else {
  147. //$apply_cache = FLEXI_ACCESS ? ($user->gmid == '0' || $user->gmid == '0,1') : ($user->gid <= 18); // This is for registered too
  148. $apply_cache = $aid <= 0; // ACCESS LEVEL : PUBLIC 0 , REGISTERED 1
  149. }
  150. $apply_cache = $apply_cache && FLEXI_CACHE;
  151. }
  152. if ($apply_cache) {
  153. $itemcache = JFactory::getCache('com_flexicontent_items'); // Get Joomla Cache of '...items' Caching Group
  154. $itemcache->setCaching(1); // Force cache ON
  155. $itemcache->setLifeTime(FLEXI_CACHE_TIME); // Set expiration to default e.g. one hour
  156. $filtercache = JFactory::getCache('com_flexicontent_filters'); // Get Joomla Cache of '...filters' Caching Group
  157. $filtercache->setCaching(1); // Force cache ON
  158. $filtercache->setLifeTime(FLEXI_CACHE_TIME); // Set expiration to default e.g. one hour
  159. // Auto-clean expired item & filters cache, only done here once
  160. if (FLEXI_GC && !$expired_cleaned) {
  161. $itemcache->gc();
  162. $filtercache->gc();
  163. $expired_cleaned = true;
  164. }
  165. }*/
  166. // @TODO : move to the constructor
  167. // This is optimized regarding the use of SINGLE QUERY to retrieve the core item data
  168. $vars['tags'] = FlexicontentFields::_getTags($items);
  169. $vars['cats'] = FlexicontentFields::_getCategories($items);
  170. $vars['favourites'] = FlexicontentFields::_getFavourites($items);
  171. $vars['favoured'] = FlexicontentFields::_getFavoured($items);
  172. $vars['authors'] = FlexicontentFields::_getAuthors($items);
  173. $vars['modifiers'] = FlexicontentFields::_getModifiers($items);
  174. $vars['typenames'] = FlexicontentFields::_getTypenames($items);
  175. $vars['votes'] = FlexicontentFields::_getVotes($items);
  176. $vars['custom'] = FlexicontentFields::_getCustomValues($items);
  177. FlexicontentFields::getItemFields($items, $vars, $view, $aid);
  178. if ( $print_logging_info ) @$fc_run_times['field_values_params'] += round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  179. if ($params) // NULL/empty parameters mean only retrieve field values
  180. {
  181. // CHECK if 'always_create_fields_display' enabled and create the display for all item's fields
  182. // *** This should be normally set to ZERO (never), to avoid a serious performance penalty !!!
  183. foreach ($items as $i => $item)
  184. {
  185. $always_create_fields_display = $cparams->get('always_create_fields_display',0);
  186. $flexiview = JRequest::getVar('view', false);
  187. // 0: never, 1: always, 2: only in item view
  188. if ($always_create_fields_display==1 || ($always_create_fields_display==2 && $flexiview==FLEXI_ITEMVIEW) ) {
  189. if ($items[$i]->fields)
  190. {
  191. foreach ($items[$i]->fields as $field)
  192. {
  193. $values = isset($items[$i]->fieldvalues[$field->id]) ? $items[$i]->fieldvalues[$field->id] : array();
  194. $field = FlexicontentFields::renderField($items[$i], $field, $values, $method='display', $view);
  195. }
  196. }
  197. }
  198. }
  199. // Render field positions
  200. $items = FlexicontentFields::renderPositions($items, $view, $params, $use_tmpl);
  201. }
  202. return $items;
  203. }
  204. /**
  205. * Method to fetch the fields from an item object
  206. *
  207. * @access private
  208. * @return object
  209. * @since 1.5
  210. */
  211. static function & getItemFields(&$items, &$vars, $view=FLEXI_ITEMVIEW, $aid=false)
  212. {
  213. if ( empty($items) ) return;
  214. static $type_fields = array();
  215. $mainframe = JFactory::getApplication();
  216. $dispatcher = JDispatcher::getInstance();
  217. $db = JFactory::getDBO();
  218. $user = JFactory::getUser();
  219. jimport('joomla.html.parameter');
  220. foreach ($items as $i => $item)
  221. {
  222. if (!FLEXI_J16GE && $item->sectionid != FLEXI_SECTION) continue;
  223. $item_id = $item->id;
  224. $cats = isset($vars['cats'][$item_id]) ? $vars['cats'][$item_id] : array();
  225. $tags = isset($vars['tags'][$item_id]) ? $vars['tags'][$item_id] : array();
  226. $favourites= isset($vars['favourites'][$item_id])? $vars['favourites'][$item_id]->favs : 0;
  227. $favoured = isset($vars['favoured'][$item_id]) ? $vars['favoured'][$item_id]->fav : 0;
  228. $author = isset($vars['authors'][$item_id]) ? $vars['authors'][$item_id] : '';
  229. $modifier = isset($vars['modifiers'][$item_id]) ? $vars['modifiers'][$item_id] : '';
  230. $typename = isset($vars['typenames'][$item_id]) ? $vars['typenames'][$item_id] : '';
  231. $vote = isset($vars['votes'][$item_id]) ? $vars['votes'][$item_id] : '';
  232. $custom = isset($vars['custom'][$item_id]) ? $vars['custom'][$item_id] : array();
  233. // ONCE per Content Item Type
  234. if ( !isset($type_fields[$item->type_id]) )
  235. {
  236. if (FLEXI_J16GE) {
  237. $aid_arr = is_array($aid) ? $aid : $user->getAuthorisedViewLevels();
  238. $aid_list = implode(",", $aid);
  239. $andaccess = ' AND fi.access IN (0,'.$aid_list.')' ;
  240. $joinaccess = '';
  241. } else {
  242. $aid = $aid!==false ? (int) $aid : (int) $user->get('aid');
  243. $andaccess = FLEXI_ACCESS ? ' AND (gi.aro IN ( '.$user->gmid.' ) OR fi.access <= '. $aid . ')' : ' AND fi.access <= '.$aid ;
  244. $joinaccess = FLEXI_ACCESS ? ' LEFT JOIN #__flexiaccess_acl AS gi ON fi.id = gi.axo AND gi.aco = "read" AND gi.axosection = "field"' : '' ;
  245. }
  246. $query = 'SELECT fi.*'
  247. . ' FROM #__flexicontent_fields AS fi'
  248. . ' LEFT JOIN #__flexicontent_fields_type_relations AS ftrel ON ftrel.field_id = fi.id AND ftrel.type_id = '.$item->type_id
  249. . $joinaccess
  250. . ' WHERE fi.published = 1'
  251. . $andaccess
  252. . ' GROUP BY fi.id'
  253. . ' ORDER BY ftrel.ordering, fi.ordering, fi.name'
  254. ;
  255. $db->setQuery($query);
  256. $type_fields[$item->type_id] = $db->loadObjectList('name');
  257. }
  258. $item->fields = array();
  259. if ($type_fields[$item->type_id]) foreach($type_fields[$item->type_id] as $field_name => $field_data)
  260. $item->fields[$field_name] = clone($field_data);
  261. $item->fields = $item->fields ? $item->fields : array();
  262. if (!isset($item->parameters)) $item->parameters = FLEXI_J16GE ? new JRegistry($item->attribs) : new JParameter($item->attribs);
  263. $item->params = $item->parameters;
  264. $item->text = $item->introtext . chr(13).chr(13) . $item->fulltext;
  265. $item->tags = $tags;
  266. $item->cats = $cats;
  267. $item->favs = $favourites;
  268. $item->fav = $favoured;
  269. $item->creator = @$author->alias ? $author->alias : (@$author->name ? $author->name : '') ;
  270. $item->author = & $item->creator; // An alias ... of creator
  271. $item->modifier = @$modifier->name ? $modifier->name : $item->creator; // If never modified, set modifier to be the creator
  272. $item->modified = ($item->modified != $db->getNulldate()) ? $item->modified : $item->created; // If never modified, set modification date to be the creation date
  273. $item->cmail = @$author->email ? $author->email : '' ;
  274. $item->cuname = @$author->username ? $author->username : '' ;
  275. $item->mmail = @$modifier->email ? $modifier->email : $item->cmail;
  276. $item->muname = @$modifier->muname ? $modifier->muname : $item->cuname;
  277. $item->typename = @$typename->name ? $typename->name : JText::_('Article');
  278. $item->vote = @$vote ? $vote : '';
  279. // some aliases to much CORE field names
  280. $item->categories = & $item->cats;
  281. $item->favourites = & $item->favs;
  282. $item->document_type = & $item->typename;
  283. $item->voting = & $item->vote;
  284. // custom field values
  285. $item->fieldvalues = $custom;
  286. /*if ($item->fields) {
  287. // IMPORTANT the items model and possibly other will set item PROPERTY version_id to indicate loading an item version,
  288. // It is not the responisibility of this CODE to try to detect previewing of an item version, it is better left to the model
  289. $item->fieldvalues = FlexicontentFields::_getFieldsvalues($item->id, $item->fields, !empty($item->version_id) ? $item->version_id : 0);
  290. }*/
  291. }
  292. return $items;
  293. }
  294. /**
  295. * Method to render (display method) a field on demand and return the display
  296. *
  297. * @access public
  298. * @return object
  299. * @since 1.5.5
  300. */
  301. static function &getFieldDisplay(&$item_arr, $fieldname, $single_item_vals=null, $method='display', $view = FLEXI_ITEMVIEW)
  302. {
  303. // 1. Convert to array of items if not an array already
  304. if ( empty($item_arr) )
  305. return __FUNCTION__."(): empty item data given";
  306. else if ( !is_array($item_arr) )
  307. $items = array( & $item_arr );
  308. else
  309. $items = & $item_arr;
  310. // 2. Make sure that fields have been created for all given items
  311. $_items = array();
  312. foreach ($items as $i => $item) if (!isset($item->fields)) $_items[] = & $items[$i];
  313. if ( count($_items) ) FlexicontentFields::getFields($_items, $view);
  314. // 3. Check and create HTML display for the given field name
  315. $_return = array();
  316. foreach ($items as $item)
  317. {
  318. // Check if we have already created the display and skip current item
  319. if ( isset($item->onDemandFields[$fieldname]->{$method}) ) continue;
  320. // Find the field inside item
  321. foreach ($item->fields as $field) {
  322. if ( !empty($field->name) && $field->name==$fieldname ) break;
  323. }
  324. // Check for not found field, and skip it, this is either due to no access or wrong name ...
  325. $item->onDemandFields[$fieldname] = new stdClass();
  326. if ( empty($field->name) || $field->name!=$fieldname) {
  327. $item->onDemandFields[$fieldname]->label = '';
  328. $item->onDemandFields[$fieldname]->noaccess = true;
  329. $item->onDemandFields[$fieldname]->errormsg = 'field not assigned to this type of item or current user has no access';
  330. $item->onDemandFields[$fieldname]->{$method} = '';
  331. continue;
  332. }
  333. // Get field's values if they were custom values were not given
  334. if ( $single_item_vals!==null && count($items) == 1 ) {
  335. // $values is used only if rendering a single item
  336. $values = $single_item_vals;
  337. } else {
  338. $values = isset($item->fieldvalues[$field->id]) ? $item->fieldvalues[$field->id] : array();
  339. }
  340. // Set other field data like label and field itself !!!
  341. $item->onDemandFields[$fieldname]->label = $field->label;
  342. $item->onDemandFields[$fieldname]->noaccess = false;
  343. $item->onDemandFields[$fieldname]->field = & $field;
  344. // Render the (display) method of the field
  345. if (!isset($field->{$method})) $field = FlexicontentFields::renderField($item, $field, $values, $method, $view);
  346. if (!isset($field->{$method})) $field->{$method} = '';
  347. $item->onDemandFields[$fieldname]->{$method} = & $field->{$method};
  348. $_method_html[$item->id] = & $field->{$method};
  349. }
  350. // Return field(s) HTML (in case of multiple items this will be an array indexable by item ids
  351. if ( !is_array($item_arr) ) {
  352. $_method_html = @ $_method_html[$item_arr->id]; // Suppress field name not found ...
  353. }
  354. return $_method_html;
  355. }
  356. /**
  357. * Method to render a field
  358. *
  359. * @access public
  360. * @return object
  361. * @since 1.5
  362. */
  363. static function renderField(&$_items, &$_field, &$values, $method='display', $view=FLEXI_ITEMVIEW)
  364. {
  365. static $_trigger_plgs_ft = array();
  366. $flexiview = JRequest::getVar('view');
  367. // If $method (e.g. display method) is already created, then return the $field without recreating the $method
  368. if ( is_object($_field) && isset($_field->{$method}) ) return $_field;
  369. // Handle multi-item call
  370. if (!is_array($_items)) $items = array( & $_items ); else $items = & $_items ;
  371. // **********************************************************************************************
  372. // Create field parameters in an optimized way, and also apply Type Customization for CORE fields
  373. // **********************************************************************************************
  374. foreach($items as $item) {
  375. $field = is_object($_field) ? $_field : $item->fields[$_field];
  376. $field->item_id = (int)$item->id;
  377. $field->value = $values; // NOTE: currently ignored and overwritten by all CORE fields
  378. FlexicontentFields::loadFieldConfig($field, $item);
  379. }
  380. // ***************************************************************************************************
  381. // Create field HTML by calling the appropriate DISPLAY-CREATING field plugin method.
  382. // NOTE 1: We will not pass the 'values' method parameter to the display-creating field method,
  383. // instead we have set it above as the 'value' field property
  384. // NOTE 2: For CUSTOM fields the 'values' method parameter is prefered over the 'value' field property
  385. // For CORE field, both the above ('values' method parameter and 'value' field property) are
  386. // ignored and instead the other method parameters are used, along with the ITEM properties
  387. // ****************************************************************************************************
  388. // Log content plugin and other performance information
  389. $cparams = JComponentHelper::getParams('com_flexicontent');
  390. $print_logging_info = $cparams->get('print_logging_info');
  391. if ($print_logging_info) global $fc_run_times;
  392. if ($print_logging_info) $start_microtime = microtime(true);
  393. if ($field->iscore == 1) // CORE field
  394. {
  395. //$results = $dispatcher->trigger('onDisplayCoreFieldValue', array( &$field, $item, &$item->parameters, $item->tags, $item->cats, $item->favs, $item->fav, $item->vote ));
  396. //FLEXIUtilities::call_FC_Field_Func('core', 'onDisplayCoreFieldValue', array( &$_field, & $items, &$item->parameters, $item->tags, $item->cats, $item->favs, $item->fav, $item->vote, null, $method ) );
  397. $items_params = null;
  398. FLEXIUtilities::call_FC_Field_Func('core', 'onDisplayCoreFieldValue', array( &$_field, & $items, & $items_params, false, false, false, false, false, null, $method ) );
  399. }
  400. else // NON CORE field
  401. {
  402. //$results = $dispatcher->trigger('onDisplayFieldValue', array( &$field, $item ));
  403. FLEXIUtilities::call_FC_Field_Func($field->field_type, 'onDisplayFieldValue', array(&$field, $_items, null, $method) );
  404. }
  405. if ($print_logging_info) {
  406. $field_render_time = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  407. if ( isset($fc_run_times['render_subfields'][$item->id."_".$field->id]) ) {
  408. $field_render_time = $field_render_time - $fc_run_times['render_subfields'][$item->id."_".$field->id];
  409. @$fc_run_times['render_subfields'][$field->field_type] += $fc_run_times['render_subfields'][$item->id."_".$field->id];
  410. unset($fc_run_times['render_subfields'][$item->id."_".$field->id]);
  411. }
  412. @$fc_run_times['render_field'][$field->field_type] += $field_render_time;
  413. }
  414. // *****************************************
  415. // Trigger content plugins on the field text
  416. // *****************************************
  417. if ( !isset($_trigger_plgs_ft[$field->name]) ) {
  418. $_t = $field->parameters->get('trigger_onprepare_content', 0);
  419. if ($flexiview=='category') $_t = $_t && $field->parameters->get('trigger_plgs_incatview', 1);
  420. $_trigger_plgs_ft[$field->name] = $_t;
  421. }
  422. if ( $_trigger_plgs_ft[$field->name] ) {
  423. if ($print_logging_info) $start_microtime = microtime(true);
  424. FlexicontentFields::triggerContentPlugins($field, $item, $method, $view);
  425. if ( $print_logging_info ) @$fc_run_times['content_plg'] += round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  426. }
  427. return $field;
  428. }
  429. /**
  430. * Method to selectively trigger content plugins for the text of the specified field
  431. *
  432. * @access public
  433. * @return object
  434. * @since 1.5
  435. */
  436. static function triggerContentPlugins(&$field, &$item, $method, $view=FLEXI_ITEMVIEW)
  437. {
  438. $debug = false;
  439. static $_plgs_loaded = array();
  440. static $_fields_plgs = array();
  441. static $_initialize = false;
  442. static $_view, $_option, $limitstart;
  443. static $dispatcher, $fcdispatcher;
  444. //$flexiparams = JComponentHelper::getParams('com_flexicontent');
  445. //$print_logging_info = $flexiparams->get('print_logging_info');
  446. // Log content plugin and other performance information
  447. //if ($print_logging_info) global $fc_run_times;
  448. if (!$_initialize) {
  449. // some request and other variables
  450. $_view = JRequest::getVar('view');
  451. $_option = JRequest::getVar('option');
  452. $limitstart = JRequest::getVar('limitstart', 0, '', 'int');
  453. $_initialize = true;
  454. // ***********************************************************************
  455. // We use a custom Dispatcher to allow selective Content Plugin triggering
  456. // ***********************************************************************
  457. require_once (JPATH_SITE.DS.'components'.DS.'com_flexicontent'.DS.'helpers'.DS.'dispatcher.php');
  458. $dispatcher = JDispatcher::getInstance();
  459. $fcdispatcher = FCDispatcher::getInstance_FC($debug);
  460. }
  461. if ($debug) echo "<br><br>Executing plugins for <b>".$field->name."</b>:<br>";
  462. if ( !@$_fields_plgs[$field->name] )
  463. {
  464. // Make sure the necessary plugin are already loaded, but do not try to load them again since this will harm performance
  465. if (!$field->parameters->get('plugins'))
  466. {
  467. $_plgs = null;
  468. if (!@$_plgs_loaded['__ALL__']) {
  469. JPluginHelper::importPlugin('content', $plugin = null, $autocreate = true, $dispatcher);
  470. $_plgs_loaded['__ALL__'] = 1;
  471. }
  472. }
  473. else
  474. {
  475. $_plgs = $field->parameters->get('plugins');
  476. $_plgs = $_plgs ? $_plgs : array();
  477. $_plgs = is_array($_plgs) ? $_plgs : explode('|', $_plgs); // compatibility because old versions did not JSON encode the parameters
  478. if (!@$_plgs_loaded['__ALL__']) foreach ($_plgs as $_plg) if (!@$_plgs_loaded[$_plg]) {
  479. JPluginHelper::importPlugin('content', $_plg, $autocreate = true, $dispatcher);
  480. $_plgs_loaded[$_plg] = 1;
  481. }
  482. }
  483. $_fields_plgs[$field->name] = $_plgs;
  484. }
  485. $plg_arr = $_fields_plgs[$field->name];
  486. // Suppress some plugins from triggering for compatibility reasons, e.g.
  487. // (a) jcomments, jom_comment_bot plugins, because we will get comments HTML manually inside the template files
  488. $suppress_arr = array('jcomments', 'jom_comment_bot');
  489. FLEXIUtilities::suppressPlugins($suppress_arr, 'suppress' );
  490. // Initialize field for plugin triggering
  491. $field->text = isset($field->{$method}) ? $field->{$method} : '';
  492. $field->title = $item->title;
  493. $field->slug = $item->slug;
  494. $field->sectionid = !FLEXI_J16GE ? $item->sectionid : false;
  495. $field->catid = $item->catid;
  496. $field->catslug = @$item->categoryslug;
  497. $field->fieldid = $field->id;
  498. $field->id = $item->id;
  499. $field->state = $item->state;
  500. $field->type_id = $item->type_id;
  501. // CASE: FLEXIcontent item view:
  502. // Set triggering 'context' to 'com_content.article', (and also set the 'view' request variable)
  503. if ($view == FLEXI_ITEMVIEW) {
  504. JRequest::setVar('view', 'article');
  505. $context = 'com_content.article';
  506. }
  507. // ALL OTHER CASES: (FLEXIcontent category, FLEXIcontent module, etc),
  508. // Set triggering 'context' to 'com_content.article', (and also set the 'view' request variable)
  509. else {
  510. JRequest::setVar('view', 'category');
  511. $context = 'com_content.category';
  512. }
  513. // Set the 'option' to 'com_content' but set a flag 'isflexicontent' to indicate triggering from inside FLEXIcontent ... code
  514. JRequest::setVar('option', 'com_content');
  515. JRequest::setVar("isflexicontent", "yes");
  516. // Trigger content plugins on field's HTML display, as if they were a "joomla article"
  517. if (FLEXI_J16GE) $results = $fcdispatcher->trigger('onContentPrepare', array ($context, &$field, &$item->parameters, $limitstart), $plg_arr);
  518. else $results = $fcdispatcher->trigger('onPrepareContent', array (&$field, &$item->parameters, $limitstart), false, $plg_arr);
  519. // Restore 'view' and 'option' request variables
  520. JRequest::setVar('view', $_view);
  521. JRequest::setVar('option', $_option);
  522. $field->id = $field->fieldid;
  523. $field->{$method} = $field->text;
  524. // Restore suppressed plugins
  525. FLEXIUtilities::suppressPlugins( $suppress_arr,'restore' );
  526. }
  527. /**
  528. * Method to get the fields in their positions
  529. *
  530. * @access private
  531. * @return object
  532. * @since 1.5
  533. */
  534. static function &renderPositions(&$items, $view = FLEXI_ITEMVIEW, $params = null, $use_tmpl = true)
  535. {
  536. if (!$items) return;
  537. if (!$params) return $items;
  538. if ($view == 'category') $layout = 'clayout';
  539. if ($view == FLEXI_ITEMVIEW) $layout = 'ilayout';
  540. // field's source code, can use this JRequest variable, to detect who rendered the fields (e.g. they can detect rendering from 'module')
  541. JRequest::setVar("flexi_callview", $view);
  542. if ( $use_tmpl && ($view == 'category' || $view == FLEXI_ITEMVIEW) ) {
  543. $fbypos = flexicontent_tmpl::getFieldsByPositions($params->get($layout, 'default'), $view);
  544. } else { // $view == 'module', or other
  545. // Create a fake template position, for fields defined via parameters
  546. $fbypos[0] = new stdClass();
  547. $fbypos[0]->fields = explode(',', $params->get('fields'));
  548. $fbypos[0]->methods = explode(',', $params->get('methods'));
  549. $fbypos[0]->position = $view;
  550. }
  551. $always_create_fields_display = $params->get('always_create_fields_display',0);
  552. foreach ($items as $item)
  553. {
  554. if ($always_create_fields_display != 3) { // value 3 means never create for any view (blog template incompatible)
  555. // 'description' item field is implicitly used by category layout of some templates (blog), render it
  556. $custom_values = false;
  557. if ($view == 'category') {
  558. if (isset($item->fields['text'])) {
  559. $field = $item->fields['text'];
  560. $field = FlexicontentFields::renderField($item, $field, $custom_values, $method='display', $view);
  561. }
  562. }
  563. // 'core' item fields are IMPLICITLY used by some item layout of some templates (blog), render them
  564. else if ($view == FLEXI_ITEMVIEW) {
  565. foreach ($item->fields as $field) {
  566. if ($field->iscore) {
  567. $field = FlexicontentFields::renderField($item, $field, $custom_values, $method='display', $view);
  568. }
  569. }
  570. }
  571. }
  572. }
  573. // *** RENDER fields on DEMAND, (if present in template positions)
  574. foreach ($fbypos as $pos) {
  575. // RENDER fields if they are present in a template position (or in a dummy template position ... e.g. when called by module)
  576. foreach ($pos->fields as $c => $f) {
  577. // Render field (if already rendered above, the function will return result immediately)
  578. $method = (isset($pos->methods[$c]) && $pos->methods[$c]) ? $pos->methods[$c] : 'display';
  579. // Check that field with given name: $f exists, (this will handle deleted fields, that still exist in a template position)
  580. $item = reset($items);
  581. if (!isset($item->fields[$f])) continue;
  582. $field = $item->fields[$f];
  583. if ($field->iscore) {
  584. $values = null;
  585. FlexicontentFields::renderField($items, $f, $values, $method, $view);
  586. }
  587. else foreach ($items as $item) {
  588. $field = $item->fields[$f];
  589. // Set field values, currently, this exists for CUSTOM fields only, OR versioned CORE/CUSTOM fields too ...
  590. $values = isset($item->fieldvalues[$field->id]) ? $item->fieldvalues[$field->id] : array();
  591. $field = FlexicontentFields::renderField($item, $field, $values, $method, $view);
  592. }
  593. foreach ($items as $item) {
  594. $field = $item->fields[$f];
  595. // Set template position field data
  596. if (isset($field->display) && strlen($field->display))
  597. {
  598. if (!isset($item->positions[$pos->position]))
  599. $item->positions[$pos->position] = new stdClass();
  600. $item->positions[$pos->position]->{$f} = new stdClass();
  601. $item->positions[$pos->position]->{$f}->id = $field->id;
  602. $item->positions[$pos->position]->{$f}->id = $field->id;
  603. $item->positions[$pos->position]->{$f}->name = $field->name;
  604. $item->positions[$pos->position]->{$f}->label = $field->parameters->get('display_label') ? $field->label : '';
  605. $item->positions[$pos->position]->{$f}->display = $field->display;
  606. }
  607. }
  608. }
  609. }
  610. return $items;
  611. }
  612. /**
  613. * Method to get the values of the fields for an item
  614. *
  615. * @access private
  616. * @return object
  617. * @since 1.5
  618. */
  619. static function _getFieldsvalues($item, $fields, $version=0)
  620. {
  621. $db = JFactory::getDBO();
  622. $query = 'SELECT field_id, value'
  623. .( $version ? ' FROM #__flexicontent_items_versions':' FROM #__flexicontent_fields_item_relations')
  624. .' WHERE item_id = ' . (int)$item
  625. .( $version ? ' AND version=' . (int)$version:'')
  626. .' AND value > "" '
  627. .' ORDER BY field_id, valueorder'
  628. ;
  629. $db->setQuery($query);
  630. $values = $db->loadObjectList();
  631. $fieldvalues = array();
  632. foreach ($fields as $f) {
  633. foreach ($values as $v) {
  634. if ((int)$f->id == (int)$v->field_id) {
  635. $fieldvalues[$f->id][] = $v->value;
  636. }
  637. }
  638. }
  639. return $fieldvalues;
  640. }
  641. /**
  642. * Method to get the values of the fields for multiple items at once
  643. *
  644. * @access private
  645. * @return object
  646. * @since 1.5
  647. */
  648. static function _getCustomValues($items)
  649. {
  650. $versioned_item = count($items)==1 && !empty($items[0]->version_id) && !empty($items[0]->created_by);
  651. $version = $versioned_item ? $items[0]->version_id : 0;
  652. $item_ids = array();
  653. foreach ($items as $item) $item_ids[] = $item->id;
  654. $db = JFactory::getDBO();
  655. $query = 'SELECT field_id, value, item_id'
  656. .( $version ? ' FROM #__flexicontent_items_versions':' FROM #__flexicontent_fields_item_relations')
  657. .' WHERE item_id IN (' . implode(',', $item_ids) .')'
  658. .( $version ? ' AND version=' . (int)$version:'')
  659. .' AND value > "" '
  660. .' ORDER BY item_id, field_id, valueorder' // first 2 parts are not needed ...
  661. ;
  662. $db->setQuery($query);
  663. $values = $db->loadObjectList();
  664. $fieldvalues = array();
  665. foreach ($values as $v) {
  666. $fieldvalues[$v->item_id][$v->field_id][] = $v->value;
  667. }
  668. return $fieldvalues;
  669. }
  670. /**
  671. * Method to get the tags
  672. *
  673. * @access private
  674. * @return object
  675. * @since 1.5
  676. */
  677. static function _getTags($items)
  678. {
  679. // This is fix for versioned field of creator in items view when previewing
  680. $versioned_item = count($items)==1 && !empty($items[0]->version_id) && !empty($items[0]->tags);
  681. $db = JFactory::getDBO();
  682. if ($versioned_item) {
  683. if (!count($items[0]->tags)) return array();
  684. $tids = $items[0]->tags;
  685. $query = 'SELECT DISTINCT t.id, t.name, ' . $items[0]->id .' as itemid, '
  686. . ' CASE WHEN CHAR_LENGTH(t.alias) THEN CONCAT_WS(\':\', t.id, t.alias) ELSE t.id END as slug'
  687. . ' FROM #__flexicontent_tags AS t'
  688. . " WHERE t.id IN ('" . implode("','", $tids) . "')"
  689. . ' AND t.published = 1'
  690. . ' ORDER BY t.name'
  691. ;
  692. } else {
  693. $cids = array();
  694. foreach ($items as $item) { array_push($cids, $item->id); }
  695. $query = 'SELECT DISTINCT t.id, t.name, i.itemid,'
  696. . ' CASE WHEN CHAR_LENGTH(t.alias) THEN CONCAT_WS(\':\', t.id, t.alias) ELSE t.id END as slug'
  697. . ' FROM #__flexicontent_tags AS t'
  698. . ' JOIN #__flexicontent_tags_item_relations AS i ON i.tid = t.id'
  699. . " WHERE i.itemid IN ('" . implode("','", $cids) . "')"
  700. . ' AND t.published = 1'
  701. . ' ORDER BY t.name'
  702. ;
  703. }
  704. $db->setQuery( $query );
  705. $tags = $db->loadObjectList();
  706. // improve performance by doing a single pass of tags to aggregate them per item
  707. $taglists = array();
  708. foreach ($tags as $tag) {
  709. $taglists[$tag->itemid][] = $tag;
  710. }
  711. return $taglists;
  712. }
  713. /**
  714. * Method to get the categories
  715. *
  716. * @access private
  717. * @return object
  718. * @since 1.5
  719. */
  720. static function _getCategories($items)
  721. {
  722. // This is fix for versioned field of creator in items view when previewing
  723. $versioned_item = count($items)==1 && !empty($items[0]->version_id) && !empty($items[0]->categories);
  724. $db = JFactory::getDBO();
  725. if ($versioned_item) {
  726. $catids = $items[0]->categories;
  727. $query = 'SELECT DISTINCT c.id, c.title, ' . $items[0]->id .' as itemid, '
  728. . ' CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as slug'
  729. . ' FROM #__categories AS c'
  730. . " WHERE c.id IN ('" . implode("','", $catids) . "')"
  731. ;
  732. } else {
  733. $cids = array();
  734. foreach ($items as $item) { array_push($cids, $item->id); }
  735. $query = 'SELECT DISTINCT c.id, c.title, rel.itemid,'
  736. . ' CASE WHEN CHAR_LENGTH(c.alias) THEN CONCAT_WS(\':\', c.id, c.alias) ELSE c.id END as slug'
  737. . ' FROM #__categories AS c'
  738. . ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
  739. . " WHERE rel.itemid IN ('" . implode("','", $cids) . "')"
  740. ;
  741. }
  742. $db->setQuery( $query );
  743. $cats = $db->loadObjectList();
  744. // improve performance by doing a single pass of cats to aggregate them per item
  745. $catlists = array();
  746. foreach ($cats as $cat) {
  747. $catlists[$cat->itemid][] = $cat;
  748. }
  749. return $catlists;
  750. }
  751. /**
  752. * Method to get the nr of favourites
  753. *
  754. * @access private
  755. * @return object
  756. * @since 1.5
  757. */
  758. static function _getFavourites($items)
  759. {
  760. $db = JFactory::getDBO();
  761. $cids = array();
  762. foreach ($items as $item) { array_push($cids, $item->id); }
  763. $query = 'SELECT itemid, COUNT(id) AS favs FROM #__flexicontent_favourites'
  764. . " WHERE itemid IN ('" . implode("','", $cids) . "')"
  765. . ' GROUP BY itemid'
  766. ;
  767. $db->setQuery($query);
  768. $favs = $db->loadObjectList('itemid');
  769. return $favs;
  770. }
  771. /**
  772. * Method to get the favourites of an user
  773. *
  774. * @access private
  775. * @return object
  776. * @since 1.5
  777. */
  778. static function _getFavoured($items)
  779. {
  780. $db = JFactory::getDBO();
  781. $cids = array();
  782. foreach ($items as $item) { array_push($cids, $item->id); }
  783. $user = JFactory::getUser();
  784. $query = 'SELECT itemid, COUNT(id) AS fav FROM #__flexicontent_favourites'
  785. . " WHERE itemid IN ('" . implode("','", $cids) . "')"
  786. . " AND userid = '" . ((int)$user->id) ."'"
  787. . ' GROUP BY itemid'
  788. ;
  789. $db->setQuery($query);
  790. $fav = $db->loadObjectList('itemid');
  791. return $fav;
  792. }
  793. /**
  794. * Method to get the modifiers of the items
  795. *
  796. * @access private
  797. * @return object
  798. * @since 1.5
  799. */
  800. static function _getModifiers($items)
  801. {
  802. // This is fix for versioned field of modifier in items view when previewing
  803. $versioned_item = count($items)==1 && !empty($items[0]->version_id) && !empty($items[0]->modified_by);
  804. $db = JFactory::getDBO();
  805. $cids = array();
  806. foreach ($items as $item) { array_push($cids, $item->id); }
  807. $query = 'SELECT i.id, u.name, u.username, u.email FROM #__content AS i'
  808. . ' LEFT JOIN #__users AS u ON ' . ( $versioned_item ? 'u.id = '.$items[0]->modified_by : 'u.id = i.modified_by' )
  809. . " WHERE i.id IN ('" . implode("','", $cids) . "')"
  810. ;
  811. $db->setQuery($query);
  812. $modifiers = $db->loadObjectList('id');
  813. return $modifiers;
  814. }
  815. /**
  816. * Method to get the authors of the items
  817. *
  818. * @access private
  819. * @return object
  820. * @since 1.5
  821. */
  822. static function _getAuthors($items)
  823. {
  824. // This is fix for versioned field of creator in items view when previewing
  825. $versioned_item = count($items)==1 && !empty($items[0]->version_id) && !empty($items[0]->created_by);
  826. $db = JFactory::getDBO();
  827. $cids = array();
  828. foreach ($items as $item) { array_push($cids, $item->id); }
  829. $query = 'SELECT i.id, u.name, i.created_by_alias as alias, u.username, u.email FROM #__content AS i'
  830. . ' LEFT JOIN #__users AS u ON ' . ( $versioned_item ? 'u.id = '.$items[0]->created_by : 'u.id = i.created_by' )
  831. . " WHERE i.id IN ('" . implode("','", $cids) . "')"
  832. ;
  833. $db->setQuery($query);
  834. $authors = $db->loadObjectList('id');
  835. return $authors;
  836. }
  837. /**
  838. * Method to get the types names of the items
  839. *
  840. * @access private
  841. * @return object
  842. * @since 1.5
  843. */
  844. static function _getTypenames($items)
  845. {
  846. $db = JFactory::getDBO();
  847. $type_ids = array();
  848. foreach ($items as $item) { $type_ids[$item->type_id]=1; }
  849. $type_ids = array_keys($type_ids);
  850. $query = 'SELECT id, name FROM #__flexicontent_types'
  851. . " WHERE id IN ('" . implode("','", $type_ids) . "')"
  852. ;
  853. $db->setQuery($query);
  854. $types = $db->loadObjectList('id');
  855. $typenames = array();
  856. foreach ($items as $item) {
  857. $typenames[$item->id] = new stdClass();
  858. $typenames[$item->id]->name = $types[$item->type_id]->name;
  859. }
  860. return $typenames;
  861. }
  862. /**
  863. * Method to get the votes of the items
  864. *
  865. * @access private
  866. * @return object
  867. * @since 1.5
  868. */
  869. static function _getVotes($items)
  870. {
  871. $db = JFactory::getDBO();
  872. $cids = array();
  873. foreach ($items as $item) { array_push($cids, $item->id); }
  874. $query = 'SELECT * FROM #__content_rating'
  875. . " WHERE content_id IN ('" . implode("','", $cids) . "')"
  876. ;
  877. $db->setQuery($query);
  878. $votes = $db->loadObjectList('content_id');
  879. $query = 'SELECT *, field_id as extra_id FROM #__flexicontent_items_extravote'
  880. . " WHERE content_id IN ('" . implode("','", $cids) . "')"
  881. ;
  882. $db->setQuery($query);
  883. $extra_votes= $db->loadObjectList();
  884. // Assign each item 's extra votes to the item's votes as member variable "extra"
  885. foreach ($extra_votes as $extra_vote ) {
  886. $votes[$extra_vote->content_id]->extra[$extra_vote->extra_id] = $extra_vote;
  887. }
  888. return $votes;
  889. }
  890. // ***********************************************************
  891. // Methods for creating field configuration in an OPTMIZED way
  892. // ***********************************************************
  893. // Method to create field parameters in an optimized way, and also apply Type Customization for CORE fields
  894. static function loadFieldConfig(&$field, &$item, $name='', $field_type='', $label='', $desc='', $iscore=1) {
  895. $db = JFactory::getDBO();
  896. static $tparams = array();
  897. static $tinfo = array();
  898. static $fdata = array();
  899. static $no_typeparams = null;
  900. if ($no_typeparams) $no_typeparams = FLEXI_J16GE ? new JRegistry() : new JParameter("");
  901. static $is_form=null;
  902. if ($is_form===null) $is_form = JRequest::getVar('task')=='edit' && JRequest::getVar('option')=='com_flexicontent';
  903. // Create basic field data if no field given
  904. if (!empty($name)) {
  905. $field->iscore = $iscore; $field->name = $name; $field->field_type = $field_type; $field->label = $label; $field->description = $desc; $field->attribs = '';
  906. }
  907. // Get Content Type parameters if not already retrieved
  908. $type_id = @$item->type_id;
  909. if ($type_id && ( !isset($tinfo[$type_id]) || !isset($tparams[$type_id]) ) )
  910. {
  911. $tinfo[$type_id] = $tparams[$type_id] = null;
  912. FlexicontentFields::_getTypeToCoreFieldParams ($type_id, $tinfo[$type_id], $tparams[$type_id]);
  913. }
  914. // Set Content Type parameters otherwise set empty defaults (e.g. new item form with not typeid set)
  915. $type_data_exist = $type_id && $tinfo[$type_id] && $tparams[$type_id] ;
  916. $typename = $type_data_exist ? $tinfo[$type_id]['typename'] : '';
  917. $typealias = $type_data_exist ? $tinfo[$type_id]['typealias'] : '';
  918. $tindex = $type_data_exist ? $typename.'_'.$typealias : 'no_type';
  919. if ($type_data_exist) $typeparams = & $tparams[$type_id]; else $typeparams = & $no_typeparams;
  920. // Create the (CREATED ONCE per field) SHARED object that will contain: (a) label, (b) description, (c) all (merged) field parameters
  921. // Create parameters once per custom field OR once per pair of: Core FIELD type - Item CONTENT type
  922. if ( !isset($fdata[$tindex][$field->name]) ) {
  923. if ( !$field->iscore || !$type_id ) {
  924. // CUSTOM field or CORE field with no type
  925. $fdata[$tindex][$field->name] = new stdClass();
  926. $fdata[$tindex][$field->name]->parameters = FLEXI_J16GE ? new JRegistry($field->attribs) : new JParameter($field->attribs);
  927. } else {
  928. $pn_prefix = $field->field_type!='maintext' ? $field->name : $field->field_type;
  929. // Initialize an empty object, and create parameters object of the field
  930. $fdata[$tindex][$field->name] = new stdClass();
  931. $fdata[$tindex][$field->name]->parameters = FLEXI_J16GE ? new JRegistry($field->attribs) : new JParameter($field->attribs);
  932. // SET a type specific label, description for the current CORE field (according to current language)
  933. $field_label_type = $tparams[$type_id]->get($pn_prefix.'_label', '');
  934. $field_desc_type = $tparams[$type_id]->get($pn_prefix.($is_form ? '_desc' : '_viewdesc'), '');
  935. FlexicontentFields::_getLangSpecificValue ($type_id, $field_label_type, 'label', $fdata[$tindex][$field->name]);
  936. FlexicontentFields::_getLangSpecificValue ($type_id, $field_desc_type, 'description', $fdata[$tindex][$field->name]);
  937. // Override field parameters with Type specific Parameters
  938. if ( isset($tinfo[$type_id]['params'][$pn_prefix]) ) {
  939. foreach ($tinfo[$type_id]['params'][$pn_prefix] as $param_name => $param_value) {
  940. $fdata[$tindex][$field->name]->parameters->set( $param_name, $param_value) ;
  941. }
  942. }
  943. // SPECIAL CASE: check if it exists a FAKE (custom) field that customizes CORE field per Content Type
  944. $query = "SELECT attribs, published FROM #__flexicontent_fields WHERE name=".$db->Quote($field->name."_".$typealias);
  945. $db->setQuery($query); //echo $query;
  946. $data = $db->loadObject(); //print_r($data);
  947. if (@$data->published) JFactory::getApplication()->enqueueMessage(__FUNCTION__."(): Please unpublish plugin with name: ".$field->name."_".$typealias." it is used for customizing a core field",'error');
  948. // Finally merge custom field parameters with the type specific parameters ones
  949. if ($data) {
  950. $ts_params = FLEXI_J16GE ? new JRegistry($data->attribs) : new JParameter($data->attribs);
  951. $fdata[$tindex][$field->name]->parameters->merge($ts_params);
  952. }
  953. }
  954. }
  955. // Set custom label, description or maintain default
  956. $field->label = isset($fdata[$tindex][$field->name]->label) ? $fdata[$tindex][$field->name]->label : $field->label;
  957. $field->description = isset($fdata[$tindex][$field->name]->description) ? $fdata[$tindex][$field->name]->description : $field->description;
  958. $field->label = JText::_($field->label);
  959. // Finally set field's parameters, but to clone ... or not to clone, better clone to allow customizations for individual item fields ...
  960. $field->parameters = clone($fdata[$tindex][$field->name]->parameters);
  961. return $field;
  962. }
  963. // Method to override PARAMETER VALUES with their Type Specific values
  964. static function _getTypeToCoreFieldParams ($type_id, & $tinfo, & $tparams) {
  965. static $core_field_names = null;
  966. if ( $core_field_names == null ) {
  967. $query = "SELECT field_type FROM #__flexicontent_fields WHERE iscore=1";
  968. //echo $query;
  969. $db = JFactory::getDBO();
  970. $db->setQuery($query);
  971. $core_field_names = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  972. $core_field_names[] = 'maintext';
  973. $core_field_names = array_flip($core_field_names);
  974. unset($core_field_names['text']);
  975. }
  976. $query = 'SELECT t.attribs, t.name, t.alias FROM #__flexicontent_types AS t WHERE t.id = ' . $type_id;
  977. $db = JFactory::getDBO();
  978. $db->setQuery($query);
  979. $typedata = $db->loadObject();
  980. if ( $typedata ) {
  981. $tinfo['typename'] = $typedata->name;
  982. $tinfo['typealias'] = $typedata->alias;
  983. $tparams = FLEXI_J16GE ? new JRegistry($typedata->attribs) : new JParameter($typedata->attribs);
  984. $_tparams = $tparams->toArray();
  985. $tinfo['params'] = array();
  986. // Extra voting parameters of --voting-- field parameters are overriden contiditionally
  987. if ( $tparams->get('voting_override_extra_votes', 0) ) {
  988. $tinfo['params']['voting']['voting_extra_votes'] = $tparams->get('voting_extra_votes', '');
  989. $main_label = $tparams->get('voting_main_label', '') ? $tparams->get('voting_main_label', '') : JText::_('FLEXI_OVERALL'); // Set default label in case of empty
  990. $tinfo['params']['voting']['main_label'] = $main_label;
  991. }
  992. foreach ($_tparams as $param_name => $param_value) {
  993. $res = preg_split('/_/', $param_name, 2);
  994. if ( count($res) < 2 ) continue;
  995. $o_field_type = $res[0]; $o_param_name = $res[1];
  996. if ( !isset($core_field_names[$o_field_type]) ) continue;
  997. //echo "$o_field_type _ $o_param_name = $param_value <br>\n";
  998. $skipparam = false;
  999. if ( strlen($param_value) ) {
  1000. if ($o_field_type=='voting') {
  1001. $skipparam = in_array($o_param_name, array('override_extra_votes','voting_extra_votes','voting_main_label'));
  1002. } else if ( in_array($o_param_name, array('label','desc','viewdesc')) ) {
  1003. $skipparam = true;
  1004. }
  1005. if (!$skipparam) {
  1006. $tinfo['params'][$o_field_type][$o_param_name] = $param_value;
  1007. //echo "$o_field_type _ $o_param_name = $param_value <br>\n";
  1008. }
  1009. }
  1010. }
  1011. //echo "<pre>"; print_r($tinfo['params']); echo "</pre>";
  1012. }
  1013. }
  1014. // Method get a language specific value from given Content Type (or other) Data
  1015. static function _getLangSpecificValue ($type_id, $type_prop_value, $prop_name, & $fdata)
  1016. {
  1017. //--. Get a 2 character language tag
  1018. static $lang = null;
  1019. $cntLang = substr(JFactory::getLanguage()->getTag(), 0,2); // Current Content language (Can be natively switched in J2.5)
  1020. $urlLang = JRequest::getWord('lang', '' ); // Language from URL (Can be switched via Joomfish in J1.5)
  1021. $lang = (FLEXI_J16GE || empty($urlLang)) ? $cntLang : $urlLang;
  1022. // --. SET a type specific label for the current field
  1023. // a. Try field label to get for current language
  1024. $result = preg_match("/(\[$lang\])=([^[]+)/i", $type_prop_value, $matches);
  1025. if ($result) {
  1026. $fdata->{$prop_name} = $matches[2];
  1027. } else if ($type_prop_value) {
  1028. // b. Try to get default for all languages
  1029. $result = preg_match("/(\[default\])=([^[]+)/i", $type_prop_value, $matches);
  1030. if ($result) {
  1031. $fdata->{$prop_name} = $matches[2];
  1032. } else {
  1033. // c. Check that no languages specific string are defined
  1034. $result = preg_match("/(\[??\])=([^[]+)/i", $type_prop_value, $matches);
  1035. if (!$result) {
  1036. $fdata->{$prop_name} = $type_prop_value;
  1037. }
  1038. }
  1039. } else {
  1040. // d. Maintain field 's default label
  1041. }
  1042. }
  1043. // **************************************************************************************
  1044. // Methods (a) for INDEXED FIELDs, and (b) for replacement field values / item properties
  1045. // **************************************************************************************
  1046. // Common method to get the allowed element values (field values with index,label,... properties) for fields that use indexed values
  1047. static function indexedField_getElements(&$field, $item, $extra_props=array(), &$item_pros=true, $create_filter=false)
  1048. {
  1049. static $_elements_cache = null;
  1050. if ( isset($_elements_cache[$field->id]) ) return $_elements_cache[$field->id];
  1051. $canCache = true;
  1052. $sql_mode = $field->parameters->get( 'sql_mode', 0 ) ; // For fields that use this parameter
  1053. $field_elements = $field->parameters->get( 'field_elements', '' ) ;
  1054. if ($create_filter) {
  1055. $filter_customize_options = $field->parameters->get('filter_customize_options', 0);
  1056. $filter_custom_options = $field->parameters->get('filter_custom_options', '');
  1057. if ( $filter_customize_options && $filter_custom_options) {
  1058. // Custom query for value retrieval
  1059. $sql_mode = $filter_customize_options==1;
  1060. $field_elements = $filter_custom_options;
  1061. } else if ( !$field_elements ) {
  1062. $field_elements = "SELECT value, value as text FROM #__flexicontent_fields_item_relations as fir WHERE field_id='{field_id}' AND value != '' GROUP BY value";
  1063. }
  1064. // Set parameters may be used later
  1065. $field->parameters->set('sql_mode', $sql_mode);
  1066. $field->parameters->set('field_elements', $field_elements);
  1067. }
  1068. // TODO: examine this in combination with canCache
  1069. //$field_elements = FlexicontentFields::replaceFieldValue( $field, $item, $field_elements, 'field_elements' );
  1070. if ($sql_mode) { // SQL mode, parameter field_elements contains an SQL query
  1071. $db = JFactory::getDBO();
  1072. // Get/verify query string, check if item properties and other replacements are allowed and replace them
  1073. $query = preg_match('#^select#i', $field_elements) ? $field_elements : '';
  1074. $query = FlexicontentFields::doQueryReplacements($field_elements, $field, $item, $item_pros, $canCache);
  1075. // Execute SQL query to retrieve the field value - label pair, and any other extra properties
  1076. if ( $query ) {
  1077. $db->setQuery($query);
  1078. $results = $db->loadObjectList('value');
  1079. }
  1080. // !! CHECK: DB query failed or produced no data
  1081. if (!$query || !is_array($results)) {
  1082. if ( !$canCache ) return false;
  1083. else return ($_elements_cache[$field->id] = false);
  1084. }
  1085. } else { // Elements mode, parameter field_elements contain list of allowed values
  1086. // Parse the elements used by field unsetting last element if empty
  1087. $listelements = preg_split("/[\s]*%%[\s]*/", $field_elements);
  1088. if ( empty($listelements[count($listelements)-1]) ) {
  1089. unset($listelements[count($listelements)-1]);
  1090. }
  1091. $props_needed = 2 + count($extra_props);
  1092. // Split elements into their properties: value, label, extra_prop1, extra_prop2
  1093. $listarrays = array();
  1094. $results = array();
  1095. foreach ($listelements as $listelement) {
  1096. $listelement_props = preg_split("/[\s]*::[\s]*/", $listelement);
  1097. if (count($listelement_props) < $props_needed) {
  1098. echo "Error in field: ".$field->label." while splitting element: ".$listelement." properties needed: ".$props_needed." properties found: ".count($listelement_props);
  1099. return ($_elements_cache[$field->id] = false);
  1100. }
  1101. $val = $listelement_props[0];
  1102. $results[$val] = new stdClass();
  1103. $results[$val]->value = $listelement_props[0];
  1104. $results[$val]->text = JText::_($listelement_props[1]); // the text label
  1105. $el_prop_count = 2;
  1106. foreach ($extra_props as $extra_prop) {
  1107. $results[$val]->{$extra_prop} = @ $listelement_props[$el_prop_count]; // extra property for fields that use it
  1108. $el_prop_count++;
  1109. }
  1110. }
  1111. }
  1112. // Return found elements, caching them if possible (if no item specific elements are used)
  1113. if ( $canCache ) $_elements_cache[$field->id] = & $results;
  1114. return $results;
  1115. }
  1116. // Common method to map element value INDEXES to value objects for fields that use indexed values
  1117. static function indexedField_getValues(&$field, &$elements, $value_indexes, $prepost_prop='text')
  1118. {
  1119. // Check for empty values
  1120. if ( !is_array($value_indexes) && !strlen($value_indexes) ) return array();
  1121. // Make sure indexes is an array
  1122. $value_indexes = !is_array($value_indexes) ? array($value_indexes) : $value_indexes;
  1123. $pretext=''; $posttext='';
  1124. if ( $prepost_prop ) {
  1125. $pretext = $field->parameters->get( 'pretext', '' ) ;
  1126. $posttext = $field->parameters->get( 'posttext', '' ) ;
  1127. $remove_space = $field->parameters->get( 'remove_space', 0 ) ;
  1128. $pretext = $remove_space ? $pretext : $pretext . ' ';
  1129. $posttext = $remove_space ? $posttext : ' ' . $posttext;
  1130. }
  1131. // Get the labels of used values in an display[] array
  1132. $values = array();
  1133. foreach($value_indexes as $val_index) {
  1134. if ( !strlen($val_index) ) continue;
  1135. if ( !isset($elements[$val_index]) ) continue;
  1136. $values[$val_index] = get_object_vars($elements[$val_index] );
  1137. //print_r($values[$val_index]); echo "<br/>\n";
  1138. if ($prepost_prop) {
  1139. $values[$val_index][$prepost_prop] = $pretext . $values[$val_index][$prepost_prop] . $posttext;
  1140. }
  1141. }
  1142. return $values;
  1143. }
  1144. // Helper method to replace item properties for the SQL value mode for various fields
  1145. static function doQueryReplacements(&$query, &$field, &$item, &$item_pros=true, $canCache=null)
  1146. {
  1147. // replace item properties
  1148. preg_match_all("/{item->[^}]+}/", $query, $matches);
  1149. $canCache = count($matches[0]) == 0;
  1150. if ( !$item_pros && count($matches[0]) ) { $item_pros = count($matches[0]); return ''; }
  1151. // If needed replace item properties, loading the item if not already loaded
  1152. if (count($matches[0]) && !$item) {
  1153. if ( !@$field->item_id ) { echo __FUNCTION__."(): field->item_id is not set"; return; }
  1154. $item = JTable::getInstance( $type = 'flexicontent_items', $prefix = '', $config = array() );
  1155. $item->load( $field->item_id );
  1156. }
  1157. foreach ($matches[0] as $replacement_tag) {
  1158. $replacement_value = '$'.substr($replacement_tag, 1, -1);
  1159. eval ("\$replacement_value = \"$replacement_value\";");
  1160. $query = str_replace($replacement_tag, $replacement_value, $query);
  1161. }
  1162. // replace field properties
  1163. if ($field) {
  1164. preg_match_all("/{field->[^}]+}/", $query, $matches);
  1165. foreach ($matches[0] as $replacement_tag) {
  1166. $replacement_value = '$'.substr($replacement_tag, 1, -1);
  1167. eval ("\$replacement_value = \" $replacement_value\";");
  1168. $query = str_replace($replacement_tag, $replacement_value, $query);
  1169. }
  1170. }
  1171. // replace current user language
  1172. $query = str_replace("{curr_userlang_shorttag}", flexicontent_html::getUserCurrentLang(), $query);
  1173. $query = str_replace("{curr_userlang_fulltag}", flexicontent_html::getUserCurrentLang(), $query);
  1174. return $query;
  1175. }
  1176. // Helper method to replace a field value inside a given named variable of a given item/field pair
  1177. static function replaceFieldValue( &$field, &$item, $variable, $varname, & $cacheable = false )
  1178. {
  1179. static $parsed = array();
  1180. static $d;
  1181. static $c;
  1182. // Parse field variable if not already parsed
  1183. if ( !isset($parsed[$field->id][$varname]) )
  1184. {
  1185. $parsed[$field->id][$varname] = true;
  1186. $result = preg_match_all("/\{\{([a-zA-Z_0-9]+)(##)?([0-9]+)?(##)?([a-zA-Z_0-9]+)?\}\}/", $variable, $field_matches);
  1187. if ($result) {
  1188. $d[$field->id][$varname]['fulltxt'] = $field_matches[0];
  1189. $d[$field->id][$varname]['fieldname'] = $field_matches[1];
  1190. $d[$field->id][$varname]['valueno'] = $field_matches[3];
  1191. $d[$field->id][$varname]['propname'] = $field_matches[5];
  1192. } else {
  1193. $d[$field->id][$varname]['fulltxt'] = array();
  1194. }
  1195. $result = preg_match_all("/\{\{(item->)([a-zA-Z_0-9]+)\}\}/", $variable, $field_matches);
  1196. if ($result) {
  1197. $c[$field->id][$varname]['fulltxt'] = $field_matches[0];
  1198. $c[$field->id][$varname]['propname'] = $field_matches[2];
  1199. } else {
  1200. $c[$field->id][$varname]['fulltxt'] = array();
  1201. }
  1202. if ( !count($d[$field->id][$varname]['fulltxt']) && !count($c[$field->id][$varname]['fulltxt']) ) {
  1203. $cacheable = true;
  1204. }
  1205. }
  1206. // Replace variable
  1207. foreach($d[$field->id][$varname]['fulltxt'] as $i => $fulltxt)
  1208. {
  1209. $fieldname = $d[$field->id][$varname]['fieldname'][$i];
  1210. $valueno = $d[$field->id][$varname]['valueno'][$i] ? (int) $d[$field->id][$varname]['valueno'][$i] : 0;
  1211. $propname = $d[$field->id][$varname]['propname'][$i] ? $d[$field->id][$varname]['propname'][$i] : '';
  1212. $fieldid = @ $item->fields[$fieldname]->id;
  1213. $value = @ $item->fieldvalues[$fieldid][$valueno];
  1214. if ( !$fieldid ) {
  1215. $value = 'Field with name: '.$fieldname.' not found';
  1216. $variable = str_replace($fulltxt, $value, $variable);
  1217. continue;
  1218. }
  1219. $is_indexable = $propname && preg_match("/^_([a-zA-Z_0-9]+)_$/", $propname, $prop_matches) && ($propname = $prop_matches[1]);
  1220. if ( $is_indexable )
  1221. {
  1222. if ( $propname!='value' ) // no need for value to retrieve custom elements
  1223. {
  1224. $extra_props = $propname!='text' ? array($propname) : array(); // this will work only if field has a single extra property
  1225. $extra_props = array();
  1226. if ( !isset($item->fields[$fieldname]->parameters) ) {
  1227. FlexicontentFields::loadFieldConfig($item->fields[$fieldname], $item);
  1228. }
  1229. $elements = FlexicontentFields::indexedField_getElements( $item->fields[$fieldname], $item, $extra_props );
  1230. $value = @ $elements[$value]->{$propname};
  1231. }
  1232. } else if ( $propname ) {
  1233. $value = @ unserialize ( $value );
  1234. $value = @ $value[$propname];
  1235. }
  1236. $variable = str_replace($fulltxt, $value, $variable);
  1237. //echo "<pre>"; print_r($item->fieldvalues[$fieldid]); echo "</pre>"; echo "Replaced $fulltxt with ITEM field VALUE: $value <br>";
  1238. }
  1239. // Replace variable
  1240. foreach($c[$field->id][$varname]['fulltxt'] as $i => $fulltxt)
  1241. {
  1242. $propname = $c[$field->id][$varname]['propname'][$i];
  1243. if ( !isset($item->{$propname}) ) {
  1244. $value = 'Item property with name: '.$propname.' not found';
  1245. $variable = str_replace($fulltxt, $value, $variable);
  1246. continue;
  1247. }
  1248. $value = $item->{$propname};
  1249. $variable = str_replace($fulltxt, $value, $variable);
  1250. //echo "<pre>"; echo "</pre>"; echo "Replaced $fulltxt with ITEM property VALUE: $value <br>";
  1251. }
  1252. // Return variable after all replacements
  1253. return $variable;
  1254. }
  1255. // *********************************************************************
  1256. // Methods for getting fields that support BASIC / ADVANCED search modes
  1257. // *********************************************************************
  1258. // Method to get various - SETs - of search fields, according to given limitations
  1259. // Param 'search_type' : search, filter, all-search, dirty-search, dirty-nosupport, non-search
  1260. static function getSearchFields($key='name', $indexer='advanced', $search_fields=null, $content_types=null, $load_params=true, $item_id=0, $search_type='all-search')
  1261. {
  1262. $db = JFactory::getDBO();
  1263. if ($search_type=='search') { // All fields marked as text-searchable (also are published)
  1264. $where = $indexer=='basic' ? ' f.issearch IN (1,2)' : ' f.isadvsearch IN (1,2) ';
  1265. $where = '('.$where.' AND f.published = 1)';
  1266. } else if ($search_type=='filter') { // All fields marked as filterable (also are published)
  1267. $where = $indexer=='basic' ? ' f.isfilter IN (1,2)' : ' f.isadvfilter IN (1,2) ';
  1268. $where = '('.$where.' AND f.published = 1)';
  1269. } else if ($search_type=='all-search') { // ALL fields that must enter values in search index (also are published)
  1270. $where = $indexer=='basic' ? ' f.issearch IN (1,2)' : ' ( f.isadvsearch IN (1,2) OR f.isadvfilter IN (1,2) )';
  1271. $where = '('.$where.' AND f.published = 1)';
  1272. } else if ($search_type=='dirty-search' || $search_type=='dirty-nosupport') { // ONLY 'dirty' search fields (also are published)
  1273. $where = $indexer=='basic' ? ' f.issearch = 2' : ' ( f.isadvsearch = 2 OR f.isadvfilter = 2 )';
  1274. $where = '('.$where.' AND f.published = 1)';
  1275. } else if ($search_type=='non-search') { // ALL non-search fields (either OFF or unpublished)
  1276. $where = $indexer=='basic' ? ' f.issearch IN (-1,0)' : ' ( f.isadvsearch IN (-1,0) AND f.isadvfilter IN (-1,0) )';
  1277. $where = '('.$where.' OR f.published = 0)';
  1278. } else {
  1279. die(__FUNCTION__ . "(): unknown value for 'search_type' parameter"); // nothing to TODO
  1280. }
  1281. $where .=
  1282. (!empty($search_fields) && is_array($search_fields) ? " AND f.name IN (".implode(',', $search_fields).") " : ""). // Limit to given search fields list
  1283. (!empty($search_fields) && is_string($search_fields) ? " AND f.name IN (".$search_fields.") " : ""). // Limit to given search fields list
  1284. (!empty($content_types) ? " AND ftr.type_id IN (".implode(',', $content_types).") " : "") // Limit to given contnt types list
  1285. ;
  1286. $query = 'SELECT f.*'
  1287. .' FROM #__flexicontent_fields AS f'
  1288. .' JOIN #__flexicontent_fields_type_relations AS ftr ON ftr.field_id = f.id'
  1289. .' WHERE '. $where
  1290. .' GROUP BY f.id'
  1291. .' ORDER BY f.ordering, f.name'
  1292. ;
  1293. $db->setQuery($query);
  1294. $fields = $db->loadObjectList($key);
  1295. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  1296. $sp_fields = array();
  1297. $nsp_fields = array();
  1298. foreach ($fields as $field_id => $field)
  1299. {
  1300. // Skip fields not being capable of advanced/basic search
  1301. if ( $indexer=='basic' ) {
  1302. if ( ! FlexicontentFields::getPropertySupport($field->field_type, $field->iscore, $search_type=='filter' ? 'supportfilter' : 'supportsearch') ) {
  1303. $nsp_fields[$field_id] = $field;
  1304. continue;
  1305. }
  1306. } else if ($search_type != 'non-search') {
  1307. $no_supportadvsearch = ! FlexicontentFields::getPropertySupport($field->field_type, $field->iscore, 'supportadvsearch');
  1308. $no_supportadvfilter = ! FlexicontentFields::getPropertySupport($field->field_type, $field->iscore, 'supportadvfilter');
  1309. $skip_field = ($no_supportadvsearch && $search_type=='search') || ($no_supportadvfilter && $search_type=='filter') ||
  1310. ($no_supportadvsearch && $no_supportadvfilter && in_array($search_type, array('all-search', 'dirty-nosupport') ) );
  1311. if ($skip_field) {
  1312. $nsp_fields[$field_id] = $field;
  1313. continue;
  1314. }
  1315. }
  1316. $field->item_id = $item_id;
  1317. $field->value = !$item_id ? false : $this->getExtrafieldvalue($field->id, $version=0, $item_id); // WARNING: getExtrafieldvalue() is Frontend method
  1318. if ($load_params) $field->parameters = FLEXI_J16GE ? new JRegistry($field->attribs) : new JParameter($field->attribs);
  1319. $sp_fields[$field_id] = $field;
  1320. }
  1321. if ($indexer=='advanced' && $search_type=='dirty-nosupport')
  1322. return $nsp_fields;
  1323. else
  1324. return $sp_fields;
  1325. }
  1326. // Method to get properties support for CORE fields
  1327. static function getPropertySupport_BuiltIn()
  1328. {
  1329. static $info = null;
  1330. if ($info!==null) return $info;
  1331. $info = new stdClass();
  1332. $info->core_search= array('title', 'maintext', 'tags', 'categories' // CORE fields as text searchable
  1333. , 'created', 'modified', 'createdby','modifiedby', 'type'
  1334. );
  1335. $info->core_filters = array('tags', 'categories' // CORE fields as filters
  1336. , 'created', 'modified', 'createdby', 'modifiedby', 'type', 'state'
  1337. );
  1338. $info->core_advsearch = array('title', 'maintext', 'tags', 'categories' // CORE fields as text searchable for search view
  1339. , 'created', 'modified', 'createdby','modifiedby', 'type'
  1340. );
  1341. $info->core_advfilters = array('title', 'maintext', 'tags', 'categories' // CORE fields as filters for search view
  1342. , 'created', 'modified', 'createdby', 'modifiedby', 'type', 'state'
  1343. );
  1344. $info->indexable_fields = array('categories', 'tags', 'type', 'select', 'selectmultiple', 'checkbox', 'checkboximage', 'radio', 'radioimage');
  1345. return $info;
  1346. }
  1347. // Method to get used the properties supported by given field_type
  1348. static function getPropertySupport($field_type, $iscore, $spname=null)
  1349. {
  1350. static $fi = null;
  1351. if ($fi === null) $fi = FlexicontentFields::getPropertySupport_BuiltIn();
  1352. static $cparams = null;
  1353. if ($cparams === null) $cparams = JComponentHelper::getParams( 'com_flexicontent' );
  1354. static $support_ft = array();
  1355. if ( isset( $support_ft[$field_type] ) ) return !$spname ? $support_ft[$field_type] : $support_ft[$field_type]->{$spname};
  1356. // Existing fields with field type
  1357. if ($field_type)
  1358. {
  1359. // Make sure that the Joomla plugin that implements the type of current flexi field, has been imported
  1360. //JPluginHelper::importPlugin('flexicontent_fields', $field_type);
  1361. FLEXIUtilities::call_FC_Field_Func($iscore ? 'core' : $field_type, null, null);
  1362. // Get Methods implemented by the field
  1363. $classname = 'plgFlexicontent_fields'.($iscore ? 'core' : $field_type);
  1364. $classmethods = get_class_methods($classname);
  1365. // SEARCH/FILTER related properties
  1366. $supportsearch = $iscore ? in_array($field_type, $fi->core_search) : in_array('onIndexSearch', $classmethods);
  1367. $supportfilter = $iscore ? in_array($field_type, $fi->core_filters) : in_array('onDisplayFilter', $classmethods);
  1368. $supportadvsearch = $iscore ? in_array($field_type, $fi->core_advsearch) : in_array('onIndexAdvSearch', $classmethods);
  1369. $supportadvfilter = $iscore ? in_array($field_type, $fi->core_advfilters) : in_array('onAdvSearchDisplayFilter', $classmethods);
  1370. // ITEM FORM related properties
  1371. $supportuntranslatable = !$iscore || $field_type=='maintext';
  1372. $supportvalueseditable = !$iscore || $field_type=='maintext';
  1373. $supportformhidden = !$iscore || $field_type=='maintext';
  1374. $supportedithelp = !$iscore || $field_type=='maintext';
  1375. // New fields without field type
  1376. } else {
  1377. // SEARCH/FILTER related properties
  1378. $supportsearch = false;
  1379. $supportfilter = false;
  1380. $supportadvsearch = false;
  1381. $supportadvfilter = false;
  1382. // ITEM FORM related properties
  1383. $supportuntranslatable = !$iscore;
  1384. $supportvalueseditable = !$iscore;
  1385. $supportformhidden = !$iscore;
  1386. $supportedithelp = !$iscore;
  1387. }
  1388. // This property is usable only when Translation Groups are enabled
  1389. $supportuntranslatable = $supportuntranslatable && $cparams->get('enable_translation_groups');
  1390. $support_ft[$field_type] = new stdClass();
  1391. $support_ft[$field_type]->supportsearch = $supportsearch;
  1392. $support_ft[$field_type]->supportfilter = $supportfilter;
  1393. $support_ft[$field_type]->supportadvsearch = $supportadvsearch;
  1394. $support_ft[$field_type]->supportadvfilter = $supportadvfilter;
  1395. $support_ft[$field_type]->supportuntranslatable = $supportuntranslatable;
  1396. $support_ft[$field_type]->supportvalueseditable = $supportvalueseditable;
  1397. $support_ft[$field_type]->supportformhidden = $supportformhidden;
  1398. $support_ft[$field_type]->supportedithelp = $supportedithelp;
  1399. return !$spname ? $support_ft[$field_type] : $support_ft[$field_type]->{$spname};
  1400. }
  1401. // *****************************************************************************
  1402. // Common methods for - populating - the BASIC and ADVANCED search INDEX records
  1403. // *****************************************************************************
  1404. // Common method to create (insert) advanced search index DB records for various fields,
  1405. // this can be called by fields or copied inside the field to allow better customization
  1406. static function onIndexAdvSearch(&$field, &$values, &$item, $required_props=array(), $search_props=array(), $props_spacer=' ', $filter_func=null) {
  1407. FlexicontentFields::createIndexRecords($field, $values, $item, $required_props, $search_props, $props_spacer, $filter_func, $for_advsearch=1);
  1408. }
  1409. // Common method to create basic text search index for various fields (added as the property field->search),
  1410. // this can be called by fields or copied inside the field to allow better customization
  1411. static function onIndexSearch(&$field, &$values, &$item, $required_props=array(), $search_props=array(), $props_spacer=' ', $filter_func=null) {
  1412. FlexicontentFields::createIndexRecords($field, $values, $item, $required_props, $search_props, $props_spacer, $filter_func, $for_advsearch=0);
  1413. }
  1414. // Common method to create basic/advanced search index for various fields
  1415. static function createIndexRecords(&$field, &$values, &$item, $required_props=array(), $search_props=array(), $props_spacer=' ', $filter_func=null, $for_advsearch=0) {
  1416. $fi = FlexicontentFields::getPropertySupport($field->field_type, $field->iscore);
  1417. $db = JFactory::getDBO();
  1418. if ( !$for_advsearch )
  1419. {
  1420. // Check if field type supports text search, this will also skip fields wrongly marked as text searchable
  1421. if ( !$fi->supportsearch || !$field->issearch ) {
  1422. $field->search = array();
  1423. return;
  1424. }
  1425. }
  1426. else {
  1427. $field->ai_query_vals = array();
  1428. // Check if field type supports advanced search text searchable or filterable, this will also skip fields wrongly marked
  1429. if ( !($fi->supportadvsearch && $field->isadvsearch) && !($fi->supportadvfilter && $field->isadvfilter) )
  1430. return;
  1431. }
  1432. // A null indicates to retrieve values
  1433. if ($values===null) {
  1434. $items_values = FlexicontentFields::searchIndex_getFieldValues($field,$item, $for_advsearch);
  1435. } else {
  1436. $items_values = !is_array($values) ? array($values) : $values;
  1437. $items_values = array($field->item_id => $items_values);
  1438. }
  1439. // Make sure posted data is an array
  1440. $unserialize = (isset($field->unserialize)) ? $field->unserialize : ( count($required_props) || count($search_props) );
  1441. // Create the new search data
  1442. foreach($items_values as $itemid => $item_values)
  1443. {
  1444. if ( @$field->isindexed ) {
  1445. // Get Elements of the field these will be cached if they do not depend on the item ...
  1446. $field->item_id = $itemid; // in case it needs to be loaded to replace item properties in a SQL query
  1447. $item_pros = false;
  1448. $elements = FlexicontentFields::indexedField_getElements($field, $item, $field->extra_props, $item_pros, $createFilter=true);
  1449. // Map index field vlaues to their real properties
  1450. $item_values = FlexicontentFields::indexedField_getValues($field, $elements, $item_values, $prepost_prop='');
  1451. }
  1452. $searchindex = array();
  1453. foreach($item_values as $vi => $v)
  1454. {
  1455. // Make sure multi-property data are unserialized
  1456. if ($unserialize) {
  1457. $data = @ unserialize($v);
  1458. $v = ($v === 'b:0;' || $data !== false) ? $data : $v;
  1459. }
  1460. // Check value that current should not be included in search index
  1461. if ( !is_array($v) && !strlen($v) ) continue;
  1462. foreach ($required_props as $cp) if (!@$v[$cp]) continue;
  1463. // Create search value
  1464. $search_value = array();
  1465. foreach ($search_props as $sp) {
  1466. if ( isset($v[$sp]) && strlen($v[$sp]) ) $search_value[] = $v[$sp];
  1467. }
  1468. if (count($search_props) && !count($search_value)) continue; // all search properties were empty, skip this value
  1469. $searchindex[$vi] = (count($search_props)) ? implode($props_spacer, $search_value) : $v;
  1470. $searchindex[$vi] = $filter_func ? $filter_func($searchindex[$vi]) : $searchindex[$vi];
  1471. }
  1472. if ( !$for_advsearch )
  1473. {
  1474. $field->search[$itemid] = implode(' | ', $searchindex);
  1475. }
  1476. else {
  1477. $n = 0;
  1478. foreach ($searchindex as $vi => $search_text)
  1479. {
  1480. // Add new search value into the DB
  1481. $query_val = "( "
  1482. .$field->id. "," .$itemid. "," .($n++). "," .$db->Quote($search_text). "," .$db->Quote($vi).
  1483. ")";
  1484. $field->ai_query_vals[] = $query_val;
  1485. }
  1486. }
  1487. }
  1488. //echo $field->name . ": "; print_r($values);echo "<br/>";
  1489. //echo implode(' | ', $searchindex) ."<br/><br/>";
  1490. }
  1491. // Method to retrieve field values to be used for creating search indexes
  1492. static function searchIndex_getFieldValues(&$field, &$item, $for_advsearch=0)
  1493. {
  1494. $db = JFactory::getDBO();
  1495. static $nullDate = null;
  1496. if ($nullDate===null) $nullDate = $db->getNullDate();
  1497. // Create DB query to retrieve field values
  1498. $values = null;
  1499. switch ($field->field_type)
  1500. {
  1501. case 'title':
  1502. $query = 'SELECT i.title AS value, i.id AS itemid'
  1503. .' FROM #__content AS i'
  1504. .' WHERE i.id IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1505. break;
  1506. case 'maintext':
  1507. $query = 'SELECT CONCAT_WS(\' \', i.introtext, i.fulltext) AS value, i.id AS itemid'
  1508. .' FROM #__content AS i'
  1509. .' WHERE i.id IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1510. break;
  1511. case 'categories':
  1512. $query = 'SELECT c.id AS value_id, c.title AS value, rel.itemid AS itemid'
  1513. .' FROM #__categories AS c'
  1514. .' JOIN #__flexicontent_cats_item_relations AS rel ON c.id=rel.catid'
  1515. .' WHERE c.id<>0 AND rel.itemid IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1516. break;
  1517. case 'tags':
  1518. $query = 'SELECT t.id AS value_id, t.name AS value, rel.itemid AS itemid'
  1519. .' FROM #__flexicontent_tags AS t'
  1520. .' JOIN #__flexicontent_tags_item_relations AS rel ON t.id=rel.tid'
  1521. .' WHERE t.id<>0 AND rel.itemid IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1522. break;
  1523. case 'type':
  1524. $textcol = ', t.name AS value';
  1525. $query = ' SELECT t.id AS value_id '.$textcol.', ext.item_id AS itemid'
  1526. . ' FROM #__flexicontent_types AS t'
  1527. .' JOIN #__flexicontent_items_ext AS ext ON t.id=ext.type_id '
  1528. .' WHERE t.id<>0 AND ext.item_id IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1529. break;
  1530. case 'created': case 'modified':
  1531. if (!isset($valuecols[$field->field_type])) {
  1532. $date_filter_group = $field->parameters->get('date_filter_group', 'month');
  1533. if ($date_filter_group=='year') { $date_valformat='%Y'; }
  1534. else if ($date_filter_group=='month') { $date_valformat='%Y-%m'; }
  1535. else { $date_valformat='%Y-%m-%d'; }
  1536. $valuecols[$field->field_type] = sprintf(' DATE_FORMAT(i.%s, "%s") ', $field->field_type, $date_valformat);
  1537. }
  1538. $valuecol = $valuecols[$field->field_type];
  1539. $query = 'SELECT '.$valuecol.' AS value_id, i.id AS itemid'
  1540. .' FROM #__content AS i'
  1541. .' WHERE i.'.$field->name.'<>'.$db->Quote($nullDate).' AND i.id IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1542. break;
  1543. case 'createdby': case 'modifiedby':
  1544. $textcol = ', u.name AS value';
  1545. $query = ' SELECT u.id AS value_id '.$textcol.', i.id AS itemid'
  1546. . ' FROM #__users AS u'
  1547. .' JOIN #__content AS i ON i.'.$field->name.' = u.id'
  1548. .' WHERE u.id<>0 AND i.id IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')';
  1549. break;
  1550. default:
  1551. if ($field->iscore) $values=array(); //die('Field Type: '.$field->field_type.' does not support FLEXIcontent Advanced Search Indexing');
  1552. $valuesselect = @$field->field_valuesselect ? $field->field_valuesselect : ' fi.value AS value ';
  1553. $valuesjoin = @$field->field_valuesjoin ? $field->field_valuesjoin : '';
  1554. $groupby = @$field->field_groupby ? $field->field_groupby .', fi.item_id' : ' GROUP BY fi.value, fi.item_id ';
  1555. $query = 'SELECT '.$valuesselect.', fi.item_id AS itemid'
  1556. .' FROM #__flexicontent_fields_item_relations as fi'
  1557. .' JOIN #__content as i ON i.id=fi.item_id'
  1558. . $valuesjoin
  1559. .' WHERE fi.field_id='.$field->id.' AND fi.item_id IN ('.(@$field->query_itemids ? implode(',', $field->query_itemids) : $field->item_id) .')'
  1560. .$groupby;
  1561. break;
  1562. }
  1563. // Execute query if not already done to load a single value column with no value id
  1564. $_raw = !empty($field->field_rawvalues);
  1565. if ($values === null) {
  1566. $db->setQuery($query);
  1567. $_values = $db->loadObjectList();
  1568. $values = array();
  1569. if ($_values) foreach($_values as $v) {
  1570. if (isset($v->value_id))
  1571. $values[$v->itemid][$v->value_id] = $_raw ? (array) $v : (isset($v->value) ? $v->value : $v->value_id);
  1572. else
  1573. $values[$v->itemid][] = $_raw ? (array) $v : $v->value;
  1574. }
  1575. }
  1576. return $values;
  1577. }
  1578. // ********************************************************************************************
  1579. // Methods for - MATCHING - Field Filters of FC views, (thus limiting the current ITEM LISTING)
  1580. // ********************************************************************************************
  1581. // Private Method to create a generic matching of filter
  1582. static function createFilterValueMatchSQL(&$filter, &$value, $is_full_text=0, $is_search=0)
  1583. {
  1584. $db = JFactory::getDBO();
  1585. $display_filter_as = $filter->parameters->get( $is_search ? 'display_filter_as_s' : 'display_filter_as', 0 );
  1586. $filter_compare_type = $filter->parameters->get( 'filter_compare_type', 0 );
  1587. $require_all = count($value)>1 && !in_array( $display_filter_as, array(1,2,3) ) ?
  1588. $filter->parameters->get( 'filter_values_require_all', 0 ) : 0;
  1589. //echo "createFilterValueMatchSQL : filter name: ".$filter->name." Filter Type: ".$display_filter_as." Values: "; print_r($value); echo "<br>";
  1590. // Make sure the current filtering values match the field filter configuration to be single or multi-value
  1591. if ( in_array($display_filter_as, array(2,3,5,6)) ) {
  1592. if (!is_array($value)) $value = array( $value );
  1593. } else {
  1594. if (is_array($value)) $value = array ( @ $value[0] );
  1595. else $value = array ( $value );
  1596. }
  1597. $_value = array();
  1598. foreach ($value as $i => $v) {
  1599. $v = trim($v);
  1600. if ( strlen($v) ) $_value[$i] = $v;
  1601. }
  1602. $value = $_value;
  1603. if ( !count($value) ) return '';
  1604. // For text input format the strings, if this was requested by the filter
  1605. $istext_input = $display_filter_as==1 || $display_filter_as==3;
  1606. if ($istext_input && isset($filter->filter_valueformat)) {
  1607. foreach($value as $i => $val) {
  1608. if ( !$filter_compare_type ) $typecasted_val = $db->Quote($value[$i]);
  1609. else $typecasted_val = $filter_compare_type==1 ? intval($value[$i]) : floatval($value[$i]);
  1610. $value[$i] = str_replace('__filtervalue__', $typecasted_val, $filter->filter_valueformat);
  1611. }
  1612. $quoted=true;
  1613. }
  1614. $valueswhere = '';
  1615. switch ($display_filter_as) {
  1616. // RANGE cases
  1617. case 2: case 3:
  1618. if ( ! @ $quoted ) foreach($value as $i => $v) {
  1619. if ( !$filter_compare_type ) $value[$i] = $db->Quote($v);
  1620. else $value[$i] = $filter_compare_type==1 ? intval($v) : floatval($v);
  1621. }
  1622. $value_empty = !strlen(@$value[1]) && strlen(@$value[2]) ? ' OR _v_="" OR _v_ IS NULL' : '';
  1623. if ( strlen(@$value[1]) ) $valueswhere .= ' AND (_v_ >=' . $value[1] . ')';
  1624. if ( strlen(@$value[2]) ) $valueswhere .= ' AND (_v_ <=' . $value[2] . $value_empty . ')';
  1625. break;
  1626. // SINGLE TEXT select value cases
  1627. case 1:
  1628. // DO NOT put % in front of the value since this will force a full table scan instead of indexed column scan
  1629. $_value_like = $value[0].($is_full_text ? '*' : '%');
  1630. if (empty($quoted)) $_value_like = $db->Quote($_value_like);
  1631. if ($is_full_text)
  1632. $valueswhere .= ' AND MATCH (_v_) AGAINST ('.$_value_like.' IN BOOLEAN MODE)';
  1633. else
  1634. $valueswhere .= ' AND _v_ LIKE ' . $_value_like;
  1635. break;
  1636. // EXACT value cases
  1637. case 0: case 4: case 5: default:
  1638. $value_clauses = array();
  1639. if ( ! $require_all ) {
  1640. foreach ($value as $val) {
  1641. $value_clauses[] = '_v_=' . $db->Quote( $val );
  1642. }
  1643. $valueswhere .= ' AND ('.implode(' OR ', $value_clauses).') ';
  1644. } else {
  1645. foreach ($value as $val) {
  1646. $value_clauses[] = $db->Quote( $val );
  1647. }
  1648. $valueswhere = ' AND _v_ IN ('. implode(',', $value_clauses) .')';
  1649. }
  1650. break;
  1651. }
  1652. //echo $valueswhere . "<br>";
  1653. return $valueswhere;
  1654. }
  1655. // Method to get the active filter result for Content Lists Views (an SQL where clause part OR an array of item ids, matching field filter)
  1656. static function getFiltered( &$filter, $value, $return_sql=true )
  1657. {
  1658. $db = JFactory::getDBO();
  1659. // Check if field type supports advanced search
  1660. $support = FlexicontentFields::getPropertySupport($filter->field_type, $filter->iscore);
  1661. if ( ! $support->supportfilter ) return null;
  1662. $valueswhere = FlexicontentFields::createFilterValueMatchSQL($filter, $value, $is_full_text=0, $is_search=0);
  1663. if ( !$valueswhere ) { return; }
  1664. $colname = @$filter->filter_colname ? $filter->filter_colname : 'value';
  1665. $valueswhere = str_replace('_v_', $colname, $valueswhere);
  1666. $valuesjoin = @$filter->filter_valuesjoin ? $filter->filter_valuesjoin : ' JOIN #__flexicontent_fields_item_relations rel ON rel.item_id=c.id AND rel.field_id = ' . $filter->id;
  1667. // Decide to require all values
  1668. $display_filter_as = $filter->parameters->get('display_filter_as', 0 );
  1669. $require_all = count($value)>1 && !in_array( $display_filter_as, array(1,2,3) ) ?
  1670. $filter->parameters->get( 'filter_values_require_all', 0 ) : 0;
  1671. if ( @$filter->filter_valuesjoin ) {
  1672. $query = 'SELECT '.($require_all ? 'c.id' : 'DISTINCT c.id')
  1673. .' FROM #__content c'
  1674. .$filter->filter_valuesjoin
  1675. .' WHERE 1'
  1676. . $valueswhere ;
  1677. if ($require_all) {
  1678. // Do not use distinct on column, it makes it is very slow, despite column having an index !!
  1679. // e.g. HAVING COUNT(DISTINCT colname) = ...
  1680. // Instead the field code should make sure that no duplicate values are saved in the DB !!
  1681. $query .= ' GROUP BY c.id ' .' HAVING COUNT(*) >= '.count($value);
  1682. }
  1683. } else {
  1684. $query = 'SELECT '.($require_all ? 'rel.item_id' : 'DISTINCT rel.item_id')
  1685. .' FROM #__flexicontent_fields_item_relations as rel'
  1686. .' WHERE rel.field_id = ' . $filter->id
  1687. . $valueswhere ;
  1688. if ($require_all) {
  1689. // Do not use distinct on column, it makes it is very slow, despite column having an index !!
  1690. // e.g. HAVING COUNT(DISTINCT colname) = ...
  1691. // Instead the field code should make sure that no duplicate values are saved in the DB !!
  1692. $query .= ' GROUP BY rel.item_id ' .' HAVING COUNT(*) >= '.count($value);
  1693. }
  1694. }
  1695. //$query .= ' GROUP BY id'; // BAD PERFORMANCE ?
  1696. if ( !$return_sql ) {
  1697. //echo "<br>FlexicontentFields::getFiltered() ".$filter->name." appying query :<br>". $query."<br>\n";
  1698. $db->setQuery($query);
  1699. $filtered = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  1700. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  1701. return $filtered;
  1702. } else {
  1703. return ' AND i.id IN ('. $query .')';
  1704. }
  1705. }
  1706. // Method to get the active filter result Search View (an SQL where clause part OR an array of item ids, matching field filter)
  1707. static function getFilteredSearch( &$filter, $value, $return_sql=true )
  1708. {
  1709. $db = JFactory::getDBO();
  1710. // Check if field type supports advanced search
  1711. $support = FlexicontentFields::getPropertySupport($filter->field_type, $filter->iscore);
  1712. if ( ! $support->supportadvsearch && ! $support->supportadvfilter ) return null;
  1713. $valueswhere = FlexicontentFields::createFilterValueMatchSQL($filter, $value, $is_full_text=1, $is_search=1);
  1714. if ( !$valueswhere ) { return; }
  1715. // Decide to require all values
  1716. $display_filter_as = $filter->parameters->get( 'display_filter_as_s', 0 );
  1717. $require_all = count($value)>1 && !in_array( $display_filter_as, array(1,2,3) ) ?
  1718. $filter->parameters->get( 'filter_values_require_all', 0 ) : 0;
  1719. $istext_input = $display_filter_as==1 || $display_filter_as==3;
  1720. //$colname = $istext_input ? 'fs.search_index' : 'fs.value_id';
  1721. $colname = @ $filter->isindexed && !$istext_input ? 'fs.value_id' : 'fs.search_index';
  1722. $valueswhere = str_replace('_v_', $colname, $valueswhere);
  1723. // Get ALL items that have such values for the given field
  1724. $query = "SELECT ".($require_all ? 'fs.item_id' : 'DISTINCT fs.item_id')
  1725. ." FROM #__flexicontent_advsearch_index AS fs"
  1726. ." WHERE fs.field_id='".$filter->id."' "
  1727. . $valueswhere ;
  1728. if ($require_all) {
  1729. // Do not use distinct on column, it makes it is very slow, despite column having an index !!
  1730. // e.g. HAVING COUNT(DISTINCT colname) = ...
  1731. // Instead the field code should make sure that no duplicate values are saved in the DB !!
  1732. $query .= ' GROUP BY fs.item_id ' .' HAVING COUNT(*) >= '.count($value);
  1733. }
  1734. if ( !$return_sql ) {
  1735. //echo "<br>FlexicontentFields::getFiltered() ".$filter->name." appying query :<br>". $query."<br>\n";
  1736. $db->setQuery($query);
  1737. $filtered = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  1738. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  1739. return $filtered;
  1740. } else {
  1741. return ' AND i.id IN ('. $query .')';
  1742. }
  1743. }
  1744. // **********************************************
  1745. // Methods for creating Field Filters of FC views
  1746. // **********************************************
  1747. // Method to create a category (content list) or search filter
  1748. static function createFilter(&$filter, $value='', $formName='adminForm', $indexed_elements=false, $search_prop='')
  1749. {
  1750. static $apply_cache = null;
  1751. static $faceted_overlimit_msg = null;
  1752. $user = JFactory::getUser();
  1753. $mainframe = JFactory::getApplication();
  1754. //$cparams = $mainframe->getParams('com_flexicontent');
  1755. $cparams = JComponentHelper::getParams('com_flexicontent'); // createFilter maybe called in backend too ...
  1756. $print_logging_info = $cparams->get('print_logging_info');
  1757. global $is_fc_component;
  1758. $view = JRequest::getVar('view');
  1759. $isCategoryView = $is_fc_component && $view=='category';
  1760. $isSearchView = $is_fc_component && $view=='search';
  1761. if ( $print_logging_info ) {
  1762. global $fc_run_times;
  1763. $start_microtime = microtime(true);
  1764. }
  1765. // Apply caching to public or just registered users
  1766. $apply_cache = 1;
  1767. if ($apply_cache) {
  1768. $itemcache = JFactory::getCache('com_flexicontent_filters'); // Get Joomla Cache of '...items' Caching Group
  1769. $itemcache->setCaching(1); // Force cache ON
  1770. $itemcache->setLifeTime(FLEXI_CACHE_TIME); // Set expiration to default e.g. one hour
  1771. }
  1772. $isdate = in_array($filter->field_type, array('date','created','modified')) || $filter->parameters->get('isdate',0);
  1773. $default_size = $isdate ? 15 : 30;
  1774. $_s = $isSearchView ? '_s' : '';
  1775. // Some parameter shortcuts
  1776. $label_filter = $filter->parameters->get( 'display_label_filter'.$_s, 0 ) ; // How to show filter label
  1777. $size = $filter->parameters->get( 'text_filter_size', $default_size ); // Size of filter
  1778. $faceted_filter = $filter->parameters->get( 'faceted_filter'.$_s, 2);
  1779. $display_filter_as = $filter->parameters->get( 'display_filter_as'.$_s, 0 ); // Filter Type of Display
  1780. $filter_as_range = in_array($display_filter_as, array(2,3)) ;
  1781. $require_all = !in_array( $display_filter_as, array(1,2,3) ) ? $filter->parameters->get( 'filter_values_require_all', 0 ) : 0;
  1782. $combine_tip = $filter->parameters->get( 'filter_values_require_all_tip', 0 );
  1783. $show_matching_items = $filter->parameters->get( 'show_matching_items'.$_s, 1 );
  1784. $show_matches = $filter_as_range || !$faceted_filter ? 0 : $show_matching_items;
  1785. $hide_disabled_values = $filter->parameters->get( 'hide_disabled_values'.$_s, 0 );
  1786. $get_filter_vals = in_array($display_filter_as, array(0,2,4,5,6));
  1787. $filter_ffname = 'filter_'.$filter->id;
  1788. $filter_ffid = $formName.'_'.$filter->id.'_val';
  1789. // Make sure the current filtering values match the field filter configuration to single or multi-value
  1790. if ( in_array($display_filter_as, array(2,3,5,6)) ) {
  1791. if (!is_array($value)) $value = strlen($value) ? array($value) : array();
  1792. } else {
  1793. if (is_array($value)) $value = @ $value[0];
  1794. }
  1795. //print_r($value);
  1796. // Alter search property name (indexed fields only), remove underscore _ at start & end of it
  1797. if ($indexed_elements && $search_prop) {
  1798. preg_match("/^_([a-zA-Z_0-9]+)_$/", $search_prop, $prop_matches);
  1799. $search_prop = @ $prop_matches[1];
  1800. }
  1801. // Get filtering values, this can be cached if not filtering according to current category filters
  1802. if ( $get_filter_vals )
  1803. {
  1804. $view_join = '';
  1805. $view_where = '';
  1806. $filters_where = array();
  1807. // *** Limiting of displayed filter values according to current category filtering, but show all field values if filter is active
  1808. if ( $isCategoryView ) {
  1809. // category view, use parameter to decide if limitting filter values
  1810. global $fc_catviev;
  1811. if ( $faceted_filter ) {
  1812. $view_join = @ $fc_catviev['join_clauses'];
  1813. $view_where = $fc_catviev['where_conf_only'];
  1814. $filters_where = $fc_catviev['filters_where'];
  1815. $view_total = $fc_catviev['view_total'];
  1816. if ($fc_catviev['alpha_where']) $filters_where['alpha'] = $fc_catviev['alpha_where']; // we use count bellow ... so add it only if it is non-empty
  1817. }
  1818. } else if ( $isSearchView ) {
  1819. // search view, use parameter to decide if limitting filter values
  1820. global $fc_searchview;
  1821. if ( $faceted_filter ) {
  1822. $view_join = $fc_searchview['join_clauses'];
  1823. $view_where = $fc_searchview['where_conf_only'];
  1824. $filters_where = $fc_searchview['filters_where'];
  1825. $view_total = isset($fc_searchview['view_total']) ? $fc_searchview['view_total'] : 0;
  1826. }
  1827. }
  1828. $createFilterValues = !$isSearchView ? 'createFilterValues' : 'createFilterValuesSearch';
  1829. // Get filter values considering PAGE configuration (regardless of ACTIVE filters)
  1830. if ( $apply_cache ) {
  1831. $itemcache->setLifeTime(FLEXI_CACHE_TIME); // Set expiration to default e.g. one hour
  1832. $results_page = $itemcache->call(array('FlexicontentFields', $createFilterValues), $filter, $view_join, $view_where, array(), $indexed_elements, $search_prop);
  1833. } else {
  1834. if (!$isSearchView)
  1835. $results_page = FlexicontentFields::createFilterValues($filter, $view_join, $view_where, array(), $indexed_elements, $search_prop);
  1836. else
  1837. $results_page = FlexicontentFields::createFilterValuesSearch($filter, $view_join, $view_where, array(), $indexed_elements, $search_prop);
  1838. }
  1839. // Get filter values considering ACTIVE filters, but only if there is at least ONE filter active
  1840. $faceted_max_item_limit = 10000;
  1841. if ( $faceted_filter==2 && count($filters_where) ) {
  1842. if ($view_total <= $faceted_max_item_limit) {
  1843. // DO NOT cache at this point the filter combinations are endless, so they will produce big amounts of cached data, that will be rarely used ...
  1844. if (!$isSearchView)
  1845. $results_active = FlexicontentFields::createFilterValues($filter, $view_join, $view_where, $filters_where, $indexed_elements, $search_prop);
  1846. else
  1847. $results_active = FlexicontentFields::createFilterValuesSearch($filter, $view_join, $view_where, $filters_where, $indexed_elements, $search_prop);
  1848. } else if ($faceted_overlimit_msg === null) {
  1849. // Set a notice message about not counting item per filter values and instead showing item TOTAL of current category / view
  1850. $faceted_overlimit_msg = 1;
  1851. $filter_messages = JRequest::getVar('filter_message', array());
  1852. $filter_messages[] = JText::sprintf('FLEXI_FACETED_ITEM_LIST_OVER_LIMIT', $faceted_max_item_limit, $view_total);
  1853. JRequest::setVar('filter_messages', $filter_messages);
  1854. }
  1855. }
  1856. // Decide which results to show those based: (a) on active filters or (b) on page configuration
  1857. // This depends if hiding disabled values (for FACETED: 2) AND if active filters exist
  1858. $use_active_vals = $hide_disabled_values && isset($results_active);
  1859. $results_shown = $use_active_vals ? $results_active : $results_page;
  1860. $update_found = !$use_active_vals && isset($results_active);
  1861. // Set usage counters
  1862. $results = array();
  1863. foreach ($results_shown as $i => $result) {
  1864. $results[$i] = $result;
  1865. // FACETED: 0,1 or NOT showing usage
  1866. // Set usage to non-zero value e.g. -1 ... which maybe used (e.g. disabling values) but not be displayed
  1867. if (!$show_matches || $faceted_filter<2)
  1868. $results[$i]->found = -1;
  1869. // FACETED: 2 and SHOWING PAGE VALUES (not hiding values or no active filters),
  1870. // Set usage of filter values that was calculated according to active filters
  1871. // 1. this overrides value usage calculated for page's configuration (faceted: 1)
  1872. // 2. we set zero if value was not found
  1873. else if ($update_found)
  1874. $results[$i]->found = (int) @ $results_active[$i]->found;
  1875. // FACETED: 1 or hiding unavailable values ... leave value unchanged (if it has been calculated)
  1876. else ;
  1877. // Append value usage to value's label
  1878. if ($faceted_filter==2 && $show_matches && $results[$i]->found)
  1879. $results[$i]->text .= ' ('.$results[$i]->found.')';
  1880. }
  1881. } else {
  1882. $faceted_filter = 0; // clear faceted filter flag
  1883. }
  1884. // Prepend Field's Label to filter HTML
  1885. // Commented out because it was moved in to form template file
  1886. //$filter->html = $label_filter==1 ? $filter->label.': ' : '';
  1887. $filter->html = '';
  1888. // *** Create the form field(s) used for filtering
  1889. switch ($display_filter_as) {
  1890. case 0: case 2: case 6: // 0: Select (single value selectable), 2: Dual select (value range), 6: Multi Select (multiple values selectable)
  1891. $options = array();
  1892. // MULTI-select does not has an internal label a drop-down list option
  1893. if ($display_filter_as != 6) {
  1894. $first_option_txt = $label_filter==2 ? $filter->label : JText::_('FLEXI_ANY');
  1895. $options[] = JHTML::_('select.option', '', '- '.$first_option_txt.' -');
  1896. }
  1897. // Make use of select2 lib
  1898. flexicontent_html::loadFramework('select2');
  1899. $classes = " use_select2_lib";
  1900. $extra_param = '';
  1901. // MULTI-select: special label and prompts
  1902. if ($display_filter_as == 6) {
  1903. $classes .= ' fc_label_internal fc_prompt_internal';
  1904. // Add field's LABEL internally or click to select PROMPT (via js)
  1905. $_inner_lb = $label_filter==2 ? $filter->label : JText::_('FLEXI_CLICK_TO_LIST');
  1906. // Add type to filter PROMPT (via js)
  1907. $extra_param = ' fc_label_text="'.flexicontent_html::escapeJsText($_inner_lb,'s').'"';
  1908. $extra_param .= ' fc_prompt_text="'.flexicontent_html::escapeJsText(JText::_('FLEXI_TYPE_TO_FILTER'),'s').'"';
  1909. }
  1910. // Create HTML tag attributes
  1911. $attribs_str = ' class="fc_field_filter'.$classes.'" '.$extra_param;
  1912. $attribs_str .= $display_filter_as==6 ? ' multiple="multiple" size="20" ' : '';
  1913. //$attribs_str .= ($display_filter_as==0 || $display_filter_as==6) ? ' onchange="document.getElementById(\''.$formName.'\').submit();"' : '';
  1914. foreach($results as $result) {
  1915. if ( !strlen($result->value) ) continue;
  1916. $options[] = JHTML::_('select.option', $result->value, JText::_($result->text), 'value', 'text', $disabled = ($faceted_filter==2 && !$result->found));
  1917. }
  1918. if ($display_filter_as==6 && $combine_tip) {
  1919. $filter->html .= ' <span class="fc_filter_tip_inline">'.JText::_(!$require_all ? 'FLEXI_ANY_OF' : 'FLEXI_ALL_OF').'</span> ';
  1920. }
  1921. if ($display_filter_as==0 || $display_filter_as==6) {
  1922. $filter->html .= JHTML::_('select.genericlist', $options, $filter_ffname.'[]', $attribs_str, 'value', 'text', $value, $filter_ffid);
  1923. } else {
  1924. $filter->html .= JHTML::_('select.genericlist', $options, $filter_ffname.'[1]', $attribs_str, 'value', 'text', @ $value[1], $filter_ffid.'1');
  1925. $filter->html .= '<span class="fc_range"></span>';
  1926. $filter->html .= JHTML::_('select.genericlist', $options, $filter_ffname.'[2]', $attribs_str, 'value', 'text', @ $value[2], $filter_ffid.'2');
  1927. }
  1928. break;
  1929. case 1: case 3: // (TODO: autocomplete) ... 1: Text input, 3: Dual text input (value range), both of these can be JS date calendars
  1930. $_inner_lb = $label_filter==2 ? $filter->label : JText::_($isdate ? 'FLEXI_CLICK_CALENDAR' : 'FLEXI_TYPE_TO_LIST');
  1931. $_inner_lb = flexicontent_html::escapeJsText($_inner_lb,'s');
  1932. $attribs_str = ' class="fc_field_filter fc_label_internal" fc_label_text="'.$_inner_lb.'"';
  1933. $attribs_arr = array('class'=>'fc_field_filter fc_label_internal', 'fc_label_text' => $_inner_lb );
  1934. if ($display_filter_as==1) {
  1935. if ($isdate) {
  1936. $filter->html .= FlexicontentFields::createCalendarField($value, $allowtime=0, $filter_ffname, $filter_ffid, $attribs_arr);
  1937. } else
  1938. $filter->html .= '<input id="'.$filter_ffid.'" name="'.$filter_ffname.'" '.$attribs_str.' type="text" size="'.$size.'" value="'.@ $value.'" />';
  1939. } else {
  1940. if ($isdate) {
  1941. $filter->html .= '<span class="fc_filter_element">';
  1942. $filter->html .= FlexicontentFields::createCalendarField(@ $value[1], $allowtime=0, $filter_ffname.'[1]', $filter_ffid.'1', $attribs_arr);
  1943. $filter->html .= '</span>';
  1944. $filter->html .= '<span class="fc_range"></span>';
  1945. $filter->html .= '<span class="fc_filter_element">';
  1946. $filter->html .= FlexicontentFields::createCalendarField(@ $value[2], $allowtime=0, $filter_ffname.'[2]', $filter_ffid.'2', $attribs_arr);
  1947. $filter->html .= '</span>';
  1948. } else {
  1949. $size = (int)($size / 2);
  1950. $filter->html .= '<span class="fc_filter_element">';
  1951. $filter->html .= '<input name="'.$filter_ffname.'[1]" '.$attribs_str.' type="text" size="'.$size.'" value="'.@ $value[1].'" />';
  1952. $filter->html .= '</span>';
  1953. $filter->html .= '<span class="fc_range"></span>';
  1954. $filter->html .= '<span class="fc_filter_element">';
  1955. $filter->html .= '<input name="'.$filter_ffname.'[2]" '.$attribs_str.' type="text" size="'.$size.'" value="'.@ $value[2].'" />'."\n";
  1956. $filter->html .= '</span>';
  1957. }
  1958. }
  1959. break;
  1960. case 4: case 5: // 4: radio (single value selectable), 5: checkbox (multiple values selectable)
  1961. $lf_min = 10; // add parameter for this ?
  1962. $add_lf = count($results) >= $lf_min;
  1963. if ($add_lf) flexicontent_html::loadFramework('mCSB');
  1964. $clear_values = 0;
  1965. $value_style = $clear_values ? 'float:left; clear:both;' : '';
  1966. $i = 0;
  1967. $checked = ($display_filter_as==5) ? !count($value) || !strlen(reset($value)) : !strlen($value);
  1968. $checked_attr = $checked ? 'checked="checked"' : '';
  1969. $checked_class = $checked ? 'fc_highlight' : '';
  1970. $checked_class_li = $checked ? ' fc_checkradio_checked' : '';
  1971. $filter->html .= '<span class="fc_checkradio_group_wrapper fc_add_scroller'.($add_lf ? ' fc_list_filter_wrapper':'').'">';
  1972. $filter->html .= '<ul class="fc_field_filter fc_checkradio_group'.($add_lf ? ' fc_list_filter':'').'">';
  1973. $filter->html .= '<li class="fc_checkradio_option fc_checkradio_special'.$checked_class_li.'" style="'.$value_style.'">';
  1974. $filter->html .= ($label_filter==2 ? ' <span class="fc_filter_label_inline">'.$filter->label.'</span> ' : '');
  1975. if ($display_filter_as==4) {
  1976. $filter->html .= ' <input href="javascript:;" onchange="fc_toggleClassGrp(this, \'fc_highlight\', 1);" ';
  1977. $filter->html .= ' id="'.$filter_ffid.$i.'" type="radio" name="'.$filter_ffname.'" ';
  1978. $filter->html .= ' value="" '.$checked_attr.' class="fc_checkradio" />';
  1979. } else {
  1980. $filter->html .= ' <input href="javascript:;" onchange="fc_toggleClass(this, \'fc_highlight\', 1);" ';
  1981. $filter->html .= ' id="'.$filter_ffid.$i.'" type="checkbox" name="'.$filter_ffname.'['.$i.']" ';
  1982. $filter->html .= ' value="" '.$checked_attr.' class="fc_checkradio" />';
  1983. }
  1984. $filter->html .= '<label class="hasTip '.$checked_class.'" for="'.$filter_ffid.$i.'" '
  1985. .' title="::'.JText::_('FLEXI_REMOVE_ALL').'"'
  1986. .($checked ? ' style="display:none!important;" ' : 'style="background:none!important; padding-left:0px!important;"').'>'.
  1987. '<span class="fc_delall_filters"></span>';
  1988. $filter->html .= '</label> '
  1989. .($combine_tip ? ' <span class="fc_filter_tip_inline">'.JText::_(!$require_all ? 'FLEXI_ANY_OF' : 'FLEXI_ALL_OF').'</span> ' : '')
  1990. .' </li>';
  1991. $i++;
  1992. foreach($results as $result) {
  1993. if ( !strlen($result->value) ) continue;
  1994. $checked = ($display_filter_as==5) ? in_array($result->value, $value) : $result->value==$value;
  1995. $checked_attr = $checked ? ' checked=checked ' : '';
  1996. $disable_attr = $faceted_filter==2 && !$result->found ? ' disabled=disabled ' : '';
  1997. $checked_class = $checked ? 'fc_highlight' : '';
  1998. $checked_class .= $faceted_filter==2 && !$result->found ? ' fcdisabled ' : '';
  1999. $checked_class_li = $checked ? ' fc_checkradio_checked' : '';
  2000. $filter->html .= '<li class="fc_checkradio_option'.$checked_class_li.'" style="'.$value_style.'">';
  2001. if ($display_filter_as==4) {
  2002. $filter->html .= ' <input href="javascript:;" onchange="fc_toggleClassGrp(this, \'fc_highlight\');" ';
  2003. $filter->html .= ' id="'.$filter_ffid.$i.'" type="radio" name="'.$filter_ffname.'" ';
  2004. $filter->html .= ' value="'.$result->value.'" '.$checked_attr.$disable_attr.' class="fc_checkradio" />';
  2005. } else {
  2006. $filter->html .= ' <input href="javascript:;" onchange="fc_toggleClass(this, \'fc_highlight\');" ';
  2007. $filter->html .= ' id="'.$filter_ffid.$i.'" type="checkbox" name="'.$filter_ffname.'['.$i.']" ';
  2008. $filter->html .= ' value="'.$result->value.'" '.$checked_attr.$disable_attr.' class="fc_checkradio" />';
  2009. }
  2010. $filter->html .= '<label class="'.$checked_class.'" for="'.$filter_ffid.$i.'">';
  2011. $filter->html .= JText::_($result->text);
  2012. $filter->html .= '</label>';
  2013. $filter->html .= '</li>';
  2014. $i++;
  2015. }
  2016. $filter->html .= '</ul>';
  2017. $filter->html .= '</span>';
  2018. break;
  2019. }
  2020. if ( $print_logging_info ) {
  2021. $current_filter_creation = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2022. $flt_active_count = isset($filters_where) ? count($filters_where) : 0;
  2023. $faceted_str = array(0=>'non-FACETED MODE', 1=>'FACETED MODE: current view &nbsp; (cacheable) &nbsp; ', 2=>'FACETED MODE: current filters:'." (".$flt_active_count.' active) ');
  2024. $fc_run_times['create_filter'][$filter->name] = $current_filter_creation;
  2025. if ( isset($fc_run_times['_create_filter_init']) ) {
  2026. $fc_run_times['create_filter'][$filter->name] -= $fc_run_times['_create_filter_init'];
  2027. $fc_run_times['create_filter_init'] = $fc_run_times['_create_filter_init'];
  2028. unset($fc_run_times['_create_filter_init']);
  2029. }
  2030. $fc_run_times['create_filter_type'][$filter->name] = $faceted_str[$faceted_filter];
  2031. }
  2032. //$filter_display_typestr = array(0=>'Single Select', 1=>'Single Text', 2=>'Range Dual Select', 3=>'Range Dual Text', 4=>'Radio Buttons', 5=>'Checkbox Buttons');
  2033. //echo "FIELD name: <b>". $filter->name ."</b> Field Type: <b>". $filter->field_type."</b> Filter Type: <b>". $filter_display_typestr[$display_filter_as] ."</b> (".$display_filter_as.") ".sprintf(" %.2f s",$current_filter_creation/1000000)." <br/>";
  2034. }
  2035. // Method to create a calendar form field according to a given configuation, e.g. called during Filter Creation of FC views
  2036. static function createCalendarField($value, $date_allowtime, $fieldname, $elementid, $attribs='', $skip_on_invalid=false, $timezone=false)
  2037. {
  2038. // 'false' timezone means ==> use server setting (=joomla site configured TIMEZONE),
  2039. // in J1.5 this must be null for using server setting (=joomla site configured OFFSET)
  2040. $timezone = ($timezone === false && !FLEXI_J16GE) ? null : $timezone;
  2041. @list($date, $time) = preg_split('#\s+#', $value, $limit=2);
  2042. $time = ($date_allowtime==2 && !$time) ? '00:00' : $time;
  2043. try {
  2044. if ( !$value ) {
  2045. $date = '';
  2046. } else if (!$date_allowtime || !$time) {
  2047. $date = JHTML::_('date', $date, JText::_( FLEXI_J16GE ? 'Y-m-d' : '%Y-%m-%d' ), $timezone);
  2048. } else {
  2049. $date = JHTML::_('date', $value, JText::_( FLEXI_J16GE ? 'Y-m-d H:i' : '%Y-%m-%d %H:%M' ), $timezone);
  2050. }
  2051. } catch ( Exception $e ) {
  2052. if (!$skip_on_invalid) return '';
  2053. else $date = '';
  2054. }
  2055. // Create JS calendar
  2056. $date_formats_map = array('0'=>'%Y-%m-%d', '1'=>'%Y-%m-%d %H:%M', '2'=>'%Y-%m-%d 00:00');
  2057. $date_format = $date_formats_map[$date_allowtime];
  2058. $calendar = JHTML::_('calendar', $date, $fieldname, $elementid, $date_format, $attribs);
  2059. return $calendar;
  2060. }
  2061. // Method to create filter values for a field filter to be used in content lists views (category, etc)
  2062. static function createFilterValues($filter, $view_join, $view_where, $filters_where, $indexed_elements, $search_prop)
  2063. {
  2064. $faceted_filter = $filter->parameters->get( 'faceted_filter', 2);
  2065. $display_filter_as = $filter->parameters->get( 'display_filter_as', 0 ); // Filter Type of Display
  2066. $filter_as_range = in_array($display_filter_as, array(2,3)) ;
  2067. $show_matching_items = $filter->parameters->get( 'show_matching_items', 1 );
  2068. $show_matches = $filter_as_range || !$faceted_filter ? 0 : $show_matching_items;
  2069. if ($faceted_filter || !$indexed_elements) {
  2070. $_results = FlexicontentFields::getFilterValues($filter, $view_join, $view_where, $filters_where);
  2071. }
  2072. // Support of value-indexed fields
  2073. if ( !$faceted_filter && $indexed_elements) {
  2074. $results = & $indexed_elements;
  2075. } else
  2076. if ( $indexed_elements ) {
  2077. // Limit indexed element according to DB results found
  2078. $results = array_intersect_key($indexed_elements, $_results);
  2079. if ($faceted_filter==2 && $show_matches) foreach ($results as $i => $result) {
  2080. $result->found = $_results[$i]->found;
  2081. }
  2082. // Support for multi-property fields
  2083. } else if ($search_prop) {
  2084. // Check and unserialize values
  2085. foreach ($_results as $i => $result) {
  2086. $v = @unserialize($result->value);
  2087. if ( $v || $result->value === 'b:0;' ) $_results[$i] = & $v;
  2088. }
  2089. // Index values via the search property
  2090. $results = array();
  2091. foreach ($_results as $i => $result) {
  2092. if ( is_array($_results[$i]) ) $_results[$i] = (object) $_results[$i];
  2093. else $_results[$i] = (object) array($search_prop=>$_results[$i]);
  2094. if ( isset($_results[$i]->$search_prop) ) $results[ $_results[$i]->$search_prop ] = $_results[$i];
  2095. }
  2096. // non-indexable or single property field
  2097. } else {
  2098. $results = & $_results;
  2099. }
  2100. foreach ($results as $i => $result) {
  2101. $results[$i]->text = JText::_($result->text);
  2102. }
  2103. // Skip sorting for indexed elements, DB query or element entry is responsible
  2104. // for ordering indexable fields, also skip if ordering is done by the filter
  2105. if ( !$indexed_elements && empty($filter->filter_orderby) ) uksort($results, 'strcasecmp');
  2106. return $results;
  2107. }
  2108. // Method to create filter values for a field filter to be used in search view
  2109. static function createFilterValuesSearch($filter, $view_join, $view_where, $filters_where, $indexed_elements, $search_prop)
  2110. {
  2111. $faceted_filter = $filter->parameters->get( 'faceted_filter_s', 2);
  2112. $display_filter_as = $filter->parameters->get( 'display_filter_as_s', 0 ); // Filter Type of Display
  2113. $filter_as_range = in_array($display_filter_as, array(2,3)) ;
  2114. //$show_matching_items = $filter->parameters->get( 'show_matching_items_s', 1 );
  2115. //$show_matches = $filter_as_range || !$faceted_filter ? 0 : $show_matching_items;
  2116. $filter->filter_isindexed = (boolean) $indexed_elements;
  2117. $_results = FlexicontentFields::getFilterValuesSearch($filter, $view_join, $view_where, $filters_where);
  2118. $results = & $_results;
  2119. foreach ($results as $i => $result) {
  2120. $results[$i]->text = JText::_($result->text);
  2121. }
  2122. // Skip sorting for indexed elements, DB query or element entry is responsible
  2123. // for ordering indexable fields, also skip if ordering is done by the filter
  2124. if ( !$indexed_elements && empty($filter->filter_orderby) ) uksort($results, 'strcasecmp');
  2125. return $results;
  2126. }
  2127. // Retrieves all available filter values of the given field according to the given VIEW'S FILTERING (Content Lists)
  2128. static function getFilterValues(&$filter, &$view_join, &$view_where, &$filters_where)
  2129. {
  2130. //echo "<pre>"; debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); echo "</pre>";
  2131. $db = JFactory::getDBO();
  2132. $filter_where_curr = '';
  2133. foreach ($filters_where as $filter_id => $filter_where) {
  2134. if ($filter_id != $filter->id) $filter_where_curr .= ' ' . $filter_where;
  2135. }
  2136. //echo "filter_where_curr : ". $filter_where_curr ."<br/>";
  2137. // partial SQL clauses
  2138. $valuesselect = @$filter->filter_valuesselect ? $filter->filter_valuesselect : ' fi.value AS value, fi.value AS text';
  2139. $valuesfrom = @$filter->filter_valuesfrom ? $filter->filter_valuesfrom : ($filter->iscore ? ' FROM #__content AS i' : ' FROM #__flexicontent_fields_item_relations AS fi ');
  2140. $valuesjoin = @$filter->filter_valuesjoin ? $filter->filter_valuesjoin : ' ';
  2141. $valueswhere = @$filter->filter_valueswhere ? $filter->filter_valueswhere : ' AND fi.field_id ='.$filter->id;
  2142. // full SQL clauses
  2143. $groupby = @$filter->filter_groupby ? $filter->filter_groupby : ' GROUP BY value ';
  2144. $having = @$filter->filter_having ? $filter->filter_having : '';
  2145. $orderby = @$filter->filter_orderby ? $filter->filter_orderby : '';
  2146. $faceted_filter = $filter->parameters->get( 'faceted_filter', 2);
  2147. $display_filter_as = $filter->parameters->get( 'display_filter_as', 0 ); // Filter Type of Display
  2148. $filter_as_range = in_array($display_filter_as, array(2,3)) ;
  2149. $show_matching_items = $filter->parameters->get( 'show_matching_items', 1 );
  2150. $show_matches = $filter_as_range || !$faceted_filter ? 0 : $show_matching_items;
  2151. static $item_ids_list = null;
  2152. static $item_ids_sub_query = null;
  2153. if ( $faceted_filter ) {
  2154. // Find items belonging to current view
  2155. if ($item_ids_list === null && empty($view_where) ) $item_ids_list = '';
  2156. if ($item_ids_list === null || $item_ids_sub_query === null) {
  2157. $sub_query = 'SELECT DISTINCT i.id '."\n"
  2158. . ' FROM #__content AS i'."\n"
  2159. . $view_join."\n"
  2160. . $view_where."\n"
  2161. ;
  2162. global $fc_run_times, $fc_jprof, $fc_catviev;
  2163. $start_microtime = microtime(true);
  2164. $view_total = (int) @ $fc_catviev['view_total'];
  2165. $use_item_list_below = 0;
  2166. if ($view_total >= $use_item_list_below) {
  2167. // Use sub-query only if current view has more than 0 items
  2168. $item_ids_sub_query = $sub_query;
  2169. } else {
  2170. // If current view has less than nnn items, then pre-calculate an item_id list ... ??? this is may or may not be benfitial ...
  2171. try {
  2172. // 1, try to bypass joomla SQL layer, including Falang (slow in large sites, not needed in this query ?) !!
  2173. $rows = flexicontent_db::directQuery($sub_query, false, true);
  2174. $item_ids = array();
  2175. foreach ($rows as $row) $item_ids[] = $row->id;
  2176. }
  2177. catch (Exception $e) {
  2178. // 2, get items via normal joomla SQL layer
  2179. $db->setQuery($sub_query);
  2180. $item_ids = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  2181. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  2182. }
  2183. $item_ids_list = implode(',', $item_ids);
  2184. unset($item_ids);
  2185. }
  2186. $fc_run_times['_create_filter_init'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2187. }
  2188. $item_id_col = $filter->iscore ? 'i.id' : 'fi.item_id';
  2189. $query = 'SELECT '. $valuesselect .($faceted_filter && $show_matches ? ', COUNT(DISTINCT '.$item_id_col.') as found ' : '')."\n"
  2190. . $valuesfrom."\n"
  2191. .(!$filter->iscore ? ' JOIN #__content AS i ON fi.item_id = i.id ' : '') // CUSTOM fields only, content table is not already JOINED, join it
  2192. .(@ $filters_where['search'] ? ' JOIN #__flexicontent_items_ext AS ie ON ie.item_id = i.id' : '') // extended data TABLE, needs to be joined when doing TEXT search
  2193. . $valuesjoin."\n"
  2194. . ' WHERE 1 '."\n"
  2195. . (!$item_ids_list ? '' : ' AND '.$item_id_col.' IN('.$item_ids_list.')'."\n")
  2196. . (!$item_ids_sub_query ? '' : ' AND '.$item_id_col.' IN('.$item_ids_sub_query.')'."\n")
  2197. . ($filter->iscore ? $filter_where_curr : str_replace('i.id', 'fi.item_id', $filter_where_curr))."\n"
  2198. . $valueswhere."\n"
  2199. . $groupby."\n"
  2200. . $having."\n"
  2201. . $orderby
  2202. ;
  2203. }
  2204. // NON-FACETED filter
  2205. else {
  2206. $query = 'SELECT DISTINCT '. $valuesselect ."\n"
  2207. . $valuesfrom."\n"
  2208. . $valuesjoin."\n"
  2209. . ' WHERE 1 '."\n"
  2210. . $valueswhere."\n"
  2211. //. $groupby."\n"
  2212. . $having."\n"
  2213. . $orderby
  2214. ;
  2215. }
  2216. //if ( in_array($filter->field_type, array('tags','created','modified')) ) echo nl2br($query);
  2217. $db->setQuery($query);
  2218. $results = $db->loadObjectList('value');
  2219. if ($db->getErrorNum()) {
  2220. $filter->html = "Filter for : {$filter->label} cannot be displayed, error during db query :<br />" .$query ."<br/>" .__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg());
  2221. return array();
  2222. }
  2223. return $results;
  2224. }
  2225. // Retrieves all available filter values of the given field according to the given VIEW'S FILTERING (Search view)
  2226. static function getFilterValuesSearch(&$filter, &$view_join, &$view_where, &$filters_where)
  2227. {
  2228. //echo "<pre>"; debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); echo "</pre>";
  2229. $db = JFactory::getDBO();
  2230. $filter_where_curr = '';
  2231. foreach ($filters_where as $filter_id => $filter_where) {
  2232. if ($filter_id != $filter->id) $filter_where_curr .= ' ' . $filter_where;
  2233. }
  2234. $valuesselect = @$filter->filter_isindexed ? ' ai.value_id as value, ai.search_index as text ' : ' ai.search_index as value, ai.search_index as text';
  2235. $faceted_filter = $filter->parameters->get( 'faceted_filter_s', 2);
  2236. $display_filter_as = $filter->parameters->get( 'display_filter_as_s', 0 ); // Filter Type of Display
  2237. $filter_as_range = in_array($display_filter_as, array(2,3)) ;
  2238. $show_matching_items = $filter->parameters->get( 'show_matching_items_s', 1 );
  2239. $show_matches = $filter_as_range || !$faceted_filter ? 0 : $show_matching_items;
  2240. static $item_ids_list = null;
  2241. static $item_ids_sub_query = null;
  2242. if ( $faceted_filter )
  2243. {
  2244. // Find items belonging to current view
  2245. if ($item_ids_list === null && empty($view_where) ) $item_ids_list = '';
  2246. if ($item_ids_list === null || $item_ids_sub_query === null) {
  2247. $sub_query = 'SELECT DISTINCT ai.item_id '."\n"
  2248. .' FROM #__flexicontent_advsearch_index AS ai'."\n"
  2249. .' JOIN #__content i ON ai.item_id = i.id'."\n"
  2250. . $view_join."\n"
  2251. .' WHERE '."\n"
  2252. . $view_where."\n"
  2253. ;
  2254. $db->setQuery($sub_query);
  2255. global $fc_run_times, $fc_jprof, $fc_searchview;
  2256. $start_microtime = microtime(true);
  2257. $view_total = 1; //(int) @ $fc_searchview['view_total'];
  2258. $use_item_list_below = 0;
  2259. if ($view_total >= $use_item_list_below) {
  2260. // Use sub-query only if current view has more than 0 items
  2261. $item_ids_sub_query = $sub_query;
  2262. } else {
  2263. // If current view has less than nnn items, then pre-calculate an item_id list ... ??? this is may or may not be benfitial ...
  2264. try {
  2265. // 1, try to bypass joomla SQL layer, including Falang (slow in large sites, not needed in this query ?) !!
  2266. $rows = flexicontent_db::directQuery($sub_query, false, true);
  2267. $item_ids = array();
  2268. foreach ($rows as $row) $item_ids[] = $row->id;
  2269. }
  2270. catch (Exception $e) {
  2271. // 2, get items via normal joomla SQL layer
  2272. $db->setQuery($sub_query);
  2273. $item_ids = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  2274. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  2275. }
  2276. $item_ids_list = implode(',', $item_ids);
  2277. unset($item_ids);
  2278. }
  2279. $fc_run_times['_create_filter_init'] = round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2280. }
  2281. // Get ALL records that have such values for the given field
  2282. $query = 'SELECT '. $valuesselect .($faceted_filter && $show_matches ? ', COUNT(DISTINCT ai.item_id) as found ' : '')."\n"
  2283. . ' FROM #__flexicontent_advsearch_index AS ai'."\n"
  2284. . ' WHERE 1 '."\n"
  2285. . (!$item_ids_list ? '' : ' AND ai.item_id IN('.$item_ids_list.')'."\n")
  2286. . (!$item_ids_sub_query ? '' : ' AND ai.item_id IN('.$item_ids_sub_query.')'."\n")
  2287. . ' AND ai.field_id='.(int)$filter->id."\n"
  2288. . str_replace('i.id', 'ai.item_id', $filter_where_curr)."\n"
  2289. . ' GROUP BY ai.search_index, ai.value_id'."\n"
  2290. ;
  2291. } else {
  2292. // Get ALL records that have such values for the given field
  2293. $query = 'SELECT '. $valuesselect .($faceted_filter && $show_matches ? ', COUNT(DISTINCT i.id) as found ' : '')."\n"
  2294. .' FROM #__flexicontent_advsearch_index AS ai'."\n"
  2295. .' JOIN #__content i ON ai.item_id = i.id'."\n"
  2296. . $view_join."\n"
  2297. .' WHERE '."\n"
  2298. . ($view_where ? $view_where.' AND ' : '')."\n"
  2299. .' ai.field_id='.(int)$filter->id."\n"
  2300. . $filter_where_curr
  2301. .' GROUP BY ai.search_index, ai.value_id'."\n"
  2302. ;
  2303. /*$query = 'SELECT DISTINCT '. $valuesselect ."\n"
  2304. .' FROM #__flexicontent_advsearch_index AS ai'."\n"
  2305. .' WHERE ai.field_id='.(int)$filter->id."\n"
  2306. //.' GROUP BY ai.search_index, ai.value_id'."\n"
  2307. ;*/
  2308. }
  2309. $db->setQuery($query);
  2310. $results = $db->loadObjectList('text');
  2311. //echo "<br/>". count($results) ."<br/>";
  2312. //echo nl2br($query) ."<br/><br/>";
  2313. if ($db->getErrorNum()) {
  2314. $filter->html = "Filter for : {$filter->label} cannot be displayed, error during db query :<br />" .$query ."<br/>" .__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg());
  2315. return array();
  2316. }
  2317. return $results;
  2318. }
  2319. /**
  2320. * Method to set custom filters values VIA configuration parameters
  2321. * -- CASE 1: CONTENT LISTS (component / category / menu items / filtering module)
  2322. * these are set as HTTP Request variables to be used by the filtering mechanism of the category model (content lists)
  2323. * -- CASE 2: Custom Fields SCOPE of Universal Content MODULE
  2324. * these are returned as an array to be used directly into the SQL query
  2325. *
  2326. * @access public
  2327. * @return object
  2328. * @since 1.5
  2329. */
  2330. static function setFilterValues( &$cparams, $mfilter_name='persistent_filters', $is_persistent=1, $set_method="httpReq" )
  2331. {
  2332. $field_filters = array(); // Used when set_method is 'array' instead of 'httpReq'
  2333. $is_persistent = // Non-httpReq method does not have initial filters
  2334. $set_method!="httpReq" ? 1 : $is_persistent;
  2335. // Get configuration parameter holding the custom field filtering and abort if empty
  2336. $mfilter_data = $cparams->get($mfilter_name, '');
  2337. if (!$mfilter_data) {
  2338. $cparams->set($mfilter_name, ''); // Set to empty string for J1.5 compatibility, otherwise this could be empty array too
  2339. return array();
  2340. }
  2341. // Parse configuration parameter into individual fields
  2342. $mfilter_arr = preg_split("/[\s]*%%[\s]*/", $mfilter_data);
  2343. if ( empty($mfilter_arr[count($mfilter_arr)-1]) ) {
  2344. unset($mfilter_arr[count($mfilter_arr)-1]);
  2345. }
  2346. // This array contains the field (filter) ID that were parsed without errors
  2347. $filter_ids = array();
  2348. foreach ($mfilter_arr as $mfilter)
  2349. {
  2350. // a. Split elements into their properties: filter_id, filter_value
  2351. $_data = preg_split("/[\s]*##[\s]*/", $mfilter); //print_r($_data);
  2352. $filter_id = (int) $_data[0];
  2353. $filter_value = @$_data[1];
  2354. //echo "filter_".$filter_id.": "; print_r( $filter_value ); echo "<br/>";
  2355. // b. Basic parsing error check: a non numeric field id
  2356. if ( !$filter_id ) continue;
  2357. // c. Add field (filter) ID into those that are valid
  2358. $filter_ids[] = $filter_id;
  2359. // d. Skip field filter, if it is not persistent and user user has overriden it
  2360. if ( !$is_persistent && JRequest::getVar('filter_'.$filter_id, false) !== false ) continue;
  2361. // CASE: range values: value01---value02
  2362. if (strpos($filter_value, '---') !== false) {
  2363. $filter_value = explode('---', $filter_value);
  2364. $filter_value[2] = $filter_value[1];
  2365. $filter_value[1] = $filter_value[0];
  2366. unset($filter_value[0]);
  2367. }
  2368. // CASE: multiple values: value01+++value02+++value03+++value04
  2369. else if (strpos($filter_value, '+++') !== false) {
  2370. $filter_value = explode('+++', $filter_value);
  2371. }
  2372. // CASE: specific value: value01
  2373. else {}
  2374. // INDIRECT method of using field filter (via HTTP request)
  2375. if ($set_method=='httpReq')
  2376. JRequest::setVar('filter_'.$filter_id, $filter_value);
  2377. // DIRECT method of using field filter (via a returned array)
  2378. else
  2379. $field_filters[$filter_id] = $filter_value;
  2380. }
  2381. // INDIRECT method of using field filter (via HTTP request),
  2382. // NOTE: we overwrite the above configuration parameter of custom field filters with an ARRAY OF VALID FILTER IDS, to
  2383. // indicate to category/search model security not to skip these if they are not IN category/search configured filters list
  2384. if ($set_method=='httpReq') {
  2385. count($filter_ids) ?
  2386. $cparams->set($mfilter_name, FLEXI_J16GE ? $filter_ids : implode( '|', $filter_ids) ) :
  2387. $cparams->set($mfilter_name, false ); // FALSE means do not retrieve ALL
  2388. }
  2389. // DIRECT method filter values, return an array of filter values (for direct usage into an SQL query)
  2390. else {
  2391. return $field_filters;
  2392. }
  2393. }
  2394. /**
  2395. * Method to get data of filters
  2396. *
  2397. * @access public
  2398. * @return object
  2399. * @since 1.5
  2400. */
  2401. static function &getFilters($filt_param='filters', $usage_param='use_filters', & $params = null, $check_access=true)
  2402. {
  2403. // Parameter that controls using these filters
  2404. $filters = array();
  2405. if ( $usage_param!='__ALL_FILTERS__' && $params && !$params->get($usage_param,0) ) return $filters;
  2406. // Get Filter IDs, false means do retrieve any filter
  2407. $filter_ids = $params ? $params->get($filt_param, array()) : array();
  2408. if ($filter_ids === false) return $filters;
  2409. // Sanitize the given filter_ids ... just in case
  2410. if ( !is_array($filter_ids) ) $filter_ids = array($filter_ids);
  2411. $filter_ids = array_filter($filter_ids, 'is_numeric');
  2412. $user = JFactory::getUser();
  2413. $db = JFactory::getDBO();
  2414. // None selected filters means ALL
  2415. $and_scope = $usage_param!='__ALL_FILTERS__' && count($filter_ids) ? ' AND fi.id IN (' . implode(',', $filter_ids) . ')' : '';
  2416. // Use ACCESS Level, usually this is only for shown filters
  2417. $and_access = '';
  2418. if ($check_access) {
  2419. if (FLEXI_J16GE) {
  2420. $aid_arr = $user->getAuthorisedViewLevels();
  2421. $aid_list = implode(",", $aid_arr);
  2422. $and_access = ' AND fi.access IN (0,'.$aid_list.') ';
  2423. } else {
  2424. $aid = (int) $user->get('aid');
  2425. if (FLEXI_ACCESS) {
  2426. $readperms = FAccess::checkUserElementsAccess($user->gmid, 'read');
  2427. if (isset($readperms['field']) && count($readperms['field']) ) {
  2428. $and_access = ' AND ( fi.access <= '.$aid.' OR fi.id IN ('.implode(",", $readperms['field']).') )';
  2429. } else {
  2430. $and_access = ' AND fi.access <= '.$aid;
  2431. }
  2432. } else {
  2433. $and_access = ' AND fi.access <= '.$aid;
  2434. }
  2435. }
  2436. }
  2437. // Create and execute SQL query for retrieving filters
  2438. $query = 'SELECT fi.*'
  2439. . ' FROM #__flexicontent_fields AS fi'
  2440. . ' WHERE fi.published = 1'
  2441. . ' AND fi.isfilter = 1'
  2442. . $and_access
  2443. . $and_scope
  2444. . ' ORDER BY fi.ordering, fi.name'
  2445. ;
  2446. $db->setQuery($query);
  2447. $filters = $db->loadObjectList('name');
  2448. if ( !$filters ) {
  2449. $filters = array(); // need to do this because we return reference, but false here will also mean an error
  2450. return $filters;
  2451. }
  2452. // Create filter parameters, language filter label, etc
  2453. foreach ($filters as $filter) {
  2454. $filter->parameters = FLEXI_J16GE ? new JRegistry($filter->attribs) : new JParameter($filter->attribs);
  2455. $filter->label = JText::_($filter->label);
  2456. }
  2457. // Return found filters
  2458. return $filters;
  2459. }
  2460. /**
  2461. * Method to creat the HTML of filters
  2462. *
  2463. * @access public
  2464. * @return object
  2465. * @since 1.5
  2466. */
  2467. static function renderFilters( &$params, &$filters, $form_name )
  2468. {
  2469. // Make the filter compatible with Joomla standard cache
  2470. $cache = JFactory::getCache('com_flexicontent');
  2471. $cache->clean();
  2472. $display_label_filter_override = (int) $params->get('show_filter_labels', 0);
  2473. foreach ($filters as $filter_name => $filter)
  2474. {
  2475. $filtervalue = JRequest::getVar('filter_'.$filter->id, '', 'default');
  2476. //print_r($filtervalue);
  2477. // make sure filter HTML is cleared, and create it
  2478. $display_label_filter_saved = $filter->parameters->get('display_label_filter');
  2479. if ( $display_label_filter_override ) $filter->parameters->set('display_label_filter', $display_label_filter_override); // suppress labels inside filter's HTML (hide or show all labels externally)
  2480. // else ... filter default label behavior
  2481. $filter->html = ''; // make sure filter HTML display is cleared
  2482. $field_type = $filter->iscore ? 'core' : $filter->field_type;
  2483. //$results = $dispatcher->trigger('onDisplayFilter', array( &$filter, $filtervalue ));
  2484. FLEXIUtilities::call_FC_Field_Func($field_type, 'onDisplayFilter', array( &$filter, $filtervalue, $form_name ) );
  2485. $filter->parameters->set('display_label_filter', $display_label_filter_saved);
  2486. $lists['filter_' . $filter->id] = $filtervalue;
  2487. }
  2488. }
  2489. // **********************************************************************************************************
  2490. // Helper methods to create GENERIC ITEM LISTs which also includes RENDERED display of fields and custom HTML
  2491. // **********************************************************************************************************
  2492. // Helper method to perform HTML replacements on given list of item ids (with optional catids too), the items list is either given
  2493. // as parameter or the list is created via the items that have as value the id of 'parentitem' for field with id 'reverse_field'
  2494. static function getItemsList(&$params, &$_itemids_catids=null, $isform=0, $reverse_field=0, &$parentfield, &$parentitem, &$return_item_list=false)
  2495. {
  2496. // Execute query to get item list data
  2497. $db = JFactory::getDBO();
  2498. $query = FlexicontentFields::createItemsListSQL($params, $_itemids_catids, $isform, $reverse_field, $parentfield, $parentitem);
  2499. $db->setQuery($query);
  2500. $item_list = $db->loadObjectList('id');
  2501. if ($db->getErrorNum()) JFactory::getApplication()->enqueueMessage(__FUNCTION__.'(): SQL QUERY ERROR:<br/>'.nl2br($db->getErrorMsg()),'error');
  2502. // Item list must be returned too ...
  2503. if ($return_item_list) $return_item_list = & $item_list;
  2504. // No published related items or SQL query failed, return
  2505. if ( !$item_list ) return '';
  2506. if ($_itemids_catids) foreach($item_list as $_item) // if it exists ... add prefered catid to items list data
  2507. $_item->rel_catid = @ $_itemids_catids[$_item->id]->catid;
  2508. return FlexicontentFields::createItemsListHTML($params, $item_list, $isform, $parentfield, $parentitem);
  2509. }
  2510. // Helper method to create SQL query for retrieving items list data
  2511. static function createItemsListSQL(&$params, &$_itemids_catids=null, $isform=0, $reverse_field=0, &$parentfield, &$parentitem)
  2512. {
  2513. $db = JFactory::getDBO();
  2514. $sfx = $isform ? '_form' : '';
  2515. // Get data like aliases and published state
  2516. $publish_where = '';
  2517. if ($params->get('use_publish_dates', 1 ))
  2518. {
  2519. // Date-Times are stored as UTC, we should use current UTC time to compare and not user time (requestTime),
  2520. // thus the items are published globally at the time the author specified in his/her local clock
  2521. //$app = JFactory::getApplication();
  2522. //$now = FLEXI_J16GE ? $app->requestTime : $app->get('requestTime'); // NOT correct behavior it should be UTC (below)
  2523. //$date = JFactory::getDate();
  2524. //$now = FLEXI_J16GE ? $date->toSql() : $date->toMySQL(); // NOT good if string passed to function that will be cached, because string continuesly different
  2525. $_nowDate = 'UTC_TIMESTAMP()'; //$db->Quote($now);
  2526. $nullDate = $db->getNullDate();
  2527. $publish_where = ' AND ( i.publish_up = '.$db->Quote($nullDate).' OR i.publish_up <= '.$_nowDate.' )';
  2528. $publish_where .= ' AND ( i.publish_down = '.$db->Quote($nullDate).' OR i.publish_down >= '.$_nowDate.' )';
  2529. }
  2530. // item IDs via reversing a relation field
  2531. if ($reverse_field) {
  2532. $item_join = ' JOIN #__flexicontent_fields_item_relations AS fi_rel'
  2533. .' ON i.id=fi_rel.item_id AND fi_rel.field_id=' .$reverse_field .' AND CAST(fi_rel.value AS UNSIGNED)=' .$parentitem->id;
  2534. }
  2535. // item IDs via a given list (relation field and ... maybe other cases too)
  2536. else {
  2537. $item_where = ' AND i.id IN ('. implode(",", array_keys($_itemids_catids)) .')';
  2538. }
  2539. // Get orderby SQL CLAUSE ('ordering' is passed by reference but no frontend user override is used (we give empty 'request_var')
  2540. $order = $params->get( 'orderby'.$sfx, 'alpha' );
  2541. $orderby = flexicontent_db::buildItemOrderBy($params, $order, $request_var='', $config_param='', $item_tbl_alias = 'i', $relcat_tbl_alias = 'rel', '', '', $sfx, $support_2nd_lvl=true);
  2542. $orderby_join = '';
  2543. // Create JOIN for ordering items by a custom field (use SFC)
  2544. if ( 'field' == $order[1] ) {
  2545. $orderbycustomfieldid = (int)$params->get('orderbycustomfieldid'.$sfx, 0);
  2546. $orderby_join .= ' LEFT JOIN #__flexicontent_fields_item_relations AS f ON f.item_id = i.id AND f.field_id='.$orderbycustomfieldid;
  2547. }
  2548. // Create JOIN for ordering items by a custom field (Level 2)
  2549. if ( $sfx=='' && 'field' == $order[2] ) {
  2550. $orderbycustomfieldid_2nd = (int)$params->get('orderbycustomfieldid'.'_2nd', 0);
  2551. $orderby_join .= ' LEFT JOIN #__flexicontent_fields_item_relations AS f2 ON f2.item_id = i.id AND f2.field_id='.$orderbycustomfieldid_2nd;
  2552. }
  2553. // Create JOIN for ordering items by a most commented
  2554. if ( in_array('commented', $order) ) {
  2555. $orderby_col = ', count(com.object_id) AS comments_total';
  2556. $orderby_join .= ' LEFT JOIN #__jcomments AS com ON com.object_id = i.id';
  2557. }
  2558. // Create JOIN for ordering items by a most rated
  2559. if ( in_array('rated', $order) ) {
  2560. $orderby_col = ', (cr.rating_sum / cr.rating_count) * 20 AS votes';
  2561. $orderby_join .= ' LEFT JOIN #__content_rating AS cr ON cr.content_id = i.id';
  2562. }
  2563. // Create JOIN for ordering items by author name
  2564. if ( in_array('author', $order) || in_array('rauthor', $order) ) {
  2565. $orderby_join .= ' LEFT JOIN #__users AS u ON u.id = i.created_by';
  2566. }
  2567. // Because query includes specific items it should be fast
  2568. $query = 'SELECT i.*, ext.type_id,'
  2569. .' GROUP_CONCAT(c.id SEPARATOR ",") AS catidlist, '
  2570. .' GROUP_CONCAT(c.alias SEPARATOR ",") AS cataliaslist '
  2571. . @ $orderby_col
  2572. .' FROM #__content AS i '
  2573. .' LEFT JOIN #__flexicontent_items_ext AS ext ON i.id=ext.item_id '
  2574. . @ $item_join
  2575. . @ $orderby_join
  2576. .' LEFT JOIN #__flexicontent_cats_item_relations AS rel ON i.id=rel.itemid ' // to get info for item's categories
  2577. .' LEFT JOIN #__categories AS c ON c.id=rel.catid '
  2578. .' WHERE 1 '
  2579. . @ $item_where
  2580. . $publish_where
  2581. .' GROUP BY i.id '
  2582. . $orderby
  2583. ;
  2584. //echo "<pre>".$query."</pre>";
  2585. return $query;
  2586. }
  2587. // Helper method to create HTML display of an item list according to replacements
  2588. static function createItemsListHTML(&$params, &$item_list, $isform=0, &$parentfield, &$parentitem)
  2589. {
  2590. $db = JFactory::getDBO();
  2591. global $globalcats, $globalnoroute, $fc_run_times;
  2592. if (!is_array($globalnoroute)) $globalnoroute = array();
  2593. // Get fields of type relation
  2594. static $disallowed_fieldnames = null;
  2595. $disallowed_fields = array('relation', 'relation_reverse');
  2596. if ($disallowed_fieldnames===null) {
  2597. $query = "SELECT name FROM #__flexicontent_fields WHERE field_type IN ('". implode("','", $disallowed_fields) ."')";
  2598. $db->setQuery($query);
  2599. $field_name_col = FLEXI_J16GE ? $db->loadColumn() : $db->loadResultArray();
  2600. $disallowed_fieldnames = !$field_name_col ? array() : array_flip($field_name_col);
  2601. }
  2602. // Prefix - Suffix - Separator parameters, replacing other field values if found
  2603. $remove_space = $params->get( 'remove_space', 0 ) ;
  2604. $pretext = $params->get( $isform ? 'pretext_form' : 'pretext', '' ) ;
  2605. $posttext = $params->get( $isform ? 'posttext_form' : 'posttext', '' ) ;
  2606. $separatorf = $params->get( $isform ? 'separator' : 'separatorf' ) ;
  2607. $opentag = $params->get( $isform ? 'opentag_form' : 'opentag', '' ) ;
  2608. $closetag = $params->get( $isform ? 'closetag_form' : 'closetag', '' ) ;
  2609. if($pretext) { $pretext = $remove_space ? $pretext : $pretext . ' '; }
  2610. if($posttext) { $posttext = $remove_space ? $posttext : ' ' . $posttext; }
  2611. switch($separatorf)
  2612. {
  2613. case 0:
  2614. $separatorf = '&nbsp;';
  2615. break;
  2616. case 1:
  2617. $separatorf = '<br />';
  2618. break;
  2619. case 2:
  2620. $separatorf = '&nbsp;|&nbsp;';
  2621. break;
  2622. case 3:
  2623. $separatorf = ',&nbsp;';
  2624. break;
  2625. case 4:
  2626. $separatorf = $closetag . $opentag;
  2627. break;
  2628. default:
  2629. $separatorf = '&nbsp;';
  2630. break;
  2631. }
  2632. // some parameter shortcuts
  2633. $relitem_html = $params->get( $isform ? 'relitem_html_form' : 'relitem_html', '__display_text__' ) ;
  2634. $displayway = $params->get( $isform ? 'displayway_form' : 'displayway', 1 ) ;
  2635. $addlink = $params->get( $isform ? 'addlink_form' : 'addlink', 1 ) ;
  2636. $addtooltip = $params->get( $isform ? 'addtooltip_form' : 'addtooltip', 1 ) ;
  2637. // Parse and identify custom fields
  2638. $result = preg_match_all("/\{\{([a-zA-Z_0-9]+)(##)?([a-zA-Z_0-9]+)?\}\}/", $relitem_html, $field_matches);
  2639. $custom_field_reps = $result ? $field_matches[0] : array();
  2640. $custom_field_names = $result ? $field_matches[1] : array();
  2641. $custom_field_methods = $result ? $field_matches[3] : array();
  2642. /*foreach ($custom_field_names as $i => $custom_field_name)
  2643. $parsed_fields[] = $custom_field_names[$i] . ($custom_field_methods[$i] ? "->". $custom_field_methods[$i] : "");
  2644. echo "$relitem_html :: Fields for Related Items List: ". implode(", ", $parsed_fields ? $parsed_fields : array() ) ."<br/>\n";*/
  2645. // Parse and identify language strings and then make language replacements
  2646. $result = preg_match_all("/\%\%([^%]+)\%\%/", $relitem_html, $translate_matches);
  2647. $translate_strings = $result ? $translate_matches[1] : array('FLEXI_READ_MORE_ABOUT');
  2648. foreach ($translate_strings as $translate_string)
  2649. $relitem_html = str_replace('%%'.$translate_string.'%%', JText::_($translate_string), $relitem_html);
  2650. foreach($item_list as $result)
  2651. {
  2652. // Check if related item is published and skip if not published
  2653. if ($result->state != 1 && $result->state != -5) continue;
  2654. $itemslug = $result->id.":".$result->alias;
  2655. $catslug = "";
  2656. // Check if removed from category or inside a noRoute category or inside a non-published category
  2657. // and use main category slug or other routable & published category slug
  2658. $catid_arr = explode(",", $result->catidlist);
  2659. $catalias_arr = explode(",", $result->cataliaslist);
  2660. for($i=0; $i<count($catid_arr); $i++) {
  2661. $itemcataliases[$catid_arr[$i]] = $catalias_arr[$i];
  2662. }
  2663. $rel_itemid = $result->id;
  2664. $rel_catid = !empty($result->rel_catid) ? $result->rel_catid : $result->catid;
  2665. if ( isset($itemcataliases[$rel_catid]) && !in_array($rel_catid, $globalnoroute) && $globalcats[$rel_catid]->published) {
  2666. $catslug = $rel_catid.":".$itemcataliases[$rel_catid];
  2667. } else if (!in_array($result->catid, $globalnoroute) && $globalcats[$result->catid]->published ) {
  2668. $catslug = $globalcats[$result->catid]->slug;
  2669. } else {
  2670. foreach ($catid_arr as $catid) {
  2671. if ( !in_array($catid, $globalnoroute) && $globalcats[$catid]->published) {
  2672. $catslug = $globalcats[$catid]->slug;
  2673. break;
  2674. }
  2675. }
  2676. }
  2677. $result->slug = $itemslug;
  2678. $result->categoryslug = $catslug;
  2679. }
  2680. // Perform field's display replacements
  2681. if ( $i_slave = $parentfield ? $parentitem->id."_".$parentfield->id : '' ) {
  2682. $fc_run_times['render_subfields'][$i_slave] = 0;
  2683. }
  2684. foreach($custom_field_names as $i => $custom_field_name)
  2685. {
  2686. if ( isset($disallowed_fieldnames[$custom_field_name]) ) continue;
  2687. if ( $custom_field_methods[$i] == 'label' ) continue;
  2688. if ($i_slave) $start_microtime = microtime(true);
  2689. $display_var = $custom_field_methods[$i] ? $custom_field_methods[$i] : 'display';
  2690. FlexicontentFields::getFieldDisplay($item_list, $custom_field_name, $custom_field_values=null, $display_var);
  2691. if ($i_slave) $fc_run_times['render_subfields'][$i_slave] += round(1000000 * 10 * (microtime(true) - $start_microtime)) / 10;
  2692. }
  2693. $display = array();
  2694. foreach($item_list as $result)
  2695. {
  2696. // Check if related item is published and skip if not published
  2697. if ($result->state != 1 && $result->state != -5) continue;
  2698. // a. Replace some custom made strings
  2699. $item_url = JRoute::_(FlexicontentHelperRoute::getItemRoute($result->slug, $result->categoryslug));
  2700. $item_title_escaped = htmlspecialchars($result->title, ENT_COMPAT, 'UTF-8');
  2701. $item_tooltip = ' class="hasTip relateditem" title="'. JText::_('FLEXI_READ_MORE_ABOUT').'::'.$item_title_escaped.'" ';
  2702. $display_text = $displayway ? $result->title : $result->id;
  2703. $display_text = !$addlink ? $display_text : '<a href="'.$item_url.'"'.($addtooltip ? $item_tooltip : '').' >' .$display_text. '</a>';
  2704. $curr_relitem_html = $relitem_html;
  2705. $curr_relitem_html = str_replace('__item_url__', $item_url, $curr_relitem_html);
  2706. $curr_relitem_html = str_replace('__item_title_escaped__', $item_title_escaped, $curr_relitem_html);
  2707. $curr_relitem_html = str_replace('__item_tooltip__', $item_tooltip, $curr_relitem_html);
  2708. $curr_relitem_html = str_replace('__display_text__', $display_text, $curr_relitem_html);
  2709. // b. Replace item properties, e.g. {item->id}, (item->title}, etc
  2710. $null_field = null;
  2711. FlexicontentFields::doQueryReplacements($curr_relitem_html, $null_field, $result);
  2712. // c. Replace HTML display of various item fields
  2713. $err_mssg = 'Cannot replace field: "%s" because it is of not allowed field type: "%s", which can cause loop or other problem';
  2714. foreach($custom_field_names as $i => $custom_field_name) {
  2715. $_field = @ $result->fields[$custom_field_name];
  2716. $custom_field_display = '';
  2717. if ($is_disallowed_field = isset($disallowed_fieldnames[$custom_field_name])) {
  2718. $custom_field_display .= sprintf($err_mssg, $custom_field_name, @ $_field->field_type);
  2719. } else {
  2720. $display_var = $custom_field_methods[$i] ? $custom_field_methods[$i] : 'display';
  2721. $custom_field_display .= @ $_field->{$display_var};
  2722. }
  2723. $curr_relitem_html = str_replace($custom_field_reps[$i], $custom_field_display, $curr_relitem_html);
  2724. }
  2725. $display[] = trim($pretext . $curr_relitem_html . $posttext);
  2726. }
  2727. $display = $opentag . implode($separatorf, $display) . $closetag;
  2728. return $display;
  2729. }
  2730. // **********************************************
  2731. // Helper methods for handling runtime statistics
  2732. // **********************************************
  2733. static function getFieldRenderTimes( &$fields_render_total=0 )
  2734. {
  2735. global $fc_run_times;
  2736. $fields_render = array();
  2737. foreach ($fc_run_times['render_field'] as $field_type => $field_msecs)
  2738. {
  2739. // Total rendering time of fields
  2740. $fields_render_total += $field_msecs;
  2741. // Create Log a message about current field rendering time
  2742. $fld_msg = $field_type." : ". sprintf("%.3f s",$field_msecs/1000000);
  2743. // Check if field rendered other fields as part of it's display
  2744. if ( isset($fc_run_times['render_subfields'][$field_type]) ) {
  2745. $fld_msg .= " <small> - Field rendered other fields. Time was (retrieval+render)= ";
  2746. $fld_msg .= sprintf("%.3f s", $fc_run_times['render_subfields'][$field_type]/1000000).'</small>';
  2747. }
  2748. $fields_render[] = $fld_msg;
  2749. }
  2750. return $fields_render;
  2751. }
  2752. static function getFilterCreationTimes( &$filters_creation_total=0 )
  2753. {
  2754. global $fc_run_times;
  2755. $filters_creation = array();
  2756. if ( isset($fc_run_times['create_filter_init']) ) {
  2757. $filters_creation_total += $fc_run_times['create_filter_init'];
  2758. }
  2759. foreach ($fc_run_times['create_filter'] as $field_type => $filter_msecs)
  2760. {
  2761. // Total creation time of filters
  2762. $filters_creation_total += $filter_msecs;
  2763. // Create Log a message about current filter creation time
  2764. $fld_msg = $field_type." ... ".$fc_run_times['create_filter_type'][$field_type].": ". sprintf("%.3f s",$filter_msecs/1000000);
  2765. $filters_creation[] = $fld_msg;
  2766. }
  2767. return $filters_creation;
  2768. }
  2769. }