BxEventsModule.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  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. use Spatie\CalendarLinks\Link;
  12. /**
  13. * Events profiles module.
  14. */
  15. class BxEventsModule extends BxBaseModGroupsModule implements iBxDolCalendarService
  16. {
  17. function __construct(&$aModule)
  18. {
  19. parent::__construct($aModule);
  20. $CNF = &$this->_oConfig->CNF;
  21. $this->_aSearchableNamesExcept = array_merge($this->_aSearchableNamesExcept, [
  22. $CNF['FIELD_TIMEZONE'],
  23. $CNF['FIELD_JOIN_CONFIRMATION'],
  24. $CNF['FIELD_REMINDER']
  25. ]);
  26. $this->_aBrowsingFiltersKeys = array_merge($this->_aBrowsingFiltersKeys, [
  27. 'by_location_country', 'by_location_state', 'by_location_city',
  28. 'by_date', 'date_start', 'date_end', 'timezone'
  29. ]);
  30. }
  31. public function actionCheckIn()
  32. {
  33. $iId = (int)bx_get('id');
  34. if(empty($iId) || empty($this->_iProfileId))
  35. return echoJson(['code' => 1]);
  36. if(!$this->serviceCheckIn($iId, $this->_iProfileId))
  37. return echoJson(['code' => 2]);
  38. return echoJson(['code' => 0, 'reload' => 1]);
  39. }
  40. public function actionCalendarSync($iContentId = 0)
  41. {
  42. $aContentInfo = (int)$iContentId ? $this->_oDb->getContentInfoById((int)$iContentId) : null;
  43. if (!$aContentInfo) {
  44. $this->_oTemplate->displayPageNotFound();
  45. exit;
  46. }
  47. if (CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aContentInfo)) {
  48. $this->_oTemplate->displayAccessDenied();
  49. exit;
  50. }
  51. $CNF = $this->_oConfig->CNF;
  52. if (!isset($aContentInfo['date_start']) || !$aContentInfo['date_start'] || !isset($aContentInfo['date_end']) || !$aContentInfo['date_end'] || !$aContentInfo[$CNF['FIELD_TIMEZONE']] || empty($CNF['OBJECT_METATAGS'])) {
  53. $this->_oTemplate->displayPageNotFound();
  54. exit;
  55. }
  56. $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
  57. $sLocation = $oMetatags && $oMetatags->locationsIsEnabled() ? $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false) : '';
  58. $oDateStart = new DateTime('@' . $aContentInfo['date_start']);
  59. $oDateEnd = new DateTime('@' . ($aContentInfo['date_end'] > $aContentInfo['repeat_stop'] ? $aContentInfo['date_end'] : $aContentInfo['repeat_stop']));
  60. $oTz = new DateTimeZone($aContentInfo[$CNF['FIELD_TIMEZONE']]);
  61. $oDateStart->setTimezone($oTz);
  62. $oDateEnd->setTimezone($oTz);
  63. $oICalLink = $oDateStart && $oDateEnd ? Link::create(
  64. $aContentInfo[$CNF['FIELD_TITLE']],
  65. $oDateStart,
  66. $oDateEnd
  67. ) : null;
  68. if ($sLocation)
  69. $oICalLink->address($sLocation);
  70. $s = $oICalLink->ics();
  71. if (!preg_match('/^data:([a-zA-Z0-9\/]+);charset=[a-zA-Z0-9]+;base64,(.*)$/', $s, $m)) {
  72. $this->_oTemplate->displayErrorOccured();
  73. exit;
  74. }
  75. header('Content-type: ' . $m[1] . '; charset=utf-8');
  76. header('Content-Disposition: inline; filename="event.ics"');
  77. echo base64_decode($m[2]);
  78. }
  79. public function actionCalendarData()
  80. {
  81. $aParams = [
  82. 'type' => ($sType = bx_get('type')) !== false ? $sType : 'browse',
  83. 'start' => bx_get('start'),
  84. 'end' => bx_get('end')
  85. ];
  86. if(($iEventId = bx_get('event')) !== false)
  87. $aParams['event_id'] = (int)$iEventId;
  88. if(($iProfileId = bx_get('profile_id')) !== false)
  89. $aParams['profile_id'] = (int)$iProfileId;
  90. if(($iContextId = bx_get('context_id')) !== false)
  91. $aParams['context_id'] = (int)$iContextId;
  92. $aResult = $this->serviceCalendarData($aParams);
  93. if(isset($aResult['data']))
  94. $aResult = $aResult['data'];
  95. header('Content-Type: application/json; charset=utf-8');
  96. echo json_encode($aResult);
  97. }
  98. public function serviceCheckIn($iId, $iProfileId = 0)
  99. {
  100. if(!$iProfileId)
  101. $iProfileId = $this->_iProfileId;
  102. if(!$iProfileId)
  103. return false;
  104. $aDataEntry = $this->_oDb->getContentInfoById($iId);
  105. if(empty($aDataEntry) || !is_array($aDataEntry))
  106. return false;
  107. if($this->checkAllowedCheckIn($aDataEntry) !== CHECK_ACTION_RESULT_ALLOWED)
  108. return false;
  109. if(!$this->_oDb->checkIn($iProfileId, $iId))
  110. return false;
  111. bx_alert($this->getName(), 'check_in', $iId, $iProfileId, [
  112. 'event_profile_id' => $aDataEntry['profile_id'],
  113. ]);
  114. return true;
  115. }
  116. public function serviceCalendarData($aParams = [])
  117. {
  118. $CNF = &$this->_oConfig->CNF;
  119. if($this->_bIsApi && is_string($aParams)) {
  120. $aParams = json_decode($aParams, true);
  121. if(!empty($aParams['params']))
  122. $aParams = $aParams['params'];
  123. if (isset($aParams['profile_id']) && $aParams['profile_id'] == '{profile_id}')
  124. $aParams['profile_id'] = bx_get_logged_profile_id();
  125. }
  126. $sModule = $this->getName();
  127. $oPrivacy = BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_VIEW']);
  128. $iNow = time();
  129. if(empty($aParams['start']))
  130. $sStart = date('d.m.Y', $iNow);
  131. else if(is_numeric($aParams['start']))
  132. $sStart = date('d.m.Y', $aParams['start']);
  133. else
  134. $sStart = $aParams['start'];
  135. if(empty($aParams['end']))
  136. $sEnd = date('d.m.Y', $iNow + 3600 * 24 * 30);
  137. else if(is_numeric($aParams['end']))
  138. $sEnd = date('d.m.Y', $aParams['end']);
  139. else
  140. $sEnd = $aParams['end'];
  141. $iContentId = 0;
  142. $iContextId = 0;
  143. $aSQLPart = [];
  144. switch($aParams['type']) {
  145. case 'event':
  146. $iContentId = !empty($aParams['event_id']) ? (int)$aParams['event_id'] : 0;
  147. $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
  148. if(CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aContentInfo))
  149. $this->_oTemplate->displayAccessDenied();
  150. break;
  151. case 'author':
  152. $iProfileId = !empty($aParams['profile_id']) ? (int)$aParams['profile_id'] : 0;
  153. $aSQLPart = [
  154. 'where' => $this->_oDb->prepareAsString("AND `{$CNF['TABLE_ENTRIES']}`.`{$CNF['FIELD_AUTHOR']}`=?", $iProfileId)
  155. ];
  156. break;
  157. case 'joined':
  158. $iProfileId = !empty($aParams['profile_id']) ? (int)$aParams['profile_id'] : 0;
  159. $oConnection = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
  160. if(!$oConnection)
  161. return [];
  162. $aEventsJoined = $oConnection->getConnectedContent($iProfileId, true);
  163. if(empty($aEventsJoined) || !is_array($aEventsJoined))
  164. return [];
  165. $aSQLPart = [
  166. 'join' => "INNER JOIN `sys_profiles` ON `{$CNF['TABLE_ENTRIES']}`.`{$CNF['FIELD_ID']}`=`sys_profiles`.`content_id` AND `sys_profiles`.`type`='{$sModule}'",
  167. 'where' => "AND `sys_profiles`.`id` IN (" . $this->_oDb->implode_escape($aEventsJoined) . ")"
  168. ];
  169. break;
  170. case 'followed':
  171. $iProfileId = !empty($aParams['profile_id']) ? (int)$aParams['profile_id'] : 0;
  172. $oConnection = BxDolConnection::getObjectInstance('sys_profiles_subscriptions');
  173. if(!$oConnection)
  174. return [];
  175. $aEventsFollowed = $oConnection->getConnectedContent($iProfileId, false);
  176. if(empty($aEventsFollowed) || !is_array($aEventsFollowed))
  177. return [];
  178. $aSQLPart = [
  179. 'join' => "INNER JOIN `sys_profiles` ON `{$CNF['TABLE_ENTRIES']}`.`{$CNF['FIELD_ID']}`=`sys_profiles`.`content_id` AND `sys_profiles`.`type`='{$sModule}'",
  180. 'where' => "AND `sys_profiles`.`id` IN (" . $this->_oDb->implode_escape($aEventsFollowed) . ")"
  181. ];
  182. break;
  183. case 'context':
  184. $iContextId = !empty($aParams['context_id']) ? (int)$aParams['context_id'] : 0;
  185. if(!$this->serviceIsEnableForContext($iContextId))
  186. return [];
  187. $aSQLPart = $oPrivacy->getContentByGroupAsSQLPart(-$iContextId);
  188. break;
  189. default:
  190. $aSQLPart = $oPrivacy->getContentPublicAsSQLPart(0, $oPrivacy->getPartiallyVisiblePrivacyGroups());
  191. }
  192. // get entries
  193. $aEntries = $this->_oDb->getEntriesByDate($sStart, $sEnd, $iContentId, $aSQLPart);
  194. foreach($aEntries as &$aEntry){
  195. $aEntry['cover'] = $this->serviceGetCover($aEntry['id']);
  196. }
  197. bx_alert($this->getName(), 'calendar_data', 0, false, array(
  198. 'params' => $aParams,
  199. 'event' => $iContentId,
  200. 'context_id' => $iContextId,
  201. 'start' => $sStart,
  202. 'end' => $sEnd,
  203. 'sql_part' => &$aSQLPart,
  204. 'data' => &$aEntries,
  205. ));
  206. return [
  207. 'params' => ['start' => strtotime($sStart), 'end' => strtotime($sEnd)],
  208. 'data' => $aEntries
  209. ];
  210. }
  211. public function serviceIsIcalExportAvaliable($iContentId)
  212. {
  213. return true;
  214. $aContentInfo = $iContentId ? $this->_oDb->getContentInfoById($iContentId) : false;
  215. if ($aContentInfo)
  216. return false;
  217. return $aContentInfo['date_start'] && $aContentInfo['date_end'];
  218. }
  219. public function decodeDataAPI($aData, $aParams = [])
  220. {
  221. $CNF = $this->_oConfig->CNF;
  222. $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
  223. $aResult = parent::decodeDataAPI($aData, $aParams);
  224. $aResult = array_merge($aResult, [
  225. 'date_start' => $aData[$CNF['FIELD_DATE_START']],
  226. 'date_end' => $aData[$CNF['FIELD_DATE_END']],
  227. 'description' => $aData[$CNF['FIELD_TEXT']],
  228. 'location_data' => $oMetatags->locationGet($aData[$CNF['FIELD_ID']]),
  229. 'threshold' => $aData['threshold'],
  230. 'location' => $oMetatags->locationsString($aData[$CNF['FIELD_ID']], false)
  231. ]);
  232. return $aResult;
  233. }
  234. public function serviceGetCalendarEntries($iProfileId)
  235. {
  236. $CNF = &$this->_oConfig->CNF;
  237. $oConn = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
  238. $aData = $oConn->getConnectedContent($iProfileId);
  239. $aData2 = array(0);
  240. foreach($aData as $iProfileId2){
  241. $oProfile = BxDolProfile::getInstance($iProfileId2);
  242. array_push($aData2, $oProfile->getContentId());
  243. }
  244. $aSQLPart['where'] = " AND `bx_events_data`.`id` IN(" . implode(',', $aData2) . ")";
  245. return $this->_oDb->getEntriesByDate(bx_get('start'), bx_get('end'), null, $aSQLPart);
  246. }
  247. public function serviceGetSafeServices()
  248. {
  249. return array_merge(parent::serviceGetSafeServices(), [
  250. 'CheckIn' => '',
  251. 'BrowsePastProfiles' => '',
  252. 'Calendar' => '',
  253. 'CalendarData' => ''
  254. ]);
  255. }
  256. public function serviceEntityEditQuestionnaire($iProfileId = 0)
  257. {
  258. $CNF = &$this->_oConfig->CNF;
  259. if(!$iProfileId)
  260. $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
  261. if(!$iProfileId)
  262. return '';
  263. $aProfileInfo = BxDolProfileQuery::getInstance()->getInfoById($iProfileId);
  264. if(empty($aProfileInfo) || !is_array($aProfileInfo))
  265. return '';
  266. $aContentInfo = $this->_oDb->getContentInfoById($aProfileInfo['content_id']);
  267. if(empty($aContentInfo) || !is_array($aContentInfo))
  268. return '';
  269. if($this->checkAllowedEdit($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
  270. return MsgBox(_t('_Access denied'));
  271. $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_QUESTIONS_MANAGE']);
  272. if(!$oGrid)
  273. return '';
  274. if($this->_bIsApi){
  275. return [
  276. bx_api_get_block('grid', $oGrid->getCodeAPI())
  277. ];
  278. }
  279. return $oGrid->getCode();
  280. }
  281. public function serviceEntitySessions($iProfileId = 0)
  282. {
  283. $CNF = &$this->_oConfig->CNF;
  284. if(!$iProfileId)
  285. $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
  286. if(!$iProfileId)
  287. return '';
  288. $aProfileInfo = BxDolProfileQuery::getInstance()->getInfoById($iProfileId);
  289. if(empty($aProfileInfo) || !is_array($aProfileInfo))
  290. return '';
  291. $aContentInfo = $this->_oDb->getContentInfoById($aProfileInfo['content_id']);
  292. if(empty($aContentInfo) || !is_array($aContentInfo))
  293. return '';
  294. if($this->checkAllowedEdit($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
  295. return MsgBox(_t('_Access denied'));
  296. $oGrid = BxDolGrid::getObjectInstance($CNF['OBJECT_GRID_SESSIONS_MANAGE']);
  297. if(!$oGrid)
  298. return '';
  299. if($this->_bIsApi){
  300. return [
  301. bx_api_get_block('grid', $oGrid->getCodeAPI())
  302. ];
  303. }
  304. return $oGrid->getCode();
  305. }
  306. /**
  307. * @page service Service Calls
  308. * @section bx_events Events
  309. * @subsection bx_events-browse Browse
  310. * @subsubsection bx_events-browse_upcoming_profiles browse_upcoming_profiles
  311. *
  312. * @code bx_srv('bx_events', 'browse_upcoming_profiles', [...]); @endcode
  313. *
  314. * Browse upcoming events
  315. *
  316. * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
  317. * @param $bAjaxPaginate use AJAX paginate or not
  318. *
  319. * @see BxEventsModule::serviceBrowseUpcomingProfiles
  320. */
  321. /**
  322. * @ref bx_events-browse_upcoming_profiles "browse_upcoming_profiles"
  323. */
  324. public function serviceBrowseUpcomingProfiles ($aParams = false)
  325. {
  326. $bDisplayEmptyMsg = false;
  327. if(isset($aParams['empty_message'])) {
  328. $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
  329. unset($aParams['empty_message']);
  330. }
  331. $bAjaxPaginate = true;
  332. if(isset($aParams['ajax_paginate'])) {
  333. $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
  334. unset($aParams['ajax_paginate']);
  335. }
  336. return $this->_serviceBrowse ('upcoming', $aParams, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  337. }
  338. /**
  339. * @page service Service Calls
  340. * @section bx_events Events
  341. * @subsection bx_events-browse Browse
  342. * @subsubsection bx_events-browse_upcoming_connected_profiles browse_upcoming_connected_profiles
  343. *
  344. * @code bx_srv('bx_events', 'browse_upcoming_connected_profiles', [...]); @endcode
  345. *
  346. * Browse upcoming connected (followed) events
  347. *
  348. * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
  349. * @param $bAjaxPaginate use AJAX paginate or not
  350. *
  351. * @see BxEventsModule::serviceBrowseUpcomingConnectedProfiles
  352. */
  353. /**
  354. * @ref bx_events-browse_upcoming_connected_profiles "browse_upcoming_connected_profiles"
  355. */
  356. public function serviceBrowseUpcomingConnectedProfiles ($aParams = false)
  357. {
  358. if(!is_array($aParams) || empty($aParams['object']) || empty($aParams['profile']))
  359. return '';
  360. $bDisplayEmptyMsg = false;
  361. if(isset($aParams['empty_message'])) {
  362. $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
  363. unset($aParams['empty_message']);
  364. }
  365. $bAjaxPaginate = true;
  366. if(isset($aParams['ajax_paginate'])) {
  367. $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
  368. unset($aParams['ajax_paginate']);
  369. }
  370. $aDefaults = array(
  371. 'type' => 'content',
  372. 'mutual' => false,
  373. 'profile2' => 0
  374. );
  375. return $this->_serviceBrowse ('upcoming_connected', array_merge($aDefaults, $aParams), BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  376. }
  377. public function serviceBrowseRecentProfiles ($bDisplayEmptyMsg = false, $bAjaxPaginate = true)
  378. {
  379. $sMode = 'recent';
  380. $sFilterSelector = '#' . $this->_oConfig->getHtmlIds('popup_bfilters_recent');
  381. $aFilterParams = ['mode' => $sMode];
  382. return $this->_serviceBrowse ($sMode, [
  383. 'filters' => [
  384. 'values' => [],
  385. 'onclick' => $this->_oConfig->getJsObject('main') . ".changeBrowsingFilters(this, '" . $sFilterSelector . "', " . json_encode($aFilterParams) . ")"
  386. ]
  387. ], BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  388. }
  389. /**
  390. * @page service Service Calls
  391. * @section bx_events Events
  392. * @subsection bx_events-browse Browse
  393. * @subsubsection bx_events-browse_past_profiles browse_past_profiles
  394. *
  395. * @code bx_srv('bx_events', 'browse_past_profiles', [...]); @endcode
  396. *
  397. * Browse past events
  398. *
  399. * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
  400. * @param $bAjaxPaginate use AJAX paginate or not
  401. *
  402. * @see BxEventsModule::serviceBrowsePastProfiles
  403. */
  404. /**
  405. * @ref bx_events-browse_past_profiles "browse_past_profiles"
  406. */
  407. public function serviceBrowsePastProfiles ($bDisplayEmptyMsg = false, $bAjaxPaginate = true)
  408. {
  409. return $this->_serviceBrowse ('past', false, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  410. }
  411. /**
  412. * @page service Service Calls
  413. * @section bx_events Events
  414. * @subsection bx_events-browse Browse
  415. * @subsubsection bx_events-browse_past_profiles browse_past_profiles_by_params
  416. *
  417. * @code bx_srv('bx_events', 'browse_past_profiles_by_params', [...]); @endcode
  418. *
  419. * Browse past events
  420. *
  421. * @param $aParams array with all parameters
  422. *
  423. * @see BxEventsModule::serviceBrowsePastProfilesByParams
  424. */
  425. /**
  426. * @ref bx_events-browse_past_profiles_by_params "browse_past_profiles_by_params"
  427. */
  428. public function serviceBrowsePastProfilesByParams ($aParams)
  429. {
  430. $bDisplayEmptyMsg = false;
  431. if(isset($aParams['empty_message'])) {
  432. $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
  433. unset($aParams['empty_message']);
  434. }
  435. $bAjaxPaginate = true;
  436. if(isset($aParams['ajax_paginate'])) {
  437. $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
  438. unset($aParams['ajax_paginate']);
  439. }
  440. return $this->_serviceBrowse ('past', $aParams, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
  441. }
  442. /**
  443. * @page service Service Calls
  444. * @section bx_events Events
  445. * @subsection bx_events-page_blocks Page Blocks
  446. * @subsubsection bx_events-calendar calendar
  447. *
  448. * @code bx_srv('bx_events', 'calendar', [...]); @endcode
  449. *
  450. * Shows event or events calendar
  451. *
  452. * @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.
  453. * @param $sTemplate template to use to show calendar, or leave empty for default template, possible options: calendar.html, calendar_compact.html
  454. * @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.
  455. *
  456. * @see BxEventsModule::serviceCalendar
  457. */
  458. /**
  459. * @ref bx_events-calendar "calendar"
  460. */
  461. public function serviceCalendar($aData = [], $sTemplate = 'calendar.html')
  462. {
  463. if(isset($aData['event']) && !isset($aData['type']))
  464. $aData['type'] = 'event';
  465. if(isset($aData['profile_id']) && !isset($aData['type']))
  466. $aData['type'] = 'joined';
  467. if(isset($aData['context_id'])) {
  468. if(!$this->serviceIsEnableForContext($aData['context_id']))
  469. return '';
  470. $aData['type'] = 'context';
  471. }
  472. if(!isset($aData['type']))
  473. $aData['type'] = 'browse';
  474. $oCalendar = new BxTemplCalendar([
  475. 'eventSources' => [
  476. bx_append_url_params(BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'calendar_data', $aData),
  477. ],
  478. ], $this->_oTemplate);
  479. if (bx_is_api())
  480. return [bx_api_get_block('calendar', ['request_url' => $this->getName() . '/calendar_data&params[]=' . json_encode(['params' => $aData])])];
  481. $this->_oTemplate->addCss(['main.css']);
  482. return $oCalendar->display($sTemplate);
  483. }
  484. public function serviceSessions($iContentId = 0)
  485. {
  486. if(!$iContentId)
  487. $iContentId = (int)bx_get('id');
  488. if(!$iContentId)
  489. return '';
  490. return $this->_oTemplate->getBlockSessions($iContentId);
  491. }
  492. /**
  493. * @page service Service Calls
  494. * @section bx_events Events
  495. * @subsection bx_events-other Other
  496. * @subsubsection bx_events-process_reminders process_reminders
  497. *
  498. * @code bx_srv('bx_events', 'process_reminders'); @endcode
  499. *
  500. * Send remiders to event's participants.
  501. *
  502. * It must be processed once every hour for the proper processing.
  503. * @return nothing
  504. *
  505. * @see BxEventsModule::serviceProcessReminders
  506. */
  507. /**
  508. * @ref bx_events-process_reminders "process_reminders"
  509. */
  510. public function serviceProcessReminders()
  511. {
  512. $CNF = &$this->_oConfig->CNF;
  513. $iNow = time();
  514. $bUseIn = $this->_oConfig->isInternalNotifications();
  515. $sModule = $this->getName();
  516. // get all events for today and tomorrow, since the max reminder is 24 hours
  517. $aEntries = $this->_oDb->getEntriesByDate('@' . time(), '@' . (time() + 3600 * getParam('bx_events_reminder_interval')));
  518. foreach($aEntries as $aEntry) {
  519. $aReminders = $bUseIn ? [$aEntry['reminder']] : [24, 1];
  520. if(!$aReminders)
  521. continue;
  522. $oEventProfile = BxDolProfile::getInstanceByContentAndType($aEntry[$CNF['FIELD_ID']], $sModule);
  523. if(!$oEventProfile)
  524. continue;
  525. $iEventProfileId = $oEventProfile->id();
  526. foreach($aReminders as $iReminder) {
  527. $iTimestamp = $aEntry['start_utc'] - (3600 * $iReminder);
  528. if($iNow <= ($iTimestamp - 3600) || $iNow >= $iTimestamp)
  529. continue;
  530. if($bUseIn)
  531. $this->sendReminders($aEntry);
  532. bx_alert($sModule, 'reminder', $aEntry[$CNF['FIELD_ID']], $iEventProfileId, array(
  533. 'object_author_id' => $iEventProfileId,
  534. 'reminder' => $iReminder
  535. ));
  536. }
  537. }
  538. }
  539. public function serviceGetNotificationsData()
  540. {
  541. $CNF = &$this->_oConfig->CNF;
  542. $aResults = parent::serviceGetNotificationsData();
  543. $sModule = $this->_aModule['name'];
  544. $aSettingsTypes = ['follow_context'];
  545. $aResults['handlers'] = array_merge($aResults['handlers'], [
  546. ['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']],
  547. ]);
  548. $aResults['settings'] = array_merge($aResults['settings'], [
  549. ['group' => 'reminder', 'unit' => $sModule, 'action' => 'reminder', 'types' => $aSettingsTypes],
  550. ]);
  551. $aResults['alerts'] = array_merge($aResults['alerts'], [
  552. ['unit' => $sModule, 'action' => 'reminder'],
  553. ]);
  554. return $aResults;
  555. }
  556. public function serviceGetNotificationsInsertData($oAlert, $aHandler, $aDataItems)
  557. {
  558. if($oAlert->sAction != 'reminder' || empty($aDataItems) || !is_array($aDataItems) || empty($oAlert->aExtras['reminder']))
  559. return $aDataItems;
  560. foreach($aDataItems as $iIndex => $aDataItem) {
  561. $aContent = [];
  562. if(!empty($aDataItem['content']))
  563. $aContent = unserialize($aDataItem['content']);
  564. $aContent['reminder'] = (int)$oAlert->aExtras['reminder'];
  565. $aDataItems[$iIndex]['content'] = serialize($aContent);
  566. }
  567. return $aDataItems;
  568. }
  569. public function serviceGetNotificationsReminder($aEvent)
  570. {
  571. $CNF = &$this->_oConfig->CNF;
  572. if(!isset($aEvent['content']['reminder']))
  573. return [];
  574. $iContentId = (int)$aEvent['object_id'];
  575. $oEventProfile = BxDolProfile::getInstanceByContentAndType((int)$iContentId, $this->getName());
  576. if(!$oEventProfile)
  577. return [];
  578. $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
  579. if(empty($aContentInfo) || !is_array($aContentInfo))
  580. return [];
  581. return [
  582. 'entry_sample' => $CNF['T']['txt_sample_single'],
  583. 'entry_url' => bx_absolute_url(str_replace(BX_DOL_URL_ROOT, '', $oEventProfile->getUrl()), '{bx_url_root}'),
  584. 'entry_caption' => $oEventProfile->getDisplayName(),
  585. 'entry_author' => $oEventProfile->id(),
  586. 'lang_key' => '_bx_events_txt_ntfs_reminder_' . $aEvent['content']['reminder']
  587. ];
  588. }
  589. /**
  590. * @page service Service Calls
  591. * @section bx_events Events
  592. * @subsection bx_events-internal Internal
  593. * @subsubsection bx_events-get_timeline_post get_timeline_post
  594. *
  595. * @code bx_srv('bx_events', 'get_timeline_post', [...]); @endcode
  596. *
  597. * Get Timeline post. It's needed for Timeline module.
  598. *
  599. * @param $aEvent timeline event array from Timeline module
  600. * @return array in special format which is needed specifically for Timeline module to display the data.
  601. *
  602. * @see BxEventsModule::serviceGetTimelinePost
  603. */
  604. /**
  605. * @ref bx_events-get_timeline_post "get_timeline_post"
  606. */
  607. public function serviceGetTimelinePost($aEvent, $aBrowseParams = array())
  608. {
  609. $a = parent::serviceGetTimelinePost($aEvent, $aBrowseParams);
  610. if($a === false)
  611. return false;
  612. $aContentInfo = $this->_oDb->getContentInfoById($aEvent['object_id']);
  613. if(empty($aContentInfo) || !is_array($aContentInfo))
  614. return $a;
  615. $CNF = $this->_oConfig->CNF;
  616. $oDateStart = date_create('@' . $aContentInfo['date_start']);
  617. if ($oDateStart){
  618. $oDateStart->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
  619. $a['content']['date_start'] = $aContentInfo['date_start'];
  620. }
  621. $oDateEnd = date_create('@' . $aContentInfo['date_end']);
  622. if ($oDateEnd){
  623. $oDateEnd->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
  624. $a['content']['date_end'] = $aContentInfo['date_end'];
  625. }
  626. $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
  627. $sLocationString = $oMetatags ? $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false) : false;
  628. $a['content']['raw'] = $this->_oTemplate->parseHtmlByName('timeline_post.html', [
  629. 'title' => $a['content']['title'],
  630. 'title_attr' => bx_html_attribute($a['content']['title']),
  631. 'url' => $a['content']['url'],
  632. 'bx_if:date' => [
  633. 'condition' => $oDateStart,
  634. 'content' => [
  635. 'date' => $oDateStart ? bx_time_js($aContentInfo['date_start'], BX_FORMAT_DATE_TIME, true) : '',
  636. 'date_c' => $oDateStart->format('c'),
  637. ]],
  638. 'bx_if:location' => array(
  639. 'condition' => !!$sLocationString,
  640. 'content' => array('location' => $sLocationString),
  641. ),
  642. ]);
  643. return $a;
  644. }
  645. public function actionIntervals()
  646. {
  647. $sAction = bx_get('a');
  648. $sMethodName = 'subaction' . ucfirst($sAction);
  649. if (!method_exists($this, $sMethodName)) {
  650. $this->_oTemplate->pageNotFound();
  651. return;
  652. }
  653. $this->$sMethodName();
  654. }
  655. public function subactionRestore($iContentId = 0)
  656. {
  657. if (!$iContentId)
  658. $iContentId = (int)bx_get('c');
  659. $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
  660. if (
  661. ($aContentInfo && CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedEdit ($aContentInfo))
  662. ||
  663. (!$aContentInfo && CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedAdd ())
  664. ) {
  665. $this->_oTemplate->displayAccessDenied();
  666. exit;
  667. }
  668. $a = $this->_oDb->getIntervals($iContentId);
  669. foreach ($a as $iIntervalId => $r) {
  670. $a[$iIntervalId]['file_id'] = $r['interval_id'];
  671. $a[$iIntervalId]['repeat_stop'] = bx_process_output ($r['repeat_stop'], BX_DATA_DATE_TS);
  672. }
  673. if ('json' == bx_get('f')) {
  674. header('Content-type: text/html; charset=utf-8');
  675. echo json_encode($a);
  676. }
  677. }
  678. public function subactionDelete()
  679. {
  680. header('Content-type: text/html; charset=utf-8');
  681. $iIntervalId = (int)bx_get('id');
  682. if (!($aContentInfo = $this->_oDb->getContentInfoByIntervalId($iIntervalId))) {
  683. echo _t('_sys_request_page_not_found_cpt');
  684. }
  685. elseif (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedEdit ($aContentInfo))) {
  686. echo $sMsg;
  687. }
  688. elseif (!$this->_oDb->deleteIntervalById($iIntervalId)) {
  689. echo _t('_sys_txt_error_occured');
  690. }
  691. else {
  692. echo 'ok';
  693. }
  694. }
  695. public function checkAllowedCheckIn($aDataEntry, $isPerformAction = false)
  696. {
  697. $CNF = &$this->_oConfig->CNF;
  698. $sError = _t('_sys_txt_access_denied');
  699. if(!$this->_iProfileId || !$this->isFan($aDataEntry[$CNF['FIELD_ID']], $this->_iProfileId))
  700. return $sError;
  701. if(!$this->isOngoing($aDataEntry))
  702. return $sError;
  703. if($this->_oDb->isCheckedIn($this->_iProfileId, $aDataEntry[$CNF['FIELD_ID']]))
  704. return $sError;
  705. return CHECK_ACTION_RESULT_ALLOWED;
  706. }
  707. public function checkAllowedIcalExport ($aDataEntry, $isPerformAction = false)
  708. {
  709. return $aDataEntry['date_start'] && $aDataEntry['date_end'] ? CHECK_ACTION_RESULT_ALLOWED : _t('_sys_txt_access_denied');
  710. }
  711. /**
  712. * Send reminder to event's paritcipants
  713. * @param $aContentInfo content info
  714. * @return number of sent reminders
  715. */
  716. protected function sendReminders($aContentInfo)
  717. {
  718. $CNF = $this->_oConfig->CNF;
  719. $oConnection = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
  720. if (!$oConnection)
  721. return false;
  722. $oGroupProfile = BxDolProfile::getInstanceByContentAndType($aContentInfo[$CNF['FIELD_ID']], $this->getName());
  723. if (!$oGroupProfile)
  724. return false;
  725. $aParticipants = $oConnection->getConnectedInitiators($oGroupProfile->id(), true);
  726. if (!$aParticipants)
  727. return true;
  728. $oDateBegin = new DateTime();
  729. $oDateBegin->setTimestamp($aContentInfo['start_utc']);
  730. $oDateBegin->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
  731. $sEntryBegin = $oDateBegin->format('r');
  732. $sEntryBeginShort = date(getParam($CNF['PARAM_FORMAT_DATE']), $oDateBegin->getTimestamp());
  733. $oDateBegin->setTimezone(new DateTimeZone('UTC'));
  734. $sEntryBeginUTC = $oDateBegin->format('c');
  735. $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
  736. $sLocationString = $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false);
  737. $iCounter = 0;
  738. foreach ($aParticipants as $iProfileId) {
  739. $b = sendMailTemplate($CNF['EMAIL_REMINDER'], 0, $iProfileId, array(
  740. 'EntryUrl' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $aContentInfo[$CNF['FIELD_ID']])),
  741. 'EntryTitle' => $aContentInfo['title'],
  742. 'EntryBegin' => $sEntryBegin,
  743. 'EntryBeginShort' => $sEntryBeginShort,
  744. 'EntryBeginUTC' => $sEntryBeginUTC,
  745. 'EntryLocation' => $sLocationString ? _t('_bx_events_email_reminder_location', $sLocationString) : '',
  746. ), BX_EMAIL_NOTIFY);
  747. $iCounter += $b ? 1 : 0;
  748. }
  749. return $iCounter;
  750. }
  751. public function isOngoing($aDataEntry)
  752. {
  753. $CNF = &$this->_oConfig->CNF;
  754. if(empty($aDataEntry[$CNF['FIELD_DATE_START']]) || empty($aDataEntry[$CNF['FIELD_DATE_END']]))
  755. return false;
  756. $iTime = time();
  757. return $iTime >= $aDataEntry[$CNF['FIELD_DATE_START']] && $iTime <= $aDataEntry[$CNF['FIELD_DATE_END']];
  758. }
  759. }
  760. /** @} */