/** * * * @return LightOpenID */ public function getOpenID() { $OpenID = new LightOpenID(); if ($url = Gdn::request()->get('url')) { if (!filter_var($url, FILTER_VALIDATE_URL)) { throw new Gdn_UserException(sprintf(t('ValidateUrl'), 'OpenID'), 400); } // Don't allow open ID on a non-standard scheme. $scheme = parse_url($url, PHP_URL_SCHEME); if (!in_array($scheme, array('http', 'https'))) { throw new Gdn_UserException(sprintf(t('ValidateUrl'), 'OpenID'), 400); } // Don't allow open ID on a non-standard port. $port = parse_url($url, PHP_URL_PORT); if ($port && !in_array($port, array(80, 8080, 443))) { throw new Gdn_UserException(t('OpenID is not allowed on non-standard ports.')); } $OpenID->identity = $url; } $Url = url('/entry/connect/openid', true); $UrlParts = explode('?', $Url); parse_str(val(1, $UrlParts, ''), $Query); $Query = array_merge($Query, arrayTranslate($_GET, array('display', 'Target'))); $OpenID->returnUrl = $UrlParts[0] . '?' . http_build_query($Query); $OpenID->required = array('contact/email', 'namePerson/first', 'namePerson/last', 'pref/language'); $this->EventArguments['OpenID'] = $OpenID; $this->fireEvent('GetOpenID'); return $OpenID; }
/** * Render a breadcrumb trail for the user based on the page they are on. * * @param array $Params * @param object $Smarty * @return string */ function smarty_function_breadcrumbs($Params, &$Smarty) { $Breadcrumbs = $Smarty->Controller->data('Breadcrumbs'); if (!is_array($Breadcrumbs)) { $Breadcrumbs = array(); } $Options = arrayTranslate($Params, array('homeurl' => 'HomeUrl', 'hidelast' => 'HideLast')); return Gdn_Theme::breadcrumbs($Breadcrumbs, val('homelink', $Params, true), $Options); }
/** * Modifies category data before it is returned. * * Adds CountAllDiscussions column to each category representing the sum of * discussions within this category as well as all subcategories. * * @since 2.0.17 * @access public * * @param object $Data SQL result. */ public static function addCategoryColumns($Data) { $Result =& $Data->result(); $Result2 = $Result; foreach ($Result as &$Category) { if (!property_exists($Category, 'CountAllDiscussions')) { $Category->CountAllDiscussions = $Category->CountDiscussions; } if (!property_exists($Category, 'CountAllComments')) { $Category->CountAllComments = $Category->CountComments; } // Calculate the following field. $Following = !((bool) val('Archived', $Category) || (bool) val('Unfollow', $Category)); $Category->Following = $Following; $DateMarkedRead = val('DateMarkedRead', $Category); $UserDateMarkedRead = val('UserDateMarkedRead', $Category); if (!$DateMarkedRead) { $DateMarkedRead = $UserDateMarkedRead; } elseif ($UserDateMarkedRead && Gdn_Format::toTimestamp($UserDateMarkedRead) > Gdn_Format::ToTimeStamp($DateMarkedRead)) { $DateMarkedRead = $UserDateMarkedRead; } // Set appropriate Last* columns. setValue('LastTitle', $Category, val('LastDiscussionTitle', $Category, null)); $LastDateInserted = val('LastDateInserted', $Category, null); if (val('LastCommentUserID', $Category) == null) { setValue('LastCommentUserID', $Category, val('LastDiscussionUserID', $Category, null)); setValue('DateLastComment', $Category, val('DateLastDiscussion', $Category, null)); setValue('LastUserID', $Category, val('LastDiscussionUserID', $Category, null)); $LastDiscussion = arrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name')); setValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, false, '/') . '#latest'); if (is_null($LastDateInserted)) { setValue('LastDateInserted', $Category, val('DateLastDiscussion', $Category, null)); } } else { $LastDiscussion = arrayTranslate($Category, array('LastDiscussionID' => 'DiscussionID', 'CategoryID' => 'CategoryID', 'LastTitle' => 'Name')); setValue('LastUserID', $Category, val('LastCommentUserID', $Category, null)); setValue('LastUrl', $Category, DiscussionUrl($LastDiscussion, false, '/') . '#latest'); if (is_null($LastDateInserted)) { setValue('LastDateInserted', $Category, val('DateLastComment', $Category, null)); } } $LastDateInserted = val('LastDateInserted', $Category, null); if ($DateMarkedRead) { if ($LastDateInserted) { $Category->Read = Gdn_Format::toTimestamp($DateMarkedRead) >= Gdn_Format::toTimestamp($LastDateInserted); } else { $Category->Read = true; } } else { $Category->Read = false; } foreach ($Result2 as $Category2) { if ($Category2->TreeLeft > $Category->TreeLeft && $Category2->TreeRight < $Category->TreeRight) { $Category->CountAllDiscussions += $Category2->CountDiscussions; $Category->CountAllComments += $Category2->CountComments; } } } }
/** * Check an addon's file to extract the addon information out of it. * * @param string $Path The path to the file. * @param bool $ThrowError Whether or not to throw an exception if there is a problem analyzing the addon. * @return array An array of addon information. */ public static function analyzeAddon($Path, $ThrowError = true) { if (!file_exists($Path)) { if ($ThrowError) { throw new Exception("{$Path} not found.", 404); } return false; } $Addon = []; $Result = []; $InfoPaths = array('/settings/about.php', '/default.php', '/class.*.plugin.php', '/about.php', '/definitions.php', '/index.php', 'vanilla2export.php'); // Get the list of potential files to analyze. if (is_dir($Path)) { $Entries = self::getInfoFiles($Path, $InfoPaths); $DeleteEntries = false; } else { $Entries = self::getInfoZip($Path, $InfoPaths, false, $ThrowError); $DeleteEntries = true; } foreach ($Entries as $Entry) { if ($Entry['Name'] == '/index.php') { // This could be the core vanilla package. $Version = self::parseCoreVersion($Entry['Path']); if (!$Version) { continue; } // The application was confirmed. $Addon = array('AddonKey' => 'vanilla', 'AddonTypeID' => ADDON_TYPE_CORE, 'Name' => 'Vanilla', 'Description' => 'Vanilla is an open-source, standards-compliant, multi-lingual, fully extensible discussion forum for the web. Anyone who has web-space that meets the requirements can download and use Vanilla for free!', 'Version' => $Version, 'License' => 'GPLv2', 'Path' => $Entry['Path']); break; } elseif ($Entry['Name'] == 'vanilla2export.php') { // This could be the vanilla porter. $Version = self::parseCoreVersion($Entry['Path']); if (!$Version) { continue; } $Addon = array('AddonKey' => 'porter', 'AddonTypeID' => ADDON_TYPE_CORE, 'Name' => 'Vanilla Porter', 'Description' => 'Drop this script in your existing site and navigate to it in your web browser to export your existing forum data to the Vanilla 2 import format.', 'Version' => $Version, 'License' => 'GPLv2', 'Path' => $Entry['Path']); break; } else { // This could be an addon. $Info = self::parseInfoArray($Entry['Path']); if (!is_array($Info) && count($Info)) { continue; } $Key = key($Info); $Variable = $Info['Variable']; $Info = $Info[$Key]; // Validate the addon. $Name = $Entry['Name']; $Valid = true; if (!val('Name', $Info)) { $Info['Name'] = $Key; } // Validate basic fields. $checkResult = self::checkRequiredFields($Info); if (count($checkResult)) { $Result = array_merge($Result, $checkResult); $Valid = false; } // Validate folder name matches key. if (isset($Entry['Base']) && strcasecmp($Entry['Base'], $Key) != 0 && $Variable != 'ThemeInfo') { $Result[] = "{$Name}: The addon's key is not the same as its folder name."; $Valid = false; } if (!$Valid) { continue; } // The addon is valid. $Addon = array_merge(array('AddonKey' => $Key, 'AddonTypeID' => ''), $Info); switch ($Variable) { case 'ApplicationInfo': $Addon['AddonTypeID'] = ADDON_TYPE_APPLICATION; break; case 'LocaleInfo': $Addon['AddonTypeID'] = ADDON_TYPE_LOCALE; break; case 'PluginInfo': $Addon['AddonTypeID'] = ADDON_TYPE_PLUGIN; break; case 'ThemeInfo': $Addon['AddonTypeID'] = ADDON_TYPE_THEME; break; } } } if ($DeleteEntries) { $FolderPath = substr($Path, 0, -4); Gdn_FileSystem::removeFolder($FolderPath); } // Add the addon requirements. if (!empty($Addon)) { $Requirements = arrayTranslate($Addon, ['RequiredApplications' => 'Applications', 'RequiredPlugins' => 'Plugins', 'RequiredThemes' => 'Themes']); foreach ($Requirements as $Type => $Items) { if (!is_array($Items)) { unset($Requirements[$Type]); } } $Addon['Requirements'] = dbencode($Requirements); $Addon['Checked'] = true; $Addon['Path'] = $Path; $UploadsPath = PATH_UPLOADS . '/'; if (stringBeginsWith($Addon['Path'], $UploadsPath)) { $Addon['File'] = substr($Addon['Path'], strlen($UploadsPath)); } if (is_file($Path)) { $Addon['MD5'] = md5_file($Path); $Addon['FileSize'] = filesize($Path); } } elseif ($ThrowError) { $Msg = implode("\n", $Result); throw new Gdn_UserException($Msg, 400); } else { return false; } return $Addon; }
/** * An intermediate page for jsConnect that checks SSO against and then posts the information to /entry/connect. * * @param EntryController $Sender * @param string $Action A specific action. It can be one of the following: * * - blank: The default action. * - guest: There is no user signed in. * - * @param string $Target The url to redirect to after a successful connect. * @throws /Exception Throws an exception when the jsConnect provider is not found. */ public function entryController_jsConnect_create($Sender, $Action = '', $Target = '') { $Sender->setData('_NoMessages', true); if ($Action) { if ($Action == 'guest') { $Sender->addDefinition('CheckPopup', true); $Target = $Sender->Form->getFormValue('Target', '/'); $Sender->RedirectUrl = $Target; $Sender->render('JsConnect', '', 'plugins/jsconnect'); } else { parse_str($Sender->Form->getFormValue('JsConnect'), $JsData); $Error = val('error', $JsData); $Message = val('message', $JsData); if ($Error === 'timeout' && !$Message) { $Message = t('Your sso timed out.', 'Your sso timed out during the request. Please try again.'); } Gdn::dispatcher()->passData('Exception', $Message ? htmlspecialchars($Message) : htmlspecialchars($Error))->dispatch('home/error'); } } else { $client_id = $Sender->setData('client_id', $Sender->Request->get('client_id', 0)); $Provider = self::getProvider($client_id); if (empty($Provider)) { throw NotFoundException('Provider'); } $Get = arrayTranslate($Sender->Request->get(), ['client_id', 'display']); $Sender->addDefinition('JsAuthenticateUrl', self::connectUrl($Provider, true)); $Sender->addJsFile('jsconnect.js', 'plugins/jsconnect'); $Sender->setData('Title', t('Connecting...')); $Sender->Form->Action = url('/entry/connect/jsconnect?' . http_build_query($Get)); $Sender->Form->addHidden('JsConnect', ''); $Sender->Form->addHidden('Target', $Target); $Sender->MasterView = 'empty'; $Sender->Render('JsConnect', '', 'plugins/jsconnect'); } }
/** * Check an addon's file to extract the addon information out of it. * * @param string $Path The path to the file. * @param bool $Fix Whether or not to fix files that have been zipped incorrectly. * @return array An array of addon information. */ public static function analyzeAddon($Path, $ThrowError = true) { if (!file_exists($Path)) { if ($ThrowError) { throw new Exception("{$Path} not found.", 404); } return false; } $Result = array(); $InfoPaths = array('/settings/about.php', '/default.php', '/class.*.plugin.php', '/about.php', '/definitions.php', '/index.php', 'vanilla2export.php'); // Get the list of potential files to analyze. if (is_dir($Path)) { $Entries = self::_GetInfoFiles($Path, $InfoPaths); } else { $Entries = self::_GetInfoZip($Path, $InfoPaths, false, $ThrowError); $DeleteEntries = true; } foreach ($Entries as $Entry) { if ($Entry['Name'] == '/index.php') { // This could be the core vanilla package. $Version = self::ParseCoreVersion($Entry['Path']); if (!$Version) { continue; } // The application was confirmed. $Addon = array('AddonKey' => 'vanilla', 'AddonTypeID' => ADDON_TYPE_CORE, 'Name' => 'Vanilla', 'Description' => 'Vanilla is an open-source, standards-compliant, multi-lingual, fully extensible discussion forum for the web. Anyone who has web-space that meets the requirements can download and use Vanilla for free!', 'Version' => $Version, 'Path' => $Entry['Path']); break; } elseif ($Entry['Name'] == 'vanilla2export.php') { // This could be the vanilla porter. $Version = self::ParseCoreVersion($Entry['Path']); if (!$Version) { continue; } $Addon = array('AddonKey' => 'porter', 'AddonTypeID' => ADDON_TYPE_CORE, 'Name' => 'Vanilla Porter', 'Description' => 'Drop this script in your existing site and navigate to it in your web browser to export your existing forum data to the Vanilla 2 import format.', 'Version' => $Version, 'Path' => $Entry['Path']); break; } else { // This could be an addon. $Info = self::ParseInfoArray($Entry['Path']); if (!is_array($Info) && count($Info)) { continue; } $Key = key($Info); $Variable = $Info['Variable']; $Info = $Info[$Key]; // Validate the addon. $Name = $Entry['Name']; $Valid = true; if (!val('Name', $Info)) { $Info['Name'] = $Key; } if (!val('Description', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Description')); $Valid = false; } if (!val('Version', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Version')); $Valid = false; } if (isset($Entry['Base']) && strcasecmp($Entry['Base'], $Key) != 0 && $Variable != 'ThemeInfo') { $Result[] = "{$Name}: The addon's key is not the same as its folder name."; $Valid = false; } if (!$Valid) { continue; } // The addon is valid. $Addon = array_merge(array('AddonKey' => $Key, 'AddonTypeID' => ''), $Info); switch ($Variable) { case 'ApplicationInfo': $Addon['AddonTypeID'] = ADDON_TYPE_APPLICATION; break; case 'LocaleInfo': $Addon['AddonTypeID'] = ADDON_TYPE_LOCALE; break; case 'PluginInfo': $Addon['AddonTypeID'] = ADDON_TYPE_PLUGIN; break; case 'ThemeInfo': $Addon['AddonTypeID'] = ADDON_TYPE_THEME; break; } } } if ($DeleteEntries) { $FolderPath = substr($Path, 0, -4); Gdn_FileSystem::RemoveFolder($FolderPath); } // Add the addon requirements. if ($Addon) { $Requirements = arrayTranslate($Addon, array('RequiredApplications' => 'Applications', 'RequiredPlugins' => 'Plugins', 'RequiredThemes' => 'Themes')); foreach ($Requirements as $Type => $Items) { if (!is_array($Items)) { unset($Requirements[$Type]); } } $Addon['Requirements'] = serialize($Requirements); $Addon['Checked'] = true; $Addon['Path'] = $Path; $UploadsPath = PATH_UPLOADS . '/'; if (stringBeginsWith($Addon['Path'], $UploadsPath)) { $Addon['File'] = substr($Addon['Path'], strlen($UploadsPath)); } if (is_file($Path)) { $Addon['MD5'] = md5_file($Path); $Addon['FileSize'] = filesize($Path); } } elseif ($ThrowError) { $Msg = implode("\n", $Result); throw new Gdn_UserException($Msg, 400); } else { return false; } return $Addon; // Figure out what kind of addon this is. $Root = ''; $NewRoot = ''; $Addon = false; foreach ($Entries as $Entry) { $Name = '/' . ltrim($Entry['name'], '/'); $Filename = basename($Name); $Folder = substr($Name, 0, -strlen($Filename)); $NewRoot = ''; // Check to see if the entry is a plugin file. if ($Filename == 'default.php' || StringEndsWith($Filename, '.plugin.php')) { if (count(explode('/', $Folder)) > 3) { // The file is too deep to be a plugin file. continue; } // This could be a plugin file, but we have to examine its info array. $Zip->extractTo($FolderPath, $Entry['name']); $FilePath = CombinePaths(array($FolderPath, $Name)); $Info = self::ParseInfoArray($FilePath, 'PluginInfo'); Gdn_FileSystem::RemoveFolder(dirname($FilePath)); if (!is_array($Info) || !count($Info)) { continue; } // Check to see if the info array conforms to a plugin spec. $Key = key($Info); $Info = $Info[$Key]; $Root = trim($Folder, '/'); $Valid = true; // Make sure the key matches the folder name. if ($Root && strcasecmp($Root, $Key) != 0) { $Result[] = "{$Name}: The plugin's key is not the same as its folder name."; $Valid = false; } else { $NewRoot = $Root; } if (!val('Description', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Description')); $Valid = false; } if (!val('Version', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Version')); $Valid = false; } if ($Valid) { // The plugin was confirmed. $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_PLUGIN, 'Name' => val('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path); break; } continue; } // Check to see if the entry is an application file. if (StringEndsWith($Name, '/settings/about.php')) { if (count(explode('/', $Folder)) > 4) { $Result[] = "{$Name}: The application's info array was not in the correct location."; // The file is too deep to be a plugin file. continue; } // This could be a plugin file, but we have to examine its info array. $Zip->extractTo($FolderPath, $Entry['name']); $FilePath = CombinePaths(array($FolderPath, $Name)); $Info = self::ParseInfoArray($FilePath, 'ApplicationInfo'); Gdn_FileSystem::RemoveFolder(dirname($FilePath)); if (!is_array($Info) || !count($Info)) { $Result[] = "{$Name}: The application's info array could not be parsed."; continue; } $Key = key($Info); $Info = $Info[$Key]; $Root = trim(substr($Name, 0, -strlen('/settings/about.php')), '/'); $Valid = true; // Make sure the key matches the folder name. if ($Root && strcasecmp($Root, $Key) != 0) { $Result[] = "{$Name}: The application's key is not the same as its folder name."; $Valid = false; } else { $NewRoot = $Root; } if (!val('Description', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Description')); $Valid = false; } if (!val('Version', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Version')); $Valid = false; } if ($Valid) { // The application was confirmed. $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_APPLICATION, 'Name' => val('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path); break; } continue; } // Check to see if the entry is a theme file. if (StringEndsWith($Name, '/about.php')) { if (count(explode('/', $Folder)) > 3) { // The file is too deep to be a plugin file. continue; } // This could be a theme file, but we have to examine its info array. $Zip->extractTo($FolderPath, $Entry['name']); $FilePath = CombinePaths(array($FolderPath, $Name)); $Info = self::ParseInfoArray($FilePath, 'ThemeInfo'); Gdn_FileSystem::RemoveFolder(dirname($FilePath)); if (!is_array($Info) || !count($Info)) { continue; } $Key = key($Info); $Info = $Info[$Key]; $Valid = true; $Root = trim(substr($Name, 0, -strlen('/about.php')), '/'); // Make sure the theme is at least one folder deep. if (strlen($Root) == 0) { $Result[] = $Name . ': The theme must be in a folder.'; $Valid = false; } if (!val('Description', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Description')); $Valid = false; } if (!val('Version', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Version')); $Valid = false; } if ($Valid) { // The application was confirmed. $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_THEME, 'Name' => val('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path); break; } } if (StringEndsWith($Name, '/definitions.php')) { if (count(explode('/', $Folder)) > 3) { // The file is too deep to be a plugin file. continue; } // This could be a locale pack, but we have to examine its info array. $Zip->extractTo($FolderPath, $Entry['name']); $FilePath = CombinePaths(array($FolderPath, $Name)); $Info = self::ParseInfoArray($FilePath, 'LocaleInfo'); Gdn_FileSystem::RemoveFolder(dirname($FilePath)); if (!is_array($Info) || !count($Info)) { continue; } $Key = key($Info); $Info = $Info[$Key]; $Valid = true; $Root = trim(substr($Name, 0, -strlen('/definitions.php')), '/'); // Make sure the locale is at least one folder deep. if ($Root != $Key) { $Result[] = $Name . ': The locale pack\'s key must be the same as its folder name.'; $Valid = false; } if (!val('Locale', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Locale')); $Valud = false; } elseif (strcasecmp($Info['Locale'], $Key) == 0) { $Result[] = $Name . ': ' . t('The locale\'s key cannot be the same as the name of the locale.'); $Valid = false; } if (!val('Description', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Description')); $Valid = false; } if (!val('Version', $Info)) { $Result[] = $Name . ': ' . sprintf(t('ValidateRequired'), t('Version')); $Valid = false; } if ($Valid) { // The locale pack was confirmed. $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_LOCALE, 'Name' => val('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path); break; } } // Check to see if the entry is a core file. if (StringEndsWith($Name, '/index.php')) { if (count(explode('/', $Folder)) != 3) { // The file is too deep to be the core's index.php continue; } // This could be a theme file, but we have to examine its info array. $Zip->extractTo($FolderPath, $Entry['name']); $FilePath = CombinePaths(array($FolderPath, $Name)); // Get the version number from the core. $Version = self::ParseCoreVersion($FilePath); if (!$Version) { continue; } // The application was confirmed. $Addon = array('AddonKey' => 'vanilla', 'AddonTypeID' => ADDON_TYPE_CORE, 'Name' => 'Vanilla', 'Description' => 'Vanilla is an open-source, standards-compliant, multi-lingual, fully extensible discussion forum for the web. Anyone who has web-space that meets the requirements can download and use Vanilla for free!', 'Version' => $Version, 'Path' => $Path); $Info = array(); break; } } if ($Addon) { // Add the requirements. $Requirements = arrayTranslate($Info, array('RequiredApplications' => 'Applications', 'RequiredPlugins' => 'Plugins', 'RequiredThemes' => 'Themes')); foreach ($Requirements as $Type => $Items) { if (!is_array($Items)) { unset($Requirements[$Type]); } } $Addon['Requirements'] = serialize($Requirements); $Addon['Checked'] = true; $UploadsPath = PATH_ROOT . '/uploads/'; if (stringBeginsWith($Addon['Path'], $UploadsPath)) { $Addon['File'] = substr($Addon['Path'], strlen($UploadsPath)); } if ($Fix) { // Delete extraneous files. foreach ($Deletes as $Delete) { $Zip->deleteName($Delete['name']); } } } $Zip->close(); if (file_exists($FolderPath)) { Gdn_FileSystem::RemoveFolder($FolderPath); } if ($Addon) { $Addon['MD5'] = md5_file($Path); $Addon['FileSize'] = filesize($Path); return $Addon; } else { if ($ThrowError) { $Msg = implode("\n", $Result); throw new Exception($Msg, 400); } else { return false; } } }
/** * Send welcome email to user. * * @param int $UserID * @param string $Password * @param string $RegisterType * @param array|null $AdditionalData * @throws Exception */ public function sendWelcomeEmail($UserID, $Password, $RegisterType = 'Add', $AdditionalData = null) { $Session = Gdn::session(); $Sender = $this->getID($Session->UserID); $User = $this->getID($UserID); if (!ValidateEmail($User->Email)) { return; } $AppTitle = Gdn::config('Garden.Title'); $Email = new Gdn_Email(); $Email->subject(sprintf(t('[%s] Welcome Aboard!'), $AppTitle)); $Email->to($User->Email); $emailTemplate = $Email->getEmailTemplate(); $Data = []; $Data['User'] = arrayTranslate((array) $User, ['UserID', 'Name', 'Email']); $Data['Sender'] = arrayTranslate((array) $Sender, ['Name', 'Email']); $Data['Title'] = $AppTitle; if (is_array($AdditionalData)) { $Data = array_merge($Data, $AdditionalData); } $Data['EmailKey'] = valr('Attributes.EmailKey', $User); $message = '<p>' . formatString(t('Hello {User.Name}!'), $Data) . ' '; $message .= $this->getEmailWelcome($RegisterType, $User, $Data, $Password); // Add the email confirmation key. if ($Data['EmailKey']) { $emailUrlFormat = '{/entry/emailconfirm,exurl,domain}/{User.UserID,rawurlencode}/{EmailKey,rawurlencode}'; $url = formatString($emailUrlFormat, $Data); $message .= '<p>' . t('You need to confirm your email address before you can continue.') . '</p>'; $emailTemplate->setButton($url, t('Confirm My Email Address')); } else { $emailTemplate->setButton(externalUrl('/'), t('Access the Site')); } $emailTemplate->setMessage($message); $emailTemplate->setTitle(t('Welcome Aboard!')); $Email->setEmailTemplate($emailTemplate); try { $Email->send(); } catch (Exception $e) { if (debug()) { throw $e; } } }
/** * * * @param $Sender * @param string $Dir */ public function entryController_twauthorize_create($Sender, $Dir = '') { $Query = arrayTranslate($Sender->Request->get(), array('display', 'Target')); $Query = http_build_query($Query); if ($Dir == 'profile') { // This is a profile connection. $this->redirectUri(self::profileConnecUrl()); } $this->authorize($Query); }
/** * Get meta data of a single conversation. * * @param int $ConversationID Unique ID of conversation. * @param string $datasetType The format of the resulting conversation. * @param array $options Options to modify the get. Currently supports `viewingUserID`. * @return array|stdClass Returns a conversation. */ public function getID($ConversationID, $datasetType = false, $options = []) { if (is_numeric($datasetType)) { deprecated('ConversationModel->getID(int, int)', 'ConversationModel->getID(int, string, array)'); $viewingUserID = $datasetType; $datasetType = false; } else { $viewingUserID = val('viewingUserID', $options); } $datasetType = $datasetType ?: DATASET_TYPE_OBJECT; // Get the conversation. $Conversation = $this->getWhere(array('ConversationID' => $ConversationID))->firstRow(DATASET_TYPE_ARRAY); if ($viewingUserID) { $Data = $this->SQL->getWhere('UserConversation', array('ConversationID' => $ConversationID, 'UserID' => $viewingUserID))->firstRow(DATASET_TYPE_ARRAY); // Convert the array. $UserConversation = arrayTranslate($Data, array('LastMessageID', 'CountReadMessages', 'DateLastViewed', 'Bookmarked')); $UserConversation['CountNewMessages'] = $Conversation['CountMessages'] - $Data['CountReadMessages']; } else { $UserConversation = array('CountNewMessages' => 0, 'CountReadMessages' => $Conversation['CountMessages'], 'DateLastViewed' => $Conversation['DateUpdated']); } $Conversation = array_merge($Conversation, $UserConversation); if ($datasetType === DATASET_TYPE_OBJECT) { $Conversation = (object) $Conversation; } return $Conversation; }
echo val('Description', $Info, ''); ?> </td> </tr> <tr class="<?php echo $RowClass; ?> "> <td class="Info"><?php echo anchor(t($ToggleText), '/settings/locales/' . strtolower($ToggleText) . '/' . urlencode($Key) . '/' . $Session->TransientKey(), $ToggleText . 'Addon SmallButton'); ?> </td> <td class="Alt Info"><?php $RequiredApplications = val('RequiredApplications', $Info, false); $RequiredPlugins = val('RequiredPlugins', $Info, false); $InfoItems = arrayTranslate($Info, array('Locale' => t('Locale'), 'Version' => t('Version'))); $InfoString = ImplodeAssoc(': ', '<span>|</span>', $InfoItems); // if (is_array($RequiredApplications) || is_array($RequiredPlugins)) { // if ($Info != '') // $Info .= '<span>|</span>'; // // $Info .= t('Requires: '); // } // $i = 0; // if (is_array($RequiredApplications)) { // if ($i > 0) // $Info .= ', '; // // foreach ($RequiredApplications as $RequiredApplication => $VersionInfo) { // $Info .= sprintf(t('%1$s Version %2$s'), $RequiredApplication, $VersionInfo); // ++$i;
/** * Change ban data on a user (ban or unban them). * * @since 2.0.18 * @access public * * @param array $User * @param bool $BannedValue Whether user is banned. * @param array|false $Ban An array representing the specific auto-ban. */ public function saveUser($User, $BannedValue, $Ban = false) { $BannedValue = (bool) $BannedValue; $Banned = $User['Banned']; if (static::isBanned($Banned, self::BAN_AUTOMATIC) === $BannedValue) { return; } $NewBanned = static::setBanned($Banned, $BannedValue, self::BAN_AUTOMATIC); Gdn::userModel()->setField($User['UserID'], 'Banned', $NewBanned); $BanningUserID = Gdn::session()->UserID; // This is true when a session is started and the session user has a new ip address and it matches a banning rule ip address if ($User['UserID'] == $BanningUserID) { $BanningUserID = val('InsertUserID', $Ban, Gdn::userModel()->GetSystemUserID()); } // Add the activity. $ActivityModel = new ActivityModel(); $Activity = array('ActivityType' => 'Ban', 'ActivityUserID' => $User['UserID'], 'RegardingUserID' => $BanningUserID, 'NotifyUserID' => ActivityModel::NOTIFY_MODS); $BannedString = $BannedValue ? 'banned' : 'unbanned'; if ($Ban) { $Activity['HeadlineFormat'] = '{ActivityUserID,user} was ' . $BannedString . ' (based on {Data.BanType}: {Data.BanValue}).'; $Activity['Data'] = arrayTranslate($Ban, array('BanType', 'BanValue')); $Activity['Story'] = $Ban['Notes']; $Activity['RecordType'] = 'Ban'; if (isset($Ban['BanID'])) { $Activity['BanID'] = $Ban['BanID']; } } else { $Activity['HeadlineFormat'] = '{ActivityUserID,user} was ' . $BannedString . '.'; } $ActivityModel->save($Activity); }
/** * Allow the admin to input the keys that their service uses to send data. * * @param array $rawProfile profile as it is returned from the provider. * * @return array Profile array transformed by child class or as is. */ public function translateProfileResults($rawProfile = []) { $provider = $this->provider(); $email = val('ProfileKeyEmail', $provider, 'email'); $translatedKeys = [val('ProfileKeyEmail', $provider, 'email') => 'Email', val('ProfileKeyPhoto', $provider, 'picture') => 'Photo', val('ProfileKeyName', $provider, 'displayname') => 'Name', val('ProfileKeyFullName', $provider, 'name') => 'FullName', val('ProfileKeyUniqueID', $provider, 'user_id') => 'UniqueID']; $profile = arrayTranslate($rawProfile, $translatedKeys, true); $profile['Provider'] = $this->providerKey; return $profile; }
/** * * * @param null $Schema * @throws Exception */ public function initialize($Schema = null) { if ($Schema !== null) { $this->Schema($Schema); } $Form = $this->Form(); if ($Form->authenticatedPostBack()) { // Grab the data from the form. $Data = array(); $Post = $Form->formValues(); foreach ($this->_Schema as $Row) { $Name = $Row['Name']; $Config = $Row['Config']; // For API calls make this a sparse save. if ($this->Controller()->deliveryType() === DELIVERY_TYPE_DATA && !array_key_exists($Name, $Post)) { continue; } if (strtolower(val('Control', $Row)) == 'imageupload') { $Form->SaveImage($Name, arrayTranslate($Row, array('Prefix', 'Size'))); } $Value = $Form->getFormValue($Name); if ($Value == val('Default', $Value, '')) { $Value = ''; } $Data[$Config] = $Value; $this->Controller()->setData($Name, $Value); } // Save it to the config. saveToConfig($Data, array('RemoveEmpty' => true)); $this->_Sender->informMessage(t('Saved')); } else { // Load the form data from the config. $Data = array(); foreach ($this->_Schema as $Row) { $Data[$Row['Name']] = c($Row['Config'], val('Default', $Row, '')); } $Form->setData($Data); $this->Controller()->Data = $Data; } }
/** * * * @param array $Data * @param bool $Preference * @param array $Options * @return array * @throws Exception */ public function save($Data, $Preference = false, $Options = array()) { trace('ActivityModel->save()'); $Activity = $Data; $this->_touch($Activity); if ($Activity['ActivityUserID'] == $Activity['NotifyUserID'] && !val('Force', $Options)) { trace('Skipping activity because it would notify the user of something they did.'); return; // don't notify users of something they did. } // Check the user's preference. if ($Preference) { list($Popup, $Email) = self::notificationPreference($Preference, $Activity['NotifyUserID'], 'both'); if ($Popup && !$Activity['Notified']) { $Activity['Notified'] = self::SENT_PENDING; } if ($Email && !$Activity['Emailed']) { $Activity['Emailed'] = self::SENT_PENDING; } if (!$Activity['Notified'] && !$Activity['Emailed'] && !val('Force', $Options)) { trace("Skipping activity because the user has no preference set."); return; } } $ActivityType = self::getActivityType($Activity['ActivityType']); $ActivityTypeID = val('ActivityTypeID', $ActivityType); if (!$ActivityTypeID) { trace("There is no {$ActivityType} activity type.", TRACE_WARNING); $ActivityType = self::getActivityType('Default'); $ActivityTypeID = val('ActivityTypeID', $ActivityType); } $Activity['ActivityTypeID'] = $ActivityTypeID; $NotificationInc = 0; if ($Activity['NotifyUserID'] > 0 && $Activity['Notified']) { $NotificationInc = 1; } // Check to see if we are sharing this activity with another one. if ($CommentActivityID = val('CommentActivityID', $Activity['Data'])) { $CommentActivity = $this->getID($CommentActivityID); $Activity['Data']['CommentNotifyUserID'] = $CommentActivity['NotifyUserID']; } // Make sure this activity isn't a duplicate. if (val('CheckRecord', $Options)) { // Check to see if this record already notified so we don't notify multiple times. $Where = arrayTranslate($Activity, array('NotifyUserID', 'RecordType', 'RecordID')); $Where['DateUpdated >'] = Gdn_Format::toDateTime(strtotime('-2 days')); // index hint $CheckActivity = $this->SQL->getWhere('Activity', $Where)->firstRow(); if ($CheckActivity) { return false; } } // Check to share the activity. if (val('Share', $Options)) { $this->Share($Activity); } // Group he activity. if ($GroupBy = val('GroupBy', $Options)) { $GroupBy = (array) $GroupBy; $Where = array(); foreach ($GroupBy as $ColumnName) { $Where[$ColumnName] = $Activity[$ColumnName]; } $Where['NotifyUserID'] = $Activity['NotifyUserID']; // Make sure to only group activities by day. $Where['DateInserted >'] = Gdn_Format::toDateTime(strtotime('-1 day')); // See if there is another activity to group these into. $GroupActivity = $this->SQL->getWhere('Activity', $Where)->firstRow(DATASET_TYPE_ARRAY); if ($GroupActivity) { $GroupActivity['Data'] = @unserialize($GroupActivity['Data']); $Activity = $this->mergeActivities($GroupActivity, $Activity); $NotificationInc = 0; } } $Delete = false; if ($Activity['Emailed'] == self::SENT_PENDING) { $this->email($Activity); $Delete = val('_Delete', $Activity); } $ActivityData = $Activity['Data']; if (isset($Activity['Data']) && is_array($Activity['Data'])) { $Activity['Data'] = serialize($Activity['Data']); } $this->defineSchema(); $Activity = $this->filterSchema($Activity); $ActivityID = val('ActivityID', $Activity); if (!$ActivityID) { if (!$Delete) { $this->addInsertFields($Activity); touchValue('DateUpdated', $Activity, $Activity['DateInserted']); $this->EventArguments['Activity'] =& $Activity; $this->EventArguments['ActivityID'] = null; $Handled = false; $this->EventArguments['Handled'] =& $Handled; $this->fireEvent('BeforeSave'); if (count($this->validationResults()) > 0) { return false; } if ($Handled) { // A plugin handled this activity so don't save it. return $Activity; } if (val('CheckSpam', $Options)) { // Check for spam $Spam = SpamModel::isSpam('Activity', $Activity); if ($Spam) { return SPAM; } // Check for approval $ApprovalRequired = CheckRestriction('Vanilla.Approval.Require'); if ($ApprovalRequired && !val('Verified', Gdn::session()->User)) { LogModel::insert('Pending', 'Activity', $Activity); return UNAPPROVED; } } $ActivityID = $this->SQL->insert('Activity', $Activity); $Activity['ActivityID'] = $ActivityID; } } else { $Activity['DateUpdated'] = Gdn_Format::toDateTime(); unset($Activity['ActivityID']); $this->EventArguments['Activity'] =& $Activity; $this->EventArguments['ActivityID'] = $ActivityID; $this->fireEvent('BeforeSave'); if (count($this->validationResults()) > 0) { return false; } $this->SQL->put('Activity', $Activity, array('ActivityID' => $ActivityID)); $Activity['ActivityID'] = $ActivityID; } $Activity['Data'] = $ActivityData; if (isset($CommentActivity)) { $CommentActivity['Data']['SharedActivityID'] = $Activity['ActivityID']; $CommentActivity['Data']['SharedNotifyUserID'] = $Activity['NotifyUserID']; $this->setField($CommentActivity['ActivityID'], 'Data', $CommentActivity['Data']); } if ($NotificationInc > 0) { $CountNotifications = Gdn::userModel()->getID($Activity['NotifyUserID'])->CountNotifications + $NotificationInc; Gdn::userModel()->setField($Activity['NotifyUserID'], 'CountNotifications', $CountNotifications); } // If this is a wall post then we need to notify on that. if (val('Name', $ActivityType) == 'WallPost' && $Activity['NotifyUserID'] == self::NOTIFY_PUBLIC) { $this->notifyWallPost($Activity); } return $Activity; }
/** * Send welcome email to user. * * @param $UserID * @param $Password * @param string $RegisterType * @param null $AdditionalData * @throws Exception */ public function sendWelcomeEmail($UserID, $Password, $RegisterType = 'Add', $AdditionalData = null) { $Session = Gdn::session(); $Sender = $this->getID($Session->UserID); $User = $this->getID($UserID); if (!ValidateEmail($User->Email)) { return; } $AppTitle = Gdn::config('Garden.Title'); $Email = new Gdn_Email(); $Email->subject(sprintf(t('[%s] Welcome Aboard!'), $AppTitle)); $Email->to($User->Email); $emailTemplate = $Email->getEmailTemplate(); $Data = array(); $Data['User'] = arrayTranslate((array) $User, array('UserID', 'Name', 'Email')); $Data['Sender'] = arrayTranslate((array) $Sender, array('Name', 'Email')); $Data['Title'] = $AppTitle; if (is_array($AdditionalData)) { $Data = array_merge($Data, $AdditionalData); } $Data['EmailKey'] = valr('Attributes.EmailKey', $User); $message = '<p>' . formatString(t('Hello {User.Name}!'), $Data) . ' '; switch ($RegisterType) { case 'Connect': $message .= formatString(t('You have successfully connected to {Title}.'), $Data) . ' ' . t('Find your account information below.') . '<br></p>' . '<p>' . sprintf(t('%s: %s'), t('Username'), val('Name', $User)) . '<br>' . formatString(t('Connected With: {ProviderName}'), $Data) . '</p>'; break; case 'Register': $message .= formatString(t('You have successfully registered for an account at {Title}.'), $Data) . ' ' . t('Find your account information below.') . '<br></p>' . '<p>' . sprintf(t('%s: %s'), t('Username'), val('Name', $User)) . '<br>' . sprintf(t('%s: %s'), t('Email'), val('Email', $User)) . '</p>'; break; default: $message .= sprintf(t('%s has created an account for you at %s.'), val('Name', $Sender), $AppTitle) . ' ' . t('Find your account information below.') . '<br></p>' . '<p>' . sprintf(t('%s: %s'), t('Email'), val('Email', $User)) . '<br>' . sprintf(t('%s: %s'), t('Password'), $Password) . '</p>'; } // Add the email confirmation key. if ($Data['EmailKey']) { $emailUrlFormat = '{/entry/emailconfirm,exurl,domain}/{User.UserID,rawurlencode}/{EmailKey,rawurlencode}'; $url = formatString($emailUrlFormat, $Data); $message .= '<p>' . t('You need to confirm your email address before you can continue.') . '</p>'; $emailTemplate->setButton($url, t('Confirm My Email Address')); } else { $emailTemplate->setButton(externalUrl('/'), t('Access the Site')); } $emailTemplate->setMessage($message); $emailTemplate->setTitle(t('Welcome Aboard!')); $Email->setEmailTemplate($emailTemplate); $Email->send(); }
/** * Pre-populate the form with values from the query string. * * @param Gdn_Form $Form * @param bool $LimitCategories Whether to turn off the category dropdown if there is only one category to show. */ protected function populateForm($Form) { $Get = $this->Request->get(); $Get = array_change_key_case($Get); $Values = arrayTranslate($Get, array('name' => 'Name', 'tags' => 'Tags', 'body' => 'Body')); foreach ($Values as $Key => $Value) { $Form->setValue($Key, $Value); } if (isset($Get['category'])) { $Category = CategoryModel::categories($Get['category']); if ($Category && $Category['PermsDiscussionsAdd']) { $Form->setValue('CategoryID', $Category['CategoryID']); } } }
/** * Do password reset. * * @access public * @since 2.0.0 * * @param int $UserID Unique. * @param string $PasswordResetKey Authenticate with unique, 1-time code sent via email. */ public function passwordReset($UserID = '', $PasswordResetKey = '') { $PasswordResetKey = trim($PasswordResetKey); if (!is_numeric($UserID) || $PasswordResetKey == '' || $this->UserModel->getAttribute($UserID, 'PasswordResetKey', '') != $PasswordResetKey) { $this->Form->addError('Failed to authenticate your password reset request. Try using the reset request form again.'); Logger::event('password_reset_failure', Logger::NOTICE, '{username} failed to authenticate password reset request.'); } $Expires = $this->UserModel->getAttribute($UserID, 'PasswordResetExpires'); if ($this->Form->errorCount() === 0 && $Expires < time()) { $this->Form->addError('@' . t('Your password reset token has expired.', 'Your password reset token has expired. Try using the reset request form again.')); Logger::event('password_reset_failure', Logger::NOTICE, '{username} has an expired reset token.'); } if ($this->Form->errorCount() == 0) { $User = $this->UserModel->getID($UserID, DATASET_TYPE_ARRAY); if ($User) { $User = arrayTranslate($User, array('UserID', 'Name', 'Email')); $this->setData('User', $User); } } else { $this->setData('Fatal', true); } if ($this->Form->errorCount() == 0 && $this->Form->isPostBack() === true) { $Password = $this->Form->getFormValue('Password', ''); $Confirm = $this->Form->getFormValue('Confirm', ''); if ($Password == '') { $this->Form->addError('Your new password is invalid'); Logger::event('password_reset_failure', Logger::NOTICE, 'Failed to reset the password for {username}. Password is invalid.'); } elseif ($Password != $Confirm) { $this->Form->addError('Your passwords did not match.'); } Logger::event('password_reset_failure', Logger::NOTICE, 'Failed to reset the password for {username}. Passwords did not match.'); if ($this->Form->errorCount() == 0) { $User = $this->UserModel->passwordReset($UserID, $Password); Logger::event('password_reset', Logger::NOTICE, '{username} has reset their password.'); Gdn::session()->start($User->UserID, true); // $Authenticator = Gdn::authenticator()->AuthenticateWith('password'); // $Authenticator->FetchData($Authenticator, array('Email' => $User->Email, 'Password' => $Password, 'RememberMe' => FALSE)); // $AuthUserID = $Authenticator->Authenticate(); redirect('/'); } } $this->render(); }
/** * Render basic data about user. * * @since 2.0.? * @access public * @param int $UserID Unique ID. */ public function get($UserID = false) { if (!$UserID) { $UserID = Gdn::session()->UserID; } if (($UserID != Gdn::session()->UserID || !Gdn::session()->UserID) && !checkPermission('Garden.Users.Edit')) { throw new Exception(t('You do not have permission to view other profiles.'), 401); } $UserModel = new UserModel(); // Get the user. $User = $UserModel->getID($UserID, DATASET_TYPE_ARRAY); if (!$User) { throw notFoundException('User'); } $PhotoUrl = $User['Photo']; if ($PhotoUrl && strpos($PhotoUrl, '//') == false) { $PhotoUrl = url('/uploads/' . changeBasename($PhotoUrl, 'n%s'), true); } $User['Photo'] = $PhotoUrl; // Remove unwanted fields. $this->Data = arrayTranslate($User, array('UserID', 'Name', 'Email', 'Photo')); $this->render(); }
/** * Send password email. * * @param $UserID * @param $Password * @throws Exception */ public function sendPasswordEmail($UserID, $Password) { $Session = Gdn::session(); $Sender = $this->getID($Session->UserID); $User = $this->getID($UserID); $AppTitle = Gdn::config('Garden.Title'); $Email = new Gdn_Email(); $Email->subject(sprintf(t('[%s] Password Reset'), $AppTitle)); $Email->to($User->Email); $Data = array(); $Data['User'] = arrayTranslate((array) $User, array('Name', 'Email')); $Data['Sender'] = arrayTranslate((array) $Sender, array('Name', 'Email')); $Data['Title'] = $AppTitle; $EmailFormat = t('EmailPassword'); if (strpos($EmailFormat, '{') !== false) { $Message = formatString($EmailFormat, $Data); } else { $Message = sprintf($EmailFormat, $User->Name, $Sender->Name, $AppTitle, ExternalUrl('/'), $Password, $User->Email); } $Message = $this->_addEmailHeaderFooter($Message, $Data); $Email->message($Message); $Email->send(); }
/** * * * @param $Search * @param int $Offset * @param int $Limit * @return array|null * @throws Exception */ public function search($Search, $Offset = 0, $Limit = 20) { // If there are no searches then return an empty array. if (trim($Search) == '') { return array(); } // Figure out the exact search mode. if ($this->ForceSearchMode) { $SearchMode = $this->ForceSearchMode; } else { $SearchMode = strtolower(c('Garden.Search.Mode', 'matchboolean')); } if ($SearchMode == 'matchboolean') { if (strpos($Search, '+') !== false || strpos($Search, '-') !== false) { $SearchMode = 'boolean'; } else { $SearchMode = 'match'; } } else { $this->_SearchMode = $SearchMode; } if ($ForceDatabaseEngine = c('Database.ForceStorageEngine')) { if (strcasecmp($ForceDatabaseEngine, 'myisam') != 0) { $SearchMode = 'like'; } } if (strlen($Search) <= 4) { $SearchMode = 'like'; } $this->_SearchMode = $SearchMode; $this->EventArguments['Search'] = $Search; $this->fireEvent('Search'); if (count($this->_SearchSql) == 0) { return array(); } // Perform the search by unioning all of the sql together. $Sql = $this->SQL->select()->from('_TBL_ s')->orderBy('s.DateInserted', 'desc')->limit($Limit, $Offset)->GetSelect(); $Sql = str_replace($this->Database->DatabasePrefix . '_TBL_', "(\n" . implode("\nunion all\n", $this->_SearchSql) . "\n)", $Sql); $this->fireEvent('AfterBuildSearchQuery'); if ($this->_SearchMode == 'like') { $Search = '%' . $Search . '%'; } foreach ($this->_Parameters as $Key => $Value) { $this->_Parameters[$Key] = $Search; } $Parameters = $this->_Parameters; $this->reset(); $this->SQL->reset(); $Result = $this->Database->query($Sql, $Parameters)->resultArray(); foreach ($Result as $Key => $Value) { if (isset($Value['Summary'])) { $Value['Summary'] = Condense(Gdn_Format::to($Value['Summary'], $Value['Format'])); $Result[$Key] = $Value; } switch ($Value['RecordType']) { case 'Discussion': $Discussion = arrayTranslate($Value, array('PrimaryID' => 'DiscussionID', 'Title' => 'Name', 'CategoryID')); $Result[$Key]['Url'] = DiscussionUrl($Discussion, 1); break; } } return $Result; }
/** * Add a method to the ModerationController to handle merging discussions. * * @param Gdn_Controller $Sender */ public function moderationController_mergeDiscussions_create($Sender) { $Session = Gdn::session(); $Sender->Form = new Gdn_Form(); $Sender->title(t('Merge Discussions')); $DiscussionModel = new DiscussionModel(); $CheckedDiscussions = Gdn::userModel()->getAttribute($Session->User->UserID, 'CheckedDiscussions', array()); if (!is_array($CheckedDiscussions)) { $CheckedDiscussions = array(); } $DiscussionIDs = $CheckedDiscussions; $Sender->setData('DiscussionIDs', $DiscussionIDs); $CountCheckedDiscussions = count($DiscussionIDs); $Sender->setData('CountCheckedDiscussions', $CountCheckedDiscussions); $Discussions = $DiscussionModel->SQL->whereIn('DiscussionID', $DiscussionIDs)->get('Discussion')->resultArray(); $Sender->setData('Discussions', $Discussions); // Make sure none of the selected discussions are ghost redirects. $discussionTypes = array_column($Discussions, 'Type'); if (in_array('redirect', $discussionTypes)) { throw Gdn_UserException('You cannot merge redirects.', 400); } // Perform the merge if ($Sender->Form->authenticatedPostBack()) { // Create a new discussion record $MergeDiscussion = false; $MergeDiscussionID = $Sender->Form->getFormValue('MergeDiscussionID'); foreach ($Discussions as $Discussion) { if ($Discussion['DiscussionID'] == $MergeDiscussionID) { $MergeDiscussion = $Discussion; break; } } $RedirectLink = $Sender->Form->getFormValue('RedirectLink'); if ($MergeDiscussion) { $ErrorCount = 0; // Verify that the user has permission to perform the merge. $Category = CategoryModel::categories($MergeDiscussion['CategoryID']); if ($Category && !$Category['PermsDiscussionsEdit']) { throw permissionException('Vanilla.Discussions.Edit'); } $DiscussionModel->defineSchema(); $MaxNameLength = val('Length', $DiscussionModel->Schema->getField('Name')); // Assign the comments to the new discussion record $DiscussionModel->SQL->update('Comment')->set('DiscussionID', $MergeDiscussionID)->whereIn('DiscussionID', $DiscussionIDs)->put(); $CommentModel = new CommentModel(); foreach ($Discussions as $Discussion) { if ($Discussion['DiscussionID'] == $MergeDiscussionID) { continue; } // Create a comment out of the discussion. $Comment = arrayTranslate($Discussion, array('Body', 'Format', 'DateInserted', 'InsertUserID', 'InsertIPAddress', 'DateUpdated', 'UpdateUserID', 'UpdateIPAddress', 'Attributes', 'Spam', 'Likes', 'Abuse')); $Comment['DiscussionID'] = $MergeDiscussionID; $CommentModel->Validation->results(true); $CommentID = $CommentModel->save($Comment); if ($CommentID) { // Move any attachments (FileUpload plugin awareness) if (class_exists('MediaModel')) { $MediaModel = new MediaModel(); $MediaModel->reassign($Discussion['DiscussionID'], 'discussion', $CommentID, 'comment'); } if ($RedirectLink) { // The discussion needs to be changed to a moved link. $RedirectDiscussion = array('Name' => SliceString(sprintf(t('Merged: %s'), $Discussion['Name']), $MaxNameLength), 'Type' => 'redirect', 'Body' => formatString(t('This discussion has been <a href="{url,html}">merged</a>.'), array('url' => DiscussionUrl($MergeDiscussion))), 'Format' => 'Html'); $DiscussionModel->setField($Discussion['DiscussionID'], $RedirectDiscussion); $CommentModel->updateCommentCount($Discussion['DiscussionID']); $CommentModel->removePageCache($Discussion['DiscussionID']); } else { // Delete discussion that was merged. $DiscussionModel->delete($Discussion['DiscussionID']); } } else { $Sender->informMessage($CommentModel->Validation->resultsText()); $ErrorCount++; } } // Update counts on all affected discussions. $CommentModel->updateCommentCount($MergeDiscussionID); $CommentModel->removePageCache($MergeDiscussionID); // Clear selections Gdn::userModel()->saveAttribute($Session->UserID, 'CheckedDiscussions', false); ModerationController::informCheckedDiscussions($Sender); if ($ErrorCount == 0) { $Sender->jsonTarget('', '', 'Refresh'); } } } $Sender->render('MergeDiscussions', '', 'plugins/SplitMerge'); }
/** * Log an operation into the log table. * * @param string $Operation The operation being performed. This is usually one of: * - Delete: The record has been deleted. * - Edit: The record has been edited. * - Spam: The record has been marked spam. * - Moderate: The record requires moderation. * - Pending: The record needs pre-moderation. * @param string $RecordType The type of record being logged. This usually correspond to the tablename of the record. * @param array $Data The record data. * - If you are logging just one row then pass the row as an array. * - You can pass an additional _New element to tell the logger what the new data is. * @return int The log id. */ public static function insert($Operation, $RecordType, $Data, $Options = array()) { if ($Operation === false) { return; } // Check to see if we are storing two versions of the data. if (($InsertUserID = self::_LogValue($Data, 'Log_InsertUserID')) === null) { $InsertUserID = Gdn::session()->UserID; } if (($InsertIPAddress = self::_LogValue($Data, 'Log_InsertIPAddress')) == null) { $InsertIPAddress = Gdn::request()->IPAddress(); } // Do some known translations for the parent record ID. if (($ParentRecordID = self::_LogValue($Data, 'ParentRecordID')) === null) { switch ($RecordType) { case 'Activity': $ParentRecordID = self::_LogValue($Data, 'CommentActivityID', 'CommentActivityID'); break; case 'Comment': $ParentRecordID = self::_LogValue($Data, 'DiscussionID', 'DiscussionID'); break; } } // Get the row information from the data or determine it based on the type. $LogRow = array('Operation' => $Operation, 'RecordType' => $RecordType, 'RecordID' => self::_LogValue($Data, 'RecordID', $RecordType . 'ID'), 'RecordUserID' => self::_LogValue($Data, 'RecordUserID', 'UpdateUserID', 'InsertUserID'), 'RecordIPAddress' => self::_LogValue($Data, 'RecordIPAddress', 'LastIPAddress', 'InsertIPAddress'), 'RecordDate' => self::_LogValue($Data, 'RecordDate', 'DateUpdated', 'DateInserted'), 'InsertUserID' => $InsertUserID, 'InsertIPAddress' => $InsertIPAddress, 'DateInserted' => Gdn_Format::toDateTime(), 'ParentRecordID' => $ParentRecordID, 'CategoryID' => self::_LogValue($Data, 'CategoryID'), 'OtherUserIDs' => implode(',', val('OtherUserIDs', $Options, array())), 'Data' => serialize($Data)); if ($LogRow['RecordDate'] == null) { $LogRow['RecordDate'] = Gdn_Format::toDateTime(); } $GroupBy = val('GroupBy', $Options); // Make sure we aren't grouping by null values. if (is_array($GroupBy)) { foreach ($GroupBy as $Name) { if (val($Name, $LogRow) === null) { $GroupBy = false; break; } } } if ($GroupBy) { $GroupBy[] = 'Operation'; $GroupBy[] = 'RecordType'; // Check to see if there is a record already logged here. $Where = array_combine($GroupBy, arrayTranslate($LogRow, $GroupBy)); $LogRow2 = Gdn::sql()->getWhere('Log', $Where)->firstRow(DATASET_TYPE_ARRAY); if ($LogRow2) { $LogID = $LogRow2['LogID']; $Set = array(); $Data = array_merge(unserialize($LogRow2['Data']), $Data); $OtherUserIDs = explode(',', $LogRow2['OtherUserIDs']); if (!is_array($OtherUserIDs)) { $OtherUserIDs = array(); } if (!$LogRow2['InsertUserID']) { $Set['InsertUserID'] = $InsertUserID; } elseif ($InsertUserID != $LogRow2['InsertUserID'] && !in_array($InsertUserID, $OtherUserIDs)) { $OtherUserIDs[] = $InsertUserID; } if (array_key_exists('OtherUserIDs', $Options)) { $OtherUserIDs = array_merge($OtherUserIDs, $Options['OtherUserIDs']); $OtherUserIDs = array_unique($OtherUserIDs); $OtherUserIDs = array_diff($OtherUserIDs, array($InsertUserID)); $Count = count($OtherUserIDs) + 1; } else { $Count = (int) $LogRow2['CountGroup'] + 1; } $Set['OtherUserIDs'] = implode(',', $OtherUserIDs); $Set['CountGroup'] = $Count; $Set['Data'] = serialize($Data); $Set['DateUpdated'] = Gdn_Format::toDateTime(); if (self::$_TransactionID > 0) { $Set['TransactionLogID'] = self::$_TransactionID; } elseif (self::$_TransactionID === true) { if ($LogRow2['TransactionLogID']) { self::$_TransactionID = $LogRow2['TransactionLogID']; } else { self::$_TransactionID = $LogID; $Set['TransactionLogID'] = $LogID; } } Gdn::sql()->put('Log', $Set, array('LogID' => $LogID)); } else { $L = self::_Instance(); $L->EventArguments['Log'] =& $LogRow; $L->fireEvent('BeforeInsert'); if (self::$_TransactionID > 0) { $LogRow['TransactionLogID'] = self::$_TransactionID; } $LogID = Gdn::sql()->insert('Log', $LogRow); if (self::$_TransactionID === true) { // A new transaction was started and needs to assigned. self::$_TransactionID = $LogID; Gdn::sql()->put('Log', array('TransactionLogID' => $LogID), array('LogID' => $LogID)); } $L->EventArguments['LogID'] = $LogID; $L->fireEvent('AfterInsert'); } } else { if (self::$_TransactionID > 0) { $LogRow['TransactionLogID'] = self::$_TransactionID; } // Insert the log entry. $L = self::_Instance(); $L->EventArguments['Log'] =& $LogRow; $L->fireEvent('BeforeInsert'); $LogID = Gdn::sql()->insert('Log', $LogRow); if (self::$_TransactionID === true) { // A new transaction was started and needs to assigned. self::$_TransactionID = $LogID; Gdn::sql()->put('Log', array('TransactionLogID' => $LogID), array('LogID' => $LogID)); } $L->EventArguments['LogID'] = $LogID; $L->fireEvent('AfterInsert'); } return $LogID; }
/** * Inserts or updates the discussion via form values. * * Events: BeforeSaveDiscussion, AfterSaveDiscussion. * * @since 2.0.0 * @access public * * @param array $FormPostValues Data sent from the form model. * @param array $Settings Currently unused. * @return int $DiscussionID Unique ID of the discussion. */ public function save($FormPostValues, $Settings = false) { $Session = Gdn::session(); // Define the primary key in this model's table. $this->defineSchema(); // Add & apply any extra validation rules: $this->Validation->applyRule('Body', 'Required'); $this->Validation->addRule('MeAction', 'function:ValidateMeAction'); $this->Validation->applyRule('Body', 'MeAction'); $MaxCommentLength = Gdn::config('Vanilla.Comment.MaxLength'); if (is_numeric($MaxCommentLength) && $MaxCommentLength > 0) { $this->Validation->SetSchemaProperty('Body', 'Length', $MaxCommentLength); $this->Validation->applyRule('Body', 'Length'); } // Validate category permissions. $CategoryID = val('CategoryID', $FormPostValues); if ($CategoryID > 0) { $Category = CategoryModel::categories($CategoryID); if ($Category && !$Session->checkPermission('Vanilla.Discussions.Add', true, 'Category', val('PermissionCategoryID', $Category))) { $this->Validation->addValidationResult('CategoryID', 'You do not have permission to post in this category'); } } // Get the DiscussionID from the form so we know if we are inserting or updating. $DiscussionID = val('DiscussionID', $FormPostValues, ''); // See if there is a source ID. if (val('SourceID', $FormPostValues)) { $DiscussionID = $this->SQL->getWhere('Discussion', arrayTranslate($FormPostValues, array('Source', 'SourceID')))->value('DiscussionID'); if ($DiscussionID) { $FormPostValues['DiscussionID'] = $DiscussionID; } } elseif (val('ForeignID', $FormPostValues)) { $DiscussionID = $this->SQL->getWhere('Discussion', array('ForeignID' => $FormPostValues['ForeignID']))->value('DiscussionID'); if ($DiscussionID) { $FormPostValues['DiscussionID'] = $DiscussionID; } } $Insert = $DiscussionID == '' ? true : false; $this->EventArguments['Insert'] = $Insert; if ($Insert) { unset($FormPostValues['DiscussionID']); // If no categoryid is defined, grab the first available. if (!val('CategoryID', $FormPostValues) && !c('Vanilla.Categories.Use')) { $FormPostValues['CategoryID'] = val('CategoryID', CategoryModel::defaultCategory(), -1); } $this->addInsertFields($FormPostValues); // The UpdateUserID used to be required. Just add it if it still is. if (!$this->Schema->getProperty('UpdateUserID', 'AllowNull', true)) { $FormPostValues['UpdateUserID'] = $FormPostValues['InsertUserID']; } // $FormPostValues['LastCommentUserID'] = $Session->UserID; $FormPostValues['DateLastComment'] = $FormPostValues['DateInserted']; } else { // Add the update fields. $this->addUpdateFields($FormPostValues); } // Set checkbox values to zero if they were unchecked if (val('Announce', $FormPostValues, '') === false) { $FormPostValues['Announce'] = 0; } if (val('Closed', $FormPostValues, '') === false) { $FormPostValues['Closed'] = 0; } if (val('Sink', $FormPostValues, '') === false) { $FormPostValues['Sink'] = 0; } // Prep and fire event $this->EventArguments['FormPostValues'] =& $FormPostValues; $this->EventArguments['DiscussionID'] = $DiscussionID; $this->fireEvent('BeforeSaveDiscussion'); // Validate the form posted values $this->validate($FormPostValues, $Insert); $ValidationResults = $this->validationResults(); // If the body is not required, remove it's validation errors. $BodyRequired = c('Vanilla.DiscussionBody.Required', true); if (!$BodyRequired && array_key_exists('Body', $ValidationResults)) { unset($ValidationResults['Body']); } if (count($ValidationResults) == 0) { // If the post is new and it validates, make sure the user isn't spamming if (!$Insert || !$this->checkForSpam('Discussion')) { // Get all fields on the form that relate to the schema $Fields = $this->Validation->schemaValidationFields(); // Check for spam. $spam = SpamModel::isSpam('Discussion', $Fields); if ($spam) { return SPAM; } // Get DiscussionID if one was sent $DiscussionID = intval(val('DiscussionID', $Fields, 0)); // Remove the primary key from the fields for saving. unset($Fields['DiscussionID']); $StoredCategoryID = false; if ($DiscussionID > 0) { // Updating $Stored = $this->getID($DiscussionID, DATASET_TYPE_OBJECT); // Block Format change if we're forcing the formatter. if (c('Garden.ForceInputFormatter')) { unset($Fields['Format']); } // Clear the cache if necessary. $CacheKeys = array(); if (val('Announce', $Stored) != val('Announce', $Fields)) { $CacheKeys[] = $this->getAnnouncementCacheKey(); $CacheKeys[] = $this->getAnnouncementCacheKey(val('CategoryID', $Stored)); } if (val('CategoryID', $Stored) != val('CategoryID', $Fields)) { $CacheKeys[] = $this->getAnnouncementCacheKey(val('CategoryID', $Fields)); } foreach ($CacheKeys as $CacheKey) { Gdn::cache()->remove($CacheKey); } self::serializeRow($Fields); $this->SQL->put($this->Name, $Fields, array($this->PrimaryKey => $DiscussionID)); setValue('DiscussionID', $Fields, $DiscussionID); LogModel::logChange('Edit', 'Discussion', (array) $Fields, $Stored); if (val('CategoryID', $Stored) != val('CategoryID', $Fields)) { $StoredCategoryID = val('CategoryID', $Stored); } } else { // Inserting. if (!val('Format', $Fields) || c('Garden.ForceInputFormatter')) { $Fields['Format'] = c('Garden.InputFormatter', ''); } if (c('Vanilla.QueueNotifications')) { $Fields['Notified'] = ActivityModel::SENT_PENDING; } // Check for approval $ApprovalRequired = checkRestriction('Vanilla.Approval.Require'); if ($ApprovalRequired && !val('Verified', Gdn::session()->User)) { LogModel::insert('Pending', 'Discussion', $Fields); return UNAPPROVED; } // Create discussion $this->serializeRow($Fields); $DiscussionID = $this->SQL->insert($this->Name, $Fields); $Fields['DiscussionID'] = $DiscussionID; // Update the cache. if ($DiscussionID && Gdn::cache()->activeEnabled()) { $CategoryCache = array('LastDiscussionID' => $DiscussionID, 'LastCommentID' => null, 'LastTitle' => Gdn_Format::text($Fields['Name']), 'LastUserID' => $Fields['InsertUserID'], 'LastDateInserted' => $Fields['DateInserted'], 'LastUrl' => DiscussionUrl($Fields)); CategoryModel::setCache($Fields['CategoryID'], $CategoryCache); // Clear the cache if necessary. if (val('Announce', $Fields)) { Gdn::cache()->remove($this->getAnnouncementCacheKey(val('CategoryID', $Fields))); if (val('Announce', $Fields) == 1) { Gdn::cache()->remove($this->getAnnouncementCacheKey()); } } } // Update the user's discussion count. $InsertUser = Gdn::userModel()->getID($Fields['InsertUserID']); $this->updateUserDiscussionCount($Fields['InsertUserID'], val('CountDiscussions', $InsertUser, 0) > 100); // Mark the user as participated. $this->SQL->replace('UserDiscussion', array('Participated' => 1), array('DiscussionID' => $DiscussionID, 'UserID' => val('InsertUserID', $Fields))); // Assign the new DiscussionID to the comment before saving. $FormPostValues['IsNewDiscussion'] = true; $FormPostValues['DiscussionID'] = $DiscussionID; // Do data prep. $DiscussionName = val('Name', $Fields, ''); $Story = val('Body', $Fields, ''); $NotifiedUsers = array(); $UserModel = Gdn::userModel(); $ActivityModel = new ActivityModel(); if (val('Type', $FormPostValues)) { $Code = 'HeadlineFormat.Discussion.' . $FormPostValues['Type']; } else { $Code = 'HeadlineFormat.Discussion'; } $HeadlineFormat = t($Code, '{ActivityUserID,user} started a new discussion: <a href="{Url,html}">{Data.Name,text}</a>'); $Category = CategoryModel::categories(val('CategoryID', $Fields)); $Activity = array('ActivityType' => 'Discussion', 'ActivityUserID' => $Fields['InsertUserID'], 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Discussion', 'RecordID' => $DiscussionID, 'Route' => DiscussionUrl($Fields), 'Data' => array('Name' => $DiscussionName, 'Category' => val('Name', $Category))); // Allow simple fulltext notifications if (c('Vanilla.Activity.ShowDiscussionBody', false)) { $Activity['Story'] = $Story; } // Notify all of the users that were mentioned in the discussion. $Usernames = getMentions($DiscussionName . ' ' . $Story); // Use our generic Activity for events, not mentions $this->EventArguments['Activity'] = $Activity; // Notify everyone that has advanced notifications. if (!c('Vanilla.QueueNotifications')) { try { $Fields['DiscussionID'] = $DiscussionID; $this->notifyNewDiscussion($Fields, $ActivityModel, $Activity); } catch (Exception $Ex) { throw $Ex; } } // Notifications for mentions foreach ($Usernames as $Username) { $User = $UserModel->getByUsername($Username); if (!$User) { continue; } // Check user can still see the discussion. if (!$this->canView($Fields, $User->UserID)) { continue; } $Activity['HeadlineFormat'] = t('HeadlineFormat.Mention', '{ActivityUserID,user} mentioned you in <a href="{Url,html}">{Data.Name,text}</a>'); $Activity['NotifyUserID'] = val('UserID', $User); $ActivityModel->queue($Activity, 'Mention'); } // Throw an event for users to add their own events. $this->EventArguments['Discussion'] = $Fields; $this->EventArguments['NotifiedUsers'] = $NotifiedUsers; $this->EventArguments['MentionedUsers'] = $Usernames; $this->EventArguments['ActivityModel'] = $ActivityModel; $this->fireEvent('BeforeNotification'); // Send all notifications. $ActivityModel->saveQueue(); } // Get CategoryID of this discussion $Discussion = $this->getID($DiscussionID, DATASET_TYPE_OBJECT); $CategoryID = val('CategoryID', $Discussion, false); // Update discussion counter for affected categories. if ($Insert || $StoredCategoryID) { $this->incrementNewDiscussion($Discussion); } if ($StoredCategoryID) { $this->updateDiscussionCount($StoredCategoryID); } // Fire an event that the discussion was saved. $this->EventArguments['FormPostValues'] = $FormPostValues; $this->EventArguments['Fields'] = $Fields; $this->EventArguments['DiscussionID'] = $DiscussionID; $this->fireEvent('AfterSaveDiscussion'); } } return $DiscussionID; }
/** * Do code checks on an uploaded addon. * * @param int $AddonID Addon to check. * @param bool|false $SaveVersionID Whether to save the version id. * @throws Exception Addon not found. */ public function check($AddonID, $SaveVersionID = false) { $this->permission('Addons.Addon.Manage'); if ($SaveVersionID !== false) { // Get the version data. $Version = $this->AddonModel->SQL->getWhere('AddonVersion', array('AddonVersionID' => $SaveVersionID))->firstRow(DATASET_TYPE_ARRAY); $this->AddonModel->save($Version); $this->Form->setValidationResults($this->AddonModel->validationResults()); } $Addon = $this->AddonModel->getID($AddonID, false, ['GetVersions' => true]); $AddonTypes = Gdn::sql()->get('AddonType')->resultArray(); $AddonTypes = Gdn_DataSet::index($AddonTypes, 'AddonTypeID'); if (!$Addon) { throw notFoundException('Addon'); } // Get the data for the most recent version of the addon. $upload = new Gdn_Upload(); // Also used per version below. $Path = $upload->copyLocal($Addon['File']); $AddonData = arrayTranslate((array) $Addon, array('AddonID', 'AddonKey', 'Name', 'Type', 'Description', 'Requirements', 'Checked')); try { $FileAddonData = UpdateModel::analyzeAddon($Path); if ($FileAddonData) { $AddonData = array_merge($AddonData, arrayTranslate($FileAddonData, array('AddonKey' => 'File_AddonKey', 'Name' => 'File_Name', 'File_Type', 'Description' => 'File_Description', 'Requirements' => 'File_Requirements', 'Checked' => 'File_Checked'))); $AddonData['File_Type'] = valr($FileAddonData['AddonTypeID'] . '.Label', $AddonTypes, 'Unknown'); } $upload->delete($Path); } catch (Exception $Ex) { $AddonData['File_Error'] = $Ex->getMessage(); } $this->setData('Addon', $AddonData); // Go through the versions and make sure we get the versions to check out. $Versions = array(); foreach ($Addon['Versions'] as $Version) { $Version = $Version; $Path = $upload->copyLocal($Version['File']); try { $VersionData = arrayTranslate((array) $Version, array('AddonVersionID', 'Version', 'AddonKey', 'Name', 'MD5', 'FileSize', 'Checked')); $FileVersionData = UpdateModel::analyzeAddon($Path); $FileVersionData = arrayTranslate($FileVersionData, array('Version' => 'File_Version', 'AddonKey' => 'File_AddonKey', 'Name' => 'File_Name', 'MD5' => 'File_MD5', 'FileSize' => 'File_FileSize', 'Checked' => 'File_Checked')); $upload->delete($Path); } catch (Exception $Ex) { $FileVersionData = array('File_Error' => $Ex->getMessage()); } $Versions[] = array_merge($VersionData, $FileVersionData); } $this->setData('Versions', $Versions); $this->addModule('AddonHelpModule'); $this->render(); }
/** * * * @throws Exception * @throws Gdn_UserException */ public function save() { $this->permission('Garden.Users.Edit'); if (!Gdn::request()->isAuthenticatedPostBack()) { throw new Exception('Requires POST', 405); } $Form = new Gdn_Form(); if ($SSOString = $Form->getFormValue('SSOString')) { $Parts = explode(' ', $SSOString); $String = $Parts[0]; $Data = json_decode(base64_decode($String), true); $User = arrayTranslate($Data, array('name' => 'Name', 'email' => 'Email', 'photourl' => 'Photo', 'client_id' => 'ClientID', 'uniqueid' => 'UniqueID')); } else { $User = $Form->formValues(); } if (!isset($User['UserID']) && isset($User['UniqueID'])) { // Try and find the user based on SSO. $Auth = Gdn::userModel()->getAuthentication($User['UniqueID'], $User['ClientID']); if ($Auth) { $User['UserID'] = $Auth['UserID']; } } if (!isset($User['UserID'])) { // Add some default values to make saving easier. if (!isset($User['RoleID'])) { $DefaultRoles = RoleModel::getDefaultRoles(RoleModel::TYPE_MEMBER); $User['RoleID'] = $DefaultRoles; } elseif (is_numeric($User['RoleID'])) { // UserModel->save() demands an array for RoleID. $User['RoleID'] = array($User['RoleID']); } if (!isset($User['Password'])) { $User['Password'] = md5(microtime()); $User['HashMethod'] = 'Random'; } } $UserID = Gdn::userModel()->save($User, array('SaveRoles' => isset($User['RoleID']), 'NoConfirmEmail' => true)); if ($UserID) { if (!isset($User['UserID'])) { $User['UserID'] = $UserID; } if (isset($User['ClientID']) && isset($User['UniqueID'])) { Gdn::userModel()->saveAuthentication(array('UserID' => $User['UserID'], 'Provider' => $User['ClientID'], 'UniqueID' => $User['UniqueID'])); } $this->setData('User', $User); } else { throw new Gdn_UserException(Gdn::userModel()->Validation->resultsText()); } $this->render('Blank', 'Utility'); }
/** * Get meta data of a single conversation. * * @since 2.0.0 * @access public * * @param int $ConversationID Unique ID of conversation. * @param int $ViewingUserID Unique ID of current user. * @return Gdn_DataSet SQL result (single row). */ public function getID($ConversationID, $ViewingUserID = false) { // Get the conversation. $Conversation = $this->getWhere(array('ConversationID' => $ConversationID))->firstRow(DATASET_TYPE_ARRAY); if ($ViewingUserID) { $Data = $this->SQL->getWhere('UserConversation', array('ConversationID' => $ConversationID, 'UserID' => $ViewingUserID))->firstRow(DATASET_TYPE_ARRAY); // Convert the array. $UserConversation = arrayTranslate($Data, array('LastMessageID', 'CountReadMessages', 'DateLastViewed', 'Bookmarked')); $UserConversation['CountNewMessages'] = $Conversation['CountMessages'] - $Data['CountReadMessages']; } else { $UserConversation = array('CountNewMessages' => 0, 'CountReadMessages' => $Conversation['CountMessages'], 'DateLastViewed' => $Conversation['DateUpdated']); } $Conversation = array_merge($Conversation, $UserConversation); return (object) $Conversation; }