BxEventsModule.php 35 KB

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