function writeModuleDiscussion($Discussion, $Px = 'Bookmark') { ?> <li id="<?php echo "{$Px}_{$Discussion->DiscussionID}"; ?> " class="<?php echo CssClass($Discussion); ?> "> <span class="Options"> <?php // echo OptionsList($Discussion); echo BookmarkButton($Discussion); ?> </span> <div class="Title"><?php echo anchor(Gdn_Format::text($Discussion->Name, false), DiscussionUrl($Discussion) . ($Discussion->CountCommentWatch > 0 ? '#Item_' . $Discussion->CountCommentWatch : ''), 'DiscussionLink'); ?> </div> <div class="Meta"> <?php $Last = new stdClass(); $Last->UserID = $Discussion->LastUserID; $Last->Name = $Discussion->LastName; echo NewComments($Discussion); echo '<span class="MItem">' . Gdn_Format::date($Discussion->LastDate, 'html') . UserAnchor($Last) . '</span>'; ?> </div> </li> <?php }
/** * Handle discussion option menu bump action. */ public function DiscussionController_Bump_Create($Sender, $Args) { $Sender->Permission('Garden.Moderation.Manage'); // Get discussion $DiscussionID = $Sender->Request->Get('discussionid'); $Discussion = $Sender->DiscussionModel->GetID($DiscussionID); if (!$Discussion) { throw NotFoundException('Discussion'); } // Update DateLastComment & redirect $Sender->DiscussionModel->SetProperty($DiscussionID, 'DateLastComment', Gdn_Format::ToDateTime()); Redirect(DiscussionUrl($Discussion)); }
/** * Handle discussion option menu NoIndex action (simple toggle). */ public function DiscussionController_NoIndex_Create($Sender, $Args) { $Sender->Permission(array('Garden.Moderation.Manage', 'Garden.Curation.Manage'), FALSE); // Get discussion $DiscussionID = $Sender->Request->Get('discussionid'); $Discussion = $Sender->DiscussionModel->GetID($DiscussionID); if (!$Discussion) { throw NotFoundException('Discussion'); } // Toggle NoIndex $NoIndex = GetValue('NoIndex', $Discussion) ? 0 : 1; // Update DateLastComment & redirect $Sender->DiscussionModel->SetProperty($DiscussionID, 'NoIndex', $NoIndex); Redirect(DiscussionUrl($Discussion)); }
function WriteDiscussionEvent($Discussion, $Prefix = null) { ?> <li class="<?php echo CssClass($Discussion); ?> "> <div class="Title"> <?php echo Anchor(Gdn_Format::Text($Discussion->Name, false), DiscussionUrl($Discussion) . ($Discussion->CountCommentWatch > 0 ? '#Item_' . $Discussion->CountCommentWatch : ''), 'DiscussionLink'); ?> </div><div class="Meta"><span class="MItem"> <?php echo Gdn_Format::Date($Discussion->DiscussionEventDate, 'html'); ?> </span></div> </li> <?php }
function writeModuleDiscussion($Discussion, $Px = 'Bookmark', $showPhotos = false) { ?> <li id="<?php echo "{$Px}_{$Discussion->DiscussionID}"; ?> " class="<?php echo CssClass($Discussion); ?> "> <?php if ($showPhotos) { $firstUser = userBuilder($Discussion, 'First'); echo userPhoto($firstUser, ['LinkClass' => 'IndexPhoto']); } ?> <span class="Options"> <?php // echo OptionsList($Discussion); echo BookmarkButton($Discussion); ?> </span> <div class="Title"><?php echo anchor(Gdn_Format::text($Discussion->Name, false), DiscussionUrl($Discussion) . ($Discussion->CountCommentWatch > 0 ? '#Item_' . $Discussion->CountCommentWatch : ''), 'DiscussionLink'); ?> </div> <div class="Meta DiscussionsModuleMeta"> <?php $Last = new stdClass(); $Last->UserID = $Discussion->LastUserID; $Last->Name = $Discussion->LastName; echo NewComments($Discussion); $translation = pluralTranslate($Discussion->CountComments, '%s comment html', '%s comments html', t('%s comment'), t('%s comments')); echo '<span class="MItem">' . Gdn_Format::date($Discussion->LastDate, 'html') . UserAnchor($Last) . '</span>'; echo '<span class="MItem CountComments Hidden">' . sprintf($translation, $Discussion->CountComments) . '</span>'; ?> </div> </li> <?php }
/** * Handle discussion option menu Change Author action. */ public function DiscussionController_Author_Create($Sender, $Args) { $DiscussionID = $Sender->Request->Get('discussionid'); $Discussion = $Sender->DiscussionModel->GetID($DiscussionID); if (!$Discussion) { throw NotFoundException('Discussion'); } // Check edit permission $Sender->Permission('Vanilla.Discussions.Edit', TRUE, 'Category', $Discussion->PermissionCategoryID); if ($Sender->Form->AuthenticatedPostBack()) { // Change the author $Name = $Sender->Form->GetFormValue('Author', ''); $UserModel = new UserModel(); if (trim($Name) != '') { $User = $UserModel->GetByUsername(trim($Name)); if (is_object($User)) { if ($Discussion->InsertUserID == $User->UserID) { $Sender->Form->AddError('That user is already the discussion author.'); } else { // Change discussion InsertUserID $Sender->DiscussionModel->SetField($DiscussionID, 'InsertUserID', $User->UserID); // Update users' discussion counts $Sender->DiscussionModel->UpdateUserDiscussionCount($Discussion->InsertUserID); $Sender->DiscussionModel->UpdateUserDiscussionCount($User->UserID, TRUE); // Increment // Go to the updated discussion Redirect(DiscussionUrl($Discussion)); } } else { $Sender->Form->AddError('No user with that name was found.'); } } } else { // Form to change the author $Sender->SetData('Title', $Discussion->Name); } $Sender->Render('changeauthor', '', 'plugins/AuthorSelector'); }
/** * * * @param $Sender * @param $Args * @throws Gdn_UserException */ public function commentModel_afterSaveComment_handler($Sender, $Args) { if (!$this->socialSharing() || !$this->accessToken()) { return; } $Share = valr('FormPostValues.ShareTwitter', $Args); if ($Share && $this->accessToken()) { $Row = $Args['FormPostValues']; $DiscussionModel = new DiscussionModel(); $Discussion = $DiscussionModel->getID(val('DiscussionID', $Row)); if (!$Discussion) { return; } $Url = DiscussionUrl($Discussion, '', true); $Message = SliceTwitter(Gdn_Format::plainText($Row['Body'], $Row['Format'])) . ' ' . $Url; $R = $this->API('/statuses/update.json', array('status' => $Message), 'POST'); } }
function writePageLink($Discussion, $PageNumber) { echo anchor($PageNumber, DiscussionUrl($Discussion, $PageNumber)); }
/** * Add a method to the ModerationController to handle merging discussions. * @param Gdn_Controller $Sender */ public function ModerationController_MergeDiscussions_Create($Sender) { $Session = Gdn::Session(); $Sender->Form = new Gdn_Form(); $Sender->Title(T('Merge Discussions')); $DiscussionModel = new DiscussionModel(); $CheckedDiscussions = Gdn::UserModel()->GetAttribute($Session->User->UserID, 'CheckedDiscussions', array()); if (!is_array($CheckedDiscussions)) { $CheckedDiscussions = array(); } $DiscussionIDs = $CheckedDiscussions; $Sender->SetData('DiscussionIDs', $DiscussionIDs); $CountCheckedDiscussions = count($DiscussionIDs); $Sender->SetData('CountCheckedDiscussions', $CountCheckedDiscussions); $Discussions = $DiscussionModel->SQL->WhereIn('DiscussionID', $DiscussionIDs)->Get('Discussion')->ResultArray(); $Sender->SetData('Discussions', $Discussions); // Perform the merge if ($Sender->Form->AuthenticatedPostBack()) { // Create a new discussion record $MergeDiscussion = FALSE; $MergeDiscussionID = $Sender->Form->GetFormValue('MergeDiscussionID'); foreach ($Discussions as $Discussion) { if ($Discussion['DiscussionID'] == $MergeDiscussionID) { $MergeDiscussion = $Discussion; break; } } $RedirectLink = $Sender->Form->GetFormValue('RedirectLink'); if ($MergeDiscussion) { $ErrorCount = 0; // Verify that the user has permission to perform the merge. $Category = CategoryModel::Categories($MergeDiscussion['CategoryID']); if ($Category && !$Category['PermsDiscussionsEdit']) { throw PermissionException('Vanilla.Discussions.Edit'); } $DiscussionModel->DefineSchema(); $MaxNameLength = GetValue('Length', $DiscussionModel->Schema->GetField('Name')); // Assign the comments to the new discussion record $DiscussionModel->SQL->Update('Comment')->Set('DiscussionID', $MergeDiscussionID)->WhereIn('DiscussionID', $DiscussionIDs)->Put(); $CommentModel = new CommentModel(); foreach ($Discussions as $Discussion) { if ($Discussion['DiscussionID'] == $MergeDiscussionID) { continue; } // Create a comment out of the discussion. $Comment = ArrayTranslate($Discussion, array('Body', 'Format', 'DateInserted', 'InsertUserID', 'InsertIPAddress', 'DateUpdated', 'UpdateUserID', 'UpdateIPAddress', 'Attributes', 'Spam', 'Likes', 'Abuse')); $Comment['DiscussionID'] = $MergeDiscussionID; $CommentModel->Validation->Results(TRUE); $CommentID = $CommentModel->Save($Comment); if ($CommentID) { // Move any attachments (FileUpload plugin awareness) if (class_exists('MediaModel')) { $MediaModel = new MediaModel(); $MediaModel->Reassign($Discussion['DiscussionID'], 'discussion', $CommentID, 'comment'); } if ($RedirectLink) { // The discussion needs to be changed to a moved link. $RedirectDiscussion = array('Name' => SliceString(sprintf(T('Merged: %s'), $Discussion['Name']), $MaxNameLength), 'Type' => 'redirect', 'Body' => FormatString(T('This discussion has been <a href="{url,html}">merged</a>.'), array('url' => DiscussionUrl($MergeDiscussion))), 'Format' => 'Html'); $DiscussionModel->SetField($Discussion['DiscussionID'], $RedirectDiscussion); $CommentModel->UpdateCommentCount($Discussion['DiscussionID']); $CommentModel->RemovePageCache($Discussion['DiscussionID']); } else { // Delete discussion that was merged. $DiscussionModel->Delete($Discussion['DiscussionID']); } } else { $Sender->InformMessage($CommentModel->Validation->ResultsText()); $ErrorCount++; } } // Update counts on all affected discussions. $CommentModel->UpdateCommentCount($MergeDiscussionID); $CommentModel->RemovePageCache($MergeDiscussionID); // Clear selections Gdn::UserModel()->SaveAttribute($Session->UserID, 'CheckedDiscussions', FALSE); ModerationController::InformCheckedDiscussions($Sender); if ($ErrorCount == 0) { $Sender->JsonTarget('', '', 'Refresh'); } } } $Sender->Render('MergeDiscussions', '', 'plugins/SplitMerge'); }
function WritePromotedContent($Content, $Sender) { static $UserPhotoFirst = NULL; if ($UserPhotoFirst === NULL) { $UserPhotoFirst = C('Vanilla.Comment.UserPhotoFirst', TRUE); } $ContentType = GetValue('ItemType', $Content); $ContentID = GetValue("{$ContentType}ID", $Content); $Author = GetValue('Author', $Content); switch (strtolower($ContentType)) { case 'comment': $ContentURL = CommentUrl($Content); break; case 'discussion': $ContentURL = DiscussionUrl($Content); break; } ?> <div id="<?php echo "Promoted_{$ContentType}_{$ContentID}"; ?> " class="<?php echo CssClass($Content); ?> "> <div class="AuthorWrap"> <span class="Author"> <?php if ($UserPhotoFirst) { echo UserPhoto($Author); echo UserAnchor($Author, 'Username'); } else { echo UserAnchor($Author, 'Username'); echo UserPhoto($Author); } $Sender->FireEvent('AuthorPhoto'); ?> </span> <span class="AuthorInfo"> <?php echo ' ' . WrapIf(htmlspecialchars(GetValue('Title', $Author)), 'span', array('class' => 'MItem AuthorTitle')); echo ' ' . WrapIf(htmlspecialchars(GetValue('Location', $Author)), 'span', array('class' => 'MItem AuthorLocation')); $Sender->FireEvent('AuthorInfo'); ?> </span> </div> <div class="Meta CommentMeta CommentInfo"> <span class="MItem DateCreated"> <?php echo Anchor(Gdn_Format::Date($Content['DateInserted'], 'html'), $Permalink, 'Permalink', array('rel' => 'nofollow')); ?> </span> <?php // Include source if one was set if ($Source = GetValue('Source', $Content)) { echo Wrap(sprintf(T('via %s'), T($Source . ' Source', $Source)), 'span', array('class' => 'MItem Source')); } $Sender->FireEvent('ContentInfo'); ?> </div> <div class="Title"><?php echo Anchor(Gdn_Format::Text($Content['Name'], FALSE), $ContentURL, 'DiscussionLink'); ?> </div> <div class="Body"><?php echo Anchor(strip_tags(Gdn_Format::To($Content['Body'], $Content['Format'])), $ContentURL, 'BodyLink'); ?> </div> </div> <?php }
public function notifyNewDiscussion($DiscussionID) { if (!c('Vanilla.QueueNotifications')) { throw forbiddenException('NotifyNewDiscussion'); } if (!$this->Request->isPostBack()) { throw forbiddenException('GET'); } // Grab the discussion. $Discussion = $this->DiscussionModel->getID($DiscussionID); if (!$Discussion) { throw notFoundException('Discussion'); } if (val('Notified', $Discussion) != ActivityModel::SENT_PENDING) { die('Not pending'); } // Mark the notification as in progress. $this->DiscussionModel->setField($DiscussionID, 'Notified', ActivityModel::SENT_INPROGRESS); $discussionType = val('Type', $Discussion); if ($discussionType) { $Code = "HeadlineFormat.Discussion.{$discussionType}"; } 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', $Discussion)); $Activity = array('ActivityType' => 'Discussion', 'ActivityUserID' => $Discussion->InsertUserID, 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Discussion', 'RecordID' => $DiscussionID, 'Route' => DiscussionUrl($Discussion), 'Data' => array('Name' => $Discussion->Name, 'Category' => val('Name', $Category))); $ActivityModel = new ActivityModel(); $this->DiscussionModel->NotifyNewDiscussion($Discussion, $ActivityModel, $Activity); $ActivityModel->SaveQueue(); $this->DiscussionModel->setField($DiscussionID, 'Notified', ActivityModel::SENT_OK); die('OK'); }
public function incrementNewDiscussion($Discussion) { if (is_numeric($Discussion)) { $Discussion = $this->getID($Discussion); } if (!$Discussion) { return; } $this->SQL->update('Category')->set('CountDiscussions', 'CountDiscussions + 1', false)->set('LastDiscussionID', val('DiscussionID', $Discussion))->set('LastCommentID', null)->set('LastDateInserted', val('DateInserted', $Discussion))->where('CategoryID', val('CategoryID', $Discussion))->put(); $Category = CategoryModel::categories(val('CategoryID', $Discussion)); if ($Category) { CategoryModel::SetCache($Category['CategoryID'], array('CountDiscussions' => $Category['CountDiscussions'] + 1, 'LastDiscussionID' => val('DiscussionID', $Discussion), 'LastCommentID' => null, 'LastDateInserted' => val('DateInserted', $Discussion), 'LastTitle' => Gdn_Format::text(val('Name', $Discussion, t('No Title'))), 'LastUserID' => val('InsertUserID', $Discussion), 'LastDiscussionUserID' => val('InsertUserID', $Discussion), 'LastUrl' => DiscussionUrl($Discussion, false, '//') . '#latest')); } }
public function IncrementNewDiscussion($Discussion) { if (is_numeric($Discussion)) { $Discussion = $this->GetID($Discussion); } if (!$Discussion) { return; } $this->SQL->Update('Category')->Set('CountDiscussions', 'CountDiscussions + 1', FALSE)->Set('LastDiscussionID', GetValue('DiscussionID', $Discussion))->Set('LastCommentID', NULL)->Set('LastDateInserted', GetValue('DateInserted', $Discussion))->Where('CategoryID', GetValue('CategoryID', $Discussion))->Put(); $Category = CategoryModel::Categories(GetValue('CategoryID', $Discussion)); if ($Category) { CategoryModel::SetCache($Category['CategoryID'], array('CountDiscussions' => $Category['CountDiscussions'] + 1, 'LastDiscussionID' => GetValue('DiscussionID', $Discussion), 'LastCommentID' => NULL, 'LastDateInserted' => GetValue('DateInserted', $Discussion), 'LastTitle' => Gdn_Format::Text(GetValue('Name', $Discussion, T('No Title'))), 'LastUserID' => GetValue('InsertUserID', $Discussion), 'LastDiscussionUserID' => GetValue('InsertUserID', $Discussion), 'LastUrl' => DiscussionUrl($Discussion, FALSE, '//') . '#latest')); } }
/** * Form to ask for the destination of the move, confirmation and permission check. */ public function ConfirmDiscussionMoves($DiscussionID = NULL) { $Session = Gdn::Session(); $this->Form = new Gdn_Form(); $DiscussionModel = new DiscussionModel(); $this->Title(T('Confirm')); if ($DiscussionID) { $CheckedDiscussions = (array) $DiscussionID; $ClearSelection = FALSE; } else { $CheckedDiscussions = Gdn::UserModel()->GetAttribute($Session->User->UserID, 'CheckedDiscussions', array()); if (!is_array($CheckedDiscussions)) { $CheckedDiscussions = array(); } $ClearSelection = TRUE; } $DiscussionIDs = $CheckedDiscussions; $CountCheckedDiscussions = count($DiscussionIDs); $this->SetData('CountCheckedDiscussions', $CountCheckedDiscussions); // Check for edit permissions on each discussion $AllowedDiscussions = array(); $DiscussionData = $DiscussionModel->SQL->Select('DiscussionID, Name, DateLastComment, CategoryID')->From('Discussion')->WhereIn('DiscussionID', $DiscussionIDs)->Get(); $DiscussionData = Gdn_DataSet::Index($DiscussionData->ResultArray(), array('DiscussionID')); foreach ($DiscussionData as $DiscussionID => $Discussion) { $Category = CategoryModel::Categories($Discussion['CategoryID']); if ($Category && $Category['PermsDiscussionsEdit']) { $AllowedDiscussions[] = $DiscussionID; } } $this->SetData('CountAllowed', count($AllowedDiscussions)); $CountNotAllowed = $CountCheckedDiscussions - count($AllowedDiscussions); $this->SetData('CountNotAllowed', $CountNotAllowed); if ($this->Form->AuthenticatedPostBack()) { // Retrieve the category id $CategoryID = $this->Form->GetFormValue('CategoryID'); $Category = CategoryModel::Categories($CategoryID); $RedirectLink = $this->Form->GetFormValue('RedirectLink'); // User must have add permission on the target category if (!$Category['PermsDiscussionsAdd']) { throw ForbiddenException('@' . T('You do not have permission to add discussions to this category.')); } // Iterate and move. foreach ($AllowedDiscussions as $DiscussionID) { // Create the shadow redirect. if ($RedirectLink) { $Discussion = GetValue($DiscussionID, $DiscussionData); $DiscussionModel->DefineSchema(); $MaxNameLength = GetValue('Length', $DiscussionModel->Schema->GetField('Name')); $RedirectDiscussion = array('Name' => SliceString(sprintf(T('Moved: %s'), $Discussion['Name']), $MaxNameLength), 'DateInserted' => $Discussion['DateLastComment'], 'Type' => 'redirect', 'CategoryID' => $Discussion['CategoryID'], 'Body' => FormatString(T('This discussion has been <a href="{url,html}">moved</a>.'), array('url' => DiscussionUrl($Discussion))), 'Format' => 'Html', 'Closed' => TRUE); $RedirectID = $DiscussionModel->Save($RedirectDiscussion); if (!$RedirectID) { $this->Form->SetValidationResults($DiscussionModel->ValidationResults()); break; } } $DiscussionModel->SetField($DiscussionID, 'CategoryID', $CategoryID); } // Clear selections. if ($ClearSelection) { Gdn::UserModel()->SaveAttribute($Session->UserID, 'CheckedDiscussions', FALSE); ModerationController::InformCheckedDiscussions($this); } if ($this->Form->ErrorCount() == 0) { $this->JsonTarget('', '', 'Refresh'); } } $this->Render(); }
/** * Insert or update meta data about the comment. * * Updates unread comment totals, bookmarks, and activity. Sends notifications. * * @since 2.0.0 * @access public * * @param array $CommentID Unique ID for this comment. * @param int $Insert Used as a boolean for whether this is a new comment. * @param bool $CheckExisting Not used. * @param bool $IncUser Whether or not to just increment the user's comment count rather than recalculate it. */ public function Save2($CommentID, $Insert, $CheckExisting = TRUE, $IncUser = FALSE) { $Session = Gdn::Session(); $UserModel = Gdn::UserModel(); // Load comment data $Fields = $this->GetID($CommentID, DATASET_TYPE_ARRAY); // Clear any session stashes related to this discussion $DiscussionModel = new DiscussionModel(); $DiscussionID = GetValue('DiscussionID', $Fields); $Discussion = $DiscussionModel->GetID($DiscussionID); $Session->Stash('CommentForForeignID_' . GetValue('ForeignID', $Discussion)); // Make a quick check so that only the user making the comment can make the notification. // This check may be used in the future so should not be depended on later in the method. if ($Fields['InsertUserID'] != $Session->UserID) { return; } // Update the discussion author's CountUnreadDiscussions (ie. // the number of discussions created by the user that s/he has // unread messages in) if this comment was not added by the // discussion author. $this->UpdateUser($Session->UserID, $IncUser && $Insert); if ($Insert) { // UPDATE COUNT AND LAST COMMENT ON CATEGORY TABLE if ($Discussion->CategoryID > 0) { $CountComments = $this->SQL->Select('CountComments', 'sum', 'CountComments')->From('Discussion')->Where('CategoryID', $Discussion->CategoryID)->Get()->FirstRow()->CountComments; $CategoryModel = new CategoryModel(); $CategoryModel->SetField($Discussion->CategoryID, array('LastDiscussionID' => $DiscussionID, 'LastCommentID' => $CommentID, 'CountComments' => $CountComments, 'LastDateInserted' => $Fields['DateInserted'])); // Update the cache. if ($DiscussionID && Gdn::Cache()->ActiveEnabled()) { $CategoryCache = array('LastTitle' => $Discussion->Name, 'LastUserID' => $Fields['InsertUserID'], 'LastUrl' => DiscussionUrl($Discussion) . '#latest'); CategoryModel::SetCache($Discussion->CategoryID, $CategoryCache); } } // Prepare the notification queue. $ActivityModel = new ActivityModel(); $HeadlineFormat = T('HeadlineFormat.Comment', '{ActivityUserID,user} commented on <a href="{Url,html}">{Data.Name,text}</a>'); $Category = CategoryModel::Categories($Discussion->CategoryID); $Activity = array('ActivityType' => 'Comment', 'ActivityUserID' => $Fields['InsertUserID'], 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Comment', 'RecordID' => $CommentID, 'Route' => "/discussion/comment/{$CommentID}#Comment_{$CommentID}", 'Data' => array('Name' => $Discussion->Name, 'Category' => GetValue('Name', $Category))); // Allow simple fulltext notifications if (C('Vanilla.Activity.ShowCommentBody', FALSE)) { $Activity['Story'] = GetValue('Body', $Fields); } // Notify users who have bookmarked the discussion. $BookmarkData = $DiscussionModel->GetBookmarkUsers($DiscussionID); foreach ($BookmarkData->Result() as $Bookmark) { // Check user can still see the discussion. if (!$UserModel->GetCategoryViewPermission($Bookmark->UserID, $Discussion->CategoryID)) { continue; } $Activity['NotifyUserID'] = $Bookmark->UserID; $ActivityModel->Queue($Activity, 'BookmarkComment', array('CheckRecord' => TRUE)); } // Record user-comment activity. if ($Discussion != FALSE) { $Activity['NotifyUserID'] = GetValue('InsertUserID', $Discussion); $ActivityModel->Queue($Activity, 'DiscussionComment'); } // Record advanced notifications. if ($Discussion !== FALSE) { $this->RecordAdvancedNotications($ActivityModel, $Activity, $Discussion); } // Notify any users who were mentioned in the comment. $Usernames = GetMentions($Fields['Body']); $NotifiedUsers = array(); foreach ($Usernames as $i => $Username) { $User = $UserModel->GetByUsername($Username); if (!$User) { unset($Usernames[$i]); continue; } // Check user can still see the discussion. if (!$UserModel->GetCategoryViewPermission($User->UserID, $Discussion->CategoryID)) { continue; } $HeadlineFormatBak = $Activity['HeadlineFormat']; $Activity['HeadlineFormat'] = T('HeadlineFormat.Mention', '{ActivityUserID,user} mentioned you in <a href="{Url,html}">{Data.Name,text}</a>'); $Activity['NotifyUserID'] = $User->UserID; $ActivityModel->Queue($Activity, 'Mention'); $Activity['HeadlineFormat'] = $HeadlineFormatBak; } // Throw an event for users to add their own events. $this->EventArguments['Comment'] = $Fields; $this->EventArguments['Discussion'] = $Discussion; $this->EventArguments['NotifiedUsers'] = array_keys(ActivityModel::$Queue); $this->EventArguments['MentionedUsers'] = $Usernames; $this->EventArguments['ActivityModel'] = $ActivityModel; $this->FireEvent('BeforeNotification'); // Send all notifications. $ActivityModel->SaveQueue(); } }
public function Search($Search, $Offset = 0, $Limit = 20) { // If there are no searches then return an empty array. if (trim($Search) == '') { return array(); } // Figure out the exact search mode. if ($this->ForceSearchMode) { $SearchMode = $this->ForceSearchMode; } else { $SearchMode = strtolower(C('Garden.Search.Mode', 'matchboolean')); } if ($SearchMode == 'matchboolean') { if (strpos($Search, '+') !== FALSE || strpos($Search, '-') !== FALSE) { $SearchMode = 'boolean'; } else { $SearchMode = 'match'; } } else { $this->_SearchMode = $SearchMode; } if ($ForceDatabaseEngine = C('Database.ForceStorageEngine')) { if (strcasecmp($ForceDatabaseEngine, 'myisam') != 0) { $SearchMode = 'like'; } } if (strlen($Search) <= 4) { $SearchMode = 'like'; } $this->_SearchMode = $SearchMode; $this->FireEvent('Search'); if (count($this->_SearchSql) == 0) { return array(); } // Perform the search by unioning all of the sql together. $Sql = $this->SQL->Select()->From('_TBL_ s')->OrderBy('s.DateInserted', 'desc')->Limit($Limit, $Offset)->GetSelect(); $Sql = str_replace($this->Database->DatabasePrefix . '_TBL_', "(\n" . implode("\nunion all\n", $this->_SearchSql) . "\n)", $Sql); $this->EventArguments['Search'] = $Search; $this->FireEvent('AfterBuildSearchQuery'); if ($this->_SearchMode == 'like') { $Search = '%' . $Search . '%'; } foreach ($this->_Parameters as $Key => $Value) { $this->_Parameters[$Key] = $Search; } $Parameters = $this->_Parameters; $this->Reset(); $this->SQL->Reset(); $Result = $this->Database->Query($Sql, $Parameters)->ResultArray(); foreach ($Result as $Key => $Value) { if (isset($Value['Summary'])) { $Value['Summary'] = Condense(Gdn_Format::To($Value['Summary'], $Value['Format'])); $Result[$Key] = $Value; } switch ($Value['RecordType']) { case 'Discussion': $Discussion = ArrayTranslate($Value, array('PrimaryID' => 'DiscussionID', 'Title' => 'Name', 'CategoryID')); $Result[$Key]['Url'] = DiscussionUrl($Discussion, 1); break; } } return $Result; }
/** * Insert sorting tabs after first comment. */ public function DiscussionController_BeforeCommentDisplay_Handler($Sender) { // if (!C('Plugins.Voting.Enabled')) // return; $AnswerCount = $Sender->Discussion->CountComments - 1; $Type = GetValue('Type', $Sender->EventArguments, 'Comment'); if ($Type == 'Comment' && !GetValue('VoteHeaderWritten', $Sender)) { //$Type != 'Comment' && $AnswerCount > 0) { ?> <li class="Item"> <div class="VotingSort"> <?php echo Wrap($AnswerCount . ' ' . Plural($AnswerCount, 'Comment', 'Comments'), 'strong'); echo ' sorted by ' . Anchor('Votes', Url(DiscussionUrl($Sender->Discussion) . '?Sort=popular', TRUE), '', array('rel' => 'nofollow', 'class' => self::CommentSort() == 'popular' ? 'Active' : '')) . ' ' . Anchor('Date Added', Url(DiscussionUrl($Sender->Discussion) . '?Sort=date', TRUE), '', array('rel' => 'nofollow', 'class' => self::CommentSort() == 'date' ? 'Active' : '')); ?> </div> </li> <?php $Sender->VoteHeaderWritten = TRUE; } }
echo T('Follows'); ?> </td> <td><?php echo T('Views'); ?> </td> </tr> </thead> <tbody> <?php foreach ($this->Data['DiscussionData'] as $Discussion) { ?> <tr> <th><?php echo Anchor(htmlspecialchars($Discussion->Name), DiscussionUrl($Discussion)); ?> </th> <td><?php echo number_format($Discussion->CountComments); ?> </td> <td><?php echo number_format($Discussion->CountBookmarks); ?> </td> <td><?php echo number_format($Discussion->CountViews); ?> </td> </tr>
/** * Form to ask for the destination of the move, confirmation and permission check. */ public function confirmDiscussionMoves($DiscussionID = null) { $Session = Gdn::session(); $this->Form = new Gdn_Form(); $DiscussionModel = new DiscussionModel(); $CategoryModel = new CategoryModel(); $this->title(t('Confirm')); if ($DiscussionID) { $CheckedDiscussions = (array) $DiscussionID; $ClearSelection = false; } else { $CheckedDiscussions = Gdn::userModel()->getAttribute($Session->User->UserID, 'CheckedDiscussions', array()); if (!is_array($CheckedDiscussions)) { $CheckedDiscussions = array(); } $ClearSelection = true; } $DiscussionIDs = $CheckedDiscussions; $CountCheckedDiscussions = count($DiscussionIDs); $this->setData('CountCheckedDiscussions', $CountCheckedDiscussions); // Check for edit permissions on each discussion $AllowedDiscussions = array(); $DiscussionData = $DiscussionModel->SQL->select('DiscussionID, Name, DateLastComment, CategoryID, CountComments')->from('Discussion')->whereIn('DiscussionID', $DiscussionIDs)->get(); $DiscussionData = Gdn_DataSet::Index($DiscussionData->resultArray(), array('DiscussionID')); foreach ($DiscussionData as $DiscussionID => $Discussion) { $Category = CategoryModel::categories($Discussion['CategoryID']); if ($Category && $Category['PermsDiscussionsEdit']) { $AllowedDiscussions[] = $DiscussionID; } } $this->setData('CountAllowed', count($AllowedDiscussions)); $CountNotAllowed = $CountCheckedDiscussions - count($AllowedDiscussions); $this->setData('CountNotAllowed', $CountNotAllowed); if ($this->Form->authenticatedPostBack()) { // Retrieve the category id $CategoryID = $this->Form->getFormValue('CategoryID'); $Category = CategoryModel::categories($CategoryID); $RedirectLink = $this->Form->getFormValue('RedirectLink'); // User must have add permission on the target category if (!$Category['PermsDiscussionsAdd']) { throw forbiddenException('@' . t('You do not have permission to add discussions to this category.')); } $AffectedCategories = array(); // Iterate and move. foreach ($AllowedDiscussions as $DiscussionID) { $Discussion = val($DiscussionID, $DiscussionData); // Create the shadow redirect. if ($RedirectLink) { $DiscussionModel->defineSchema(); $MaxNameLength = val('Length', $DiscussionModel->Schema->GetField('Name')); $RedirectDiscussion = array('Name' => SliceString(sprintf(t('Moved: %s'), $Discussion['Name']), $MaxNameLength), 'DateInserted' => $Discussion['DateLastComment'], 'Type' => 'redirect', 'CategoryID' => $Discussion['CategoryID'], 'Body' => formatString(t('This discussion has been <a href="{url,html}">moved</a>.'), array('url' => DiscussionUrl($Discussion))), 'Format' => 'Html', 'Closed' => true); // Pass a forced input formatter around this exception. if (c('Garden.ForceInputFormatter')) { $InputFormat = c('Garden.InputFormatter'); saveToConfig('Garden.InputFormatter', 'Html', false); } $RedirectID = $DiscussionModel->save($RedirectDiscussion); // Reset the input formatter if (c('Garden.ForceInputFormatter')) { saveToConfig('Garden.InputFormatter', $InputFormat, false); } if (!$RedirectID) { $this->Form->setValidationResults($DiscussionModel->validationResults()); break; } } $DiscussionModel->setField($DiscussionID, 'CategoryID', $CategoryID); if (!isset($AffectedCategories[$Discussion['CategoryID']])) { $AffectedCategories[$Discussion['CategoryID']] = array(-1, -$Discussion['CountComments']); } else { $AffectedCategories[$Discussion['CategoryID']][0] -= 1; $AffectedCategories[$Discussion['CategoryID']][1] -= $Discussion['CountComments']; } if (!isset($AffectedCategories[$CategoryID])) { $AffectedCategories[$CategoryID] = array(1, $Discussion['CountComments']); } else { $AffectedCategories[$CategoryID][0] += 1; $AffectedCategories[$CategoryID][1] += $Discussion['CountComments']; } } // Update recent posts and counts on all affected categories. foreach ($AffectedCategories as $CategoryID => $Counts) { $CategoryModel->SetRecentPost($CategoryID); $CategoryModel->SQL->update('Category')->set('CountDiscussions', 'CountDiscussions' . ($Counts[0] < 0 ? ' - ' : ' + ') . abs($Counts[0]), false)->set('CountComments', 'CountComments' . ($Counts[1] < 0 ? ' - ' : ' + ') . abs($Counts[1]), false)->where('CategoryID', $CategoryID)->put(); } // Clear selections. if ($ClearSelection) { Gdn::userModel()->saveAttribute($Session->UserID, 'CheckedDiscussions', false); ModerationController::InformCheckedDiscussions($this); } if ($this->Form->errorCount() == 0) { $this->jsonTarget('', '', 'Refresh'); } } $this->render(); }
$Contents = $this->_Content; echo '<ul class="DataList Compact BlogList">'; foreach ($Contents as $Content) { static $UserPhotoFirst = NULL; if ($UserPhotoFirst === NULL) { $UserPhotoFirst = C('Vanilla.Comment.UserPhotoFirst', TRUE); } $ContentType = GetValue('ItemType', $Content); $ContentID = GetValue("{$ContentType}ID", $Content); $Author = GetValue('Author', $Content); switch (strtolower($ContentType)) { case 'comment': $ContentURL = CommentUrl($Content); break; case 'discussion': $ContentURL = DiscussionUrl($Content); break; } ?> <li id="<?php echo "{$ContentType}_{$ContentID}"; ?> " class="Item"> <h3><?php echo Anchor(Gdn_Format::Text($Content['Name'], FALSE), $ContentURL); ?> </h3> <div class="Item-Header"> <div class="AuthorWrap"> <span class="Author"> <?php
/** * Writes a discussion in table row format. */ function WriteDiscussionRow($Discussion, &$Sender, &$Session, $Alt2) { if (!property_exists($Sender, 'CanEditDiscussions')) { $Sender->CanEditDiscussions = GetValue('PermsDiscussionsEdit', CategoryModel::Categories($Discussion->CategoryID)) && C('Vanilla.AdminCheckboxes.Use'); } $CssClass = CssClass($Discussion); $DiscussionUrl = $Discussion->Url; if ($Session->UserID) { $DiscussionUrl .= '#latest'; } $Sender->EventArguments['DiscussionUrl'] =& $DiscussionUrl; $Sender->EventArguments['Discussion'] =& $Discussion; $Sender->EventArguments['CssClass'] =& $CssClass; $First = UserBuilder($Discussion, 'First'); if ($Discussion->LastUserID) { $Last = UserBuilder($Discussion, 'Last'); } else { $Last = $First; } // $Sender->EventArguments['FirstUser'] = &$First; // $Sender->EventArguments['LastUser'] = &$Last; // // $Sender->FireEvent('BeforeDiscussionName'); $DiscussionName = $Discussion->Name; if ($DiscussionName == '') { $DiscussionName = T('Blank Discussion Topic'); } $Sender->EventArguments['DiscussionName'] =& $DiscussionName; $Discussion->CountPages = ceil($Discussion->CountComments / $Sender->CountCommentsPerPage); $FirstPageUrl = DiscussionUrl($Discussion, 1); $LastPageUrl = DiscussionUrl($Discussion, FALSE) . '#latest'; ?> <tr id="Discussion_<?php echo $Discussion->DiscussionID; ?> " class="<?php echo $CssClass; ?> "> <?php echo AdminCheck($Discussion, array('<td class="CheckBoxColumn"><div class="Wrap">', '</div></td>')); ?> <td class="DiscussionName"> <div class="Wrap"> <span class="Options"> <?php echo OptionsList($Discussion); echo BookmarkButton($Discussion); ?> </span> <?php echo Anchor($DiscussionName, $DiscussionUrl, 'Title') . ' '; $Sender->FireEvent('AfterDiscussionTitle'); WriteMiniPager($Discussion); echo NewComments($Discussion); if ($Sender->Data('_ShowCategoryLink', TRUE)) { echo CategoryLink($Discussion, ' ' . T('in') . ' '); } // Other stuff that was in the standard view that you may want to display: echo '<div class="Meta Meta-Discussion">'; WriteTags($Discussion); echo '</div>'; // if ($Source = GetValue('Source', $Discussion)) // echo ' '.sprintf(T('via %s'), T($Source.' Source', $Source)); // ?> </div> </td> <td class="BlockColumn BlockColumn-User FirstUser"> <div class="Block Wrap"> <?php echo UserPhoto($First, array('Size' => 'Small')); echo UserAnchor($First, 'UserLink BlockTitle'); echo '<div class="Meta">'; echo Anchor(Gdn_Format::Date($Discussion->FirstDate, 'html'), $FirstPageUrl, 'CommentDate MItem'); echo '</div>'; ?> </div> </td> <td class="BigCount CountComments"> <div class="Wrap"> <?php // Exact Number // echo number_format($Discussion->CountComments); // Round Number echo BigPlural($Discussion->CountComments, '%s comment'); ?> </div> </td> <td class="BigCount CountViews"> <div class="Wrap"> <?php // Exact Number // echo number_format($Discussion->CountViews); // Round Number echo BigPlural($Discussion->CountViews, '%s view'); ?> </div> </td> <td class="BlockColumn BlockColumn-User LastUser"> <div class="Block Wrap"> <?php if ($Last) { echo UserPhoto($Last, array('Size' => 'Small')); echo UserAnchor($Last, 'UserLink BlockTitle'); echo '<div class="Meta">'; echo Anchor(Gdn_Format::Date($Discussion->LastDate, 'html'), $LastPageUrl, 'CommentDate MItem'); echo '</div>'; } else { echo ' '; } ?> </div> </td> </tr> <?php }
/** * Modifies category data before it is returned. * * Adds CountAllDiscussions column to each category representing the sum of * discussions within this category as well as all subcategories. * * @since 2.0.17 * @access public * * @param object $Data SQL result. */ public static function AddCategoryColumns($Data) { $Result =& $Data->Result(); $Result2 = $Result; foreach ($Result as &$Category) { if (!property_exists($Category, 'CountAllDiscussions')) { $Category->CountAllDiscussions = $Category->CountDiscussions; } if (!property_exists($Category, 'CountAllComments')) { $Category->CountAllComments = $Category->CountComments; } // Calculate the following field. $Following = !((bool) GetValue('Archived', $Category) || (bool) GetValue('Unfollow', $Category)); $Category->Following = $Following; $DateMarkedRead = GetValue('DateMarkedRead', $Category); $UserDateMarkedRead = GetValue('UserDateMarkedRead', $Category); if (!$DateMarkedRead) { $DateMarkedRead = $UserDateMarkedRead; } elseif ($UserDateMarkedRead && Gdn_Format::ToTimestamp($UserDateMarkedRead) > Gdn_Format::ToTimeStamp($DateMarkedRead)) { $DateMarkedRead = $UserDateMarkedRead; } // Set appropriate Last* columns. SetValue('LastTitle', $Category, GetValue('LastDiscussionTitle', $Category, NULL)); $LastDateInserted = GetValue('LastDateInserted', $Category, NULL); if (GetValue('LastCommentUserID', $Category) == NULL) { SetValue('LastCommentUserID', $Category, GetValue('LastDiscussionUserID', $Category, NULL)); SetValue('DateLastComment', $Category, GetValue('DateLastDiscussion', $Category, NULL)); SetValue('LastUserID', $Category, GetValue('LastDiscussionUserID', $Category, NULL)); $LastDiscussion = ArrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name')); SetValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, FALSE, '//') . '#latest'); if (is_null($LastDateInserted)) { SetValue('LastDateInserted', $Category, GetValue('DateLastDiscussion', $Category, NULL)); } } else { $LastDiscussion = ArrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name')); SetValue('LastUserID', $Category, GetValue('LastCommentUserID', $Category, NULL)); SetValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, FALSE, '//') . '#latest'); if (is_null($LastDateInserted)) { SetValue('LastDateInserted', $Category, GetValue('DateLastComment', $Category, NULL)); } } $LastDateInserted = GetValue('LastDateInserted', $Category, NULL); if ($DateMarkedRead) { if ($LastDateInserted) { $Category->Read = Gdn_Format::ToTimestamp($DateMarkedRead) >= Gdn_Format::ToTimestamp($LastDateInserted); } else { $Category->Read = TRUE; } } else { $Category->Read = FALSE; } foreach ($Result2 as $Category2) { if ($Category2->TreeLeft > $Category->TreeLeft && $Category2->TreeRight < $Category->TreeRight) { $Category->CountAllDiscussions += $Category2->CountDiscussions; $Category->CountAllComments += $Category2->CountComments; } } } }
/** * Join external records to an array. * * @param array &$Data The data to join. * In order to join records each row must have the a RecordType and RecordID column. * @param string $Column The name of the column to put the record in. * If this is blank then the record will be merged into the row. * @param bool $Unset Whether or not to unset rows that don't have a record. * @since 2.3 */ function joinRecords(&$Data, $Column = '', $Unset = false) { $IDs = array(); $AllowedCats = DiscussionModel::CategoryPermissions(); if ($AllowedCats === false) { // This user does not have permission to view anything. $Data = array(); return; } // Gather all of the ids to fetch. foreach ($Data as &$Row) { if (!$Row['RecordType']) { continue; } $RecordType = ucfirst(StringEndsWith($Row['RecordType'], '-Total', true, true)); $Row['RecordType'] = $RecordType; $ID = $Row['RecordID']; $IDs[$RecordType][$ID] = $ID; } // Fetch all of the data in turn. $JoinData = array(); foreach ($IDs as $RecordType => $RecordIDs) { if ($RecordType == 'Comment') { Gdn::SQL()->Select('d.Name, d.CategoryID')->Join('Discussion d', 'd.DiscussionID = r.DiscussionID'); } $Rows = Gdn::SQL()->Select('r.*')->WhereIn($RecordType . 'ID', array_values($RecordIDs))->Get($RecordType . ' r')->ResultArray(); $JoinData[$RecordType] = Gdn_DataSet::Index($Rows, array($RecordType . 'ID')); } // Join the rows. $Unsets = array(); foreach ($Data as $Index => &$Row) { $RecordType = $Row['RecordType']; $ID = $Row['RecordID']; if (!isset($JoinData[$RecordType][$ID])) { if ($Unset) { $Unsets[] = $Index; } continue; // orphaned? } $Record = $JoinData[$RecordType][$ID]; if ($AllowedCats !== true) { // Check to see if the user has permission to view this record. $CategoryID = GetValue('CategoryID', $Record, -1); if (!in_array($CategoryID, $AllowedCats)) { $Unsets[] = $Index; continue; } } switch ($RecordType) { case 'Discussion': $Url = DiscussionUrl($Record, '', '/') . '#latest'; break; case 'Comment': $Url = CommentUrl($Record, '/'); $Record['Name'] = sprintf(T('Re: %s'), $Record['Name']); break; default: $Url = ''; } $Record['Url'] = $Url; if ($Column) { $Row[$Column] = $Record; } else { $Row = array_merge($Row, $Record); } } foreach ($Unsets as $Index) { unset($Data[$Index]); } // Join the users. Gdn::UserModel()->JoinUsers($Data, array('InsertUserID')); if (!empty($Unsets)) { $Data = array_values($Data); } }
/** * Writes a discussion in table row format. */ function writeDiscussionRow($Discussion, $Sender, $Session) { if (!property_exists($Sender, 'CanEditDiscussions')) { $Sender->CanEditDiscussions = val('PermsDiscussionsEdit', CategoryModel::categories($Discussion->CategoryID)) && c('Vanilla.AdminCheckboxes.Use'); } $CssClass = CssClass($Discussion); $DiscussionUrl = $Discussion->Url; if ($Session->UserID) { $DiscussionUrl .= '#latest'; } $Sender->EventArguments['DiscussionUrl'] =& $DiscussionUrl; $Sender->EventArguments['Discussion'] =& $Discussion; $Sender->EventArguments['CssClass'] =& $CssClass; $First = UserBuilder($Discussion, 'First'); if ($Discussion->LastUserID) { $Last = UserBuilder($Discussion, 'Last'); } else { $Last = $First; } $Sender->EventArguments['FirstUser'] =& $First; $Sender->EventArguments['LastUser'] =& $Last; $Sender->fireEvent('BeforeDiscussionName'); $DiscussionName = $Discussion->Name; // If there are no word character detected in the title treat it as if it is blank. if (!preg_match('/\\w/u', $DiscussionName)) { $DiscussionName = t('Blank Discussion Topic'); } $Sender->EventArguments['DiscussionName'] =& $DiscussionName; static $FirstDiscussion = true; if (!$FirstDiscussion) { $Sender->fireEvent('BetweenDiscussion'); } else { $FirstDiscussion = false; } $Discussion->CountPages = ceil($Discussion->CountComments / $Sender->CountCommentsPerPage); $FirstPageUrl = DiscussionUrl($Discussion, 1); $LastPageUrl = DiscussionUrl($Discussion, val('CountPages', $Discussion)) . '#latest'; ?> <tr id="Discussion_<?php echo $Discussion->DiscussionID; ?> " class="<?php echo $CssClass; ?> "> <?php $Sender->fireEvent('BeforeDiscussionContent'); ?> <?php echo AdminCheck($Discussion, array('<td class="CheckBoxColumn"><div class="Wrap">', '</div></td>')); ?> <td class="DiscussionName"> <div class="Wrap"> <span class="Options"> <?php echo OptionsList($Discussion); echo BookmarkButton($Discussion); ?> </span> <?php $Sender->fireEvent('BeforeDiscussionTitle'); echo anchor($DiscussionName, $DiscussionUrl, 'Title') . ' '; $Sender->fireEvent('AfterDiscussionTitle'); WriteMiniPager($Discussion); echo NewComments($Discussion); if ($Sender->data('_ShowCategoryLink', true)) { echo CategoryLink($Discussion, ' ' . t('in') . ' '); } // Other stuff that was in the standard view that you may want to display: echo '<div class="Meta Meta-Discussion">'; WriteTags($Discussion); echo '</div>'; // if ($Source = val('Source', $Discussion)) // echo ' '.sprintf(t('via %s'), t($Source.' Source', $Source)); // ?> </div> </td> <td class="BlockColumn BlockColumn-User FirstUser"> <div class="Block Wrap"> <?php echo userPhoto($First, array('Size' => 'Small')); echo userAnchor($First, 'UserLink BlockTitle'); echo '<div class="Meta">'; echo anchor(Gdn_Format::date($Discussion->FirstDate, 'html'), $FirstPageUrl, 'CommentDate MItem'); echo '</div>'; ?> </div> </td> <td class="BigCount CountComments"> <div class="Wrap"> <?php // Exact Number // echo number_format($Discussion->CountComments); // Round Number echo BigPlural($Discussion->CountComments, '%s comment'); ?> </div> </td> <td class="BigCount CountViews"> <div class="Wrap"> <?php // Exact Number // echo number_format($Discussion->CountViews); // Round Number echo BigPlural($Discussion->CountViews, '%s view'); ?> </div> </td> <td class="BlockColumn BlockColumn-User LastUser"> <div class="Block Wrap"> <?php if ($Last) { echo userPhoto($Last, array('Size' => 'Small')); echo userAnchor($Last, 'UserLink BlockTitle'); echo '<div class="Meta">'; echo anchor(Gdn_Format::date($Discussion->LastDate, 'html'), $LastPageUrl, 'CommentDate MItem'); echo '</div>'; } else { echo ' '; } ?> </div> </td> </tr> <?php }
/** * * * @param $Sender * @param $Args * @throws Gdn_UserException */ public function commentModel_afterSaveComment_handler($Sender, $Args) { if (!$this->socialSharing()) { return; } if (!$this->accessToken()) { return; } $ShareFacebook = valr('FormPostValues.ShareFacebook', $Args); if ($ShareFacebook) { $Row = $Args['FormPostValues']; $DiscussionModel = new DiscussionModel(); $Discussion = $DiscussionModel->getID(val('DiscussionID', $Row)); if (!$Discussion) { die('no discussion'); } $Url = DiscussionUrl($Discussion, '', true); $Message = SliceParagraph(Gdn_Format::plainText($Row['Body'], $Row['Format']), 160); if ($this->accessToken()) { $R = $this->api('/me/feed', array('link' => $Url, 'message' => $Message)); } } }
/** * Create or update a comment. * * @since 2.0.0 * @access public * * @param int $DiscussionID Unique ID to add the comment to. If blank, this method will throw an error. */ public function Comment($DiscussionID = '') { // Get $DiscussionID from RequestArgs if valid if ($DiscussionID == '' && count($this->RequestArgs)) { if (is_numeric($this->RequestArgs[0])) { $DiscussionID = $this->RequestArgs[0]; } } // If invalid $DiscussionID, get from form. $this->Form->SetModel($this->CommentModel); $DiscussionID = is_numeric($DiscussionID) ? $DiscussionID : $this->Form->GetFormValue('DiscussionID', 0); // Set discussion data $this->DiscussionID = $DiscussionID; $this->Discussion = $Discussion = $this->DiscussionModel->GetID($DiscussionID); // Is this an embedded comment being posted to a discussion that doesn't exist yet? $vanilla_type = $this->Form->GetFormValue('vanilla_type', ''); $vanilla_url = $this->Form->GetFormValue('vanilla_url', ''); $vanilla_category_id = $this->Form->GetFormValue('vanilla_category_id', ''); $Attributes = array('ForeignUrl' => $vanilla_url); $vanilla_identifier = $this->Form->GetFormValue('vanilla_identifier', ''); // Only allow vanilla identifiers of 32 chars or less - md5 if larger if (strlen($vanilla_identifier) > 32) { $Attributes['vanilla_identifier'] = $vanilla_identifier; $vanilla_identifier = md5($vanilla_identifier); } if (!$Discussion && $vanilla_url != '' && $vanilla_identifier != '') { $Discussion = $Discussion = $this->DiscussionModel->GetForeignID($vanilla_identifier, $vanilla_type); if ($Discussion) { $this->DiscussionID = $DiscussionID = $Discussion->DiscussionID; $this->Form->SetValue('DiscussionID', $DiscussionID); } } // If so, create it! if (!$Discussion && $vanilla_url != '' && $vanilla_identifier != '') { // Add these values back to the form if they exist! $this->Form->AddHidden('vanilla_identifier', $vanilla_identifier); $this->Form->AddHidden('vanilla_type', $vanilla_type); $this->Form->AddHidden('vanilla_url', $vanilla_url); $this->Form->AddHidden('vanilla_category_id', $vanilla_category_id); $PageInfo = FetchPageInfo($vanilla_url); $Title = GetValue('Title', $PageInfo, ''); if ($Title == '') { $Title = T('Undefined discussion subject.'); } $Description = GetValue('Description', $PageInfo, ''); $Images = GetValue('Images', $PageInfo, array()); $LinkText = T('EmbededDiscussionLinkText', 'Read the full story here'); $Body = FormatString(' <div class="EmbeddedContent">{Image}<strong>{Title}</strong> <p>{Excerpt}</p> <p><a href="{Url}">{LinkText}</a></p> <div class="ClearFix"></div> </div>', array('Title' => $Title, 'Excerpt' => $Description, 'Image' => count($Images) > 0 ? Img(GetValue(0, $Images), array('class' => 'LeftAlign')) : '', 'Url' => $vanilla_url, 'LinkText' => $LinkText)); if ($Body == '') { $Body = $vanilla_url; } if ($Body == '') { $Body = T('Undefined discussion body.'); } // Validate the CategoryID for inserting. $Category = CategoryModel::Categories($vanilla_category_id); if (!$Category) { $vanilla_category_id = C('Vanilla.Embed.DefaultCategoryID', 0); if ($vanilla_category_id <= 0) { // No default category defined, so grab the first non-root category and use that. $vanilla_category_id = $this->DiscussionModel->SQL->Select('CategoryID')->From('Category')->Where('CategoryID >', 0)->Get()->FirstRow()->CategoryID; // No categories in the db? default to 0 if (!$vanilla_category_id) { $vanilla_category_id = 0; } } } else { $vanilla_category_id = $Category['CategoryID']; } $SystemUserID = Gdn::UserModel()->GetSystemUserID(); $EmbeddedDiscussionData = array('InsertUserID' => $SystemUserID, 'DateInserted' => Gdn_Format::ToDateTime(), 'UpdateUserID' => $SystemUserID, 'DateUpdated' => Gdn_Format::ToDateTime(), 'CategoryID' => $vanilla_category_id, 'ForeignID' => $vanilla_identifier, 'Type' => $vanilla_type, 'Name' => $Title, 'Body' => $Body, 'Format' => 'Html', 'Attributes' => serialize($Attributes)); $this->EventArguments['Discussion'] = $EmbeddedDiscussionData; $this->FireEvent('BeforeEmbedDiscussion'); $DiscussionID = $this->DiscussionModel->SQL->Insert('Discussion', $EmbeddedDiscussionData); $ValidationResults = $this->DiscussionModel->ValidationResults(); if (count($ValidationResults) == 0 && $DiscussionID > 0) { $this->Form->AddHidden('DiscussionID', $DiscussionID); // Put this in the form so reposts won't cause new discussions. $this->Form->SetFormValue('DiscussionID', $DiscussionID); // Put this in the form values so it is used when saving comments. $this->SetJson('DiscussionID', $DiscussionID); $this->Discussion = $Discussion = $this->DiscussionModel->GetID($DiscussionID); // Update the category discussion count if ($vanilla_category_id > 0) { $this->DiscussionModel->UpdateDiscussionCount($vanilla_category_id, $DiscussionID); } } } // If no discussion was found, error out if (!$Discussion) { $this->Form->AddError(T('Failed to find discussion for commenting.')); } $PermissionCategoryID = GetValue('PermissionCategoryID', $Discussion); // Setup head $this->AddJsFile('jquery.autogrow.js'); $this->AddJsFile('post.js'); $this->AddJsFile('autosave.js'); // Setup comment model, $CommentID, $DraftID $Session = Gdn::Session(); $CommentID = isset($this->Comment) && property_exists($this->Comment, 'CommentID') ? $this->Comment->CommentID : ''; $DraftID = isset($this->Comment) && property_exists($this->Comment, 'DraftID') ? $this->Comment->DraftID : ''; $this->EventArguments['CommentID'] = $CommentID; $this->EventArguments['DraftID'] = $DraftID; // Determine whether we are editing $Editing = $CommentID > 0 || $DraftID > 0; $this->EventArguments['Editing'] = $Editing; // If closed, cancel & go to discussion if ($Discussion && $Discussion->Closed == 1 && !$Editing && !$Session->CheckPermission('Vanilla.Discussions.Close', TRUE, 'Category', $PermissionCategoryID)) { Redirect(DiscussionUrl($Discussion)); } // Add hidden IDs to form $this->Form->AddHidden('DiscussionID', $DiscussionID); $this->Form->AddHidden('CommentID', $CommentID); $this->Form->AddHidden('DraftID', $DraftID, TRUE); // Check permissions if ($Discussion && $Editing) { // Permisssion to edit if ($this->Comment->InsertUserID != $Session->UserID) { $this->Permission('Vanilla.Comments.Edit', TRUE, 'Category', $Discussion->PermissionCategoryID); } // Make sure that content can (still) be edited. $EditContentTimeout = C('Garden.EditContentTimeout', -1); $CanEdit = $EditContentTimeout == -1 || strtotime($this->Comment->DateInserted) + $EditContentTimeout > time(); if (!$CanEdit) { $this->Permission('Vanilla.Comments.Edit', TRUE, 'Category', $Discussion->PermissionCategoryID); } // Make sure only moderators can edit closed things if ($Discussion->Closed) { $this->Permission('Vanilla.Comments.Edit', TRUE, 'Category', $Discussion->PermissionCategoryID); } } else { if ($Discussion) { // Permission to add $this->Permission('Vanilla.Comments.Add', TRUE, 'Category', $Discussion->PermissionCategoryID); } } if (!$this->Form->IsPostBack()) { // Form was validly submitted if (isset($this->Comment)) { $this->Form->SetData((array) $this->Comment); } } else { // Save as a draft? $FormValues = $this->Form->FormValues(); $FormValues = $this->CommentModel->FilterForm($FormValues); if ($DraftID == 0) { $DraftID = $this->Form->GetFormValue('DraftID', 0); } $Type = GetIncomingValue('Type'); $Draft = $Type == 'Draft'; $this->EventArguments['Draft'] = $Draft; $Preview = $Type == 'Preview'; if ($Draft) { $DraftID = $this->DraftModel->Save($FormValues); $this->Form->AddHidden('DraftID', $DraftID, TRUE); $this->Form->SetValidationResults($this->DraftModel->ValidationResults()); } else { if (!$Preview) { $Inserted = !$CommentID; $CommentID = $this->CommentModel->Save($FormValues); // The comment is now half-saved. if (is_numeric($CommentID) && $CommentID > 0) { if ($this->_DeliveryType == DELIVERY_TYPE_ALL) { $this->CommentModel->Save2($CommentID, $Inserted, TRUE, TRUE); } else { $this->JsonTarget('', Url("/vanilla/post/comment2.json?commentid={$CommentID}&inserted={$Inserted}"), 'Ajax'); } // $Discussion = $this->DiscussionModel->GetID($DiscussionID); $Comment = $this->CommentModel->GetID($CommentID); $this->EventArguments['Discussion'] = $Discussion; $this->EventArguments['Comment'] = $Comment; $this->FireEvent('AfterCommentSave'); } elseif ($CommentID === SPAM) { $this->StatusMessage = T('Your post has been flagged for moderation.'); } $this->Form->SetValidationResults($this->CommentModel->ValidationResults()); if ($CommentID > 0 && $DraftID > 0) { $this->DraftModel->Delete($DraftID); } } } // Handle non-ajax requests first: if ($this->_DeliveryType == DELIVERY_TYPE_ALL) { if ($this->Form->ErrorCount() == 0) { // Make sure that this form knows what comment we are editing. if ($CommentID > 0) { $this->Form->AddHidden('CommentID', $CommentID); } // If the comment was not a draft if (!$Draft) { // Redirect to the new comment. if ($CommentID > 0) { Redirect("discussion/comment/{$CommentID}/#Comment_{$CommentID}"); } elseif ($CommentID == SPAM) { $this->SetData('DiscussionUrl', DiscussionUrl($Discussion)); $this->View = 'Spam'; } } elseif ($Preview) { // If this was a preview click, create a comment shell with the values for this comment $this->Comment = new stdClass(); $this->Comment->InsertUserID = $Session->User->UserID; $this->Comment->InsertName = $Session->User->Name; $this->Comment->InsertPhoto = $Session->User->Photo; $this->Comment->DateInserted = Gdn_Format::Date(); $this->Comment->Body = ArrayValue('Body', $FormValues, ''); $this->AddAsset('Content', $this->FetchView('preview')); } else { // If this was a draft save, notify the user about the save $this->InformMessage(sprintf(T('Draft saved at %s'), Gdn_Format::Date())); } } } else { // Handle ajax-based requests if ($this->Form->ErrorCount() > 0) { // Return the form errors $this->ErrorMessage($this->Form->Errors()); } else { // Make sure that the ajax request form knows about the newly created comment or draft id $this->SetJson('CommentID', $CommentID); $this->SetJson('DraftID', $DraftID); if ($Preview) { // If this was a preview click, create a comment shell with the values for this comment $this->Comment = new stdClass(); $this->Comment->InsertUserID = $Session->User->UserID; $this->Comment->InsertName = $Session->User->Name; $this->Comment->InsertPhoto = $Session->User->Photo; $this->Comment->DateInserted = Gdn_Format::Date(); $this->Comment->Body = ArrayValue('Body', $FormValues, ''); $this->View = 'preview'; } elseif (!$Draft) { // If the comment was not a draft // If Editing a comment if ($Editing) { // Just reload the comment in question $this->Offset = 1; $Comments = $this->CommentModel->GetIDData($CommentID); $this->SetData('Comments', $Comments); // Load the discussion $this->ControllerName = 'discussion'; $this->View = 'comments'; // Also define the discussion url in case this request came from the post screen and needs to be redirected to the discussion $this->SetJson('DiscussionUrl', DiscussionUrl($this->Discussion) . '#Comment_' . $CommentID); } else { // If the comment model isn't sorted by DateInserted or CommentID then we can't do any fancy loading of comments. $OrderBy = GetValueR('0.0', $this->CommentModel->OrderBy()); // $Redirect = !in_array($OrderBy, array('c.DateInserted', 'c.CommentID')); // $DisplayNewCommentOnly = $this->Form->GetFormValue('DisplayNewCommentOnly'); // if (!$Redirect) { // // Otherwise load all new comments that the user hasn't seen yet // $LastCommentID = $this->Form->GetFormValue('LastCommentID'); // if (!is_numeric($LastCommentID)) // $LastCommentID = $CommentID - 1; // Failsafe back to this new comment if the lastcommentid was not defined properly // // // Don't reload the first comment if this new comment is the first one. // $this->Offset = $LastCommentID == 0 ? 1 : $this->CommentModel->GetOffset($LastCommentID); // // Do not load more than a single page of data... // $Limit = C('Vanilla.Comments.PerPage', 30); // // // Redirect if the new new comment isn't on the same page. // $Redirect |= !$DisplayNewCommentOnly && PageNumber($this->Offset, $Limit) != PageNumber($Discussion->CountComments - 1, $Limit); // } // if ($Redirect) { // // The user posted a comment on a page other than the last one, so just redirect to the last page. // $this->RedirectUrl = Gdn::Request()->Url("discussion/comment/$CommentID/#Comment_$CommentID", TRUE); // } else { // // Make sure to load all new comments since the page was last loaded by this user // if ($DisplayNewCommentOnly) $this->Offset = $this->CommentModel->GetOffset($CommentID); $Comments = $this->CommentModel->GetIDData($CommentID); $this->SetData('Comments', $Comments); $this->SetData('NewComments', TRUE); $this->ClassName = 'DiscussionController'; $this->ControllerName = 'discussion'; $this->View = 'comments'; // } // Make sure to set the user's discussion watch records $CountComments = $this->CommentModel->GetCount($DiscussionID); $Limit = is_object($this->Data('Comments')) ? $this->Data('Comments')->NumRows() : $Discussion->CountComments; $Offset = $CountComments - $Limit; $this->CommentModel->SetWatch($this->Discussion, $Limit, $Offset, $CountComments); } } else { // If this was a draft save, notify the user about the save $this->InformMessage(sprintf(T('Draft saved at %s'), Gdn_Format::Date())); } // And update the draft count $UserModel = Gdn::UserModel(); $CountDrafts = $UserModel->GetAttribute($Session->UserID, 'CountDrafts', 0); $this->SetJson('MyDrafts', T('My Drafts')); $this->SetJson('CountDrafts', $CountDrafts); } } } // Include data for FireEvent if (property_exists($this, 'Discussion')) { $this->EventArguments['Discussion'] = $this->Discussion; } if (property_exists($this, 'Comment')) { $this->EventArguments['Comment'] = $this->Comment; } $this->FireEvent('BeforeCommentRender'); if ($this->DeliveryType() == DELIVERY_TYPE_DATA) { $Comment = $this->Data('Comments')->FirstRow(DATASET_TYPE_ARRAY); if ($Comment) { $Photo = $Comment['InsertPhoto']; if (strpos($Photo, '//') === FALSE) { $Photo = Gdn_Upload::Url(ChangeBasename($Photo, 'n%s')); } $Comment['InsertPhoto'] = $Photo; } $this->Data = array('Comment' => $Comment); $this->RenderData($this->Data); } else { // Render default view. $this->Render(); } }
/** * Insert or update meta data about the comment. * * Updates unread comment totals, bookmarks, and activity. Sends notifications. * * @since 2.0.0 * @access public * * @param array $CommentID Unique ID for this comment. * @param int $Insert Used as a boolean for whether this is a new comment. * @param bool $CheckExisting Not used. * @param bool $IncUser Whether or not to just increment the user's comment count rather than recalculate it. */ public function save2($CommentID, $Insert, $CheckExisting = true, $IncUser = false) { $Session = Gdn::session(); $UserModel = Gdn::userModel(); // Load comment data $Fields = $this->getID($CommentID, DATASET_TYPE_ARRAY); // Clear any session stashes related to this discussion $DiscussionModel = new DiscussionModel(); $DiscussionID = val('DiscussionID', $Fields); $Discussion = $DiscussionModel->getID($DiscussionID); $Session->Stash('CommentForForeignID_' . GetValue('ForeignID', $Discussion)); // Make a quick check so that only the user making the comment can make the notification. // This check may be used in the future so should not be depended on later in the method. if (Gdn::controller()->deliveryType() === DELIVERY_TYPE_ALL && $Fields['InsertUserID'] != $Session->UserID) { return; } // Update the discussion author's CountUnreadDiscussions (ie. // the number of discussions created by the user that s/he has // unread messages in) if this comment was not added by the // discussion author. $this->UpdateUser($Fields['InsertUserID'], $IncUser && $Insert); // Mark the user as participated. $this->SQL->replace('UserDiscussion', array('Participated' => 1), array('DiscussionID' => $DiscussionID, 'UserID' => val('InsertUserID', $Fields))); if ($Insert) { // UPDATE COUNT AND LAST COMMENT ON CATEGORY TABLE if ($Discussion->CategoryID > 0) { $Category = CategoryModel::categories($Discussion->CategoryID); if ($Category) { $CountComments = val('CountComments', $Category, 0) + 1; if ($CountComments < self::COMMENT_THRESHOLD_SMALL || $CountComments < self::COMMENT_THRESHOLD_LARGE && $CountComments % self::COUNT_RECALC_MOD == 0) { $CountComments = $this->SQL->select('CountComments', 'sum', 'CountComments')->from('Discussion')->where('CategoryID', $Discussion->CategoryID)->get()->firstRow()->CountComments; } } $CategoryModel = new CategoryModel(); $CategoryModel->setField($Discussion->CategoryID, array('LastDiscussionID' => $DiscussionID, 'LastCommentID' => $CommentID, 'CountComments' => $CountComments, 'LastDateInserted' => $Fields['DateInserted'])); // Update the cache. $CategoryCache = array('LastTitle' => $Discussion->Name, 'LastUserID' => $Fields['InsertUserID'], 'LastUrl' => DiscussionUrl($Discussion) . '#latest'); CategoryModel::SetCache($Discussion->CategoryID, $CategoryCache); } // Prepare the notification queue. $ActivityModel = new ActivityModel(); $HeadlineFormat = t('HeadlineFormat.Comment', '{ActivityUserID,user} commented on <a href="{Url,html}">{Data.Name,text}</a>'); $Category = CategoryModel::categories($Discussion->CategoryID); $Activity = array('ActivityType' => 'Comment', 'ActivityUserID' => $Fields['InsertUserID'], 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Comment', 'RecordID' => $CommentID, 'Route' => "/discussion/comment/{$CommentID}#Comment_{$CommentID}", 'Data' => array('Name' => $Discussion->Name, 'Category' => val('Name', $Category))); // Allow simple fulltext notifications if (c('Vanilla.Activity.ShowCommentBody', false)) { $Activity['Story'] = val('Body', $Fields); $Activity['Format'] = val('Format', $Fields); } // Pass generic activity to events. $this->EventArguments['Activity'] = $Activity; // Notify users who have bookmarked the discussion. $BookmarkData = $DiscussionModel->GetBookmarkUsers($DiscussionID); foreach ($BookmarkData->result() as $Bookmark) { // Check user can still see the discussion. if (!$UserModel->GetCategoryViewPermission($Bookmark->UserID, $Discussion->CategoryID)) { continue; } $Activity['NotifyUserID'] = $Bookmark->UserID; $Activity['Data']['Reason'] = 'bookmark'; $ActivityModel->Queue($Activity, 'BookmarkComment', array('CheckRecord' => true)); } // Notify users who have participated in the discussion. $ParticipatedData = $DiscussionModel->GetParticipatedUsers($DiscussionID); foreach ($ParticipatedData->result() as $UserRow) { if (!$UserModel->GetCategoryViewPermission($UserRow->UserID, $Discussion->CategoryID)) { continue; } $Activity['NotifyUserID'] = $UserRow->UserID; $Activity['Data']['Reason'] = 'participated'; $ActivityModel->Queue($Activity, 'ParticipateComment', array('CheckRecord' => true)); } // Record user-comment activity. if ($Discussion != false) { $InsertUserID = val('InsertUserID', $Discussion); // Check user can still see the discussion. if ($UserModel->GetCategoryViewPermission($InsertUserID, $Discussion->CategoryID)) { $Activity['NotifyUserID'] = $InsertUserID; $Activity['Data']['Reason'] = 'mine'; $ActivityModel->Queue($Activity, 'DiscussionComment'); } } // Record advanced notifications. if ($Discussion !== false) { $Activity['Data']['Reason'] = 'advanced'; $this->RecordAdvancedNotications($ActivityModel, $Activity, $Discussion); } // Notify any users who were mentioned in the comment. $Usernames = GetMentions($Fields['Body']); foreach ($Usernames as $i => $Username) { $User = $UserModel->GetByUsername($Username); if (!$User) { unset($Usernames[$i]); continue; } // Check user can still see the discussion. if (!$UserModel->GetCategoryViewPermission($User->UserID, $Discussion->CategoryID)) { continue; } $HeadlineFormatBak = $Activity['HeadlineFormat']; $Activity['HeadlineFormat'] = t('HeadlineFormat.Mention', '{ActivityUserID,user} mentioned you in <a href="{Url,html}">{Data.Name,text}</a>'); $Activity['NotifyUserID'] = $User->UserID; $Activity['Data']['Reason'] = 'mention'; $ActivityModel->Queue($Activity, 'Mention'); $Activity['HeadlineFormat'] = $HeadlineFormatBak; } unset($Activity['Data']['Reason']); // Throw an event for users to add their own events. $this->EventArguments['Comment'] = $Fields; $this->EventArguments['Discussion'] = $Discussion; $this->EventArguments['NotifiedUsers'] = array_keys(ActivityModel::$Queue); $this->EventArguments['MentionedUsers'] = $Usernames; $this->EventArguments['ActivityModel'] = $ActivityModel; $this->fireEvent('BeforeNotification'); // Send all notifications. $ActivityModel->SaveQueue(); } }
public function CommentModel_AfterSaveComment_Handler($Sender, $Args) { if (!$this->SocialSharing()) { return; } if (!$this->AccessToken()) { return; } $Share = GetValueR('FormPostValues.ShareTwitter', $Args); if ($Share && $this->AccessToken()) { $Row = $Args['FormPostValues']; $DiscussionModel = new DiscussionModel(); $Discussion = $DiscussionModel->GetID(GetValue('DiscussionID', $Row)); if (!$Discussion) { return; } $Url = DiscussionUrl($Discussion, '', TRUE); $Message = SliceTwitter(Gdn_Format::PlainText($Row['Body'], $Row['Format'])) . ' ' . $Url; $R = $this->API('/statuses/update.json', array('status' => $Message), 'POST'); // decho($R); // die(); // } else { // die("$Share ".$this->AccessToken()); } }
/** * 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. * @return int $DiscussionID Unique ID of the discussion. */ public function Save($FormPostValues) { $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 = GetValue('CategoryID', $FormPostValues); if ($CategoryID > 0) { $Category = CategoryModel::Categories($CategoryID); if ($Category && !$Session->CheckPermission('Vanilla.Discussions.Add', TRUE, 'Category', GetValue('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 = ArrayValue('DiscussionID', $FormPostValues, ''); // See if there is a source ID. if (GetValue('SourceID', $FormPostValues)) { $DiscussionID = $this->SQL->GetWhere('Discussion', ArrayTranslate($FormPostValues, array('Source', 'SourceID')))->Value('DiscussionID'); if ($DiscussionID) { $FormPostValues['DiscussionID'] = $DiscussionID; } } elseif (GetValue('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 (!GetValue('CategoryID', $FormPostValues) && !C('Vanilla.Categories.Use')) { $FormPostValues['CategoryID'] = GetValue('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 (ArrayValue('Announce', $FormPostValues, '') === FALSE) { $FormPostValues['Announce'] = 0; } if (ArrayValue('Closed', $FormPostValues, '') === FALSE) { $FormPostValues['Closed'] = 0; } if (ArrayValue('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(); // Get DiscussionID if one was sent $DiscussionID = intval(ArrayValue('DiscussionID', $Fields, 0)); // Remove the primary key from the fields for saving $Fields = RemoveKeyFromArray($Fields, 'DiscussionID'); $StoredCategoryID = FALSE; if ($DiscussionID > 0) { // Updating $Stored = $this->GetID($DiscussionID, DATASET_TYPE_ARRAY); // Clear the cache if necessary. if (GetValue('Announce', $Stored) != GetValue('Announce', $Fields)) { $CacheKeys = array('Announcements'); $this->SQL->Cache($CacheKeys); } 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 (GetValue('CategoryID', $Stored) != GetValue('CategoryID', $Fields)) { $StoredCategoryID = GetValue('CategoryID', $Stored); } } else { // Inserting. if (!GetValue('Format', $Fields) || C('Garden.ForceInputFormatter')) { $Fields['Format'] = C('Garden.InputFormatter', ''); } if (C('Vanilla.QueueNotifications')) { $Fields['Notified'] = ActivityModel::SENT_PENDING; } // Check for spam. $Spam = SpamModel::IsSpam('Discussion', $Fields); if ($Spam) { return SPAM; } // Check for approval $ApprovalRequired = CheckRestriction('Vanilla.Approval.Require'); if ($ApprovalRequired && !GetValue('Verified', Gdn::Session()->User)) { LogModel::Insert('Pending', 'Discussion', $Fields); return UNAPPROVED; } // Create discussion $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 (GetValue('Announce', $Fields)) { Gdn::Cache()->Remove('Announcements'); } } // Update the user's discussion count. $this->UpdateUserDiscussionCount(Gdn::Session()->UserID); // Assign the new DiscussionID to the comment before saving. $FormPostValues['IsNewDiscussion'] = TRUE; $FormPostValues['DiscussionID'] = $DiscussionID; // Do data prep. $DiscussionName = ArrayValue('Name', $Fields, ''); $Story = ArrayValue('Body', $Fields, ''); $NotifiedUsers = array(); $UserModel = Gdn::UserModel(); $ActivityModel = new ActivityModel(); if (GetValue('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(GetValue('CategoryID', $Fields)); $Activity = array('ActivityType' => 'Discussion', 'ActivityUserID' => $Fields['InsertUserID'], 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Discussion', 'RecordID' => $DiscussionID, 'Route' => DiscussionUrl($Fields), 'Data' => array('Name' => $DiscussionName, 'Category' => GetValue('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 = array_merge(GetMentions($DiscussionName), GetMentions($Story)); $Usernames = array_unique($Usernames); // Use our generic Activity for events, not mentions $this->EventArguments['Activity'] = $Activity; // Notifications for mentions foreach ($Usernames as $Username) { $User = $UserModel->GetByUsername($Username); if (!$User) { continue; } // Check user can still see the discussion. if (!$UserModel->GetCategoryViewPermission($User->UserID, GetValue('CategoryID', $Fields))) { continue; } $Activity['HeadlineFormat'] = T('HeadlineFormat.Mention', '{ActivityUserID,user} mentioned you in <a href="{Url,html}">{Data.Name,text}</a>'); $Activity['NotifyUserID'] = GetValue('UserID', $User); $ActivityModel->Queue($Activity, 'Mention'); } // Notify everyone that has advanced notifications. if (!C('Vanilla.QueueNotifications')) { try { $Fields['DiscussionID'] = $DiscussionID; $this->NotifyNewDiscussion($Fields, $ActivityModel, $Activity); } catch (Exception $Ex) { throw $Ex; } } // 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_ARRAY); $CategoryID = GetValue('CategoryID', $Discussion, FALSE); // Update discussion counter for affected categories $this->UpdateDiscussionCount($CategoryID, $Insert ? $Discussion : FALSE); 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; }
/** * Alternate version of Index that uses the embed master view. * * @param int $DiscussionID Unique identifier, if discussion has been created. * @param string $DiscussionStub Deprecated. * @param int $Offset * @param int $Limit */ public function Embed($DiscussionID = '', $DiscussionStub = '', $Offset = '', $Limit = '') { $this->Title(T('Comments')); // Add theme data $this->Theme = C('Garden.CommentsTheme', $this->Theme); Gdn_Theme::Section('Comments'); // Force view options $this->MasterView = 'empty'; $this->CanEditComments = FALSE; // Don't show the comment checkboxes on the embed comments page // Add some css to help with the transparent bg on embedded comments if ($this->Head) { $this->Head->AddString('<style type="text/css"> body { background: transparent !important; } </style>'); } // Javascript files & options $this->AddJsFile('jquery.gardenmorepager.js'); $this->AddJsFile('jquery.autogrow.js'); $this->RemoveJsFile('autosave.js'); $this->AddJsFile('discussion.js'); $this->AddDefinition('DoInform', '0'); // Suppress inform messages on embedded page. $this->AddDefinition('SelfUrl', Gdn::Request()->PathAndQuery()); $this->AddDefinition('Embedded', TRUE); // Define incoming variables (prefer querystring parameters over method parameters) $DiscussionID = is_numeric($DiscussionID) && $DiscussionID > 0 ? $DiscussionID : 0; $DiscussionID = GetIncomingValue('vanilla_discussion_id', $DiscussionID); $Offset = GetIncomingValue('Offset', $Offset); $Limit = GetIncomingValue('Limit', $Limit); $vanilla_identifier = GetIncomingValue('vanilla_identifier', ''); // Only allow vanilla identifiers of 32 chars or less - md5 if larger if (strlen($vanilla_identifier) > 32) { $vanilla_identifier = md5($vanilla_identifier); } $vanilla_type = GetIncomingValue('vanilla_type', 'page'); $vanilla_url = GetIncomingValue('vanilla_url', ''); $vanilla_category_id = GetIncomingValue('vanilla_category_id', ''); $ForeignSource = array('vanilla_identifier' => $vanilla_identifier, 'vanilla_type' => $vanilla_type, 'vanilla_url' => $vanilla_url, 'vanilla_category_id' => $vanilla_category_id); $this->SetData('ForeignSource', $ForeignSource); // Set comment sorting $SortComments = C('Garden.Embed.SortComments') == 'desc' ? 'desc' : 'asc'; $this->SetData('SortComments', $SortComments); // Retrieve the discussion record $Discussion = FALSE; if ($DiscussionID > 0) { $Discussion = $this->DiscussionModel->GetID($DiscussionID); } else { if ($vanilla_identifier != '' && $vanilla_type != '') { $Discussion = $this->DiscussionModel->GetForeignID($vanilla_identifier, $vanilla_type); } } // Set discussion data if we have one for this page if ($Discussion) { $this->Permission('Vanilla.Discussions.View', TRUE, 'Category', $Discussion->PermissionCategoryID); $this->SetData('Discussion', $Discussion, TRUE); $this->SetData('DiscussionID', $Discussion->DiscussionID, TRUE); $this->Title($Discussion->Name); // Actual number of comments, excluding the discussion itself $ActualResponses = $Discussion->CountComments; // Define the query offset & limit if (!is_numeric($Limit) || $Limit < 0) { $Limit = C('Garden.Embed.CommentsPerPage', 30); } $OffsetProvided = $Offset != ''; list($Offset, $Limit) = OffsetLimit($Offset, $Limit); $this->Offset = $Offset; if (C('Vanilla.Comments.AutoOffset')) { if ($ActualResponses <= $Limit) { $this->Offset = 0; } if ($this->Offset == $ActualResponses) { $this->Offset -= $Limit; } } else { if ($this->Offset == '') { $this->Offset = 0; } } if ($this->Offset < 0) { $this->Offset = 0; } // Set the canonical url to have the proper page title. $this->CanonicalUrl(DiscussionUrl($Discussion, PageNumber($this->Offset, $Limit))); // Load the comments. $CurrentOrderBy = $this->CommentModel->OrderBy(); if (StringBeginsWith(GetValueR('0.0', $CurrentOrderBy), 'c.DateInserted')) { $this->CommentModel->OrderBy('c.DateInserted ' . $SortComments); } // allow custom sort $this->SetData('Comments', $this->CommentModel->Get($Discussion->DiscussionID, $Limit, $this->Offset), TRUE); if (count($this->CommentModel->Where()) > 0) { $ActualResponses = FALSE; } $this->SetData('_Count', $ActualResponses); // Build a pager $PagerFactory = new Gdn_PagerFactory(); $this->EventArguments['PagerType'] = 'MorePager'; $this->FireEvent('BeforeBuildPager'); $this->Pager = $PagerFactory->GetPager($this->EventArguments['PagerType'], $this); $this->Pager->ClientID = 'Pager'; $this->Pager->MoreCode = 'More Comments'; $this->Pager->Configure($this->Offset, $Limit, $ActualResponses, 'discussion/embed/' . $Discussion->DiscussionID . '/' . Gdn_Format::Url($Discussion->Name) . '/%1$s'); $this->Pager->CurrentRecords = $this->Comments->NumRows(); $this->FireEvent('AfterBuildPager'); } // Define the form for the comment input $this->Form = Gdn::Factory('Form', 'Comment'); $this->Form->Action = Url('/vanilla/post/comment/'); $this->Form->AddHidden('CommentID', ''); $this->Form->AddHidden('Embedded', 'true'); // Tell the post controller that this is an embedded page (in case there are custom views it needs to pick up from a theme). $this->Form->AddHidden('DisplayNewCommentOnly', 'true'); // Only load/display the new comment after posting (don't load all new comments since the page last loaded). // Grab the page title if ($this->Request->Get('title')) { $this->Form->SetValue('Name', $this->Request->Get('title')); } // Set existing DiscussionID for comment form if ($Discussion) { $this->Form->AddHidden('DiscussionID', $Discussion->DiscussionID); } foreach ($ForeignSource as $Key => $Val) { // Drop the foreign source information into the form so it can be used if creating a discussion $this->Form->AddHidden($Key, $Val); // Also drop it into the definitions so it can be picked up for stashing comments $this->AddDefinition($Key, $Val); } // Retrieve & apply the draft if there is one: $Draft = FALSE; if (Gdn::Session()->UserID && $Discussion) { $DraftModel = new DraftModel(); $Draft = $DraftModel->Get(Gdn::Session()->UserID, 0, 1, $Discussion->DiscussionID)->FirstRow(); $this->Form->AddHidden('DraftID', $Draft ? $Draft->DraftID : ''); } if ($Draft) { $this->Form->SetFormValue('Body', $Draft->Body); } else { // Look in the session stash for a comment $StashComment = Gdn::Session()->Stash('CommentForForeignID_' . $ForeignSource['vanilla_identifier'], '', FALSE); if ($StashComment) { $this->Form->SetValue('Body', $StashComment); $this->Form->SetFormValue('Body', $StashComment); } } // Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { if ($this->Discussion) { $this->SetJson('LessRow', $this->Pager->ToString('less')); $this->SetJson('MoreRow', $this->Pager->ToString('more')); } $this->View = 'comments'; } // Ordering note for JS if ($SortComments == 'desc') { $this->AddDefinition('PrependNewComments', '1'); } // Report the discussion id so js can use it. if ($Discussion) { $this->AddDefinition('DiscussionID', $Discussion->DiscussionID); } $this->FireEvent('BeforeDiscussionRender'); $this->Render(); }