/** * Add photo record * * @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. * * @return mixed array with nodeid (int), success (bool), cacheEvents (array of strings), nodeVals (array of field => value). */ public function add($data, array $options = array()) { //Store this so we know whether we should call afterAdd() $skipTransaction = !empty($options['skipTransaction']); $options += array('skipDupCheck' => true); if (empty($data['filedataid'])) { throw new vB_Exception_Api('incomplete_data'); } 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'])) { $user = vB_Api::instanceInternal('user')->fetchUserName($userid); $data['authorname'] = $user; } } try { if (!$skipTransaction) { $this->assertor->beginTransaction(); } $options['skipTransaction'] = true; /** Validate Filedata */ $newNode = parent::add($data, $options); /** Update filedata refcount */ $fileData = $this->assertor->getRow('vBForum:filedata', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, 'filedataid' => $data["filedataid"])); $refCount = $fileData["refcount"] + 1; $photodata = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, vB_dB_Query::CONDITIONS_KEY => array('filedataid' => $data['filedataid']), 'refcount' => $refCount); $this->assertor->assertQuery('vBForum:filedata', $photodata); if (!$skipTransaction) { $this->assertor->commitTransaction(); } } catch (exception $e) { if (!$skipTransaction) { $this->assertor->rollbackTransaction(); } throw $e; } if (!$skipTransaction) { //The child classes that have their own transactions all set this to true so afterAdd is always called just once. $this->afterAdd($newNode['nodeid'], $data, $options, $newNode['cacheEvents'], $newNode['nodeVals']); } return $newNode; }
public function add($data, array $options = array()) { //Store this so we know whether we should call afterAdd() $skipTransaction = !empty($options['skipTransaction']); //todo -- lock the caption to the description until we collapse the fields. Remove when caption goes away if (isset($data['caption'])) { $data['description'] = $data['caption']; } else { if (isset($data['description'])) { $data['caption'] = $data['description']; } } try { if (!$skipTransaction) { $this->assertor->beginTransaction(); } $options['skipTransaction'] = true; $result = parent::add($data, $options); if ($result) { $this->assertor->assertQuery('vBForum:node', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, 'nodeid' => $data['parentid'], 'hasphoto' => 1)); // Increment the refcount in filedata. // Note that content_attach API's validate() ensures that we have $data['filedataid'] set. $this->assertor->assertQuery('updateFiledataRefCount', array('countChange' => 1, 'filedataid' => $data['filedataid'])); } if (!$skipTransaction) { $this->assertor->commitTransaction(); } } catch (exception $e) { if (!$skipTransaction) { $this->assertor->rollbackTransaction(); } throw $e; } 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']); } return $result; }
public function add($data, array $options = array('nodeonly' => false)) { $options += array('skipDupCheck' => true); //Store this so we know whether we should call afterAdd() $skipTransaction = !empty($options['skipTransaction']); // VBV-833: we allow interfaces to not specify a parent. Main channel should be used in that case if (!isset($data['parentid']) or $data['parentid'] <= 0) { $data['parentid'] = vB::getDbAssertor()->getField('vBForum:channel', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT, vB_dB_Query::CONDITIONS_KEY => array('guid' => vB_Channel::DEFAULT_CHANNEL_PARENT))); } else { // if we are not using the default channel parent, we need to check for pagetemplates if (!isset($data['templates'])) { $parent = vB::getDbAssertor()->getRow('vBForum:channel', array(vB_dB_Query::COLUMNS_KEY => array('nodeid', 'guid'), vB_dB_Query::CONDITIONS_KEY => array('nodeid' => $data['parentid']))); switch ($parent['guid']) { case vB_Channel::DEFAULT_SOCIALGROUP_PARENT: // This is done only when saving from activity stream configuration, once it is removed we can get rid of this $data['templates']['vB5_Route_Channel'] = vB_Page::getSGCategoryPageTemplate(); $data['templates']['vB5_Route_Conversation'] = vB_Page::getSGCategoryConversPageTemplate(); $data['category'] = 1; break; case vB_Channel::DEFAULT_ARTICLE_PARENT: // articles $data['templates']['vB5_Route_Channel'] = vB_Page::getArticleChannelPageTemplate(); $data['templates']['vB5_Route_Conversation'] = vB_Page::getArticleConversPageTemplate(); break; default: // use inherited from parent channel break; } } } if (!isset($data['guid'])) { // creating guid $data['guid'] = vB_Xml_Export_Channel::createGUID($data); } // parse options array if (isset($data['options'])) { if (is_array($data['options'])) { $value = $this->buildChannelOptions(0, $data['options']); if ($value !== FALSE) { $data['options'] = $value; } else { // do not update field unset($data['options']); } } else { // should we accept raw ints as updates? unset($data['options']); } } if (empty($data['urlident']) and !empty($data['title'])) { $data['urlident'] = $this->getUniqueUrlIdent($data['title']); } if (!isset($options['nodeonly']) || !$options['nodeonly']) { // if we are going to create pages, verify that prefix/regex generated is valid BEFORE creating the node vB5_Route_Channel::validatePrefix($data); } try { if (!$skipTransaction) { $this->assertor->beginTransaction(); } $options['skipTransaction'] = true; $result = parent::add($data, $options); if (!isset($options['nodeonly']) || !$options['nodeonly']) { $this->nodeLibrary->clearCacheEvents($result['nodeid']); $this->createChannelPages($result['nodeid'], $data); } if (!$skipTransaction) { $this->assertor->commitTransaction(); } } catch (exception $e) { if (!$skipTransaction) { $this->assertor->rollbackTransaction(); } throw $e; } //and announce that the cached channel structure has changed. $result['cacheEvents'][] = 'vB_ChannelStructure_chg'; if (isset($data['filedataid']) and vB::getUserContext()->getChannelPermission('forumpermissions', 'canuploadchannelicon', $result['nodeid'])) { $this->qryAfterAdd[] = array('definition' => 'incrementFiledataRefcountAndMakePublic', 'data' => array('filedataid' => $data['filedataid'])); } 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']); } if (!defined('VB_AREA') or !in_array(VB_AREA, array('Install', 'Upgrade'))) { vB::getUserContext()->rebuildGroupAccess(); vB::getUserContext()->reloadUserPerms(); vB_Channel::rebuildChannelTypes(); } 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; }