Format a timestamp or the current time to go into the database.
public static toDateTime ( integer $Timestamp = '' ) : string | ||
$Timestamp | integer | |
return | string | The formatted date and time. |
/** * Grabs all new notifications and adds them to the sender's inform queue. * * This method gets called by dashboard's hooks file to display new * notifications on every pageload. * * @since 2.0.18 * @access public * * @param Gdn_Controller $Sender The object calling this method. */ public static function informNotifications($Sender) { $Session = Gdn::session(); if (!$Session->isValid()) { return; } $ActivityModel = new ActivityModel(); // Get five pending notifications. $Where = array('NotifyUserID' => Gdn::session()->UserID, 'Notified' => ActivityModel::SENT_PENDING); // If we're in the middle of a visit only get very recent notifications. $Where['DateUpdated >'] = Gdn_Format::toDateTime(strtotime('-5 minutes')); $Activities = $ActivityModel->getWhere($Where, 0, 5)->resultArray(); $ActivityIDs = array_column($Activities, 'ActivityID'); $ActivityModel->setNotified($ActivityIDs); $Sender->EventArguments['Activities'] =& $Activities; $Sender->fireEvent('InformNotifications'); foreach ($Activities as $Activity) { if ($Activity['Photo']) { $UserPhoto = anchor(img($Activity['Photo'], array('class' => 'ProfilePhotoMedium')), $Activity['Url'], 'Icon'); } else { $UserPhoto = ''; } $Excerpt = Gdn_Format::plainText($Activity['Story']); $ActivityClass = ' Activity-' . $Activity['ActivityType']; $Sender->informMessage($UserPhoto . Wrap($Activity['Headline'], 'div', array('class' => 'Title')) . Wrap($Excerpt, 'div', array('class' => 'Excerpt')), 'Dismissable AutoDismiss' . $ActivityClass . ($UserPhoto == '' ? '' : ' HasIcon')); } }
/** * Resolves a discussion * * @param object $discussion * @param int $resolve * @return void */ public function resolve(&$discussion, $resolve) { $resolution = array('Resolved' => $resolve, 'DateResolved' => $resolve ? Gdn_Format::toDateTime() : null, 'ResolvedUserID' => $resolve ? Gdn::session()->UserID : null); $discussionID = val('DiscussionID', $discussion); self::discussionModel()->setField($discussionID, $resolution); svalr('Resolved', $discussion, $resolve); }
/** * Create the target category that is needed for this plugin to work. */ public function structure() { $category = (array) CategoryModel::instance()->getByCode('welcome'); $cachedCategoryID = val('CategoryID', $category, false); if (!$cachedCategoryID) { $categoryModel = CategoryModel::instance(); $categoryModel->save(['ParentCategoryID' => -1, 'Depth' => 1, 'InsertUserID' => 1, 'UpdateUserID' => 1, 'DateInserted' => Gdn_Format::toDateTime(), 'DateUpdated' => Gdn_Format::toDateTime(), 'Name' => 'Welcome', 'UrlCode' => 'welcome', 'Description' => 'Introduce yourself to the community!', 'PermissionCategoryID' => -1]); } }
public function structure() { // Get a user for operations. $UserID = Gdn::sql()->GetWhere('User', array('Name' => 'Akismet', 'Admin' => 2))->Value('UserID'); if (!$UserID) { $UserID = Gdn::sql()->Insert('User', array('Name' => 'Akismet', 'Password' => RandomString('20'), 'HashMethod' => 'Random', 'Email' => '*****@*****.**', 'DateInserted' => Gdn_Format::toDateTime(), 'Admin' => '2')); } saveToConfig('Plugins.Akismet.UserID', $UserID); }
public function markRead($CategoryID, $TKey) { if (Gdn::session()->validateTransientKey($TKey)) { $this->CategoryModel->SaveUserTree($CategoryID, array('DateMarkedRead' => Gdn_Format::toDateTime())); } if ($this->deliveryType() == DELIVERY_TYPE_ALL) { redirect('/categories'); } $this->render(); }
/** * Show the contributor agreement form & workflow for signing. */ public function index() { if (!Gdn::session()->isValid()) { $this->View = 'signin'; } else { if ($this->Form->authenticatedPostBack() && $this->Form->getFormValue('Agree', '') == '1') { Gdn::sql()->update('User')->set('DateContributorAgreement', Gdn_Format::toDateTime(), true, false)->where('UserID', Gdn::session()->UserID)->put(); $this->View = 'done'; } } $this->render(); }
/** * * * @param string $ID * @param string $ServeFile */ public function index($ID = '', $ServeFile = '0') { $this->addJsFile('jquery.js'); // Define the item being downloaded if (strtolower($ID) == 'vanilla') { $ID = 'vanilla-core'; } $UrlFilename = Gdn::request()->filename(); $PathInfo = pathinfo($UrlFilename); $Ext = val('extension', $PathInfo); if ($Ext == 'zip') { $ServeFile = '1'; $ID = $Ext = val('filename', $PathInfo); } // Find the requested addon $this->Addon = $this->AddonModel->getSlug($ID, true); $this->setData('Addon', $this->Addon); if (!is_array($this->Addon) || !val('File', $this->Addon)) { $this->Addon = array('Name' => 'Not Found', 'Version' => 'undefined', 'File' => ''); } else { $AddonID = $this->Addon['AddonID']; if ($ServeFile != '1') { $this->addJsFile('get.js'); } if ($ServeFile == '1') { // Record this download $this->Database->sql()->insert('Download', array('AddonID' => $AddonID, 'DateInserted' => Gdn_Format::toDateTime(), 'RemoteIp' => @$_SERVER['REMOTE_ADDR'])); $this->AddonModel->setProperty($AddonID, 'CountDownloads', $this->Addon['CountDownloads'] + 1); if (val('Slug', $this->Addon)) { $Filename = $this->Addon['Slug']; } else { $Filename = "{$this->Addon['Name']}-{$this->Addon['Version']}"; } $Filename = Gdn_Format::url($Filename) . '.zip'; $File = $this->Addon['File']; $Url = Gdn_Upload::url($File); Gdn_FileSystem::serveFile($Url, $Filename); } } $this->addModule('AddonHelpModule'); $this->render(); }
/** * Enforces AgeGate verification at registration submission. * * @param EntryController $sender Sending Controller. * @param array $args Arguments. */ public function entryController_registerValidation_handler($sender, $args) { $day = (int) $sender->Form->getFormValue('DateOfBirth_Day', 0); $month = (int) $sender->Form->getFormValue('DateOfBirth_Month', 0); $year = (int) $sender->Form->getFormValue('DateOfBirth_Year', 0); if ($day == 0 || $year == 0 || $month == 0) { $sender->UserModel->Validation->addValidationResult('', "Please select a valid Date of Birth."); return; } $dob = Gdn_Format::toDateTime(mktime(0, 0, 0, $month, $day, $year)); $datetime1 = new DateTime($year . '-' . $month . '-' . $day); $datetime2 = new DateTime(); $interval = $datetime1->diff($datetime2); $age = $interval->format('%y'); $minimumAge = C('Plugins.AgeGate.MinimumAge', 0); $minimumAgeWithConsent = C('Plugins.AgeGate.MinimumAgeWithConsent', false); $addConfirmation = C('Plugins.AgeGate.AddConfirmation', false); if ($minimumAgeWithConsent) { if ($addConfirmation && $age < $minimumAgeWithConsent && $age >= $minimumAge) { $sender->UserModel->Validation->applyRule('AgeGateConfirmation', 'Required', 'You must confirm you have received consent to register.'); return; } if ($age < $minimumAge) { $sender->UserModel->Validation->addValidationResult('', sprintf("You must be at least %d years old to register.", $minimumAge)); return; } } elseif ($age < $minimumAge) { if ($addConfirmation) { $sender->UserModel->Validation->applyRule('AgeGateConfirmation', 'Required', 'You must confirm you have received consent to register.'); } else { $sender->UserModel->Validation->addValidationResult('', sprintf("You must be at least %d years old to register.", $minimumAge)); } return; } // Set the value on the form so that it will be saved to user model if ($sender->Form->errorCount() == 0 && !$sender->UserModel->Validation->results()) { $sender->Form->_FormValues['DateOfBirth'] = $dob; } }
protected function getFileHeader() { $Now = Gdn_Format::toDateTime(); $Result = "<?php if (!defined('APPLICATION')) exit();\n/** This file was generated by the LocaleModel on {$Now} **/\n\n"; return $Result; }
/** * Save message from form submission. * * @since 2.0.0 * @access public * * @param array $FormPostValues Values submitted via form. * @return int Unique ID of message created or updated. */ public function save($FormPostValues, $Conversation = null, $Options = array()) { $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->addInsertFields($FormPostValues); $this->EventArguments['FormPostValues'] = $FormPostValues; $this->fireEvent('BeforeSaveValidation'); // Determine if spam check should be skipped. $SkipSpamCheck = !empty($Options['NewConversation']); // Validate the form posted values $MessageID = false; if ($this->validate($FormPostValues) && !$this->checkForSpam('ConversationMessage', $SkipSpamCheck)) { $Fields = $this->Validation->schemaValidationFields(); // All fields on the form that relate to the schema touchValue('Format', $Fields, c('Garden.InputFormatter', 'Html')); $this->EventArguments['Fields'] = $Fields; $this->fireEvent('BeforeSave'); $MessageID = $this->SQL->insert($this->Name, $Fields); $this->LastMessageID = $MessageID; $ConversationID = val('ConversationID', $Fields, 0); if (!$Conversation) { $Conversation = $this->SQL->getWhere('Conversation', array('ConversationID' => $ConversationID))->firstRow(DATASET_TYPE_ARRAY); } $Message = $this->getID($MessageID); $this->EventArguments['Conversation'] = $Conversation; $this->EventArguments['Message'] = $Message; $this->fireEvent('AfterSave'); // Get the new message count for the conversation. $SQLR = $this->SQL->select('MessageID', 'count', 'CountMessages')->select('MessageID', 'max', 'LastMessageID')->from('ConversationMessage')->where('ConversationID', $ConversationID)->get()->firstRow(DATASET_TYPE_ARRAY); if (sizeof($SQLR)) { list($CountMessages, $LastMessageID) = array_values($SQLR); } else { return; } // Update the conversation's DateUpdated field. $DateUpdated = Gdn_Format::toDateTime(); $this->SQL->update('Conversation c')->set('CountMessages', $CountMessages)->set('LastMessageID', $LastMessageID)->set('UpdateUserID', Gdn::session()->UserID)->set('DateUpdated', $DateUpdated)->where('ConversationID', $ConversationID)->put(); // Update the last message of the users that were previously up-to-date on their read messages. $this->SQL->update('UserConversation uc')->set('uc.LastMessageID', $MessageID)->set('uc.DateConversationUpdated', $DateUpdated)->where('uc.ConversationID', $ConversationID)->where('uc.Deleted', '0')->where('uc.CountReadMessages', $CountMessages - 1)->where('uc.UserID <>', $Session->UserID)->put(); // Update the date updated of the users that were not up-to-date. $this->SQL->update('UserConversation uc')->set('uc.DateConversationUpdated', $DateUpdated)->where('uc.ConversationID', $ConversationID)->where('uc.Deleted', '0')->where('uc.CountReadMessages <>', $CountMessages - 1)->where('uc.UserID <>', $Session->UserID)->put(); // Update the sending user. $this->SQL->update('UserConversation uc')->set('uc.CountReadMessages', $CountMessages)->set('Deleted', 0)->set('uc.DateConversationUpdated', $DateUpdated)->where('ConversationID', $ConversationID)->where('UserID', $Session->UserID)->put(); // Find users involved in this conversation $UserData = $this->SQL->select('UserID')->select('LastMessageID')->select('Deleted')->from('UserConversation')->where('ConversationID', $ConversationID)->get()->result(DATASET_TYPE_ARRAY); $UpdateCountUserIDs = array(); $NotifyUserIDs = array(); // Collapse for call to UpdateUserCache and ActivityModel. $InsertUserFound = false; foreach ($UserData as $UpdateUser) { $LastMessageID = val('LastMessageID', $UpdateUser); $UserID = val('UserID', $UpdateUser); $Deleted = val('Deleted', $UpdateUser); if ($UserID == val('InsertUserID', $Fields)) { $InsertUserFound = true; if ($Deleted) { $this->SQL->put('UserConversation', array('Deleted' => 0, 'DateConversationUpdated' => $DateUpdated), array('ConversationID' => $ConversationID, 'UserID' => $UserID)); } } // Update unread for users that were up to date if ($LastMessageID == $MessageID) { $UpdateCountUserIDs[] = $UserID; } // Send activities to users that have not deleted the conversation if (!$Deleted) { $NotifyUserIDs[] = $UserID; } } if (!$InsertUserFound) { $UserConversation = array('UserID' => val('InsertUserID', $Fields), 'ConversationID' => $ConversationID, 'LastMessageID' => $LastMessageID, 'CountReadMessages' => $CountMessages, 'DateConversationUpdated' => $DateUpdated); $this->SQL->insert('UserConversation', $UserConversation); } if (sizeof($UpdateCountUserIDs)) { $ConversationModel = new ConversationModel(); $ConversationModel->updateUserUnreadCount($UpdateCountUserIDs, true); } $this->fireEvent('AfterAdd'); $activityModel = new ActivityModel(); foreach ($NotifyUserIDs as $notifyUserID) { if ($Session->UserID == $notifyUserID) { continue; // don't notify self. } // Notify the users of the new message. $activity = array('ActivityType' => 'ConversationMessage', 'ActivityUserID' => val('InsertUserID', $Fields), 'NotifyUserID' => $notifyUserID, 'HeadlineFormat' => t('HeadlineFormat.ConversationMessage', '{ActivityUserID,user} sent you a <a href="{Url,html}">message</a>'), 'RecordType' => 'Conversation', 'RecordID' => $ConversationID, 'Story' => val('Body', $Fields, ''), 'Format' => val('Format', $Fields, c('Garden.InputFormatter')), 'Route' => "/messages/{$ConversationID}#{$MessageID}"); if (c('Conversations.Subjects.Visible') && val('Subject', $Conversation, '')) { $activity['HeadlineFormat'] = val('Subject', $Conversation, ''); } $activityModel->queue($activity, 'ConversationMessage'); } $activityModel->saveQueue(); } return $MessageID; }
/** * * * @param $discussion_id * @param $tags * @param array $types * @param int $category_id * @param string $new_type * @throws Exception */ public function saveDiscussion($discussion_id, $tags, $types = array(''), $category_id = 0, $new_type = '') { // First grab all of the current tags. $all_tags = $current_tags = $this->getDiscussionTags($discussion_id, TagModel::IX_TAGID); // Put all the default tag types in the types if necessary. if (in_array('', $types)) { $types = array_merge($types, array_keys($this->defaultTypes())); $types = array_unique($types); } // Remove the types from the current tags that we don't need anymore. $current_tags = array_filter($current_tags, function ($row) use($types) { if (in_array($row['Type'], $types)) { return true; } return false; }); // Turn the tags into a nice array. if (is_string($tags)) { $tags = TagModel::SplitTags($tags); } $new_tags = array(); $tag_ids = array(); // See which tags are new and which ones aren't. foreach ($tags as $tag_id) { if (is_id($tag_id)) { $tag_ids[$tag_id] = true; } else { $new_tags[TagModel::tagSlug($tag_id)] = $tag_id; } } // See if any of the new tags actually exist by searching by name. if (!empty($new_tags)) { $found_tags = $this->getWhere(array('Name' => array_keys($new_tags)))->resultArray(); foreach ($found_tags as $found_tag_row) { $tag_ids[$found_tag_row['TagID']] = $found_tag_row; unset($new_tags[TagModel::TagSlug($found_tag_row['Name'])]); } } // Add any remaining tags that need to be added. if (Gdn::session()->checkPermission('Plugins.Tagging.Add')) { foreach ($new_tags as $name => $full_name) { $new_tag = array('Name' => trim(str_replace(' ', '-', strtolower($name)), '-'), 'FullName' => $full_name, 'Type' => $new_type, 'CategoryID' => $category_id, 'InsertUserID' => Gdn::session()->UserID, 'DateInserted' => Gdn_Format::toDateTime(), 'CountDiscussions' => 0); $tag_id = $this->SQL->options('Ignore', true)->insert('Tag', $new_tag); $tag_ids[$tag_id] = true; } } // Grab the tags so we can see more information about them. $save_tags = $this->getWhere(array('TagID' => array_keys($tag_ids)))->resultArray(); // Add any parent tags that may need to be added. foreach ($save_tags as $save_tag) { $parent_tag_id = val('ParentTagID', $save_tag); if ($parent_tag_id) { $tag_ids[$parent_tag_id] = true; } $all_tags[$save_tag['TagID']] = $save_tag; } // Remove tags that are already associated with the discussion. // $same_tag_ids = array_intersect_key($tag_ids, $current_tags); // $current_tags = array_diff_key($current_tags, $same_tag_ids); // $tag_ids = array_diff_key($tag_ids, $same_tag_ids); // Figure out the tags we need to add. $insert_tag_ids = array_diff_key($tag_ids, $current_tags); // Figure out the tags we need to remove. $delete_tag_ids = array_diff_key($current_tags, $tag_ids); $now = Gdn_Format::toDateTime(); // Insert the new tag mappings. foreach ($insert_tag_ids as $tag_id => $bool) { if (isset($all_tags[$tag_id])) { $insert_category_id = $all_tags[$tag_id]['CategoryID']; } else { $insert_category_id = $category_id; } $this->SQL->options('Ignore', true)->insert('TagDiscussion', array('DiscussionID' => $discussion_id, 'TagID' => $tag_id, 'DateInserted' => $now, 'CategoryID' => $insert_category_id)); } // Delete the old tag mappings. if (!empty($delete_tag_ids)) { $this->SQL->delete('TagDiscussion', array('DiscussionID' => $discussion_id, 'TagID' => array_keys($delete_tag_ids))); } // Increment the tag counts. if (!empty($insert_tag_ids)) { $this->SQL->update('Tag')->set('CountDiscussions', 'CountDiscussions + 1', false)->whereIn('TagID', array_keys($insert_tag_ids))->put(); } // Decrement the tag counts. if (!empty($delete_tag_ids)) { $this->SQL->update('Tag')->set('CountDiscussions', 'CountDiscussions - 1', false)->whereIn('TagID', array_keys($delete_tag_ids))->put(); } }
/** * * * @param array $FormPostValues * @param array|bool $UserModel * @param array $Options * @return bool * @throws Exception */ public function save($FormPostValues, $UserModel, $Options = array()) { $Session = Gdn::session(); $UserID = $Session->UserID; $SendEmail = val('SendEmail', $Options, true); $Resend = val('Resend', $Options, false); // Define the primary key in this model's table. $this->defineSchema(); // Add & apply any extra validation rules: $this->Validation->applyRule('Email', 'Email'); // Make sure required db fields are present. $this->AddInsertFields($FormPostValues); if (!isset($FormPostValues['DateExpires'])) { $Expires = strtotime(c('Garden.Registration.InviteExpiration')); if ($Expires > time()) { $FormPostValues['DateExpires'] = Gdn_Format::toDateTime($Expires); } } $FormPostValues['Code'] = $this->GetInvitationCode(); // Validate the form posted values if ($this->validate($FormPostValues, true) === true) { $Fields = $this->Validation->ValidationFields(); // All fields on the form that need to be validated $Email = arrayValue('Email', $Fields, ''); // Make sure this user has a spare invitation to send. $InviteCount = $UserModel->GetInvitationCount($UserID); if ($InviteCount == 0) { $this->Validation->addValidationResult('Email', 'You do not have enough invitations left.'); return false; } // Make sure that the email does not already belong to an account in the application. $TestData = $UserModel->getWhere(array('Email' => $Email)); if ($TestData->numRows() > 0) { $this->Validation->addValidationResult('Email', 'The email you have entered is already related to an existing account.'); return false; } // Make sure that the email does not already belong to an invitation in the application. $TestData = $this->getWhere(array('Email' => $Email)); $DeleteID = false; if ($TestData->numRows() > 0) { if (!$Resend) { $this->Validation->addValidationResult('Email', 'An invitation has already been sent to the email you entered.'); return false; } else { // Mark the old invitation for deletion. $DeleteID = val('InvitationID', $TestData->firstRow(DATASET_TYPE_ARRAY)); } } // Define the fields to be inserted $Fields = $this->Validation->SchemaValidationFields(); // Call the base model for saving $InvitationID = $this->insert($Fields); // Delete an old invitation. if ($InvitationID && $DeleteID) { $this->delete($DeleteID); } // Now that saving has succeeded, update the user's invitation settings if ($InviteCount > 0) { $UserModel->ReduceInviteCount($UserID); } // And send the invitation email if ($SendEmail) { try { $this->send($InvitationID); } catch (Exception $ex) { $this->Validation->addValidationResult('Email', sprintf(t('Although the invitation was created successfully, the email failed to send. The server reported the following error: %s'), strip_tags($ex->getMessage()))); return false; } } return true; } return false; }
/** * Delete a single user. * * @param int $userID The user to delete. * @param array $options See {@link UserModel::deleteContent()}, and {@link UserModel::getDelete()}. */ public function deleteID($userID, $options = []) { if ($userID == $this->getSystemUserID()) { $this->Validation->addValidationResult('', 'You cannot delete the system user.'); return false; } $Content = []; // Remove shared authentications. $this->getDelete('UserAuthentication', ['UserID' => $userID], $Content); // Remove role associations. $this->getDelete('UserRole', ['UserID' => $userID], $Content); $this->deleteContent($userID, $options, $Content); // Remove the user's information $this->SQL->update('User')->set(['Name' => t('[Deleted User]'), 'Photo' => null, 'Password' => randomString('10'), 'About' => '', 'Email' => 'user_' . $userID . '@deleted.email', 'ShowEmail' => '0', 'Gender' => 'u', 'CountVisits' => 0, 'CountInvitations' => 0, 'CountNotifications' => 0, 'InviteUserID' => null, 'DiscoveryText' => '', 'Preferences' => null, 'Permissions' => null, 'Attributes' => dbencode(['State' => 'Deleted']), 'DateSetInvitations' => null, 'DateOfBirth' => null, 'DateUpdated' => Gdn_Format::toDateTime(), 'HourOffset' => '0', 'Score' => null, 'Admin' => 0, 'Deleted' => 1])->where('UserID', $userID)->put(); // Remove user's cache rows $this->clearCache($userID); return true; }
/** * 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', ''); $isEmbeddedComments = $vanilla_url != '' && $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 && $isEmbeddedComments) { $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 && $isEmbeddedComments) { // 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); if (!($Title = $this->Form->getFormValue('Name'))) { $Title = val('Title', $PageInfo, ''); if ($Title == '') { $Title = t('Undefined discussion subject.'); if (!empty($PageInfo['Exception']) && $PageInfo['Exception'] === "Couldn't connect to host.") { $Title .= ' ' . t('Page timed out.'); } } } $Description = val('Description', $PageInfo, ''); $Images = val('Images', $PageInfo, array()); $LinkText = t('EmbededDiscussionLinkText', 'Read the full story here'); if (!$Description && count($Images) == 0) { $Body = formatString('<p><a href="{Url}">{LinkText}</a></p>', array('Url' => $vanilla_url, 'LinkText' => $LinkText)); } else { $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(val(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']; } $EmbedUserID = c('Garden.Embed.UserID'); if ($EmbedUserID) { $EmbedUser = Gdn::userModel()->getID($EmbedUserID); } if (!$EmbedUserID || !$EmbedUser) { $EmbedUserID = Gdn::userModel()->getSystemUserID(); } $EmbeddedDiscussionData = array('InsertUserID' => $EmbedUserID, 'DateInserted' => Gdn_Format::toDateTime(), 'DateUpdated' => Gdn_Format::toDateTime(), 'CategoryID' => $vanilla_category_id, 'ForeignID' => $vanilla_identifier, 'Type' => $vanilla_type, 'Name' => $Title, 'Body' => $Body, 'Format' => 'Html', 'Attributes' => dbencode($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, DATASET_TYPE_OBJECT, array('Slave' => false)); // 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.')); } /** * Special care is taken for embedded comments. Since we don't currently use an advanced editor for these * comments, we may need to apply certain filters and fixes to the data to maintain its intended display * with the input format (e.g. maintaining newlines). */ if ($isEmbeddedComments) { $inputFormatter = $this->Form->getFormValue('Format', c('Garden.InputFormatter')); switch ($inputFormatter) { case 'Wysiwyg': $this->Form->setFormValue('Body', nl2br($this->Form->getFormValue('Body'))); break; } } $PermissionCategoryID = val('PermissionCategoryID', $Discussion); // Setup head $this->addJsFile('jquery.autosize.min.js'); $this->addJsFile('autosave.js'); $this->addJsFile('post.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) { // Permission 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); } $this->Form->setFormValue('CommentID', $CommentID); } elseif ($Discussion) { // Permission to add $this->permission('Vanilla.Comments.Add', true, 'Category', $Discussion->PermissionCategoryID); } if ($this->Form->authenticatedPostBack()) { // Save as a draft? $FormValues = $this->Form->formValues(); $FormValues = $this->CommentModel->filterForm($FormValues); if (!$Editing) { unset($FormValues['CommentID']); } 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()); } elseif (!$Preview) { // Fix an undefined title if we can. if ($this->Form->getFormValue('Name') && val('Name', $Discussion) == t('Undefined discussion subject.')) { $Set = array('Name' => $this->Form->getFormValue('Name')); if (isset($vanilla_url) && $vanilla_url && strpos(val('Body', $Discussion), t('Undefined discussion subject.')) !== false) { $LinkText = t('EmbededDiscussionLinkText', 'Read the full story here'); $Set['Body'] = formatString('<p><a href="{Url}">{LinkText}</a></p>', array('Url' => $vanilla_url, 'LinkText' => $LinkText)); } $this->DiscussionModel->setField(val('DiscussionID', $Discussion), $Set); } $Inserted = !$CommentID; $CommentID = $this->CommentModel->save($FormValues); // The comment is now half-saved. if (is_numeric($CommentID) && $CommentID > 0) { if (in_array($this->deliveryType(), array(DELIVERY_TYPE_ALL, DELIVERY_TYPE_DATA))) { $this->CommentModel->save2($CommentID, $Inserted, true, true); } else { $this->jsonTarget('', url("/post/comment2.json?commentid={$CommentID}&inserted={$Inserted}"), 'Ajax'); } // $Discussion = $this->DiscussionModel->getID($DiscussionID); $Comment = $this->CommentModel->getID($CommentID, DATASET_TYPE_OBJECT, array('Slave' => false)); $this->EventArguments['Discussion'] = $Discussion; $this->EventArguments['Comment'] = $Comment; $this->fireEvent('AfterCommentSave'); } elseif ($CommentID === SPAM || $CommentID === UNAPPROVED) { $this->StatusMessage = t('CommentRequiresApprovalStatus', 'Your comment will appear after it is approved.'); } $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 = val('Body', $FormValues, ''); $this->Comment->Format = val('Format', $FormValues, c('Garden.InputFormatter')); $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 = val('Body', $FormValues, ''); $this->Comment->Format = val('Format', $FormValues, c('Garden.InputFormatter')); $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, array('Slave' => false)); $this->setData('Comments', $Comments); $this->setData('Discussion', $Discussion); // 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 = valr('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, array('Slave' => false)); $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); } } } elseif ($this->Request->isPostBack()) { throw new Gdn_UserException(t('Invalid CSRF token.', 'Invalid CSRF token. Please try again.'), 401); } else { // Load form if (isset($this->Comment)) { $this->Form->setData((array) $this->Comment); } } // 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) { if ($this->data('Comments') instanceof Gdn_DataSet) { $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 { require_once $this->fetchViewLocation('helper_functions', 'Discussion'); // Render default view. $this->render(); } }
/** * Update a conversation as read for a specific user id. * * @since 2.0.0 * @access public * * @param int $ConversationID Unique ID of conversation effected. * @param int $ReadingUserID Unique ID of current user. */ public function markRead($ConversationID, $ReadingUserID) { // Update the the read conversation count for the user. $this->SQL->update('UserConversation uc')->join('Conversation c', 'c.ConversationID = uc.ConversationID')->set('uc.CountReadMessages', 'c.CountMessages', false)->set('uc.DateLastViewed', Gdn_Format::toDateTime())->set('uc.LastMessageID', 'c.LastMessageID', false)->where('c.ConversationID', $ConversationID)->where('uc.ConversationID', $ConversationID)->where('uc.UserID', $ReadingUserID)->put(); // Also update the unread conversation count for this user $CountUnread = $this->countUnread($ReadingUserID); // Also write through to the current session user. if ($ReadingUserID > 0 && $ReadingUserID == Gdn::session()->UserID) { Gdn::session()->User->CountUnreadConversations = $CountUnread; } }
/** * * * @param $sender controller instance. * @param int|string $commentID Identifier of the comment. * * @throws notFoundException */ public function _commentOptions($sender, $commentID) { $sender->Form = new Gdn_Form(); $Comment = $sender->CommentModel->getID($commentID, DATASET_TYPE_ARRAY); if (!$Comment) { throw notFoundException('Comment'); } $Discussion = $sender->DiscussionModel->getID(val('DiscussionID', $Comment)); $sender->permission('Vanilla.Discussions.Edit', true, 'Category', val('PermissionCategoryID', $Discussion)); if ($sender->Form->authenticatedPostBack()) { $QnA = $sender->Form->getFormValue('QnA'); if (!$QnA) { $QnA = null; } $CurrentQnA = val('QnA', $Comment); if ($CurrentQnA != $QnA) { $Set = array('QnA' => $QnA); if ($QnA == 'Accepted') { $Set['DateAccepted'] = Gdn_Format::toDateTime(); $Set['AcceptedUserID'] = Gdn::session()->UserID; } else { $Set['DateAccepted'] = null; $Set['AcceptedUserID'] = null; } $sender->CommentModel->setField($commentID, $Set); $sender->Form->setValidationResults($sender->CommentModel->validationResults()); // Determine QnA change if ($Comment['QnA'] != $QnA) { $Change = 0; switch ($QnA) { case 'Rejected': $Change = -1; if ($Comment['QnA'] != 'Accepted') { $Change = 0; } break; case 'Accepted': $Change = 1; break; default: if ($Comment['QnA'] == 'Rejected') { $Change = 0; } if ($Comment['QnA'] == 'Accepted') { $Change = -1; } break; } } // Apply change effects if ($Change) { // Update the user $UserID = val('InsertUserID', $Comment); $this->recalculateUserQnA($UserID); // Update reactions if ($this->Reactions) { include_once Gdn::controller()->fetchViewLocation('reaction_functions', '', 'plugins/Reactions'); $Rm = new ReactionModel(); // If there's change, reactions will take care of it $Rm->react('Comment', $Comment['CommentID'], 'AcceptAnswer'); } } } // Recalculate the Q&A status of the discussion. $this->recalculateDiscussionQnA($Discussion); Gdn::controller()->jsonTarget('', '', 'Refresh'); } else { $sender->Form->setData($Comment); } $sender->setData('Comment', $Comment); $sender->setData('Discussion', $Discussion); $sender->setData('_QnAs', array('Accepted' => t('Yes'), 'Rejected' => t('No'), '' => t("Don't know"))); $sender->setData('Title', t('Q&A Options')); $sender->render('CommentOptions', '', 'plugins/QnA'); }
public function notificationsPopin() { $this->permission('Garden.SignIn.Allow'); $Where = array('NotifyUserID' => Gdn::session()->UserID, 'DateUpdated >=' => Gdn_Format::toDateTime(strtotime('-2 weeks'))); $this->ActivityModel = new ActivityModel(); $Activities = $this->ActivityModel->getWhere($Where, 0, 5)->resultArray(); $this->setData('Activities', $Activities); $this->ActivityModel->markRead(Gdn::session()->UserID); $this->setData('Title', t('Notifications')); $this->render('Popin', 'Activity', 'Dashboard'); }
/** * Adds $this->UpdateUserID and $this->DateUpdated fields to an associative * array of fieldname/values if those fields exist on the table being updated. * * @param array $Fields The array of fields to add the values to. */ protected function addUpdateFields(&$Fields) { $this->defineSchema(); if ($this->Schema->fieldExists($this->Name, $this->DateUpdated)) { if (!isset($Fields[$this->DateUpdated])) { $Fields[$this->DateUpdated] = Gdn_Format::toDateTime(); } } $Session = Gdn::session(); if ($Session->UserID > 0 && $this->Schema->fieldExists($this->Name, $this->UpdateUserID)) { if (!isset($Fields[$this->UpdateUserID])) { $Fields[$this->UpdateUserID] = $Session->UserID; } } if ($this->Schema->FieldExists($this->Name, 'UpdateIPAddress') && !isset($Fields['UpdateIPAddress'])) { $Fields['UpdateIPAddress'] = Gdn::request()->ipAddress(); } }
/** * Used by $this->Stash() to create & manage sessions for users & guests. * * This is a stop-gap solution until full session management for users & * guests can be imlemented. */ private function _getStashSession($ValueToStash) { $CookieName = c('Garden.Cookie.Name', 'Vanilla'); $Name = $CookieName . '-sid'; // Grab the entire session record $SessionID = val($Name, $_COOKIE, ''); // If there is no session, and no value for saving, return; if ($SessionID == '' && $ValueToStash == '') { return false; } $Session = Gdn::SQL()->select()->from('Session')->where('SessionID', $SessionID)->get()->firstRow(); if (!$Session) { $SessionID = betterRandomString(32); $TransientKey = substr(md5(mt_rand()), 0, 11) . '!'; // Save the session information to the database. Gdn::SQL()->insert('Session', array('SessionID' => $SessionID, 'UserID' => Gdn::session()->UserID, 'TransientKey' => $TransientKey, 'DateInserted' => Gdn_Format::toDateTime(), 'DateUpdated' => Gdn_Format::toDateTime())); Trace("Inserting session stash {$SessionID}"); $Session = Gdn::SQL()->select()->from('Session')->where('SessionID', $SessionID)->get()->firstRow(); // Save a session cookie $Path = c('Garden.Cookie.Path', '/'); $Domain = c('Garden.Cookie.Domain', ''); $Expire = 0; // If the domain being set is completely incompatible with the current domain then make the domain work. $CurrentHost = Gdn::request()->host(); if (!stringEndsWith($CurrentHost, trim($Domain, '.'))) { $Domain = ''; } safeCookie($Name, $SessionID, $Expire, $Path, $Domain); $_COOKIE[$Name] = $SessionID; } $Session->Attributes = @unserialize($Session->Attributes); if (!$Session->Attributes) { $Session->Attributes = array(); } return $Session; }
/** * Update an existing category, handling its tree properties and caching. * * @param array $category The category to update. * @return bool Returns **true** if the category updated or **false** otherwise. */ public function update(array $category) { if (empty($category['CategoryID'])) { throw new Gdn_UserException("Category ID is required."); } $category += ['DateUpdated' => Gdn_Format::toDateTime(), 'UpdateUserID' => 1]; // Get the current category. $oldCategory = $this->get($category['CategoryID']); if (!$oldCategory) { $inserted = $this->insert($category); if ($inserted) { return $category['CategoryID']; } else { return false; } } else { $this->sql->put('Category', $category, ['CategoryID' => $category['CategoryID']]); $this->refreshCache($category['CategoryID']); // Did my parent change? if ((int) $oldCategory['ParentCategoryID'] !== (int) $category['ParentCategoryID']) { // Increment the new parent and decrement the old parent. $this->sql->put('Category', ['CountCategories-' => 1], ['CategoryID' => $oldCategory['ParentCategoryID']]); $this->sql->put('Category', ['CountCategories+' => 1], ['CategoryID' => $category['ParentCategoryID']]); $this->refreshCache($oldCategory['ParentCategoryID']); $this->refreshCache($category['ParentCategoryID']); } return true; } }
/** * If looking at the root node, make sure it exists and that the * nested set columns exist in the table. * * @since 2.0.15 * @access public */ public function applyUpdates() { if (!c('Vanilla.NestedCategoriesUpdate')) { // Add new columns $Construct = Gdn::database()->Structure(); $Construct->table('Category')->column('TreeLeft', 'int', true)->column('TreeRight', 'int', true)->column('Depth', 'int', true)->column('CountComments', 'int', '0')->column('LastCommentID', 'int', true)->set(0, 0); // Insert the root node if ($this->SQL->getWhere('Category', array('CategoryID' => -1))->numRows() == 0) { $this->SQL->insert('Category', array('CategoryID' => -1, 'TreeLeft' => 1, 'TreeRight' => 4, 'Depth' => 0, 'InsertUserID' => 1, 'UpdateUserID' => 1, 'DateInserted' => Gdn_Format::toDateTime(), 'DateUpdated' => Gdn_Format::toDateTime(), 'Name' => t('Root Category Name', 'Root'), 'UrlCode' => '', 'Description' => t('Root Category Description', 'Root of category tree. Users should never see this.'))); } // Build up the TreeLeft & TreeRight values. $this->rebuildTree(); saveToConfig('Vanilla.NestedCategoriesUpdate', 1); } }
} $SQL = Gdn::database()->sql(); $Construct = Gdn::database()->Structure(); $Px = $Construct->DatabasePrefix(); $Construct->table('Category'); $CategoryExists = $Construct->TableExists(); $PermissionCategoryIDExists = $Construct->columnExists('PermissionCategoryID'); $LastDiscussionIDExists = $Construct->columnExists('LastDiscussionID'); $Construct->PrimaryKey('CategoryID')->column('ParentCategoryID', 'int', true)->column('TreeLeft', 'int', true)->column('TreeRight', 'int', true)->column('Depth', 'int', true)->column('CountDiscussions', 'int', '0')->column('CountComments', 'int', '0')->column('DateMarkedRead', 'datetime', null)->column('AllowDiscussions', 'tinyint', '1')->column('Archived', 'tinyint(1)', '0')->column('Name', 'varchar(255)')->column('UrlCode', 'varchar(255)', true)->column('Description', 'varchar(500)', true)->column('Sort', 'int', true)->column('CssClass', 'varchar(50)', true)->column('Photo', 'varchar(255)', true)->column('PermissionCategoryID', 'int', '-1')->column('PointsCategoryID', 'int', '0')->column('HideAllDiscussions', 'tinyint(1)', '0')->column('DisplayAs', array('Categories', 'Discussions', 'Heading', 'Default'), 'Default')->column('InsertUserID', 'int', false, 'key')->column('UpdateUserID', 'int', true)->column('DateInserted', 'datetime')->column('DateUpdated', 'datetime')->column('LastCommentID', 'int', null)->column('LastDiscussionID', 'int', null)->column('LastDateInserted', 'datetime', null)->column('AllowedDiscussionTypes', 'varchar(255)', null)->column('DefaultDiscussionType', 'varchar(10)', null)->set($Explicit, $Drop); $RootCategoryInserted = false; if ($SQL->getWhere('Category', array('CategoryID' => -1))->numRows() == 0) { $SQL->insert('Category', array('CategoryID' => -1, 'TreeLeft' => 1, 'TreeRight' => 4, 'InsertUserID' => 1, 'UpdateUserID' => 1, 'DateInserted' => Gdn_Format::toDateTime(), 'DateUpdated' => Gdn_Format::toDateTime(), 'Name' => 'Root', 'UrlCode' => '', 'Description' => 'Root of category tree. Users should never see this.', 'PermissionCategoryID' => -1)); $RootCategoryInserted = true; } if ($Drop || !$CategoryExists) { $SQL->insert('Category', array('ParentCategoryID' => -1, 'TreeLeft' => 2, 'TreeRight' => 3, 'InsertUserID' => 1, 'UpdateUserID' => 1, 'DateInserted' => Gdn_Format::toDateTime(), 'DateUpdated' => Gdn_Format::toDateTime(), 'Name' => 'General', 'UrlCode' => 'general', 'Description' => 'General discussions', 'PermissionCategoryID' => -1)); } elseif ($CategoryExists && !$PermissionCategoryIDExists) { if (!c('Garden.Permissions.Disabled.Category')) { // Existing installations need to be set up with per/category permissions. $SQL->update('Category')->set('PermissionCategoryID', 'CategoryID', false)->put(); $SQL->update('Permission')->set('JunctionColumn', 'PermissionCategoryID')->where('JunctionColumn', 'CategoryID')->put(); } } if ($CategoryExists) { $CategoryModel = new CategoryModel(); $CategoryModel->RebuildTree(); unset($CategoryModel); } // Construct the discussion table. $Construct->table('Discussion'); $DiscussionExists = $Construct->TableExists();
* @copyright 2009-2016 Vanilla Forums Inc. * @license http://www.opensource.org/licenses/gpl-2.0.php GNU GPL v2 * @since 2.0 * @package Vanilla */ $SQL = Gdn::database()->sql(); // Only do this once, ever. $Row = $SQL->get('Discussion', '', 'asc', 1)->firstRow(DATASET_TYPE_ARRAY); if ($Row) { return; } $DiscussionModel = new DiscussionModel(); // Prep default content $DiscussionTitle = "BAM! You’ve got a sweet forum"; $DiscussionBody = "There’s nothing sweeter than a fresh new forum, ready to welcome your community. A Vanilla Forum has all the bits and pieces you need to build an awesome discussion platform customized to your needs. Here’s a few tips:\n<ul>\n <li>Use the <a href=\"/dashboard/settings/gettingstarted\">Getting Started</a> list in the Dashboard to configure your site.</li>\n <li>Don’t use too many categories. We recommend 3-8. Keep it simple!</li>\n <li>“Announce” a discussion (click the gear) to stick to the top of the list, and “Close” it to stop further comments.</li>\n <li>Use “Sink” to take attention away from a discussion. New comments will no longer bring it back to the top of the list.</li>\n <li>Bookmark a discussion (click the star) to get notifications for new comments. You can edit notification settings from your profile.</li>\n</ul>\nGo ahead and edit or delete this discussion, then spread the word to get this place cooking. Cheers!"; $CommentBody = "This is the first comment on your site and it’s an important one.\n\nDon’t see your must-have feature? We keep Vanilla nice and simple by default. Use <b>addons</b> to get the special sauce your community needs.\n\nNot sure which addons to enable? Our favorites are Button Bar and Tagging. They’re almost always a great start."; $WallBody = "Ping! An activity post is a public way to talk at someone. When you update your status here, it posts it on your activity feed."; // Prep content meta data $SystemUserID = Gdn::userModel()->GetSystemUserID(); $TargetUserID = Gdn::session()->UserID; $Now = Gdn_Format::toDateTime(); $CategoryID = val('CategoryID', CategoryModel::DefaultCategory()); // Get wall post type ID $WallCommentTypeID = $SQL->getWhere('ActivityType', array('Name' => 'WallPost'))->value('ActivityTypeID'); // Insert first discussion & comment $DiscussionID = $SQL->Options('Ignore', true)->insert('Discussion', array('Name' => t('StubDiscussionTitle', $DiscussionTitle), 'Body' => t('StubDiscussionBody', $DiscussionBody), 'Format' => 'Html', 'CategoryID' => $CategoryID, 'ForeignID' => 'stub', 'InsertUserID' => $SystemUserID, 'DateInserted' => $Now, 'DateLastComment' => $Now, 'LastCommentUserID' => $SystemUserID, 'CountComments' => 1)); $CommentID = $SQL->insert('Comment', array('DiscussionID' => $DiscussionID, 'Body' => t('StubCommentBody', $CommentBody), 'Format' => 'Html', 'InsertUserID' => $SystemUserID, 'DateInserted' => $Now)); $SQL->update('Discussion')->set('LastCommentID', $CommentID)->where('DiscussionID', $DiscussionID)->put(); $DiscussionModel->UpdateDiscussionCount($CategoryID); // Insert first wall post $SQL->insert('Activity', array('Story' => t('StubWallBody', $WallBody), 'Format' => 'Html', 'HeadlineFormat' => '{RegardingUserID,you} → {ActivityUserID,you}', 'NotifyUserID' => -1, 'ActivityUserID' => $TargetUserID, 'RegardingUserID' => $SystemUserID, 'ActivityTypeID' => $WallCommentTypeID, 'InsertUserID' => $SystemUserID, 'DateInserted' => $Now, 'DateUpdated' => $Now));
/** * @return Gdn_SQLDriver $this */ public function history($UpdateFields = true, $InsertFields = false) { $UserID = valr('User.UserID', Gdn::session(), Gdn::session()->UserID); if ($InsertFields) { $this->set('DateInserted', Gdn_Format::toDateTime())->set('InsertUserID', $UserID); } if ($UpdateFields) { $this->set('DateUpdated', Gdn_Format::toDateTime())->set('UpdateUserID', $UserID); } return $this; }
/** * * * @param $Data * @param array $Options * @return string */ public static function format($Data, $Options = array()) { if (is_string($Options)) { $Options = array('VariableName' => $Options); } $Defaults = array('VariableName' => 'Configuration', 'WrapPHP' => true, 'SafePHP' => true, 'Headings' => true, 'ByLine' => true, 'FormatStyle' => 'Array'); $Options = array_merge($Defaults, $Options); $VariableName = val('VariableName', $Options); $WrapPHP = val('WrapPHP', $Options, true); $SafePHP = val('SafePHP', $Options, true); $ByLine = val('ByLine', $Options, false); $Headings = val('Headings', $Options, true); $FormatStyle = val('FormatStyle', $Options); $Formatter = "Format{$FormatStyle}Assignment"; $FirstLine = ''; $Lines = array(); if ($WrapPHP) { $FirstLine .= "<?php "; } if ($SafePHP) { $FirstLine .= "if (!defined('APPLICATION')) exit();"; } if (!empty($FirstLine)) { $Lines[] = $FirstLine; } if (!is_array($Data)) { return $Lines[0]; } $LastKey = false; foreach ($Data as $Key => $Value) { if ($Headings && $LastKey != $Key && is_array($Value)) { $Lines[] = ''; $Lines[] = '// ' . $Key; $LastKey = $Key; } if ($FormatStyle == 'Array') { $Prefix = '$' . $VariableName . "[" . var_export($Key, true) . "]"; } if ($FormatStyle == 'Dotted') { $Prefix = '$' . $VariableName . "['" . trim(var_export($Key, true), "'"); } $Formatter($Lines, $Prefix, $Value); } if ($ByLine) { $Session = Gdn::session(); $User = $Session->UserID > 0 && is_object($Session->User) ? $Session->User->Name : 'Unknown'; $Lines[] = ''; $Lines[] = '// Last edited by ' . $User . ' (' . RemoteIp() . ')' . Gdn_Format::toDateTime(); } $Result = implode(PHP_EOL, $Lines); return $Result; }
/** * * * @param $Log * @param bool $DeleteLog * @throws Exception * @throws Gdn_UserException */ protected function _RestoreOne($Log, $DeleteLog = true) { // Throw an event to see if the restore is being overridden. $Handled = false; $this->EventArguments['Handled'] =& $Handled; $this->EventArguments['Log'] =& $Log; $this->fireEvent('BeforeRestore'); if ($Handled) { return; // a plugin handled the restore. } if ($Log['RecordType'] == 'Configuration') { throw new Gdn_UserException('Restoring configuration edits is currently not supported.'); } if ($Log['RecordType'] == 'Registration') { $TableName = 'User'; } else { $TableName = $Log['RecordType']; } $Data = $Log['Data']; if (isset($Data['Attributes'])) { $Attr = 'Attributes'; } elseif (isset($Data['Data'])) { $Attr = 'Data'; } else { $Attr = ''; } if ($Attr) { if (is_string($Data[$Attr])) { $Data[$Attr] = @unserialize($Data[$Attr]); } // Record a bit of information about the restoration. if (!is_array($Data[$Attr])) { $Data[$Attr] = array(); } $Data[$Attr]['RestoreUserID'] = Gdn::session()->UserID; $Data[$Attr]['DateRestored'] = Gdn_Format::toDateTime(); } // decho($Data, 'Row being restored'); if (!isset($Columns[$TableName])) { $Columns[$TableName] = Gdn::sql()->FetchColumns($TableName); } $Set = array_flip($Columns[$TableName]); // Set the sets from the data. foreach ($Set as $Key => $Value) { if (isset($Data[$Key])) { $Value = $Data[$Key]; if (is_array($Value)) { $Value = serialize($Value); } $Set[$Key] = $Value; } else { unset($Set[$Key]); } } switch ($Log['Operation']) { case 'Edit': // We are restoring an edit so just update the record. $IDColumn = $Log['RecordType'] . 'ID'; $Where = array($IDColumn => $Log['RecordID']); unset($Set[$IDColumn]); Gdn::sql()->put($TableName, $Set, $Where); break; case 'Delete': case 'Spam': case 'Moderate': case 'Pending': case 'Ban': $IDColumn = $Log['RecordType'] . 'ID'; if (!$Log['RecordID']) { // This log entry was never in the table. // unset($TableName); if (isset($Set['DateInserted'])) { $Set['DateInserted'] = Gdn_Format::toDateTime(); } } // Insert the record back into the db. if ($Log['Operation'] == 'Spam' && $Log['RecordType'] == 'Registration') { saveToConfig(array('Garden.Registration.NameUnique' => false, 'Garden.Registration.EmailUnique' => false), '', false); if (isset($Data['Username'])) { $Set['Name'] = $Data['Username']; } $ID = Gdn::userModel()->InsertForBasic($Set, false, array('ValidateSpam' => false)); if (!$ID) { throw new Exception(Gdn::userModel()->Validation->resultsText()); } else { Gdn::userModel()->SendWelcomeEmail($ID, '', 'Register'); } } else { $ID = Gdn::sql()->Options('Replace', true)->insert($TableName, $Set); if (!$ID && isset($Log['RecordID'])) { $ID = $Log['RecordID']; } // Unban a user. if ($Log['RecordType'] == 'User' && $Log['Operation'] == 'Ban') { Gdn::userModel()->setField($ID, 'Banned', 0); } // Keep track of a discussion ID so that its count can be recalculated. if ($Log['Operation'] != 'Edit') { switch ($Log['RecordType']) { case 'Discussion': $this->_RecalcIDs['Discussion'][$ID] = true; break; case 'Comment': $this->_RecalcIDs['Discussion'][$Log['ParentRecordID']] = true; break; } } if ($Log['Operation'] == 'Pending') { switch ($Log['RecordType']) { case 'Discussion': if (val('UserDiscussion', $this->_RecalcIDs) && val($Log['RecordUserID'], $this->_RecalcIDs['UserDiscussion'])) { $this->_RecalcIDs['UserDiscussion'][$Log['RecordUserID']]++; } else { $this->_RecalcIDs['UserDiscussion'][$Log['RecordUserID']] = 1; } break; case 'Comment': if (val('UserComment', $this->_RecalcIDs) && val($Log['RecordUserID'], $this->_RecalcIDs['UserComment'])) { $this->_RecalcIDs['UserComment'][$Log['RecordUserID']]++; } else { $this->_RecalcIDs['UserComment'][$Log['RecordUserID']] = 1; } break; } } } break; } // Fire 'after' event if (isset($ID)) { $this->EventArguments['InsertID'] = $ID; } $this->fireEvent('AfterRestore'); if ($DeleteLog) { Gdn::sql()->delete('Log', array('LogID' => $Log['LogID'])); } }
/** * Marks the specified announcement as dismissed by the specified user. * * @since 2.0.0 * @access public * * @param int $DiscussionID Unique ID of discussion being affected. * @param int $UserID Unique ID of the user being affected. */ public function dismissAnnouncement($DiscussionID, $UserID) { $Count = $this->SQL->select('UserID')->from('UserDiscussion')->where('DiscussionID', $DiscussionID)->where('UserID', $UserID)->get()->numRows(); $CountComments = $this->SQL->select('CountComments')->from('Discussion')->where('DiscussionID', $DiscussionID)->get()->firstRow()->CountComments; if ($Count > 0) { $this->SQL->update('UserDiscussion')->set('CountComments', $CountComments)->set('DateLastViewed', Gdn_Format::toDateTime())->set('Dismissed', '1')->where('DiscussionID', $DiscussionID)->where('UserID', $UserID)->put(); } else { $this->SQL->Options('Ignore', true); $this->SQL->insert('UserDiscussion', array('UserID' => $UserID, 'DiscussionID' => $DiscussionID, 'CountComments' => $CountComments, 'DateLastViewed' => Gdn_Format::toDateTime(), 'Dismissed' => '1')); } }
/** * Get the number of comments inserted since the given timestamp. * * @since 1.0 * @access public */ public function getCommentCountSince($DiscussionID, $DateAllViewed) { // Only for members if (!Gdn::session()->isValid()) { return; } // Validate DiscussionID $DiscussionID = (int) $DiscussionID; if (!$DiscussionID) { throw new Exception('A valid DiscussionID is required in GetCommentCountSince.'); } // Get new comment count return Gdn::database()->sql()->from('Comment c')->where('DiscussionID', $DiscussionID)->where('DateInserted >', Gdn_Format::toDateTime($DateAllViewed))->getCount(); }
/** * Record the user's watch data. * * @since 2.0.0 * @access public * * @param object $Discussion Discussion being watched. * @param int $Limit Max number to get. * @param int $Offset Number to skip. * @param int $TotalComments Total in entire discussion (hard limit). */ public function setWatch($Discussion, $Limit, $Offset, $TotalComments) { $NewComments = false; $Session = Gdn::session(); if ($Session->UserID > 0) { // Max comments we could have seen $CountWatch = $Limit + $Offset; if ($CountWatch > $TotalComments) { $CountWatch = $TotalComments; } // This dicussion looks familiar... if (is_numeric($Discussion->CountCommentWatch)) { if ($CountWatch < $Discussion->CountCommentWatch) { $CountWatch = $Discussion->CountCommentWatch; } if (isset($Discussion->DateLastViewed)) { $NewComments |= Gdn_Format::toTimestamp($Discussion->DateLastComment) > Gdn_Format::toTimestamp($Discussion->DateLastViewed); } if ($TotalComments > $Discussion->CountCommentWatch) { $NewComments |= true; } // Update the watch data. if ($NewComments) { // Only update the watch if there are new comments. $this->SQL->put('UserDiscussion', array('CountComments' => $CountWatch, 'DateLastViewed' => Gdn_Format::toDateTime()), array('UserID' => $Session->UserID, 'DiscussionID' => $Discussion->DiscussionID)); } } else { // Make sure the discussion isn't archived. $ArchiveDate = c('Vanilla.Archive.Date', false); if (!$ArchiveDate || Gdn_Format::toTimestamp($Discussion->DateLastComment) > Gdn_Format::toTimestamp($ArchiveDate)) { $NewComments = true; // Insert watch data. $this->SQL->Options('Ignore', true); $this->SQL->insert('UserDiscussion', array('UserID' => $Session->UserID, 'DiscussionID' => $Discussion->DiscussionID, 'CountComments' => $CountWatch, 'DateLastViewed' => Gdn_Format::toDateTime())); } } /** * Fuzzy way of trying to automatically mark a cateogyr read again * if the user reads all the comments on the first few pages. */ // If this discussion is in a category that has been marked read, // check if reading this thread causes it to be completely read again $CategoryID = val('CategoryID', $Discussion); if ($CategoryID) { $Category = CategoryModel::categories($CategoryID); if ($Category) { $DateMarkedRead = val('DateMarkedRead', $Category); if ($DateMarkedRead) { // Fuzzy way of looking back about 2 pages into the past $LookBackCount = c('Vanilla.Discussions.PerPage', 50) * 2; // Find all discussions with content from after DateMarkedRead $DiscussionModel = new DiscussionModel(); $Discussions = $DiscussionModel->get(0, 101, array('CategoryID' => $CategoryID, 'DateLastComment>' => $DateMarkedRead)); unset($DiscussionModel); // Abort if we get back as many as we asked for, meaning a // lot has happened. $NumDiscussions = $Discussions->numRows(); if ($NumDiscussions <= $LookBackCount) { // Loop over these and see if any are still unread $MarkAsRead = true; while ($Discussion = $Discussions->NextRow(DATASET_TYPE_ARRAY)) { if ($Discussion['Read']) { continue; } $MarkAsRead = false; break; } // Mark this category read if all the new content is read if ($MarkAsRead) { $CategoryModel = new CategoryModel(); $CategoryModel->SaveUserTree($CategoryID, array('DateMarkedRead' => Gdn_Format::toDateTime())); unset($CategoryModel); } } } } } } }
/** * */ public function structure() { // Get a user for operations. $UserID = Gdn::sql()->getWhere('User', array('Name' => 'StopForumSpam', 'Admin' => 2))->value('UserID'); if (!$UserID) { $UserID = Gdn::sql()->insert('User', array('Name' => 'StopForumSpam', 'Password' => randomString('20'), 'HashMethod' => 'Random', 'Email' => '*****@*****.**', 'DateInserted' => Gdn_Format::toDateTime(), 'Admin' => '2')); } saveToConfig('Plugins.StopForumSpam.UserID', $UserID, array('CheckExisting' => true)); }