PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/mess.php

https://github.com/blakeparkinson/dumbgame
PHP | 4102 lines | 3193 code | 407 blank | 502 comment | 1102 complexity | a0dc3cca20036ba39a066f4ec41c4dde MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. *
  4. * @category Edmodo
  5. * @package Edmodo_Helper
  6. * @copyright Copyright (c) 2012 Edmodo USA Inc. (http://www.edmodo.com)
  7. *
  8. */
  9. /**
  10. *
  11. * @category Edmodo
  12. * @package Edmodo_Helper
  13. *
  14. */
  15. class MessagingHelper
  16. {
  17. const MESSAGE='message';
  18. const MESSAGE_ID='message_id';
  19. const CREATOR_ID='creator_id';
  20. const TYPE='type';
  21. const CREATION_DATE='creation_date';
  22. const COLUMN_PUBLIC='public';
  23. const MESSAGE_TYPE_TEXT='text';
  24. const MESSAGE_TYPE_LINK='link';
  25. const MESSAGE_TYPE_VIDEO='video';
  26. const MESSAGE_TYPE_FILE='file';
  27. const MESSAGE_TYPE_ASSIGNMENT='assignment';
  28. const MESSAGE_TYPE_SYSTEM='system';
  29. const MESSAGE_TYPE_COMMENT='comment';
  30. const MESSAGE_TYPE_ALERT='alert';
  31. const MESSAGE_TYPE_GRADE='grade';
  32. const MESSAGE_TYPE_POLL='poll';
  33. const MESSAGE_TYPE_FEED='feed';
  34. const MESSAGE_TYPE_EMBED='embed';
  35. const MESSAGE_TYPE_QUIZ='quiz';
  36. const MESSAGE_TYPE_APP_MESSAGE='app_message';
  37. public static $VALID_MESSAGE_TYPES = array(
  38. MessagingHelper::MESSAGE_TYPE_TEXT,
  39. MessagingHelper::MESSAGE_TYPE_LINK,
  40. MessagingHelper::MESSAGE_TYPE_VIDEO,
  41. MessagingHelper::MESSAGE_TYPE_FILE,
  42. MessagingHelper::MESSAGE_TYPE_ASSIGNMENT,
  43. MessagingHelper::MESSAGE_TYPE_SYSTEM,
  44. MessagingHelper::MESSAGE_TYPE_COMMENT,
  45. MessagingHelper::MESSAGE_TYPE_ALERT,
  46. MessagingHelper::MESSAGE_TYPE_GRADE,
  47. MessagingHelper::MESSAGE_TYPE_POLL,
  48. MessagingHelper::MESSAGE_TYPE_FEED,
  49. MessagingHelper::MESSAGE_TYPE_EMBED,
  50. MessagingHelper::MESSAGE_TYPE_QUIZ,
  51. MessagingHelper::MESSAGE_TYPE_APP_MESSAGE,
  52. );
  53. private $use_gearman = NOTIFICATIONS_USE_GEARMAN;
  54. private $last_direct_msg_receivers;
  55. private $last_indirect_msg_receivers;
  56. private $spotted_replies;
  57. private $feed_type;
  58. private $user_id;
  59. private $user_groups;
  60. private $community_groups;
  61. private $parent_info;
  62. private $institutional_info;
  63. private static $layout_translator;
  64. private static $system_msgs_translator;
  65. public $post_limit_reached = false;
  66. public static $is_api_call = false;
  67. public static $limit_comments_query = true;
  68. /**
  69. *
  70. * @var MessagingHelper
  71. */
  72. private static $_instance;
  73. /**
  74. * Implements the "singleton" pattern for this class
  75. *
  76. * @param string $feed_type
  77. * @return MessagingHelper
  78. */
  79. static function getInstance ($feed_type = 'HOME_FEED')
  80. {
  81. if (! isset(self::$_instance)) {
  82. self::$_instance = new self($feed_type);
  83. } else {
  84. self::$_instance->setFeedType($feed_type);
  85. }
  86. return self::$_instance;
  87. }
  88. /**
  89. * Set class instance
  90. * Use only to mock/stub instance for tests
  91. *
  92. * @param MessagingHelper $instance
  93. * @throws Exception
  94. */
  95. static function setInstance ($instance)
  96. {
  97. if (! $instance instanceof MessagingHelper) {
  98. throw new Exception('Invalid instance class');
  99. }
  100. self::$_instance = $instance;
  101. }
  102. /**
  103. */
  104. static function clearInstance ()
  105. {
  106. self::$_instance = null;
  107. }
  108. private function __construct($feed_type = 'HOME_FEED')
  109. {
  110. //----------------------------
  111. // Stores the user_ids of the users who received the msg
  112. // directly (people typed their names in the sharebox)
  113. $this->last_direct_msg_receivers = array();
  114. //----------------------------
  115. // Stores the user_ids of the users who received the msg
  116. // indirectly (they are part of a group who received a post)
  117. $this->last_indirect_msg_receivers = array();
  118. //----------------------------
  119. // Stores a boolean map of the comments that have been already spotted by the user
  120. $this->spotted_replies = array();
  121. //----------------------------
  122. // Stores a boolean map of the comments that have been already spotted by the user
  123. $this->feed_type = $feed_type;
  124. //----------------------------
  125. // Stores id of the user of the feed we are looking at
  126. $this->viewed_user_id = 0;
  127. //----------------------------
  128. // Stores the status of the user with respect to the community stream being viewed
  129. $this->community_user_status = null;
  130. //----------------------------
  131. // Stores the groups that the user whose feed we are looking at is part of
  132. $this->viewed_user_groups = array();
  133. //----------------------------
  134. // Stores the groups that are being viewed as a receiver on the community page
  135. $this->community_groups = array();
  136. //----------------------------
  137. // Stores the schools/districts that the user whose feed we are looking at is part of
  138. $this->institutional_info = array();
  139. //Stores the parent's info about his students
  140. $this->parent_info = array();
  141. //----------------------------
  142. // Layout translator
  143. self::$layout_translator = null;
  144. //----------------------------
  145. // System messages translator
  146. self::$system_msgs_translator = null;
  147. }
  148. public function setFeedType($feed_type = 'HOME_FEED')
  149. {
  150. $this->feed_type = $feed_type;
  151. }
  152. public static function initializeTranslators($language){
  153. if(!isset(self::$layout_translator)){
  154. // self::$layout_translator = new TranslationHelper(new Zend_Translate('tmx', PATH2_LANGUAGES . 'layout.tmx', $language));
  155. self::$layout_translator = TranslationHelper::getInstance(PATH2_LANGUAGES . 'layout.tmx', $language);
  156. }
  157. if(!isset(self::$system_msgs_translator)){
  158. // self::$system_msgs_translator = new TranslationHelper(new Zend_Translate('tmx', PATH2_LANGUAGES . 'system.tmx', $language));
  159. self::$system_msgs_translator = TranslationHelper::getInstance(PATH2_LANGUAGES . 'system.tmx', $language);
  160. }
  161. }
  162. /**
  163. * This sends a message from the system to a user
  164. * @param string $message_identifier is the system message identifier
  165. * @param array $receiver_id is the id of the intended message recipient
  166. */
  167. public function sendSystemMessage($message_identifier, $receiver_ids = array()){
  168. $message_data = array(
  169. 'type' => 'system',
  170. 'message' => $message_identifier
  171. );
  172. $this->sendMessage(
  173. $message_data,
  174. array('people' => $receiver_ids),
  175. Users::getInstance()->getUserInfo(EDMODO_SYSTEM_USER_ID)
  176. );
  177. }
  178. /**
  179. * This sends a message to one or more peers
  180. * @param array $message_data contains the message's content plus additional info about the message
  181. * @param array $receivers contains the intended message recipients (people or locations or both)
  182. * @param array $sender_info is the sender's account info.
  183. * @param Boolean $resource_resending weather this message is being sent to resend a resource (eg from the Library).
  184. * @param $attached_resources
  185. * @param bool $check_send_rights, flag to indicate if a check should be performed to verify that the sender has rights to send the message
  186. * @param bool $use_response_obj_return, flag to indicate if a HandlerResponseObj should be returned instead of the normal returns
  187. * @param bool $bypass_moderation, flag to indicate if the message should be sent even if the poster is a student and one of the recipients a moderated group
  188. * @return message_id
  189. */
  190. public function sendMessage($message_data, $receivers, $sender_info, $resource_resending = FALSE, $attached_resources = '', $check_send_rights = true, $use_response_obj_return = false, $bypass_moderation = false )
  191. {
  192. $response_obj = new HandlerResponseObj();
  193. if($this->isSpammer($sender_info, $message_data)){
  194. if($use_response_obj_return){
  195. $response_obj->setUnsuccessful();
  196. $response_obj->post_limit_reached = true;
  197. $response_obj->addError('User has reached maximum limit of posts for the last hour');
  198. return $response_obj;
  199. }else{
  200. return false;
  201. }
  202. }
  203. if($check_send_rights){
  204. if ( $sender_info['user_id'] != EDMODO_SYSTEM_USER_ID && !$this->userHasRightsToSendMessage( $sender_info, $receivers, $message_data ))
  205. {
  206. if ($use_response_obj_return) {
  207. $response_obj->setUnsuccessful();
  208. $response_obj->addError('User does not have rights to send message to the specified recipients');
  209. return $response_obj;
  210. } else {
  211. return false;
  212. }
  213. }
  214. }
  215. $community_id=null;
  216. // Messages are only sent immediately if none of the recipients is a moderated group
  217. if ( !$bypass_moderation && !empty($receivers['locations']) ){
  218. $groups_db = Groups::getInstance();
  219. foreach ( $receivers['locations'] as $location ){
  220. if ( $location['type'] == 'group' ){
  221. $group = $groups_db->find($location['id'])->current();
  222. // For small groups, the parent group is checked for moderation
  223. $main_group_info = SmallGroups::getInstance()->getGroupsMainGroup($location['id']);
  224. if ( empty($main_group_info) ){
  225. $main_group_info = $group;
  226. }
  227. if ( $main_group_info['moderated'] && ( $sender_info['type'] == 'STUDENT' || !$groups_db->userOwnsGroup( $sender_info['user_id'], $main_group_info['group_id'], true )) ){
  228. // Post moderated message here
  229. ModeratedMessagesHandler::sendModeratedMessage($message_data, $attached_resources, $group, $sender_info);
  230. if ($use_response_obj_return) {
  231. $response_obj->setSuccessful();
  232. $response_obj->is_moderated = true;
  233. $response_obj->message_id = 0;
  234. $response_obj->scheduled_message_id = 0;
  235. return $response_obj;
  236. } else {
  237. return false;
  238. }
  239. }
  240. }
  241. }
  242. }
  243. // Scheduled message
  244. if ( $sender_info['type'] == 'TEACHER' && isset($message_data['scheduled']) && $message_data['scheduled'] ){
  245. $scheduled_message_id = ScheduledMessagesHandler::sendScheduledMessage($message_data, $attached_resources, $receivers, $sender_info);
  246. if ($use_response_obj_return) {
  247. $response_obj->setSuccessful();
  248. $response_obj->is_scheduled = true;
  249. $response_obj->message_id = 0;
  250. $response_obj->scheduled_message_id = $scheduled_message_id;
  251. return $response_obj;
  252. } else {
  253. return $scheduled_message_id;
  254. }
  255. }
  256. if(empty($message_data['posted_in'])) $message_data['posted_in'] = null;
  257. if(empty($message_data['posted_in_id'])) $message_data['posted_in_id'] = null;
  258. if(empty($message_data['comment_to'])) $message_data['comment_to'] = null;
  259. $message_data['other_reply_count'] = 0;
  260. if ($message_data['comment_to']) {
  261. $message_data['other_reply_count'] = ArrayHelper::elem(NewComments::getInstance()->getCommentCount(array($message_data['comment_to'])), $message_data['comment_to'], 1) - 1;
  262. }
  263. //--------------------------------
  264. // Clean up the Message Receiver arrays
  265. $this->last_direct_msg_receivers= array();
  266. $this->last_indirect_msg_receivers= array();
  267. // STORE MESSAGE
  268. $message = array(
  269. 'creator_id' => $sender_info['user_id'],
  270. 'type' => $message_data['type'],
  271. 'comment_to' => $message_data['comment_to']
  272. );
  273. if (isset($message_data['creation_date'])) {
  274. $message['creation_date'] = $message_data['creation_date'];
  275. }
  276. $message_id = Messages::getInstance()->store( $message );
  277. // send a 'community post' message to the activity stream
  278. // add to the user's share score
  279. if(isset($receivers['locations'])){
  280. foreach ( $receivers['locations'] as $location ){
  281. if($location['type']=='community'){
  282. //ActivityHandler::triggerCommunityPost($location['id'],$message_id,ActivityCommunityPost::TYPE_SUBJECT);
  283. ShareScore::getInstance()->incrementShareScoreByValue($sender_info['user_id'], 10);
  284. }
  285. }
  286. }
  287. /*
  288. if(isset($receivers['people'])){
  289. foreach($receivers['people'] as $user_id){
  290. $user=Users::getInstance()->find($user_id);
  291. if(isset($user[Users::TYPE]) && $user[Users::TYPE]==Users::TYPE_PUBLISHER){
  292. ActivityHandler::triggerCommunityPost($user_id,$message_id,ActivityCommunityPost::TYPE_PUBLISHER);
  293. }
  294. }
  295. }
  296. */
  297. // STORE THINGS ASSOCIATED WITH MESSAGE
  298. $store_message_data_response = $this->storeMessageData($message_id, $message_data, $sender_info, $receivers, $resource_resending, $attached_resources);
  299. if (isset($store_message_data_response['assignment_id'])) {
  300. $response_obj->assignment_id = $store_message_data_response['assignment_id'];
  301. }
  302. if (isset($store_message_data_response['quiz_run_id'])) {
  303. $response_obj->quiz_run_id = $store_message_data_response['quiz_run_id'];
  304. }
  305. $has_link_or_embed = $store_message_data_response['has_link_or_embed'];
  306. $message_data['has_community_relevance'] = $has_link_or_embed ? $this->hasCommunityRelevance($message_data) : false;
  307. $this->storeMessageReceiversNotification($sender_info, $message_id, $receivers, $message_data);
  308. // System messages for which a notification won't be sent
  309. $silent_system_messages = array('added-to-small-group', 'twitter-off-no-email', 'twitter-off-with-email');
  310. $send_system_message = true;
  311. foreach ( $silent_system_messages as $silent_system_message ){
  312. if ( strpos($message_data['message'], $silent_system_message) == false ){
  313. $send_system_message = false;
  314. break;
  315. }
  316. }
  317. // Notifications are sent
  318. if( ENABLE_EMAIL_NOTIFICATIONS && $message_data['type'] != MessagingHelper::MESSAGE_TYPE_FEED && $message_data['type'] != MessagingHelper::MESSAGE_TYPE_APP_MESSAGE && ($message_data['type'] != MessagingHelper::MESSAGE_TYPE_SYSTEM || $send_system_message ) ) {
  319. $message_data['message_id'] = $message_id;
  320. if ($message_data['type'] == 'quiz') {
  321. $quiz_info = Quiz::getInstance()->search(array('quiz_id' => $message_data['quiz_id']));
  322. $message_data['quiz_title'] = $quiz_info[0]['title'];
  323. $message_data['quiz_description'] = $quiz_info[0]['description'];
  324. }
  325. $message_data[TrackingHelper::DATA_DATE_REQUESTED] = time();
  326. $workerArray = array(
  327. 'message_data' => $message_data,
  328. 'selected_receivers' => $receivers,
  329. 'account_info' => $sender_info,
  330. 'server_name' => ENVIROMENT_DOMAIN //will now use this .ini variable that should be: "edmodo.com", "edmodoqa.com", or "clubmodo.com" for dev env
  331. );
  332. $serialized_data = serialize($workerArray);
  333. if ($this->use_gearman)
  334. {
  335. GearmanHelper::getInstance()->doBackgroundJob('sendNotifications', $serialized_data);
  336. }
  337. else
  338. {
  339. NotificationsWorker::send_notifications(new NotificationTestJob($serialized_data));
  340. }
  341. }
  342. $this->setMaxMessageIdForReceivers($sender_info['user_id'], $receivers, $message_id);
  343. // Add this post to the publisher's cached messages
  344. if ( $sender_info['type'] == 'PUBLISHER' ){
  345. Messages::getInstance()->cachePublisherMessageId( $sender_info['user_id'], $message_id );
  346. }
  347. $this->preProcessPossibleGdocs($message_data, $attached_resources);
  348. //Processing google docs
  349. if(isset($message_data['google_docs']) && !empty($message_data['google_docs']))
  350. {
  351. $google_handler = GoogleApiHandler::getInstance();
  352. if(isset($message_data['google_docs']['docs_info'])){
  353. }
  354. $docs_info = isset($message_data['google_docs']['docs_info']) ? $message_data['google_docs']['docs_info']: NULL;
  355. $role = isset($message_data['google_docs']['role']) ? $message_data['google_docs']['role']: NULL;
  356. $scope = isset($message_data['google_docs']['scope']) ? $message_data['google_docs']['scope']: NULL;
  357. $google_info = UsersGoogleInfo::getInstance()->getUserGoogleInfo( $sender_info["user_id"] );
  358. if(is_array($google_info))
  359. $full_info = array_merge($sender_info,$google_info);
  360. else
  361. $full_info = $sender_info;
  362. $message_data_tmp = $message_data;
  363. $message_data_tmp['message_id'] = $message_id;
  364. $message_data_tmp['obj_type'] = 'MESSAGE';
  365. $gdocs_links = $google_handler->setPermissionsForReceivers($docs_info,$receivers,$full_info,$role,$scope, $message_data_tmp);
  366. }
  367. if ($use_response_obj_return) {
  368. $response_obj->setSuccessful();
  369. $response_obj->is_moderated = false;
  370. $response_obj->message_id = $message_id;
  371. $response_obj->scheduled_message_id = 0;
  372. return $response_obj;
  373. } else {
  374. return $message_id;
  375. }
  376. }
  377. private function isSpammer($sender_info, $message_data){
  378. $is_spammer = false;
  379. $user_id = $sender_info['user_id'];
  380. if($message_data['type'] != 'feed' && $user_id != EDMODO_SYSTEM_USER_ID){
  381. $rate_limiter = RateLimitHandler::getInstance();
  382. $rate_limiter->increment('posts_per_user_per_hour', $user_id);
  383. if ($rate_limiter->isLimited('posts_per_user_per_hour', $user_id)) {
  384. $is_spammer = true;
  385. }
  386. }
  387. return $is_spammer;
  388. }
  389. public function sendComment($comment_data, $receivers, $sender_info, $check_send_rights = true, $bypass_moderation = false, $use_response_obj_return = false)
  390. {
  391. $response_obj = new HandlerResponseObj();
  392. if($check_send_rights){
  393. if ( $sender_info['user_id'] != EDMODO_SYSTEM_USER_ID && !$this->userHasRightsToSendMessage( $sender_info, $receivers, $comment_data, true )){
  394. if ($use_response_obj_return) {
  395. $response_obj->setUnsuccessful();
  396. $response_obj->addError('User does not have rights to reply to this message.');
  397. return $response_obj;
  398. } else {
  399. return false;
  400. }
  401. }
  402. }
  403. // check that the number of replies to this post has not exceeded the limit
  404. if(NewComments::getInstance()->postHasExceededReplyCount($comment_data['comment_to'])){
  405. // update the message's last updated timestamp to indicate that it should be re-fetched by clients that cache the data (e.g. Android App)
  406. if (!MessageData::getInstance()->updateMessageLastUpdatedTime($comment_data['comment_to'])) {
  407. throw new Exception('Failed to update message last updated timestamp');
  408. }
  409. if ($use_response_obj_return) {
  410. $response_obj->setUnsuccessful();
  411. $response_obj->reply_count_exceeded = true;
  412. $response_obj->addError('Reply count exceeded for this post.');
  413. return $response_obj;
  414. } else {
  415. return false;
  416. }
  417. }
  418. // Comments sent by students are only sent immediately if none of the recipients of the original message is a moderated group
  419. if ( !$bypass_moderation && !empty($receivers['locations']) ){
  420. $groups_db = Groups::getInstance();
  421. foreach ( $receivers['locations'] as $location ){
  422. if ( $location['type'] == 'group' ){
  423. $group = $groups_db->find($location['id'])->current();
  424. if ( $group['moderated'] && ( $sender_info['type'] == 'STUDENT' || !$groups_db->userOwnsGroup( $sender_info['user_id'], $group['group_id'], true )) ){
  425. // Post moderated message here
  426. $comment_data['group_id'] = $group['group_id'];
  427. ModeratedMessagesHandler::sendModeratedComment($comment_data, $receivers['locations'], $sender_info);
  428. if ($use_response_obj_return) {
  429. $response_obj->setSuccessful();
  430. $response_obj->is_moderated = true;
  431. $response_obj->comment_id = 0;
  432. return $response_obj;
  433. } else {
  434. return false;
  435. }
  436. }
  437. }
  438. }
  439. }
  440. if(empty($comment_data['posted_in'])) $comment_data['posted_in'] = null;
  441. if(empty($comment_data['posted_in_id'])) $comment_data['posted_in_id'] = null;
  442. $comment_data['type'] = 'comment';
  443. //--------------------------------
  444. // Clean up the Message Receiver arrays
  445. $this->last_direct_msg_receivers= array();
  446. $this->last_indirect_msg_receivers= array();
  447. // STORE MESSAGE
  448. $comment = array(
  449. 'creator_id' => $sender_info['user_id'],
  450. 'comment_to' => $comment_data['comment_to'],
  451. 'content' => $comment_data['message']
  452. );
  453. if (isset($comment_data['creation_date'])) {
  454. $comment['creation_date'] = $comment_data['creation_date'];
  455. }
  456. $comment_id = NewComments::getInstance()->store( $comment );
  457. $comment_data['other_reply_count'] = 0;
  458. if ($comment_data['comment_to']) {
  459. $comment_data['other_reply_count'] = ArrayHelper::elem(NewComments::getInstance()->getCommentCount(array($comment_data['comment_to'])), $comment_data['comment_to'], 1) - 1;
  460. }
  461. // Parse the assignment's description for latex math expressions and generate images
  462. LatexHandler::getInstance()->extractMathExpressions($comment_data['message']);
  463. $this->storeMessageReceiversNotification($sender_info, $comment_id, $receivers, $comment_data, true);
  464. if( ENABLE_EMAIL_NOTIFICATIONS ){
  465. $comment_data['comment_id'] = $comment_id;
  466. $comment_data[TrackingHelper::DATA_DATE_REQUESTED] = time();
  467. $workerArray = array(
  468. 'message_data' => $comment_data,
  469. 'selected_receivers'=> $receivers,
  470. 'account_info' => $sender_info,
  471. 'server_name' => $_SERVER['HTTP_HOST']
  472. );
  473. $serialized_data = serialize($workerArray);
  474. if ($this->use_gearman)
  475. {
  476. GearmanHelper::getInstance()->doBackgroundJob('sendNotifications', $serialized_data);
  477. }
  478. else
  479. {
  480. NotificationsWorker::send_notifications(new NotificationTestJob($serialized_data));
  481. }
  482. }
  483. $this->setMaxMessageIdForReceivers($sender_info['user_id'], $receivers, $comment_id, true);
  484. if ($use_response_obj_return) {
  485. $response_obj->setSuccessful();
  486. $response_obj->is_moderated = false;
  487. $response_obj->comment_id = $comment_id;
  488. return $response_obj;
  489. } else {
  490. return $comment_id;
  491. }
  492. }
  493. private function hasCommunityRelevance( $message_data ){
  494. $has_community_relevance = true;
  495. $unwanted_urls = array(
  496. 'spreadsheets.google.com',
  497. 'docs.google.com',
  498. 'chalk.edmodo.com',
  499. 'surveymonkey.com',
  500. 'pbworks.com',
  501. 'sites.google.com',
  502. 'coveritlive.com'
  503. );
  504. if (isset( $message_data['links'] )){
  505. foreach ( $message_data['links'] as $link ){
  506. foreach ( $unwanted_urls as $unwanted_url ){
  507. if ( strpos($link['url'], $unwanted_url) !== false ){
  508. $has_community_relevance = false;
  509. break;
  510. }
  511. }
  512. }
  513. }
  514. if ($has_community_relevance && isset( $message_data['embeds'] )){
  515. foreach ( $message_data['embeds'] as $embed ){
  516. foreach ( $unwanted_urls as $unwanted_url ){
  517. if ( strpos($embed['url'], $unwanted_url) !== false ){
  518. $has_community_relevance = false;
  519. break;
  520. }
  521. }
  522. }
  523. }
  524. return $has_community_relevance;
  525. }
  526. private function setMaxMessageIdForReceivers($sender_id, $receivers, $message_id, $is_comment = false){
  527. $cache_handler = MemcacheHandler::getInstance();
  528. if ( $cache_handler->cachingAvailable() ){
  529. $user_ids = array();
  530. $user_ids[] = $sender_id;
  531. if(isset($receivers['people']))
  532. {
  533. foreach( $receivers['people'] as $person ){
  534. $user_ids[] = $person;
  535. }
  536. }
  537. if(!empty($receivers['locations'])){
  538. foreach( $receivers['locations'] as $location ){
  539. switch( $location['type'] ){
  540. case 'all-groups':
  541. $groups_db = Groups::getInstance();
  542. $groups = $groups_db->getUserGroups($sender_id);
  543. foreach( $groups as $group ){
  544. $members = $groups_db->getGroupMembers($group['group_id'], true);
  545. if(count($members) < 100){
  546. //if the group is too big, don't do this
  547. foreach( $members as $member ){
  548. $user_ids[] = $member['user_id'];
  549. }
  550. }
  551. }
  552. break;
  553. case 'connections':
  554. $connections = Connections::getInstance()->getConnectedUserIds($sender_id);
  555. foreach( $connections as $connection_id ){
  556. $user_ids[] = $connection_id;
  557. }
  558. break;
  559. case 'all':
  560. $groups_db = Groups::getInstance();
  561. $groups = $groups_db->getUserGroups($sender_id);
  562. foreach( $groups as $group ){
  563. $members = $groups_db->getGroupMembers($group['group_id'], true);
  564. foreach( $members as $member ){
  565. $user_ids[] = $member['user_id'];
  566. }
  567. }
  568. $connections = Connections::getInstance()->getConnectedUserIds($sender_id);
  569. foreach( $connections as $connection_id){
  570. $user_ids[] = $connection_id;
  571. }
  572. break;
  573. case 'group':
  574. $members = Groups::getInstance()->getGroupMembers($location['id'], true);
  575. if(count($members) < 100){
  576. foreach( $members as $member ){
  577. $user_ids[] = $member['user_id'];
  578. }
  579. }
  580. break;
  581. case 'group-parents':
  582. $parents = ParentsStudents::getParents($location['id'], true);
  583. foreach( $parents as $parent ){
  584. $user_ids[] = $parent['parent_id'];
  585. }
  586. break;
  587. case 'school':
  588. $member_ids = Schools::getInstance()->getSchoolMembers($location['id']);
  589. if(count($member_ids) < 1000){
  590. $user_ids = array_merge($user_ids, $member_ids);
  591. }
  592. break;
  593. case 'school_vip':
  594. $member_ids = Schools::getInstance()->getSchoolMembers($location['id'], true, 'teachers-admins');
  595. $user_ids = array_merge($user_ids, $member_ids);
  596. break;
  597. case 'district':
  598. $member_ids = Districts::getInstance()->getDistrictMembers($location['id']);
  599. if(count($member_ids) < 2000){
  600. $user_ids = array_merge($user_ids, $member_ids);
  601. }
  602. break;
  603. case 'district_vip':
  604. $member_ids = Districts::getInstance()->getDistrictMembers($location['id'], true, 'teachers-admins');
  605. if(count($member_ids) < 1000){
  606. $user_ids = array_merge($user_ids, $member_ids);
  607. }
  608. break;
  609. }
  610. }
  611. }
  612. $max_identifier = 'max_message_id';
  613. if ( $is_comment ){
  614. $max_identifier = 'max_comment_id';
  615. }
  616. foreach( $user_ids as $user_id ){
  617. $cache_handler->save($message_id, $user_id.$max_identifier);
  618. }
  619. }
  620. }
  621. /**
  622. * Stores the message's data
  623. * @return array An array containing two keys: has_link_or_embed and assignment_id
  624. */
  625. private function storeMessageData
  626. ($message_id, $message_data, $sender_info, $receivers = null, $resource_resending = FALSE, $attached_resources = '')
  627. {
  628. $response = array();
  629. $has_link_or_embed = false;
  630. $content = $message_data['message'];
  631. // handle special post types
  632. switch ($message_data['type']){
  633. case ASSIGNMENT:
  634. // For some reason, message_data sometimes does not have this optional field.
  635. // Setting to NULL, because Assignments->store() checks to see if this field = NULL.
  636. if (empty($message_data['default_total'])) {
  637. $message_data['default_total'] = null;
  638. }
  639. // if lock_after_due is not specified, set it to 0 by default
  640. $lock_after_due = isset($message_data['lock_after_due']) ? $message_data['lock_after_due'] : 0;
  641. $assignment_id = Assignments::getInstance()->store($message_id, $message_data['assignment-description'], $message_data['due-date'], $message_data['default_total'], $lock_after_due);
  642. $response['assignment_id'] = $assignment_id;
  643. $content = $message_data[ASSIGNMENT];
  644. // Parse the assignment's description for latex math expressions and generate images
  645. LatexHandler::getInstance()->extractMathExpressions($message_data['assignment-description']);
  646. break;
  647. case 'quiz':
  648. $quiz_info = Quiz::getInstance()->search(array('quiz_id' => $message_data['quiz_id']));
  649. $record = array
  650. (
  651. QuizRun::QUIZ_ID=>$message_data['quiz_id'],
  652. QuizRun::DUE_DATE=>$message_data['quiz-due-date'],
  653. QuizRun::AUTO_GRADE=>'1',
  654. QuizRun::SAVE_TO_GRADEBOOK=>$message_data['auto-grade'],
  655. QuizRun::MESSAGE_ID=>$message_id,
  656. QuizRun::SHOW_RESULTS=>$quiz_info[0]['show_results'],
  657. QuizRun::TIME_LIMIT=>$quiz_info[0]['time_limit'],
  658. QuizRun::STATUS=>QuizRun::STATUS_ASSIGNED,
  659. );
  660. $quizRunModel=QuizRun::getInstance();
  661. $quiz_run_id=$quizRunModel->store($record);
  662. //delete any 'preview' quiz_runs for this $quiz_id
  663. $quiz_runs=$quizRunModel->search(array(QuizRun::QUIZ_ID=>$message_data['quiz_id'],QuizRun::PREVIEW=>'1'));
  664. foreach($quiz_runs as $quiz_run){
  665. $quizRunModel->delete($quiz_run[QuizRun::QUIZ_RUN_ID]);
  666. }
  667. $response['quiz_run_id'] = $quiz_run_id;
  668. $content = '';
  669. break;
  670. case 'poll':
  671. // insert the answers as well...
  672. PollAnswers::getInstance()->storeAnswers($message_id, $message_data['poll_answers']);
  673. $content = $message_data['poll_question'];
  674. break;
  675. default:
  676. break;
  677. }
  678. // Parse the message's content for latex math expressions and generate images
  679. LatexHandler::getInstance()->extractMathExpressions($content);
  680. // Message data is stored in the database
  681. $data = array
  682. (
  683. 'message_id' => $message_id,
  684. 'content' => $content //if it's a link, it's the same as the link's title, for now
  685. );
  686. MessageData::getInstance()->store( $data );
  687. if( ! empty($message_data['links']) )
  688. {
  689. $has_link_or_embed = true;
  690. // Store links
  691. LinkHandler::getInstance()->processAttachedLinks($message_data['links'],$message_id,$sender_info,$receivers, 'MESSAGE', $message_data['type']);
  692. }
  693. if( ! empty($message_data['files']) ){
  694. // Store files
  695. FileHandler::getInstance()->processAttachedFiles($message_data['files'],$message_id,$sender_info,$receivers);
  696. }
  697. if (is_array($attached_resources)) {
  698. foreach ($attached_resources as $item_id) {
  699. $item_info = LibraryItems::getInstance()->getLibraryItem($item_id);
  700. if ($sender_info['type'] != 'PUBLISHER') {
  701. LibraryItems::getInstance()->updateRelations($item_id, $receivers);
  702. }
  703. if ($item_info['library_item_type'] == 'FILE') {
  704. MessagesFiles::getInstance()->store(array('message_id' => $message_id, 'file_id' => $item_info['library_item_resource_id']));
  705. } else if ($item_info['library_item_type'] == 'LINK') {
  706. MessagesLinks::getInstance()->store(array('message_id' => $message_id, 'link_id' => $item_info['library_item_resource_id']));
  707. $has_link_or_embed = true;
  708. } else if ($item_info['library_item_type'] == 'EMBED') {
  709. MessagesEmbeds::getInstance()->store(array('message_id' => $message_id, 'embed_id' => $item_info['library_item_resource_id']));
  710. $has_link_or_embed = true;
  711. }
  712. }
  713. }
  714. $response['has_link_or_embed'] = $has_link_or_embed;
  715. return $response;
  716. }
  717. /**
  718. * Stores the corresponding notifications for a message's receivers
  719. * @param $sender_info is the message's sender info
  720. * @param $message_origin is the location where the message was posted
  721. * @param $receivers is an array with all the message's receivers
  722. */
  723. private function storeMessageReceiversNotification($sender_info, $message_id, $receivers, $message_data, $is_comment = false){
  724. $locations = null;
  725. $message_sent_to_group = false;
  726. if(!empty($receivers['locations'])){
  727. $message_sent_to_group = true;
  728. $locations = $receivers['locations'];
  729. }
  730. $message_data[TrackingHelper::DATA_DATE_REQUESTED] = time();
  731. $db_recipients_array = array();
  732. if($message_sent_to_group) {
  733. $this->checkIfMessageSentToEveryone($sender_info, $locations);
  734. $already_cached_in_community = array();
  735. $communities_model = SubjectCommunities::getInstance();
  736. $all_community_ids = $communities_model->getSubjectCommunityIds();
  737. $is_verified_teacher = false;
  738. if($sender_info['type'] == 'TEACHER'){
  739. $is_verified_teacher = CoppaHandler::userIsCoppaVerified($sender_info['user_id']);
  740. }
  741. foreach($locations as $location) {
  742. //store this message location...
  743. if ( $location['type'] == 'connections' ){
  744. // Messages sent to connections have the sender's user id in the posted_in_id field of message_recipients_connections
  745. $location['id'] = $sender_info['user_id'];
  746. }
  747. $recipient = array(
  748. 'type' => $location['type'],
  749. 'message_id' => $message_id,
  750. 'posted_in_id' => $location['id'],
  751. );
  752. $db_recipients_array[] = $recipient;
  753. switch( $location['type'] )
  754. {
  755. case 'group':
  756. case 'GROUP':
  757. $this->setGroupLastMessageReceivers($location['id']);
  758. if ( !$is_comment ){
  759. Messages::getInstance()->cacheGroupMessageId($location['id'], $message_id);
  760. }
  761. break;
  762. case 'group_parents':
  763. case 'GROUP_PARENTS':
  764. if ( !$is_comment ){
  765. Messages::getInstance()->cacheGroupMessageId($location['id'], $message_id);
  766. // If the message was sent to a group's parents receiver (and not to the group itself), its message_id
  767. // is stored in a cached array later used to avoid displaying it to students viewing the group's stream
  768. $message_sent_to_group_parents_only = true;
  769. foreach($locations as $other_location){
  770. if ( ($other_location['type'] == 'group' || $other_location['type'] == 'GROUP') && $other_location['id'] == $location['id'] ){
  771. $message_sent_to_group_parents_only = false;
  772. break;
  773. }
  774. }
  775. if ( $message_sent_to_group_parents_only ){
  776. Messages::getInstance()->cacheGroupParentsMessageId($location['id'], $message_id);
  777. }
  778. }
  779. break;
  780. case 'connections':
  781. $this->setConnectionsLastMessageReceivers($sender_info['user_id']);
  782. break;
  783. case 'community':
  784. if( $is_verified_teacher &&
  785. $message_data['type'] != 'comment' &&
  786. !isset($already_cached_in_community[$location['id']])){
  787. Messages::getInstance()->cacheCommunityMessageId($location['id'], $message_id);
  788. $already_cached_in_community[$location['id']] = true;
  789. }
  790. if($location['id'] == SUPPORT_COMMUNITY_ID && !$is_comment){
  791. //Send an email to the support address
  792. $title = 'Post from ' . $sender_info['first_name'] . ' ' . $sender_info['last_name'] . ' (' . $sender_info['username'] . ')';
  793. $body_text = $message_data['message'];
  794. $recipients = array(
  795. array(
  796. 'email' => SUPPORT_COMMUNITY_EMAIL,
  797. 'username' => 'Support',
  798. ),
  799. );
  800. $tracking_data = TrackingHelper::makeTrackingData(TrackingHelper::EMAILTYPE_SUPPORT_COMMUNITY__NOTIFICATION, null, null, TrackingHelper::TYPE_SYSTEM, null, TrackingHelper::TYPE_INTERNAL, SUPPORT_COMMUNITY_EMAIL, $message_id, TrackingHelper::TYPE_MESSAGE);
  801. MailHandler::getInstance($body_text)->setTrackingData($tracking_data)->sendEmailWrapper($recipients, $title);
  802. }
  803. break;
  804. }
  805. }
  806. }
  807. $people_count = 0;
  808. if (isset($receivers['people'])){
  809. $people_count = count($receivers['people']);
  810. }
  811. if($people_count > 0){
  812. //add direct notifications for people
  813. if($people_count == 1){
  814. $receiver_info = Users::getInstance()->find($receivers['people'][0]);
  815. if ( $receiver_info['type'] == 'PUBLISHER' ){
  816. Messages::getInstance()->cachePublisherMessageId( $receiver_info['user_id'], $message_id );
  817. }
  818. }
  819. foreach($receivers['people'] as $user_id){
  820. $recipient = array(
  821. 'type' => 'user',
  822. 'message_id' => $message_id,
  823. 'posted_in_id' => $user_id,
  824. );
  825. $db_recipients_array[] = $recipient;
  826. $this->last_direct_msg_receivers[] = $user_id;
  827. $this->last_message_receivers[] = $user_id;
  828. }
  829. }
  830. // this is what actually writes to the message_recipients_* tables for the given message_id
  831. if ( !$is_comment ){
  832. Messages::getInstance()->bulkStore($db_recipients_array);
  833. }
  834. }
  835. /**
  836. * Check if a message was sent to Everyone (all the user's groups)
  837. */
  838. private function checkIfMessageSentToEveryone($sender_info, &$locations){
  839. $all_groups = $all_connections = false;
  840. $list = $locations;
  841. foreach ($list as $i => $location) {
  842. switch ($location['type']) {
  843. case 'all-groups':
  844. $all_groups = true;
  845. $locations = array();
  846. break;
  847. case 'connections':
  848. $all_connections = true;
  849. unset($locations[$i]);
  850. break;
  851. case 'all':
  852. // the message was sent to everyone
  853. $all_groups = true;
  854. $all_connections = true;
  855. unset($locations[$i]);
  856. break;
  857. default:
  858. }
  859. }
  860. if($all_groups){
  861. //send to all the user's groups
  862. $all_user_groups = Groups::getInstance()->getUserGroups($sender_info['user_id'], false, false);
  863. foreach($all_user_groups as $group){
  864. //new rule - the user must own the group in order to send the message
  865. if ($group['read_only'] != 1 && !empty($group['user_owns_group'])) {
  866. $locations[] = array('type' => GROUP, 'id' => $group['group_id']);
  867. }
  868. }
  869. }
  870. if($all_connections && ($sender_info['type'] == 'TEACHER' || $sender_info['type'] == 'PUBLISHER')){
  871. //send to all the teacher's connections
  872. $locations[] = array('type' => 'connections', 'id' => 0);
  873. }
  874. }
  875. /*
  876. * Adds the corresponding user_ids to the last_indirect_msg_receivers array (for spotlight purposes)
  877. */
  878. private function setConnectionsLastMessageReceivers($sender_id)
  879. {
  880. $user_ids = Connections::getInstance()->getConnectedUserIds($sender_id);
  881. foreach($user_ids as $user_id){
  882. $this->last_indirect_msg_receivers[] = $user_id;
  883. $this->last_message_receivers[] = $user_id;
  884. }
  885. }
  886. /*
  887. * Adds the corresponding user_ids to the last_indirect_msg_receivers array (for spotlight purposes)
  888. */
  889. private function setGroupLastMessageReceivers($group_id){
  890. $user_ids = Groups::getInstance()->getUserIds($group_id);
  891. foreach($user_ids as $user_id){
  892. $this->last_indirect_msg_receivers[] = $user_id;
  893. $this->last_message_receivers[] = $user_id;
  894. }
  895. }
  896. /**
  897. * Returns a list of user_ids who received the last message
  898. * @return array
  899. */
  900. public function getLastMessageReceivers()
  901. {
  902. $last_msg_receivers = array_merge($this->last_direct_msg_receivers,$this->last_indirect_msg_receivers);
  903. return $last_msg_receivers;
  904. }
  905. /**
  906. * Returns a list of direct user_ids who received the last message
  907. * @return array
  908. */
  909. public function getLastMessageDirectReceivers()
  910. {
  911. return $this->last_direct_msg_receivers;
  912. }
  913. /*
  914. * Modifies the contents of a mesage
  915. * $params string message_id is the id of the message to edit
  916. */
  917. public function editMessage($message_id, $content, $link_id = null, $new_url = null){
  918. if(!empty($link_id)){
  919. Links::getInstance()->editLink($link_id, $new_url);
  920. }
  921. MessageData::getInstance()->editMessage($message_id, $content);
  922. // Parse the message's content for latex math expressions and generate images
  923. LatexHandler::getInstance()->extractMathExpressions($content);
  924. }
  925. public function checkFilteredMessages($language, &$filters, $library_button = false, $get_community_counters = false, $get_html = false, $view = null)
  926. {
  927. $result = array();
  928. $the_filters = array
  929. (
  930. 'get_all_messages' => (!isset($filters['all_msgs']) || $filters['all_msgs'] == 'true') ? true : false,
  931. 'get_comments_only' => (!isset($filters['comments_only']) || $filters['comments_only'] == 'false') ? false : true,
  932. 'search_str' => isset($filters['search_str']) ? $filters['search_str'] : '',
  933. 'people_filter' => (!isset($filters['people_filter']) || $filters['people_filter'] == 'null') ? 'EVERYONE' : $filters['people_filter'],
  934. 'group_id' => (!isset($filters['group_id']) || $filters['group_id'] == 'null') ? null : $filters['group_id'],
  935. 'direct_user_id' => (!isset($filters['direct_user_id']) || $filters['direct_user_id'] == 'null') ? null : $filters['direct_user_id'],
  936. 'tag_id' => (!isset($filters['tag_id']) || $filters['tag_id'] == 'null') ? null : $filters['tag_id'],
  937. 'community_id' => (!isset($filters['community_id']) || $filters['community_id'] == 'null') ? null : $filters['community_id'],
  938. 'include_communities' => (!isset($filters['include_communities']) || $filters['include_communities'] == 'null') ? null : $filters['include_communities'],
  939. 'connections' => (!isset($filters['connections']) || $filters['connections'] == 'null') ? null : $filters['connections'],
  940. 'publishers' => (!isset($filters['publishers']) || $filters['publishers'] == 'null') ? null : $filters['publishers'],
  941. 'current_last_msg_id' => isset($filters['current_last_msg_id']) ? $filters['current_last_msg_id'] : 0,
  942. 'max_messages' => isset($filters['max_messages']) ? $filters['max_messages'] : 20,
  943. 'page' => isset($filters['page']) ? $filters['page'] : false,
  944. 'sqllimit' => isset($filters['sqllimit']) ? true : false,
  945. 'max_message_id' => (!isset($filters['max_message_id']) || $filters['max_message_id'] == 'null') ? null : $filters['max_message_id'],
  946. 'max_comment_id' => (!isset($filters['max_comment_id']) || $filters['max_comment_id'] == 'null') ? null : $filters['max_comment_id'],
  947. 'public_only' => (!isset($filters['public_only']) || $filters['public_only'] == 'false' || !$filters['public_only']) ? false : true,
  948. 'sender_id' => isset($filters['sender_id']) ? $filters['sender_id'] : false,
  949. 'user_feed_id' => (!isset($filters['user_feed_id']) || $filters['user_feed_id'] == 'null') ? null : $filters['user_feed_id'],
  950. 'students_info_for_parent' => isset($filters['students_info_for_parent'])? $filters['students_info_for_parent'] : null,
  951. 'parents' => isset($filters['parents'])? $filters['parents'] : null,
  952. 'is_inst_subdomain' => isset($filters['is_inst_subdomain'])? $filters['is_inst_subdomain'] : false,
  953. 'message_ids_in_stream' => isset($filters['message_ids_in_stream']) && !empty($filters['message_ids_in_stream'])? $filters['message_ids_in_stream'] : null,
  954. 'get_school_vip_msgs' => isset($filters['get_school_vip_msgs'])? $filters['get_school_vip_msgs'] : false,
  955. 'trending_posts' => isset($filters['trending_posts'])? $filters['trending_posts'] : false,
  956. 'max_spotlight_id' => (!isset($filters['max_spotlight_id']) || $filters['max_spotlight_id'] == 'null') ? null : $filters['max_spotlight_id']
  957. );
  958. //Set the message feed type to HOME_FEED if it's the case:
  959. if(empty($the_filters['people_filter']) || $the_filters['people_filter'] == 'EVERYONE'){
  960. $this->s

Large files files are truncated, but you can click here to view the full file