Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
page_upcoming_events_list.inc
1 <?php
17 require_once SQ_CORE_PACKAGE_PATH.'/page/page.inc';
18 require_once SQ_FUDGE_PATH.'/general/text.inc';
19 require_once SQ_PACKAGES_PATH.'/calendar/lib/calendar_common.inc';
20 
33 {
34 
35 
42  function __construct($assetid=0)
43  {
44  $this->_ser_attrs = TRUE;
45  parent::__construct($assetid);
46  }//end constructor
47 
48 
59  function _createAdditional(&$link)
60  {
61  if (!parent::_createAdditional($link)) return FALSE;
62 
63  $am =& $GLOBALS['SQ_SYSTEM']->am;
64 
65  // create a page contents bodycopy
66  $am->includeAsset('bodycopy');
67  $page_contents_bc = new Bodycopy();
68  $page_contents_bc->setAttrValue('name', 'Page Contents');
69  $link = Array('asset' => &$this, 'value' => 'page_contents', 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1);
70  if (!$page_contents_bc->create($link)) return FALSE;
71 
72  // create a type formats folder
73  $am->includeAsset('folder');
74  $type_formats_folder = new Folder();
75  $type_formats_folder->setAttrValue('name', 'Type Formats');
76  $link = Array('asset' => &$this, 'value' => 'type_formats', 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 1);
77  if (!$type_formats_folder->create($link)) return FALSE;
78 
79  // create type format bodycopies
80  $tf_link = Array('asset' => &$type_formats_folder, 'link_type' => SQ_LINK_TYPE_2, 'is_dependant' => 1, 'is_exclusive' => 0);
81  foreach (Array('calendar_event_single', 'calendar_event_recurring') as $event_type) {
82  $tf_link['value'] = $event_type;
83  $format_bc = new Bodycopy();
84  $format_bc->setAttrValue('name', $am->getTypeInfo($event_type, 'name').' Format');
85  if (!$format_bc->create($tf_link)) return FALSE;
86  $format_bc = NULL;
87  }
88 
89  return TRUE;
90 
91  }//end _createAdditional()
92 
93 
101  function _getAllowedLinks()
102  {
103  $page_links = parent::_getAllowedLinks();
104  $page_links[SQ_LINK_TYPE_2]['bodycopy'] = Array('card' => 1, 'exclusive' => TRUE);
105  return $page_links;
106 
107  }//end _getAllowedLinks()
108 
109 
120  function onRequestKeywords(&$broadcaster, $vars=Array())
121  {
122  if (!isset($vars['keywords'])) {
123  $vars['keywords'] = Array();
124  }
125  $tf_folder = $this->getFolder('type_formats');
126  $broadcaster_parents = $GLOBALS['SQ_SYSTEM']->am->getParents($broadcaster->id);
127  if (isset($broadcaster_parents[$tf_folder->id])) {
128  $tf_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($tf_folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'major');
129  $tf_bcs = Array();
130  foreach ($tf_links as $link) {
131  $tf_bcs[$link['minorid']] = $link['value'];
132  }
133  $bc_id = reset(array_intersect(array_keys($tf_bcs), array_keys($broadcaster_parents)));
134  if ($bc_id) {
135  $GLOBALS['SQ_SYSTEM']->am->includeAsset($tf_bcs[$bc_id]);
136  $dummy_asset = new $tf_bcs[$bc_id];
137  $asset_keywords = $dummy_asset->getAvailableKeywords();
138  foreach ($asset_keywords as $kw => $desc) {
139  $vars['keywords'][$kw] = $desc;
140  }
141  return TRUE;
142  }
143  } else if (isset($broadcaster_parents[$this->id])) {
144  // it's the contents BC
145  $vars['keywords']['event_list'] = 'List of Events';
146  return TRUE;
147  }
148  return FALSE;
149 
150  }//end onRequestKeywords()
151 
152 
159  function printBody()
160  {
161  $cache_key = $this->_getCacheKey();
162  $cached_contents = '';
163 
164  $_REQUEST['SQ_CALENDAR_DATE'] = date('Y-m-d');
165 
166  if (!empty($cache_key)) {
167  $cm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('cache_manager');
168  $cached_contents = $cm->loadFromCache($this->id, $this->type(), $cache_key);
169  }
170 
171  if ($cached_contents !== FALSE) {
172  echo $cached_contents;
173  } else {
174  // No cached version, so carry out the calculations
175  $am =& $GLOBALS['SQ_SYSTEM']->am;
176  $mm = NULL;
177  $page_links = $am->getLinks($this->id, SQ_LINK_TYPE_2, Array('folder', 'bodycopy'), TRUE, 'major', NULL, TRUE);
178  $tf_folder_id = NULL;
179  $pb_bodycopy_id = NULL;
180  foreach ($page_links as $link) {
181  switch ($link['minor_type_code']) {
182  case 'folder':
183  $this->_tmp['tf_folder_id'] = $link['minorid'];
184  break;
185  case 'bodycopy':
186  $pb_bodycopy_id = $link['minorid'];
187  break;
188  }
189  }
190  if (is_null($this->_tmp['tf_folder_id'])) {
191  trigger_localised_error('CAL0059', E_USER_WARNING, $this->id);
192  return;
193  }
194  if (is_null($pb_bodycopy_id)) {
195  trigger_localised_error('CAL0060', E_USER_WARNING, $this->id);
196  return;
197  }
198 
199  ob_start();
200  $pb = $am->getAsset($pb_bodycopy_id, 'bodycopy');
201  $replacements = $this->_getPageContentsReplacements();
202  $calendar_replacements = $replacements;
203  $keywords = $pb->getKeywords();
204  foreach ($keywords as $word) {
205  if (isset($calendar_replacements[$word]) && !empty($calendar_replacements[$word])) {
206  $replacements[$word] = $calendar_replacements[$word];
207  } else {
208  $replacements[$word] = $this->getKeywordReplacement($word);
209  }
210  }
211  $pb->setKeywordReplacements($replacements);
212  $pb->printBody();
213 
214  if (!empty($cache_key)) {
215  $cm->saveToCache($this->id, $this->type(), $cache_key, ob_get_contents());
216  }
217  ob_end_flush();
218  }//end else
219 
220  }//end printBody()
221 
222 
237  function getKeywordReplacement($keyword)
238  {
239  $prefix = $this->getPrefix();
240 
241  // No keywords here, just go and get the global keywords
242  return parent::getKeywordReplacement($keyword);
243 
244  }//end getKeywordReplacement()
245 
246 
253  function _getCacheKey()
254  {
255  $cache_key = '';
256  $contextid = $GLOBALS['SQ_SYSTEM']->getContextId();
257 
258  $num_events = $this->attr('num_events');
259  $horizon = $this->attr('horizon');
260 
261  // All attributes will have some contents of no need to check for empty
262  $cache_key .= $num_events; // First entry so no colon out front like a little soldier
263  $cache_key .= ':'.$horizon;
264 
265  $cache_key .= '-ctx'.$contextid;
266 
267  return $cache_key;
268 
269  }//end _getCacheKey()
270 
271 
279  {
280  $am =& $GLOBALS['SQ_SYSTEM']->am;
281  $type_format_links = $am->getLinks($this->_tmp['tf_folder_id'], SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'major', NULL, TRUE);
282  $type_formats = Array();
283  $needs_asset = Array();
284  $somebody_doesnt_need_asset = FALSE;
285  $metadata_keywords = Array();
286  $asset_info_fields = Array(
287  'assetid',
288  'type_code',
289  'version',
290  'name',
291  'short_name',
292  'status',
293  'languages',
294  'charset',
295  'force_secure',
296  'created',
297  'created_userid',
298  'updated',
299  'updated_userid',
300  'published',
301  'published_userid',
302  );
303  foreach ($type_format_links as $link) {
304  if (!empty($link['value'])) {
305  $needs_asset[$link['value']] = FALSE;
306  $metadata_keywords[$link['value']] = Array();
307  $bc = $GLOBALS['SQ_SYSTEM']->am->getAsset($link['minorid']);
308  $type_formats[$link['value']] = $bc->getRawBodycopyContent();
309  $required_keywords[$link['value']] = $bc->getKeywords();
310  foreach ($required_keywords[$link['value']] as $keyword) {
311  if (strpos($keyword, 'asset_') !== 0) {
312  $needs_asset[$link['value']] = TRUE;
313  } else {
314  // the keyword starts with 'asset_' but it might still require the asset
315  if ((strpos($keyword, 'asset_metadata_') !== 0) && (!in_array(substr($keyword, 6), $asset_info_fields))) {
316  $needs_asset[$link['value']] = TRUE;
317  }
318  }
319  if (strpos($keyword, 'asset_metadata_') === 0) {
320  $metadata_keywords[$link['value']][] = $keyword;
321  }
322  }
323  if (!$needs_asset[$link['value']]) {
324  $somebody_doesnt_need_asset = TRUE;
325  }
326  }
327  }
328 
329  $events = $this->_getEvents();
330 
331  // Process multi-date events
332  // Obtain the bodycopy types we have customised
333  $tf_folder = $this->getFolder('type_formats');
334  $tf_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($tf_folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE, 'major');
335  $tf_bcs = Array();
336  $types_available = Array();
337  foreach ($tf_links as $link) {
338  $type = $link['value'];
339  $tf_bcs[] = $type;
340  $parent_types = $GLOBALS['SQ_SYSTEM']->am->getTypeAncestors($type, FALSE);
341  foreach ($parent_types as $parent_type) {
342  $types_available[$parent_type][] = $type;
343  }
344  }
345 
346  $multi_date_events = Array();
347  $multi_date_types = Array('calendar_event_multi_date');
348  if (isset($types_available['calendar_event_multi_date'])) {
349  foreach ($types_available['calendar_event_multi_date'] as $multi_date_type) {
350  $multi_date_types[] = $multi_date_type;
351  }
352  }
353 
354  foreach ($events as $event) {
355  $event_type_code = $event['type_code'];
356  if (($event_type_code == 'calendar_event_single') || ($event_type_code == 'calendar_event_recurring')) {
357  $event_parents = $GLOBALS['SQ_SYSTEM']->am->getParents($event['assetid'], $multi_date_types);
358  foreach ($event_parents as $parent_id => $event_parent) {
359  if (!isset($multi_date_events[$parent_id])) {
360  $multi_date_events[$parent_id] = 1;
361  }
362  }
363  }
364  }
365 
366  $event_list = '';
367  $events_data = Array();
368  $mm = NULL;
369 
370  // Print one instance of each Multi-Date Event
371  foreach ($multi_date_events as $multi_date_event_id => $value_set) {
372  $asset = $am->getAsset($multi_date_event_id);
373 
374  $type_code = $asset->type();
375  $format_type = $asset->type();
376 
377  if (isset($required_keywords[$format_type])) {
378  foreach ($required_keywords[$format_type] as $keyword) {
379  if ($keyword == 'asset_contents') {
380  ob_start();
381  $asset->printBody();
382  $replacements[$keyword] = ob_get_contents();
383  ob_end_clean();
384  } else {
385  $replacements[$keyword] = $asset->getKeywordReplacement($keyword);
386  }
387  }
388  }
389 
390  // get metadata keywords
391  if (!empty($metadata_keywords[$type_code])) {
392  if (!empty($metadata_keywords)) {
393  if (is_null($mm)) {
394  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
395  }
396  // get all the metadata keywords for this asset
397  $metadata_values = $mm->getMetadataFieldValues($id, $metadata_keywords[$type_code]);
398 
399  foreach ($metadata_values as $field => $value) {
400  $replacements['asset_metadata_'.$field] = $value;
401  }
402  }
403  }
404 
405  if (isset($required_keywords[$format_type])) {
406  $entry = $type_formats[$format_type];
407  }
408  replace_keywords($entry, $replacements);
409  $event_list .= $entry;
410 
411  }//end foreach
412  // End multi-date event processing
413 
414 
415  if ($somebody_doesnt_need_asset) {
416  $keys = Array();
417  foreach ($events as $key => $value) {
418  $key_assetid = strtok($key, ':');
419  $keys[$key_assetid] = $key_assetid;
420  }
421  $asset_infos = $am->getAssetInfo($keys);
422  }
423 
424  $recurring_event_descendants = array_merge($am->getTypeDescendants('calendar_event_recurring', TRUE), Array('calendar_event_modification'));
425 
426  foreach($events as $id => $event) {
427  $events[$id]['current_date'] = $_REQUEST['SQ_CALENDAR_DATE'];
428  }
429 
430  foreach ($events as $id => $event) {
431  $shadow_id = $id;
432  $id = strtok($id, ':');
433  $replacements = Array();
434 
435  // Use the Display Format of the asset type if it has been defined, otherwise use the
436  // Display Format of the parent of the asset type. This means that custom Calendar Event
437  // assets will be displayed using their own format if one is defined
438  if (in_array($event['type_code'], $types_available)) {
439  $format_type = $event_type_code;
440  } else {
441  $format_type = 'calendar_event_single';
442 
443  if (in_array($event['type_code'], $recurring_event_descendants)) {
444  $format_type = 'calendar_event_recurring';
445  }
446  }
447 
448  $asset = $am->getAsset($id);
449  $event_occurance = $event['start_date_ts'];
450  if ($asset && $asset->type() == 'calendar_event_recurring') {
451 
452  $current_date = $events[$shadow_id]['current_date'];
453 
454  // If event recurs at current_date move to next day for correct evaluation of next recurrance date
455  if ($asset->hasOccurrenceOnDate($current_date)) {
456  $current_date = add_days_to_iso($current_date, 1);
457  }
458  $next_occurance = $asset->getFirstOccurrenceAfter($current_date);
459  if ($next_occurance === FALSE) continue; //we exceeded the stop date
460 
461  foreach($events as $temp_id => $temp_event) {
462  $original_id = strtok($temp_id, ':');
463  if ($original_id == $id) {
464  $events[$temp_id]['current_date'] = $next_occurance;
465  $_REQUEST['SQ_CALENDAR_DATE'] = $next_occurance;
466  }
467  }
468  $event_occurance = strtotime($next_occurance);
469 
470  } else if ($asset && $asset->type() == 'calendar_event_modification') {
471 
472  $pattern = '/( [0-9\-]{2}:[0-9\-]{2}:[0-9\-]{2})/';
473 
474  $occurance_date = preg_replace($pattern, '', $asset->attr('start_date'));
475  // for the modification event we ned to find what recurring
476  // event we are modifying and update that events instances
477  $link = $GLOBALS['SQ_SYSTEM']->am->getParents($id, 'calendar_event_recurring', NULL, NULL, TRUE, 1, 1);
478 
479  foreach($events as $temp_id => $temp_event) {
480  $original_id = strtok($temp_id, ':');
481  if (array_key_exists($original_id, $link)) {
482  $events[$temp_id]['current_date'] = $occurance_date;
483  $recuring_event_last_occurance_date = $_REQUEST['SQ_CALENDAR_DATE'];
484  $_REQUEST['SQ_CALENDAR_DATE'] = $occurance_date;
485  }
486  }
487  $event_occurance = strtotime($occurance_date);
488  }
489 
490  // get asset keywords
491  if ($asset && $needs_asset[$format_type] ) {
492  foreach ($required_keywords[$format_type] as $keyword) {
493  if ($keyword == 'asset_contents') {
494  ob_start();
495  $asset->printBody();
496  $replacements[$keyword] = ob_get_contents();
497  ob_end_clean();
498  } else {
499  $replacements[$keyword] = $asset->getKeywordReplacement($keyword);
500  }
501  }
502  } else {
503  // we can do this without the asset
504  foreach ($asset_infos[$id] as $field => $value) {
505  $replacements['asset_'.$field] = $value;
506  }
507  $replacements['asset_assetid'] = $id;
508  }
509 
510 
511  // get metadata keywords
512  if (!empty($metadata_keywords[$event['type_code']])) {
513  if (!empty($metadata_keywords)) {
514  if (is_null($mm)) {
515  $mm = $GLOBALS['SQ_SYSTEM']->getMetadataManager();
516  }
517  // get all the metadata keywords for this asset
518  $metadata_values = $mm->getMetadataFieldValues($id, $metadata_keywords[$event['type_code']]);
519 
520  foreach ($metadata_values as $field => $value) {
521  $replacements['asset_metadata_'.$field] = $value;
522  }
523  }
524  }
525 
526  $entry = $type_formats[$format_type];
527  replace_keywords($entry, $replacements);
528  $events_data[] = Array('data' => $entry, 'start_date_ts' => $event_occurance);
529 
530  if ($asset && $asset->type() == 'calendar_event_modification')
531  $_REQUEST['SQ_CALENDAR_DATE'] = $recuring_event_last_occurance_date;
532 
533  }//end foreach events
534 
535  // Sort the resulting events based on their instance start time
536  uasort($events_data, Array('Calendar_Common', 'compareStartDates'));
537  foreach($events_data as $event_data) {
538  $event_list .= $event_data['data'];
539  }
540 
541  if (isset($_REQUEST['SQ_CALENDAR_DATE'])) {
542  unset($_REQUEST['SQ_CALENDAR_DATE']);
543  }
544 
545  return Array('event_list' => $event_list);
546 
547  }//end _getPageContentsReplacements()
548 
549 
557  {
558  $parameter_map = $this->getAttribute('parameter_map');
559  $raw_dynamic_root_nodes = $parameter_map->getParameterValue('replacement_root_node');
560  if (!empty($raw_dynamic_root_nodes)) {
561  $dynamic_root_nodes = Array();
562  // note that dynamic roots can be supplied as an array of asset ids or
563  // as a comma delimited string of asset ids
564  if (!is_array($raw_dynamic_root_nodes)) {
565  $raw_dynamic_root_nodes = explode(',', $raw_dynamic_root_nodes);
566  }
567 
568  $original_root_ids = array_keys($this->attr('root_nodes'));
569  foreach ($raw_dynamic_root_nodes as $dynamic_root) {
570  if (!strlen($dynamic_root)) continue;
571  // Check to see if the dynamic parameter specified is a child of one of the root
572  // nodes. If so, use the dynamic parameter as the root node; otherwise return an error.
573  $dynamic_parents = $GLOBALS['SQ_SYSTEM']->am->getParents($dynamic_root);
574  $matching_root_ids = array_intersect(array_keys($dynamic_parents), $original_root_ids);
575  if (empty($matching_root_ids)) {
576  trigger_localised_error('CAL0067', E_USER_WARNING, $dynamic_root, $this->id);
577  } else {
578  $dynamic_root_nodes[$dynamic_root] = 1;
579  }
580  }
581 
582  if (!empty($dynamic_root_nodes)) {
583  return $dynamic_root_nodes;
584  }
585 
586  }//end if
587  return Array();
588 
589  }//end _getEventRootNodeIds()
590 
591 
599  function _getEvents()
600  {
601  $db = MatrixDAL::getDb();
602  // build sql to restrict the tree locations
603  $root_nodes = $this->_getEventRootNodeIds();
604  if (empty($root_nodes)) {
605  $root_nodes = $this->attr('root_nodes');
606  }
607  if (empty($root_nodes)) {
608  trigger_localised_error('CAL0028', E_USER_WARNING);
609  return Array();
610  }
611 
612  $start_ts = time();
613  $horizon_ts = strtotime('+'.$this->attr('horizon').'days');
614  $all_events = Array();
615 
616  while ((count($all_events) < $this->attr('num_events')) && ($start_ts < $horizon_ts)) {
617 
618  $end_ts = strtotime('+'.$this->attr('horizon').' days', $start_ts);
619  if ($end_ts > $horizon_ts) break;
620  $single_result = Array();
621  $recur_result = Array();
622 
623  // get the single events
624  $bind_vars = Array();
625  $date_sql = '(cd.start_date_ts BETWEEN :start_date_ts AND :end_date_ts) OR (cd.start_date_ts < :start_date_ts_1 AND cd.end_date_ts >= :start_date_ts_2)';
626  $sql = Calendar_Common::getSingleEventQueryBase(array_keys($root_nodes), 'calendar_event_single', TRUE, $bind_vars).' AND ('.$date_sql.')';
627 
628  $single_result = Array();
629  try {
630  $query = MatrixDAL::preparePdoQuery($sql);
631  foreach($bind_vars as $bind_value => $bind_var) {
632  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
633  }
634  MatrixDAL::bindValueToPdo($query, 'start_date_ts', $start_ts, PDO::PARAM_INT);
635  MatrixDAL::bindValueToPdo($query, 'start_date_ts_1', $start_ts, PDO::PARAM_INT);
636  MatrixDAL::bindValueToPdo($query, 'start_date_ts_2', $start_ts, PDO::PARAM_INT);
637  MatrixDAL::bindValueToPdo($query, 'end_date_ts', $end_ts, PDO::PARAM_INT);
638  $single_result = MatrixDAL::executePdoAll($query);
639  } catch (Exception $e) {
640  throw new Exception($e->getMessage());
641  }//end
642  $single_result = Calendar_Common::condenseResultTreeids($single_result);
643 
644  // get the recurring events
645  $bind_vars = Array();
646  $sql = Calendar_Common::getRecurringEventQueryBase(array_keys($root_nodes), 'period', date('Y-m-d'), $this->attr('horizon'), 'calendar_event_recurring', $bind_vars);
647 
648  $recur_result = Array();
649  try {
650  $query = MatrixDAL::preparePdoQuery($sql);
651  foreach($bind_vars as $bind_value => $bind_var) {
652  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
653  }
654  $recur_result = MatrixDAL::executePdoAll($query);
655  } catch (Exception $e) {
656  throw new Exception($e->getMessage());
657  }//end
658  $recur_result = Calendar_Common::condenseResultTreeids($recur_result);
659 
660  foreach ($single_result as $event_id => $single_event) {
661  // don't expand the single events but the event
662  // cancellations and modification needs to be expanded
663  if ($single_event['type_code'] == 'calendar_event_single') continue;
664  $recur_result[$event_id] = $single_event;
665  unset($single_result[$event_id]);
666  }
667  $recur_events = $recur_result;
668  $recur_result = Calendar_Common::expandEventList($recur_result, date('Y-m-d', $start_ts), date('Y-m-d', $end_ts));
669 
670  // If the recurring event's frequency is less than global frequency threshold set,
671  // limit the recurring event instances to the first instance only
672  $recur_result = Calendar_Common::limitRecurringEventInstances($recur_events, $recur_result);
673 
674  $new_events = $single_result + $recur_result;
676 
677  $all_events += $new_events;
678 
679  $start_ts = $end_ts - 1; // for next loop if necessary
680 
681  }
682 
683  uasort($all_events, Array('Calendar_Common', 'compareStartDates'));
684  $res = Array();
685  $i = 0;
686  foreach ($all_events as $id => $event) {
687  if ($i >= $this->attr('num_events')) break;
688  $res[$id] = $event;
689  $i++;
690  }
691  return $res;
692 
693  }//end _getEvents()
694 
695 
706  function getFormats($type='type_formats')
707  {
708  if (!isset($this->_tmp['formats'][$type])) {
709  $folder = $this->getFolder($type);
710  $format_links = $GLOBALS['SQ_SYSTEM']->am->getLinks($folder->id, SQ_LINK_TYPE_2, 'bodycopy', TRUE);
711  if (empty($format_links)) return Array();
712 
713  $formats = Array();
714  foreach ($format_links as $link_data) {
715  $formats[$link_data['minorid']] = $link_data['value'];
716  }
717  $this->_tmp['formats'][$type] = $formats;
718  }
719  return $this->_tmp['formats'][$type];
720 
721  }//end getFormats()
722 
723 
732  function &getFolder($type='type_formats')
733  {
734  $null = NULL;
735  $link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_TYPE_2, 'folder', TRUE, $type);
736  if (empty($link)) return $null;
737 
738  $folder = $GLOBALS['SQ_SYSTEM']->am->getAsset($link['minorid'], $link['minor_type_code']);
739  if (is_null($folder)) return $null;
740 
741  return $folder;
742 
743  }//end getFolder()
744 
745 
746 }//end class
747 ?>