/**
* Inserts or updates the discussion via form values.
*
* Events: BeforeSaveDiscussion, AfterSaveDiscussion.
*
* @since 2.0.0
* @access public
*
* @param array $FormPostValues Data sent from the form model.
* @param array $Settings Currently unused.
* @return int $DiscussionID Unique ID of the discussion.
*/
public function save($FormPostValues, $Settings = false)
{
$Session = Gdn::session();
// Define the primary key in this model's table.
$this->defineSchema();
// Add & apply any extra validation rules:
$this->Validation->applyRule('Body', 'Required');
$this->Validation->addRule('MeAction', 'function:ValidateMeAction');
$this->Validation->applyRule('Body', 'MeAction');
$MaxCommentLength = Gdn::config('Vanilla.Comment.MaxLength');
if (is_numeric($MaxCommentLength) && $MaxCommentLength > 0) {
$this->Validation->SetSchemaProperty('Body', 'Length', $MaxCommentLength);
$this->Validation->applyRule('Body', 'Length');
}
// Validate category permissions.
$CategoryID = val('CategoryID', $FormPostValues);
if ($CategoryID > 0) {
$Category = CategoryModel::categories($CategoryID);
if ($Category && !$Session->checkPermission('Vanilla.Discussions.Add', true, 'Category', val('PermissionCategoryID', $Category))) {
$this->Validation->addValidationResult('CategoryID', 'You do not have permission to post in this category');
}
}
// Get the DiscussionID from the form so we know if we are inserting or updating.
$DiscussionID = val('DiscussionID', $FormPostValues, '');
// See if there is a source ID.
if (val('SourceID', $FormPostValues)) {
$DiscussionID = $this->SQL->getWhere('Discussion', arrayTranslate($FormPostValues, array('Source', 'SourceID')))->value('DiscussionID');
if ($DiscussionID) {
$FormPostValues['DiscussionID'] = $DiscussionID;
}
} elseif (val('ForeignID', $FormPostValues)) {
$DiscussionID = $this->SQL->getWhere('Discussion', array('ForeignID' => $FormPostValues['ForeignID']))->value('DiscussionID');
if ($DiscussionID) {
$FormPostValues['DiscussionID'] = $DiscussionID;
}
}
$Insert = $DiscussionID == '' ? true : false;
$this->EventArguments['Insert'] = $Insert;
if ($Insert) {
unset($FormPostValues['DiscussionID']);
// If no categoryid is defined, grab the first available.
if (!val('CategoryID', $FormPostValues) && !c('Vanilla.Categories.Use')) {
$FormPostValues['CategoryID'] = val('CategoryID', CategoryModel::defaultCategory(), -1);
}
$this->addInsertFields($FormPostValues);
// The UpdateUserID used to be required. Just add it if it still is.
if (!$this->Schema->getProperty('UpdateUserID', 'AllowNull', true)) {
$FormPostValues['UpdateUserID'] = $FormPostValues['InsertUserID'];
}
// $FormPostValues['LastCommentUserID'] = $Session->UserID;
$FormPostValues['DateLastComment'] = $FormPostValues['DateInserted'];
} else {
// Add the update fields.
$this->addUpdateFields($FormPostValues);
}
// Set checkbox values to zero if they were unchecked
if (val('Announce', $FormPostValues, '') === false) {
$FormPostValues['Announce'] = 0;
}
if (val('Closed', $FormPostValues, '') === false) {
$FormPostValues['Closed'] = 0;
}
if (val('Sink', $FormPostValues, '') === false) {
$FormPostValues['Sink'] = 0;
}
// Prep and fire event
$this->EventArguments['FormPostValues'] =& $FormPostValues;
$this->EventArguments['DiscussionID'] = $DiscussionID;
$this->fireEvent('BeforeSaveDiscussion');
// Validate the form posted values
$this->validate($FormPostValues, $Insert);
$ValidationResults = $this->validationResults();
// If the body is not required, remove it's validation errors.
$BodyRequired = c('Vanilla.DiscussionBody.Required', true);
if (!$BodyRequired && array_key_exists('Body', $ValidationResults)) {
unset($ValidationResults['Body']);
}
if (count($ValidationResults) == 0) {
// If the post is new and it validates, make sure the user isn't spamming
if (!$Insert || !$this->checkForSpam('Discussion')) {
// Get all fields on the form that relate to the schema
$Fields = $this->Validation->schemaValidationFields();
// Check for spam.
$spam = SpamModel::isSpam('Discussion', $Fields);
if ($spam) {
return SPAM;
}
// Get DiscussionID if one was sent
$DiscussionID = intval(val('DiscussionID', $Fields, 0));
// Remove the primary key from the fields for saving.
unset($Fields['DiscussionID']);
$StoredCategoryID = false;
if ($DiscussionID > 0) {
// Updating
$Stored = $this->getID($DiscussionID, DATASET_TYPE_OBJECT);
// Block Format change if we're forcing the formatter.
if (c('Garden.ForceInputFormatter')) {
unset($Fields['Format']);
}
// Clear the cache if necessary.
$CacheKeys = array();
if (val('Announce', $Stored) != val('Announce', $Fields)) {
$CacheKeys[] = $this->getAnnouncementCacheKey();
$CacheKeys[] = $this->getAnnouncementCacheKey(val('CategoryID', $Stored));
}
if (val('CategoryID', $Stored) != val('CategoryID', $Fields)) {
$CacheKeys[] = $this->getAnnouncementCacheKey(val('CategoryID', $Fields));
}
foreach ($CacheKeys as $CacheKey) {
Gdn::cache()->remove($CacheKey);
}
self::serializeRow($Fields);
$this->SQL->put($this->Name, $Fields, array($this->PrimaryKey => $DiscussionID));
setValue('DiscussionID', $Fields, $DiscussionID);
LogModel::logChange('Edit', 'Discussion', (array) $Fields, $Stored);
if (val('CategoryID', $Stored) != val('CategoryID', $Fields)) {
$StoredCategoryID = val('CategoryID', $Stored);
}
} else {
// Inserting.
if (!val('Format', $Fields) || c('Garden.ForceInputFormatter')) {
$Fields['Format'] = c('Garden.InputFormatter', '');
}
if (c('Vanilla.QueueNotifications')) {
$Fields['Notified'] = ActivityModel::SENT_PENDING;
}
// Check for approval
$ApprovalRequired = checkRestriction('Vanilla.Approval.Require');
if ($ApprovalRequired && !val('Verified', Gdn::session()->User)) {
LogModel::insert('Pending', 'Discussion', $Fields);
return UNAPPROVED;
}
// Create discussion
$this->serializeRow($Fields);
$DiscussionID = $this->SQL->insert($this->Name, $Fields);
$Fields['DiscussionID'] = $DiscussionID;
// Update the cache.
if ($DiscussionID && Gdn::cache()->activeEnabled()) {
$CategoryCache = array('LastDiscussionID' => $DiscussionID, 'LastCommentID' => null, 'LastTitle' => Gdn_Format::text($Fields['Name']), 'LastUserID' => $Fields['InsertUserID'], 'LastDateInserted' => $Fields['DateInserted'], 'LastUrl' => DiscussionUrl($Fields));
CategoryModel::setCache($Fields['CategoryID'], $CategoryCache);
// Clear the cache if necessary.
if (val('Announce', $Fields)) {
Gdn::cache()->remove($this->getAnnouncementCacheKey(val('CategoryID', $Fields)));
if (val('Announce', $Fields) == 1) {
Gdn::cache()->remove($this->getAnnouncementCacheKey());
}
}
}
// Update the user's discussion count.
$InsertUser = Gdn::userModel()->getID($Fields['InsertUserID']);
$this->updateUserDiscussionCount($Fields['InsertUserID'], val('CountDiscussions', $InsertUser, 0) > 100);
// Mark the user as participated.
$this->SQL->replace('UserDiscussion', array('Participated' => 1), array('DiscussionID' => $DiscussionID, 'UserID' => val('InsertUserID', $Fields)));
// Assign the new DiscussionID to the comment before saving.
$FormPostValues['IsNewDiscussion'] = true;
$FormPostValues['DiscussionID'] = $DiscussionID;
// Do data prep.
$DiscussionName = val('Name', $Fields, '');
$Story = val('Body', $Fields, '');
$NotifiedUsers = array();
$UserModel = Gdn::userModel();
$ActivityModel = new ActivityModel();
if (val('Type', $FormPostValues)) {
$Code = 'HeadlineFormat.Discussion.' . $FormPostValues['Type'];
} else {
$Code = 'HeadlineFormat.Discussion';
}
$HeadlineFormat = t($Code, '{ActivityUserID,user} started a new discussion: <a href="{Url,html}">{Data.Name,text}</a>');
$Category = CategoryModel::categories(val('CategoryID', $Fields));
$Activity = array('ActivityType' => 'Discussion', 'ActivityUserID' => $Fields['InsertUserID'], 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Discussion', 'RecordID' => $DiscussionID, 'Route' => DiscussionUrl($Fields), 'Data' => array('Name' => $DiscussionName, 'Category' => val('Name', $Category)));
// Allow simple fulltext notifications
if (c('Vanilla.Activity.ShowDiscussionBody', false)) {
$Activity['Story'] = $Story;
}
// Notify all of the users that were mentioned in the discussion.
$Usernames = getMentions($DiscussionName . ' ' . $Story);
// Use our generic Activity for events, not mentions
$this->EventArguments['Activity'] = $Activity;
// Notify everyone that has advanced notifications.
if (!c('Vanilla.QueueNotifications')) {
try {
$Fields['DiscussionID'] = $DiscussionID;
$this->notifyNewDiscussion($Fields, $ActivityModel, $Activity);
} catch (Exception $Ex) {
throw $Ex;
}
}
// Notifications for mentions
foreach ($Usernames as $Username) {
$User = $UserModel->getByUsername($Username);
if (!$User) {
continue;
}
// Check user can still see the discussion.
if (!$this->canView($Fields, $User->UserID)) {
continue;
}
$Activity['HeadlineFormat'] = t('HeadlineFormat.Mention', '{ActivityUserID,user} mentioned you in <a href="{Url,html}">{Data.Name,text}</a>');
$Activity['NotifyUserID'] = val('UserID', $User);
$ActivityModel->queue($Activity, 'Mention');
}
// Throw an event for users to add their own events.
$this->EventArguments['Discussion'] = $Fields;
$this->EventArguments['NotifiedUsers'] = $NotifiedUsers;
$this->EventArguments['MentionedUsers'] = $Usernames;
$this->EventArguments['ActivityModel'] = $ActivityModel;
$this->fireEvent('BeforeNotification');
// Send all notifications.
$ActivityModel->saveQueue();
}
// Get CategoryID of this discussion
$Discussion = $this->getID($DiscussionID, DATASET_TYPE_OBJECT);
$CategoryID = val('CategoryID', $Discussion, false);
// Update discussion counter for affected categories.
if ($Insert || $StoredCategoryID) {
$this->incrementNewDiscussion($Discussion);
}
if ($StoredCategoryID) {
$this->updateDiscussionCount($StoredCategoryID);
}
// Fire an event that the discussion was saved.
$this->EventArguments['FormPostValues'] = $FormPostValues;
$this->EventArguments['Fields'] = $Fields;
$this->EventArguments['DiscussionID'] = $DiscussionID;
$this->fireEvent('AfterSaveDiscussion');
}
}
return $DiscussionID;
}