예제 #1
0
 public function SetWatch($Discussion, $Limit, $Offset, $TotalComments)
 {
     // Record the user's watch data
     $Session = Gdn::Session();
     if ($Session->UserID > 0) {
         $CountWatch = $Limit + $Offset;
         if ($CountWatch > $TotalComments) {
             $CountWatch = $TotalComments;
         }
         if (is_numeric($Discussion->CountCommentWatch)) {
             // Update the watch data
             if ($CountWatch != $Discussion->CountCommentWatch && $CountWatch > $Discussion->CountCommentWatch) {
                 // 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 = Gdn::Config('Vanilla.Archive.Date');
             if (!$ArchiveDate || Gdn_Format::ToTimestamp($Discussion->DateLastComment) > Gdn_Format::ToTimestamp($ArchiveDate)) {
                 // Insert watch data
                 $this->SQL->Insert('UserDiscussion', array('UserID' => $Session->UserID, 'DiscussionID' => $Discussion->DiscussionID, 'CountComments' => $CountWatch, 'DateLastViewed' => Gdn_Format::ToDateTime()));
             }
         }
     }
 }
 /**
  * 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
  */
 function DiscussionModel_AfterAddColumns_Handler(&$Sender)
 {
     if (!C('Plugins.AllViewed.Enabled')) {
         return;
     }
     // Only for members
     $Session = Gdn::Session();
     if (!$Session->IsValid()) {
         return;
     }
     // Recalculate New count with user's DateAllViewed
     $Sender->Data = GetValue('Data', $Sender->EventArguments, '');
     $Result =& $Sender->Data->Result();
     foreach ($Result as &$Discussion) {
         if (Gdn_Format::ToTimestamp($Discussion->DateLastComment) <= Gdn_Format::ToTimestamp($Session->User->DateAllViewed)) {
             $Discussion->CountUnreadComments = 0;
             // Covered by AllViewed
         } elseif ($Discussion->CountCommentWatch == 0) {
             // AllViewed used, but new comments since then
             $Discussion->CountCommentWatch = -1;
             // hack around "incomplete comment count" logic in WriteDiscussion
             $Discussion->CountUnreadComments = $Discussion->CountComments;
         }
     }
 }
예제 #3
0
 /**
  * Checks to see if the user is spamming. Returns TRUE if the user is spamming.
  */
 public function CheckForSpam($Type)
 {
     $Spam = FALSE;
     if (!in_array($Type, array('Comment', 'Discussion'))) {
         trigger_error(ErrorMessage(sprintf('Spam check type unknown: %s', $Type), 'VanillaModel', 'CheckForSpam'), E_USER_ERROR);
     }
     $Session = Gdn::Session();
     $CountSpamCheck = $Session->GetAttribute('Count' . $Type . 'SpamCheck', 0);
     $DateSpamCheck = $Session->GetAttribute('Date' . $Type . 'SpamCheck', 0);
     $SecondsSinceSpamCheck = time() - Gdn_Format::ToTimestamp($DateSpamCheck);
     $SpamCount = Gdn::Config('Vanilla.' . $Type . '.SpamCount');
     if (!is_numeric($SpamCount) || $SpamCount < 2) {
         $SpamCount = 2;
     }
     // 2 spam minimum
     $SpamTime = Gdn::Config('Vanilla.' . $Type . '.SpamTime');
     if (!is_numeric($SpamTime) || $SpamTime < 0) {
         $SpamTime = 30;
     }
     // 30 second minimum spam span
     $SpamLock = Gdn::Config('Vanilla.' . $Type . '.SpamLock');
     if (!is_numeric($SpamLock) || $SpamLock < 30) {
         $SpamLock = 30;
     }
     // 30 second minimum lockout
     // Definition:
     // Users cannot post more than $SpamCount comments within $SpamTime
     // seconds or their account will be locked for $SpamLock seconds.
     // 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();
     $UserModel->SaveAttribute($Session->UserID, $Attributes);
     return $Spam;
 }
   /**
    * 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
    */
   function DiscussionModel_AfterAddColumns_Handler(&$Sender) {
      if (!C('Plugins.AllViewed.Enabled')) return;
      // Only for members
      $Session = Gdn::Session();
      if(!$Session->IsValid()) return;
      
      // Recalculate New count with user's DateAllViewed   
      $Sender->Data = GetValue('Data', $Sender->EventArguments, '');
      $Result = &$Sender->Data->Result();
      $DateAllViewed = Gdn_Format::ToTimestamp($Session->User->DateAllViewed);
		foreach($Result as &$Discussion) {
		   if ($DateAllViewed != 0) { // Only if they've used AllViewed
			   if (Gdn_Format::ToTimestamp($Discussion->DateLastComment) <= $DateAllViewed)
				   $Discussion->CountUnreadComments = 0; // Covered by AllViewed
            elseif ($Discussion->DateLastViewed == $DateAllViewed) // AllViewed used since last "real" view, but new comments since then
			      $Discussion->CountUnreadComments = $this->GetCommentCountSince($Discussion->DiscussionID, $DateAllViewed);
			}
		}
   }
예제 #5
0
 protected function DrawEdited($Sender)
 {
     $Record = $Sender->Data('Discussion');
     if (!$Record) {
         $Record = $Sender->Data('Record');
     }
     if (!$Record) {
         return;
     }
     $PermissionCategoryID = GetValue('PermissionCategoryID', $Record);
     $Data = $Record;
     $RecordType = 'discussion';
     $RecordID = GetValue('DiscussionID', $Data);
     // But override if comment
     if (isset($Sender->EventArguments['Comment']) || GetValue('RecordType', $Record) == 'comment') {
         $Data = $Sender->EventArguments['Comment'];
         $RecordType = 'comment';
         $RecordID = GetValue('CommentID', $Data);
     }
     $UserCanEdit = Gdn::Session()->CheckPermission('Vanilla.' . ucfirst($RecordType) . 's.Edit', TRUE, 'Category', $PermissionCategoryID);
     if (is_null($Data->DateUpdated)) {
         return;
     }
     if (Gdn_Format::ToTimestamp($Data->DateUpdated) <= Gdn_Format::ToTimestamp($Data->DateInserted)) {
         return;
     }
     $SourceUserID = $Data->InsertUserID;
     $UpdatedUserID = $Data->UpdateUserID;
     $UserData = Gdn::UserModel()->GetID($UpdatedUserID);
     $Edited = array('EditUser' => GetValue('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>)');
     }
     $Display = '<div class="PostEdited">' . FormatString($Format, $Edited) . '</div>';
     echo $Display;
 }
 public function Activity($UserReference = '', $Username = '', $UserID = '')
 {
     $this->Permission('Garden.Profiles.View');
     $this->GetUserInfo($UserReference, $Username, $UserID);
     $this->SetTabView('Activity');
     $this->ActivityModel = new ActivityModel();
     $Session = Gdn::Session();
     $Comment = $this->Form->GetFormValue('Comment');
     if ($Session->UserID > 0 && $this->Form->AuthenticatedPostBack() && !StringIsNullOrEmpty($Comment)) {
         $Comment = substr($Comment, 0, 1000);
         // Limit to 1000 characters...
         // Update About if necessary
         $ActivityType = 'WallComment';
         $SendNotification = TRUE;
         if ($Session->UserID == $this->User->UserID) {
             $SendNotification = FALSE;
             $this->UserModel->SaveAbout($Session->UserID, $Comment);
             $this->User->About = $Comment;
             $this->SetJson('UserData', $this->FetchView('user'));
             $ActivityType = 'AboutUpdate';
         }
         $NewActivityID = $this->ActivityModel->Add($Session->UserID, $ActivityType, $Comment, $this->User->UserID, '', '/profile/' . $this->User->UserID . '/' . Gdn_Format::Url($this->User->Name), $SendNotification);
         if ($this->_DeliveryType === DELIVERY_TYPE_ALL) {
             Redirect('dashboard/profile/' . $UserReference);
         } else {
             // Load just the single new comment
             $this->HideActivity = TRUE;
             $this->ActivityData = $this->ActivityModel->GetWhere('ActivityID', $NewActivityID);
             $this->View = 'activities';
             $this->ControllerName = 'activity';
         }
     } else {
         $this->ProfileUserID = $this->User->UserID;
         $this->ActivityData = $this->ActivityModel->Get($this->User->UserID);
         if ($this->ActivityData->NumRows() > 0) {
             $ActivityData = $this->ActivityData->Result();
             $ActivityIDs = ConsolidateArrayValuesByKey($ActivityData, 'ActivityID');
             $LastActivity = $this->ActivityData->FirstRow();
             $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);
             $this->CommentData = $this->ActivityModel->GetComments($ActivityIDs);
         } else {
             $this->CommentData = FALSE;
         }
     }
     // 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();
 }
예제 #7
0
 /**
  * Authenticates the user with the provided Authenticator class.
  *
  * @param int $UserID The UserID to start the session with.
  * @param bool $SetIdentity Whether or not to set the identity (cookie) or make this a one request session.
  */
 public function Start($UserID = FALSE, $SetIdentity = TRUE)
 {
     if (!Gdn::Config('Garden.Installed')) {
         return;
     }
     // Retrieve the authenticated UserID from the Authenticator module.
     $UserModel = Gdn::Authenticator()->GetUserModel();
     $this->UserID = $UserID ? $UserID : Gdn::Authenticator()->GetIdentity();
     $this->User = FALSE;
     // Now retrieve user information
     if ($this->UserID > 0) {
         // Instantiate a UserModel to get session info
         $this->User = $UserModel->GetSession($this->UserID);
         if ($this->User) {
             if ($UserID && $SetIdentity) {
                 Gdn::Authenticator()->SetIdentity($UserID);
             }
             if (Gdn::Authenticator()->ReturningUser($this->User)) {
                 $UserModel->UpdateLastVisit($this->UserID, $this->User->Attributes, $this->User->Attributes['HourOffset']);
             }
             $UserModel->EventArguments['User'] =& $this->User;
             $UserModel->FireEvent('AfterGetSession');
             $this->_Permissions = Gdn_Format::Unserialize($this->User->Permissions);
             $this->_Preferences = Gdn_Format::Unserialize($this->User->Preferences);
             $this->_Attributes = Gdn_Format::Unserialize($this->User->Attributes);
             $this->_TransientKey = is_array($this->_Attributes) ? ArrayValue('TransientKey', $this->_Attributes) : FALSE;
             if ($this->_TransientKey === FALSE) {
                 $this->_TransientKey = $UserModel->SetTransientKey($this->UserID);
             }
             // If the user hasn't been active in the session-time, update their date last active
             $SessionLength = Gdn::Config('Garden.Session.Length', '15 minutes');
             if (Gdn_Format::ToTimestamp($this->User->DateLastActive) < strtotime($SessionLength . ' ago')) {
                 $UserModel->Save(array('UserID' => $this->UserID, 'DateLastActive' => Gdn_Format::ToDateTime()));
             }
         } else {
             $this->UserID = 0;
             $this->User = FALSE;
             if ($SetIdentity) {
                 Gdn::Authenticator()->SetIdentity(NULL);
             }
         }
     }
     // Load guest permissions if necessary
     if ($this->UserID == 0) {
         $this->_Permissions = Gdn_Format::Unserialize($UserModel->DefinePermissions(0));
     }
 }
예제 #8
0
 public function GetID($DiscussionID)
 {
     $Session = Gdn::Session();
     $this->FireEvent('BeforeGetID');
     $Data = $this->SQL->Select('d.*')->Select('ca.Name', '', 'Category')->Select('ca.UrlCode', '', 'CategoryUrlCode')->Select('w.DateLastViewed, w.Dismissed, w.Bookmarked')->Select('w.CountComments', '', 'CountCommentWatch')->Select('d.DateLastComment', '', 'LastDate')->Select('d.LastCommentUserID', '', 'LastUserID')->Select('lcu.Name', '', 'LastName')->Select('iu.Name', '', 'InsertName')->Select('iup.Name', '', 'InsertPhoto')->From('Discussion d')->Join('Category ca', 'd.CategoryID = ca.CategoryID', 'left')->Join('UserDiscussion w', 'd.DiscussionID = w.DiscussionID and w.UserID = ' . $Session->UserID, 'left')->Join('User iu', 'd.InsertUserID = iu.UserID', 'left')->Join('Photo iup', 'iu.PhotoID = iup.PhotoID', 'left')->Join('Comment lc', 'd.LastCommentID = lc.CommentID', 'left')->Join('User lcu', 'lc.InsertUserID = lcu.UserID', 'left')->Where('d.DiscussionID', $DiscussionID)->Get()->FirstRow();
     if ($Data && Gdn_Format::ToTimestamp($Data->DateLastComment) <= Gdn_Format::ToTimestamp(Gdn::Config('Vanilla.Archive.Date', 0))) {
         $Data->Closed = '1';
     }
     return $Data;
 }
예제 #9
0
 /**
  * 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)
 {
     $NewComment = FALSE;
     $Session = Gdn::Session();
     if ($Session->UserID > 0) {
         $CountWatch = $Limit + $Offset + 1;
         // Include the first comment (in the discussion table) in the count.
         if ($CountWatch > $TotalComments) {
             $CountWatch = $TotalComments;
             $NewComment = TRUE;
         }
         if (is_numeric($Discussion->CountCommentWatch)) {
             if (isset($Discussion->DateLastViewed)) {
                 $NewComment |= Gdn_Format::ToTimestamp($Discussion->DateLastComment) > Gdn_Format::ToTimestamp($Discussion->DateLastViewed);
             }
             // Update the watch data.
             if ($NewComment || $CountWatch > $Discussion->CountCommentWatch) {
                 // 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 = Gdn::Config('Vanilla.Archive.Date');
             if (!$ArchiveDate || Gdn_Format::ToTimestamp($Discussion->DateLastComment) > Gdn_Format::ToTimestamp($ArchiveDate)) {
                 // Insert watch data
                 $this->SQL->Insert('UserDiscussion', array('UserID' => $Session->UserID, 'DiscussionID' => $Discussion->DiscussionID, 'CountComments' => $CountWatch, 'DateLastViewed' => Gdn_Format::ToDateTime()));
             }
         }
     }
 }
예제 #10
0
?>
<li id="<?php 
echo 'Bookmark_' . $Discussion->DiscussionID;
?>
">
   <strong><?php 
echo Anchor($Discussion->Name, '/discussion/' . $Discussion->DiscussionID . '/' . Gdn_Format::Url($Discussion->Name) . ($Discussion->CountCommentWatch > 0 ? '/#Item_' . $Discussion->CountCommentWatch : ''), 'DiscussionLink');
?>
</strong>
   <div class="Meta">
      <?php 
echo '<span>' . $Discussion->CountComments . '</span>';
$CountUnreadComments = $Discussion->CountComments - $Discussion->CountCommentWatch;
// Logic for incomplete comment count.
if ($Discussion->CountCommentWatch == 0 && ($DateLastViewed = GetValue('DateLastViewed', $Discussion))) {
    if (Gdn_Format::ToTimestamp($DateLastViewed) >= Gdn_Format::ToTimestamp($Discussion->LastDate)) {
        $CountUnreadComments = 0;
        $Discussion->CountCommentWatch = $Discussion->CountComments;
    } else {
        $CountUnreadComments = '';
    }
}
if ($CountUnreadComments > 0 || $CountUnreadComments === '') {
    echo '<strong>' . trim(sprintf('%s new', $CountUnreadComments)) . '</strong>';
}
$Last = new stdClass();
$Last->UserID = $Discussion->LastUserID;
$Last->Name = $Discussion->LastName;
echo '<span>' . Gdn_Format::Date($Discussion->LastDate) . ' ' . UserAnchor($Last) . '</span>';
?>
   </div>
 /**
  * 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 = $Activities[0];
         $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();
 }
 /**
  * Display custom fields on Profile.
  */
 public function UserInfoModule_OnBasicInfo_Handler($Sender)
 {
     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
             $ProfileFields['DateOfBirth'] = date(T('Birthday Format', 'F j, Y'), Gdn_Format::ToTimestamp($Sender->User->DateOfBirth));
             $AllFields['DateOfBirth'] = array('Label' => T('Birthday'), 'OnProfile' => TRUE);
         }
         // Display all non-hidden fields
         $ProfileFields = array_reverse($ProfileFields);
         foreach ($ProfileFields as $Name => $Value) {
             if (!$Value) {
                 continue;
             }
             if (!GetValue('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::Html($Value) . '</dd> ';
         }
     } catch (Exception $ex) {
         // No errors
     }
 }
   protected static function CalculateData(&$Data) {
		foreach ($Data as &$Category) {
         $Category['CountAllDiscussions'] = $Category['CountDiscussions'];
         $Category['CountAllComments'] = $Category['CountComments'];
         $Category['Url'] = '/categories/'.rawurlencode($Category['UrlCode']);

         // Calculate the following field.
         $Following = !((bool)GetValue('Archived', $Category) || (bool)GetValue('Unfollow', $Category));
         $Category['Following'] = $Following;

         // Calculate the read field.
         if (isset($Category['DateLastComment'])) {
            $DateMarkedRead = GetValue('UserDateMarkedRead', $Category);
            if (!$DateMarkedRead)
               $DateMarkedRead = GetValue('DateMarkedRead', $Category);
            if ($DateMarkedRead || !GetValue('DateLastComment', $Category))
               $Category['Read'] = Gdn_Format::ToTimestamp($DateMarkedRead) >= Gdn_Format::ToTimestamp($Category['DateLastComment']);
            else
               $Category['Read'] = FALSE;
         }
         $Category['Children'] = array();
		}
      
      $Keys = array_reverse(array_keys($Data));
      foreach ($Keys as $Key) {
         $Cat = $Data[$Key];
         $ParentID = $Cat['ParentCategoryID'];

         if (isset($Data[$ParentID])) {
            $Data[$ParentID]['CountAllDiscussions'] += $Cat['CountAllDiscussions'];
            $Data[$ParentID]['CountAllComments'] += $Cat['CountAllComments'];
            $Data[$ParentID]['Children'][] =& $Data[$Key];

            if ($ParentID != $Cat['CategoryID'])
               $Data[$Key]['Parent'] =& $Data[$ParentID];
         }
      }
	}
예제 #14
0
 /**
  * Signin process that multiple authentication methods.
  *
  * @access public
  * @since 2.0.0
  * @author Tim Gunter
  * 
  * @param string $Method
  * @param array $Arg1
  * @return string Rendered XHTML template.
  */
 public function SignIn($Method = FALSE, $Arg1 = FALSE)
 {
     $this->AddJsFile('entry.js');
     $this->SetData('Title', T('Sign In'));
     $this->Form->AddHidden('Target', $this->Target());
     $this->Form->AddHidden('ClientHour', date('Y-m-d H:00'));
     // Use the server's current hour as a default.
     // Additional signin methods are set up with plugins.
     $Methods = array();
     $this->SetData('MainFormArgs', array($Arg1));
     $this->SetData('Methods', $Methods);
     $this->SetData('FormUrl', Url('entry/signin'));
     $this->FireEvent('SignIn');
     if ($this->Form->IsPostBack()) {
         $this->Form->ValidateRule('Email', 'ValidateRequired', sprintf(T('%s is required.'), T('Email/Username')));
         $this->Form->ValidateRule('Password', 'ValidateRequired');
         // Check the user.
         if ($this->Form->ErrorCount() == 0) {
             $Email = $this->Form->GetFormValue('Email');
             $User = Gdn::UserModel()->GetByEmail($Email);
             if (!$User) {
                 $User = Gdn::UserModel()->GetByUsername($Email);
             }
             if (!$User) {
                 $this->Form->AddError('ErrorCredentials');
             } else {
                 $ClientHour = $this->Form->GetFormValue('ClientHour');
                 $HourOffset = Gdn_Format::ToTimestamp($ClientHour) - time();
                 $HourOffset = round($HourOffset / 3600);
                 // Check the password.
                 $PasswordHash = new Gdn_PasswordHash();
                 if ($PasswordHash->CheckPassword($this->Form->GetFormValue('Password'), GetValue('Password', $User), GetValue('HashMethod', $User))) {
                     Gdn::Session()->Start(GetValue('UserID', $User), TRUE, (bool) $this->Form->GetFormValue('RememberMe'));
                     if (!Gdn::Session()->CheckPermission('Garden.SignIn.Allow')) {
                         $this->Form->AddError('ErrorPermission');
                         Gdn::Session()->End();
                     } else {
                         if ($HourOffset != Gdn::Session()->User->HourOffset) {
                             Gdn::UserModel()->SetProperty(Gdn::Session()->UserID, 'HourOffset', $HourOffset);
                         }
                         $this->_SetRedirect();
                     }
                 } else {
                     $this->Form->AddError('ErrorCredentials');
                 }
             }
         }
     } else {
         if ($Target = $this->Request->Get('Target')) {
             $this->Form->AddHidden('Target', $Target);
         }
         $this->Form->SetValue('RememberMe', TRUE);
     }
     return $this->Render();
 }
예제 #15
0
 /**
  * Get data for a single discussion by ID.
  * 
  * @since 2.0.0
  * @access public
  * 
  * @param int $DiscussionID Unique ID of discussion to get.
  * @return object SQL result.
  */
 public function GetID($DiscussionID)
 {
     $Session = Gdn::Session();
     $this->FireEvent('BeforeGetID');
     $Data = $this->SQL->Select('d.*')->Select('w.DateLastViewed, w.Dismissed, w.Bookmarked')->Select('w.CountComments', '', 'CountCommentWatch')->Select('d.DateLastComment', '', 'LastDate')->Select('d.LastCommentUserID', '', 'LastUserID')->From('Discussion d')->Join('UserDiscussion w', 'd.DiscussionID = w.DiscussionID and w.UserID = ' . $Session->UserID, 'left')->Where('d.DiscussionID', $DiscussionID)->Get()->FirstRow();
     if (!$Data) {
         return $Data;
     }
     $Data->Name = Gdn_Format::Text($Data->Name);
     $Data->Attributes = @unserialize($Data->Attributes);
     $Data->Url = DiscussionUrl($Data);
     $Data->Tags = $this->FormatTags($Data->Tags);
     // Join in the category.
     $Category = CategoryModel::Categories($Data->CategoryID);
     if (!$Category) {
         $Category = FALSE;
     }
     $Data->Category = $Category['Name'];
     $Data->CategoryUrlCode = $Category['UrlCode'];
     $Data->PermissionCategoryID = $Category['PermissionCategoryID'];
     // Join in the users.
     $Data = array($Data);
     Gdn::UserModel()->JoinUsers($Data, array('LastUserID', 'InsertUserID'));
     CategoryModel::JoinCategories($Data);
     $Data = $Data[0];
     //         ->Select('lcu.Name', '', 'LastName')
     //			->Select('iu.Name', '', 'InsertName')
     //			->Select('iu.Photo', '', 'InsertPhoto')
     //         ->Select('iu.Email', '', 'InsertEmail')
     //         ->Join('User iu', 'd.InsertUserID = iu.UserID', 'left') // Insert user
     //			->Join('Comment lc', 'd.LastCommentID = lc.CommentID', 'left') // Last comment
     //         ->Join('User lcu', 'lc.InsertUserID = lcu.UserID', 'left') // Last comment user
     // Close if older than archive date
     if ($Data && $Data->DateLastComment && Gdn_Format::ToTimestamp($Data->DateLastComment) <= Gdn_Format::ToTimestamp(Gdn::Config('Vanilla.Archive.Date', 0))) {
         $Data->Closed = '1';
     }
     if (C('Vanilla.Views.Denormalize', FALSE)) {
         $this->AddDenormalizedViews($Data);
     }
     return $Data;
 }
예제 #16
0
 public static function LogChange($Operation, $RecordType, $NewData, $OldData = NULL)
 {
     $RecordID = isset($NewData['RecordID']) ? $NewData['RecordID'] : $NewData[$RecordType . 'ID'];
     // Grab the record from the DB.
     if ($OldData == NULL) {
         $OldData = Gdn::SQL()->GetWhere($RecordType, array($RecordType . 'ID' => $RecordID))->ResultArray();
     } elseif (!isset($OldData[0])) {
         $OldData = array($OldData);
     }
     foreach ($OldData as $Row) {
         // Don't log the change if it's right after an insert.
         if (isset($Row['DateInserted']) && time() - Gdn_Format::ToTimestamp($Row['DateInserted']) < C('Garden.Log.FloodControl', 20) * 60) {
             continue;
         }
         $Row['_New'] = $NewData;
         self::Insert($Operation, $RecordType, $Row);
     }
 }
예제 #17
0
 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 = GetValue('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');
 }
예제 #18
0
function WriteDiscussion($Discussion, &$Sender, &$Session, $Alt)
{
    $CssClass = 'Item';
    $CssClass .= $Discussion->Bookmarked == '1' ? ' Bookmarked' : '';
    $CssClass .= $Alt . ' ';
    $CssClass .= $Discussion->Announce == '1' ? ' Announcement' : '';
    $CssClass .= $Discussion->InsertUserID == $Session->UserID ? ' Mine' : '';
    $CountUnreadComments = 0;
    if (is_numeric($Discussion->CountUnreadComments)) {
        $CountUnreadComments = $Discussion->CountUnreadComments;
    }
    // Logic for incomplete comment count.
    if ($Discussion->CountCommentWatch == 0 && ($DateLastViewed = GetValue('DateLastViewed', $Discussion))) {
        if (Gdn_Format::ToTimestamp($DateLastViewed) >= Gdn_Format::ToTimestamp($Discussion->LastDate)) {
            $CountUnreadComments = 0;
            $Discussion->CountCommentWatch = $Discussion->CountComments;
        } else {
            $CountUnreadComments = '';
        }
    }
    $CssClass .= $CountUnreadComments > 0 && $Session->IsValid() ? ' New' : '';
    $Sender->EventArguments['Discussion'] =& $Discussion;
    $First = UserBuilder($Discussion, 'First');
    $Last = UserBuilder($Discussion, 'Last');
    $DiscussionName = Gdn_Format::Text($Discussion->Name);
    if ($DiscussionName == '') {
        $DiscussionName = T('Blank Discussion Topic');
    }
    static $FirstDiscussion = TRUE;
    if (!$FirstDiscussion) {
        $Sender->FireEvent('BetweenDiscussion');
    } else {
        $FirstDiscussion = FALSE;
    }
    ?>
<li class="<?php 
    echo $CssClass;
    ?>
">
   <?php 
    $Sender->FireEvent('BeforeDiscussionContent');
    WriteOptions($Discussion, $Sender, $Session);
    ?>
   <div class="ItemContent Discussion">
      <?php 
    echo Anchor($DiscussionName, '/discussion/' . $Discussion->DiscussionID . '/' . Gdn_Format::Url($Discussion->Name) . ($Discussion->CountCommentWatch > 0 && C('Vanilla.Comments.AutoOffset') ? '/#Item_' . $Discussion->CountCommentWatch : ''), 'Title');
    ?>
      <?php 
    $Sender->FireEvent('AfterDiscussionTitle');
    ?>
      <div class="Meta">
         <?php 
    if ($Discussion->Announce == '1') {
        ?>
         <span class="Announcement"><?php 
        echo T('Announcement');
        ?>
</span>
         <?php 
    }
    ?>
         <?php 
    if ($Discussion->Closed == '1') {
        ?>
         <span class="Closed"><?php 
        echo T('Closed');
        ?>
</span>
         <?php 
    }
    ?>
         <span class="CommentCount"><?php 
    printf(Plural($Discussion->CountComments, '%s comment', '%s comments'), $Discussion->CountComments);
    ?>
</span>
         <?php 
    if ($Session->IsValid()) {
        if ($CountUnreadComments == $Discussion->CountComments) {
            echo '<strong>' . T('New') . '</strong>';
        } else {
            if ($CountUnreadComments > 0) {
                echo '<strong>' . Plural($CountUnreadComments, '%s New', '%s New Plural') . '</strong>';
            }
        }
    }
    if ($Discussion->LastCommentID != '') {
        echo '<span class="LastCommentBy">' . sprintf(T('Most recent by %1$s'), UserAnchor($Last)) . '</span>';
        echo '<span class="LastCommentDate">' . Gdn_Format::Date($Discussion->LastDate) . '</span>';
    } else {
        echo '<span class="LastCommentBy">' . sprintf(T('Started by %1$s'), UserAnchor($First)) . '</span>';
        echo '<span class="LastCommentDate">' . Gdn_Format::Date($Discussion->FirstDate) . '</span>';
    }
    if (C('Vanilla.Categories.Use')) {
        echo Wrap(Anchor($Discussion->Category, '/categories/' . $Discussion->CategoryUrlCode, 'Category'));
    }
    $Sender->FireEvent('DiscussionMeta');
    ?>
      </div>
   </div>
</li>
<?php 
}
   /**
	 * 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) {
      $Session = Gdn::Session();
      if ($Session->UserID > 0) {
         $CountWatch = $Limit + $Offset + 1; // Include the first comment (in the discussion table) in the count.
         if ($CountWatch > $TotalComments)
            $CountWatch = $TotalComments;
            
         if (is_numeric($Discussion->CountCommentWatch)) {
            $NewComment = FALSE;
            if (isset($Discussion->DateLastViewed))
               $NewComment |= Gdn_Format::ToTimestamp($Discussion->DateLastComment) > Gdn_Format::ToTimestamp($Discussion->DateLastViewed);

            // Update the watch data.
				if ($NewComment || ($CountWatch != $Discussion->CountCommentWatch && $CountWatch > $Discussion->CountCommentWatch)) {
					// 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 = Gdn::Config('Vanilla.Archive.Date');
				if(!$ArchiveDate || (Gdn_Format::ToTimestamp($Discussion->DateLastComment) > Gdn_Format::ToTimestamp($ArchiveDate))) {
					// Insert watch data
					$this->SQL->Insert(
						'UserDiscussion',
						array(
							'UserID' => $Session->UserID,
							'DiscussionID' => $Discussion->DiscussionID,
							'CountComments' => $CountWatch,
                     'DateLastViewed' => Gdn_Format::ToDateTime()
						)
					);
				}
			}
			// decho('Limit: '.$Limit.'; Offset: '.$Offset.'; CountComments: '.$TotalComments);
			// decho('Setting Watch Records for Discussion '.$Discussion->DiscussionID.' to CountWatch: '.$CountWatch.' Limit: '.$Limit.' Offset: '.$Offset.' TotalComments ' . $TotalComments);
			// die();
		}
   }
예제 #20
0
 /**
  * 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);
     }
 }
예제 #21
0
 /**
  * Set the user's timezone (hour offset).
  *
  * @since 2.0.0
  * @access public
  * @param string $ClientDate Client-reported datetime.
  * @param string $TransientKey Security token.
  */
 public function SetClientHour($ClientDate = '', $TransientKey = '')
 {
     $this->_DeliveryType = DELIVERY_TYPE_BOOL;
     $Session = Gdn::Session();
     $Success = FALSE;
     $ClientTimestamp = Gdn_Format::ToTimestamp($ClientDate);
     if (is_numeric($ClientTimestamp) && $Session->UserID > 0 && $Session->ValidateTransientKey($TransientKey)) {
         $UserModel = Gdn::UserModel();
         $HourOffset = $ClientTimestamp - time();
         $HourOffset = round($HourOffset / 3600);
         $UserModel->SetField($Session->UserID, 'HourOffset', $HourOffset);
         $Success = TRUE;
     }
     $this->Render();
 }
예제 #22
0
 /**
  * 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 = GetValue('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();
             unset($Values['Roles']);
             $AuthUserID = $this->UserModel->Register($Values, array('Method' => 'Invitation'));
             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 = GetValue('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();
 }
예제 #23
0
 /**
  * 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
  * @todo Remove debugging info if/when this is working correctly.
  *
  * @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 = GetValue('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;
 }
예제 #24
0
 /** 
  * 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 = '', $Offset = '0')
 {
     $this->Permission('Garden.Profiles.View');
     // Object setup
     $Session = Gdn::Session();
     $this->ActivityModel = new ActivityModel();
     // Calculate offset
     $Offset = is_numeric($Offset) ? $Offset : 0;
     if ($Offset < 0) {
         $Offset = 0;
     }
     // Get user, tab, and comment
     $this->GetUserInfo($UserReference, $Username, $UserID);
     $this->SetTabView('Activity');
     $Comment = $this->Form->GetFormValue('Comment');
     if ($Session->UserID > 0 && $this->Form->AuthenticatedPostBack() && !StringIsNullOrEmpty($Comment) && CheckPermission('Garden.Profiles.Edit')) {
         // Active user has submitted a comment
         $Comment = substr($Comment, 0, 1000);
         // Limit to 1000 characters...
         // Update About if necessary.
         $SendNotification = TRUE;
         if ($Session->UserID == $this->User->UserID) {
             $SendNotification = FALSE;
             $this->UserModel->SaveAbout($Session->UserID, $Comment);
             $this->User->About = $Comment;
             $this->SetJson('UserData', $this->FetchView('user'));
             $ActivityUserID = $Session->UserID;
             $RegardingUserID = $ActivityUserID;
             $ActivityType = 'AboutUpdate';
         } else {
             $ActivityUserID = $this->User->UserID;
             $RegardingUserID = $Session->UserID;
             $ActivityType = 'WallPost';
         }
         // Create activity entry
         $NewActivityID = $this->ActivityModel->Add($ActivityUserID, $ActivityType, $Comment, $RegardingUserID, '', '/profile/' . $this->ProfileUrl(), FALSE);
         // @todo Add a notification too.
         if ($this->_DeliveryType === DELIVERY_TYPE_ALL) {
             Redirect('dashboard/profile/' . $this->ProfileUrl());
         } else {
             // Load just the single new comment
             $this->HideActivity = TRUE;
             $this->ActivityData = $this->ActivityModel->GetWhere('ActivityID', $NewActivityID);
             $this->View = 'activities';
             $this->ControllerName = 'activity';
         }
     } else {
         // Load data to display
         $this->ProfileUserID = $this->User->UserID;
         $Limit = 50;
         $this->SetData('ActivityData', $this->ActivityModel->Get($this->User->UserID, $Offset, $Limit), TRUE);
         $TotalRecords = $this->ActivityModel->GetCount($this->User->UserID);
         if ($this->ActivityData->NumRows() > 0) {
             $ActivityData = $this->ActivityData->Result();
             $ActivityIDs = ConsolidateArrayValuesByKey($ActivityData, 'ActivityID');
             $LastActivity = $this->ActivityData->FirstRow();
             $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);
             $this->CommentData = $this->ActivityModel->GetComments($ActivityIDs);
         } else {
             $this->CommentData = FALSE;
         }
         // Build a pager
         $PagerFactory = new Gdn_PagerFactory();
         $this->Pager = $PagerFactory->GetPager('MorePager', $this);
         $this->Pager->MoreCode = 'More';
         $this->Pager->LessCode = 'Newer Activity';
         $this->Pager->ClientID = 'Pager';
         $this->Pager->Configure($Offset, $Limit, $TotalRecords, 'profile/activity/' . $this->User->UserID . '/' . Gdn_Format::Url($this->User->Name) . '/' . $this->User->UserID . '/%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'));
             if ($Offset > 0) {
                 $this->View = 'activities';
                 $this->ControllerName = 'Activity';
             }
         }
     }
     // 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();
 }
예제 #25
0
 /**
  * Modifies category data before it is returned.
  *
  * Adds CountAllDiscussions column to each category representing the sum of
  * discussions within this category as well as all subcategories.
  * 
  * @since 2.0.17
  * @access public
  *
  * @param object $Data SQL result.
  */
 public static function AddCategoryColumns($Data)
 {
     $Result =& $Data->Result();
     $Result2 = $Result;
     foreach ($Result as &$Category) {
         if (!property_exists($Category, 'CountAllDiscussions')) {
             $Category->CountAllDiscussions = $Category->CountDiscussions;
         }
         if (!property_exists($Category, 'CountAllComments')) {
             $Category->CountAllComments = $Category->CountComments;
         }
         // Calculate the following field.
         $Following = !((bool) GetValue('Archived', $Category) || (bool) GetValue('Unfollow', $Category));
         $Category->Following = $Following;
         $DateMarkedRead = GetValue('DateMarkedRead', $Category);
         $UserDateMarkedRead = GetValue('UserDateMarkedRead', $Category);
         if (!$DateMarkedRead) {
             $DateMarkedRead = $UserDateMarkedRead;
         } elseif ($UserDateMarkedRead && Gdn_Format::ToTimestamp($UserDateMarkedRead) > Gdn_Format::ToTimeStamp($DateMarkedRead)) {
             $DateMarkedRead = $UserDateMarkedRead;
         }
         // Set appropriate Last* columns.
         SetValue('LastTitle', $Category, GetValue('LastDiscussionTitle', $Category, NULL));
         $LastDateInserted = GetValue('LastDateInserted', $Category, NULL);
         if (GetValue('LastCommentUserID', $Category) == NULL) {
             SetValue('LastCommentUserID', $Category, GetValue('LastDiscussionUserID', $Category, NULL));
             SetValue('DateLastComment', $Category, GetValue('DateLastDiscussion', $Category, NULL));
             SetValue('LastUserID', $Category, GetValue('LastDiscussionUserID', $Category, NULL));
             $LastDiscussion = ArrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name'));
             SetValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, FALSE, '//') . '#latest');
             if (is_null($LastDateInserted)) {
                 SetValue('LastDateInserted', $Category, GetValue('DateLastDiscussion', $Category, NULL));
             }
         } else {
             $LastDiscussion = ArrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name'));
             SetValue('LastUserID', $Category, GetValue('LastCommentUserID', $Category, NULL));
             SetValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, FALSE, '//') . '#latest');
             if (is_null($LastDateInserted)) {
                 SetValue('LastDateInserted', $Category, GetValue('DateLastComment', $Category, NULL));
             }
         }
         $LastDateInserted = GetValue('LastDateInserted', $Category, NULL);
         if ($DateMarkedRead) {
             if ($LastDateInserted) {
                 $Category->Read = Gdn_Format::ToTimestamp($DateMarkedRead) >= Gdn_Format::ToTimestamp($LastDateInserted);
             } else {
                 $Category->Read = TRUE;
             }
         } else {
             $Category->Read = FALSE;
         }
         foreach ($Result2 as $Category2) {
             if ($Category2->TreeLeft > $Category->TreeLeft && $Category2->TreeRight < $Category->TreeRight) {
                 $Category->CountAllDiscussions += $Category2->CountDiscussions;
                 $Category->CountAllComments += $Category2->CountComments;
             }
         }
     }
 }
 /**
  * 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'] = GetValue('CountVisits', $User, 0) + 1;
         }
     }
     // Generate the AllIPs field.
     $AllIPs = GetValue('AllIPAddresses', $User, array());
     if (is_string($AllIPs)) {
         $AllIPs = explode(',', $AllIPs);
         SetValue('AllIPAddresses', $User, $AllIPs);
     }
     if (!is_array($AllIPs)) {
         $AllIPs = array();
     }
     if ($IP = GetValue('InsertIPAddress', $User)) {
         $AllIPs[] = ForceIPv4($IP);
     }
     if ($IP = GetValue('LastIPAddress', $User)) {
         $AllIPs[] = $IP;
     }
     $AllIPs = array_unique($AllIPs);
     sort($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 (GetValue($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);
         }
     }
 }
예제 #27
0
 /**
  * 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_AfterAddColumns_Handler($Sender)
 {
     // Only for members
     $Session = Gdn::Session();
     if (!$Session->IsValid()) {
         return;
     }
     // Recalculate New count with user's DateAllViewed
     $DateAllViewed = Gdn_Format::ToTimestamp($Session->User->DateAllViewed);
     foreach ($Sender->EventArguments['Data']->Result() as $Discussion) {
         if ($DateAllViewed != 0) {
             // Only if they've used AllViewed
             if (Gdn_Format::ToTimestamp($Discussion->DateInserted) > $DateAllViewed) {
                 // Discussion is newer than last 'AllViewed' click
                 continue;
             }
             if (Gdn_Format::ToTimestamp($Discussion->DateLastComment) <= $DateAllViewed) {
                 // Covered by AllViewed
                 $Discussion->CountUnreadComments = 0;
             } elseif (Gdn_Format::ToTimestamp($Discussion->DateLastViewed) == $DateAllViewed || !$Discussion->DateLastViewed) {
                 // User clicked AllViewed
                 // Discussion is older than click
                 // Last comment is newer than click
                 // No UserDiscussion record found OR UserDiscussion was set by AllViewed
                 $Discussion->CountUnreadComments = $this->GetCommentCountSince($Discussion->DiscussionID, $DateAllViewed);
             }
         }
     }
 }
예제 #28
0
 /**
  * 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 + 1);
         if ($CountWatch > $TotalComments) {
             $CountWatch = $TotalComments;
         }
         // This dicussion looks familiar...
         if (is_numeric($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 = GetValue('CategoryID', $Discussion);
         if ($CategoryID) {
             $Category = CategoryModel::Categories($CategoryID);
             if ($Category) {
                 $DateMarkedRead = GetValue('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);
                         }
                     }
                 }
             }
         }
     }
 }
예제 #29
0
 /**
  * Join message counts into the discussion list.
  * @param DiscussionModel $Sender
  * @param array $Args
  */
 public function DiscussionModel_AfterAddColumns_Handler($Sender, $Args)
 {
     if (!Gdn::Session()->UserID) {
         return;
     }
     $Data = $Args['Data'];
     $Result =& $Data->Result();
     // Gather the discussion IDs.
     $DiscusisonIDs = array();
     foreach ($Result as $Row) {
         $DiscusisonIDs[] = GetValue('DiscussionID', $Row);
     }
     // Grab all of the whispers associated to the discussions being looked at.
     $Sql = Gdn::SQL()->Select('c.DiscussionID')->Select('c.CountMessages', 'sum', 'CountMessages')->Select('c.DateUpdated', 'max', 'DateLastMessage')->From('Conversation c')->WhereIn('c.DiscussionID', $DiscusisonIDs)->GroupBy('c.DiscussionID');
     if (!Gdn::Session()->CheckPermission('Conversations.Moderation.Manage')) {
         $Sql->Join('UserConversation uc', 'c.ConversationID = uc.ConversationID')->Where('uc.UserID', Gdn::Session()->UserID);
     }
     $Conversations = $Sql->Get()->ResultArray();
     $Conversations = Gdn_DataSet::Index($Conversations, 'DiscussionID');
     foreach ($Result as &$Row) {
         $DiscusisonID = GetValue('DiscussionID', $Row);
         $CRow = GetValue($DiscusisonID, $Conversations);
         if (!$CRow) {
             continue;
         }
         $DateLastViewed = GetValue('DateLastViewed', $Row);
         $DateLastMessage = $CRow['DateLastMessage'];
         $NewWhispers = Gdn_Format::ToTimestamp($DateLastViewed) < Gdn_Format::ToTimestamp($DateLastMessage);
         SetValue('CountWhispers', $Row, $CRow['CountMessages']);
         SetValue('DateLastWhisper', $Row, $DateLastMessage);
         SetValue('NewWhispers', $Row, $NewWhispers);
     }
 }
예제 #30
0
 /**
  * Signin process that multiple authentication methods.
  *
  * @access public
  * @since 2.0.0
  * @author Tim Gunter
  * 
  * @param string $Method
  * @param array $Arg1
  * @return string Rendered XHTML template.
  */
 public function SignIn($Method = FALSE, $Arg1 = FALSE)
 {
     $this->AddJsFile('entry.js');
     $this->SetData('Title', T('Sign In'));
     $this->Form->AddHidden('Target', $this->Target());
     $this->Form->AddHidden('ClientHour', date('Y-m-d H:00'));
     // Use the server's current hour as a default.
     // Additional signin methods are set up with plugins.
     $Methods = array();
     $this->SetData('Methods', $Methods);
     $this->SetData('FormUrl', Url('entry/signin'));
     $this->FireEvent('SignIn');
     if ($this->Form->IsPostBack()) {
         $this->Form->ValidateRule('Email', 'ValidateRequired', sprintf(T('%s is required.'), T(UserModel::SigninLabelCode())));
         $this->Form->ValidateRule('Password', 'ValidateRequired');
         // Check the user.
         if ($this->Form->ErrorCount() == 0) {
             $Email = $this->Form->GetFormValue('Email');
             $User = Gdn::UserModel()->GetByEmail($Email);
             if (!$User) {
                 $User = Gdn::UserModel()->GetByUsername($Email);
             }
             if (!$User) {
                 $this->Form->AddError('@' . sprintf(T('User not found.'), strtolower(T(UserModel::SigninLabelCode()))));
             } else {
                 $ClientHour = $this->Form->GetFormValue('ClientHour');
                 $HourOffset = Gdn_Format::ToTimestamp($ClientHour) - time();
                 $HourOffset = round($HourOffset / 3600);
                 // Check the password.
                 $PasswordHash = new Gdn_PasswordHash();
                 $Password = $this->Form->GetFormValue('Password');
                 try {
                     $PasswordChecked = $PasswordHash->CheckPassword($Password, GetValue('Password', $User), GetValue('HashMethod', $User));
                 } catch (Gdn_UserException $Ex) {
                     $this->Form->AddError($Ex);
                     $this->Render();
                     return;
                 }
                 if ($PasswordChecked) {
                     // Update weak passwords
                     $HashMethod = GetValue('HashMethod', $User);
                     if ($PasswordHash->Weak || $HashMethod && strcasecmp($HashMethod, 'Vanilla') != 0) {
                         $Pw = $PasswordHash->HashPassword($Password);
                         Gdn::UserModel()->SetField(GetValue('UserID', $User), array('Password' => $Pw, 'HashMethod' => 'Vanilla'));
                     }
                     Gdn::Session()->Start(GetValue('UserID', $User), TRUE, (bool) $this->Form->GetFormValue('RememberMe'));
                     if (!Gdn::Session()->CheckPermission('Garden.SignIn.Allow')) {
                         $this->Form->AddError('ErrorPermission');
                         Gdn::Session()->End();
                     } else {
                         if ($HourOffset != Gdn::Session()->User->HourOffset) {
                             Gdn::UserModel()->SetProperty(Gdn::Session()->UserID, 'HourOffset', $HourOffset);
                         }
                         Gdn::UserModel()->FireEvent('AfterSignIn');
                         $this->_SetRedirect();
                     }
                 } else {
                     $this->Form->AddError('Invalid password.');
                 }
             }
         }
     } else {
         if ($Target = $this->Request->Get('Target')) {
             $this->Form->AddHidden('Target', $Target);
         }
         $this->Form->SetValue('RememberMe', TRUE);
     }
     return $this->Render();
 }