/** * List download stats. * * @param bool|false $Offset */ public function index($Offset = false) { $this->permission('Garden.Settings.Manage'); $this->addSideMenu('vstats'); $this->addJsFile('jquery.gardenmorepager.js'); $this->title('Vanilla Stats'); $this->Form->Method = 'get'; $Offset = is_numeric($Offset) ? $Offset : 0; $Limit = 19; $this->StatsData = array(); $Offset--; $Year = date('Y'); $Month = date('m'); $BaseDate = Gdn_Format::toTimestamp($Year . '-' . str_pad($Month, 2, '0', STR_PAD_LEFT) . '-01 00:00:00'); for ($i = $Offset; $i <= $Limit; ++$i) { $String = "-{$i} month"; $this->StatsData[] = $this->_getStats(date("Y-m-d 00:00:00", strtotime($String, $BaseDate))); } $TotalRecords = count($this->StatsData); // Build a pager $PagerFactory = new Gdn_PagerFactory(); $this->Pager = $PagerFactory->getPager('MorePager', $this); $this->Pager->MoreCode = 'More'; $this->Pager->LessCode = 'Previous'; $this->Pager->ClientID = 'Pager'; $this->Pager->Wrapper = '<tr %1$s><td colspan="6">%2$s</td></tr>'; $this->Pager->configure($Offset, $Limit, $TotalRecords, 'vstats/index/%1$s/'); // Deliver json data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); } $this->render(); }
/** * Output 'edited' notice. * * @param $Sender */ protected function drawEdited($Sender) { $Record = $Sender->data('Discussion'); if (!$Record) { $Record = $Sender->data('Record'); } if (!$Record) { return; } $PermissionCategoryID = val('PermissionCategoryID', $Record); $Data = $Record; $RecordType = 'discussion'; $RecordID = val('DiscussionID', $Data); // But override if comment if (isset($Sender->EventArguments['Comment']) || val('RecordType', $Record) == 'comment') { $Data = $Sender->EventArguments['Comment']; $RecordType = 'comment'; $RecordID = val('CommentID', $Data); } $UserCanEdit = Gdn::session()->checkPermission('Vanilla.' . ucfirst($RecordType) . 's.Edit', true, 'Category', $PermissionCategoryID); if (is_null($Data->DateUpdated)) { return; } // Do not show log link if no log would have been generated. $elapsed = Gdn_Format::toTimestamp(val('DateUpdated', $Data)) - Gdn_Format::toTimestamp(val('DateInserted', $Data)); $grace = c('Garden.Log.FloodControl', 20) * 60; if ($elapsed < $grace) { return; } $UpdatedUserID = $Data->UpdateUserID; $UserData = Gdn::userModel()->getID($UpdatedUserID); $Edited = array('EditUser' => val('Name', $UserData, t('Unknown User')), 'EditDate' => Gdn_Format::date($Data->DateUpdated, 'html'), 'EditLogUrl' => url("/log/record/{$RecordType}/{$RecordID}"), 'EditWord' => 'at'); $DateUpdateTime = Gdn_Format::toTimestamp($Data->DateUpdated); if (date('ymd', $DateUpdateTime) != date('ymd')) { $Edited['EditWord'] = 'on'; } $Format = t('PostEdited.Plain', 'Post edited by {EditUser} {EditWord} {EditDate}'); if ($UserCanEdit) { $Format = t('PostEdited.Log', 'Post edited by {EditUser} {EditWord} {EditDate} (<a href="{EditLogUrl}">log</a>)'); } echo '<div class="PostEdited">' . formatString($Format, $Edited) . '</div>'; }
/** * 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 calculate(&$Discussion) { $ArchiveTimestamp = Gdn_Format::toTimestamp(Gdn::config('Vanilla.Archive.Date', 0)); // Fix up output $Discussion->Name = Gdn_Format::text($Discussion->Name); $Discussion->Attributes = @unserialize($Discussion->Attributes); $Discussion->Url = DiscussionUrl($Discussion); $Discussion->Tags = $this->FormatTags($Discussion->Tags); // Join in the category. $Category = CategoryModel::categories($Discussion->CategoryID); if (!$Category) { $Category = false; } $Discussion->Category = $Category['Name']; $Discussion->CategoryUrlCode = $Category['UrlCode']; $Discussion->PermissionCategoryID = $Category['PermissionCategoryID']; // Add some legacy calculated columns. if (!property_exists($Discussion, 'FirstUserID')) { $Discussion->FirstUserID = $Discussion->InsertUserID; $Discussion->FirstDate = $Discussion->DateInserted; $Discussion->LastUserID = $Discussion->LastCommentUserID; $Discussion->LastDate = $Discussion->DateLastComment; } // Add the columns from UserDiscussion if they don't exist. if (!property_exists($Discussion, 'CountCommentWatch')) { $Discussion->WatchUserID = null; $Discussion->DateLastViewed = null; $Discussion->Dismissed = 0; $Discussion->Bookmarked = 0; $Discussion->CountCommentWatch = null; } // Allow for discussions to be archived if ($Discussion->DateLastComment && Gdn_Format::toTimestamp($Discussion->DateLastComment) <= $ArchiveTimestamp) { $Discussion->Closed = '1'; if ($Discussion->CountCommentWatch) { $Discussion->CountUnreadComments = $Discussion->CountComments - $Discussion->CountCommentWatch; } else { $Discussion->CountUnreadComments = 0; } // Allow for discussions to just be new. } elseif ($Discussion->CountCommentWatch === null) { $Discussion->CountUnreadComments = true; } else { $Discussion->CountUnreadComments = $Discussion->CountComments - $Discussion->CountCommentWatch; } if (!property_exists($Discussion, 'Read')) { $Discussion->Read = !(bool) $Discussion->CountUnreadComments; if ($Category && !is_null($Category['DateMarkedRead'])) { // If the category was marked explicitly read at some point, see if that applies here if ($Category['DateMarkedRead'] > $Discussion->DateLastComment) { $Discussion->Read = true; } if ($Discussion->Read) { $Discussion->CountUnreadComments = 0; } } } // Logic for incomplete comment count. if ($Discussion->CountCommentWatch == 0 && ($DateLastViewed = val('DateLastViewed', $Discussion))) { $Discussion->CountUnreadComments = true; if (Gdn_Format::toTimestamp($DateLastViewed) >= Gdn_Format::toTimestamp($Discussion->LastDate)) { $Discussion->CountCommentWatch = $Discussion->CountComments; $Discussion->CountUnreadComments = 0; } } if ($Discussion->CountUnreadComments === null) { $Discussion->CountUnreadComments = 0; } elseif ($Discussion->CountUnreadComments < 0) { $Discussion->CountUnreadComments = 0; } $Discussion->CountCommentWatch = is_numeric($Discussion->CountCommentWatch) ? $Discussion->CountCommentWatch : null; if ($Discussion->LastUserID == null) { $Discussion->LastUserID = $Discussion->InsertUserID; $Discussion->LastDate = $Discussion->DateInserted; } $this->EventArguments['Discussion'] = $Discussion; $this->fireEvent('SetCalculatedFields'); }
/** * Updates visit level information such as date last active and the user's ip address. * * @param int $UserID * @param string|int|float $ClientHour */ public function updateVisit($UserID, $ClientHour = false) { $UserID = (int) $UserID; if (!$UserID) { throw new Exception('A valid User ID is required.'); } $User = Gdn::userModel()->getID($UserID, DATASET_TYPE_ARRAY); $Fields = []; if (Gdn_Format::toTimestamp($User['DateLastActive']) < strtotime('5 minutes ago')) { // We only update the last active date once every 5 minutes to cut down on DB activity. $Fields['DateLastActive'] = Gdn_Format::toDateTime(); } // Update session level information if necessary. if ($UserID == Gdn::session()->UserID) { $IP = Gdn::request()->ipAddress(); $Fields['LastIPAddress'] = ipEncode($IP); $this->saveIP($UserID, $IP); if (Gdn::session()->newVisit()) { $Fields['CountVisits'] = val('CountVisits', $User, 0) + 1; $this->fireEvent('Visit'); } } // Set the hour offset based on the client's clock. if (is_numeric($ClientHour) && $ClientHour >= 0 && $ClientHour < 24) { $HourOffset = $ClientHour - date('G', time()); $Fields['HourOffset'] = $HourOffset; } // See if the fields have changed. $Set = []; foreach ($Fields as $Name => $Value) { if (val($Name, $User) != $Value) { $Set[$Name] = $Value; } } if (!empty($Set)) { $this->EventArguments['Fields'] =& $Set; $this->fireEvent('UpdateVisit'); $this->setField($UserID, $Set); } if ($User['LastIPAddress'] != $Fields['LastIPAddress']) { $User = $this->getID($UserID, DATASET_TYPE_ARRAY); if (!BanModel::checkUser($User, null, true, $Bans)) { $BanModel = new BanModel(); $Ban = array_pop($Bans); $BanModel->saveUser($User, true, $Ban); $BanModel->setCounts($Ban); } } }
/** * Updates visit level information such as date last active and the user's ip address. * * @param int $UserID * @param string|int|float $ClientHour */ function updateVisit($UserID, $ClientHour = false) { $UserID = (int) $UserID; if (!$UserID) { throw new Exception('A valid User ID is required.'); } $User = Gdn::userModel()->getID($UserID, DATASET_TYPE_ARRAY); $Fields = array(); if (Gdn_Format::toTimestamp($User['DateLastActive']) < strtotime('5 minutes ago')) { // We only update the last active date once every 5 minutes to cut down on DB activity. $Fields['DateLastActive'] = Gdn_Format::toDateTime(); } // Update session level information if necessary. if ($UserID == Gdn::session()->UserID) { $IP = Gdn::request()->ipAddress(); $Fields['LastIPAddress'] = $IP; if (Gdn::session()->newVisit()) { $Fields['CountVisits'] = val('CountVisits', $User, 0) + 1; $this->fireEvent('Visit'); } } // Generate the AllIPs field. $AllIPs = val('AllIPAddresses', $User, array()); if (is_string($AllIPs)) { $AllIPs = explode(',', $AllIPs); setValue('AllIPAddresses', $User, $AllIPs); } if (!is_array($AllIPs)) { $AllIPs = array(); } if ($IP = val('InsertIPAddress', $User)) { array_unshift($AllIPs, ForceIPv4($IP)); } if ($IP = val('LastIPAddress', $User)) { array_unshift($AllIPs, $IP); } // This will be a unique list of IPs, most recently used first. array_unique keeps the first key found. $AllIPs = array_unique($AllIPs); $Fields['AllIPAddresses'] = $AllIPs; // Set the hour offset based on the client's clock. if (is_numeric($ClientHour) && $ClientHour >= 0 && $ClientHour < 24) { $HourOffset = $ClientHour - date('G', time()); $Fields['HourOffset'] = $HourOffset; } // See if the fields have changed. $Set = array(); foreach ($Fields as $Name => $Value) { if (val($Name, $User) != $Value) { $Set[$Name] = $Value; } } if (!empty($Set)) { $this->EventArguments['Fields'] =& $Set; $this->fireEvent('UpdateVisit'); $this->setField($UserID, $Set); } if ($User['LastIPAddress'] != $Fields['LastIPAddress']) { $User = $this->getID($UserID, DATASET_TYPE_ARRAY); if (!BanModel::checkUser($User, null, true, $Bans)) { $BanModel = new BanModel(); $Ban = array_pop($Bans); $BanModel->saveUser($User, true, $Ban); $BanModel->setCounts($Ban); } } }
/** * Invitation-only registration. Requires code. * * @param int $InvitationCode * @since 2.0.0 */ public function registerInvitation($InvitationCode = 0) { $this->Form->setModel($this->UserModel); // Define gender dropdown options $this->GenderOptions = array('u' => t('Unspecified'), 'm' => t('Male'), 'f' => t('Female')); if (!$this->Form->isPostBack()) { $this->Form->setValue('InvitationCode', $InvitationCode); } $InvitationModel = new InvitationModel(); // Look for the invitation. $Invitation = $InvitationModel->getWhere(array('Code' => $this->Form->getValue('InvitationCode')))->firstRow(DATASET_TYPE_ARRAY); if (!$Invitation) { $this->Form->addError('Invitation not found.', 'Code'); } else { if ($Expires = val('DateExpires', $Invitation)) { $Expires = Gdn_Format::toTimestamp($Expires); if ($Expires <= time()) { } } } $this->Form->addHidden('ClientHour', date('Y-m-d H:00')); // Use the server's current hour as a default $this->Form->addHidden('Target', $this->target()); Gdn::userModel()->addPasswordStrength($this); if ($this->Form->isPostBack() === true) { $this->InvitationCode = $this->Form->getValue('InvitationCode'); // Add validation rules that are not enforced by the model $this->UserModel->defineSchema(); $this->UserModel->Validation->applyRule('Name', 'Username', $this->UsernameError); $this->UserModel->Validation->applyRule('TermsOfService', 'Required', t('You must agree to the terms of service.')); $this->UserModel->Validation->applyRule('Password', 'Required'); $this->UserModel->Validation->applyRule('Password', 'Strength'); $this->UserModel->Validation->applyRule('Password', 'Match'); // $this->UserModel->Validation->applyRule('DateOfBirth', 'MinimumAge'); $this->fireEvent('RegisterValidation'); try { $Values = $this->Form->formValues(); $Values = $this->UserModel->filterForm($Values, true); unset($Values['Roles']); $AuthUserID = $this->UserModel->register($Values, array('Method' => 'Invitation')); $this->setData('UserID', $AuthUserID); if (!$AuthUserID) { $this->Form->setValidationResults($this->UserModel->validationResults()); } else { // The user has been created successfully, so sign in now. Gdn::session()->start($AuthUserID); if ($this->Form->getFormValue('RememberMe')) { Gdn::authenticator()->setIdentity($AuthUserID, true); } $this->fireEvent('RegistrationSuccessful'); // ... and redirect them appropriately $Route = $this->redirectTo(); if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->RedirectUrl = url($Route); } else { if ($Route !== false) { redirect($Route); } } } } catch (Exception $Ex) { $this->Form->addError($Ex); } } else { // Set some form defaults. if ($Name = val('Name', $Invitation)) { $this->Form->setValue('Name', $Name); } $this->InvitationCode = $InvitationCode; } // Make sure that the hour offset for new users gets defined when their account is created $this->addJsFile('entry.js'); $this->render(); }
/** * 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) val('Archived', $Category) || (bool) val('Unfollow', $Category)); $Category->Following = $Following; $DateMarkedRead = val('DateMarkedRead', $Category); $UserDateMarkedRead = val('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, val('LastDiscussionTitle', $Category, null)); $LastDateInserted = val('LastDateInserted', $Category, null); if (val('LastCommentUserID', $Category) == null) { setValue('LastCommentUserID', $Category, val('LastDiscussionUserID', $Category, null)); setValue('DateLastComment', $Category, val('DateLastDiscussion', $Category, null)); setValue('LastUserID', $Category, val('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, val('DateLastDiscussion', $Category, null)); } } else { $LastDiscussion = arrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name')); setValue('LastUserID', $Category, val('LastCommentUserID', $Category, null)); setValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, false, '/') . '#latest'); if (is_null($LastDateInserted)) { setValue('LastDateInserted', $Category, val('DateLastComment', $Category, null)); } } $LastDateInserted = val('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; } } } }
/** * Display custom fields on Profile. */ public function userInfoModule_onBasicInfo_handler($Sender) { if ($Sender->User->Banned) { return; } try { // Get the custom fields $ProfileFields = Gdn::userModel()->getMeta($Sender->User->UserID, 'Profile.%', 'Profile.'); // Import from CustomProfileFields if available if (!count($ProfileFields) && is_object($Sender->User) && c('Plugins.CustomProfileFields.SuggestedFields', false)) { $ProfileFields = Gdn::userModel()->getAttribute($Sender->User->UserID, 'CustomProfileFields', false); if ($ProfileFields) { // Migrate to UserMeta & delete original Gdn::userModel()->setMeta($Sender->User->UserID, $ProfileFields, 'Profile.'); Gdn::userModel()->saveAttribute($Sender->User->UserID, 'CustomProfileFields', false); } } // Send them off for magic formatting $ProfileFields = $this->parseSpecialFields($ProfileFields); // Get all field data, error check $AllFields = $this->getProfileFields(); if (!is_array($AllFields) || !is_array($ProfileFields)) { return; } // DateOfBirth is special case that core won't handle // Hack it in here instead if (c('ProfileExtender.Fields.DateOfBirth.OnProfile')) { // Do not use Gdn_Format::Date because it shifts to local timezone $BirthdayStamp = Gdn_Format::toTimestamp($Sender->User->DateOfBirth); if ($BirthdayStamp) { $ProfileFields['DateOfBirth'] = date(t('Birthday Format', 'F j, Y'), $BirthdayStamp); $AllFields['DateOfBirth'] = array('Label' => t('Birthday'), 'OnProfile' => true); } } // Display all non-hidden fields $ProfileFields = array_reverse($ProfileFields); foreach ($ProfileFields as $Name => $Value) { // Skip empty and hidden fields. if (!$Value || !val('OnProfile', $AllFields[$Name])) { continue; } // Non-magic fields must be plain text, but we'll auto-link if (!in_array($Name, $this->MagicLabels)) { $Value = Gdn_Format::links(Gdn_Format::text($Value)); } echo ' <dt class="ProfileExtend Profile' . Gdn_Format::alphaNumeric($Name) . '">' . Gdn_Format::text($AllFields[$Name]['Label']) . '</dt> '; echo ' <dd class="ProfileExtend Profile' . Gdn_Format::alphaNumeric($Name) . '">' . Gdn_Format::htmlFilter($Value) . '</dd> '; } } catch (Exception $ex) { // No errors } }
/** * Modify CountUnreadComments to account for DateAllViewed. * * Required in DiscussionModel->get() just before the return: * $this->EventArguments['Data'] = $Data; * $this->fireEvent('AfterAddColumns'); * @link http://vanillaforums.org/discussion/13227 * @since 1.0 * @access public */ public function discussionModel_setCalculatedFields_handler($Sender) { // Only for members if (!Gdn::session()->isValid()) { return; } // Recalculate New count with each category's DateMarkedRead $Discussion =& $Sender->EventArguments['Discussion']; $Category = CategoryModel::categories($Discussion->CategoryID); $CategoryLastDate = Gdn_Format::toTimestamp($Category["DateMarkedRead"]); if ($CategoryLastDate != 0) { $this->checkDiscussionDate($Discussion, $CategoryLastDate); } }
/** * * * @param $Operation * @param $RecordType * @param $NewData * @param null $OldData */ public static function logChange($Operation, $RecordType, $NewData, $OldData = null) { $RecordID = isset($NewData['RecordID']) ? $NewData['RecordID'] : val($RecordType . 'ID', $NewData); // Grab the record from the DB. if ($OldData === null) { $OldData = Gdn::sql()->getWhere($RecordType, array($RecordType . 'ID' => $RecordID))->resultArray(); } elseif (!is_array($OldData)) { $OldData = array($OldData); } foreach ($OldData as $Row) { // Don't log the change if it's right after an insert. if (val('DateInserted', $Row) && time() - Gdn_Format::toTimestamp(val('DateInserted', $Row)) < c('Garden.Log.FloodControl', 20) * 60) { continue; } setValue('_New', $Row, $NewData); self::insert($Operation, $RecordType, $Row); } }
/** * Show activity feed for this user. * * @since 2.0.0 * @access public * @param mixed $UserReference Unique identifier, possible ID or username. * @param string $Username Username. * @param int $UserID Unique ID. * @param int $Offset How many to skip (for paging). */ public function activity($UserReference = '', $Username = '', $UserID = '', $Page = '') { $this->permission('Garden.Profiles.View'); $this->editMode(false); // Object setup $Session = Gdn::session(); $this->ActivityModel = new ActivityModel(); // Calculate offset. list($Offset, $Limit) = offsetLimit($Page, 30); // Get user, tab, and comment $this->getUserInfo($UserReference, $Username, $UserID); $UserID = $this->User->UserID; $Username = $this->User->Name; $this->_setBreadcrumbs(t('Activity'), userUrl($this->User, '', 'activity')); $this->setTabView('Activity'); $Comment = $this->Form->getFormValue('Comment'); // Load data to display $this->ProfileUserID = $this->User->UserID; $Limit = 30; $NotifyUserIDs = array(ActivityModel::NOTIFY_PUBLIC); if (Gdn::session()->checkPermission('Garden.Moderation.Manage')) { $NotifyUserIDs[] = ActivityModel::NOTIFY_MODS; } $Activities = $this->ActivityModel->getWhere(array('ActivityUserID' => $UserID, 'NotifyUserID' => $NotifyUserIDs), $Offset, $Limit)->resultArray(); $this->ActivityModel->joinComments($Activities); $this->setData('Activities', $Activities); if (count($Activities) > 0) { $LastActivity = reset($Activities); $LastModifiedDate = Gdn_Format::toTimestamp($this->User->DateUpdated); $LastActivityDate = Gdn_Format::toTimestamp($LastActivity['DateInserted']); if ($LastModifiedDate < $LastActivityDate) { $LastModifiedDate = $LastActivityDate; } // Make sure to only query this page if the user has no new activity since the requesting browser last saw it. $this->SetLastModified($LastModifiedDate); } // Set the canonical Url. if (is_numeric($this->User->Name) || Gdn_Format::url($this->User->Name) != strtolower($this->User->Name)) { $this->canonicalUrl(url('profile/' . $this->User->UserID . '/' . Gdn_Format::url($this->User->Name), true)); } else { $this->canonicalUrl(url('profile/' . strtolower($this->User->Name), true)); } $this->render(); }
/** * Checks to see if the user is spamming. Returns TRUE if the user is spamming. * * Users cannot post more than $SpamCount comments within $SpamTime * seconds or their account will be locked for $SpamLock seconds. * * @since 2.0.0 * @access public * * @param string $Type Valid values are 'Comment' or 'Discussion'. * @return bool Whether spam check is positive (TRUE = spammer). */ public function checkForSpam($Type) { $Session = Gdn::session(); // If spam checking is disabled or user is an admin, skip $SpamCheckEnabled = val('SpamCheck', $this, true); if ($SpamCheckEnabled === false || $Session->User->Admin || $Session->checkPermission('Garden.Moderation.Manage')) { return false; } $Spam = false; // Validate $Type if (!in_array($Type, array('Comment', 'Discussion'))) { trigger_error(ErrorMessage(sprintf('Spam check type unknown: %s', $Type), 'VanillaModel', 'CheckForSpam'), E_USER_ERROR); } $CountSpamCheck = $Session->getAttribute('Count' . $Type . 'SpamCheck', 0); $DateSpamCheck = $Session->getAttribute('Date' . $Type . 'SpamCheck', 0); $SecondsSinceSpamCheck = time() - Gdn_Format::toTimestamp($DateSpamCheck); // Get spam config settings $SpamCount = Gdn::config('Vanilla.' . $Type . '.SpamCount'); if (!is_numeric($SpamCount) || $SpamCount < 1) { $SpamCount = 1; // 1 spam minimum } $SpamTime = Gdn::config('Vanilla.' . $Type . '.SpamTime'); if (!is_numeric($SpamTime) || $SpamTime < 30) { $SpamTime = 30; // 30 second minimum spam span } $SpamLock = Gdn::config('Vanilla.' . $Type . '.SpamLock'); if (!is_numeric($SpamLock) || $SpamLock < 60) { $SpamLock = 60; // 60 second minimum lockout } // Apply a spam lock if necessary $Attributes = array(); if ($SecondsSinceSpamCheck < $SpamLock && $CountSpamCheck >= $SpamCount && $DateSpamCheck !== false) { // TODO: REMOVE DEBUGGING INFO AFTER THIS IS WORKING PROPERLY /* echo '<div>SecondsSinceSpamCheck: '.$SecondsSinceSpamCheck.'</div>'; echo '<div>SpamLock: '.$SpamLock.'</div>'; echo '<div>CountSpamCheck: '.$CountSpamCheck.'</div>'; echo '<div>SpamCount: '.$SpamCount.'</div>'; echo '<div>DateSpamCheck: '.$DateSpamCheck.'</div>'; echo '<div>SpamTime: '.$SpamTime.'</div>'; */ $Spam = true; $this->Validation->addValidationResult('Body', '@' . sprintf(t('You have posted %1$s times within %2$s seconds. A spam block is now in effect on your account. You must wait at least %3$s seconds before attempting to post again.'), $SpamCount, $SpamTime, $SpamLock)); // Update the 'waiting period' every time they try to post again $Attributes['Date' . $Type . 'SpamCheck'] = Gdn_Format::toDateTime(); } else { if ($SecondsSinceSpamCheck > $SpamTime) { $Attributes['Count' . $Type . 'SpamCheck'] = 1; $Attributes['Date' . $Type . 'SpamCheck'] = Gdn_Format::toDateTime(); } else { $Attributes['Count' . $Type . 'SpamCheck'] = $CountSpamCheck + 1; } } // Update the user profile after every comment $UserModel = Gdn::userModel(); if ($Session->UserID) { $UserModel->saveAttribute($Session->UserID, $Attributes); } return $Spam; }
/** * Display custom fields on Profile. */ public function userInfoModule_onBasicInfo_handler($Sender) { if ($Sender->User->Banned) { return; } try { // Get the custom fields $ProfileFields = Gdn::userModel()->getMeta($Sender->User->UserID, 'Profile.%', 'Profile.'); // Get allowed GDN_User fields. $Blacklist = array_combine($this->ReservedNames, $this->ReservedNames); $NativeFields = array_diff_key((array) $Sender->User, $Blacklist); // Combine custom fields (GDN_UserMeta) with GDN_User fields. // This is OK because we're blacklisting our $ReservedNames AND whitelisting $AllFields below. $ProfileFields = array_merge($ProfileFields, $NativeFields); // Import from CustomProfileFields if available if (!count($ProfileFields) && is_object($Sender->User) && c('Plugins.CustomProfileFields.SuggestedFields', false)) { $ProfileFields = Gdn::userModel()->getAttribute($Sender->User->UserID, 'CustomProfileFields', false); if ($ProfileFields) { // Migrate to UserMeta & delete original Gdn::userModel()->setMeta($Sender->User->UserID, $ProfileFields, 'Profile.'); Gdn::userModel()->saveAttribute($Sender->User->UserID, 'CustomProfileFields', false); } } // Send them off for magic formatting $ProfileFields = $this->parseSpecialFields($ProfileFields); // Get all field data, error check $AllFields = $this->getProfileFields(); if (!is_array($AllFields) || !is_array($ProfileFields)) { return; } // DateOfBirth is special case that core won't handle // Hack it in here instead if (c('ProfileExtender.Fields.DateOfBirth.OnProfile')) { // Do not use Gdn_Format::Date because it shifts to local timezone $BirthdayStamp = Gdn_Format::toTimestamp($Sender->User->DateOfBirth); if ($BirthdayStamp) { $ProfileFields['DateOfBirth'] = date(t('Birthday Format', 'F j, Y'), $BirthdayStamp); $AllFields['DateOfBirth'] = array('Label' => t('Birthday'), 'OnProfile' => true); } } // Display all non-hidden fields require_once Gdn::controller()->fetchViewLocation('helper_functions', '', 'plugins/ProfileExtender', true, false); $ProfileFields = array_reverse($ProfileFields, true); extendedProfileFields($ProfileFields, $AllFields, $this->MagicLabels); } catch (Exception $ex) { // No errors } }