protected function sendLegacyEmailNotification($data) { $options = vB::getDatastore()->getValue('options'); if (isset($options['enableemail']) and !$options['enableemail']) { return; // email notifications are globally disabled } /* * Save some data for this page load. Since we can potentially fetch a bunch of the same * data over and over if multiple users are getting notifications about the same content * that was just created, let's get rid of the redundant calls when this function is called * in a loop. * Todo: we should write a bulk-send-email-notification function * * I can't actually think of a case where a single content creation that triggers a number * of email notifications will have different contentnodeid in its group of notifications, * but let's just be safe and allow for that possibility. */ static $recipientIndependentDataArray; $contentnodeid = isset($data['contentnodeid']) ? $data['contentnodeid'] : 0; if (!empty($contentnodeid) and empty($recipientIndependentDataArray[$contentnodeid])) { //we need to load this using the correct library class or things get weird. //note that if we load it from cache here it will be in the local memory cache //when we load the full content and we won't do it twice. $cached = vB_Library_Content::fetchFromCache(array($contentnodeid), vB_Library_Content::CACHELEVEL_FULLCONTENT); if (isset($cached['found'][$contentnodeid])) { $contenttypeid = $cached['found'][$contentnodeid]['contenttypeid']; } else { $row = $this->assertor->getRow('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::COLUMNS_KEY => array('contenttypeid'), 'nodeid' => $contentnodeid)); $contenttypeid = $row['contenttypeid']; } $contentLib = vB_Library_Content::getContentLib($contenttypeid); $currentNode = $contentLib->getContent($contentnodeid); $currentNode = $currentNode[$contentnodeid]; /* * These data are static & independent of the recipient of this message, assuming that * we're not trying to hide any data. If we are going to check permissions for each * recipient, we should probably check view perms & remove from the recipients list * BEFORE we ever get to this function, and just not send them a notification instead of * hiding data. */ $recipientIndependentDataArray[$contentnodeid] = array('authorname' => $currentNode['userinfo']['username'], 'nodeurl' => vB5_Route::buildUrl('node|fullurl', array('nodeid' => $contentnodeid)), 'previewtext' => vB_String::getPreviewText($currentNode['rawtext']), 'nodeid' => $currentNode['nodeid'], 'starter' => $currentNode['starter'], 'startertitle' => isset($currentNode['startertitle']) ? $currentNode['startertitle'] : '', 'parentid' => $currentNode['parentid'], 'channeltitle' => $currentNode['channeltitle'], 'channeltype' => $currentNode['channeltype']); } // additional data used for subscription notifications if (isset($data['subscriptionnodeid'])) { $subId = $data['subscriptionnodeid']; if (!isset($recipientIndependentDataArray[$contentnodeid]['add_sub_data'][$subId])) { //we need to load this using the correct library class or things get weird. //note that if we load it from cache here it will be in the local memory cache //when we load the full content and we won't do it twice. $cached = vB_Library_Content::fetchFromCache(array($subId), vB_Library_Content::CACHELEVEL_FULLCONTENT); if (isset($cached['found'][$subId])) { $contenttypeid = $cached['found'][$subId]['contenttypeid']; } else { $row = $this->assertor->getRow('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::COLUMNS_KEY => array('contenttypeid'), 'nodeid' => $subId)); $contenttypeid = $row['contenttypeid']; } if ($contenttypeid) { $contentLib = vB_Library_Content::getContentLib($contenttypeid); $subbedNode = $contentLib->getContent($subId); $subbedNode = $subbedNode[$subId]; $channelTypeId = vB_Types::instance()->getContentTypeID('vBForum_Channel'); if ($subbedNode['contenttypeid'] == $channelTypeId) { $nodetype = 'channel'; } else { $nodetype = 'post'; } $recipientIndependentDataArray[$contentnodeid]['add_sub_data'][$subId] = array('title' => $subbedNode['title'], 'nodetype' => $nodetype); } } // We only expect channeltype_forum, channeltype_article, channeltype_blog, channeltype_group, but // a channeltype_<> phrase for each of vB_Channel::$channelTypes should exist, so we can do this $channeltype = "channeltype_" . $recipientIndependentDataArray[$contentnodeid]['channeltype']; $nodetype = $recipientIndependentDataArray[$contentnodeid]['add_sub_data'][$subId]['nodetype']; // While phrases are dependent on languageid, it's not really recipient or userid // dependent and is shared/static among different recipients, so let's keep track // of them. if (!isset($phrases[$data['languageid']])) { $phrases[$data['languageid']] = vB_Api::instanceInternal('phrase')->fetch(array($channeltype, $nodetype), $data['languageid']); } } // keep track of the about strings that are for "content" notifications $temporary = array(vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_REPLY, vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_COMMENT, vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_THREADCOMMENT, vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_SUBSCRIPTION, vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_USERMENTION); $contentNotificationAboutStrings = array(); foreach ($temporary as $aboutString) { $contentNotificationAboutStrings[$aboutString] = $aboutString; } if ($data['about'] == vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_VM) { // A VM should have only 1 recipient, so no good reason to cache this like other URLs. Also // VMs don't seem to work with the /node/ route, so we can't rely on that. $vmURL = vB5_Route::buildUrl('visitormessage|fullurl', array('nodeid' => $contentnodeid)); $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases('visitormessage', array($data['username'], $recipientIndependentDataArray[$contentnodeid]['authorname'], $vmURL, $recipientIndependentDataArray[$contentnodeid]['previewtext'], $options['bbtitle']), array(), $data['languageid']); } elseif ($data['about'] == vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_VOTE) { // Vote notifications have their own section because their phrases aren't in the same format as the others // Since a vote doesn't have a node associated with it, we don't have a "currentnode" data. // Note that the poll library sets aboutid & contentid both to the nodeid of the poll-post $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases('vote', array($data['username'], vB_Api::instanceInternal('user')->fetchUserName(vB::getCurrentSession()->get('userid')), $recipientIndependentDataArray[$contentnodeid]['nodeurl'], $options['bbtitle']), array($recipientIndependentDataArray[$contentnodeid]['startertitle']), $data['languageid']); } elseif (isset($contentNotificationAboutStrings[$data['about']])) { // Normally the subject would contain the topic title, but if it's a subscription let's pass in the // title of the actual subscription node whether it's a channel, thread, blog etc. $emailSubjectVars = array($recipientIndependentDataArray[$contentnodeid]['startertitle']); // Since subscription email subjects don't have a title, we'll include the title in // the email body as a 7th variable. $emailBodyVars = array($data['username'], $recipientIndependentDataArray[$contentnodeid]['authorname'], $recipientIndependentDataArray[$contentnodeid]['nodeurl'], $recipientIndependentDataArray[$contentnodeid]['previewtext'], $options['bbtitle'], $options['frontendurl'] . '/member/' . $data['userid'] . '/subscriptions'); // blog & social groups have special phrases, unless it's a subscription notification or a user mention. // If the latter, just send the generic subscription or user mention email. // As of 5.1.4, we do not expect to hit this block for blogs, because the people who *would* // receive this (the blog poster) are automatically subscribed to the blog channel, and // subscription notifications always trump other types of notifications. See VBV-13466 if ($recipientIndependentDataArray[$contentnodeid]['channeltype'] == 'blog' and $data['about'] != vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_SUBSCRIPTION and $data['about'] != vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_USERMENTION) { $mailPhrase = 'comment_blogentry'; } else { if ($recipientIndependentDataArray[$contentnodeid]['channeltype'] == 'group' and $data['about'] != vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_SUBSCRIPTION and $data['about'] != vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_USERMENTION) { $mailPhrase = 'comment_grouptopic'; } else { switch ($data['about']) { case vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_REPLY: $mailPhrase = 'reply_thread'; break; case vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_COMMENT: $mailPhrase = 'comment_post'; break; case vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_THREADCOMMENT: $mailPhrase = 'comment_thread'; break; case vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_SUBSCRIPTION: $mailPhrase = 'subscribed_thread'; // $subId, $nodetype, $channeltype aer set above as long as 'subscriptionnodeid' was passed in. if (!empty($subId)) { // A new post in your {2} {3} subscription: {1} $emailSubjectVars = array($recipientIndependentDataArray[$contentnodeid]['add_sub_data'][$subId]['title'], $phrases[$data['languageid']][$channeltype], $phrases[$data['languageid']][$nodetype]); // Since we removed the starter title from the subject, add it to the message. $emailBodyVars[] = $recipientIndependentDataArray[$contentnodeid]['startertitle']; } break; case vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_USERMENTION: $mailPhrase = 'usermention_post'; // subject: <username> mentioned you in <content item title> $emailSubjectVars = array($recipientIndependentDataArray[$contentnodeid]['authorname'], $recipientIndependentDataArray[$contentnodeid]['startertitle']); break; default: if ($recipientIndependentDataArray[$contentnodeid]['starter'] == $recipientIndependentDataArray[$contentnodeid]['parentid']) { $mailPhrase = 'reply_thread'; } else { $mailPhrase = 'reply_post'; } break; } } } $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases($mailPhrase, $emailBodyVars, $emailSubjectVars, $data['languageid']); } elseif ($data['about'] == vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_RATE) { $node = $recipientIndependentDataArray[$contentnodeid]; // It doesn't make sense to call blog & article starters as "threads", so just go with "post" if ($node['nodeid'] == $node['starter'] and ($node['channeltype'] == 'forum' or $node['channeltype'] == 'group')) { $mailPhrase = 'like_thread'; } else { $mailPhrase = 'like_post'; } $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases($mailPhrase, array($data['username'], vB_Api::instanceInternal('user')->fetchUserName($data['senderid']), $node['nodeurl'], $options['bbtitle']), array($options['bbtitle'])); } elseif ($data['about'] == vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_FOLLOW) { // the use of vB5_Route::buildUrl() below should be ok performant wise, because we're not expecting to send a bulk email for this // notification type. We should avoid hacky hacks (see above in the content notification section for avoiding vB5_Route::buildUrl() // to each recipient's subscription page) when forgivable. $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases('follow_approve', array($data['username'], vB_Api::instanceInternal('user')->fetchUserName($data['senderid']), vB5_Route::buildUrl('profile|fullurl', array('userid' => $data['senderid'], 'tab' => 'subscribed')), $options['bbtitle']), array($options['bbtitle'])); } elseif ($data['about'] == vB_Library_Content_Privatemessage::NOTIFICATION_TYPE_FOLLOWING) { /* Per dev chat discussion, this never existed. Since there doesn't seem to be a strong customer request for this, I'm gonna leave it out. I'm leaving this section in so that it's trivial to add in the future. */ $maildata = array(); } else { // We don't know how to handle this. $maildata = array(); } if (!empty($data['email']) and !empty($maildata)) { // Send the email vB_Mail::vbmail($data['email'], $maildata['subject'], $maildata['message'], false); } }
/** * Publish this node to facebook feed * * @param array $node -- the $node array * @param boolean $explicit -- is this message explicitly shared? See * https://developers.facebook.com/docs/opengraph/using-actions/v2.2#explicitsharing * @return bool */ public function publishNode($node, $explicit) { if (!$this->userIsLoggedIn()) { return false; } $options = vB::getDatastore()->getValue('options'); // is the node published/visible/public in vB? if ($node['showpublished'] != 1 or $node['approved'] != 1) { return false; } $isStarter = $node['nodeid'] == $node['starter']; // can new discussion, photo, link, poll etc be published? if ($isStarter and !$options['fbfeednewthread']) { return false; } // can replies to discussion, photo, link, poll etc be published? if (!$isStarter and !$options['fbfeedpostreply']) { return false; } // get node URL $extra = array(); $anchor = ''; if (!$isStarter) { $extra['p'] = $node['nodeid']; $anchor = 'post' . $node['nodeid']; } $nodeUrl = vB5_Route::buildUrl($node['routeid'] . '|fullurl', $node, $extra, $anchor); // $message should *really* be set by the plaintext parser, which hasn't been // brought over from vB4 $message = vB_String::stripBbcode($node['content']['rawtext'], false, false, false, true); $previewtext = vB_String::getPreviewText($message); //we need a title otherwise it just doesn't look good $title = $node['title']; if (!$title and !empty($node['content']['startertitle'])) { $title = $node['content']['startertitle']; } $image_url = ''; if (!empty($node['content']['previewimage'])) { $image_url = $node['content']['previewimage']; if (is_numeric($image_url)) { $image_url = $options['frontendurl'] . "/filedata/fetch?id={$image_url}&type=thumb"; } } return $this->publishMessage('', $title, $nodeUrl, $previewtext, $image_url, $explicit); }
/** * This lists messages for current user * * @param mixed- can pass sort direction, type, page, perpage, or folderid. * @return mixed - array-includes folderId, title, quantity not read. Also 'page' is array of node records for page 1. */ public function listSpecialPrivateMessages($data = array()) { $params = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_METHOD, 'userid' => vB::getCurrentSession()->get('userid')); foreach (array('sortDir', vB_dB_Query::PARAM_LIMITSTART, 'contenttypeid', 'folderid', 'userid') as $param) { if (isset($data[$param])) { $params[$param] = $data[$param]; } } if (isset($params['perpage']) and intval($params['perpage'])) { $params[vB_dB_Query::PARAM_LIMIT] = 50; } else { $params[vB_dB_Query::PARAM_LIMIT] = 50; } if (empty($params[vB_dB_Query::PARAM_LIMITSTART])) { $params[vB_dB_Query::PARAM_LIMITSTART] = 1; } $messageQry = $this->assertor->assertQuery('vBForum:listSpecialMessages', $params); $messages = array(); if ($messageQry and is_object($messageQry)) { foreach ($messageQry as $message) { $messages[$message['nodeid']] = $message; } if (!empty($messages)) { $userApi = vB_Api::instanceInternal('user'); foreach ($messages as $key => $message) { $messages[$key]['senderAvatar'] = $userApi->fetchAvatar($message['userid']); if (empty($message['previewtext'])) { $messages[$key]['previewtext'] = vB_String::getPreviewText($message['rawtext']); } } } } return $messages; }
/** * Adds a new node. * * @param mixed Array of field => value pairs which define the record. * @param array Array of options for the content being created. * Understands skipTransaction, skipFloodCheck, floodchecktime, skipDupCheck, skipNotification, nl2br, autoparselinks. * - nl2br: if TRUE, all \n will be converted to <br /> so that it's not removed by the html parser (e.g. comments). * @param bool Convert text to bbcode * * @return mixed array with nodeid (int), success (bool), cacheEvents (array of strings), nodeVals (array of field => value), attachments (array of attachment records). */ public function add($data, array $options = array(), $convertWysiwygTextToBbcode = true) { //Store this so we know whether we should call afterAdd() $skipTransaction = !empty($options['skipTransaction']); $vboptions = vB::getDatastore()->getValue('options'); $reportemail = ($vboptions['enableemail'] and $vboptions['rpemail']); $data['reportnodeid'] = intval($data['reportnodeid']); // Build node title based on reportnodeid if (!$data['reportnodeid']) { throw new vB_Exception_Api('invalid_report_node'); } $data['parentid'] = $this->ReportChannel; if (empty($data['title'])) { $reportnode = $this->nodeApi->getNodeFullContent($data['reportnodeid']); $reportnode = $reportnode[$data['reportnodeid']]; $phraseapi = vB_Api::instanceInternal('phrase'); if ($reportnode['nodeid'] == $reportnode['starter']) { // Thread starter $data['title'] = $reportnode['title']; } elseif ($reportnode['parentid'] == $reportnode['starter']) { $phrases = $phraseapi->fetch(array('reply_to')); $data['title'] = $phrases['reply_to'] . ' ' . $reportnode['startertitle']; } else { $phrases = $phraseapi->fetch(array('comment_in_a_topic')); $data['title'] = $phrases['comment_in_a_topic'] . ' ' . $reportnode['startertitle']; } } $result = parent::add($data, $options, $convertWysiwygTextToBbcode); if (!$skipTransaction) { //The child classes that have their own transactions all set this to true so afterAdd is always called just once. $this->afterAdd($result['nodeid'], $data, $options, $result['cacheEvents'], $result['nodeVals']); } // send an email if ($reportemail) { $reporterInfo = vB::getCurrentSession()->fetch_userinfo(); $nodeLib = vB_Library::instance('node'); $moderators = array(); $moderatorUsernames = ''; // Get moderators on the reported node $moderatorsArray = $nodeLib->getNodeModerators($reportnode['nodeid']); foreach ($moderatorsArray as $moderator) { $moderators[$moderator['userid']] = $moderator['userid']; } if ($vboptions['rpemail'] == 2) { // Fetch admins and super moderators $allmoderators = $nodeLib->getForumSupermoderatorsAdmins($moderators); foreach ($allmoderators as $moderator) { $moderators[$moderator['userid']] = $moderator['userid']; } } // get user info foreach ($moderators as $moderatorid => $moderator) { $moderators[$moderatorid] = vB_Library::instance('user')->fetchUserinfo($moderatorid); $moderatorUsernames .= $moderators[$moderatorid]['username'] . ', '; } // Compose the email if ($reportnode['starter'] == $reportnode['nodeid']) { $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases('reportpost_newthread', array(vB5_Route::buildUrl('profile|fullurl', array('userid' => $reporterInfo['userid'])), $reporterInfo['username'], $data['rawtext'], vB5_Route::buildUrl($reportnode['routeid'] . '|fullurl', array('nodeid' => $reportnode['nodeid'], 'title' => $reportnode['title'])), $reportnode['title'], vB::getDatastore()->getOption('bbtitle'), substr($moderatorUsernames, 0, -2), $reportnode['authorname'], vB5_Route::buildUrl('profile|fullurl', array('userid' => $reportnode['userid'])), vB_String::getPreviewText($reportnode['rawtext'])), array($reporterInfo['username'])); } else { $maildata = vB_Api::instanceInternal('phrase')->fetchEmailPhrases('reportpost', array($reporterInfo['username'], $reporterInfo['email'], $reportnode['title'], vB5_Route::buildUrl($reportnode['routeid'] . '|fullurl', array('nodeid' => $reportnode['starter'], 'userid' => $reportnode['starteruserid'], 'username' => $reportnode['starterauthorname'], 'innerPost' => $reportnode['nodeid'], 'innerPostParent' => $reportnode['parentid'])), $reportnode['startertitle'], vB5_Route::buildUrl($reportnode['routeid'] . '|fullurl', array('nodeid' => $reportnode['starter'], 'title' => $reportnode['startertitle'])), $data['rawtext']), array(vB::getDatastore()->getOption('bbtitle'))); } // Send out the emails foreach ($moderators as $moderator) { if (!empty($moderator['email'])) { vB_Mail::vbmail($moderator['email'], $maildata['subject'], $maildata['message'], false); } } } return $result; }
/** * Adds a new node. * * @param mixed Array of field => value pairs which define the record. * -- htmlstate * -- parentid * -- disable_bbcode * -- rawtext * -- and others * @param array Array of options for the content being created * Understands skipTransaction, skipFloodCheck, floodchecktime, skipDupCheck, skipNotification, nl2br, autoparselinks. * - nl2br: if TRUE, all \n will be converted to <br /> so that it's not removed by the html parser (e.g. comments). * @param bool Convert text to bbcode * * @return mixed array with nodeid (int), success (bool), cacheEvents (array of strings), nodeVals (array of field => value), attachments (array of attachment records). */ public function add($data, array $options = array(), $convertWysiwygTextToBbcode = true) { //Store this so we know whether we should call afterAdd() $skipTransaction = !empty($options['skipTransaction']); // html permission already checked in the api if (isset($data['htmlstate']) and $data['htmlstate'] == 'on' and isset($data['disable_bbcode']) and $data['disable_bbcode'] == 1) { // article 'static html' type $convertWysiwygTextToBbcode = false; if (isset($options['nl2br'])) { $options['nl2br'] = false; } } if (empty($data['parentid'])) { throw new Exception('need_parent_node'); } // Get parents for cleaning cache and checking permissions $parents = vB_Library::instance('node')->getParents($data['parentid']); $parents = array_reverse($parents); // convert to bbcode for saving if (isset($data['rawtext']) and !empty($data['rawtext'])) { // Converts new lines when CKEditor is not in use (plain text area) VBV-11279 // also used for the mobile app. if (isset($options['nl2br']) and $options['nl2br']) { $data['rawtext'] = nl2br($data['rawtext']); } if ($convertWysiwygTextToBbcode) { $channelType = vB_Types::instance()->getContentTypeId('vBForum_Channel'); // check if we can autoparselinks $options['autoparselinks'] = true; foreach ($parents as $parent) { // currently only groups and blogs seem to disallow this if (($parent['contenttypeid'] == $channelType and vB_Api::instanceInternal('socialgroup')->isSGNode($parent['nodeid']) or vB_Api::instanceInternal('blog')->isBlogNode($parent['nodeid'])) and $channelOptions = vB_Library::instance('node')->getNodeOptions($parent['nodeid'])) { $options['autoparselinks'] = $channelOptions['autoparselinks']; } } $data['rawtext'] = vB_Api::instanceInternal('bbcode')->convertWysiwygTextToBbcode($data['rawtext'], $options); if (empty($data['description'])) { $data['description'] = vB_String::getPreviewText($this->parseAndStrip($data['rawtext'])); } else { $data['description'] = vB_String::getPreviewText($this->parseAndStrip($data['description'])); } } } else { if (empty($data['description'])) { $data['description'] = isset($data['title']) ? vB_String::getPreviewText($this->parseAndStrip($data['title'])) : ''; } else { $data['description'] = vB_String::getPreviewText($this->parseAndStrip($data['description'])); } } if (empty($data['userid'])) { $user = vB::getCurrentSession()->fetch_userinfo(); $data['authorname'] = $user['username']; $userid = $data['userid'] = $user['userid']; } else { $userid = $data['userid']; if (empty($data['authorname'])) { $data['authorname'] = vB_Api::instanceInternal('user')->fetchUserName($userid); } $user = vB_Api::instance('user')->fetchUserinfo($userid); } $userContext = vB::getUserContext($userid); $isSpam = false; $skipSpam = ($userContext->getChannelPermission('forumpermissions2', 'exemptfromspamcheck', $data['parentid']) or $userContext->getChannelPermission('moderatorpermissions', 'canmoderateposts', $data['parentid']) or isset($options['skipSpamCheck']) and $options['skipSpamCheck'] and vB::getUserContext()->getChannelPermission('moderatorpermissions', 'canmoderateposts', $data['parentid'])); //run the spam check. if (!$skipSpam and $this->spamType !== false) { if (empty($this->akismet)) { $this->akismet = vB_Akismet::instance(); } $params = array('comment_type' => 'user_post', 'comment_content' => $data['rawtext']); $params['comment_author'] = $data['authorname']; $params['comment_author_email'] = $user['email']; $result = $this->akismet->verifyText($params); if ($result == 'spam') { $data['approved'] = 0; $data['showapproved'] = 0; $isSpam = true; } } if (!$skipSpam and !$isSpam and $blacklist = trim($this->options['vb_antispam_badwords'])) { $badwords = preg_split('#\\s+#', $blacklist, -1, PREG_SPLIT_NO_EMPTY); if (str_replace($badwords, '', strtolower($data['rawtext'])) != strtolower($data['rawtext'])) { $data['approved'] = 0; $data['showapproved'] = 0; $isSpam = true; } } if (!$skipSpam and !$isSpam) { preg_match_all('#\\[(url|email).*\\[/(\\1)\\]#siU', $data['rawtext'], $matches); if (isset($matches[0]) and count($matches[0]) > intval($this->options['vb_antispam_maxurl'])) { $data['approved'] = 0; $data['showapproved'] = 0; $isSpam = true; } } //We need a copy of the data, maybe $updates = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED); //Set the "hasvideo" value; if (!empty($data['rawtext'])) { $filter = '~\\[video.*\\[\\/video~i'; $matches = array(); $count = preg_match_all($filter, $data['rawtext'], $matches); if ($count > 0) { $data['hasvideo'] = 1; } else { $data['hasvideo'] = 0; } } //publishdate is set in the parent class and api. If not set in data, it'll be set to vB::getRequest()->getTimeNow() in parent::add() if (isset($data['publishdate'])) { $updates['lastcontent'] = $data['publishdate']; } else { $updates['lastcontent'] = vB::getRequest()->getTimeNow(); } if (isset($data['userid'])) { $updates['lastauthorid'] = $data['userid']; } else { $updates['lastauthorid'] = $data['userid'] = vB::getCurrentSession()->get('userid'); } if (isset($data['authorname'])) { $updates['lastcontentauthor'] = $data['authorname']; } else { $author = $this->assertor->getRow('user', array(vB_Db_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'userid' => $data['userid'])); $data['authorname'] = $author['username']; $updates['lastcontentauthor'] = $author['username']; } $published = $this->isPublished($data); try { if (!$skipTransaction) { $this->assertor->beginTransaction(); } $options['skipTransaction'] = true; $results = parent::add($data, $options); $newNode = $this->getFullContent($results['nodeid']); $newNode = array_pop($newNode); // Obtain and set generic conversation route $conversation = $this->getConversationParent($results['nodeid']); $routeid = vB_Api::instanceInternal('route')->getChannelConversationRoute($conversation['parentid']); $this->assertor->update('vBForum:node', array('routeid' => $routeid), array('nodeid' => $results['nodeid'])); if (!$skipTransaction) { $this->assertor->commitTransaction(); } } catch (exception $e) { if (!$skipTransaction) { $this->assertor->rollbackTransaction(); } throw $e; } //set the last post and count data. /* We do something similar (vBForum:fixNodeLast) that ends up affect parent last data in the parent add(), * downstream of updateParentCounts(). We should probably refactor to just do it in one place. I tried to * comment this section out, but the node test told me that they're not *quite* the same and this is * necessary. */ $approved = (isset($data['showapproved']) ? $data['showapproved'] : true and isset($data['approved']) ? $data['approved'] : true); if ($published and $approved and (!isset($options['skipUpdateLastContent']) or !$options['skipUpdateLastContent'])) { $updates['nodeid'] = $results['nodeid']; $updates['lastcontentid'] = $results['nodeid']; $parentids = array(); foreach ($parents as $node) { $parentids[] = $node['nodeid']; } $updates['parentlist'] = $parentids; $this->qryAfterAdd[] = array('definition' => 'vBForum:setLastDataParentList', 'data' => $updates); } if (!$skipTransaction) { //The child classes that have their own transactions all set this to true so afterAdd is always called just once. $this->afterAdd($results['nodeid'], $data, $options, $results['cacheEvents'], $results['nodeVals']); } if ($isSpam) { $this->assertor->assertQuery('spamlog', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERTIGNORE, 'nodeid' => $results['nodeid'])); } $cachedNodes = array($results['nodeid']); foreach ($parents as $node) { $cachedNodes[] = $node['nodeid']; } $this->nodeApi->clearCacheEvents($cachedNodes); $this->nodeApi->clearCacheEvents(array($results['nodeid'], $data['parentid'])); return $results; }
/** * Gets the preview for the messages * * @return mixed array of record-up to five each messages, then requests, then notifications */ public function previewMessages() { if (!$this->canUsePmSystem()) { throw new vB_Exception_Api('not_logged_no_permission'); } $userid = vB::getCurrentSession()->get('userid'); $this->library->checkFolders($userid); $folders = $this->library->fetchFolders($userid); $exclude = array($folders['systemfolders'][vB_Library_Content_Privatemessage::TRASH_FOLDER], $folders['systemfolders'][vB_Library_Content_Privatemessage::NOTIFICATION_FOLDER]); $lastnodeidsQry = $this->assertor->getRows('vBForum:lastNodeids', array('userid' => $userid, 'excludeFolders' => $exclude)); // since the above query might not return anything, if there are no privatemessages for the user, add a -1 to prevent // the qryResults query from breaking $lastnodeids = array(-1); foreach ($lastnodeidsQry as $lastnode) { $lastnodeids[] = $lastnode['nodeid']; } $ignoreUsersQry = $this->assertor->getRows('vBForum:getIgnoredUserids', array('userid' => $userid)); $ignoreUsers = array(-1); foreach ($ignoreUsersQry as $ignoreUser) { $ignoreUsers[] = $ignoreUser['userid']; } $qryResults = $this->assertor->assertQuery('vBForum:pmPreview', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'userid' => $userid, 'ignoreUsers' => $ignoreUsers, 'excludeFolders' => $exclude, 'nodeids' => $lastnodeids)); $results = array('message' => array('count' => 0, 'title' => vB_Phrase::fetchSinglePhrase('messages'), 'folderid' => 0, 'messages' => array()), 'request' => array('count' => 0, 'title' => vB_Phrase::fetchSinglePhrase('requests'), 'folderid' => 0, 'messages' => array()), 'notification' => array('count' => 0, 'title' => vB_Phrase::fetchSinglePhrase('notifications'), 'folderid' => 0, 'messages' => array())); $messageIds = array(); $nodeIds = array(); $userIds = array(); $userApi = vB_Api::instanceInternal('user'); $receiptDetail = vB::getUserContext()->hasPermission('genericpermissions', 'canseewholiked'); $needLast = array(); if ($qryResults->valid()) { foreach ($qryResults as $result) { if (empty($result['previewtext'])) { $result['previewtext'] = vB_String::getPreviewText($result['rawtext']); } if ($result['titlephrase'] == 'messages') { $messageIds[] = $result['nodeid']; } else { $nodeIds[] = $result['nodeid']; } // privatemessage_requestdetail template requires you to pass back the phrase name for requests. // See listRequests() for more details if ($result['msgtype'] == 'request') { // remove starting sg_ and ending _to from the about string $cleanAboutStr = preg_replace('/(^sg_)?|(_to$)?/', '', $result['about']); $result['phrasename'] = 'received_' . $cleanAboutStr . '_request_from_x_link_y'; // grab channel request types $channelRequests = $this->library->getChannelRequestTypes(); // append correct suffix for channel requests if (in_array($result['about'], $channelRequests)) { // should have a "_to_channel_z" (take request) or "_for_channel_z" (grant request) appended if (strpos($result['about'], '_to') !== false) { $result['phrasename'] .= '_to_channel_z'; } else { $result['phrasename'] .= '_for_channel_z'; } } } $result['senderAvatar'] = $userApi->fetchAvatar($result['userid']); $result['recipients'] = array(); $result['otherRecipients'] = 0; $result['responded'] = 0; $results[$result['msgtype']]['messages'][$result['nodeid']] = $result; $results[$result['msgtype']]['count']++; $userIds[] = $result['userid']; if (intval($result['lastauthorid'])) { $userIds[] = $result['lastauthorid']; } if (!$results[$result['msgtype']]['folderid']) { $results[$result['msgtype']]['folderid'] = $result['folderid']; } // set recipients needed if ($result['msgtype'] == 'message') { if (empty($result['lastauthorid']) or $result['lastauthorid'] == $userid) { $needLast[] = $result['nodeid']; } } } // @TODO check for a way to implement a generic protected library method to fetch recipients instead of cloning code through methods. // drag the needed info if (!empty($needLast)) { $needLast = array_unique($needLast); $neededUsernames = $this->assertor->assertQuery('vBForum:getPMLastAuthor', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'nodeid' => $needLast, 'userid' => $userid)); foreach ($neededUsernames as $username) { if (isset($results['message']['messages'][$username['nodeid']])) { $results['message']['messages'][$username['nodeid']]['lastcontentauthor'] = $username['username']; $results['message']['messages'][$username['nodeid']]['lastauthorid'] = $username['userid']; } } } //Now we need to sort out the other recipients for this message. $recipients = array(); if (!empty($nodeIds)) { $recipientQry = $this->assertor->assertQuery('vBForum:getPMRecipients', array('nodeid' => array_unique($nodeIds), 'userid' => $userid)); foreach ($recipientQry as $recipient) { $recipients[$recipient['nodeid']][$recipient['userid']] = $recipient; } } $messageRecipients = array(); if (!empty($messageIds)) { $recipientsInfo = $this->assertor->assertQuery('vBForum:getPMRecipientsForMessage', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_STORED, 'nodeid' => $messageIds)); $recipients = array(); if (!empty($recipientsInfo)) { foreach ($recipientsInfo as $recipient) { if (isset($results['message']['messages'][$recipient['starter']])) { if ($recipient['userid'] == $userid) { if (empty($results['message']['messages'][$recipient['starter']]['included'])) { $results['message']['messages'][$recipient['starter']]['included'] = true; } continue; } else { if ($results['message']['messages'][$recipient['starter']]['lastcontentauthor'] == $recipient['username']) { continue; } } if (!isset($results['message']['messages'][$recipient['starter']]['recipients'][$recipient['userid']])) { $results['message']['messages'][$recipient['starter']]['recipients'][$recipient['userid']] = $recipient; } } } } } //Collect the user info. Doing it this way we get a lot of info in one query. $userQuery = $this->assertor->assertQuery('user', array('userid' => array_unique($userIds))); $userInfo = array(); $userApi = vB_Api::instanceInternal('user'); foreach ($userQuery as $userRecord) { //some information we shouldn't pass along. foreach (array('token', 'scheme', 'secret', 'coppauser', 'securitytoken_raw', 'securitytoken', 'logouthash', 'fbaccesstoken', 'passworddate', 'parentemail', 'logintype', 'ipaddress', 'passworddate', 'referrerid', 'ipoints', 'infractions', 'warnings', 'infractiongroupids', 'infractiongroupid') as $field) { unset($userRecord[$field]); } $userRecord['avatar'] = $userApi->fetchAvatar($userRecord['userid'], true, $userRecord); $userInfo[$userRecord['userid']] = $userRecord; } //Now we need to scan the results list and assign the other recipients. foreach ($results as $key => $folder) { foreach ($folder['messages'] as $msgkey => $message) { if ($message['titlephrase'] == 'messages') { // set the first recipient if (!empty($message['lastcontentauthor']) and !empty($message['lastauthorid']) and $message['lastauthorid'] != $userid) { $results[$key]['messages'][$msgkey]['firstrecipient'] = array('userid' => $message['lastauthorid'], 'username' => $message['lastcontentauthor']); } else { if (!empty($message['recipients'])) { $firstrecip = reset($message['recipients']); $results[$key]['messages'][$msgkey]['firstrecipient'] = $firstrecip; unset($results[$key]['messages'][$msgkey]['recipients'][$firstrecip['userid']]); } } $results[$key]['messages'][$msgkey]['otherRecipients'] = count($results[$key]['messages'][$msgkey]['recipients']); } else { if (!empty($recipients[$message['nodeid']])) { $results[$key]['messages'][$msgkey]['recipients'] = $recipients[$message['nodeid']]; $results[$key]['messages'][$msgkey]['otherRecipients'] = count($recipients[$message['nodeid']]); $results[$key]['messages'][$msgkey]['userinfo'] = $userInfo[$message['userid']]; } } if ($message['lastauthorid']) { $results[$key]['messages'][$msgkey]['lastauthor'] = $userInfo[$message['lastauthorid']]['username']; $results[$key]['messages'][$msgkey]['lastcontentauthorid'] = $message['lastauthorid']; $results[$key]['messages'][$msgkey]['lastcontentavatar'] = $userInfo[$message['lastauthorid']]['avatar']; } } if (empty($message['previewtext'])) { $results[$key]['previewtext'] = vB_String::getPreviewText($message['rawtext']); } } } $channelRequests = $this->library->getChannelRequestTypes(); $nodeIds = array(); foreach ($results['request']['messages'] as $message) { if (in_array($message['about'], $channelRequests)) { $nodeIds[] = $message['aboutid']; } } if (!empty($nodeIds)) { $nodesInfo = vB_Library::instance('node')->getNodes($nodeIds); $arrayNodeInfo = array(); foreach ($nodesInfo as $node) { $arrayNodeInfo[$node['nodeid']] = array('title' => $node['title'], 'routeid' => $node['routeid']); } foreach ($results['request']['messages'] as $key => &$val) { if (isset($arrayNodeInfo[$val['aboutid']])) { $val['abouttitle'] = $arrayNodeInfo[$val['aboutid']]['title']; $val['aboutrouteid'] = $arrayNodeInfo[$val['aboutid']]['routeid']; } } } // add notifications $params = array('showdetail' => $receiptDetail, 'perpage' => 5, 'page' => 1, 'sortDir' => "DESC"); $notifications = vB_Library::instance('notification')->fetchNotificationsForCurrentUser($params); $results['notification']['messages'] = $notifications; $results['notification']['count'] = count($notifications); $results['notification']['folderid'] = $folders['systemfolders'][vB_Library_Content_Privatemessage::NOTIFICATION_FOLDER]; return $results; }