diff --git a/applications/conversations/controllers/class.messagescontroller.php b/applications/conversations/controllers/class.messagescontroller.php index ee87b560c1a..4a237dcb378 100755 --- a/applications/conversations/controllers/class.messagescontroller.php +++ b/applications/conversations/controllers/class.messagescontroller.php @@ -61,7 +61,9 @@ public function Initialize() { $this->SetData('Breadcrumbs', array(array('Name' => T('Inbox'), 'Url' => '/messages/inbox'))); // $this->AddModule('MeModule'); $this->AddModule('SignedInModule'); - $this->AddModule('NewConversationModule'); + + if (CheckPermission('Conversations.Conversations.Add')) + $this->AddModule('NewConversationModule'); } /** @@ -73,6 +75,7 @@ public function Initialize() { * @param string $Recipient Username of the recipient. */ public function Add($Recipient = '') { + $this->Permission('Conversations.Conversations.Add'); $this->Form->SetModel($this->ConversationModel); if ($this->Form->AuthenticatedPostBack()) { @@ -380,8 +383,10 @@ public function Index($ConversationID = FALSE, $Offset = -1, $Limit = '') { $InThisConversationModule = new InThisConversationModule($this); $InThisConversationModule->SetData($this->RecipientData); $this->AddModule($InThisConversationModule); - - $this->AddModule('AddPeopleModule'); + + // Doesn't make sense for people who can't even start conversations to be adding people + if (CheckPermission('Conversations.Conversations.Add')) + $this->AddModule('AddPeopleModule'); $Subject = $this->Data('Conversation.Subject'); if (!$Subject) diff --git a/applications/conversations/models/class.conversationmodel.php b/applications/conversations/models/class.conversationmodel.php index 4fab588d4ff..ea9d55a7330 100755 --- a/applications/conversations/models/class.conversationmodel.php +++ b/applications/conversations/models/class.conversationmodel.php @@ -50,7 +50,7 @@ public function ConversationQuery($ViewingUserID, $Join = '') { ->From('Conversation c'); - if ($ViewingUserID !== FALSE) { + if ($ViewingUserID) { $this->SQL ->Select('c.CountMessages - uc.CountReadMessages', '', 'CountNewMessages') ->Select('uc.LastMessageID, uc.CountReadMessages, uc.DateLastViewed, uc.Bookmarked') @@ -287,7 +287,7 @@ public function JoinParticipants(&$Data) { ->From('UserConversation uc') ->Join('User u', 'u.UserID = uc.UserID'); - Gdn_DataSet::Join($Data, array('alias' => 'uc', 'parent' => 'ConversationID', 'column' => 'Participants', 'UserID', 'u.Name', 'u.Photo'), array('sql' => $this->SQL)); + Gdn_DataSet::Join($Data, array('alias' => 'uc', 'parent' => 'ConversationID', 'column' => 'Participants', 'UserID', 'u.Name', 'u.Email', 'u.Photo'), array('sql' => $this->SQL)); } public function JoinLastMessages(&$Data) { diff --git a/applications/conversations/modules/class.addpeoplemodule.php b/applications/conversations/modules/class.addpeoplemodule.php index 22a152ade4b..70115975d93 100755 --- a/applications/conversations/modules/class.addpeoplemodule.php +++ b/applications/conversations/modules/class.addpeoplemodule.php @@ -17,6 +17,9 @@ class AddPeopleModule extends Gdn_Module { public $Form; public function __construct($Sender = '') { + if (!CheckPermission('Conversations.Conversations.Add')) + throw PermissionException(); + $Session = Gdn::Session(); if (property_exists($Sender, 'Conversation')) $this->Conversation = $Sender->Conversation; diff --git a/applications/conversations/settings/structure.php b/applications/conversations/settings/structure.php index c518f75dcf4..60a3903eff8 100755 --- a/applications/conversations/settings/structure.php +++ b/applications/conversations/settings/structure.php @@ -141,5 +141,6 @@ $PermissionModel = Gdn::PermissionModel(); $PermissionModel->Define(array( - 'Conversations.Moderation.Manage' => 0 + 'Conversations.Moderation.Manage' => 0, + 'Conversations.Conversations.Add' => 'Garden.Profiles.Edit', )); \ No newline at end of file diff --git a/applications/conversations/views/messages/all.php b/applications/conversations/views/messages/all.php index ecb18b69eaa..68cdd8d7a7b 100755 --- a/applications/conversations/views/messages/all.php +++ b/applications/conversations/views/messages/all.php @@ -11,10 +11,11 @@ // Pre Pager echo '
'; PagerModule::Write($PagerOptions); - - echo '
'; - echo Anchor(Sprite('SpMessage').' '.T('New Message'), '/messages/add', 'Button NewConversation Primary'); - echo '
'; + if (CheckPermission('Conversations.Conversations.Add')) { + echo '
'; + echo Anchor(Sprite('SpMessage').' '.T('New Message'), '/messages/add', 'Button NewConversation Primary'); + echo '
'; + } echo '
'; ?>
diff --git a/applications/conversations/views/messages/conversations.php b/applications/conversations/views/messages/conversations.php index 9c4f44064d0..dba7185c9a4 100755 --- a/applications/conversations/views/messages/conversations.php +++ b/applications/conversations/views/messages/conversations.php @@ -49,18 +49,23 @@ ConversationID.'/#Item_'.$JumpToItem; + echo '

'; + if ($Names) { - echo '

'; - if ($LastPhoto) { echo '
'.$LastPhoto.'
'; } - echo Anchor(htmlspecialchars($Names), $Url), '

'; + echo Anchor(htmlspecialchars($Names), $Url); } - if ($SubjectsVisible && $Subject = GetValue('Subject', $Conversation)) { - echo '
'.Anchor(htmlspecialchars($Subject), $Url).'
'; + if ($Subject = GetValue('Subject', $Conversation)) { + if ($Names) + echo Bullet(' '); + + echo ''.Anchor(htmlspecialchars($Subject), $Url).''; } + + echo ''; ?>
diff --git a/applications/conversations/views/messages/index.php b/applications/conversations/views/messages/index.php index aeac92d41d2..fb00f1cca56 100755 --- a/applications/conversations/views/messages/index.php +++ b/applications/conversations/views/messages/index.php @@ -1,11 +1,17 @@
-

Participants; ?>

+

+ Participants; + + if ($this->Data('Conversation.Subject')) { + echo + Bullet(' '). + '' .htmlspecialchars($this->Data('Conversation.Subject')).''; + } + ?> +

Data('Conversation.Subject') && C('Conversations.Subjects.Visible')) { - echo '

'.htmlspecialchars($this->Data('Conversation.Subject')).'

'; -} - if ($this->Data('_HasDeletedUsers')) { echo '
', T('One or more users have left this conversation.', 'One or more users have left this conversation. They won\'t receive any more messages unless you add them back in to the conversation.'), '
'; } diff --git a/applications/conversations/views/messages/popin.php b/applications/conversations/views/messages/popin.php index 18cd96dc89e..a644a157569 100644 --- a/applications/conversations/views/messages/popin.php +++ b/applications/conversations/views/messages/popin.php @@ -1,8 +1,9 @@
  • -
  • diff --git a/applications/conversations/views/modules/inbox.php b/applications/conversations/views/modules/inbox.php index 30174d3d00b..465a922d825 100644 --- a/applications/conversations/views/modules/inbox.php +++ b/applications/conversations/views/modules/inbox.php @@ -57,7 +57,8 @@
@@ -67,7 +68,8 @@ ?>
diff --git a/applications/dashboard/controllers/class.entrycontroller.php b/applications/dashboard/controllers/class.entrycontroller.php index 123228f617a..9b4067cfbcc 100755 --- a/applications/dashboard/controllers/class.entrycontroller.php +++ b/applications/dashboard/controllers/class.entrycontroller.php @@ -784,7 +784,7 @@ public function SignIn2() { * * @param string $TransientKey (default: "") */ - public function SignOut($TransientKey = "") { + public function SignOut($TransientKey = "", $Override = "0") { $this->CheckOverride('SignOut', $this->Target(), $TransientKey); if (Gdn::Session()->ValidateTransientKey($TransientKey) || $this->Form->IsPostBack()) { @@ -804,6 +804,7 @@ public function SignOut($TransientKey = "") { } elseif (!Gdn::Session()->IsValid()) $this->_SetRedirect(); + $this->SetData('Override', $Override); $this->SetData('Target', $this->Target()); $this->Leaving = FALSE; $this->Render(); diff --git a/applications/dashboard/controllers/class.profilecontroller.php b/applications/dashboard/controllers/class.profilecontroller.php index e2fd5dbd9ba..33420a905b1 100644 --- a/applications/dashboard/controllers/class.profilecontroller.php +++ b/applications/dashboard/controllers/class.profilecontroller.php @@ -979,14 +979,14 @@ public function SendInvite($InvitationID = '', $TransientKey = '') { public function _SetBreadcrumbs($Name = NULL, $Url = NULL) { // Add the root link. - if ($this->User->UserID == Gdn::Session()->UserID) { + if (GetValue('UserID', $this->User) == Gdn::Session()->UserID) { $Root = array('Name' => T('Profile'), 'Url' => '/profile'); $Breadcrumb = array('Name' => $Name, 'Url' => $Url); } else { $NameUnique = C('Garden.Registration.NameUnique'); - $Root = array('Name' => $this->User->Name, 'Url' => UserUrl($this->User)); - $Breadcrumb = array('Name' => $Name, 'Url' => $Url.'/'.($NameUnique ? '' : $this->User->UserID.'/').rawurlencode($this->User->Name)); + $Root = array('Name' => GetValue('Name', $this->User), 'Url' => UserUrl($this->User)); + $Breadcrumb = array('Name' => $Name, 'Url' => $Url.'/'.($NameUnique ? '' : GetValue('UserID', $this->User).'/').rawurlencode(GetValue('Name', $this->User))); } $this->Data['Breadcrumbs'][] = $Root; diff --git a/applications/dashboard/controllers/class.settingscontroller.php b/applications/dashboard/controllers/class.settingscontroller.php index 6e388f75774..45e1d27349b 100644 --- a/applications/dashboard/controllers/class.settingscontroller.php +++ b/applications/dashboard/controllers/class.settingscontroller.php @@ -842,7 +842,6 @@ public function TestAddon($AddonType = '', $AddonName = '', $TransientKey = '') } ob_clean(); - header(self::GetStatusMessage(200), TRUE, 200); echo 'Success'; } @@ -1010,46 +1009,6 @@ public function CancelPreview() { Redirect('settings/themes'); } - /** - * Remove an addon. - * - * @since 2.0.0 - * @access public - * @param string $Type Application or plugin. - * @param string $Name Unique ID of app or plugin. - * @param string $TransientKey Security token. - */ - public function RemoveAddon($Type, $Name, $TransientKey = '') { - $RequiredPermission = 'Undefined'; - switch ($Type) { - case SettingsModule::TYPE_APPLICATION: - $Manager = Gdn::Factory('ApplicationManager'); - $Enabled = 'EnabledApplications'; - $Remove = 'RemoveApplication'; - $RequiredPermission = 'Garden.Settings.Manage'; - break; - case SettingsModule::TYPE_PLUGIN: - $Manager = Gdn::Factory('PluginManager'); - $Enabled = 'EnabledPlugins'; - $Remove = 'RemovePlugin'; - $RequiredPermission = 'Garden.Settings.Manage'; - break; - } - - $Session = Gdn::Session(); - if ($Session->ValidateTransientKey($TransientKey) && $Session->CheckPermission($RequiredPermission)) { - try { - if (array_key_exists($Name, $Manager->$Enabled()) === FALSE) { - $Manager->$Remove($Name); - } - } catch (Exception $e) { - $this->Form->AddError(strip_tags($e->getMessage())); - } - } - if ($this->Form->ErrorCount() == 0) - Redirect('/settings/plugins'); - } - /** * Remove the logo from config & delete it. * diff --git a/applications/dashboard/controllers/class.usercontroller.php b/applications/dashboard/controllers/class.usercontroller.php index d7cb9e25a06..8d15a6c6bf9 100755 --- a/applications/dashboard/controllers/class.usercontroller.php +++ b/applications/dashboard/controllers/class.usercontroller.php @@ -133,10 +133,17 @@ public function Add() { $this->AddSideMenu('dashboard/user'); $RoleModel = new RoleModel(); - $AllRoles = $RoleModel->GetArray(); + $RoleData = $AllRoles = $RoleModel->GetArray(); + + // If not administrator, restrict to default registration roles. + if (!CheckPermission('Garden.Settings.Manage')) { + $DefaultRoleIDs = C('Garden.Registration.DefaultRoles', array(8)); + $DefaultRoles = array_combine($DefaultRoleIDs, $DefaultRoleIDs); + $RoleData = array_intersect_key($AllRoles, $DefaultRoles); + } // By default, people with access here can freely assign all roles - $this->RoleData = $AllRoles; + $this->RoleData = $RoleData; $UserModel = new UserModel(); $this->User = FALSE; @@ -278,7 +285,7 @@ public function AutoComplete() { * @param type $UserID */ public function Ban($UserID, $Unban = FALSE) { - $this->Permission('Garden.Moderation.Manage'); + $this->Permission(array('Garden.Moderation.Manage','Moderation.Users.Ban'), FALSE); $User = Gdn::UserModel()->GetID($UserID, DATASET_TYPE_ARRAY); if (!$User) @@ -505,14 +512,10 @@ public function Edit($UserID) { $this->Title(T('Edit User')); $this->AddSideMenu('dashboard/user'); - // By default, people with access here can freely assign all roles + // Only admins can reassign roles $RoleModel = new RoleModel(); - $RoleData = $AllRoles = $RoleModel->GetArray(); - - // Hide personal info roles - if (!CheckPermission('Garden.PersonalInfo.View')) { - $RoleData = array_filter($RoleData, 'RoleModel::FilterPersonalInfo'); - } + $AllRoles = $RoleModel->GetArray(); + $RoleData = (CheckPermission('Garden.Settings.Manage')) ? $AllRoles : array(); $UserModel = new UserModel(); $User = $UserModel->GetID($UserID, DATASET_TYPE_ARRAY); @@ -522,22 +525,32 @@ public function Edit($UserID) { $this->SetData('_CanEditUsername', $CanEditUsername); // Determine if emails can be edited - $CanEditEmail = ( - Gdn::Session()->CheckPermission('Garden.Users.Edit') && - Gdn::Session()->CheckPermission('Garden.PersonalInfo.View') - ); + $CanEditEmail = Gdn::Session()->CheckPermission('Garden.Users.Edit'); $this->SetData('_CanEditEmail', $CanEditEmail); // Decide if they have ability to confirm users $Confirmed = (bool)GetValueR('Confirmed', $User); $CanConfirmEmail = ( UserModel::RequireConfirmEmail() && - Gdn::Session()->CheckPermission('Garden.Users.Edit') && - Gdn::Session()->CheckPermission('Garden.PersonalInfo.View')); + Gdn::Session()->CheckPermission('Garden.Users.Edit')); $this->SetData('_CanConfirmEmail', $CanConfirmEmail); $this->SetData('_EmailConfirmed', $Confirmed); $User['ConfirmEmail'] = (int)$Confirmed; + // Determine whether user being edited is privileged (can escalate permissions) + $UserModel = new UserModel(); + $EditingPrivilegedUser = $UserModel->CheckPermission($User, 'Garden.Settings.Manage'); + + // Determine our password reset options + // Anyone with user editing my force reset over email + $this->ResetOptions = array( + 0 => T('Keep current password.'), + 'Auto' => T('Force user to reset their password and send email notification.') + ); + // Only admins may manually reset passwords for other admins + if (CheckPermission('Garden.Settings.Manage') || !$EditingPrivilegedUser) + $this->ResetOptions['Manual'] = T('Manually set user password. No email notification.'); + // Set the model on the form. $this->Form->SetModel($UserModel); @@ -578,12 +591,16 @@ public function Edit($UserID) { if ($CanConfirmEmail && is_bool($Confirmation)) $this->Form->SetFormValue('Confirmed', (int)$Confirmation); - // If a new password was specified, add it to the form's collection $ResetPassword = $this->Form->GetValue('ResetPassword', FALSE); - $NewPassword = $this->Form->GetValue('NewPassword', ''); - if ($ResetPassword == 'Manual') + + // If we're an admin or this isn't a privileged user, allow manual setting of password + $AllowManualReset = (CheckPermission('Garden.Settings.Manage') || !$EditingPrivilegedUser); + if ($ResetPassword == 'Manual' && $AllowManualReset) { + // If a new password was specified, add it to the form's collection + $NewPassword = $this->Form->GetValue('NewPassword', ''); $this->Form->SetFormValue('Password', $NewPassword); - + } + // Role changes // These are the new roles the editing user wishes to apply to the target diff --git a/applications/dashboard/controllers/class.utilitycontroller.php b/applications/dashboard/controllers/class.utilitycontroller.php index 9ade63638c4..f5a45a25fd0 100755 --- a/applications/dashboard/controllers/class.utilitycontroller.php +++ b/applications/dashboard/controllers/class.utilitycontroller.php @@ -376,6 +376,8 @@ public function Update() { $this->SetData('Success', TRUE); } catch (Exception $Ex) { $this->SetData('Success', FALSE); + if (Debug()) + throw $Ex; } if (Gdn::Session()->CheckPermission('Garden.Settings.Manage')) { diff --git a/applications/dashboard/models/class.activitymodel.php b/applications/dashboard/models/class.activitymodel.php index 90dbec11101..ff5e02082bd 100755 --- a/applications/dashboard/models/class.activitymodel.php +++ b/applications/dashboard/models/class.activitymodel.php @@ -1343,10 +1343,13 @@ protected function NotifyWallPost($WallPost) { $Activity = array( 'ActivityType' => 'WallPost', 'ActivityUserID' => $WallPost['RegardingUserID'], + 'Format' => $WallPost['Format'], 'NotifyUserID' => $WallPost['ActivityUserID'], 'RecordType' => 'Activity', 'RecordID' => $WallPost['ActivityID'], + 'RegardingUserID' => $WallPost['ActivityUserID'], 'Route' => UserUrl($NotifyUser, ''), + 'Story' => $WallPost['Story'], 'HeadlineFormat' => T('HeadlineFormat.NotifyWallPost', '{ActivityUserID,User} posted on your wall.') ); @@ -1377,4 +1380,4 @@ protected function _Touch(&$Data) { if (!isset($Data['Data']) || !is_array($Data['Data'])) $Data['Data'] = array(); } -} \ No newline at end of file +} diff --git a/applications/dashboard/models/class.rolemodel.php b/applications/dashboard/models/class.rolemodel.php index b8c27508d2c..bc4164eb128 100644 --- a/applications/dashboard/models/class.rolemodel.php +++ b/applications/dashboard/models/class.rolemodel.php @@ -384,7 +384,9 @@ public static function SetUserRoles(&$Users, $UserIDColumn = 'UserID', $RolesCol public function Delete($RoleID, $ReplacementRoleID) { // First update users that will be orphaned if (is_numeric($ReplacementRoleID) && $ReplacementRoleID > 0) { - $this->SQL->Update('UserRole') + $this->SQL + ->Options('Ignore', TRUE) + ->Update('UserRole') ->Join('UserRole urs', 'UserRole.UserID = urs.UserID') ->GroupBy('urs.UserID') ->Having('count(urs.RoleID) =', '1', TRUE, FALSE) diff --git a/applications/dashboard/models/class.updatemodel.php b/applications/dashboard/models/class.updatemodel.php index a7bc03a2744..2e7f2bbe5b5 100644 --- a/applications/dashboard/models/class.updatemodel.php +++ b/applications/dashboard/models/class.updatemodel.php @@ -795,10 +795,12 @@ public function RunStructure($AddonCode = NULL, $Explicit = FALSE, $Drop = FALSE $Apps = $ApplicationManager->EnabledApplications(); $AppNames = ConsolidateArrayValuesByKey($Apps, 'Folder'); $Paths = array(); - foreach ($Apps as $AppInfo) { + foreach ($Apps as $Key => $AppInfo) { $Path = PATH_APPLICATIONS."/{$AppInfo['Folder']}/settings/structure.php"; if (file_exists($Path)) $Paths[] = $Path; + + Gdn::ApplicationManager()->RegisterPermissions($Key, $this->Validation); } // Execute the structures. diff --git a/applications/dashboard/models/class.usermodel.php b/applications/dashboard/models/class.usermodel.php index 349d34c23b0..3bf125386fa 100755 --- a/applications/dashboard/models/class.usermodel.php +++ b/applications/dashboard/models/class.usermodel.php @@ -2260,7 +2260,7 @@ public function AddInsertFields(&$Fields) { // Set some required dates. $Now = Gdn_Format::ToDateTime(); $Fields[$this->DateInserted] = $Now; - $Fields['DateFirstVisit'] = $Now; + TouchValue('DateFirstVisit', $Fields, $Now); $Fields['DateLastActive'] = $Now; $Fields['InsertIPAddress'] = Gdn::Request()->IpAddress(); $Fields['LastIPAddress'] = Gdn::Request()->IpAddress(); @@ -2765,13 +2765,13 @@ public function GetInvitationCount($UserID) { ->FirstRow(); // If CountInvitations is null (ie. never been set before) or it is a new month since the DateSetInvitations - if ($User->CountInvitations == '' || is_null($User->DateSetInvitations) || Gdn_Format::Date($User->DateSetInvitations, 'n Y') != Gdn_Format::Date('', 'n Y')) { + if ($User->CountInvitations == '' || is_null($User->DateSetInvitations) || Gdn_Format::Date($User->DateSetInvitations, '%m %Y') != Gdn_Format::Date('', '%m %Y')) { // Reset CountInvitations and DateSetInvitations $this->SQL->Put( $this->Name, array( 'CountInvitations' => $InviteCount, - 'DateSetInvitations' => Gdn_Format::Date('', 'Y-m-01') // The first day of this month + 'DateSetInvitations' => Gdn_Format::Date('', '%Y-%m-01') // The first day of this month ), array('UserID' => $UserID) ); diff --git a/applications/dashboard/modules/class.menumodule.php b/applications/dashboard/modules/class.menumodule.php index 25fed086742..362948aadb9 100755 --- a/applications/dashboard/modules/class.menumodule.php +++ b/applications/dashboard/modules/class.menumodule.php @@ -160,7 +160,7 @@ public function ToString($HighlightRoute = '') { if ($RequiredPermissions !== FALSE && !is_array($RequiredPermissions)) $RequiredPermissions = explode(',', $RequiredPermissions); - // Show if there are no permissions or the user has the required permissions or the user is admin + // Show if there are no permissions or the user has ANY of the specified permissions or the user is admin $ShowLink = $Admin || $RequiredPermissions === FALSE || ArrayInArray($RequiredPermissions, $Permissions, FALSE) === TRUE; if ($ShowLink === TRUE) { diff --git a/applications/dashboard/modules/class.profileoptionsmodule.php b/applications/dashboard/modules/class.profileoptionsmodule.php index eff1b745f57..a7e4c328855 100755 --- a/applications/dashboard/modules/class.profileoptionsmodule.php +++ b/applications/dashboard/modules/class.profileoptionsmodule.php @@ -36,7 +36,7 @@ public function ToString() { } else if (C('Garden.UserAccount.AllowEdit')) { $ProfileOptions[] = array('Text' => Sprite('SpEditProfile').' '.T('Edit Profile'), 'Url' => '/profile/edit'); } - if ($Session->CheckPermission('Garden.Moderation.Manage') && $UserID != $Session->UserID) { + if ((CheckPermission('Garden.Moderation.Manage') || CheckPermission('Moderation.Users.Ban')) && $UserID != $Session->UserID) { // Ban/Unban if ($Controller->User->Banned) { $ProfileOptions[] = array('Text' => Sprite('SpBan').' '.T('Unban'), 'Url' => "/user/ban?userid=$UserID&unban=1", 'CssClass' => 'Popup'); diff --git a/applications/dashboard/settings/structure.php b/applications/dashboard/settings/structure.php index 0dd41ab3e58..ea8103c07d5 100755 --- a/applications/dashboard/settings/structure.php +++ b/applications/dashboard/settings/structure.php @@ -41,12 +41,12 @@ $RoleModel->Database = $Database; $RoleModel->SQL = $SQL; $Sort = 1; - $RoleModel->Define(array('Name' => 'Guest', 'RoleID' => 2, 'Sort' => $Sort++, 'Deletable' => '0', 'CanSession' => '0', 'Description' => 'Guests can only view content. Anyone browsing the site who is not signed in is considered to be a "Guest".')); - $RoleModel->Define(array('Name' => 'Unconfirmed', 'RoleID' => 3, 'Sort' => $Sort++, 'Deletable' => '0', 'CanSession' => '1', 'Description' => 'Users must confirm their emails before becoming full members. They get assigned to this role.')); - $RoleModel->Define(array('Name' => 'Applicant', 'RoleID' => 4, 'Sort' => $Sort++, 'Deletable' => '0', 'CanSession' => '1', 'Description' => 'Users who have applied for membership, but have not yet been accepted. They have the same permissions as guests.')); - $RoleModel->Define(array('Name' => 'Member', 'RoleID' => 8, 'Sort' => $Sort++, 'Deletable' => '1', 'CanSession' => '1', 'Description' => 'Members can participate in discussions.')); - $RoleModel->Define(array('Name' => 'Moderator', 'RoleID' => 32, 'Sort' => $Sort++, 'Deletable' => '1', 'CanSession' => '1', 'Description' => 'Moderators have permission to edit most content.')); - $RoleModel->Define(array('Name' => 'Administrator', 'RoleID' => 16, 'Sort' => $Sort++, 'Deletable' => '1', 'CanSession' => '1', 'Description' => 'Administrators have permission to do anything.')); + $RoleModel->Define(array('Name' => 'Guest', 'RoleID' => 2, 'Sort' => $Sort++, 'Deletable' => '0', 'CanSession' => '0', 'Description' => T('Guest Role Description', 'Guests can only view content. Anyone browsing the site who is not signed in is considered to be a "Guest".'))); + $RoleModel->Define(array('Name' => 'Unconfirmed', 'RoleID' => 3, 'Sort' => $Sort++, 'Deletable' => '0', 'CanSession' => '1', 'Description' => T('Unconfirmed Role Description', 'Users must confirm their emails before becoming full members. They get assigned to this role.'))); + $RoleModel->Define(array('Name' => 'Applicant', 'RoleID' => 4, 'Sort' => $Sort++, 'Deletable' => '0', 'CanSession' => '1', 'Description' => T('Applicant Role Description', 'Users who have applied for membership, but have not yet been accepted. They have the same permissions as guests.'))); + $RoleModel->Define(array('Name' => 'Member', 'RoleID' => 8, 'Sort' => $Sort++, 'Deletable' => '1', 'CanSession' => '1', 'Description' => T('Member Role Description', 'Members can participate in discussions.'))); + $RoleModel->Define(array('Name' => 'Moderator', 'RoleID' => 32, 'Sort' => $Sort++, 'Deletable' => '1', 'CanSession' => '1', 'Description' => T('Moderator Role Description', 'Moderators have permission to edit most content.'))); + $RoleModel->Define(array('Name' => 'Administrator', 'RoleID' => 16, 'Sort' => $Sort++, 'Deletable' => '1', 'CanSession' => '1', 'Description' => T('Administrator Role Description', 'Administrators have permission to do anything.'))); unset($RoleModel); } @@ -722,6 +722,10 @@ ->Column('NewUserID', 'int') ->Set(); +// Save the current input formatter to the user's config. +// This will allow us to change the default later and grandfather existing forums in. +SaveToConfig('Garden.InputFormatter', C('Garden.InputFormatter')); + // Make sure the smarty folders exist. if (!file_exists(PATH_CACHE.'/Smarty')) @mkdir(PATH_CACHE.'/Smarty'); if (!file_exists(PATH_CACHE.'/Smarty/cache')) @mkdir(PATH_CACHE.'/Smarty/cache'); diff --git a/applications/dashboard/views/entry/signout.php b/applications/dashboard/views/entry/signout.php index 2cc5b0e45ed..163c780fbb9 100644 --- a/applications/dashboard/views/entry/signout.php +++ b/applications/dashboard/views/entry/signout.php @@ -20,7 +20,7 @@ } }); - Data('Target')).'&override=0', '', array('id' => 'SignoutLink'))); ?> + Data('Target')).'&override='.$this->Data('Override', '0'), '', array('id' => 'SignoutLink'))); ?>

diff --git a/applications/dashboard/views/profile/helper_functions.php b/applications/dashboard/views/profile/helper_functions.php index 9e91079c328..724046d566f 100644 --- a/applications/dashboard/views/profile/helper_functions.php +++ b/applications/dashboard/views/profile/helper_functions.php @@ -11,11 +11,11 @@ function UserVerified($User) { if (GetValue('Verified', $User)) { $Label = T('Verified'); - $Title = T('This user has been verified as a non-spammer.'); + $Title = T('Verified Description', 'Verified users bypass spam and pre-moderation filters.'); $Url = "/user/verify.json?userid=$UserID&verified=0"; } else { $Label = T('Not Verified'); - $Title = T('This user has not been verified as a non-spammer.'); + $Title = T('Not Verified Description', 'Unverified users are passed thru any enabled spam and pre-moderation filters.'); $Url = "/user/verify.json?userid=$UserID&verified=1"; } diff --git a/applications/dashboard/views/settings/locales.php b/applications/dashboard/views/settings/locales.php index e177666a8e7..abf211a367c 100644 --- a/applications/dashboard/views/settings/locales.php +++ b/applications/dashboard/views/settings/locales.php @@ -6,7 +6,7 @@ echo '

', T('Need More Help?'), '

'; echo '
    '; echo '
  • ', Anchor(T('Enabling a Locale Pack'), 'http://vanillaforums.org/docs/Localization#Enabling'), '
  • '; - echo '
  • ', Anchor(T('Internaltionalization & Localization'), 'http://vanillaforums.org/docs/Localization'), '
  • '; + echo '
  • ', Anchor(T('Internationalization & Localization'), 'http://vanillaforums.org/docs/Localization'), '
  • '; echo '
'; ?>
diff --git a/applications/dashboard/views/settings/plugins.php b/applications/dashboard/views/settings/plugins.php index 9b521987801..3ff3914deca 100755 --- a/applications/dashboard/views/settings/plugins.php +++ b/applications/dashboard/views/settings/plugins.php @@ -89,7 +89,7 @@ > 'PluginIcon')); ?> - + TransientKey(), 'RemoveItem SmallButton'); ?>
  • -
    The basic registration form requires that new users copy text from a "Captcha" image to keep spammers out of the site. You need an account at recaptcha.net. Signing up is FREE and easy. Once you have signed up, come back here and enter the following settings:'); ?>
    +
    The basic registration form requires that new users copy text from a "Captcha" image to keep spammers out of the site. You need an account at recaptcha.net. Signing up is FREE and easy. Once you have signed up, come back here and enter the following settings:'); ?>
    diff --git a/applications/dashboard/views/user/edit.php b/applications/dashboard/views/user/edit.php index ad220ed9176..a791329d2a0 100644 --- a/applications/dashboard/views/user/edit.php +++ b/applications/dashboard/views/user/edit.php @@ -51,7 +51,7 @@
  • Form->CheckBox('Verified', T('This user is verified as a non-spammer'), array('value' => '1')); + echo $this->Form->CheckBox('Verified', T('Verified Label', 'Verified. Bypasses spam and pre-moderation filters.'), array('value' => '1')); ?>
  • @@ -63,6 +63,8 @@ $this->FireEvent('CustomUserFields') ?> + + Data('Roles'))) : ?>

    • @@ -73,18 +75,16 @@ ?>
    + +

    • T('Keep current password.'), - 'Auto' => T('Force user to reset their password and send email notification.'), - 'Manual' => T('Manually set user password. No email notification.') - ); - echo $this->Form->RadioList('ResetPassword', $ResetOptions); + echo $this->Form->RadioList('ResetPassword', $this->ResetOptions); ?>
    • + ResetOptions)) : ?>
    • Form->Label('New Password', 'NewPassword'); @@ -97,6 +97,7 @@ ?>
    • +
    InsertUserID != $Session->UserID) + if ($Comment->InsertUserID != $Session->UserID || !C('Vanilla.Comments.AllowSelfDelete')) $this->Permission('Vanilla.Comments.Delete', TRUE, 'Category', $Discussion->PermissionCategoryID); // Make sure that content can (still) be edited diff --git a/applications/vanilla/controllers/class.postcontroller.php b/applications/vanilla/controllers/class.postcontroller.php index 583ff0ddd70..fb9d3789dd5 100755 --- a/applications/vanilla/controllers/class.postcontroller.php +++ b/applications/vanilla/controllers/class.postcontroller.php @@ -297,12 +297,12 @@ public function Discussion($CategoryUrlCode = '') { $this->FireEvent('BeforeDiscussionRender'); if ($this->CategoryID) - $Breacrumbs = CategoryModel::GetAncestors($this->CategoryID); + $Breadcrumbs = CategoryModel::GetAncestors($this->CategoryID); else - $Breacrumbs = array(); - $Breacrumbs[] = array('Name' => $this->Data('Title'), 'Url' => '/post/discussion'); + $Breadcrumbs = array(); + $Breadcrumbs[] = array('Name' => $this->Data('Title'), 'Url' => '/post/discussion'); - $this->SetData('Breadcrumbs', $Breacrumbs); + $this->SetData('Breadcrumbs', $Breadcrumbs); $this->SetData('_AnnounceOptions', $this->AnnounceOptions()); diff --git a/applications/vanilla/models/class.categorymodel.php b/applications/vanilla/models/class.categorymodel.php index 5f751de430a..ff256bdcddb 100755 --- a/applications/vanilla/models/class.categorymodel.php +++ b/applications/vanilla/models/class.categorymodel.php @@ -1562,7 +1562,7 @@ public function ApplyUpdates() { // Insert the root node if ($this->SQL->GetWhere('Category', array('CategoryID' => -1))->NumRows() == 0) - $this->SQL->Insert('Category', array('CategoryID' => -1, 'TreeLeft' => 1, 'TreeRight' => 4, 'Depth' => 0, 'InsertUserID' => 1, 'UpdateUserID' => 1, 'DateInserted' => Gdn_Format::ToDateTime(), 'DateUpdated' => Gdn_Format::ToDateTime(), 'Name' => 'Root', 'UrlCode' => '', 'Description' => 'Root of category tree. Users should never see this.')); + $this->SQL->Insert('Category', array('CategoryID' => -1, 'TreeLeft' => 1, 'TreeRight' => 4, 'Depth' => 0, 'InsertUserID' => 1, 'UpdateUserID' => 1, 'DateInserted' => Gdn_Format::ToDateTime(), 'DateUpdated' => Gdn_Format::ToDateTime(), 'Name' => T('Root Category Name', 'Root'), 'UrlCode' => '', 'Description' => T('Root Category Description', 'Root of category tree. Users should never see this.'))); // Build up the TreeLeft & TreeRight values. $this->RebuildTree(); diff --git a/applications/vanilla/models/class.commentmodel.php b/applications/vanilla/models/class.commentmodel.php index 869d8a8cf0b..b5ad82b8055 100755 --- a/applications/vanilla/models/class.commentmodel.php +++ b/applications/vanilla/models/class.commentmodel.php @@ -798,6 +798,7 @@ public function Save($FormPostValues) { // Log the save. LogModel::LogChange('Edit', 'Comment', array_merge($Fields, array('CommentID' => $CommentID))); // Save the new value. + $this->SerializeRow($Fields); $this->SQL->Put($this->Name, $Fields, array('CommentID' => $CommentID)); } else { // Make sure that the comments get formatted in the method defined by Garden. @@ -819,7 +820,8 @@ public function Save($FormPostValues) { return UNAPPROVED; } - // Create comment + // Create comment. + $this->SerializeRow($Fields); $CommentID = $this->SQL->Insert($this->Name, $Fields); $this->EventArguments['CommentID'] = $CommentID; diff --git a/applications/vanilla/models/class.discussionmodel.php b/applications/vanilla/models/class.discussionmodel.php index a2241f0e8b5..f2131f40f83 100644 --- a/applications/vanilla/models/class.discussionmodel.php +++ b/applications/vanilla/models/class.discussionmodel.php @@ -1466,6 +1466,7 @@ public function Save($FormPostValues) { } // Create discussion + $this->SerializeRow($Fields); $DiscussionID = $this->SQL->Insert($this->Name, $Fields); $Fields['DiscussionID'] = $DiscussionID; diff --git a/applications/vanilla/settings/configuration.php b/applications/vanilla/settings/configuration.php index 1b7726c5202..696d0ddcee4 100644 --- a/applications/vanilla/settings/configuration.php +++ b/applications/vanilla/settings/configuration.php @@ -47,3 +47,5 @@ // Module visibility $Configuration['Vanilla']['Modules']['ShowBookmarkedModule'] = FALSE; +// Allow users to delete their own comments if are still allowed to edit (per timeout). +$Configuration['Vanilla']['Comments']['AllowSelfDelete'] = FALSE; \ No newline at end of file diff --git a/applications/vanilla/settings/structure.php b/applications/vanilla/settings/structure.php index c8ff9cb02e4..4c72ab8f3d0 100755 --- a/applications/vanilla/settings/structure.php +++ b/applications/vanilla/settings/structure.php @@ -50,6 +50,8 @@ ->Column('LastCommentID', 'int', NULL) ->Column('LastDiscussionID', 'int', NULL) ->Column('LastDateInserted', 'datetime', NULL) + ->Column('AllowedDiscussionTypes', 'varchar(255)', NULL) + ->Column('DefaultDiscussionType', 'varchar(10)', NULL) ->Set($Explicit, $Drop); $RootCategoryInserted = FALSE; diff --git a/applications/vanilla/views/categories/all.php b/applications/vanilla/views/categories/all.php index 9fda3e20d83..9cb0775ead3 100755 --- a/applications/vanilla/views/categories/all.php +++ b/applications/vanilla/views/categories/all.php @@ -73,7 +73,7 @@ if ($Category->LastTitle != '') { $CatList .= ''.sprintf( T('Most recent: %1$s by %2$s'), - Anchor(SliceString($Category->LastTitle, 40), $Category->LastUrl), + Anchor(Gdn_Format::Text(SliceString($Category->LastTitle, 40)), $Category->LastUrl), UserAnchor($LastComment) ).'' .''.Gdn_Format::Date($Category->LastDateInserted).''; diff --git a/applications/vanilla/views/discussion/helper_functions.php b/applications/vanilla/views/discussion/helper_functions.php index c4af8d37605..d43d7001b50 100644 --- a/applications/vanilla/views/discussion/helper_functions.php +++ b/applications/vanilla/views/discussion/helper_functions.php @@ -322,8 +322,8 @@ function GetCommentOptions($Comment) { $Options['EditComment'] = array('Label' => T('Edit').' '.$TimeLeft, 'Url' => '/vanilla/post/editcomment/'.$Comment->CommentID, 'EditComment'); // Can the user delete the comment? - // if (($CanEdit && $Session->UserID == $Comment->InsertUserID) || $Session->CheckPermission('Vanilla.Comments.Delete', TRUE, 'Category', $PermissionCategoryID)) - if ($Session->CheckPermission('Vanilla.Comments.Delete', TRUE, 'Category', $PermissionCategoryID)) + $SelfDeleting = ($CanEdit && $Session->UserID == $Comment->InsertUserID && C('Vanilla.Comments.AllowSelfDelete')); + if ($SelfDeleting || $Session->CheckPermission('Vanilla.Comments.Delete', TRUE, 'Category', $PermissionCategoryID)) $Options['DeleteComment'] = array('Label' => T('Delete'), 'Url' => 'vanilla/discussion/deletecomment/'.$Comment->CommentID.'/'.$Session->TransientKey().'/?Target='.urlencode("/discussion/{$Comment->DiscussionID}/x"), 'Class' => 'DeleteComment'); // DEPRECATED (as of 2.1) diff --git a/applications/vanilla/views/discussions/table_functions.php b/applications/vanilla/views/discussions/table_functions.php index ced4fd61f18..84d98ee7e0a 100644 --- a/applications/vanilla/views/discussions/table_functions.php +++ b/applications/vanilla/views/discussions/table_functions.php @@ -57,6 +57,7 @@ function WriteDiscussionRow($Discussion, &$Sender, &$Session, $Alt2) { $LastPageUrl = DiscussionUrl($Discussion, FALSE).'#latest'; ?>
  • + FireEvent('BeforeDiscussionContent'); ?>
    ', '
    ')); ?>
    @@ -68,7 +69,6 @@ function WriteDiscussionRow($Discussion, &$Sender, &$Session, $Alt2) { FireEvent('AfterDiscussionTitle'); diff --git a/index.php b/index.php index 8e8fc4a2403..eabfd485b80 100755 --- a/index.php +++ b/index.php @@ -13,7 +13,7 @@ */ define('APPLICATION', 'Vanilla'); -define('APPLICATION_VERSION', '2.2.3.5'); +define('APPLICATION_VERSION', '2.2.3.11'); // Report and track all errors. diff --git a/js/global.js b/js/global.js index e85775b0d33..1a01960306f 100755 --- a/js/global.js +++ b/js/global.js @@ -19,89 +19,8 @@ window.Vanilla = Vanilla; })(window, jQuery); - // Stuff to fire on document.ready(). jQuery(document).ready(function($) { - if ($.browser.msie) { - $('body').addClass('MSIE'); - } - - var d = new Date() - var hourOffset = -Math.round(d.getTimezoneOffset() / 60); - - // Set the ClientHour if there is an input looking for it. - $('input:hidden[name$=HourOffset]').livequery(function() { - $(this).val(hourOffset); - }); - - // Ajax/Save the ClientHour if it is different from the value in the db. - $('input:hidden[id$=SetHourOffset]').livequery(function() { - if (hourOffset != $(this).val()) { - $.post( - gdn.url('/utility/sethouroffset.json'), - { HourOffset: hourOffset, TransientKey: gdn.definition('TransientKey') } - ); - } - }); - - // Add "checked" class to item rows if checkboxes are checked within. - checkItems = function() { - var container = $(this).parents('.Item'); - if ($(this).attr('checked') == 'checked') - $(container).addClass('Checked'); - else - $(container).removeClass('Checked'); - } - $('.Item :checkbox').each(checkItems); - $('.Item :checkbox').change(checkItems); - - // Hide/Reveal the "forgot your password" form if the ForgotPassword button is clicked. - $(document).delegate('a.ForgotPassword', 'click', function() { - // Make sure we have both forms - if ($('#Form_User_SignIn').length) { - $('.Methods').toggle(); - $('#Form_User_Password').toggle(); - $('#Form_User_SignIn').toggle(); - return false; - } - }); - - // Convert date fields to datepickers - if ($.fn.datepicker) { - $('input.DatePicker').datepicker({ - showOn: "focus", - dateFormat: 'mm/dd/yy' - }); - } - - // Reveal youtube player when preview clicked. - function Youtube($container) { - var $preview = $container.find('.VideoPreview'); - var $player = $container.find('.VideoPlayer'); - - $container.addClass('Open').closest('.ImgExt').addClass('Open'); - - var width = $preview.width(), height = $preview.height(), videoid = $container.attr('id').replace('youtube-', ''); - - - var html = ''; - $player.html(html); - - $preview.hide(); - $player.show(); - - return false; - } - $(document).delegate('.Video.YouTube .VideoPreview', 'click', function(e) { - var $target = $(e.target); - var $container = $target.closest('.Video.YouTube'); - return Youtube($container); - }); - - if ($.fn.autogrow) - $('textarea.Autogrow').livequery(function() { - $(this).autogrow(); - }); $.postParseJson = function(json) { if (json.Data) json.Data = $.base64Decode(json.Data); @@ -1182,6 +1101,190 @@ jQuery(document).ready(function($) { return Math.min.apply({},this) } + if ($.browser.msie) { + $('body').addClass('MSIE'); + } + + var d = new Date() + var hourOffset = -Math.round(d.getTimezoneOffset() / 60); + + // Set the ClientHour if there is an input looking for it. + $('input:hidden[name$=HourOffset]').livequery(function() { + $(this).val(hourOffset); + }); + + // Ajax/Save the ClientHour if it is different from the value in the db. + $('input:hidden[id$=SetHourOffset]').livequery(function() { + if (hourOffset != $(this).val()) { + $.post( + gdn.url('/utility/sethouroffset.json'), + { HourOffset: hourOffset, TransientKey: gdn.definition('TransientKey') } + ); + } + }); + + // Add "checked" class to item rows if checkboxes are checked within. + checkItems = function() { + var container = $(this).parents('.Item'); + if ($(this).attr('checked') == 'checked') + $(container).addClass('Checked'); + else + $(container).removeClass('Checked'); + } + $('.Item :checkbox').each(checkItems); + $('.Item :checkbox').change(checkItems); + + // Hide/Reveal the "forgot your password" form if the ForgotPassword button is clicked. + $(document).delegate('a.ForgotPassword', 'click', function() { + // Make sure we have both forms + if ($('#Form_User_SignIn').length) { + $('.Methods').toggle(); + $('#Form_User_Password').toggle(); + $('#Form_User_SignIn').toggle(); + return false; + } + }); + + // Convert date fields to datepickers + if ($.fn.datepicker) { + $('input.DatePicker').datepicker({ + showOn: "focus", + dateFormat: 'mm/dd/yy' + }); + } + + /** + * Youtube preview revealing + * + */ + + // Reveal youtube player when preview clicked. + function Youtube($container) { + var $preview = $container.find('.VideoPreview'); + var $player = $container.find('.VideoPlayer'); + + $container.addClass('Open').closest('.ImgExt').addClass('Open'); + + var width = $preview.width(), height = $preview.height(), videoid = $container.attr('id').replace('youtube-', ''); + + + var html = ''; + $player.html(html); + + $preview.hide(); + $player.show(); + + return false; + } + $(document).delegate('.Video.YouTube .VideoPreview', 'click', function(e) { + var $target = $(e.target); + var $container = $target.closest('.Video.YouTube'); + return Youtube($container); + }); + + /** + * Twitter card embedding + * + */ + + if ($('div.twitter-card').length) { + // Twitter widgets library + window.twttr = (function (d,s,id) { + var t, js, fjs = d.getElementsByTagName(s)[0]; + if (d.getElementById(id)) return; js=d.createElement(s); js.id=id; + js.src="https://platform.twitter.com/widgets.js"; fjs.parentNode.insertBefore(js, fjs); + return window.twttr || (t = { _e: [], ready: function(f){ t._e.push(f) } }); + }(document, "script", "twitter-wjs")); + + twttr.ready(function(twttr){ + setTimeout(tweets, 300); + }); + } + + function tweets() { + $('div.twitter-card').each(function(i, el){ + var card = $(el); + var tweetUrl = card.attr('data-tweeturl'); + var tweetID = card.attr('data-tweetid'); + var cardref = card.get(0); + + twttr.widgets.createTweet( + tweetID, + cardref, + function(iframe){ card.find('a.tweet-url').remove(); }, + { + conversation: "none" + } + ); + + }); + } + + /** + * GitHub commit embedding + * + */ + +// @tim : 2013-08-24 +// Experiment on hold. +// if ($('div.github-commit').length) { +// // Github embed library +// window.GitHubCommit = (function (d,s,id) { +// var t, js, fjs = d.getElementsByTagName(s)[0]; +// if (d.getElementById(id)) return; js=d.createElement(s); js.id=id; +// js.src=gdn.url('js/library/github.embed.js'); fjs.parentNode.insertBefore(js, fjs); +// return window.GitHubCommit || (t = { _e: [], ready: function(f){ t._e.push(f) } }); +// }(document, "script", "github-embd")); +// +// GitHubCommit.ready(function(GitHubCommit){ +// setTimeout(commits, 300); +// }); +// } +// +// function commits(GitHubCommit) { +// $('div.github-commit').each(function(i, el){ +// var commit = $(el); +// var commiturl = commit.attr('data-commiturl'); +// var commituser = commit.attr('data-commituser'); +// var commitrepo = commit.attr('data-commitrepo'); +// var commithash = commit.attr('data-commithash'); +// console.log(el); +// }); +// } + + /** + * Vine image embedding + * + */ + + // Automatic, requires no JS + + /** + * Pintrest pin embedding + * + */ + + if ($('a.pintrest-pin').length) { + (function(d){ + var f = d.getElementsByTagName('SCRIPT')[0], p = d.createElement('SCRIPT'); + p.type = 'text/javascript'; + p.async = true; + p.src = '//assets.pinterest.com/js/pinit.js'; + f.parentNode.insertBefore(p, f); + }(document)); + } + + /** + * Textarea autogrow + * + */ + + if ($.fn.autogrow) { + $('textarea.Autogrow').livequery(function() { + $(this).autogrow(); + }); + } + }); // Shrink large images to fit into message space, and pop into new window when clicked. diff --git a/library/core/class.controller.php b/library/core/class.controller.php index 25c5aeba7d1..43d9e8962e4 100755 --- a/library/core/class.controller.php +++ b/library/core/class.controller.php @@ -648,7 +648,8 @@ public function DefinitionList() { if (!array_key_exists('ResolvedArgs', $this->_Definitions)) { if (sizeof($this->ReflectArgs) && ( (isset($this->ReflectArgs[0]) && $this->ReflectArgs[0] instanceof Gdn_Pluggable) || - (isset($this->ReflectArgs['Sender']) && $this->ReflectArgs['Sender'] instanceof Gdn_Pluggable) + (isset($this->ReflectArgs['Sender']) && $this->ReflectArgs['Sender'] instanceof Gdn_Pluggable) || + (isset($this->ReflectArgs['sender']) && $this->ReflectArgs['sender'] instanceof Gdn_Pluggable) )) $ReflectArgs = json_encode(array_slice($this->ReflectArgs, 1)); else @@ -1420,7 +1421,9 @@ public function RenderData($Data = NULL) { } // Add schemes to to urls. - $r = array_walk_recursive($Data, array('Gdn_Controller', '_FixUrlScheme'), Gdn::Request()->Scheme()); + if (!C('Garden.AllowSSL') || C('Garden.ForceSSL')) { + $r = array_walk_recursive($Data, array('Gdn_Controller', '_FixUrlScheme'), Gdn::Request()->Scheme()); + } switch ($this->DeliveryMethod()) { case DELIVERY_METHOD_XML: diff --git a/library/core/class.format.php b/library/core/class.format.php index 15bf13e0e40..cca252e12ac 100644 --- a/library/core/class.format.php +++ b/library/core/class.format.php @@ -982,6 +982,10 @@ public static function Links($Mixed) { $Regex, array('Gdn_Format', 'LinksCallback'), $Mixed); + + Gdn::PluginManager()->FireAs('Format')->FireEvent('Links', array( + 'Mixed' => &$Mixed + )); return $Mixed; } @@ -1047,26 +1051,77 @@ protected static function LinksCallback($Matches) { return $Matches[0]; $Url = $Matches[4]; - if ((preg_match('`(?:https?|ftp)://(www\.)?youtube\.com\/watch\?(.*)?v=(?P[^&#]+)([^#]*)(?P#t=(?P
    EOT; + + // Twitter + } elseif (preg_match("`{$TwitterUrlMatch}`", $Url, $Matches) && C('Garden.Format.Twitter', true)) { + $Result = <<