Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
listing_engine.inc
1 <?php
18 require_once SQ_CORE_PACKAGE_PATH.'/page/page.inc';
19 require_once SQ_FUDGE_PATH.'/general/text.inc';
20 require_once SQ_FUDGE_PATH.'/general/www.inc';
21 
43 class Listing_Engine extends Page
44 {
45 
46 
52  var $requires_form = FALSE;
53 
57  var $performance_log_file_name = 'performance';
58 
62  var $performance_threshold = 1000;
63 
64 
65  function __construct($assetid=0)
66  {
67  $this->_ser_attrs = TRUE;
68  parent::__construct($assetid);
69 
70  }//end constructor
71 
72 
83  function _createAdditional(&$link)
84  {
85  if (!parent::_createAdditional($link)) return FALSE;
86 
87  // add a bodycopy to this page when creating
88  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
89  $GLOBALS['SQ_SYSTEM']->am->includeAsset('folder');
90 
91  $sub_assets= Array(
92  'type_formats' => 'folder',
93  'position_formats' => 'folder',
94  'group_formats' => 'folder',
95  );
96 
97  $type_formats = NULL;
98  foreach ($sub_assets as $name => $type) {
99  $asset = new $type();
100  $copy_link = Array('asset' => &$this, 'value' => $name ,'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1);
101 
102  $asset->setAttrValue('name', ucwords(str_replace('_',' ', $name)));
103  if (!$asset->create($copy_link)) return FALSE;
104 
105  if ($name == 'type_formats') $type_formats = $asset;
106 
107  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
108  unset($asset);
109  }
110  $asset = new Bodycopy();
111  $copy_link = Array('asset' => &$type_formats, 'value' => 'default_format' ,'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1);
112  $asset->setAttrValue('name', translate('default_format'));
113  $args = Array('content' => $this->_getDefaultBodycopyContent('default_format'));
114  if (!$asset->create($copy_link, $args)) return FALSE;
115 
116  return $this->_createBodycopies();
117 
118  }//end _createAdditional()
119 
120 
128  function _createBodycopies()
129  {
130  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
131  $asset = new Bodycopy();
132  $copy_link = Array('asset' => &$this, 'value' => 'page_contents' ,'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1);
133  $asset->setAttrValue('name', 'Page Contents');
134  $args = Array('content' => $this->_getDefaultBodycopyContent('page_contents'));
135  if (!$asset->create($copy_link, $args)) return FALSE;
136 
137  if (!$this->createNoResultsBodycopy(TRUE)) {
138  return FALSE;
139  }
140 
141  return TRUE;
142 
143  }//end _createBodycopies()
144 
145 
154  function _getDefaultBodycopyContent($bodycopy_code)
155  {
156  switch ($bodycopy_code) {
157  case 'page_contents':
158  return '%asset_listing%';
159  case 'no_results':
160  return '<p>'.translate('cms_listing_no_results').'</p>';
161  case 'default_format':
162  return '%asset_name_linked%';
163  }
164  return '';
165 
166  }//end _getDefaultBodycopyContent()
167 
168 
179  function lockTypes()
180  {
181  $lock_types = parent::lockTypes();
182  $lock_types['content'] = $lock_types['menu'];
183  return $lock_types;
184 
185  }//end lockTypes()
186 
187 
208  function prepareLink(&$asset, $side_of_link, &$link_type, &$value, &$sort_order, &$dependant, &$exclusive)
209  {
210  // if a bodycopy is linking to us then we need to make it a dependant link
211  if ($side_of_link == 'major' && ($asset instanceof Bodycopy) && $dependant != '1') {
212  $dependant = '1';
213  return TRUE;
214  }
215 
216  return FALSE;
217 
218  }//end prepareLink()
219 
220 
229  function describeLink($linkid)
230  {
231  $link = $GLOBALS['SQ_SYSTEM']->am->getLinkById($linkid);
232  switch (strtolower($link['value'])) {
233  case 'root' :
234  return translate('cms_listing_root_node_link_desc');
235  break;
236  default :
237  return parent::describeLink($linkid);
238  break;
239  }
240 
241  }//end describeLink()
242 
243 
252  function isDeletableLink($linkid)
253  {
254  $folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'type_formats');
255  if (!empty($folder_link)) {
256  if ($folder_link['linkid'] == $linkid && !$GLOBALS['SQ_PURGING_TRASH']) {
257  return translate('cms_listing_cannot_delete_type_formats_link', str_replace('_',' ', $this->type()));
258  }
259  }
260 
261  $asset_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'position_formats');
262  if (!empty($asset_folder_link)) {
263  if ($asset_folder_link['linkid'] == $linkid && !$GLOBALS['SQ_PURGING_TRASH']) {
264  return translate('cms_listing_cannot_delete_position_formats_link', str_replace('_',' ', $this->type()));
265  }
266  }
267 
268  $asset_folder_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, 'group_formats');
269  if (!empty($asset_folder_link)) {
270  if ($asset_folder_link['linkid'] == $linkid && !$GLOBALS['SQ_PURGING_TRASH']) {
271  return translate('cms_listing_cannot_delete_group_formats_link', str_replace('_',' ', $this->type()));
272  }
273  }
274 
275  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'page_contents');
276  if (!empty($bodycopy_link)) {
277  if ($bodycopy_link['linkid'] == $linkid && !$GLOBALS['SQ_PURGING_TRASH']) {
278  return translate('cms_cannot_delete_link', $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($this->type(), 'name'));
279  }
280  }
281 
282  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'no_results');
283  if (empty($bodycopy_link)) {
284  // No Results bodycopy is obviously hidden, so we should be getting TYPE_3 link instead
285  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_3, 'bodycopy', TRUE, 'no_results');
286  }
287  if (!empty($bodycopy_link) && $bodycopy_link['linkid'] == $linkid && !$GLOBALS['SQ_PURGING_TRASH']) {
288  return translate('cms_cannot_delete_link', $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($this->type(), 'name'));
289  }
290 
291  return parent::isDeletableLink($linkid);
292 
293  }//end isDeletableLink()
294 
295 
319  function cloneComponentsAdditional(&$clone, $components)
320  {
321  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
322  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
323 
324  if (in_array('attributes', $components) || in_array('all', $components)) {
325 
326  // assetids of asset grouping bodycopies are sotred an array.
327  // They need to be updated the newly cloned bodycopies.
328  $asset_grouping = $this->attr('asset_grouping');
329  if (!empty($asset_grouping)) {
330  $am = $GLOBALS['SQ_SYSTEM']->am;
331  $new_asset_grouping = $asset_grouping;
332  foreach ($asset_grouping as $key => $group) {
333  if (isset($group['format_assetid']) && !empty($group['format_assetid'])) {
334  $bodycopy = $am->getAsset($group['format_assetid']);
335  $group_format_names[$key] = $bodycopy->name;
336  }
337  }
338 
339  $folder = $clone->getFolder('group_formats');
340  if (is_null($folder)) {
341  // something went wrong during the dependants cloning
342  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
343  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
344  trigger_localised_error('CMS0068', E_USER_WARNING);
345  return FALSE;
346  } else {
347  $links_to_bodycopy = $am->getLinks($folder->id, SQ_LINK_TYPE_2, 'bodycopy');
348  if (!empty($links_to_bodycopy)) {
349  foreach ($links_to_bodycopy as $link) {
350  $bodycopy = $am->getAsset($link['minorid']);
351  if (($key = array_search($bodycopy->name, $group_format_names)) !== FALSE) {
352  $new_asset_grouping[$key]['format_assetid'] = $bodycopy->id;
353  }
354  }
355  }
356  $clone->setAttrValue('asset_grouping', $new_asset_grouping);
357  $clone->saveAttributes();
358  }
359  }//end if asset grouping
360 
361  }//end if components
362 
363  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
364  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
365  return TRUE;
366 
367  }//end cloneComponentsAdditional()
368 
369 
377  function _getAllowedLinks()
378  {
379  $page_links = parent::_getAllowedLinks();
380  $page_links[SQ_LINK_TYPE_2]['folder'] = Array('card' => 'M', 'exclusive' => FALSE);
381  $page_links[SQ_LINK_NOTICE]['asset'] = Array('card' => 'M', 'exclusive' => FALSE);
382  $page_links[SQ_LINK_TYPE_2]['bodycopy'] = Array('card' => 2, 'exclusive' => FALSE);
383  $page_links[SQ_LINK_TYPE_3]['bodycopy'] = Array('card' => 1, 'exclusive' => FALSE);
384  return $page_links;
385 
386  }//end _getAllowedLinks()
387 
388 
397  function &getFolder($type='type_formats')
398  {
399  $null = NULL;
400  $link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, $type);
401  if (empty($link)) return $null;
402 
403  $folder = $GLOBALS['SQ_SYSTEM']->am->getAsset($link['minorid'], $link['minor_type_code']);
404  if (is_null($folder)) return $null;
405 
406  return $folder;
407 
408  }//end getFolder()
409 
410 
421  function getFormats($type='type_formats')
422  {
423  if (!isset($this->_tmp['formats'][$type])) {
424  $folder = $this->getFolder($type);
425  $format_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE);
426  if (empty($format_links)) return Array();
427 
428  $formats = Array();
429  foreach ($format_links as $link_data) {
430  $formats[$link_data['minorid']] = $link_data['value'];
431  }
432  $this->_tmp['formats'][$type] = $formats;
433  }
434  return $this->_tmp['formats'][$type];
435 
436  }//end getFormats()
437 
438 
445  function printBody()
446  {
447  // we need a unique cache key to represent the current result page
448  $cache_key = $this->_getCacheKey();
449 
450  $cached_contents = FALSE;
451  if (!empty($cache_key)) {
452  $cm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('cache_manager');
453  $cached_contents = $cm->loadFromCache($this->id, $this->type(), $cache_key);
454  }
455 
456  $group_by = $this->attr('group_by');
457  // if the cache is empty, go ahead and regenerate a new version
458  if ($cached_contents === FALSE) {
459 
460  // get the contents of this page
461  // we do this because when contents are prepared the query vars that are used by the form elements
462  // need to be unset for us to use them in the form action string
463  ob_start();
464  $this->printContents();
465  $contents = ob_get_clean();
466 
467  ob_start();
468  $used_fields = $this->getUsedFormFields();
469 
470  $selections = $this->attr('asset_selections');
471 
472  if (!empty($used_fields) || !empty($selections) || $this->requires_form) {
473 
474  // include the 'select all' js if required
475  $using_select_all = FALSE;
476  foreach ($selections as $selection_name => $selection_settings) {
477  if (array_get_index($selection_settings, 'type', NULL) == 'radio' && !empty($selection_settings['options']['allow_multiple'])) {
478  ?>
479  <script type="text/javascript" src="<?php echo sq_web_path('data') ?>/asset_types/listing_engine/js/select_all.js"></script>
480  <?php
481  $using_select_all = TRUE;
482  break;
483  }
484  }
485 
486  $select_all_code = $using_select_all ? $this->getSelectAllJSCodeKeywordReplacement() : '';
487  $contents = str_replace('%select_all_js_code%', $select_all_code, $contents);
488 
489  if ($this->requires_form || !empty($used_fields)) {
490  // Make a clean URL to submit to
491  $form_method = strtolower($this->_getFormSubmitMethod());
492  $form_url_components = $this->_getFormUrlComponents($used_fields);
493  $form_url = htmlspecialchars($form_url_components['url']);
494  ?>
495 
496  <form id="<?php echo $this->getPrefix() ?>" method="<?php echo $form_method ?>" action="<?php echo $form_url; ?>">
497 
498  <?php
499  if ($form_method == 'get') {
500  // we couldn't preserve get vars in the form action URL so we add them as hidden fields
501  $vars_to_preserve = $_GET;
502  foreach ($used_fields as $used_field) {
503  if (substr($used_field, -1) == '%') {
504  // do a 'like' match
505  foreach ($vars_to_preserve as $i => $v) {
506  if (0 === strpos($i, substr($used_field, 0, -1))) {
507  unset($vars_to_preserve[$i]);
508  }
509  }
510  } else {
511  if (isset($vars_to_preserve[$used_field])) {
512  unset($vars_to_preserve[$used_field]);
513  }
514  }
515  }
516  var_to_hidden_field($vars_to_preserve);
517  }
518  echo $contents;
519  ?>
520 
521  </form>
522 
523  <?php
524  } else {
525 
526  echo $contents;
527 
528  }
529 
530  } else {
531 
532  // no form at all - remove the JS code relations stuff
533  $contents = str_replace('%select_all_js_code%', '', $contents);
534  echo $contents;
535 
536  }
537 
538  if (!empty($cache_key) && (strcmp($group_by, 'random') != 0)) {
539  $cm->saveToCache($this->id, $this->type(), $cache_key, ob_get_contents());
540  }
541  ob_end_flush();
542 
543  } else {
544  // the contents have already been cached, so just output them
545  echo $cached_contents;
546 
547  }//end if no cache
548 
549  }//end printBody()
550 
551 
559  {
560  $base_url = ($this->attr('submit_to_page_url') ? $this->getURL() : current_url());
561  $form_method = strtolower($this->_getFormSubmitMethod());
562 
563  if ($form_method == 'get') {
564  // no point retaining query string vars for a GET form action because the browser
565  // won't submit them - instead we will preserve them as hidden fields later
566  $url = $base_url;
567  } else {
568  $url = replace_query_string_vars(Array(), $base_url);
569  $vars_to_preserve = Array();
570  }
571 
572  return Array(
573  'url' => $url,
574  'url_base' => $base_url,
575  );
576 
577  }//end _getFormUrlComponents()
578 
579 
588  function printNoResultsBodycopy(&$keywords)
589  {
590  $no_results_bodycopy_printed = FALSE;
591  $no_results_bodycopy = $this->getNoResultsBodycopy();
592 
593  // initialise keywords
594  $replacements = Array();
595  $replacements['asset_count'] = 0;
596  $replacements['asset_listing'] = '';
597  $replacements['page_list'] = '';
598  $replacements['page_number'] = 0;
599  $replacements['page_asset_count'] = 0;
600  $replacements['total_pages'] = 0;
601  $replacements['first_asset_position'] = 0;
602  $replacements['last_asset_position'] = 0;
603  $replacements['previous_page'] = '';
604  $replacements['previous_page_href'] = '';
605  $replacements['next_page'] = '';
606  $replacements['next_page_href'] = '';
607 
608  // print the "No Results" bodycopy if available
609  if (!is_null($no_results_bodycopy)) {
610  foreach ($keywords as $word) {
611  if (isset($replacements[$word])) continue;
612  $replacements[$word] = $this->getKeywordReplacement($word);
613  }
614 
615  // by this time we have all the replacements for our regular keywords
616  // check to see if there are any modifiers passed on to us
617  foreach ($replacements as $kw => $res) {
618  if ($res == '%'.$kw.'%') {
619  $plain_keyword = parse_keyword($kw, $modifiers);
620  if (isset($replacements[$plain_keyword]) && $replacements[$plain_keyword] !== '%'.$plain_keyword.'%' && !empty($modifiers)) {
621  $replacements[$kw] = $replacements[$plain_keyword];
622  // Apply modifiers
623  apply_keyword_modifiers($replacements[$kw], $modifiers, Array('assetid' => $this->id, 'replacements' => $replacements));
624  }
625  }
626  }
627 
628  $no_results_bodycopy->setKeywordReplacements($replacements);
629  $no_results_bodycopy->printBody();
630  $no_results_bodycopy_printed = TRUE;
631  }
632 
633  return $no_results_bodycopy_printed;
634 
635  }//end printNoResultsBodycopy()
636 
637 
644  function printContents()
645  {
646  $bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'page_contents');
647  $bodycopy_no_result_link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'no_results');
648  if (empty($bodycopy_link)) return FALSE;
649 
650  $format_bodycopy = $GLOBALS['SQ_SYSTEM']->am->getAsset($bodycopy_link['minorid'], $bodycopy_link['minor_type_code']);
651 
652  if (is_null($format_bodycopy)) return FALSE;
653 
654  require_once SQ_FUDGE_PATH.'/general/text.inc';
655  $keywords = $format_bodycopy->getKeywords();
656  if ($bodycopy_no_result_link) {
657  $format_bodycopy_no_result = $GLOBALS['SQ_SYSTEM']->am->getAsset($bodycopy_no_result_link['minorid'], $bodycopy_no_result_link['minor_type_code']);
658  $keywords = array_merge($format_bodycopy_no_result->getKeywords(),$keywords);
659  }
660  $replacements = Array();
661  // Timer!
662  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'getAssetList');
663  $children = $this->getAssetList();
664  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'getAssetList');
665 
666 
667  /*
668  * Performance Monitor
669  * Log performance warnings when listing engine tries to list a large number of assets
670  *
671  */
672  $total_numbers = isset($children) ? count($children) : 0;
673  if ($total_numbers >= $this->performance_threshold) {
674  $log_contents = 'Listing page #'.$this->id.' is trying to list '.$total_numbers.' assets, which exceeds the performance monitor threshold of '.$this->performance_threshold;
675  log_write($log_contents, $this->performance_log_file_name, E_USER_WARNING);
676  }
677 
678  $exclude_assets = $this->attr('exclude_assets');
679 
680  // get children of exclude root nodes, add them to the exclude list
681  $exclude_root_nodes = $this->attr('exclude_root_nodes');
682  $exlcude_rootids_info = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo($exclude_root_nodes, Array(), TRUE, 'type_code');
683  foreach($exclude_root_nodes as $key =>$value) {
684  if (isset($exlcude_rootids_info[$value])) {
685  $exclude_children = $GLOBALS['SQ_SYSTEM']->am->getChildren($value);
686  $exclude_assets = array_merge($exclude_assets, array_keys($exclude_children));
687  } else {
688  trigger_error('Cannot find the asset #'.$value.' that has been set as an exclude root node on the listing asset #'.$this->id);
689  }
690  }
691 
692  if (!empty($exclude_assets)) {
693  foreach ($exclude_assets as $index => $assetid) {
694  if (isset($children[$assetid])) {
695  unset($children[$assetid]);
696  }//end if
697  }//end foreach
698  }//end if
699 
700  if($this->attr('exclude_current_asset')) {
701  foreach ($children as $assetid => $data) {
702  $url = strip_url($GLOBALS['SQ_SYSTEM']->am->getAssetURL($assetid));
703  $requester = strip_url(current_url());
704  if ($requester === $url) {
705  unset($children[$assetid]);
706  }
707  }
708  }
709 
710 
711  $children =& $this->convertProxyAssetTypes($children);
712  // Timer!
713  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'filterAssetList');
714  $this->filterAssetList($children);
715  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'filterAssetList');
716 
717  $num_kids = count($children);
718  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'replaceFormatKeywords');
719  foreach ($keywords as $word) {
720  $replacements[$word] = $this->getKeywordReplacement($word);
721  }
722 
723  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'replaceFormatKeywords');
724 
725 
726  // if there are no children available, just print the page bodycopy -
727  // or the NO RESULTS bodycopy if it exists
728  if (empty($children)) {
729  $no_results_bodycopy_printed = $this->printNoResultsBodycopy($keywords);
730 
731  if (!$no_results_bodycopy_printed) {
732  $selections = $this->attr('asset_selections');
733  foreach ($selections as $selection_name => $settings) {
734  $replacements[$selection_name.'_check_all'] = '';
735  $replacements[$selection_name.'_check_all_in_group'] = '';
736  }
737 
738  $format_bodycopy->setKeywordReplacements($replacements);
739  $format_bodycopy->printBody();
740  }
741  return;
742  }
743 
744  if (!in_array('asset_listing', $keywords)) {
745  // there is no asset listing keyword in the contents, so we dont
746  // need to do all the extra processing for the listing
747  $format_bodycopy->printBody();
748  return;
749  }
750 
751  if ($this->attr('group_by') == 'grouped') {
752  // Timer!
753  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'getAssetsInfo');
754  // this flag can be set if getAssetList() function returns different format of array.
755  // you can find the example in Search List. Also see getAssetList()'s comment
756  if (!isset($this->_tmp['no_asset_info_url_cache'])) {
757  $this->_tmp['assets_info'] = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo(array_keys($children));
758  $this->_tmp['asset_urls'] = $GLOBALS['SQ_SYSTEM']->am->getAssetURL(array_keys($children));
759  }
760  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'getAssetsInfo');
761 
762  // Timer!
763  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'groupAssets');
764  // the list is sorted and positions set, now we can split to groups
765  $this->groupAssetsRecursively($this->attr('asset_grouping'), $children, $children);
766  $this->sortGroups($this->attr('asset_grouping'), $children, $children);
767  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'groupAssets');
768  }
769 
770  // Timer!
771  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'chunkAssets');
772 
773  // Which position in the list to start from
774  $start_pos = $this->attr('start_position') - 1;
775  if ($start_pos > 0) {
776  if ($this->attr('group_by') != 'grouped') {
777  $children = $start_pos < count($children) ? array_slice($children, $start_pos, count($children) - $start_pos , TRUE) : Array();
778  } else {
779  // For groups, set the start position for the individual group listing
780  foreach($children as $group_id => $child_group) {
781  $children[$group_id] = $start_pos < count($child_group) ? array_slice($child_group, $start_pos, count($child_group) - $start_pos, TRUE) : Array();
782  }//end foreach
783  }//end else
784  }//end if start position
785 
786  $num_per_page = $this->_getNumPerPage();
787 
788  $asset_result_page_var = 'result_'.$this->id.'_result_page';
789  $generic_result_page_var = 'result_page';
790 
791  // Include any GET variables passed (for instance, from the nesting asset)
792  // However, make sure that existing values in _REQUEST are not overwritten
793  if (!empty($_GET)) {
794  $_REQUEST = array_merge($_GET, $_REQUEST);
795  }
796 
797  // have [assetid]_result_page take precedence over result_page
798  $result_page = 1;
799  if (isset($_REQUEST[$generic_result_page_var])) {
800  $result_page = (int)$_REQUEST[$generic_result_page_var];
801  }
802  if (isset($_REQUEST[$asset_result_page_var])) {
803  $result_page = (int)$_REQUEST[$asset_result_page_var];
804  }
805 
806  if ($result_page <= 0) $result_page = 1;
807 
808  // get our page chunk here
809  $todo =& $this->getChunk($children, $replacements, $keywords, $result_page, $num_per_page);
810 
811  // If random grouping, we should now shove fixed positions in (it would
812  // normally be in filterAssetList). Note that in this case we must still
813  // respect the 'per page' settings - but with only one page in random
814  // mode that's easy to take care of
815  if ($this->attr('group_by') == 'random') {
816  $this->adjustAssetPositions($todo);
817  while (count($todo) > max($num_per_page, 1)) {
818  array_pop($todo);
819  }
820  }
821  // Timer!
822  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'chunkAssets');
823 
824  // For non-grouped listing, get asset info of chucked assets here
825  if ($this->attr('group_by') != 'grouped') {
826  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'getAssetsInfo');
827  // this flag can be set if getAssetList() function returns different format of array.
828  // you can find the example in Search List. Also see getAssetList()'s comment
829  if (!isset($this->_tmp['no_asset_info_url_cache'])) {
830  $this->_tmp['assets_info'] = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo(array_keys($todo));
831  $this->_tmp['asset_urls'] = $GLOBALS['SQ_SYSTEM']->am->getAssetURL(array_keys($todo));
832  }
833  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'getAssetsInfo');
834  }
835 
836  // Timer!
837  $GLOBALS['SQ_SYSTEM']->pm->startTimer($this, 'printBody');
838  // get any extra keyword replacements from this asset or asset_listing
839  $replacements = $this->getContentsKeywordReplacements($keywords) + $replacements;
840  $num_assets_showing = count($todo);
841  if ($num_assets_showing > 0) {
842  ob_start();
843  $this->printAssetList($todo);
844  $global_contents = ob_get_contents();
845  ob_end_clean();
846  } else {
847  $no_results_bodycopy_printed = $this->printNoResultsBodycopy($keywords);
848 
849  if (!$no_results_bodycopy_printed) {
850  $selections = $this->attr('asset_selections');
851  foreach ($selections as $selection_name => $settings) {
852  $replacements[$selection_name.'_check_all'] = '';
853  $replacements[$selection_name.'_check_all_in_group'] = '';
854  }
855 
856  $format_bodycopy->setKeywordReplacements($replacements);
857  $format_bodycopy->printBody();
858  }
859  return;
860  }
861 
862  // the global replacement to print the asset listing
863  $replacements['asset_listing'] = $global_contents;
864  if ($this->attr('group_by') != 'grouped') {
865  $replacements['asset_count'] = $num_kids;
866  } else {
867  $total_assets = $this->_arrayCountRecursive($children, count($this->attr('asset_grouping')));
868  $replacements['asset_count'] = $total_assets;
869  $replacements['unique_asset_count'] = $num_kids;
870  }
871 
872  $result_page = $replacements['page_number'];
873  $replacements['page_asset_count'] = $num_assets_showing;
874  // compatibility
875 
876  if ($replacements['asset_count'] == 0) {
877  $replacements['first_asset_position'] = 0;
878  } else if ($num_per_page == 0) {
879  $replacements['first_asset_position'] = 1;
880  } else {
881  $replacements['first_asset_position'] = max(1, $num_per_page * ($result_page - 1) + 1);
882  }
883 
884  if ($num_per_page == 0) {
885  $replacements['last_asset_position'] = $replacements['asset_count'];
886  } else {
887  $replacements['last_asset_position'] = min($replacements['asset_count'], $num_per_page * $result_page);
888  }
889  $replacements['select_all_js_code'] = '%select_all_js_code%';
890 
891  // by this time we have all the replacements for our regular keywords
892  // check to see if there are any modifiers passed on to us
893  foreach ($replacements as $kw => $res) {
894  if ($res == '%'.$kw.'%') {
895  $plain_keyword = parse_keyword($kw, $modifiers);
896  // check to see if the keyword is actually replaced and
897  // the replacement isn't just the keyword itself
898  $keywords_is_replaced = isset($replacements[$plain_keyword]) && $replacements[$plain_keyword] != '%'.$plain_keyword.'%' && $replacements[$plain_keyword] != $plain_keyword;
899  if (!empty($modifiers) && $keywords_is_replaced) {
900  $replacements[$kw] = $replacements[$plain_keyword];
901  // Apply modifiers
902  apply_keyword_modifiers($replacements[$kw], $modifiers, Array('assetid' => $this->id, 'replacements' => $replacements));
903  }
904  }
905  }
906 
907  // print the contents of page - replacing the global keywords
908  $format_bodycopy->setKeywordReplacements($replacements);
909  if (!in_array('select_all_js_code', $keywords)) {
910  // there is no asset listing keyword in the contents, so we dont
911  // need to do all the extra processing for the listing
912  echo '%select_all_js_code%';
913  }
914  $format_bodycopy->printBody();
915 
916  // clean up after ourselves
917  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($format_bodycopy);
918 
919  // Timer!
920  $GLOBALS['SQ_SYSTEM']->pm->stopTimer($this, 'printBody');
921  }//end printContents()
922 
923 
930  function _getCacheKey()
931  {
932  $group_by = $this->attr('group_by');
933  $contextid = $GLOBALS['SQ_SYSTEM']->getContextId();
934 
935  // we work out what screen we are on here so we can correctly cache the screen with a unique ID
936  // note that we never cache the results of a random asset listing for obvious reasons
937  $cache_key = '';
938 
939  $asset_result_page_var = 'result_'.$this->id.'_result_page';
940  $generic_result_page_var = 'result_page';
941 
942  switch ($group_by) {
943  case 'number' :
944  // have [assetid]_result_page take precedence over result_page
945  $cache_key = 1;
946  if (isset($_REQUEST[$generic_result_page_var])) {
947  $cache_key = (int)$_REQUEST[$generic_result_page_var];
948  }
949  if (isset($_REQUEST[$asset_result_page_var])) {
950  $cache_key = (int)$_REQUEST[$asset_result_page_var];
951  }
952  if ($cache_key <= 0) $cache_key = 1;
953  break;
954 
955  case 'letter' :
956  // have [assetid]_result_page take precedence over result_page
957  $cache_key = 'A';
958  if (isset($_REQUEST[$generic_result_page_var])) {
959  $cache_key = $_REQUEST[$generic_result_page_var];
960  }
961  if (isset($_REQUEST[$asset_result_page_var])) {
962  $cache_key = $_REQUEST[$asset_result_page_var];
963  }
964  break;
965  }
966 
967  // cache key takes into account different sorting scenarios
968  $cache_key .= serialize($this->getSortInfo());
969  $cache_key .= (int)$this->isDescending();
970 
971  // if we don't have a cache key from the listing it's because we shouldn't be caching
972  if ($cache_key) {
973  // as per bug #2918, nested asset lists cache too much, so here is the workaround
974  $pmap = unserialize($this->attr('parameter_map'));
975  if (isset($pmap['root_node'])) {
976  $parameter_map = $this->getAttribute('parameter_map');
977  $nodes = $parameter_map->getParameterValue('root_node');
978  if (is_array($nodes)) {
979  $root_nodes = $nodes;
980  } else {
981  $root_nodes[] = $nodes;
982  }
983  } else {
984  $root_nodes = $this->getRootNodes(FALSE);
985  }
986 
987  //as per bug #4608, cache key needs to be unique for nested search pages with dynamic stored_query_session
988  if ($this->type() == 'search_page') {
989  $pmap = unserialize($this->attr('stored_query_session'));
990  if (!empty($pmap)) {
991  $pmap = $this->getAttribute('stored_query_session');
992  $pmap_params = $pmap->getParameters();
993  if (is_array($pmap_params) && !empty($pmap_params)) {
994  foreach ($pmap_params as $params) {
995  $sq_index = $pmap->getParameterValue($params);
996  if (isset($sq_index)) $stored_queries[] = $sq_index;
997  }
998  }
999  }
1000 
1001  if (!empty($stored_queries)) {
1002  $cache_key .= '-'.serialize($stored_queries);
1003  }
1004  }
1005 
1006  // funnelback search page dynamic parameters
1007  if ($this->type() == 'funnelback_search_page') {
1008  $pmap = $this->getAttribute('dynamic_parameters');
1009  if(!empty($pmap)) {
1010  $pmap_params = $pmap->getParameters();
1011  foreach ($pmap_params as $params) {
1012  $dynamic_query = $pmap->getParameterValue($params);
1013  $cache_key .= '-'.$params.':'.$dynamic_query;
1014  }
1015  }
1016  }
1017 
1018  if (!empty($root_nodes)) {
1019  $cache_key .= '-'.implode('-', $root_nodes);
1020  }
1021 
1022  $cache_key .= $this->_getNumPerPage();
1023 
1024  // Add context to the cache key, because all our bodycopies could be
1025  // customised and we are caching the entire contents of the page
1026  $cache_key .= '-ctx'.$contextid;
1027  }
1028 
1029  return $cache_key;
1030 
1031  }//end _getCacheKey()
1032 
1033 
1040  function _getNumPerPage()
1041  {
1042  $num_per_page = $this->attr('num_per_page');
1043 
1044  // Check for keywords in the num_per_page attribute
1045  require_once SQ_FUDGE_PATH.'/general/text.inc';
1046  replace_global_keywords($num_per_page);
1047  $num_per_page_keywords = retrieve_keywords_replacements($num_per_page);
1048  $replacements = Array();
1049  foreach ($num_per_page_keywords as $keyword) {
1050  $replacements[$keyword] = $this->getKeywordReplacement($keyword);
1051  }//end foreach
1052  replace_keywords($num_per_page, $replacements);
1053  settype($num_per_page, 'integer');
1054 
1055  return $num_per_page;
1056 
1057  }//end _getNumPerPage()
1058 
1059 
1078  function getAssetList()
1079  {
1080  return Array();
1081 
1082  }//end getAssetList()
1083 
1084 
1095  function sortAssetList($asset_list, $sort_info)
1096  {
1097  // If the option to sort is No sorting, just return the asset list here and don't do any sorting
1098  if (isset($sort_info['name']) && $sort_info['name'] == 'No Sorting') {
1099  return $asset_list;
1100  }//end if
1101 
1102  if (count($asset_list) == 1 || empty($sort_info)) {
1103  return $asset_list;
1104  }
1105  if ((!is_array($asset_list)) || (empty($asset_list))) {
1106  return Array();
1107  }
1108 
1109  // random sort, shuffle the asset list
1110  if ($sort_info['type'] == '' && $sort_info['name'] == 'Random') {
1111  $keys = array_keys($asset_list);
1112  shuffle($keys);
1113  $rand_asset_list = Array();
1114  foreach ($keys as $key) {
1115  $rand_asset_list[$key] = $asset_list[$key];
1116  }
1117  return ($rand_asset_list);
1118  }
1119 
1120  $skipped_assets = Array();
1121  $sort_assets = Array();
1122  $reverse_sort = $this->isDescending();
1123 
1124  // default to a numeric sort until we are proven otherwise
1125  $sort_numeric = TRUE;
1126 
1127  // check and prepare sorting data
1128  $is_sort_by_attribute = ($sort_info['type'] == 'asset_attrib');
1129  $is_sort_by_field = ($sort_info['type'] == 'field');
1130  $is_sort_by_metadata = ($sort_info['type'] == 'metadata');
1131  $is_sort_by_keyword = ($sort_info['type'] == 'keyword');
1132 
1133  $sort_params =& $sort_info['params'];
1134  $assetids = array_keys($asset_list);
1135 
1136  if ($is_sort_by_attribute) {
1137  $sort_by_attr = $sort_params['attribute'];
1138  $sort_by_type_code = $sort_params['type_code'];
1139 
1140  foreach ($this->attr('types') as $type_code => $inherit) {
1141  // initially we plan not to process this type code
1142  $process = FALSE;
1143  if ($type_code == $sort_by_type_code) {
1144  $process = TRUE;
1145  } else {
1146  if ($inherit) {
1147  // we have to find our closest ancestor with a sort field
1148  $type_anc = $GLOBALS['SQ_SYSTEM']->am->getTypeAncestors($type_code);
1149  array_unshift($type_anc, $type_code);
1150 
1151  foreach ($type_anc as $this_type_code) {
1152  if ($this_type_code == $sort_by_type_code) {
1153  $process = TRUE;
1154  break;
1155  }
1156  }
1157  }
1158  }
1159 
1160  // get the assetids that may be affected
1161  $rem_assetids = array_keys($GLOBALS['SQ_SYSTEM']->am->getAssetInfo($assetids, $type_code, !$inherit));
1162 
1163  // do we have an attribute to sort on for this type code?
1164  // if we don't, we'll have to come back to it as a skipped asset
1165 
1166  if ($process) {
1167  if (isset($sort_params['attr_type']) && !in_array($sort_params['attr_type'], Array('int', 'float'))) {
1168  $sort_numeric = FALSE;
1169  }
1170 
1171  $attr_info = $GLOBALS['SQ_SYSTEM']->am->getAttributeValuesByName($sort_by_attr, $sort_by_type_code, $rem_assetids);
1172  foreach ($rem_assetids as $assetid) {
1173  if (isset($attr_info[$assetid])) {
1174  $sort_assets[$assetid] = strtolower($attr_info[$assetid]);
1175 
1176  if (($pos = array_search($assetid, $skipped_assets)) !== FALSE) {
1177  unset($skipped_assets[$pos]);
1178  }
1179  }
1180  }
1181 
1182  $skipped_assets = array_merge($skipped_assets, array_diff($rem_assetids,array_keys($attr_info)));
1183  } else {
1184  // add to the skipped assets array if still in the list
1185  // of used type codes
1186  $skipped_assets = array_merge($skipped_assets, $rem_assetids);
1187  }
1188  }//end foreach
1189  } else if ($is_sort_by_metadata) {
1190  // Sort by Metadata field
1191  $sort_numeric = FALSE;
1192  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
1193  $fieldid = $sort_info['field'];
1194 
1195  if (isset($sort_info['field'])) {
1196  $fieldid = $sort_info['field'];
1197  }
1198  if ($fieldid) {
1199  $field_default_val = $mm->getMetadataFieldDefaultValue($fieldid);
1200  } else {
1201  $field_default_val = '';
1202  }
1203 
1204  $presentation_value = FALSE;
1205  if ($this->attr('metadata_sort_type') == 'presentation') {
1206  $presentation_value = TRUE;
1207  }
1208 
1209  foreach ($asset_list as $assetid => $type) {
1210  $meta_field_val = $mm->getMetadataValueByAssetid($assetid,$fieldid,TRUE,$presentation_value);
1211  $field_value = $field_default_val;
1212  if (isset($meta_field_val)) {
1213  $field_value = $meta_field_val;
1214 
1215  // get keyword replacements
1216  if (preg_match('/^%(.*)%$/',$field_value,$matches)) {
1217  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
1218  if ($asset->id) {
1219  $field_value = $asset->getKeywordReplacement($matches[1]);
1220  }
1221  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
1222  }
1223  }
1224  $sort_assets[$assetid] = strtolower($field_value);
1225  }
1226 
1227  } else if ($is_sort_by_keyword) {
1228  foreach ($asset_list as $assetid => $type) {
1229  $tmp_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
1230  $replacement = $tmp_asset->getKeywordReplacement($sort_info['keyword']);
1231  if (!empty($replacement)) {
1232  $sort_assets[$assetid] = $replacement;
1233  } else {
1234  // add assets that does not have this keyword to the bottom of the list
1235  $skipped_assets[] = $assetid;
1236  }
1237  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($tmp_asset);
1238  }
1239  $sort_numeric = FALSE;
1240  } else {
1241  $sort_field = $sort_params['field'];
1242  $sortable_info = $this->getSortableAssetInfo();
1243  if (isset($sortable_info[$sort_field])) {
1244  $sort_assets = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo($assetids, NULL, TRUE, $sort_field);
1245  for (reset($sort_assets); NULL !== ($assetid = key($sort_assets)); next($sort_assets)) {
1246  $sort_assets[$assetid] = strtolower($sort_assets[$assetid]);
1247  }
1248 
1249  if ($sort_field != 'assetid') $sort_numeric = FALSE;
1250  } else {
1251 
1252  if ($reverse_sort) {
1253  return array_reverse($asset_list, TRUE);
1254  }
1255 
1256  // sort field is not defined, return the assetlist
1257  return $asset_list;
1258  }
1259 
1260  }
1261 
1262  if ($sort_numeric) {
1263  uasort($sort_assets, create_function('$a,$b','return '.($reverse_sort ? '$b - $a' : '$a - $b').';'));
1264  } else {
1265  if ($reverse_sort) {
1266  arsort($sort_assets);
1267  } else {
1268  asort($sort_assets);
1269  }
1270  }
1271 
1272  $assets_to_list = Array();
1273  $sorted_assets = array_keys($sort_assets);
1274  foreach ($sorted_assets as $assetid) {
1275  $assets_to_list[$assetid] = $asset_list[$assetid];
1276  }
1277 
1278  // add skipped assets to the bottom of the list
1279  foreach ($skipped_assets as $assetid) {
1280  $assets_to_list[$assetid] = $asset_list[$assetid];
1281  }
1282  return $assets_to_list;
1283 
1284  }//end sortAssetList()
1285 
1286 
1297  function filterAssetList(&$todo)
1298  {
1299  $group_by = $this->attr('group_by');
1300 
1301  // remove non-live assets that the user doesnt have write access to
1302  if (!empty($todo)) $this->filterAssetStatuses($todo);
1303  if (empty($todo)) return;
1304 
1305  // remove unwanted asset types
1306  switch ($group_by) {
1307  case 'number' :
1308  case 'grouped' :
1309  $this->filterAssetTypes($todo);
1310  $todo = $this->sortAssetList($todo, $this->getSortInfo());
1311  $this->adjustAssetPositions($todo);
1312  break;
1313 
1314  case 'random' :
1315  // adjusting of asset positions happens in printContents(),
1316  // after the random chunk is gathered
1317  $this->filterAssetTypes($todo);
1318  break;
1319 
1320  case 'letter' :
1321  $filtered_todo = Array();
1322  for (reset($todo); NULL !== ($todo_id = key($todo)); next($todo)) {
1323  $filtered_todo[$todo_id] = Array( 0 => Array( 'type_code' => $todo[$todo_id][0]['type_code']));
1324  }
1325 
1326  $this->filterAssetTypes($filtered_todo);
1327  foreach (array_keys($todo) as $todo_id) {
1328  if (!isset($filtered_todo[$todo_id])) {
1329  unset($todo[$todo_id]);
1330  }
1331  }
1332  unset($filtered_todo);
1333 
1334  $todo = $this->sortAssetList($todo, $this->getSortInfo());
1335 
1336  break;
1337  }//end switch
1338 
1339  }//end filterAssetList()
1340 
1341 
1354  function filterAssetStatuses(&$todo)
1355  {
1356  if (empty($todo)) return;
1357 
1358  // Sort the real assets from the shadows and bridges
1359  // We'll do status and permission checks on the bridges and apply
1360  // those results to their shadow assets.
1361  $real_assetids = Array();
1362  $bridge_assetids = Array();
1363  foreach ($todo as $assetid => $details) {
1364  if (FALSE !== strpos($assetid, ':')) {
1365  $bridge_assetids[] = strtok($assetid, ':');
1366  } else {
1367  $real_assetids[] = $assetid;
1368  }
1369  }
1370 
1371  $wanted_statuses = $this->attr('statuses');
1372  $db = MatrixDAL::getDb();
1373 
1374  if (!empty($wanted_statuses) && (array_sum($wanted_statuses) != SQ_SC_STATUS_ALL)) {
1375  // find which of the assets have the right statuses
1376  foreach (array_unique(array_merge($real_assetids, $bridge_assetids)) as $assetid) {
1377  $query_assetids[] = MatrixDAL::quote((string) $assetid);
1378  }
1379  $in_clauses = Array();
1380  foreach (array_chunk($query_assetids, 999) as $chunk) {
1381  $in_clauses[] = ' assetid IN ('.implode(', ', $chunk).')';
1382  }
1383  $sql = 'SELECT
1384  assetid
1385  FROM
1386  sq_ast
1387  WHERE
1388  ('.implode(' OR ', $in_clauses).')
1389  AND status IN ('.implode(', ', $wanted_statuses).')';
1390 
1391  $status_matches = Array();
1392  try {
1393  $query = MatrixDAL::preparePdoQuery($sql);
1394  $status_matches = MatrixDAL::executePdoAssoc($query, 0);
1395  } catch (Execption $e) {
1396  throw new Exception($e->getMessage());
1397  }
1398  } else {
1399  // We want all statuses
1400  $status_matches = array_unique(array_merge($real_assetids, $bridge_assetids));
1401  }
1402 
1403  if (!$GLOBALS['SQ_SYSTEM']->userRoot() && !$GLOBALS['SQ_SYSTEM']->userSystemAdmin()) {
1404  // Remove any sub-live assets unless we have effective read access to them
1405  if (!empty($wanted_statuses)) {
1406  $may_have_sublives = FALSE;
1407  foreach ($wanted_statuses as $status) {
1408  if ($status < SQ_STATUS_LIVE) {
1409  $may_have_sublives = TRUE;
1410  break;
1411  }
1412  }
1413  } else {
1414  $may_have_sublives = TRUE;
1415  }
1416 
1417  if ($may_have_sublives) {
1418  $sub_live_assets = Array();
1419  foreach ($status_matches as $assetid) {
1420  $query_assetids[] = MatrixDAL::quote((string) $assetid);
1421  }
1422  $in_clauses = Array();
1423  foreach (array_chunk($query_assetids, 999) as $chunk) {
1424  $in_clauses[] = ' assetid IN ('.implode(', ', $chunk).')';
1425  }
1426  $sql = 'SELECT
1427  assetid, type_code
1428  FROM
1429  sq_ast
1430  WHERE
1431  ('.implode(' OR ', $in_clauses).')
1432  AND status < :status';
1433  $sub_live_assets = NULL;
1434  try {
1435  $query = MatrixDAL::preparePdoQuery($sql);
1436  MatrixDAL::bindValueToPdo($query, 'status', SQ_STATUS_LIVE);
1437  $sub_live_assets = MatrixDAL::executePdoGroupedAssoc($query);
1438  } catch (Exception $e) {
1439  throw new Exception($e->getMessage());
1440  }//end try catch
1441 
1442  foreach ($status_matches as $i => $v) {
1443  if (isset($sub_live_assets[$v])) {
1444  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($v, $sub_live_assets[$v][0]['type_code']);
1445  if (is_null($asset) || !$asset->readAccess()) {
1446  unset($status_matches[$i]);
1447  }
1448  }
1449  }
1450  }
1451  }//end if not root or sysadmin
1452 
1453  // Now that we know who the valid candidates are, weed the rest
1454  // out of the todo list, treating shadows according to their bridge's status
1455  foreach (array_keys($todo) as $i) {
1456  if (FALSE !== strpos($i, ':')) {
1457  $bridgeid = strtok($i, ':');
1458  if (!in_array($bridgeid, $status_matches)) {
1459  unset($todo[$i]);
1460  }
1461  } else {
1462  if (!in_array($i, $status_matches)) unset($todo[$i]);
1463  }
1464  }
1465 
1466  }//end filterAssetStatuses()
1467 
1468 
1483  function filterAssetTypes(&$todo)
1484  {
1485  $allowed_types = $this->getAllowedAssetTypeMap();
1486  if (empty($allowed_types)) {
1487  $todo = Array();
1488  return;
1489  }
1490 
1491  foreach ($todo as $assetid => $type_codes) {
1492  $type_code = $type_codes[0]['type_code'];
1493  //#4539 if the type_code empty...try to get it
1494  if (empty ($type_code)){
1495  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
1496  $type_code = $asset->type();
1497  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
1498  }
1499  if (!isset($allowed_types[$type_code])) {
1500  unset($todo[$assetid]);
1501  } else {
1502  $todo[$assetid] = Array('0' => Array(
1503  'type_code' => $allowed_types[$type_code],
1504  ),
1505  );
1506  }
1507  }
1508 
1509  }//end filterAssetTypes()
1510 
1511 
1528  {
1529  if (isset($this->_tmp['allowed_asset_map'])) {
1530  return $this->_tmp['allowed_asset_map'];
1531  }
1532 
1533  $wanted_types = $this->attr('types');
1534  if (empty($wanted_types)) return Array();
1535 
1536  $allowed_asset_map = Array();
1537  foreach ($wanted_types as $allowed_parent_type => $inherit) {
1538  if (trim($allowed_parent_type) == '') {
1539  continue;
1540  } else if (!$inherit) {
1541  $allowed_asset_map[$allowed_parent_type] = $allowed_parent_type;
1542  } else {
1543  $descendants = $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($allowed_parent_type, TRUE);
1544  foreach ($descendants as $allowed_type) {
1545  if (!isset($allowed_asset_map[$allowed_type])) {
1546  $allowed_asset_map[$allowed_type] = $allowed_parent_type;
1547  }
1548  }
1549  }
1550  }
1551 
1552  $this->_tmp['allowed_asset_map'] = $allowed_asset_map;
1553  return $allowed_asset_map;
1554 
1555  }//end getAllowedAssetTypeMap()
1556 
1557 
1568  function adjustAssetPositions(&$todo)
1569  {
1570  $asset_positions = $this->attr('asset_positions');
1571  if (empty($asset_positions)) return;
1572 
1573  // replace the original todo list because we are going to build another one
1574  $all_assets = $todo;
1575  $adjusted_positions = Array();
1576  $allowed_asset_map = $this->getAllowedAssetTypeMap();
1577 
1578  // re-adjustment depends on the fact the asset_positions is oredered by position
1579  // note: list length can only be extended by placing items inside it, not at the end
1580  // this means that if the list has N elements, you cannot customize position N+1,
1581  // unless you first insert something into the middle of the list, which will make it N+1 long
1582  $maximum_possible_position = count($all_assets);
1583  foreach ($asset_positions as $position => $asset_data) {
1584  $id = $asset_data['id'];
1585 
1586  // Check to see whether we have read access to the asset - if we
1587  // don't (or if the asset no longer exists at all...!), don't use it
1588  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($id);
1589  if (is_null($asset)) {
1590  $read_access = FALSE;
1591  } else {
1592  $read_access = $asset->readAccess();
1593  }
1594  unset($asset);
1595 
1596  if (!$read_access) {
1597  unset($todo[$id]);
1598  continue;
1599  }
1600 
1601  if ($position > $maximum_possible_position) {
1602  // if this position does not fit, neither will anything else
1603  break;
1604  }
1605 
1606  if (!isset($all_assets[$id])) {
1607  // inserting a new asset
1608  $maximum_possible_position++;
1609  $type = $asset_data['type'];
1610  $all_assets[$id] = array_get_index($allowed_asset_map, $type, $type);
1611  } else {
1612  // rearranging existing list
1613  unset($todo[$id]);
1614  }
1615 
1616  $adjusted_positions[$position] = $id;
1617  }//end foreach
1618 
1619  // check if adjustment is still required
1620  if (empty($adjusted_positions)) return;
1621 
1622  $originally_ordered_ids = array_keys($todo);
1623  $todo = Array();
1624 
1625  $current_position = 1;
1626  do {
1627  if (isset($adjusted_positions[$current_position])) {
1628  $current_id = $adjusted_positions[$current_position];
1629  unset($adjusted_positions[$current_position]);
1630  } else {
1631  $current_id = array_shift($originally_ordered_ids);
1632  }
1633 
1634  $todo[$current_id] = array_get_index($all_assets, $current_id);
1635 
1636  $current_position++;
1637  } while (!empty($originally_ordered_ids) || isset($adjusted_positions[$current_position]));
1638 
1639  }//end adjustAssetPositions()
1640 
1641 
1657  function &getChunk(&$children, &$replacements, $keywords, $result_page, $num_per_page, $group_by=NULL)
1658  {
1659  // grouping by number shows a certain number of results per page (eg. 10 assets per page)
1660  // grouping by letter makes this template an A-Z listing of assets
1661  // grouping by 'random' means that we grab X random assets
1662  // grouping by custom groups means we will follow the user defined group structure
1663  if (is_null($group_by)) {
1664  $group_by = $this->attr('group_by');
1665  }
1666 
1667  $page_list_keywords = Array();
1668  foreach ($keywords as $keyword) {
1669  if (preg_match('/page_list_([0-9]+)/', $keyword, $matches)) {
1670  $page_list_keywords[$matches[1]] = $keyword;
1671  }
1672  }
1673 
1674  $this->_tmp['result_page'] = 1;
1675 
1676  $asset_result_page_var = 'result_'.$this->id.'_result_page';
1677  $generic_result_page_var = 'result_page';
1678 
1679  switch ($group_by) {
1680  case 'number' :
1681  // do it CHUNK Norris style
1682  if ($num_per_page > 0) {
1683  $chunks = array_chunk($children, $num_per_page, TRUE);
1684  } else {
1685  $chunks = Array('0' => $children);
1686  }
1687 
1688  if ($result_page > count($chunks)) {
1689  $result_page = count($chunks);
1690  }
1691  $todo = $chunks[($result_page-1)];
1692 
1693  // previous page link
1694  if ($result_page <= 1) {
1695  $replacements['previous_page'] = $this->attr('hide_no_href_links') ? '' : $this->attr('prev_page_text');
1696  $replacements['previous_page_href'] = '';
1697  } else {
1698  // blank out the 'result_page' query - we can understand it if it's given to us, but we don't want to give it to anyone else
1699  $replacements['previous_page_href'] = htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $result_page-1), NULL, NULL, TRUE));
1700  $replacements['previous_page'] = '<a href="'.$replacements['previous_page_href'].'">'.$this->attr('prev_page_text').'</a>';
1701  }
1702 
1703 
1704  // next page link
1705  if ($result_page >= count($chunks)) {
1706  $replacements['next_page'] = $this->attr('hide_no_href_links') ? '' : $this->attr('next_page_text');
1707  $replacements['next_page_href'] = '';
1708  } else {
1709  $replacements['next_page_href'] = htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $result_page+1), NULL, NULL, TRUE));
1710  $replacements['next_page'] = '<a href="'.$replacements['next_page_href'].'">'.$this->attr('next_page_text').'</a>';
1711  }
1712 
1713  $this->_tmp['result_page'] = (int)$result_page;
1714  $replacements['total_pages'] = count($chunks);
1715 
1716  $page_list = $this->_getPageList(count($chunks), $result_page);
1717  $replacements['page_list'] = $page_list;
1718  $replacements['page_list_without_unused'] = $page_list;
1719 
1720  foreach ($page_list_keywords as $window_size => $keyword) {
1721  $replacements[$keyword] = $this->_getPageList(count($chunks), $result_page, $window_size);
1722  }
1723 
1724  unset($chunks);
1725  break;
1726 
1727 
1728  case 'letter' :
1729  // other letter here refers to the situation where there are assets to list that do not start with A-Z
1730  $other_letter_value = $this->attr('other_page_text');
1731 
1732  // '@' symbol is chosen here out of the blue, this is used only as a value of the request var
1733  $other_letter = '@';
1734  foreach (range('A','Z') as $one_letter) {
1735  $all_letters[$one_letter] = $one_letter;
1736  }
1737 
1738  foreach ($children as $childid => $datas) {
1739  $data = $datas[0];
1740  $letter = strtoupper($data['first_letter']);
1741  if (!isset($all_letters[$letter])) {
1742  $letter = $other_letter;
1743  }
1744  $todo[$letter][$childid] = $data['type_code'];
1745  }
1746 
1747  // 'other' letter is only displayed if there are assets that start with non A-Z chars
1748  if (isset($todo[$other_letter])) {
1749  $all_letters[$other_letter] = $other_letter_value;
1750  }
1751 
1752  if ($this->attr('a_z_style') == 'single') {
1753  $page_list = '';
1754  $page_list_without_unused = '';
1755  foreach ($all_letters as $one_letter => $text_value) {
1756  if (isset($todo[$one_letter])) {
1757  $letter_link = ' <a href="#letter_'.$one_letter.'">'.$text_value.'</a> ';
1758  $page_list .= $letter_link;
1759  $page_list_without_unused .= $letter_link;
1760  } else {
1761  $page_list .= ' '.$text_value.' ';
1762  }
1763  }
1764 
1765  $this->_tmp['result_page'] = (int)$result_page;
1766  $replacements['page_list'] = $page_list;
1767  $replacements['page_list_without_unused'] = $page_list_without_unused;
1768 
1769  } else {
1770 
1771  // here we generate a list of only active letters
1772  $letters = Array();
1773  foreach ($all_letters as $one_letter => $text) {
1774  if (isset($todo[$one_letter])) {
1775  $letters[] = $one_letter;
1776  }
1777  }
1778 
1779  $first_available_letter = $letters[0];
1780 
1781  // have [assetid]_result_page take precedence over result_page
1782  $result_page = $first_available_letter;
1783  if (isset($_REQUEST[$generic_result_page_var])) {
1784  $result_page = $_REQUEST['result_page'];
1785  }
1786  if (isset($_REQUEST[$asset_result_page_var])) {
1787  $result_page = $_REQUEST[$asset_result_page_var];
1788  }
1789 
1790  // by default, result letter is the first letter
1791  if (!isset($todo[$result_page])) {
1792  $result_page = $first_available_letter;
1793  }
1794 
1795  $this_letter_key = array_search($result_page, $letters);
1796 
1797  // previous page link
1798  $prev_page_text = $this->attr('prev_page_text');
1799  $prev_page_link = '';
1800  if ($this_letter_key > 0) {
1801  $prev_page_link = htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $letters[$this_letter_key - 1]), NULL, NULL, TRUE));
1802  $prev_page_text = '<a href="'.$prev_page_link.'">'.$prev_page_text.'</a>';
1803  }
1804  $replacements['previous_page'] = $prev_page_text;
1805  $replacements['previous_page_href'] = $prev_page_link;
1806 
1807  // next page link
1808  $next_page_text = $this->attr('next_page_text');
1809  $next_page_href = '';
1810  if ($this_letter_key < (sizeof($letters) - 1)) {
1811  $next_page_href = htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $letters[$this_letter_key + 1]), NULL, NULL, TRUE));
1812  $next_page_text = '<a href="'.$next_page_href.'">'.$next_page_text.'</a>';
1813  }
1814  $replacements['next_page'] = $next_page_text;
1815  $replacements['next_page_href'] = $next_page_href;
1816 
1817  $replacements['total_pages'] = count($letters);
1818  $this->_tmp['result_page'] = $this_letter_key + 1;
1819 
1820  // list of pages and links to them
1821  $page_list = '';
1822  $page_list_without_unused = '';
1823  foreach ($all_letters as $one_letter => $text_value) {
1824  if (isset($todo[$one_letter])) {
1825  $page_link_format = $this->attr('page_link_format');
1826  $letter_link = str_replace(Array('%page_number%', '%page_link%'), Array($text_value, htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $one_letter)))), $page_link_format);
1827 
1828  $page_list .= $letter_link;
1829  $page_list_without_unused .= $letter_link;
1830  } else {
1831  $current_page_format = $this->attr('current_page_format');
1832  $page_list .= str_replace('%page_number%', $text_value, $current_page_format);
1833 
1834  }
1835  }
1836 
1837  $todo = $todo[$result_page];
1838  $replacements['page_list'] = $page_list;
1839  $replacements['page_list_without_unused'] = $page_list_without_unused;
1840  }
1841 
1842  break;
1843 
1844  case 'random' :
1845  // random does things differently, as there is no pagination. But this is a good place
1846  // to grab the random asset
1847  $count = count($children);
1848  $num_per_page = $this->_getNumPerPage();
1849  $todo = Array();
1850 
1851  if ($count == 1) {
1852  $todo = $children;
1853  } else if ($num_per_page <= 1) {
1854  // grab a single asset to display
1855  $random_asset = array_rand($children);
1856  $todo[$random_asset] = $children[$random_asset];
1857  } else {
1858  // this will return a subset of the children, as defined by the num_per_page
1859  // attribute.
1860  $random_assets = array_rand($children, min($num_per_page, $count));
1861  foreach ($random_assets as $value) {
1862  $todo[$value] = $children[$value];
1863  }
1864  }
1865 
1866  break;
1867 
1868  case 'grouped' :
1869  // we need to do an element count so we can work out the page stuff
1870  $total_assets = $this->_arrayCountRecursive($children, count($this->attr('asset_grouping')));
1871  $count = $num_per_page * $result_page;
1872 
1873  // if were showing all, make our count equal to all
1874  if ($count == 0) {
1875  $num_per_page = $total_assets;
1876  $count = $total_assets;
1877  }
1878 
1879  // get a chunk of the size were after, $todo will have the contents
1880  // after the function call
1881  $todo = Array();
1882 
1883  // chunk it hard, baby
1884  $this->sortGroups($this->attr('asset_grouping'), $children, $children);
1885 
1886  $this->_groupChunk($children, $todo, $count, $num_per_page, count($this->attr('asset_grouping')));
1887 
1888  if ($num_per_page == 0) {
1889  $num_per_page = count($todo);
1890  $num_pages = 1;
1891  } else {
1892  $chunks = array_chunk($todo, $num_per_page, TRUE);
1893  $num_pages = ceil($total_assets / $num_per_page);
1894  // we want the last chunk
1895  $todo = end($chunks);
1896  }
1897 
1898  // if the result page is too high, set to last page
1899  if ($num_per_page) {
1900  $result_page = min($result_page, ceil($total_assets / $num_per_page));
1901  }
1902 
1903  // previous page link
1904  if ($result_page <= 1) {
1905  $replacements['previous_page'] = $this->attr('hide_no_href_links') ? '' : $this->attr('prev_page_text');
1906  $replacements['previous_page_href'] = '';
1907  } else {
1908  $replacements['previous_page_href'] = htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $result_page-1), NULL, NULL, TRUE));
1909  $replacements['previous_page'] = '<a href="'.$replacements['previous_page_href'].'">'.$this->attr('prev_page_text').'</a>';
1910  }
1911 
1912  // next page link
1913  if ($result_page >= $num_pages) {
1914  $replacements['next_page'] = $this->attr('hide_no_href_links') ? '' : $this->attr('next_page_text');
1915  $replacements['next_page_href'] = '';
1916  } else {
1917  $replacements['next_page_href'] = htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $result_page+1), NULL, NULL, TRUE));
1918  $replacements['next_page'] = '<a href="'.$replacements['next_page_href'].'">'.$this->attr('next_page_text').'</a>';
1919  }
1920 
1921  $this->_tmp['result_page'] = (int)$result_page;
1922  $replacements['total_pages'] = $num_pages;
1923 
1924  // list of pages and links to them
1925  $page_list = $this->_getPageList($num_pages, $result_page);
1926  $replacements['page_list'] = $page_list;
1927  $replacements['page_list_without_unused'] = $page_list;
1928  foreach ($page_list_keywords as $window_size => $keyword) {
1929  $replacements[$keyword] = $this->_getPageList($num_pages, $result_page, $window_size);
1930  }
1931 
1932  break;
1933 
1934  }//end switch
1935 
1936  $replacements['page_number'] = $this->_tmp['result_page'];
1937 
1938  // do this for compatibility - deprecated. not sure if any clients
1939  // are using this, since 'current_page' is not one of the advertised keywords
1940  $replacements['current_page'] = $replacements['page_number'];
1941 
1942  return $todo;
1943 
1944  }//end getChunk()
1945 
1946 
1957  function _getPageList($total_pages, $current_page, $window_size=NULL)
1958  {
1959  $current_page_format = $this->attr('current_page_format');
1960  $page_link_format = $this->attr('page_link_format');
1961 
1962  if (is_null($window_size)) {
1963  $start_page = 1;
1964  $end_page = $total_pages;
1965  } else {
1966  $start_page = max(1, min(($current_page - floor($window_size / 2)), $total_pages - $window_size + 1));
1967  $end_page = min($total_pages, $start_page + $window_size - 1);
1968  }
1969  $asset_result_page_var = 'result_'.$this->id.'_result_page';
1970  $page_list = '';
1971  for ($i = $start_page; $i <= $end_page; $i++) {
1972  if ($i == $current_page) {
1973  $page_list .= str_replace('%page_number%', $i, $current_page_format);
1974  } else {
1975  $page_list .= str_replace(Array('%page_number%', '%page_link%'), Array($i, htmlspecialchars(replace_query_string_vars(Array($asset_result_page_var => $i)))), $page_link_format);
1976  }
1977  }
1978 
1979  return $page_list;
1980 
1981  }//end _getPageList()
1982 
1983 
1999  function _groupChunk(&$todo, &$chunk, &$remaining_count, $num_per_page, $group_limit=NULL)
2000  {
2001  // keep calling this for each group until we get to the actual assets
2002  if (($group_limit > 0) || is_null($group_limit)) {
2003  foreach ($todo as $index => $child) {
2004  if ($remaining_count >= 0) {
2005  $this->_groupChunk($todo[$index], $chunk[$index], $remaining_count, $num_per_page, ($group_limit == NULL ? NULL : $group_limit - 1));
2006  }
2007 
2008  if (empty($chunk[$index])) unset($chunk[$index]);
2009  }
2010  return;
2011  }
2012 
2013  $todo = $this->sortAssetList($todo, $this->getSortInfo());
2014  foreach ($todo as $index => $value) {
2015  if (($remaining_count > 0) && ($remaining_count <= $num_per_page)) {
2016  $chunk[$index] = $value;
2017  }
2018 
2019  $remaining_count--;
2020  }
2021 
2022  }//end _groupChunk()
2023 
2024 
2035  function printAssetList($todo)
2036  {
2037  if (empty($todo)) return;
2038 
2039  $this->_tmp['formats'] = Array();
2040  $this->_tmp['default_format'] = '';
2041  $this->_tmp['is_default'] = Array();
2042  if (empty($this->_tmp['assets_info'])) {
2043  include_once SQ_FUDGE_PATH.'/general/general.inc';
2044  $flattened = array_keys(array_flatten($todo, FALSE, TRUE));
2045  $this->_tmp['assets_info'] = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo($flattened);
2046  $this->_tmp['asset_urls'] = $GLOBALS['SQ_SYSTEM']->am->getAssetURL($flattened);
2047  }
2048 
2049  $type_folder = $this->getFolder('type_formats');
2050 
2051  $this->_tmp['type_folder_id'] = $type_folder->id;
2052 
2053  $this->_tmp['position_formats'] = Array();
2054  $position_folder = $this->getFolder('position_formats');
2055  $format_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($position_folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE);
2056  foreach ($format_links as $link_data) {
2057  $position = substr($link_data['value'], 9);
2058  $this->_tmp['position_formats'][$position] = $link_data['minorid'];
2059  }
2060 
2061  // in case it wasn't done beforehand in printContents() or something
2062  $relations = $this->_analyseCheckAllRelations($todo);
2063 
2064  ob_start();
2065  $selections = $this->attr('asset_selections');
2066  if (!empty($selections)) {
2067  ?>
2068  <script type="text/javascript">
2069  <!--
2070  <?php echo $this->_buildRelationsJS(); ?>
2071  //-->
2072  </script>
2073  <?php
2074  }
2075  $this->_tmp['js_relation_code'] = ob_get_clean();
2076 
2077  if ($this->attr('group_by') == 'grouped') {
2078  $this->_printGroups($todo, $this->attr('asset_grouping'));
2079  } else {
2080  $this->_printAssetList($todo);
2081  }
2082 
2083  }//end printAssetList()
2084 
2085 
2099  function _printGroups(&$todo, $group_info)
2100  {
2101  $pointer = reset($todo);
2102  if (!empty($group_info)) {
2103  include_once SQ_FUDGE_PATH.'/general/general.inc';
2104  // get the group bodycopy relative to this level
2105  $remaining_groups = array_shift($group_info);
2106  $format_bodycopy = $GLOBALS['SQ_SYSTEM']->am->getAsset($remaining_groups['format_assetid']);
2107  $format_keywords = $format_bodycopy->getKeywords();
2108  foreach ($todo as $group_level => $value) {
2109  // lets buffer whatever contents we need
2110  $replacements = Array();
2111 
2112  switch ($remaining_groups['group_type']) {
2113 
2114  case 'parent_asset':
2115  // we need to get info from the parent asset
2116  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($group_level);
2117  if (isset($asset->var['name'])) {
2118  $group_name = htmlspecialchars($asset->attr('name'));
2119  } else {
2120  $group_name = htmlspecialchars($asset->name);
2121  }
2122  foreach ($format_keywords as $format_keyword) {
2123  $pos = strpos($format_keyword, 'parent');
2124  if ($pos === 0) {
2125  $keyword = substr_replace($format_keyword, 'asset', 0, 6);
2126  $replacements[$format_keyword] = $asset->getKeywordReplacement($keyword);
2127  }
2128  }
2129  $replacements['group_name_linked'] = '<a href="'.$asset->getUrl().'">'.htmlspecialchars($asset->name).'</a>';
2130  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
2131  break;
2132 
2133  case 'metadata':
2134  // we already worked this information out somewhere else
2135  $group_name = $group_level;
2136  break;
2137 
2138  case 'standard_asset':
2139  // format type codes for printing
2140  if (isset($remaining_groups['field']) && $remaining_groups['field'] == 'type_code') {
2141  $group_name = $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($group_level, 'name');
2142  } else {
2143  // the field value will have been the index
2144  $group_name = $group_level;
2145  }
2146  break;
2147 
2148  case 'keyword':
2149  $group_name = $group_level;
2150  break;
2151 
2152  case 'attribute':
2153  // oh noes, implementation reserved!
2154  break;
2155 
2156  default:
2157  // there is no spoon .....
2158  break;
2159  }//end switch remaining groups
2160 
2161  if (isset($this->_tmp['group_info'])) {
2162  array_push($this->_tmp['group_info'], $group_level);
2163  } else {
2164  $this->_tmp['group_info'] = Array($group_level);
2165  }
2166 
2167  // the selection stuff needs the group_info array popped for
2168  // each group level selection, so we need a copy for _printAsset()
2169  // too
2170  if (isset($this->_tmp['group_levels'])) {
2171  array_push($this->_tmp['group_levels'], $group_level);
2172  } else {
2173  $this->_tmp['group_levels'] = Array($group_level);
2174  }
2175 
2176  ob_start();
2177  $this->_printGroups($todo[$group_level], $group_info);
2178  $group_contents = ob_get_contents();
2179  ob_end_clean();
2180 
2181  // populate replacements
2182  $replacements['group_listing'] = $group_contents;
2183  $replacements['group_name'] = $group_name;
2184 
2185  // Prints the parent_contents and parent_contents_raw keywords
2186  if ((preg_match('|%parent_contents%|is', $format_bodycopy->getRawBodycopyContent())) || (preg_match('|%parent_contents_raw%|is', $format_bodycopy->getRawBodycopyContent()))) {
2187  $parent_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($group_level);
2188  ob_start();
2189  $parent_asset->printBodyWithPaintLayout();
2190  $replacements['parent_contents'] = ob_get_contents();
2191  ob_end_clean();
2192  ob_start();
2193  $parent_asset->printBody();
2194  $replacements['parent_contents_raw'] = ob_get_contents();
2195  ob_end_clean();
2196  }
2197 
2198  $replacements['group_immed_child_group_count'] = count($todo[$group_level]);
2199  $replacements['group_total_child_asset_count'] = count(array_flatten($todo[$group_level], TRUE, FALSE));
2200 
2201  // only print the 'check all' checkbox if we're only dealing with checkboxes
2202  $selections = $this->attr('asset_selections');
2203  $selection_defaults = $this->getAssetSelectionDefaults();
2204 
2205  foreach ($selections as $selection_name => $settings) {
2206  if (!in_array($selection_name.'_check_all_in_group', $format_keywords)) {
2207  continue;
2208  }
2209  $this->registerFormField($selection_name.'_check_all_in_group');
2210 
2211  // predefine some commonly used settings, for brevity's sake
2212  $selection_options = array_get_index($settings, 'options', $selection_defaults['options']);
2213  $selection_allow_multiple = array_get_index($selection_options, 'allow_multiple', $selection_defaults['options']['allow_multiple']);
2214 
2215  if ((array_get_index($settings, 'type', $selection_defaults['type']) == 'radio') && $selection_allow_multiple) {
2216  ob_start();
2217  $onclick = Array();
2218  $group_cb_id = 'g_'.implode('_', $this->_tmp['group_info']);
2219  $control_name = make_valid_html_id($this->_getSelectionFieldNamePrefix($selection_name).str_replace(' ', '_', $group_cb_id));
2220  check_box($control_name, '1', FALSE, 'sq_listing_check_state(this, \''.$this->_getSelectionFieldNamePrefix($selection_name).'\')');
2221  $replacements[$selection_name.'_check_all_in_group'] = ob_get_clean();
2222  $replacements[$selection_name.'_selection_id'] = $control_name;
2223  }
2224  }
2225 
2226  // Keywords not evaluated above might be the ones with the modifiers
2227  // see bug #5808 %globals_X% keywords do not work in group format bodycopies
2228  // and bug #4734 keyword modifiers not working on group_name keyword
2229  foreach($format_keywords as $format_keyword) {
2230  // if it is asset based or globals keyword don't empty string the replacement
2231  // the replacement for these keywords shall be queried later on
2232  if (!isset($replacements[$format_keyword]) &&
2233  (strpos($format_keyword, 'globals_') !== FALSE || strpos($format_keyword, 'asset_') !== FALSE)) {
2234  // if we havn't got a replacement yet
2235  // dont blank out the keyword already
2236  $replacements[$format_keyword] = '%'.$format_keyword.'%';
2237  } else if (!isset($replacements[$format_keyword])) {
2238  // blank out the replacement if we are sure it is a %group_*%
2239  // keyword, as we already have replacement of it from up above
2240  $replacements[$format_keyword] = '';
2241  }
2242  }
2243 
2244  // Apply the keyword modifiers
2245  foreach($replacements as $kw => $value) {
2246  $plain_keyword = parse_keyword($kw, $modifiers);
2247  // see if there is modifier and that the keyword is not waiting to be replaced
2248  // and only then try and allpy the keyword modifier to the replacement we have
2249  if (!empty($modifiers) && '%'.$kw.'%' != $value) {
2250  $replacements[$kw] = $replacements[$plain_keyword];
2251 
2252  // Apply modifiers
2253  $assetid = ($remaining_groups['group_type'] == 'parent_asset') ? $group_level : 0;
2254  apply_keyword_modifiers($replacements[$kw], $modifiers, Array('assetid' => $assetid, 'replacements' => $replacements));
2255  }//end if
2256  }//end foreach
2257 
2258  array_pop($this->_tmp['group_info']);
2259  $format_bodycopy->setKeywordReplacements($replacements);
2260  $format_bodycopy->printBody();
2261  }//end foreach $todo
2262 
2263  } else {
2264  // Otherwise we're up to the asset list, we can do the normal listing stuff here
2265  // Check if we need to top up the asset-info array...
2266  $extras = array_diff(array_keys($todo), array_keys($this->_tmp['assets_info']));
2267  if (!empty($extras)) {
2268  $group_assets = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo(array_keys($todo));
2269  for (reset($group_assets); NULL !== ($index = key($group_assets)); next($group_assets)) {
2270  $this->_tmp['assets_info'][$index] = $group_assets[$index];
2271  }
2272  }
2273  $this->_printAssetList($todo);
2274 
2275  }
2276 
2277  }//end _printGroups()
2278 
2279 
2294  function _printAssetList($todo)
2295  {
2296  $allowed_types = $this->getAllowedAssetTypeMap();
2297  $column_layout = $this->attr('column_layout');
2298  $column_layout_type = $this->attr('column_layout_type');
2299  $using_groups = $this->attr('group_by') == 'grouped';
2300  $using_columns = (count($column_layout) > 1 || $column_layout['0'] > 1);
2301  $num_items = count($todo);
2302 
2303  $list_position = 1;
2304  $a_z_letter_format = $this->attr('a_z_letter_format');
2305  if ($this->attr('group_by') == 'letter' && $this->attr('a_z_style') == 'single') {
2306  if ($using_columns && $column_layout_type === 'table') {
2307 
2308  foreach ($todo as $letter => $assets) {
2309  $letter_text = $letter === '@' ? $this->attr('other_page_text') : $letter;
2310  $anchor = '<a name="letter_'.$letter.'">'.$letter_text.'</a>';
2311  $anchor = str_replace('%letter_group%', $anchor, $a_z_letter_format);
2312  echo $anchor;
2313  echo '<table width="100%">';
2314  $num_rows = max(array_keys($column_layout)) + 1;
2315  $num_columns = max(array_values($column_layout));
2316  $current_row = 1;
2317  for (reset($assets); NULL !== ($assetid = key($assets));) {
2318  echo '<tr>';
2319  $cols_in_row = array_get_index($column_layout, $current_row, $column_layout['0']);
2320  $each_colspan = (int)($num_columns / $cols_in_row);
2321  $first_colspan = $each_colspan + $num_columns - ($cols_in_row * $each_colspan);
2322 
2323  for ($c = 1; $c <= $cols_in_row; $c++) {
2324  $colspan = ($c == 1) ? $first_colspan : $each_colspan;
2325  ?>
2326  <td <?php echo ($colspan > 1) ? ' colspan="'.$colspan.'"' : ''; ?>>
2327  <?php
2328  if ($assetid == NULL) {
2329  echo '&nbsp;';
2330  } else {
2331  $this->_printAsset($assetid, $list_position, $num_items);
2332  next($assets);
2333  $assetid = key($assets);
2334  $list_position++;
2335  }
2336  ?>
2337  </td>
2338  <?php
2339  }
2340  echo '</tr>';
2341  $current_row++;
2342  }//end for
2343 
2344  echo '</table>';
2345  }
2346  }
2347  //if div is used to generate the column
2348  else if ($using_columns && $column_layout_type === 'div') {
2349  foreach ($todo as $letter => $assets) {
2350  $letter_text = $letter === '@' ? $this->attr('other_page_text') : $letter;
2351  $anchor = '<a name="letter_'.$letter.'">'.$letter_text.'</a>';
2352  $anchor = str_replace('%letter_group%', $anchor, $a_z_letter_format);
2353  echo $anchor;
2354  $num_rows = max(array_keys($column_layout)) + 1;
2355  $num_columns = max(array_values($column_layout));
2356  $row_class = $this->attr('div_row_class');
2357  $current_row = 1;
2358 
2359  for (reset($assets); NULL !== ($assetid = key($assets));) {
2360  echo empty($row_class) ? '<div>' : '<div class="'.$row_class.'">';
2361  $cols_in_row = array_get_index($column_layout, $current_row, $column_layout['0']);
2362 
2363  for ($c = 1; $c <= $cols_in_row; $c++) {
2364  if ($assetid != NULL) {
2365  $this->_printAsset($assetid, $list_position, $num_items);
2366  next($assets);
2367  $assetid = key($assets);
2368  $list_position++;
2369  }
2370  }
2371  $current_row++;
2372  echo '</div>';
2373  echo "\n\n";
2374  }//end for
2375  }
2376 
2377  } else {
2378  foreach ($todo as $letter => $assets) {
2379  $letter_text = $letter === '@' ? $this->attr('other_page_text') : $letter;
2380  $anchor = '<a name="letter_'.$letter.'">'.$letter_text.'</a>';
2381  $anchor = str_replace('%letter_group%', $anchor, $a_z_letter_format);
2382  echo $anchor;
2383  foreach ($assets as $assetid => $type_code) {
2384  $this->_printAsset($assetid, $list_position, $num_items);
2385  $list_position++;
2386  }
2387  }
2388  }
2389 
2390  } else {
2391  //if table style is used to generate the column
2392  if ($using_columns && $column_layout_type === 'table') {
2393  $num_rows = max(array_keys($column_layout)) + 1;
2394  $num_columns = max(array_values($column_layout));
2395 
2396  $current_row = 1;
2397 
2398  echo '<table width="100%">';
2399 
2400  for (reset($todo); NULL !== ($assetid = key($todo));) {
2401 
2402  echo '<tr>';
2403  $cols_in_row = array_get_index($column_layout, $current_row, $column_layout['0']);
2404  $each_colspan = (int)($num_columns / $cols_in_row);
2405  $first_colspan = $each_colspan + $num_columns - ($cols_in_row * $each_colspan);
2406 
2407  for ($c = 1; $c <= $cols_in_row; $c++) {
2408  $colspan = ($c == 1) ? $first_colspan : $each_colspan;
2409  ?>
2410  <td<?php echo ($colspan > 1) ? ' colspan="'.$colspan.'"' : ''; ?>>
2411  <?php
2412  if ($assetid == NULL) {
2413  echo '&nbsp;';
2414  } else {
2415  $this->_printAsset($assetid, $list_position, $num_items);
2416  next($todo);
2417  $assetid = key($todo);
2418  $list_position++;
2419  }
2420  ?>
2421  </td>
2422  <?php
2423  }
2424  echo '</tr>';
2425  $current_row++;
2426  }//end for
2427 
2428  echo '</table>';
2429  }
2430  //if div is used to generate the column
2431  else if ($using_columns && $column_layout_type === 'div') {
2432  $num_rows = max(array_keys($column_layout)) + 1;
2433  $num_columns = max(array_values($column_layout));
2434  $row_class = $this->attr('div_row_class');
2435  $current_row = 1;
2436 
2437  for (reset($todo); NULL !== ($assetid = key($todo));) {
2438  echo empty($row_class) ? '<div>' : '<div class="'.$row_class.'">';
2439  $cols_in_row = array_get_index($column_layout, $current_row, $column_layout['0']);
2440 
2441  for ($c = 1; $c <= $cols_in_row; $c++) {
2442  if ($assetid != NULL) {
2443  $this->_printAsset($assetid, $list_position, $num_items);
2444  next($todo);
2445  $assetid = key($todo);
2446  $list_position++;
2447  }
2448  }
2449  $current_row++;
2450  echo '</div>';
2451  echo "\n\n";
2452  }//end for
2453 
2454  } else {
2455  foreach ($todo as $assetid => $type_code) {
2456 
2457  $this->_printAsset($assetid, $list_position, $num_items);
2458  $list_position++;
2459  }
2460  }
2461  }
2462 
2463  }//end _printAssetList()
2464 
2465 
2476  function _getShadowAssetKeywordReplacements($assetid, $bc_keywords)
2477  {
2478  $keywords = Array();
2479  $shadow_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2480  foreach ($bc_keywords as $key => $keyword) {
2481  $keywords[$keyword] = $shadow_asset->getKeywordReplacement($keyword);
2482  }
2483 
2484  return $keywords;
2485 
2486  }//end _getShadowAssetKeywordReplacements()
2487 
2488 
2500  function _printAsset($assetid, $list_position, $num_items=0)
2501  {
2502  // allows for asset listings within asset listings, see #2727
2503  if (isset($_SESSION[SQ_SESSION_SANDBOX_INDEX]['list_current_asset_id'])) {
2504  $prev_list_current_asset_id = $_SESSION[SQ_SESSION_SANDBOX_INDEX]['list_current_asset_id'];
2505  } else {
2506  $prev_list_current_asset_id = null;
2507  }
2508  $_SESSION[SQ_SESSION_SANDBOX_INDEX]['list_current_asset_id'] = $assetid;
2509 
2510  $mm = NULL;
2511  $contents = '';
2512  $keywords = Array();
2513  $customised = $this->getFormats('type_formats');
2514 
2515  $asset_info_fields = $GLOBALS['SQ_SYSTEM']->am->getAssetInfoFields();
2516  $bodycopy = NULL;
2517 
2518  // if a ':' exists in the assetid, then it is a shadow asset
2519  $is_shadow = strpos($assetid, ':');
2520 
2521  // we can get the type code from the asset info array
2522  // if the asset info array is empty then there is a possibility that we are trying to print a shadow asset
2523  // so we load the asset and get its typecode
2524  $type_code = NULL;
2525  $info = array_get_index($this->_tmp['assets_info'], $assetid, Array('type_code' => ''));
2526  if (!empty($info['type_code'])) {
2527  $type_code = $info['type_code'];
2528  } else {
2529  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2530  $type_code = $asset->type();
2531  }
2532  // check to see if there is a specific position format for us
2533  if (isset($this->_tmp['position_formats'][$list_position])) {
2534  $bodycopy = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_tmp['position_formats'][$list_position], 'bodycopy');
2535  } else if ($num_items > 0) {
2536  /*
2537  * Secondly, check if there is a "negative position" that is applicable.
2538  * Negative position values are used to dynamically control display of list entries by using an offset from the last list element.
2539  * For example: -1 is a position format for the last entry, -2 is for the second-last entry.
2540  *
2541  * Positive position formats take precedence over "negative" ones.
2542  * Ok, now back to the code...
2543  */
2544  $negative_offset = -1 + ($list_position - $num_items);
2545  if (isset($this->_tmp['position_formats'][$negative_offset])) {
2546  $bodycopy = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_tmp['position_formats'][$negative_offset], 'bodycopy');
2547  }
2548  }//end else
2549 
2550  // if we can't find a position format
2551  // search for type format, if we don't already know that default format should be used
2552  if (is_null($bodycopy) && !in_array($type_code, $this->_tmp['is_default'])) {
2553  // is there previous information about type format?
2554  if (isset($this->_tmp['type_formats']) && array_key_exists($type_code, $this->_tmp['type_formats'])) {
2555  $type_key = array_search($this->_tmp['type_formats'][$type_code], $customised);
2556  $type_code = $this->_tmp['type_formats'][$type_code];
2557  } else {
2558  $parent = NULL;
2559  $parents = $GLOBALS['SQ_SYSTEM']->am->getTypeAncestors($type_code);
2560 
2561  if (($type_key = array_search($type_code, $customised)) === FALSE) {
2562  foreach ($parents as $parent) {
2563  // any of parent in the custom format?
2564  if (array_search($parent, $customised) !== FALSE) {
2565  $this->_tmp['type_formats'][$type_code] = $parent;
2566  $type_key = array_search($this->_tmp['type_formats'][$type_code], $customised);
2567  $type_code = $parent;
2568  }
2569  }
2570  }
2571  }
2572  if (isset($type_key) && $type_key !== FALSE) {
2573  $bodycopy = $this->_getTypeFormatBodycopy($type_code);
2574  }
2575 
2576  }
2577 
2578  // if no bodycopy found, try the default format
2579  if (is_null($bodycopy)) {
2580  if (!isset($this->_tmp['default_format_bc'])) {
2581  $link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->_tmp['type_folder_id'], SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'default_format');
2582  if ($link) {
2583  $this->_tmp['default_format_bc'] = $GLOBALS['SQ_SYSTEM']->am->getAsset($link['minorid'], 'bodycopy');
2584  } else {
2585  // TODO: localise
2586  trigger_error('Cannot Find the Default Bodycopy');
2587  return;
2588  }
2589  }
2590  $bodycopy = $this->_tmp['default_format_bc'];
2591  }
2592 
2593 
2594  // by the time we reach this point we already have a bodycopy for this asset
2595  $contextid = $GLOBALS['SQ_SYSTEM']->getContextId();
2596  if (isset($this->_tmp['bc_keywords'][$bodycopy->id][$contextid])) {
2597  $bc_keywords = $this->_tmp['bc_keywords'][$bodycopy->id][$contextid];
2598  } else {
2599  $bc_keywords = $bodycopy->getKeywords();
2600  $this->_tmp['bc_keywords'][$bodycopy->id][$contextid] = $bc_keywords;
2601  }
2602  $keywords = Array();
2603 
2604  // Asset contents keywords that we want contexted versions thereof
2605  // These will be stored in the keywords array as "keyword^context:contextid"
2606  // and accessed later on when we replace the keyword.
2607  $context_bc_keywords = Array();
2608  $paint_layouts_to_resolve = Array();
2609  $all_contexts = $GLOBALS['SQ_SYSTEM']->getAllContexts();
2610 
2611  // For each keyword starting with 'asset_contents', ensure there is an entry
2612  // in $bc_keywords for both the plain keyword and any equivalents with modifiers.
2613  //
2614  // This way plain replacement works as expected, and the modifiers can be run
2615  // later in this method
2616  foreach ($bc_keywords as $kw) {
2617  if (substr($kw, 0, strlen('asset_contents')) == 'asset_contents') {
2618  $kw = parse_keyword($kw, $modifiers);
2619  $contextid = extract_context_modifier($modifiers);
2620 
2621  if (!in_array($kw, $bc_keywords)) {
2622  array_push($bc_keywords, $kw);
2623  }
2624 
2625  // Store which context we need to get this keyword for
2626  if (array_key_exists($kw, $context_bc_keywords) === FALSE) {
2627  $context_bc_keywords[$kw] = Array();
2628  }
2629 
2630  if ($contextid === NULL) {
2631  $context_bc_keywords[$kw]['current'] = NULL;
2632  } else {
2633  $context_bc_keywords[$kw][$contextid] = $all_contexts[$contextid]['name'];
2634  }
2635 
2636  // Contents being generated with a paint layout - work out which ones
2637  // we have to resolve (to get around bug #4374, which previously limited
2638  // support to one arbitrary paint layout per bodycopy)
2639  if (substr($kw, 0, strlen('asset_contents_paint_')) == 'asset_contents_paint_') {
2640  $paint_layouts_to_resolve[] = substr($kw, strlen('asset_contents_paint_'));
2641  }
2642  }
2643  }
2644 
2645  // if the asset is a shadow asset then get the keywords separately
2646  if ($is_shadow) {
2647  $keywords = $this->_getShadowAssetKeywordReplacements($assetid, $bc_keywords);
2648 
2649  // Body with paint layout applied, if any (inherited from the
2650  // listing engine asset, usually)
2651  if (in_array('asset_contents', $bc_keywords)) {
2652  foreach ($context_bc_keywords['asset_contents'] as $contextid => $context_name) {
2653  $kw = 'asset_contents';
2654  if ($context_name !== NULL) {
2655  $kw .= '^context:'.$context_name;
2656  }
2657  if (empty($asset)) {
2658  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2659  }
2660  $keywords[$kw] = $this->_getAssetContentsBCKeywordReplacement($asset, $contextid);
2661  }
2662 
2663  }
2664 
2665  // %asset_contents_raw% will print the asset's body WITHOUT a
2666  // paint layout
2667  if (in_array('asset_contents_raw', $bc_keywords)) {
2668  foreach ($context_bc_keywords['asset_contents_raw'] as $contextid => $context_name) {
2669  $kw = 'asset_contents_raw';
2670  if ($context_name !== NULL) {
2671  $kw .= '^context:'.$context_name;
2672  }
2673  if (empty($asset)) {
2674  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2675  }
2676  $keywords[$kw] = $this->_getAssetContentsRawBCKeywordReplacement($asset, $contextid);
2677  }
2678 
2679  }
2680 
2681  } else {
2682  $this_needs_asset = FALSE;
2683  foreach ($bc_keywords as $keyword) {
2684  if (in_array($keyword, Array('asset_url', 'asset_name_linked', 'asset_short_name_linked')) && !SQ_ROLLBACK_VIEW) {
2685  continue;
2686  }
2687  if (!isset($this->_tmp['assets_info'][$assetid][substr($keyword, 6)])) {
2688  $this_needs_asset = TRUE;
2689  break;
2690  }
2691  }
2692 
2693  if ($this_needs_asset) {
2694  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
2695  if (is_null($asset)) return;
2696  foreach ($bc_keywords as $keyword) {
2697  $keywords[$keyword] = $asset->getKeywordReplacement($keyword);
2698  }
2699 
2700  // Body with paint layout applied, if any (inherited from the
2701  // listing engine asset, usually)
2702  if (array_key_exists('asset_contents', $keywords)) {
2703  foreach ($context_bc_keywords['asset_contents'] as $contextid => $context_name) {
2704  $kw = 'asset_contents';
2705  if ($context_name !== NULL) {
2706  $kw .= '^context:'.$context_name;
2707  }
2708 
2709  $keywords[$kw] = $this->_getAssetContentsBCKeywordReplacement($asset, $contextid);
2710  }
2711 
2712  }
2713 
2714  // Body with paint layout(%asset_contents_paint_ASSETID%) or user-defined paint layout(%asset_contents_paint_NAME%) applied
2715  foreach ($paint_layouts_to_resolve as $paint_layout) {
2716  $base_kw = 'asset_contents_paint_'.$paint_layout;
2717  foreach ($context_bc_keywords[$base_kw] as $contextid => $context_name) {
2718  $kw = $base_kw;
2719  if ($context_name !== NULL) {
2720  $kw .= '^context:'.$context_name;
2721  }
2722 
2723  $keywords[$kw] = $this->_getAssetContentsPaintBCKeywordReplacement($asset, $paint_layout, $contextid);
2724  }
2725 
2726  }
2727 
2728  // %asset_contents_raw% will print the asset's body WITHOUT a
2729  // paint layout
2730  if (array_key_exists('asset_contents_raw', $keywords)) {
2731  foreach ($context_bc_keywords['asset_contents_raw'] as $contextid => $context_name) {
2732  $kw = 'asset_contents_raw';
2733  if ($context_name !== NULL) {
2734  $kw .= '^context:'.$context_name;
2735  }
2736 
2737  $keywords[$kw] = $this->_getAssetContentsRawBCKeywordReplacement($asset, $contextid);
2738  }
2739 
2740  }
2741 
2742  // stuff carried over results body replacements
2743  $keywords['asset_url'] = array_get_index($this->_tmp['asset_urls'], $assetid, './?a='.$assetid);
2744  $keywords['asset_name_linked'] = '<a href="'.$keywords['asset_url'].'">'.htmlspecialchars($asset->name).'</a>';
2745  $keywords['asset_short_name_linked'] = '<a href="'.$keywords['asset_url'].'">'.htmlspecialchars($asset->short_name).'</a>';
2746 
2747  if (in_array('asset_lineage', $bc_keywords) || in_array('asset_lineage_linked', $bc_keywords)) {
2748  preg_match('/(http[s]?:\/\/)(.*)/', $keywords['asset_url'], $matches);
2749  $lineage_array = Array();
2750  if (isset($matches[2])) {
2751  $lineage_array = $GLOBALS['SQ_SYSTEM']->am->getLineageFromUrl(NULL, $matches[2]);
2752  }//end if
2753 
2754  $lineage = '';
2755  $linked_lineage = '';
2756 
2757  if (!empty($lineage_array)) {
2758  $show_root = $this->attr('root_in_lineage');
2759  $show_self = $this->attr('self_in_lineage');
2760  $first = TRUE;
2761 
2762  $served_by_apache = strpos($matches[2], '/__data/') !== FALSE || (SQ_CONF_STATIC_ROOT_URL && strpos($matches[2], SQ_CONF_STATIC_ROOT_URL.'/') !== FALSE);
2763  if (!$show_self && !$served_by_apache) {
2764  array_pop($lineage_array);
2765  }
2766  foreach ($lineage_array as $lineage_item) {
2767  if ($first && !$show_root) {
2768  $first = FALSE;
2769  continue;
2770  }//end if
2771 
2772  if (!$first && $lineage != '') {
2773  $lineage .= $this->attr('lineage_seperator');
2774  $linked_lineage .= $this->attr('lineage_seperator');
2775  }//end if
2776  $lineage .= $lineage_item['short_name'];
2777  $linked_lineage .= '<a href="'.$lineage_item['protocol'].'://'.$lineage_item['url'].'">'.htmlspecialchars($lineage_item['short_name']).'</a>';
2778  $first = FALSE;
2779  }//end if
2780  }//end if
2781  $keywords['asset_lineage'] = htmlspecialchars($lineage);
2782  $keywords['asset_lineage_linked'] = $linked_lineage;
2783  }//end if
2784 
2785  // end results body stuff
2786 
2787  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
2788  unset($asset);
2789  } else {
2790  $keywords = Array();
2791  foreach ($this->_tmp['assets_info'][$assetid] as $field => $value) {
2792  $keywords['asset_'.$field] = htmlspecialchars($value);
2793  }
2794  $keywords['asset_url'] = array_get_index($this->_tmp['asset_urls'], $assetid, './?a='.$assetid);
2795  $keywords['asset_name_linked'] = '<a href="'.$keywords['asset_url'].'">'.$keywords['asset_name'].'</a>';
2796  $keywords['asset_short_name_linked'] = '<a href="'.$keywords['asset_url'].'">'.$keywords['asset_short_name'].'</a>';
2797 
2798  }//end if needs asset
2799  }//end else
2800 
2801  $group_keywords = preg_grep('/group[0-9]+.*/', $bc_keywords);
2802  foreach ($group_keywords as $group_keyword) {
2803  preg_match('/group([0-9]+)_(.*)/', $group_keyword, $matches);
2804  if (isset($this->_tmp['group_levels'][$matches[1] - 1])) {
2805  $parent_id = $this->_tmp['group_levels'][$matches[1] - 1];
2806  if (assert_valid_assetid($parent_id, '', TRUE, FALSE)) {
2807  // we have an assetid, so do what we want with the parent
2808  switch ($matches[2]) {
2809  case 'link_value':
2810  $link = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($parent_id, $assetid);
2811  $keywords[$group_keyword] = array_get_index($link, 'value', '');
2812  break;
2813  case 'link_type':
2814  case 'dependant':
2815  case 'exclusive':
2816  default:
2817  // other possible uses
2818  break;
2819  }
2820  }
2821  }//end if
2822  }
2823 
2824  $keywords['page_href'] = $this->getHref();
2825 
2826  if ($this->attr('group_by') == 'letter') {
2827  $keywords['asset_position'] = $list_position;
2828  } else {
2829  $keywords['asset_position'] = (array_get_index($this->_tmp, 'result_page', 1) - 1) * $this->_getNumPerPage() + $list_position;
2830  }
2831 
2832  if (($list_position % 2) == 0) {
2833  $keywords['asset_odd_even'] = translate('cms_listing_asset_position_even');
2834  } else {
2835  $keywords['asset_odd_even'] = translate('cms_listing_asset_position_odd');
2836  }
2837 
2838  // asset selections
2839  $selections = $this->attr('asset_selections');
2840  $selection_defaults = $this->getAssetSelectionDefaults();
2841 
2842  if (is_array($selections) && !empty($selections)) {
2843 
2844  foreach ($selections as $selection_name => $settings) {
2845 
2846  $buffering = in_array($selection_name.'_asset_selection', $bc_keywords);
2847  $selection_prefix = $this->_getSelectionFieldNamePrefix($selection_name);
2848 
2849  if ($buffering) {
2850  ob_start();
2851  $this->registerFormField($selection_prefix);
2852  }
2853 
2854  // predefine some commonly used settings, for brevity's sake
2855  $selection_options = array_get_index($settings, 'options', $selection_defaults['options']);
2856  $selection_restrict = array_get_index($selection_options, 'restrict_range', $selection_defaults['options']['restrict_range']);
2857  $selection_restrict_enable = array_get_index($selection_restrict, 'enable', $selection_defaults['options']['restrict_range']['enable']);
2858  $selection_restrict_min = array_get_index($selection_restrict, 'min', $selection_defaults['options']['restrict_range']['min']);
2859  $selection_restrict_max = array_get_index($selection_restrict, 'max', $selection_defaults['options']['restrict_range']['max']);
2860  $selection_allow_negative = array_get_index($selection_options, 'allow_negative', $selection_defaults['options']['allow_negative']);
2861  $selection_allow_multiple = array_get_index($selection_options, 'allow_multiple', $selection_defaults['options']['allow_multiple']);
2862 
2863  // grab the values for the field to display
2864  $selection_value = $this->getAssetSelectionValue($selection_name, $assetid);
2865 
2866  if ($selection_restrict_enable) {
2867  $selection_value = ($selection_restrict_min > $selection_value ? $selection_restrict_min : $selection_value);
2868  }
2869 
2870  switch (array_get_index($settings, 'type', $selection_defaults['type'])) {
2871  case 'textbox':
2872  $selection_name_field = $selection_prefix.'['.$assetid.']';
2873  $selection_id = make_valid_html_id($selection_prefix.'_'.$assetid, '_');
2874 
2875  if ($buffering) {
2876  if ($selection_restrict_enable) {
2877  int_text_box($selection_name_field, $selection_value, $selection_allow_negative, 3, $selection_restrict_min, $selection_restrict_max, '', FALSE, FALSE, 'id="'.$selection_id.'"');
2878  } else {
2879  int_text_box($selection_name_field, $selection_value, $selection_allow_negative, 3, NULL, NULL, '', FALSE, FALSE, 'id="'.$selection_id.'"');
2880  }
2881  }
2882  break;
2883 
2884  case 'selection':
2885  $selection_name_field = $selection_prefix.'['.$assetid.']';
2886  $selection_id = make_valid_html_id($selection_prefix.'_'.$assetid, '_');
2887 
2888  if ($buffering) {
2889  // list contents
2890  $selection_list = Array();
2891  if (!empty($settings['options']['restrict_range']['enable'])) {
2892  for ($ii = $selection_restrict_min; $ii <= $selection_restrict_max; $ii++) {
2893  $selection_list[$ii] = $ii;
2894  }
2895  } else {
2896  $selection_list = Array('0');
2897  }
2898 
2899  // check that the value is on the list
2900  if (!isset($selection_list[$selection_value])) {
2901  $selection_value = reset($selection_list);
2902  }
2903 
2904  // list display
2905  combo_box($selection_name_field, $selection_list, $selection_allow_multiple, $selection_value, 0, 'id="'.$selection_id.'"');
2906  }
2907  break;
2908 
2909  case 'radio':
2910  default:
2911  if ($selection_allow_multiple) {
2912  $selection_name_field = $selection_prefix.'['.$assetid.']';
2913  $selection_id = make_valid_html_id($selection_prefix.'_'.$assetid, '_');
2914 
2915  if ($buffering) {
2916  check_box($selection_name_field, '1', ($selection_value > 0 ? TRUE : FALSE), "sq_listing_check_state(this, '".$selection_prefix."')", 'id="'.$selection_id.'"');
2917  $this->registerFormField($selection_name_field);
2918 
2919  // the flag is used to determine whether a checkbox is
2920  // present and not ticked, or not present at all
2921  // only print this once, otherwise it ends up bloating the query string
2922  if (empty($this->_tmp['asset_selection_checkbox_flag'][$selection_name])) {
2923  hidden_field('selection_flag_'.$selection_prefix, '1');
2924  $this->_tmp['asset_selection_checkbox_flag'][$selection_name] = TRUE;
2925  }
2926  }
2927  } else {
2928  $selection_name_field = $selection_prefix;
2929  $selection_id = make_valid_html_id($selection_prefix.'_'.$assetid, '_');
2930 
2931  if ($buffering) {
2932  radio_button($selection_name_field, $assetid, ($selection_value == $assetid ? TRUE : FALSE), '', 'id="'.$selection_id.'"');
2933  }
2934  }
2935  break;
2936  }//end switch
2937 
2938  // set selection and its ids keywords
2939  if ($buffering) {
2940  $keywords[$selection_name.'_asset_selection'] = ob_get_clean();
2941  }
2942  $keywords[$selection_name.'_selection_name'] = $selection_name_field;
2943  $keywords[$selection_name.'_selection_id'] = $selection_id;
2944  }//end foreach
2945  }//end if
2946 
2947  // print asset ID always
2948  $keywords['asset_assetid'] = $assetid;
2949 
2950  // kept for backwards compatibility
2951  $keywords['result_no'] =& $keywords['asset_position'];
2952  $keywords['root_nodes'] = $this->getRootNodesKeywordReplacement();
2953 
2954  $keywords += $this->getExtendedAssetKeywordReplacements();
2955  // For every keyword with modifiers starting with 'asset_contents', apply the modifiers..
2956  foreach ($keywords as $kw => $val) {
2957  if (substr($kw, 0, strlen('asset_contents')) == 'asset_contents') {
2958  $plain_keyword = parse_keyword($kw, $modifiers);
2959  if (!empty($modifiers)) {
2960  // If this is a keyword with modifiers, first see if context
2961  // is one of them, and if so, augment the plain keyword
2962  // so we get the version in the correct context
2963  $contextid = extract_context_modifier($modifiers);
2964 
2965  if ($contextid !== NULL) {
2966  $plain_keyword .= '^context:'.$all_contexts[$contextid]['name'];
2967  }
2968 
2969  // Start the modified keyword off with the plain replaced value
2970  $keywords[$kw] = $keywords[$plain_keyword];
2971 
2972  // Apply modifiers
2973  apply_keyword_modifiers($keywords[$kw], $modifiers, Array('assetid' => $assetid, 'replacements' => $keywords));
2974  }//end if
2975  }//end if
2976  }//end foreach
2977 
2978  $bodycopy->setKeywordReplacements($keywords);
2979  $bodycopy->printBody();
2980 
2981  if ($prev_list_current_asset_id) {
2982  $_SESSION[SQ_SESSION_SANDBOX_INDEX]['list_current_asset_id'] = $prev_list_current_asset_id;
2983  } else {
2984  unset($_SESSION[SQ_SESSION_SANDBOX_INDEX]['list_current_asset_id']);
2985  }
2986 
2987  }//end _printAsset()
2988 
2989 
3002  protected function _getAssetContentsBCKeywordReplacement(Asset $asset, $contextid=NULL)
3003  {
3004  $replacement = '';
3005 
3006  // Protect against infinite recursion by blocking re-printing of the
3007  // Listing Engine asset
3008  if ($asset->id !== $this->id) {
3009  // Change to alternate context?
3010  if ($contextid !== NULL) {
3011  $GLOBALS['SQ_SYSTEM']->changeContext($contextid);
3012  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($asset->id);
3013  }
3014 
3015  ob_start();
3016  $asset->printBodyWithPaintLayout();
3017  $replacement = ob_get_clean();
3018 
3019  // Restore context, and clean the asset from the cache
3020  if ($contextid !== NULL) {
3021  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
3022  unset($asset);
3023  $GLOBALS['SQ_SYSTEM']->restoreContext();
3024  }
3025 
3026  }//end if asset is not Listing Engine itself
3027 
3028  return $replacement;
3029 
3030  }//end _getAssetContentsBCKeywordReplacement()
3031 
3032 
3044  protected function _getAssetContentsRawBCKeywordReplacement(Asset $asset, $contextid=NULL)
3045  {
3046  $replacement = '';
3047 
3048  // Protect against infinite recursion by blocking re-printing of the
3049  // Listing Engine asset
3050  if ($asset->id !== $this->id) {
3051  // Change to alternate context?
3052  if ($contextid !== NULL) {
3053  $GLOBALS['SQ_SYSTEM']->changeContext($contextid);
3054  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($asset->id);
3055  }
3056 
3057  ob_start();
3058  $asset->printBody();
3059  $replacement = ob_get_clean();
3060 
3061  // Restore context, and clean the asset from the cache
3062  if ($contextid !== NULL) {
3063  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
3064  unset($asset);
3065  $GLOBALS['SQ_SYSTEM']->restoreContext();
3066  }
3067 
3068  }//end if asset is not Listing Engine itself
3069 
3070  return $replacement;
3071 
3072  }//end _getAssetContentsRawBCKeywordReplacement()
3073 
3074 
3095  protected function _getAssetContentsPaintBCKeywordReplacement(Asset $asset, $paint_layout, $contextid=NULL)
3096  {
3097  $replacement = '';
3098 
3099  // Protect against infinite recursion by blocking re-printing of the
3100  // Listing Engine asset
3101  if ($asset->id !== $this->id) {
3102  // Change to alternate context?
3103  if (($contextid !== NULL) && ($contextid !== 'current')) {
3104  $GLOBALS['SQ_SYSTEM']->changeContext($contextid);
3105  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($asset->id);
3106  }
3107 
3108  //layout code name must not be number, otherwise it will be considered as asset id
3109  if (!assert_valid_assetid($paint_layout, 'Not valid assetid', TRUE, FALSE)) {
3110  $paint_layout = $GLOBALS['SQ_SYSTEM']->am->getValueFromURL(preg_replace('/^[^:]*:\/\//', '', $asset->getUrl()), 'paint_layout::user::'.$paint_layout);
3111  }
3112 
3113  if (!empty($paint_layout) && $GLOBALS['SQ_SYSTEM']->am->assetExists($paint_layout)) {
3114  ob_start();
3115  $asset->printBodyWithPaintLayout($paint_layout);
3116  $replacement = ob_get_clean();
3117  }
3118 
3119  // Restore context, and clean the asset from the cache
3120  if (($contextid !== NULL) && ($contextid !== 'current')) {
3121  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
3122  unset($asset);
3123  $GLOBALS['SQ_SYSTEM']->restoreContext();
3124  }
3125 
3126  }//end if asset is not Listing Engine itself
3127 
3128  return $replacement;
3129 
3130  }//end _getAssetContentsPaintBCKeywordReplacement()
3131 
3132 
3141  function &_getTypeFormatBodycopy($type_code)
3142  {
3143  if (!isset($this->_tmp['type_format_bcs'][$type_code])) {
3144  $this->_tmp['type_format_bcs'][$type_code] = NULL;
3145  $link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->_tmp['type_folder_id'], SQ_LINK_TYPE_2, 'bodycopy', TRUE, $type_code);
3146  if ($link) {
3147  $this->_tmp['type_format_bcs'][$type_code] = $GLOBALS['SQ_SYSTEM']->am->getAsset($link['minorid'], 'bodycopy');
3148  } else {
3149  $this->_tmp['is_default'][] = $type_code;
3150  }
3151  }
3152  return $this->_tmp['type_format_bcs'][$type_code];
3153 
3154  }//end _getTypeFormatBodycopy()
3155 
3156 
3165  function _getSelectionFieldNamePrefix($selection_name)
3166  {
3167  return str_replace(' ', '_', $selection_name);
3168 
3169  }//end _getSelectionFieldNamePrefix()
3170 
3171 
3179  {
3180  return $GLOBALS['SQ_SYSTEM']->am->getAssetInfoFields();
3181 
3182  }//end getSortableAssetInfo()
3183 
3184 
3196  function getRootNodes($reload=TRUE)
3197  {
3198  $frontend_asset =& $GLOBALS['SQ_SYSTEM']->frontend_asset;
3199  $frontend_asset_id = (!is_null($frontend_asset)) ? $frontend_asset->id : NULL;
3200  if ($reload || (!empty($GLOBALS['SQ_SYSTEM']->frontend_asset) && !isset($this->_tmp['root_nodes_'.$frontend_asset_id]))) {
3201  $root_asset_ids = Array();
3202  $root_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_NOTICE, '', FALSE, 'major', 'root');
3203 
3204  foreach ($root_links as $root_link) {
3205  if (strstr($root_link['minorid'], ':') !== FALSE) {
3206  $root_asset_ids[] = $root_link['minorid'];
3207  } else {
3208  $root_asset_ids[] = (int)$root_link['minorid'];
3209  }
3210  }
3211 
3212  $parameter_map = $this->getAttribute('parameter_map');
3213  $raw_dynamic_root_nodes = $parameter_map->getParameterValue('root_node');
3214  $empty_dynamic_roots = TRUE;
3215 
3216  // check if we have a dynamic parameter -- if so, let's do some security checks
3217  if (!empty($raw_dynamic_root_nodes)) {
3218 
3219  $dynamic_root_nodes = Array();
3220  // note that dynamic roots can be supplied as an array of asset ids or
3221  // as a comma delimited string of asset ids
3222  if (!is_array($raw_dynamic_root_nodes)) {
3223  $raw_dynamic_root_nodes = explode(',', $raw_dynamic_root_nodes);
3224  }
3225 
3226  foreach ($raw_dynamic_root_nodes as $dynamic_root) {
3227  if (!strlen($dynamic_root)) continue;
3228 
3229  $dynamic_parents = $GLOBALS['SQ_SYSTEM']->am->getParents($dynamic_root);
3230 
3231  // first, check to see if the dynamic parameter specified is already one of the root nodes
3232  // if so, just use that single root node
3233  if (in_array($dynamic_root, $root_asset_ids)) {
3234  $dynamic_root_nodes[] = $dynamic_root;
3235  } else {
3236  // otherwise, check to see if the dynamic parameter specified is a child of one of the root
3237  // nodes. If so, use the dynamic parameter as the root node; otherwise return an error.
3238  $matching_root_ids = array_intersect(array_keys($dynamic_parents), $root_asset_ids);
3239  if (empty($matching_root_ids)) {
3240  trigger_localised_error('CMS0020', E_USER_WARNING, $dynamic_root, $this->id);
3241  } else {
3242  $dynamic_root_nodes[] = $dynamic_root;
3243  }
3244  }
3245  }
3246 
3247  if (!empty($dynamic_root_nodes)) {
3248  $root_asset_ids = $dynamic_root_nodes;
3249  $empty_dynamic_roots = FALSE;
3250  }
3251 
3252  }//end if
3253  $dynamic_parameters = $parameter_map->getParameters();
3254  if (in_array('root_node', $dynamic_parameters) && $empty_dynamic_roots && $this->attr('dynamic_root_option') == 'empty_result') {
3255  $root_asset_ids = Array();
3256  } else if (empty($root_asset_ids)) {
3257  $root_asset_ids = Array($this->id);
3258  }
3259  $this->_tmp['root_nodes_'.$frontend_asset_id] = $root_asset_ids;
3260  }//end if
3261 
3262 
3263  return isset($this->_tmp['root_nodes_'.$frontend_asset_id]) ? $this->_tmp['root_nodes_'.$frontend_asset_id] : NULL;
3264 
3265  }//end getRootNodes()
3266 
3267 
3280  {
3281  return Array();
3282 
3283  }//end getExtendedAssetKeywordReplacements()
3284 
3285 
3294  function isDescending()
3295  {
3296  $prefix = $this->getPrefix();
3297  $field_name = $prefix.'_sort_direction';
3298 
3299  $sort_order = array_get_index($_REQUEST, $field_name);
3300  switch ($sort_order) {
3301  case '1':
3302  $result = TRUE;
3303  break;
3304 
3305  case '0':
3306  $result = FALSE;
3307  break;
3308  default:
3309  $result = $this->attr('reverse_sort');
3310  }
3311 
3312  return $result;
3313 
3314  }//end isDescending()
3315 
3316 
3330  function registerFormField($field_name)
3331  {
3332  if (!empty($field_name)) {
3333  $this->_tmp['used_form_fields'][] = $field_name;
3334  }
3335 
3336  }//end registerFormField()
3337 
3338 
3349  {
3350  return array_get_index($this->_tmp, 'used_form_fields', Array());
3351 
3352  }//end getUsedFormFields()
3353 
3354 
3388  function getSortInfo()
3389  {
3390  $prefix = $this->getPrefix();
3391  $field_name = $prefix.'_sort_by';
3392 
3393  $default_sort_by = $this->attr('default_sort_by');
3394 
3395  $sort_by = array_get_index($_REQUEST, $field_name, $default_sort_by);
3396  $sort_by_list = $this->attr('sort_by');
3397  if (!isset($sort_by_list[$sort_by])) {
3398  $sort_by = $default_sort_by;
3399  }
3400  return array_get_index($sort_by_list, $sort_by, Array());
3401 
3402  }//end getSortInfo()
3403 
3404 
3423  function groupAssetsRecursively($group_levels, $assetids, &$results)
3424  {
3425  $min_depth = $this->attr('min_depth');
3426  if ($min_depth == '') $min_depth = NULL;
3427  $max_depth = $this->attr('max_depth');
3428  if ($max_depth == '') $max_depth = NULL;
3429 
3430  $root_nodes = $this->getRootNodes(FALSE);
3431 
3432  // we need assets and groups, so if none given, let's bail
3433  if (empty($assetids) || empty($group_levels)) {
3434  return;
3435  }
3436 
3437  // we need to reverse this because we want to do the inner level first
3438  $group_levels = array_reverse($group_levels);
3439 
3440  $asset_groups = Array();
3441  foreach ($assetids as $assetid => $group_datas) {
3442  $asset_groups[] = Array('assetid' => $assetid);
3443  }
3444 
3445  // keep track of the last time we encountered a parent asset group
3446  $last_parent_asset = FALSE;
3447  $limit_array = Array();
3448 
3449  foreach ($group_levels as $group_key => $group_level) {
3450  array_unshift($limit_array, array_get_index($group_level, 'max_children', NULL));
3451  $new_asset_groups = Array();
3452 
3453  // go one node UP the tree, no matter whether we're going up or down
3454  if (!is_null($min_depth)) {
3455  $min_depth -= 1 * ($this->attr('direction') == 'up' ? -1 : 1);
3456  }
3457  if (!is_null($max_depth)) {
3458  $max_depth -= 1 * ($this->attr('direction') == 'up' ? -1 : 1);
3459  }
3460 
3461  foreach ($asset_groups as $key => $group_data) {
3462  $assetid = $group_data['assetid'];
3463 
3464  if ($last_parent_asset === FALSE) {
3465  $current_parent = $assetid;
3466  } else {
3467  $current_parent = $group_data['group'][$last_parent_asset];
3468  }
3469  switch ($group_level['group_type']) {
3470  case 'parent_asset':
3471  $restrict_types = array_get_index($group_level, 'restrict_types', Array());
3472  $direct_parent_only = array_get_index($group_level, 'direct_parent_only', FALSE);
3473 
3474  // If our direct parents have been cached from a Listing
3475  // Engine-type asset (eg. Asset Listing does this if there
3476  // is only one level and it's a direct parent grouping),
3477  // then use that instead of working it out each time
3478  if ($direct_parent_only && isset($this->_tmp['direct_parent_grouping'][$current_parent])) {
3479  $value = $this->_tmp['direct_parent_grouping'][$current_parent];
3480  } else {
3481  $value = $this->getGroupableParentAssetids($current_parent, $restrict_types, $direct_parent_only);
3482  }
3483 
3484  // skip this whole section if both depth fields are NULL
3485  // otherwise, we are limiting by parent's depth, relative
3486  // to what is set for leaf nodes
3487  if (!is_null($min_depth) || !is_null($max_depth)) {
3488 
3489  if (!isset($this->_tmp['grouping_treeids'])) {
3490  $this->_tmp['grouping_treeids'] = array();
3491  }
3492 
3493  foreach ($value as $key => $parent_assetid) {
3494 
3501  if (!isset($this->_tmp['grouping_treeids'][$parent_assetid])) {
3502  if ($this->attr('direction') == 'down') {
3503 
3504  $this->_tmp['grouping_treeids'][$parent_assetid] = $GLOBALS['SQ_SYSTEM']->am->getParents($parent_assetid, '', FALSE, NULL, NULL, TRUE, $min_depth, $max_depth);
3505  } else {
3506  $this->_tmp['grouping_treeids'][$parent_assetid] = $GLOBALS['SQ_SYSTEM']->am->getChildren($parent_assetid, '', FALSE, NULL, NULL, NULL, TRUE, $min_depth, $max_depth);
3507  }
3508  }
3509  $parents = $this->_tmp['grouping_treeids'][$parent_assetid];
3510  $intersection = array_intersect(array_keys($parents), $root_nodes);
3511  if (empty($intersection) && !in_array($parent_assetid, $root_nodes)) unset($value[$key]);
3512 
3513  }
3514  }
3515 
3516  break;
3517 
3518  case 'metadata':
3519  $metadata_field = NULL;
3520  $metadate_field_id = NULL;
3521  if (!empty($group_level['metadata_field'])) {
3522  $metadata_field_id = $group_level['metadata_field'];
3523  }
3524  if (!empty($metadata_field_id)) {
3525  $metadata_field = $GLOBALS['SQ_SYSTEM']->am->getAsset($metadata_field_id);
3526  }
3527 
3528  $presentation_value = FALSE;
3529  if (!empty($group_level['metadata_sort_type'])) {
3530  if ($group_level['metadata_sort_type'] === 'presentation') {
3531  $presentation_value = TRUE;
3532  }
3533  }
3534 
3535  $value = '';
3536  if (!is_null($metadata_field)) {
3537  // get the metadata manager if we haven't already
3538  if (!isset($mm)) {
3539  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
3540  }
3541 
3542  $parent_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($current_parent);
3543  if (!is_null($parent_asset)) {
3544  $value = $mm->getMetadataValueByAssetid($current_parent, $metadata_field_id, TRUE, $presentation_value);
3545  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($parent_asset);
3546  }
3547  }
3548  break;
3549 
3550  case 'standard_asset':
3551  if (!isset($this->_tmp['assets_info'][$current_parent])) {
3552  $this->_tmp['assets_info'][$current_parent] = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo(Array($current_parent));
3553  }
3554  if (!empty($this->_tmp['assets_info'][$current_parent])) {
3555  $asset_info = $this->_tmp['assets_info'][$current_parent];
3556 
3557  $standard_asset_field = $group_level['field'];
3558  switch ($standard_asset_field) {
3559  case 'assetid':
3560  $value = $current_parent;
3561  break;
3562 
3563  case 'status':
3564  $value = get_status_description($asset_info['status']);
3565  break;
3566 
3567  default:
3568  $value = $asset_info[$standard_asset_field];
3569  break;
3570  }
3571  }
3572  break;
3573 
3574  case 'keyword':
3575  $keyword = $group_level['keyword'];
3576  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($current_parent);
3577  if(!is_null($asset)) {
3578  $value = $asset->getKeywordReplacement($keyword);
3579  }
3580  else {
3581  $value = '';
3582  }
3583  break;
3584 
3585  case 'attribute':
3586  // implementation reserved
3587  break;
3588  }//end switch
3589 
3590  if (!is_array($value)) {
3591  $group_data['group'][$group_key] = $value;
3592  $new_asset_groups[] = $group_data;
3593  } else {
3594  foreach ($value as $sub_value) {
3595  $group_data['group'][$group_key] = $sub_value;
3596  $new_asset_groups[] = $group_data;
3597  }
3598 
3599  }
3600  }//end foreach on asset groups
3601 
3602  $asset_groups = $new_asset_groups;
3603 
3604  if ($group_level['group_type'] == 'parent_asset') {
3605  $last_parent_asset = $group_key;
3606  }
3607 
3608  }//end foreach on group level
3609 
3610  $results = Array();
3611 
3612  $group_keys = array_keys($group_levels);
3613 
3614  // we need to flip the keys back to what they were to construct the array
3615  foreach ($asset_groups as $group_data) {
3616  $base =& $results;
3617  foreach (array_reverse($group_keys) as $group_key) {
3618  $key = $group_data['group'][$group_key];
3619  // Postgres returns FALSE when no records are found
3620  if (is_null($key) || (MatrixDAL::getDbType() == 'pgsql' && $key === FALSE)) $key = '';
3621  $base =& $base[$key];
3622  }
3623  $base[$group_data['assetid']] = $assetids[$group_data['assetid']];
3624 
3625  }
3626 
3627  $this->limitAssetsRecursively($results, $limit_array);
3628 
3629  unset($this->_tmp['root_node_treeids']); // just in case we are nested in a different context later
3630 
3631  }//end groupAssetsRecursively()
3632 
3633 
3651  function limitAssetsRecursively(&$results, $limit_array)
3652  {
3653  $num = array_shift($limit_array);
3654 
3655  foreach ($results as $key => $val) {
3656  if (!is_null($num) && ($num >= 1)) {
3657  while (count($results[$key]) > $num) {
3658  array_pop($results[$key]);
3659  }
3660  }
3661 
3662  if (!empty($limit_array)) {
3663  $this->limitAssetsRecursively($results[$key], $limit_array);
3664  }
3665  }
3666 
3667  }//end limitAssetsRecursively()
3668 
3669 
3684  function getGroupableParentAssetids($child_assetid, $restrict_types=Array(), $direct_parent_only=FALSE)
3685  {
3686  // if there are proxy asset types selected and results are grouped by one of those asset types, return the parent
3687  // asset ids which are cached in the convertProxyAssetTypes() method.
3688  $proxy_settings = $this->attr('proxy_types');
3689  if (!empty($proxy_settings)) {
3690  if (!empty($this->_tmp['proxy_grouping'][$child_assetid])) {
3691  foreach ($this->_tmp['proxy_grouping'][$child_assetid] as $parent_type_code => $parent_assetids) {
3692  // if the type_code of the parent assets is one of the grouped type_code, return those parent ids
3693  if (in_array($parent_type_code, $restrict_types['type_code'])) {
3694  return array_values(array_unique($parent_assetids));
3695  }
3696  }
3697  }
3698  }
3699 
3700  $parent_link_assetids = Array();
3701 
3702  $root_nodes = $this->getRootNodes(FALSE);
3703  if (empty($root_nodes) || (strpos($child_assetid, ':') !== FALSE) || ($this->attr('direction') == 'up')) {
3704  $parent_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($child_assetid, $this->attr('link_types'), '', TRUE, 'minor');
3705  $parent_link_assetids = Array();
3706  foreach ($parent_links as $parent_link) {
3707  $parent_link_assetids[] = $parent_link['majorid'];
3708  }
3709  } else {
3710  $parent_link_assetids = $this->_getGroupableParentAssetids($child_assetid, $restrict_types, $root_nodes, $direct_parent_only);
3711  }
3712  // help the malfunctioning bridge a bit
3713  if (empty($parent_link_assetids) && strpos($child_assetid, ':') !== FALSE && $direct_parent_only) {
3714  $id_parts = explode(':', $child_assetid);
3715  $parent_link_assetids[] = $id_parts[0];
3716  }
3717 
3718  return $parent_link_assetids;
3719 
3720  }//end getGroupableParentAssetids()
3721 
3722 
3734  function &convertProxyAssetTypes(&$results)
3735  {
3736  if (empty($results)) return $results;
3737 
3738  $am = $GLOBALS['SQ_SYSTEM']->am;
3739  $proxy_settings = $this->attr('proxy_types');
3740  if (empty($proxy_settings)) {
3741  // We should be able to return the results array straight back, but
3742  // we've found sometimes it returns an empty array (PHP 4.3 ref bug??).
3743  // So dump it off to another variable by value and return that
3744  $new_results = $results;
3745  return $new_results;
3746  }
3747 
3748  $collated_types = Array();
3749  $result_types = $am->getAssetInfo(array_keys($results), 'asset', FALSE, 'type_code');
3750  foreach ($result_types as $result_assetid => $result_type) {
3751  $collated_types[$result_type][] = $result_assetid;
3752  }
3753 
3754  $new_results = Array();
3755  $this->_tmp['proxy_grouping'] = Array();
3756  foreach ($collated_types as $result_type => $result_assetids) {
3757  if (isset($proxy_settings[$result_type])) {
3758  if (empty($proxy_settings[$result_type])) {
3759  return $results;
3760  }
3761 
3762  $converted_types = Array();
3763  foreach ($proxy_settings[$result_type] as $type_code => $inherit) {
3764  if ($inherit) {
3765  $converted_types = array_merge($am->getTypeDescendants($type_code, TRUE), $converted_types);
3766  } else {
3767  $converted_types[] = $type_code;
3768  }
3769  }
3770 
3771  $children = $GLOBALS['SQ_SYSTEM']->am->getLinks($result_assetids, SQ_SC_LINK_SIGNIFICANT, $converted_types);
3772 
3773  foreach ($children as $orig_assetid => $links) {
3774  foreach ($links as $link) {
3775  // keep the original score
3776  $new_results[$link['minorid']] = $results[$orig_assetid];
3777  // store the temporary array of child => parents for use later in getGroupableParentAssetids() method
3778  // if neccessary. The structure of the array is
3779  // Array(childid => Array(parent_type_code => Array(parentid))) - A child asset can have many parents in
3780  // different type codes. Grouping parentids like this will make it easier to get them all later
3781  $this->_tmp['proxy_grouping'][$link['minorid']][$result_type][] = $orig_assetid;
3782  }
3783  }
3784 
3785  // filterAssetStatuses is overridden in search_page, so call it directly
3787  }
3788  }
3789 
3790  return $new_results;
3791 
3792  }//end convertProxyAssetTypes()
3793 
3794 
3806  function _getGroupableParentAssetids($child_assetid, $restrict_types=Array(), $root_nodes=Array(), $direct_parent_only=FALSE)
3807  {
3808  // get single treeid for each root node, children of root node should be the same on all trees
3809  $groupable_parents = Array();
3810  if (!isset($this->_tmp['root_node_treeids'])) {
3811  foreach ($GLOBALS['SQ_SYSTEM']->am->getAssetTreeids($root_nodes) as $aid => $treeids) {
3812  $this->_tmp['root_node_treeids'][$aid] = reset($treeids);
3813  }
3814  }
3815  $lineages = $GLOBALS['SQ_SYSTEM']->am->getLinkLineages($child_assetid, 0, $this->_tmp['root_node_treeids'], 'type_code');
3816 
3817  foreach ($lineages as $lineage_info) {
3818 
3819  // only get direct parent, if it is set in the group by option
3820  if ($direct_parent_only && !empty($lineage_info['lineage'])) {
3821  $all_assetids = array_keys($lineage_info['lineage']);
3822  $direct_parent_assetid = array_pop($all_assetids);
3823  $direct_parent_type = array_pop($lineage_info['lineage']);
3824  // in the form of (assetid => type_code)
3825  $lineage_info['lineage'] = Array($direct_parent_assetid => $direct_parent_type);
3826  }
3827 
3828  // if not one of the selected link types continue
3829  if (!($lineage_info['link_type'] & $this->attr('link_types'))) {
3830  continue;
3831  }
3832 
3833  // if no types selected then group by root nodes
3834  if (!isset($restrict_types['type_code']) || empty($restrict_types['type_code'])) {
3835  $groupable_parents[] = array_shift(array_keys($lineage_info['lineage']));
3836  continue;
3837  }
3838  // start from the end of the current lineage
3839  $lineage = array_reverse($lineage_info['lineage'], TRUE);
3840  $add = FALSE;
3841 
3842  foreach ($lineage as $parentid => $type_code) {
3843  if (in_array($type_code, $restrict_types['type_code'])) {
3844  $add = TRUE;
3845  } else {
3846  $inherit_vals = $restrict_types['inherit'];
3847  foreach ($restrict_types['type_code'] as $type) {
3848  $inherit = array_shift($inherit_vals);
3849  if (!isset($this->_tmp['type_decendants'][$type])) {
3850  $this->_tmp['type_decendants'][$type] = $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($type, TRUE);
3851  }
3852  if ($inherit && in_array($type_code, $this->_tmp['type_decendants'][$type])) {
3853  $add = TRUE;
3854  break;
3855  }
3856  }
3857  }
3858 
3859  if ($add) {
3860  // we have found the parent for this lineage
3861  $groupable_parents[] = $parentid;
3862  break;
3863  }
3864  }
3865  }//end foreach
3866 
3867  return array_unique($groupable_parents);
3868 
3869  }//end _getGroupableParentAssetids()
3870 
3871 
3887  function sortGroups($group_levels, $assetids, &$results)
3888  {
3889  // we need assets, so if none given, let's bail
3890  if (empty($assetids)) return;
3891 
3892  // point us at the first element, discarding it as we go
3893  $group_type = array_shift($group_levels);
3894 
3895  // sort the groups
3896  switch ($group_type['group_type']) {
3897  case 'parent_asset':
3898  // find the info on the assets that are being used, sort by them,
3899  // then re-order the results based upon them
3900  if (!empty($group_type['sorting_metadata_field'])) {
3901  $sorting_metadata_field_id = $group_type['sorting_metadata_field'];
3902  if (!empty($sorting_metadata_field_id)) {
3903  $sorting_metadata_field = $GLOBALS['SQ_SYSTEM']->am->getAsset($sorting_metadata_field_id);
3904  }
3905  if (!is_null($sorting_metadata_field)) {
3906  // get the metadata manager if we haven't already
3907  if (!isset($mm)) {
3908  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
3909  }//end if
3910  $group_order = Array();
3911 
3912  $presentation_value = FALSE;
3913  if (!empty($group_type['metadata_sort_type'])) {
3914  if ($group_type['metadata_sort_type'] === 'presentation') {
3915  $presentation_value = TRUE;
3916  }
3917  }
3918 
3919  foreach ($results as $key => $arrayInfo) {
3920  $group_order[$key] = $mm->getMetadataValueByAssetid($key, $sorting_metadata_field_id, TRUE, $presentation_value);
3921  }//end foreach
3922  }//end if
3923  } else {
3924  $group_order = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo(array_keys($results), 'asset', FALSE,array_get_index($group_type, 'sort_by', 'assetid'));
3925 
3926  }//end else
3927 
3928  if (array_get_index($group_type, 'sort_order', 'asc') == 'asc') {
3929  asort($group_order);
3930  } else {
3931  arsort($group_order);
3932  }
3933  $group_results = Array();
3934  foreach (array_keys($group_order) as $assetid) {
3935  $group_results[$assetid] =& $results[$assetid];
3936  }
3937  $results = $group_results;
3938 
3939  break;
3940 
3941  case 'metadata':
3942  $group_order = Array();
3943  if (!empty($group_type['metadata_field'])) {
3944  $metadata_field = $GLOBALS['SQ_SYSTEM']->am->getAsset($group_type['metadata_field']);
3945  if(!is_null($metadata_field)) {
3946  if (!isset($mm)) {
3947  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
3948  }//end if
3949 
3950  $presentation_value = FALSE;
3951  if (!empty($group_type['metadata_sort_type'])) {
3952  if ($group_type['metadata_sort_type'] === 'presentation') {
3953  $presentation_value = TRUE;
3954  }
3955  }
3956 
3957  foreach ($results as $key => $arrayInfo) {
3958  // if the metadata field types are of Metadata_Field_Hierarchy OR Metadata_Field_Select
3959  // for other metadata fields the key is the value
3960  if (method_exists($metadata_field, 'getKeyFromValue') && !$presentation_value && $metadata_field->attr('visible_part') === 'key') {
3961  //we are sure its a metadata select field lets see whats been printed on frontend key OR value
3962  $raw_value = $metadata_field->getKeyFromValue($key);
3963  $group_order[$raw_value] = $arrayInfo;
3964  unset($results[$key]);
3965  }
3966  }//end foreach
3967  }
3968  } else {
3969  $group_order = $GLOBALS['SQ_SYSTEM']->am->getAssetInfo(array_keys($results), 'asset', FALSE,array_get_index($group_type, 'sort_by', 'assetid'));
3970 
3971  }//end else
3972 
3973  if (!empty($group_order) )$results = $group_order;
3974  //dont break here, just go with the flow ;)
3975  case 'standard_asset':
3976  case 'keyword':
3977  // metadata and standard asset field groups are sorted just on
3978  // the key's value, because the key IS the value
3979  if (array_get_index($group_type, 'sort_order', 'asc') == 'asc') {
3980  ksort($results);
3981  } else {
3982  krsort($results);
3983  }
3984  break;
3985 
3986  }//end switch
3987 
3988  // if their are group levels remaining, we want to call
3989  // this function again for each of the groups we have made
3990  if (count($group_levels) > 0) {
3991  foreach ($results as $index => $group) {
3992  $this->sortGroups($group_levels, $group, $results[$index]);
3993  }
3994  }
3995 
3996  }//end sortGroups()
3997 
3998 
4007  {
4008  // if we have no group formats, it's not worth our time
4009  $group_folder = $this->getFolder('group_formats');
4010  $groups = $this->attr('asset_grouping');
4011  if (empty($groups)) return TRUE;
4012 
4013 
4014  // set forced run level because we want to change the names of these
4015  // formats whether we have permission or not
4016  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
4017  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
4018  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
4019 
4020  $i = 1;
4021  foreach ($groups as $group) {
4022  // update the group format
4023  $format_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($group['format_assetid']);
4024  if ($format_asset) {
4025  $format_asset->setAttrValue('name', 'Group Level '.$i.' Format');
4026  $format_asset->saveAttributes();
4027  }
4028 
4029  $i++;
4030  }
4031 
4032  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
4033  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
4034  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
4035  return TRUE;
4036 
4037  }//end reindexGroupFormats()
4038 
4039 
4048  {
4049  return TRUE;
4050 
4051  }//end processAssetSelections()
4052 
4053 
4064  function getAssetSelectionValue($selection_name, $assetid)
4065  {
4066  return 0;
4067 
4068  }//end getAssetSelectionValue()
4069 
4070 
4078  {
4079  return Array(
4080  'type' => 'radio',
4081  'options' => Array(
4082  'allow_multiple' => 0,
4083  'allow_negative' => 0,
4084  'restrict_range' => Array(
4085  'enable' => 0,
4086  'min' => 0,
4087  'max' => 10,
4088  ),
4089  ),
4090  'permanent' => 0,
4091  );
4092 
4093  }//end getAssetSelectionDefaults()
4094 
4095 
4103  {
4104  return Array(
4105  'label' => '',
4106  'target_asset' => Array(
4107  'assetid' => 0,
4108  'url' => '',
4109  ),
4110  'target_url' => '',
4111  'permanent' => 0,
4112  );
4113 
4114  }//end getAssetTargetDefaults()
4115 
4116 
4126  function _arrayCountRecursive(&$array, $limit=NULL)
4127  {
4128  $count = 0;
4129 
4130  for (reset($array); NULL !== ($k = key($array)); next($array)) {
4131  $el =& $array[$k];
4132  if (is_array($el) && (is_null($limit) || ($limit > 0))) {
4133  $count += $this->_arrayCountRecursive($el, (is_null($limit) ? NULL : $limit - 1));
4134  } else {
4135  $count++;
4136  }
4137  }
4138 
4139  return $count;
4140 
4141  }//end _arrayCountRecursive()
4142 
4143 
4151  {
4152  if (!isset($this->_tmp['position_formats'])) {
4153  $this->_tmp['position_formats'] = Array();
4154 
4155  $position_folder = $this->getFolder('position_formats');
4156  $format_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($position_folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE);
4157  foreach ($format_links as $link_data) {
4158  $position = substr($link_data['value'], 9);
4159  $this->_tmp['position_formats'][$position] = $link_data['minorid'];
4160  }
4161  }
4162 
4163  return $this->_tmp['position_formats'];
4164 
4165  }//end _getPositionFormats()
4166 
4167 
4168 //-- KEYWORD DESCRIPTION --//
4169 
4170 
4181  function onRequestKeywords(&$broadcaster, $vars=Array())
4182  {
4183  $vars['keywords'] = isset($vars['keywords']) ? $vars['keywords'] : Array();
4184 
4185  $parents = $GLOBALS['SQ_SYSTEM']->am->getParents($broadcaster->id, 'bodycopy', TRUE);
4186  $parent_folders = $GLOBALS['SQ_SYSTEM']->am->getParents($broadcaster->id, 'folder', TRUE);
4187 
4188  // if we're being handled here (instead of one of our extensions),
4189  // it must be from one of our format bodycopies, or something else altogether,
4190  // so we'll try to find out which of the format types it is.
4191  $group_folder = $this->getFolder('group_formats');
4192  $position_folder = $this->getFolder('position_formats');
4193  $type_folder = $this->getFolder('type_formats');
4194 
4195  $keywords = Array();
4196  if (!is_null($group_folder) && in_array($group_folder->id, array_keys($parent_folders))) {
4197  // group format
4198  $keywords = $this->_getGroupFolderKeywords();
4199 
4200  } else if (!is_null($position_folder) && in_array($position_folder->id, array_keys($parent_folders))) {
4201  // position format
4202  $keywords = $this->_getGenericSingleAssetFormatKeywords();
4203 
4204  } else if (!is_null($type_folder) && in_array($type_folder->id, array_keys($parent_folders))) {
4205  // specific or default type format
4206  $type_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($type_folder->id, SQ_LINK_TYPE_2, 'bodycopy');
4207  $type_codes = Array();
4208  foreach ($type_links as $link_info) {
4209  if (isset($parents[$link_info['minorid']])) {
4210  $type_codes[] = $link_info['value'];
4211  }
4212  }
4213 
4214  $type_name = NULL;
4215  if ((count($type_codes) == 1) && ($type_codes[0] != 'default_format')) {
4216  $type_name = $type_codes[0];
4217  }
4218  $keywords = $this->_getGenericSingleAssetFormatKeywords($type_name);
4219 
4220  } else {
4221  // page contents format
4222  $keywords = $this->_getContentsKeywords();
4223  }
4224 
4225  $vars['keywords'] = array_merge($vars['keywords'], $keywords);
4226 
4227  }//end onRequestKeywords()
4228 
4229 
4241  {
4242  $keywords = $this->_getAdditionalSingleAssetFormatKeywords();
4243 
4244  if (!empty($type)) {
4245  $GLOBALS['SQ_SYSTEM']->am->includeAsset($type);
4246  $dummy_asset = new $type();
4247  $keywords += $dummy_asset->getAvailableKeywords();
4248  unset($dummy_asset);
4249  } else {
4250  $keywords += Asset::getAvailableKeywords();
4251  }
4252 
4253  return $keywords;
4254 
4255  }//end _getGenericSingleAssetFormatKeywords()
4256 
4257 
4268  {
4269  $keywords['asset_lineage'] = translate('asset_lineage');
4270  $keywords['asset_lineage_linked'] = translate('asset_lineage_linked');
4271  $keywords['asset_contents'] = translate('cms_listing_asset_contents');
4272  $keywords['asset_contents_paint_'] = translate('cms_listing_asset_contents_paint');
4273  $keywords['asset_name_linked'] = translate('cms_listing_asset_name_linked');
4274  $keywords['asset_short_name_linked'] = translate('cms_listing_asset_short_name_linked');
4275  $keywords['asset_position'] = translate('cms_listing_asset_position');
4276  $keywords['asset_odd_even'] = translate('cms_listing_asset_position_odd_even');
4277  $keywords['root_nodes'] = translate('cms_listing_keyword_root_nodes');
4278  $keywords['asset_contents_raw'] = translate('cms_listing_asset_contents_raw');
4279 
4280  // asset selections
4281  $selections = $this->attr('asset_selections');
4282  if (is_array($selections) && !empty($selections)) {
4283  foreach ($selections as $name => $settings) {
4284  $keywords[$name.'_asset_selection'] = translate('cms_listing_keyword_asset_selection', $name);
4285  }
4286  }
4287 
4288  return $keywords;
4289 
4290  }//end _getAdditionalSingleAssetFormatKeywords()
4291 
4292 
4307  {
4308  $keywords = parent::getAvailableKeywords();
4309  return $keywords;
4310 
4311  }//end getAvailableKeywords()
4312 
4313 
4328  {
4329  $GLOBALS['SQ_SYSTEM']->lm->includeAssetStrings('page_asset_listing');
4330 
4331  $keywords['sort_by'] = translate('cms_listing_keyword_sort_by');
4332  $keywords['sort_order'] = translate('cms_listing_keyword_sort_order');
4333  $keywords['reset_button'] = translate('cms_listing_keyword_reset_button');
4334  $keywords['submit_button'] = translate('cms_listing_keyword_submit_button');
4335  $keywords['root_nodes'] = translate('cms_listing_keyword_root_nodes');
4336 
4337  $keywords['asset_listing'] = translate('cms_listing_asset_listing');
4338  $keywords['previous_page'] = translate('cms_listing_previous_page_link');
4339  $keywords['previous_page_href'] = translate('cms_listing_previous_page_link_href');
4340  $keywords['next_page'] = translate('cms_listing_next_page_link');
4341  $keywords['next_page_href'] = translate('cms_listing_next_page_link_href');
4342 
4343  $keywords['page_list'] = translate('cms_listing_page_list');
4344  if ($this->attr('group_by') == 'letter') {
4345  $keywords['page_list_without_unused'] = translate('cms_listing_page_list_without_unused');
4346  }
4347  $keywords['page_list_X'] = translate('cms_listing_page_list_sliding');
4348 
4349  $keywords['page_number'] = translate('cms_listing_page_number');
4350  $keywords['asset_count'] = translate('cms_listing_asset_count');
4351  $keywords['page_asset_count'] = translate('cms_listing_page_asset_count');
4352  $keywords['total_pages'] = translate('cms_listing_total_pages');
4353  $keywords['first_asset_position'] = translate('cms_listing_first_asset_position');
4354  $keywords['last_asset_position'] = translate('cms_listing_last_asset_position');
4355  $keywords['unique_asset_count'] = translate('cms_listing_unique_asset_count');
4356  $keywords['structured_root_node'] = translate('cms_listing_structured_root_node');
4357 
4358  $keywords['select_all_js_code'] = 'Select All Script Code';
4359 
4360  $selections = $this->attr('asset_selections');
4361  if (is_array($selections) && !empty($selections)) {
4362  foreach ($selections as $name => $settings) {
4363  if (array_get_index($settings, 'type', '') == 'radio' && !empty($settings['options']['allow_multiple'])) {
4364  $keywords[$name.'_check_all'] = translate('cms_listing_asset_selection_check_all', $name);
4365  }
4366  }
4367  }
4368 
4369  $targets = $this->attr('asset_targets');
4370  if (is_array($targets) && !empty($targets)) {
4371  foreach ($targets as $name => $settings) {
4372  $keywords[$name.'_asset_target'] = translate('cms_listing_asset_target_button', $name);
4373  }
4374  }
4375 
4376  return $keywords;
4377 
4378  }//end _getContentsKeywords()
4379 
4380 
4391  {
4392  $keywords['asset_lineage'] = translate('asset_lineage');
4393  $keywords['asset_lineage_linked'] = translate('asset_lineage_linked');
4394  $keywords['asset_contents'] = translate('cms_listing_asset_contents');
4395  $keywords['asset_name_linked'] = translate('cms_listing_asset_name_linked');
4396  $keywords['asset_short_name_linked'] = translate('cms_listing_asset_short_name_linked');
4397  $keywords['asset_position'] = translate('cms_listing_asset_position');
4398  $keywords['root_nodes'] = translate('cms_listing_keyword_root_nodes');
4399 
4400 
4401  // asset selections
4402  $selections = $this->attr('asset_selections');
4403  if (is_array($selections) && !empty($selections)) {
4404  foreach ($selections as $name => $settings) {
4405  $keywords[$name.'_asset_selection'] = translate('cms_listing_keyword_asset_selection', $name);
4406  }
4407  }
4408 
4409  return $keywords;
4410 
4411  }//end _getTypeFormatsKeywords()
4412 
4413 
4421  {
4422  $fake_asset = new Asset();
4423  $keywords = $fake_asset->getAvailableKeywords();
4424  foreach ($keywords as $key =>$keyword) {
4425  $key1 = substr_replace($key, 'parent', 0, 5);
4426  $keywords[$key1] = $keywords[$key];
4427  unset($keywords[$key]);
4428  }
4429  $keywords['group_listing'] = translate('cms_listing_group_listing');
4430  $keywords['group_name'] = translate('cms_listing_group_name');
4431  $keywords['group_name_linked'] = translate('cms_listing_group_name_linked');
4432  $keywords['parent_contents'] = translate('cms_listing_parent_contents');
4433  $keywords['parent_contents_raw'] = translate('cms_listing_parent_contents_raw');
4434  $keywords['group_immed_child_group_count'] = translate('cms_listing_group_immed_child_group_count');
4435  $keywords['group_total_child_asset_count'] = translate('cms_listing_group_total_child_asset_count');
4436 
4437  $selections = $this->attr('asset_selections');
4438  if (is_array($selections) && !empty($selections)) {
4439  foreach ($selections as $name => $settings) {
4440  if (array_get_index($settings, 'type', '') == 'radio' && !empty($settings['options']['allow_multiple'])) {
4441  $keywords[$name.'_check_all_in_group'] = translate('cms_listing_asset_selection_check_all_in_group', $name);
4442  }
4443  }
4444  }
4445  return $keywords;
4446 
4447  }//end _getGroupFolderKeywords()
4448 
4449 
4450 //-- KEYWORD REPLACEMENT --//
4451 
4452 
4470  function getKeywordReplacement($original_keyword)
4471  {
4472  if (empty($original_keyword)) return '';
4473  $keyword = parse_keyword($original_keyword, $modifiers);
4474 
4475  // check the keyword against the dynamic keywords
4476  // asset targets
4477  if (preg_match('/^(.*)(_asset_target)$/', $keyword, $matches)) {
4478  if (isset($matches[1]) && isset($matches[2])) {
4479  $args = $matches[1];
4480  $keyword_stem = $matches[2];
4481  }
4482  }
4483 
4484  // setup the replacement function call
4485  if (!empty($keyword_stem) && !empty($args)) {
4486  $func_name = 'get'.ucwords_no_space($keyword_stem).'KeywordReplacement';
4487  if (method_exists($this, $func_name)) {
4488  $replacement = $this->$func_name($args);
4489  apply_keyword_modifiers($replacement, $modifiers, Array('assetid' => $this->id));
4490  return $replacement;
4491  }
4492  }
4493  // otherwise, fall back to the parent
4494  $replacement = parent::getKeywordReplacement($keyword);
4495 
4496  if ($replacement == '%'.$keyword.'%' && empty($modifiers)) {
4497  return $replacement;
4498  } else if($replacement == '%'.$keyword.'%' && !empty($modifiers)) {
4499  return '%'.$original_keyword.'%';
4500  }
4501 
4502  if (!is_null($replacement)) {
4503  apply_keyword_modifiers($replacement, $modifiers, Array('assetid' => $this->id));
4504  }
4505  return $replacement;
4506 
4507  }//end getKeywordReplacement()
4508 
4509 
4525  function getContentsKeywordReplacements($keywords=Array())
4526  {
4527  $replacements = Array();
4528 
4529  $selections = $this->attr('asset_selections');
4530  $selection_defaults = $this->getAssetSelectionDefaults();
4531 
4532  foreach ($selections as $selection_name => $settings) {
4533 
4534  if (!in_array($selection_name.'_check_all', $keywords)) {
4535  continue;
4536  }
4537  $this->registerFormField($selection_name.'_check_all');
4538 
4539  // predefine some commonly used settings, for brevity's sake
4540  $selection_options = array_get_index($settings, 'options', $selection_defaults['options']);
4541  $selection_allow_multiple = array_get_index($selection_options, 'allow_multiple', $selection_defaults['options']['allow_multiple']);
4542 
4543  // only print the 'check all' checkbox if we're only dealing with checkboxes
4544  if ((array_get_index($settings, 'type', $selection_defaults['type']) == 'radio') && $selection_allow_multiple) {
4545  ob_start();
4546  $onclick = Array();
4547  $group_cb_id = 'all';
4548  $control_name = make_valid_html_id($this->_getSelectionFieldNamePrefix($selection_name).str_replace(' ', '_', $group_cb_id));
4549  $this->registerFormField($control_name);
4550  check_box($control_name, '1', FALSE, "sq_listing_check_state(this, '".$this->_getSelectionFieldNamePrefix($selection_name)."')");
4551  $replacements[$selection_name.'_check_all'] = ob_get_clean();
4552  $replacements[$selection_name.'_selection_id'] = $control_name;
4553  } else {
4554  $replacements[$selection_name.'_check_all'] = '';
4555  }
4556  }
4557 
4558  return $replacements;
4559 
4560  }//end getContentsKeywordReplacements()
4561 
4562 
4572  {
4573  $prefix = $this->getPrefix();
4574  $field_name = $prefix.'_sort_by';
4575  $this->registerFormField($field_name);
4576 
4577  $default_sort_by = $this->attr('default_sort_by');
4578  $sort_by = array_get_index($_REQUEST, $field_name, $default_sort_by);
4579 
4580  $sort_by_list = $this->attr('sort_by');
4581  if (!isset($sort_by_list[$sort_by])) {
4582  $sort_by = $default_sort_by;
4583  }
4584 
4585  $options = Array();
4586  foreach ($sort_by_list as $key => $value) {
4587  $options[$key] = $value['name'];
4588  }
4589 
4590  ob_start();
4591  combo_box($field_name, $options, FALSE, $sort_by);
4592  return ob_get_clean();
4593 
4594  }//end getSortByKeywordReplacement()
4595 
4596 
4604  {
4605  $prefix = $this->getPrefix();
4606  $field_name = $prefix.'_sort_direction';
4607  $this->registerFormField($field_name);
4608 
4609  $options = Array(
4610  '0' => $this->attr('sort_direction_asc_text'),
4611  '1' => $this->attr('sort_direction_desc_text'),
4612  );
4613 
4614  ob_start();
4615  combo_box($field_name, $options, FALSE, (int)$this->isDescending());
4616  return ob_get_clean();
4617 
4618  }//end getSortOrderKeywordReplacement()
4619 
4620 
4628  {
4629  $prefix = $this->getPrefix();
4630  $field_name = $prefix.'_submit_button';
4631  $this->registerFormField($field_name);
4632 
4633  $button_text = $this->attr('submit_button_text');
4634 
4635  ob_start();
4636  submit_button($field_name, $button_text);
4637  return ob_get_clean();
4638 
4639  }//end getSubmitButtonKeywordReplacement()
4640 
4641 
4649  {
4650  $prefix = $this->getPrefix();
4651  $field_name = $prefix.'_reset_button';
4652  $this->registerFormField($field_name);
4653 
4654  $button_text = $this->attr('reset_button_text');
4655 
4656  ob_start();
4657  reset_button($field_name, $button_text);
4658  return ob_get_clean();
4659 
4660  }//end getResetButtonKeywordReplacement()
4661 
4662 
4670  {
4671  if (SQ_IN_BACKEND || SQ_IN_LIMBO) {
4672  $root_nodes = $this->getRootNodes(TRUE);
4673  } else {
4674  $root_nodes = $this->getRootNodes(FALSE);
4675  }//end if
4676  return implode(',', $root_nodes);
4677 
4678  }//end getRootNodesKeywordReplacement()
4679 
4680 
4690  {
4691  $targets = $this->attr('asset_targets');
4692  $prefix = $this->getPrefix();
4693  $field_name = $prefix.'_'.$name.'_asset_target';
4694  $this->registerFormField($field_name);
4695 
4696  if (isset($targets[$name])) {
4697  $defaults = $this->getAssetTargetDefaults();
4698  $button_text = array_get_index($targets[$name], 'label', $defaults['label']);
4699  $target = NULL;
4700 
4701  // determine whether we're using the url or the asset (url takes precedence)
4702  $target_url = array_get_index($targets[$name], 'target_url', $defaults['target_url']);
4703  if ($target_url) {
4704  $target = $target_url;
4705  } else {
4706  $target_asset = array_get_index($targets[$name], 'target_asset', $defaults['target_asset']);
4707  $target_assetid = array_get_index($target_asset, 'assetid', $defaults['target_asset']['assetid']);
4708  if ($GLOBALS['SQ_SYSTEM']->am->assetExists($target_assetid)) {
4709  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($target_assetid);
4710  $target = $asset->getURL();
4711  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($target_assetid);
4712  }
4713  }
4714 
4715  if (!is_null($target)) {
4716  ob_start();
4717  submit_button($field_name, $button_text, "document.getElementById('".$this->getPrefix()."').action = '".$target."';");
4718  return ob_get_clean();
4719  }
4720  }
4721 
4722  return '';
4723 
4724  }//end getAssetTargetKeywordReplacement()
4725 
4726 
4734  {
4735  $options = $this->attr('structured_dropdown_options');
4736  if (empty($options['root_node'])) return '';
4737 
4738  if (!empty($options['name'])) {
4739  $this->registerFormField($options['name']);
4740  }
4741 
4742  // initial type_codes, before we check for inheritance
4743  $pass_codes = array_keys($options['asset_types']);
4744  foreach ($options['asset_types'] as $code => $inherit) {
4745  if ($inherit == 1) {
4746  $pass_codes += $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($code);
4747  }
4748  }
4749  $selected = (isset($_REQUEST[$options['name']])) ? Array($_REQUEST[$options['name']]) : Array();
4750 
4751  if (isset($selected[0]) && is_array($selected[0])) {
4752  $selected = $selected[0];
4753  }
4754 
4755  ob_start();
4756  structured_drop_down($options['name'], $options['root_node'], $pass_codes, $selected, $options['width'], $options['height'], $options['max_depth'], $this->attr('check_boxes') == 2 ? TRUE : FALSE, $options['seperator'], TRUE, $options['all_text'], $this->attr('check_boxes'));
4757  $replacement = ob_get_contents();
4758  ob_end_clean();
4759  return $replacement;
4760 
4761  }//end getStructuredRootNodeKeywordReplacement()
4762 
4763 
4771  {
4772  return array_get_index($this->_tmp, 'js_relation_code', '');
4773 
4774  }//end getSelectAllJSCodeKeywordReplacement()
4775 
4776 
4777 //-- ASSET SELECTION HELPERS --//
4778 
4779 
4791  function _recurseCheckAllRelations(&$todo, &$relations, $prefix, $limit=NULL)
4792  {
4793  for (reset($todo); NULL !== ($k = key($todo)); next($todo)) {
4794  $el =& $todo[$k];
4795  if (is_array($el) && (is_null($limit) || ($limit > 0))) {
4796 
4797  $new_rel = Array('parent' => $prefix.$k, 'children' => Array());
4798  foreach (array_keys($el) as $el_key) {
4799  if ($limit > 1) {
4800  $new_rel['children'][] = $prefix.$k.'_'.$el_key;
4801  } else {
4802  $new_rel['children'][] = '['.$el_key.']';
4803  }
4804  }
4805  $relations[] = $new_rel;
4806 
4807  $this->_recurseCheckAllRelations($el, $relations, $prefix.$k.'_', (is_null($limit) ? NULL : $limit - 1));
4808  }
4809  }
4810 
4811  }//end _recurseCheckAllRelations()
4812 
4813 
4826  {
4827  if (!isset($this->_tmp['check_all_relations'])) {
4828  // if custom grouping is enabled and set up, we need to modify the
4829  // first level of groups to use the 'g' prefix
4830  if (($this->attr('group_by') == 'grouped') && (count($this->attr('asset_grouping')) > 0)) {
4831  $new_rel = Array('parent' => '_all', 'children' => Array());
4832  foreach (array_keys($todo) as $group_id) {
4833  $new_rel['children'][] = 'g_'.$group_id;
4834  }
4835  } else {
4836  $new_rel = Array('parent' => '_all', 'children' => Array());
4837  foreach (array_keys($todo) as $child) {
4838  $new_rel['children'][] = '['.$child.']';
4839 
4840  }
4841  }
4842  $relations = Array($new_rel);
4843 
4844  $this->_recurseCheckAllRelations($todo, $relations, 'g_', count($this->attr('asset_grouping')));
4845  $this->_tmp['check_all_relations'] =& $relations;
4846  }
4847 
4848  return $this->_tmp['check_all_relations'];
4849 
4850  }//end _analyseCheckAllRelations()
4851 
4852 
4860  {
4861  // order is important, and we actually need the 'ALL' one LAST so we need
4862  // to reverse this
4863  $rels = array_reverse($this->_tmp['check_all_relations']);
4864  $code = 'init_select_list_array(); select_list[\''.$this->getPrefix().'\'] = [';
4865 
4866  $select_lines = Array();
4867  foreach ($rels as $rel) {
4868  $parent_code = str_replace(' ', '_', $rel['parent']);
4869  for (reset($rel['children']); NULL !== ($k = key($rel['children'])); next($rel['children'])) {
4870  if (preg_match('|\[(.*)\]|', $rel['children'][$k], $matches)) {
4871  $rel['children'][$k] = $matches[1];
4872  $bracketed = TRUE;
4873  } else {
4874  $bracketed = FALSE;
4875  }
4876 
4877  if (is_numeric($rel['children'][$k])) {
4878  $rel['children'][$k] = str_replace(' ', '_', $rel['children'][$k]);
4879  } else {
4880  $rel['children'][$k] = make_valid_html_id(str_replace(' ', '_', $rel['children'][$k]));
4881  }
4882 
4883  if ($bracketed) {
4884  $rel['children'][$k] = '['.$rel['children'][$k].']';
4885  }
4886  }
4887  $select_lines[] = "[['".make_valid_html_id($parent_code)."'], ['".implode("', '", $rel['children'])."']]";
4888  }
4889  $code .= implode(', ', $select_lines);
4890 
4891  $code .= ']';
4892 
4893  return $code;
4894 
4895  }//end _buildRelationsJS()
4896 
4897 
4904  function _getFormSubmitMethod()
4905  {
4906  return $this->attr('form_submit_method');
4907 
4908  }//end _getFormSubmitMethod()
4909 
4910 
4911 //-- NO RESULTS BODYCOPY --//
4912 
4913 
4924  function createNoResultsBodycopy($enable_on_create=TRUE)
4925  {
4926  $bodycopy_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3, 'bodycopy', FALSE, 'major', 'no_results');
4927  $bodycopy_link = reset($bodycopy_links);
4928 
4929  // we already have a bodycopy link?!
4930  if ($bodycopy_link) {
4931  return FALSE;
4932  } else {
4933  $GLOBALS['SQ_SYSTEM']->am->includeAsset('bodycopy');
4934 
4935  $link_type = ($enable_on_create ? SQ_LINK_TYPE_2 : SQ_LINK_TYPE_3);
4936 
4937  $asset = new Bodycopy();
4938  $copy_link = Array(
4939  'asset' => &$this,
4940  'value' => 'no_results',
4941  'link_type' => $link_type,
4942  'is_dependant' => 1,
4943  'is_exclusive' => 1,
4944  );
4945 
4946  $asset->setAttrValue('name', 'Page Contents (No Results)');
4947  $args = Array('content' => $this->_getDefaultBodycopyContent('no_results'));
4948  if (!$asset->create($copy_link, $args)) return FALSE;
4949 
4950  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
4951  unset($asset);
4952  }
4953 
4954  return TRUE;
4955 
4956  }//end createNoResultsBodycopy()
4957 
4958 
4969  function &getNoResultsBodycopy($only_if_enabled=TRUE)
4970  {
4971  $asset = NULL;
4972 
4973  if ($only_if_enabled) {
4974  $link_types = SQ_LINK_TYPE_2;
4975  } else {
4976  $link_types = SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3;
4977  }
4978 
4979  $tmp_bodycopy_link = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, $link_types, 'bodycopy', FALSE, 'major', 'no_results');
4980  $bodycopy_link = reset($tmp_bodycopy_link);
4981 
4982  if ($bodycopy_link) {
4983  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($bodycopy_link['minorid'], 'bodycopy');
4984  }
4985 
4986  return $asset;
4987 
4988  }//end getNoResultsBodycopy()
4989 
4990 
4997  function isNoResultsBodycopyEnabled()
4998  {
4999  $link_types = SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3;
5000 
5001  $bodycopy_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, $link_types, 'bodycopy', FALSE, 'major', 'no_results');
5002  $bodycopy_link = reset($bodycopy_links);
5003 
5004  if ($bodycopy_link) {
5005  return ($bodycopy_link['link_type'] == SQ_LINK_TYPE_2);
5006  } else {
5007  return FALSE;
5008  }
5009 
5010 
5011  }//end isNoResultsBodycopyEnabled()
5012 
5013 
5023  function setUseNoResultsBodycopy($enabled)
5024  {
5025  $tmp_body_link = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->id, SQ_LINK_TYPE_2 | SQ_LINK_TYPE_3, 'bodycopy', FALSE, 'major', 'no_results');
5026  $bodycopy_link = reset($tmp_body_link);
5027 
5028  if (!$bodycopy_link) {
5029  // no bodycopy yet? If we're trying to set to disabled, then we don't
5030  // need to do anything - if not then we need to create it
5031  if ($enabled) {
5032  if (!$this->createNoResultsBodycopy()) return FALSE;
5033  }
5034  } else {
5035  // set link type to either TYPE_2 if enabled or TYPE_3 if disabled
5036  $new_link_type = ($enabled) ? SQ_LINK_TYPE_2 : SQ_LINK_TYPE_3;
5037  if ($bodycopy_link['link_type'] != $new_link_type) {
5038  $GLOBALS['SQ_SYSTEM']->am->updateLink($bodycopy_link['linkid'], $new_link_type);
5039  }
5040  }
5041 
5042  return TRUE;
5043 
5044  }//end setUseNoResultsBodycopy()
5045 
5046 
5047 }//end class
5048 ?>