BxEventsModule.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Events Events
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. /**
  12. * Events profiles module.
  13. */
  14. class BxEventsModule extends BxBaseModGroupsModule implements iBxDolCalendarService
  15. {
  16. function __construct(&$aModule)
  17. {
  18. parent::__construct($aModule);
  19. $CNF = &$this->_oConfig->CNF;
  20. $this->_aSearchableNamesExcept = array_merge($this->_aSearchableNamesExcept, array(
  21. $CNF['FIELD_TIMEZONE'],
  22. $CNF['FIELD_JOIN_CONFIRMATION'],
  23. $CNF['FIELD_REMINDER']
  24. ));
  25. }
  26. public function actionCalendarData()
  27. {
  28. // check permissions
  29. $aSQLPart = array();
  30. $iContentId = (int)bx_get('event');
  31. $iContextId = (int)bx_get('context_id');
  32. if ($iContentId) {
  33. $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
  34. if (CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aContentInfo)) {
  35. $this->_oTemplate->displayAccessDenied();
  36. exit;
  37. }
  38. $aSQLPart = array();
  39. }
  40. else {
  41. $oPrivacy = BxDolPrivacy::getObjectInstance($this->_oConfig->CNF['OBJECT_PRIVACY_VIEW']);
  42. if($iContextId){
  43. if (!$this->serviceIsEnableForContext($iContextId)){
  44. exit;
  45. }
  46. else{
  47. $aSQLPart = $oPrivacy ? $oPrivacy->getContentByGroupAsSQLPart(- $iContextId) : array();
  48. }
  49. }
  50. else{
  51. $aSQLPart = $oPrivacy ? $oPrivacy->getContentPublicAsSQLPart(0, $oPrivacy->getPartiallyVisiblePrivacyGroups()) : array();
  52. }
  53. }
  54. // get entries
  55. $aEntries = $this->_oDb->getEntriesByDate(bx_get('start'), bx_get('end'), bx_get('event'), $aSQLPart);
  56. bx_alert($this->getName(), 'calendar_data', 0, false, array(
  57. 'event' => $iContentId,
  58. 'context_id' => $iContextId,
  59. 'start' => bx_get('start'),
  60. 'end' => bx_get('end'),
  61. 'sql_part' => &$aSQLPart,
  62. 'data' => &$aEntries,
  63. ));
  64. header('Content-Type: application/json; charset=utf-8');
  65. echo json_encode($aEntries);
  66. }
  67. public function serviceIsIcalExportAvaliable($iContentId)
  68. {
  69. return true;
  70. $aContentInfo = $iContentId ? $this->_oDb->getContentInfoById($iContentId) : false;
  71. if ($aContentInfo)
  72. return false;
  73. return $aContentInfo['date_start'] && $aContentInfo['date_end'];
  74. }
  75. public function serviceGetCalendarEntries($iProfileId)
  76. {
  77. $CNF = &$this->_oConfig->CNF;
  78. $oConn = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
  79. $aData = $oConn->getConnectedContent($iProfileId);
  80. $aData2 = array(0);
  81. foreach($aData as $iProfileId2){
  82. $oProfile = BxDolProfile::getInstance($iProfileId2);
  83. array_push($aData2, $oProfile->getContentId());
  84. }
  85. $aSQLPart['where'] = " AND `bx_events_data`.`id` IN(" . implode(',', $aData2) . ")";
  86. return $this->_oDb->getEntriesByDate(bx_get('start'), bx_get('end'), null, $aSQLPart);
  87. }
  88. public function serviceGetSafeServices()
  89. {
  90. $a = parent::serviceGetSafeServices();
  91. return array_merge($a, array (
  92. 'BrowsePastProfiles' => '',
  93. 'Calendar' => '',
  94. ));
  95. }
  96. /**
  97. * @page service Service Calls
  98. * @section bx_events Events
  99. * @subsection bx_events-browse Browse
  100. * @subsubsection bx_events-browse_upcoming_profiles browse_upcoming_profiles
  101. *
  102. * @code bx_srv('bx_events', 'browse_upcoming_profiles', [...]); @endcode
  103. *
  104. * Browse upcoming events
  105. *
  106. * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
  107. * @param $bAjaxPaginate use AJAX paginate or not
  108. *
  109. * @see BxEventsModule::serviceBrowseUpcomingProfiles
  110. */
  111. /**
  112. * @ref bx_events-browse_upcoming_profiles "browse_upcoming_profiles"
  113. */
  114. public function serviceBrowseUpcomingProfiles ($aParams = false)
  115. {
  116. $bDisplayEmptyMsg = false;
  117. if(isset($aParams['empty_message'])) {
  118. $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
  119. unset($aParams['empty_message']);
  120. }
  121. $bAjaxPaginate = true;
  122. if(isset($aParams['ajax_paginate'])) {
  123. $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
  124. unset($aParams['ajax_paginate']);
  125. }
  126. return $this->_serviceBrowse ('upcoming', $aParams, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  127. }
  128. /**
  129. * @page service Service Calls
  130. * @section bx_events Events
  131. * @subsection bx_events-browse Browse
  132. * @subsubsection bx_events-browse_upcoming_connected_profiles browse_upcoming_connected_profiles
  133. *
  134. * @code bx_srv('bx_events', 'browse_upcoming_connected_profiles', [...]); @endcode
  135. *
  136. * Browse upcoming connected (followed) events
  137. *
  138. * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
  139. * @param $bAjaxPaginate use AJAX paginate or not
  140. *
  141. * @see BxEventsModule::serviceBrowseUpcomingConnectedProfiles
  142. */
  143. /**
  144. * @ref bx_events-browse_upcoming_connected_profiles "browse_upcoming_connected_profiles"
  145. */
  146. public function serviceBrowseUpcomingConnectedProfiles ($aParams = false)
  147. {
  148. if(!is_array($aParams) || empty($aParams['object']) || empty($aParams['profile']))
  149. return '';
  150. $bDisplayEmptyMsg = false;
  151. if(isset($aParams['empty_message'])) {
  152. $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
  153. unset($aParams['empty_message']);
  154. }
  155. $bAjaxPaginate = true;
  156. if(isset($aParams['ajax_paginate'])) {
  157. $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
  158. unset($aParams['ajax_paginate']);
  159. }
  160. $aDefaults = array(
  161. 'type' => 'content',
  162. 'mutual' => false,
  163. 'profile2' => 0
  164. );
  165. return $this->_serviceBrowse ('upcoming_connected', array_merge($aDefaults, $aParams), BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  166. }
  167. /**
  168. * @page service Service Calls
  169. * @section bx_events Events
  170. * @subsection bx_events-browse Browse
  171. * @subsubsection bx_events-browse_past_profiles browse_past_profiles
  172. *
  173. * @code bx_srv('bx_events', 'browse_past_profiles', [...]); @endcode
  174. *
  175. * Browse past events
  176. *
  177. * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
  178. * @param $bAjaxPaginate use AJAX paginate or not
  179. *
  180. * @see BxEventsModule::serviceBrowsePastProfiles
  181. */
  182. /**
  183. * @ref bx_events-browse_past_profiles "browse_past_profiles"
  184. */
  185. public function serviceBrowsePastProfiles ($bDisplayEmptyMsg = false, $bAjaxPaginate = true)
  186. {
  187. return $this->_serviceBrowse ('past', false, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  188. }
  189. /**
  190. * @page service Service Calls
  191. * @section bx_events Events
  192. * @subsection bx_events-browse Browse
  193. * @subsubsection bx_events-browse_past_profiles browse_past_profiles_by_params
  194. *
  195. * @code bx_srv('bx_events', 'browse_past_profiles_by_params', [...]); @endcode
  196. *
  197. * Browse past events
  198. *
  199. * @param $aParams array with all parameters
  200. *
  201. * @see BxEventsModule::serviceBrowsePastProfilesByParams
  202. */
  203. /**
  204. * @ref bx_events-browse_past_profiles_by_params "browse_past_profiles_by_params"
  205. */
  206. public function serviceBrowsePastProfilesByParams ($aParams)
  207. {
  208. $bDisplayEmptyMsg = false;
  209. if(isset($aParams['empty_message'])) {
  210. $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
  211. unset($aParams['empty_message']);
  212. }
  213. $bAjaxPaginate = true;
  214. if(isset($aParams['ajax_paginate'])) {
  215. $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
  216. unset($aParams['ajax_paginate']);
  217. }
  218. return $this->_serviceBrowse ('past', $aParams, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  219. }
  220. /**
  221. * @page service Service Calls
  222. * @section bx_events Events
  223. * @subsection bx_events-page_blocks Page Blocks
  224. * @subsubsection bx_events-calendar calendar
  225. *
  226. * @code bx_srv('bx_events', 'calendar', [...]); @endcode
  227. *
  228. * Shows event or events calendar
  229. *
  230. * @param $aData additional data to point which events to show, leave empty to show all events, specify event's ID in 'event' array key to show calendar for one event only, specify context's ID in 'context_id' array key to show calendar for one context events only. If only one event is specified then it will show calendar only if it's repeating event.
  231. * @param $sTemplate template to use to show calendar, or leave empty for default template, possible options: calendar.html, calendar_compact.html
  232. * @return HTML string with calendar to display on the site, all necessary CSS and JS files are automatically added to the HEAD section of the site HTML. On error empty string is returned.
  233. *
  234. * @see BxEventsModule::serviceCalendar
  235. */
  236. /**
  237. * @ref bx_events-calendar "calendar"
  238. */
  239. public function serviceCalendar($aData = array(), $sTemplate = 'calendar.html')
  240. {
  241. if(isset($aData['context_id']) && !$this->serviceIsEnableForContext($aData['context_id']))
  242. return '';
  243. $oCalendar = new BxTemplCalendar([
  244. 'eventSources' => [
  245. bx_append_url_params(BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'calendar_data', $aData),
  246. ],
  247. ], $this->_oTemplate);
  248. $this->_oTemplate->addCss(['main.css']);
  249. return $oCalendar->display($sTemplate);
  250. }
  251. /**
  252. * @page service Service Calls
  253. * @section bx_events Events
  254. * @subsection bx_events-other Other
  255. * @subsubsection bx_events-process_reminders process_reminders
  256. *
  257. * @code bx_srv('bx_events', 'process_reminders'); @endcode
  258. *
  259. * Send remiders to event's participants.
  260. *
  261. * It must be processed once every hour for the proper processing.
  262. * @return nothing
  263. *
  264. * @see BxEventsModule::serviceProcessReminders
  265. */
  266. /**
  267. * @ref bx_events-process_reminders "process_reminders"
  268. */
  269. public function serviceProcessReminders()
  270. {
  271. $CNF = &$this->_oConfig->CNF;
  272. $iNow = time();
  273. $bUseIn = $this->_oConfig->isInternalNotifications();
  274. $sModule = $this->getName();
  275. // get all events for today and tomorrow, since the max reminder is 24 hours
  276. $aEntries = $this->_oDb->getEntriesByDate('@' . time(), '@' . (time() + 3600 * getParam('bx_events_reminder_interval')));
  277. foreach($aEntries as $aEntry) {
  278. $aReminders = $bUseIn ? [$aEntry['reminder']] : [24, 1];
  279. if(!$aReminders)
  280. continue;
  281. $oEventProfile = BxDolProfile::getInstanceByContentAndType($aEntry[$CNF['FIELD_ID']], $sModule);
  282. if(!$oEventProfile)
  283. continue;
  284. $iEventProfileId = $oEventProfile->id();
  285. foreach($aReminders as $iReminder) {
  286. $iTimestamp = $aEntry['start_utc'] - (3600 * $iReminder);
  287. if($iNow <= ($iTimestamp - 3600) || $iNow >= $iTimestamp)
  288. continue;
  289. if($bUseIn)
  290. $this->sendReminders($aEntry);
  291. bx_alert($sModule, 'reminder', $aEntry[$CNF['FIELD_ID']], $iEventProfileId, array(
  292. 'object_author_id' => $iEventProfileId,
  293. 'reminder' => $iReminder
  294. ));
  295. }
  296. }
  297. }
  298. public function serviceGetNotificationsData()
  299. {
  300. $CNF = &$this->_oConfig->CNF;
  301. $aResults = parent::serviceGetNotificationsData();
  302. $sModule = $this->_aModule['name'];
  303. $aSettingsTypes = ['follow_context'];
  304. $aResults['handlers'] = array_merge($aResults['handlers'], [
  305. ['group' => $sModule . '_reminder', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'reminder', 'module_name' => $sModule, 'module_method' => 'get_notifications_reminder', 'module_class' => 'Module', 'module_event_privacy' => $CNF['OBJECT_PRIVACY_VIEW_NOTIFICATION_EVENT']],
  306. ]);
  307. $aResults['settings'] = array_merge($aResults['settings'], [
  308. ['group' => 'reminder', 'unit' => $sModule, 'action' => 'reminder', 'types' => $aSettingsTypes],
  309. ]);
  310. $aResults['alerts'] = array_merge($aResults['alerts'], [
  311. ['unit' => $sModule, 'action' => 'reminder'],
  312. ]);
  313. return $aResults;
  314. }
  315. public function serviceGetNotificationsInsertData($oAlert, $aHandler, $aDataItems)
  316. {
  317. if($oAlert->sAction != 'reminder' || empty($aDataItems) || !is_array($aDataItems) || empty($oAlert->aExtras['reminder']))
  318. return $aDataItems;
  319. foreach($aDataItems as $iIndex => $aDataItem) {
  320. $aContent = [];
  321. if(!empty($aDataItem['content']))
  322. $aContent = unserialize($aDataItem['content']);
  323. $aContent['reminder'] = (int)$oAlert->aExtras['reminder'];
  324. $aDataItems[$iIndex]['content'] = serialize($aContent);
  325. }
  326. return $aDataItems;
  327. }
  328. public function serviceGetNotificationsReminder($aEvent)
  329. {
  330. $CNF = &$this->_oConfig->CNF;
  331. if(!isset($aEvent['content']['reminder']))
  332. return [];
  333. $iContentId = (int)$aEvent['object_id'];
  334. $oEventProfile = BxDolProfile::getInstanceByContentAndType((int)$iContentId, $this->getName());
  335. if(!$oEventProfile)
  336. return [];
  337. $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
  338. if(empty($aContentInfo) || !is_array($aContentInfo))
  339. return [];
  340. return [
  341. 'entry_sample' => $CNF['T']['txt_sample_single'],
  342. 'entry_url' => str_replace(BX_DOL_URL_ROOT, '{bx_url_root}', $oEventProfile->getUrl()),
  343. 'entry_caption' => $oEventProfile->getDisplayName(),
  344. 'entry_author' => $oEventProfile->id(),
  345. 'lang_key' => '_bx_events_txt_ntfs_reminder_' . $aEvent['content']['reminder']
  346. ];
  347. }
  348. /**
  349. * @page service Service Calls
  350. * @section bx_events Events
  351. * @subsection bx_events-internal Internal
  352. * @subsubsection bx_events-get_timeline_post get_timeline_post
  353. *
  354. * @code bx_srv('bx_events', 'get_timeline_post', [...]); @endcode
  355. *
  356. * Get Timeline post. It's needed for Timeline module.
  357. *
  358. * @param $aEvent timeline event array from Timeline module
  359. * @return array in special format which is needed specifically for Timeline module to display the data.
  360. *
  361. * @see BxEventsModule::serviceGetTimelinePost
  362. */
  363. /**
  364. * @ref bx_events-get_timeline_post "get_timeline_post"
  365. */
  366. public function serviceGetTimelinePost($aEvent, $aBrowseParams = array())
  367. {
  368. $a = parent::serviceGetTimelinePost($aEvent, $aBrowseParams);
  369. if($a === false)
  370. return false;
  371. $aContentInfo = $this->_oDb->getContentInfoById($aEvent['object_id']);
  372. if(empty($aContentInfo) || !is_array($aContentInfo))
  373. return $a;
  374. $CNF = $this->_oConfig->CNF;
  375. $oDateStart = date_create('@' . $aContentInfo['date_start']);
  376. if ($oDateStart)
  377. $oDateStart->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
  378. $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
  379. $sLocationString = $oMetatags ? $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false) : false;
  380. $a['content']['raw'] = $this->_oTemplate->parseHtmlByName('timeline_post.html', [
  381. 'title' => $a['content']['title'],
  382. 'title_attr' => bx_html_attribute($a['content']['title']),
  383. 'url' => $a['content']['url'],
  384. 'bx_if:date' => [
  385. 'condition' => $oDateStart,
  386. 'content' => [
  387. 'date' => $oDateStart ? bx_time_js($aContentInfo['date_start'], BX_FORMAT_DATE_TIME) : '',
  388. 'date_c' => $oDateStart->format('c'),
  389. ]],
  390. 'bx_if:location' => array(
  391. 'condition' => !!$sLocationString,
  392. 'content' => array('location' => $sLocationString),
  393. ),
  394. ]);
  395. $a['content']['title'] = '';
  396. $a['content']['text'] = '';
  397. return $a;
  398. }
  399. public function actionIntervals()
  400. {
  401. $sAction = bx_get('a');
  402. $sMethodName = 'subaction' . ucfirst($sAction);
  403. if (!method_exists($this, $sMethodName)) {
  404. $this->_oTemplate->pageNotFound();
  405. return;
  406. }
  407. $this->$sMethodName();
  408. }
  409. public function subactionRestore($iContentId = 0)
  410. {
  411. if (!$iContentId)
  412. $iContentId = (int)bx_get('c');
  413. $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
  414. if (
  415. ($aContentInfo && CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedEdit ($aContentInfo))
  416. ||
  417. (!$aContentInfo && CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedAdd ())
  418. ) {
  419. $this->_oTemplate->displayAccessDenied();
  420. exit;
  421. }
  422. $a = $this->_oDb->getIntervals($iContentId);
  423. foreach ($a as $iIntervalId => $r) {
  424. $a[$iIntervalId]['file_id'] = $r['interval_id'];
  425. $a[$iIntervalId]['repeat_stop'] = bx_process_output ($r['repeat_stop'], BX_DATA_DATE_TS);
  426. }
  427. if ('json' == bx_get('f')) {
  428. header('Content-type: text/html; charset=utf-8');
  429. echo json_encode($a);
  430. }
  431. }
  432. public function subactionDelete()
  433. {
  434. header('Content-type: text/html; charset=utf-8');
  435. $iIntervalId = (int)bx_get('id');
  436. if (!($aContentInfo = $this->_oDb->getContentInfoByIntervalId($iIntervalId))) {
  437. echo _t('_sys_request_page_not_found_cpt');
  438. }
  439. elseif (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedEdit ($aContentInfo))) {
  440. echo $sMsg;
  441. }
  442. elseif (!$this->_oDb->deleteIntervalById($iIntervalId)) {
  443. echo _t('_sys_txt_error_occured');
  444. }
  445. else {
  446. echo 'ok';
  447. }
  448. }
  449. public function checkAllowedIcalExport ($aDataEntry, $isPerformAction = false)
  450. {
  451. return $aDataEntry['date_start'] && $aDataEntry['date_end'] ? CHECK_ACTION_RESULT_ALLOWED : _t('_sys_txt_access_denied');
  452. }
  453. /**
  454. * Send reminder to event's paritcipants
  455. * @param $aContentInfo content info
  456. * @return number of sent reminders
  457. */
  458. protected function sendReminders($aContentInfo)
  459. {
  460. $CNF = $this->_oConfig->CNF;
  461. $oConnection = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
  462. if (!$oConnection)
  463. return false;
  464. $oGroupProfile = BxDolProfile::getInstanceByContentAndType($aContentInfo[$CNF['FIELD_ID']], $this->getName());
  465. if (!$oGroupProfile)
  466. return false;
  467. $aParticipants = $oConnection->getConnectedInitiators($oGroupProfile->id(), true);
  468. if (!$aParticipants)
  469. return true;
  470. $oDateBegin = new DateTime();
  471. $oDateBegin->setTimestamp($aContentInfo['start_utc']);
  472. $oDateBegin->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
  473. $sEntryBegin = $oDateBegin->format('r');
  474. $sEntryBeginShort = date(getParam('bx_events_short_date_format'), $oDateBegin->getTimestamp());
  475. $oDateBegin->setTimezone(new DateTimeZone('UTC'));
  476. $sEntryBeginUTC = $oDateBegin->format('c');
  477. $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
  478. $sLocationString = $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false);
  479. $iCounter = 0;
  480. foreach ($aParticipants as $iProfileId) {
  481. $b = sendMailTemplate($CNF['EMAIL_REMINDER'], 0, $iProfileId, array(
  482. 'EntryUrl' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $aContentInfo[$CNF['FIELD_ID']]),
  483. 'EntryTitle' => $aContentInfo['title'],
  484. 'EntryBegin' => $sEntryBegin,
  485. 'EntryBeginShort' => $sEntryBeginShort,
  486. 'EntryBeginUTC' => $sEntryBeginUTC,
  487. 'EntryLocation' => $sLocationString ? _t('_bx_events_email_reminder_location', $sLocationString) : '',
  488. ), BX_EMAIL_NOTIFY);
  489. $iCounter += $b ? 1 : 0;
  490. }
  491. return $iCounter;
  492. }
  493. }
  494. /** @} */