protected function validateAndCleanNotificationData($notificationData) { // Very similar to the vB_Notification_Content, but has to skip the "channel" check since // VM's go to their own channel. Instead, there's a isVisitorMessage() check. $newData = parent::validateAndCleanNotificationData($notificationData); unset($notificationData); if (!isset($newData['sentbynodeid'])) { throw new Exception("Missing Notification Data: sentbynodeid"); } $nodeid = $newData['sentbynodeid']; $node = vB_Library::instance('node')->getNode($nodeid, false, true); // we need to get the full content, to ensure 'channeltype' is there. if (!isset($node['nodeid'])) { throw new Exception("Invalid Notification Data: sentbynodeid"); } if (!isset($node['setfor'])) { throw new Exception("Invalid Node Data: setfor"); } // only explictly specified content types are allowed to send notifications $topLevelContentTypes = array('Gallery' => 1, 'Link' => 1, 'Poll' => 1, 'Text' => 1, 'Video' => 1); $contenttypeclass = vB_Types::instance()->getContentTypeClass($node['contenttypeid']); if (!isset($topLevelContentTypes[$contenttypeclass])) { throw new Exception("Cannot send this notification for the node's content type."); } // Only allow visitor messages. /* // node lib's fetchClosureParent() does not seem functional for the 2nd param that content lib's isVisitorMessage() // relies on, VBV-14367. Let's just check the closure table directly for now. $contentLib = vB_Library::instance('Content_' . vB_Types::instance()->getContentTypeClass($node['contenttypeid'])); if (!$contentLib->isVisitorMessage($nodeid)) { throw new Exception("Not a visitor message."); } */ $vmChannel = vB_Library::instance('node')->fetchVMChannel(); $closureCheck = vB::getDbAssertor()->getRows('vBForum:closure', array('child' => $nodeid, 'parent' => $vmChannel)); if (empty($closureCheck)) { throw new Exception("Not a visitor message."); } // We're good if we got to this point. $newData['sentbynodeid'] = (int) $node['nodeid']; if (!isset($node['userid'])) { throw new Exception("Invalid Notification Data: sentbynodeid"); } $newData['sender'] = (int) $node['userid']; return $newData; }
protected final function validateAndCleanNotificationData($notificationData) { $newData = parent::validateAndCleanNotificationData($notificationData); unset($notificationData); if (!isset($newData['sender'])) { throw new Exception("Missing Notification Data: sender"); } // sender cannot be a guest, as guests cannot have relations with members ATM. if (empty($newData['sender'])) { throw new Exception("Invalid Notification Data: sender"); } $newData['sentbynodeid'] = NULL; /* If we could, we'd also check recipients here, but recipients are added later */ return $newData; }
protected function validateAndCleanNotificationData($notificationData) { $newData = parent::validateAndCleanNotificationData($notificationData); unset($notificationData); if (!isset($newData['sentbynodeid'])) { throw new Exception("Missing Notification Data: sentbynodeid"); } $nodeid = $newData['sentbynodeid']; $node = vB_Library::instance('node')->getNode($nodeid, false, true); // we need to get the full content, to ensure 'channeltype' is there. if (!isset($node['nodeid'])) { throw new Exception("Invalid Notification Data: sentbynodeid"); } // Don't send notification if it's not visible to a "regular" user. if (!($node['showpublished'] and $node['showapproved'])) { throw new Exception("Invalid Notification Data: showpublished or showapproved"); } // The sentbynodeid MUST BE A POLL TYPE $topLevelContentTypes = array('Poll' => 1); $contenttypeclass = vB_Types::instance()->getContentTypeClass($node['contenttypeid']); if (!isset($topLevelContentTypes[$contenttypeclass])) { throw new Exception("Cannot send this notification for the node's content type."); } // Let's restrict it to certain channel types, just in case we can create polls anywhere. // Keep this in sync with vB_Channel::$channelTypes $allowedChannelTypes = array('forum' => 1, 'blog' => 1, 'article' => 1, 'group' => 1); if (!isset($allowedChannelTypes[$node['channeltype']])) { throw new Exception("Cannot send this notification for the node's channel type."); } // We're good if we got to this point. $newData['sentbynodeid'] = (int) $node['nodeid']; // Sender must be specified when constructing this type. Set by parent::validateAndCleanNotificationData() if (!isset($newData['sender'])) { throw new Exception("Invalid Notification Data: sender"); } return $newData; }
/** * Validates the notification data, checks the context to see if we should send this * notification type, and throws exceptions if we should not or cannot send this notification * type. If all is okay, it may set additional notification data specific to this notification type. * * @param Array $notificationData * * @throws Exception() If for some reason this notification type cannot be sent given * the context data in $notificationData * * @access protected */ protected function validateAndCleanNotificationData($notificationData) { $newData = parent::validateAndCleanNotificationData($notificationData); unset($notificationData); if (!isset($newData['sentbynodeid'])) { throw new Exception("Missing Notification Data: sentbynodeid"); } $nodeid = $newData['sentbynodeid']; $node = vB_Library::instance('node')->getNode($nodeid, false, true); // we need to get the full content, to ensure 'channeltype' is there. if (!isset($node['nodeid'])) { throw new Exception("Invalid Notification Data: sentbynodeid"); } // Don't send notification if it's not visible to a "regular" user. if (!($node['showpublished'] and $node['showapproved'])) { throw new Exception("Invalid Notification Data: showpublished or showapproved"); } // only explictly specified content types are allowed to send notifications $topLevelContentTypes = array('Gallery' => 1, 'Link' => 1, 'Poll' => 1, 'Text' => 1, 'Video' => 1); $contenttypeclass = vB_Types::instance()->getContentTypeClass($node['contenttypeid']); if (!isset($topLevelContentTypes[$contenttypeclass])) { throw new Exception("Cannot send this notification for the node's content type."); } // Similar to above, but for channeltypes. Keep this in sync with vB_Channel::$channelTypes $allowedChannelTypes = array('forum' => 1, 'blog' => 1, 'article' => 1, 'group' => 1); if (!isset($allowedChannelTypes[$node['channeltype']])) { throw new Exception("Cannot send this notification for the node's channel type."); } // We're good if we got to this point. $newData['sentbynodeid'] = (int) $node['nodeid']; if (!isset($node['userid'])) { throw new Exception("Invalid Notification Data: sentbynodeid"); } $newData['sender'] = (int) $node['userid']; return $newData; }
public function triggerNotificationEvent($eventstring, $data = array(), $recipients = array()) { $events = $this->getNotificationEvents(); $types = $this->getNotificationTypes(); if (!isset($events[$eventstring])) { return; } /* Expecting $classname => event type */ foreach ($events[$eventstring] as $class => $type) { switch ($type) { case 'trigger': if (!isset($data['sentbynodeid']) and isset($data['nodeid'])) { $data['sentbynodeid'] = $data['nodeid']; } $notification = new $class($eventstring, $data, $recipients); $notificationData = $notification->getNotificationData(); if (empty($notificationData)) { continue 2; } $notificationData['typeid'] = $types[$notificationData['typename']]['typeid']; $notificationRecipients = $notification->getRecipients(); $recipientsCache = $notification->getCachedRecipientData(); // holds email, languageid, emailupdate etc that we already grabbed from user table. $aboutString = vB_Library::instance('content_privatemessage')->convertNotificationTypeToLegacyAboutString($notificationData['typename']); if ($aboutString == 'subscription') { // Required for emails. // TODO figure out how to get rid of this double call to follow API (this is also called in // vB_Notification_Content_GroupByStarter_Subscription) $apiResult = vB_Api::instanceInternal('follow')->getSubscribersForNotifications($notificationData['sentbynodeid']); $subscribers = $apiResult['subscribers']; } foreach ($notificationRecipients as $userid) { $notificationData['recipient'] = $userid; /* TODO: move emails generation into notification objects so we can get rid of this ugly block below */ $sendEmail = (!empty($aboutString) and isset($recipientsCache[$userid]['emailnotification']) and $recipientsCache[$userid]['emailnotification'] == 1); if ($sendEmail) { $emailData = array('userid' => $userid, 'about' => $aboutString, 'email' => $recipientsCache[$userid]['email'], 'username' => $recipientsCache[$userid]['username'], 'languageid' => $recipientsCache[$userid]['languageid'], 'contentnodeid' => $notificationData['sentbynodeid'], 'senderid' => $notificationData['sender']); if ($aboutString == 'subscription') { $emailData['subscriptionnodeid'] = reset($subscribers[$userid]['nodeid']); } } else { $emailData = array(); } // Queue up notifications & Emails if (empty($notificationData['lookupid'])) { $this->notificationQueue[] = $notificationData; if ($sendEmail) { $this->emailQueue[] = $emailData; } } else { // Only alphamerics and '_' are allowed in typenames, and the unique prefix in the // lookupid is json_encoded (so have the form of {"blah":"cool"}) so using _{$userid} // in the array key should prevent conflicts from super weird/creative custom type names. // I hope. $key = "_{$userid}" . vB_Notification::DELIM . $notificationData['lookupid']; if (isset($this->notificationQueue[$key])) { if ($notificationData['priority'] > $this->notificationQueue[$key]['priority']) { $this->notificationQueue[$key] = $notificationData; if ($sendEmail) { $this->emailQueue[$key] = $emailData; } } } else { $this->notificationQueue[$key] = $notificationData; if ($sendEmail) { $this->emailQueue[$key] = $emailData; } } } unset($notificationData['recipient'], $sendEmail, $emailData); } break; case 'update': $class::handleUpdateEvents($eventstring, $data); break; default: break; } } vB_Notification::clearMemory(); }
public static final function clearMemory() { // Required for unit testing... Usually we can probably ignore user info changes // that happen *within* a pageload / session, but unit tests have a single PHP session // that lasts for multiple "user sessions". // This is called by vB_Library_Notification->triggerNotificationEvent(). self::$recipientsInfoCache = array(); }