public function pluginController_quoteMention_create($sender, $discussionID, $commentID, $username) { $sender->deliveryMethod(DELIVERY_METHOD_JSON); $user = Gdn::userModel()->getByUsername($username); $discussionModel = new DiscussionModel(); $discussion = $discussionModel->getID($discussionID); if (!$user || !$discussion) { throw notFoundException(); } // Make sure this endpoint can't be used to snoop around. $sender->permission('Vanilla.Discussions.View', true, 'Category', $discussion->PermissionCategoryID); // Find the previous comment of the mentioned user in this discussion. $item = Gdn::sql()->getWhere('Comment', ['DiscussionID' => $discussion->DiscussionID, 'InsertUserID' => $user->UserID, 'CommentID <' => $commentID], 'CommentID', 'desc', 1)->firstRow(); // The items ID in the DOM used for highlighting. if ($item) { $target = '#Comment_' . $item->CommentID; // The mentioned user might be the discussion creator. } elseif ($discussion->InsertUserID == $user->UserID) { $item = $discussion; $target = '#Discussion_' . $item->DiscussionID; } if (!$item) { // A success response code always means that a comment was found. $sender->statusCode(404); } $sender->renderData($item ? ['html' => nl2br(sliceString(Gdn_Format::plainText($item->Body, $item->Format), c('QuoteMention.MaxLength', 400))), 'target' => $target] : []); }
/** * Application management screen. * * @since 2.0.0 * @access public * @param string $Filter 'enabled', 'disabled', or 'all' (default) * @param string $ApplicationName Unique ID of app to be modified. * @param string $TransientKey Security token. */ public function applications($Filter = '', $ApplicationName = '') { $this->permission('Garden.Settings.Manage'); // Page setup $this->addJsFile('addons.js'); $this->addJsFile('applications.js'); $this->title(t('Applications')); $this->setHighlightRoute('dashboard/settings/applications'); if (!in_array($Filter, array('enabled', 'disabled'))) { $Filter = 'all'; } $this->Filter = $Filter; $ApplicationManager = Gdn::applicationManager(); $this->AvailableApplications = $ApplicationManager->availableVisibleApplications(); $this->EnabledApplications = $ApplicationManager->enabledVisibleApplications(); if ($ApplicationName != '') { $addon = Gdn::addonManager()->lookupAddon($ApplicationName); if (!$addon) { throw notFoundException('Application'); } if (Gdn::addonManager()->isEnabled($ApplicationName, Addon::TYPE_ADDON)) { $this->disableApplication($ApplicationName, $Filter); } else { $this->enableApplication($ApplicationName, $Filter); } } else { $this->render(); } }
/** * Creates and renders an instance of a module. * * @param string $Module * @param string $AppFolder * @param string $DeliveryType * @throws NotFoundException */ public function index($Module, $AppFolder = '', $DeliveryType = '') { if (!$DeliveryType) { $this->deliveryType(DELIVERY_TYPE_VIEW); } $ModuleClassExists = class_exists($Module); if ($ModuleClassExists) { // Make sure that the class implements Gdn_IModule $ReflectionClass = new ReflectionClass($Module); if ($ReflectionClass->implementsInterface("Gdn_IModule")) { // Check any incoming app folder against real application list. $appWhitelist = Gdn::applicationManager()->enabledApplicationFolders(); // Set the proper application folder on this controller so that things render properly. if ($AppFolder && in_array($AppFolder, $appWhitelist)) { $this->ApplicationFolder = $AppFolder; } else { $Filename = str_replace('\\', '/', substr($ReflectionClass->getFileName(), strlen(PATH_ROOT))); // Figure our the application folder for the module. $Parts = explode('/', trim($Filename, '/')); if ($Parts[0] == 'applications' && in_array($Parts[1], $appWhitelist)) { $this->ApplicationFolder = $Parts[1]; } } $ModuleInstance = new $Module($this); $ModuleInstance->Visible = true; $WhiteList = array('Limit', 'Help'); foreach ($this->Request->get() as $Key => $Value) { if (in_array($Key, $WhiteList)) { // Set a sane max limit for this open-ended way of calling modules. if ($Key == 'Limit' && $Value > 200) { throw new Exception(t('Invalid limit.'), 400); } $ModuleInstance->{$Key} = $Value; } } $this->setData('_Module', $ModuleInstance); $this->render('Index', false, 'dashboard'); return; } } throw notFoundException(htmlspecialchars($Module)); }
/** * Creates and renders an instance of a module. * * @param string $Module * @param string $AppFolder * @param string $DeliveryType * @throws NotFoundException */ public function index($Module, $AppFolder = '', $DeliveryType = '') { if (!$DeliveryType) { $this->deliveryType(DELIVERY_TYPE_VIEW); } $ModuleClassExists = class_exists($Module); if ($ModuleClassExists) { // Make sure that the class implements Gdn_IModule $ReflectionClass = new ReflectionClass($Module); if ($ReflectionClass->implementsInterface("Gdn_IModule")) { // Set the proper application folder on this controller so that things render properly. if ($AppFolder) { $this->ApplicationFolder = $AppFolder; } else { $Filename = str_replace('\\', '/', substr($ReflectionClass->getFileName(), strlen(PATH_ROOT))); // Figure our the application folder for the module. $Parts = explode('/', trim($Filename, '/')); if ($Parts[0] == 'applications') { $this->ApplicationFolder = $Parts[1]; } } $ModuleInstance = new $Module($this); $ModuleInstance->Visible = true; $WhiteList = array('Limit', 'Help'); foreach ($this->Request->get() as $Key => $Value) { if (in_array($Key, $WhiteList)) { $ModuleInstance->{$Key} = $Value; } } $this->setData('_Module', $ModuleInstance); $this->render('Index', false, 'dashboard'); return; } } throw notFoundException($Module); }
/** * * * @param $Log * @param bool $DeleteLog * @throws Exception * @throws Gdn_UserException */ public function restore($Log, $DeleteLog = true) { static $Columns = array(); if (is_numeric($Log)) { // Grab the log. $LogID = $Log; $Log = $this->getWhere(array('LogID' => $LogID)); if (!$Log) { throw notFoundException('Log'); } $Log = array_pop($Log); } // decho($Log, 'Log'); $this->_RestoreOne($Log, $DeleteLog); // Check for a transaction. if ($TransactionID = $Log['TransactionLogID']) { $Logs = $this->getWhere(array('TransactionLogID' => $TransactionID), '', 'asc', 0, 200); foreach ($Logs as $LogRow) { if ($LogRow['LogID'] == $Log['LogID']) { continue; } $this->_RestoreOne($LogRow, $DeleteLog); } } // Check for child data. if (isset($Log['Data']['_Data'])) { $Data = $Log['Data']['_Data']; foreach ($Data as $RecordType => $Rows) { foreach ($Rows as $Row) { $LogRow = array_merge($Log, array('RecordType' => $RecordType, 'Data' => $Row)); if ($RecordType == 'Comment') { $LogRow['ParentRecordID'] = $Row['DiscussionID']; } $this->_RestoreOne($LogRow, false); } } } // die(); }
/** * Test a theme for dependencies and parse errors. * * @param string $themeName The case-sensitive theme name. * @return bool Returns * @throws Gdn_UserException Throws an exception when there was an issue testing the theme. */ public function testTheme($themeName) { $addon = $this->addonManager->lookupTheme($themeName); if (!$addon) { throw notFoundException('Plugin'); } try { $this->addonManager->checkRequirements($addon, true); $addon->test(true); } catch (\Exception $ex) { throw new Gdn_UserException($ex->getMessage(), $ex->getCode()); } return true; }
/** * Turn on a social plugin. * * @param $Plugin * @throws Exception * @throws Gdn_UserException */ public function enable($Plugin) { $this->permission('Garden.Settings.Manage'); $Connections = $this->GetConnections(); if (!array_key_exists($Plugin, $Connections)) { throw notFoundException('SocialConnect Plugin'); } Gdn::pluginManager()->EnablePlugin($Plugin, null); $Connections = $this->GetConnections(); $Connection = val($Plugin, $Connections); require_once $this->fetchViewLocation('connection_functions'); ob_start(); WriteConnection($Connection); $Row = ob_get_clean(); $this->jsonTarget("#Provider_{$Connection['Index']}", $Row); $this->informMessage(t("Plugin enabled.")); unset($this->Data['Connections']); $this->render('blank', 'utility'); }
/** * Show all discussions in a particular category. * * @since 2.0.0 * @access public * * @param string $CategoryIdentifier Unique category slug or ID. * @param int $Offset Number of discussions to skip. */ public function index($CategoryIdentifier = '', $Page = '0') { // Figure out which category layout to choose (Defined on "Homepage" settings page). $Layout = c('Vanilla.Categories.Layout'); if ($CategoryIdentifier == '') { switch ($Layout) { case 'mixed': $this->View = 'discussions'; $this->Discussions(); break; case 'table': $this->table(); break; default: $this->View = 'all'; $this->All(); break; } return; } else { $Category = CategoryModel::categories($CategoryIdentifier); if (empty($Category)) { // Try lowercasing before outright failing $LowerCategoryIdentifier = strtolower($CategoryIdentifier); if ($LowerCategoryIdentifier != $CategoryIdentifier) { $Category = CategoryModel::categories($LowerCategoryIdentifier); if ($Category) { redirect("/categories/{$LowerCategoryIdentifier}", 301); } } throw notFoundException(); } $Category = (object) $Category; Gdn_Theme::section($Category->CssClass); // Load the breadcrumbs. $this->setData('Breadcrumbs', CategoryModel::GetAncestors(val('CategoryID', $Category))); $this->setData('Category', $Category, true); $this->title(htmlspecialchars(val('Name', $Category, ''))); $this->Description(val('Description', $Category), true); if ($Category->DisplayAs == 'Categories') { if (val('Depth', $Category) > c('Vanilla.Categories.NavDepth', 0)) { // Headings don't make sense if we've cascaded down one level. saveToConfig('Vanilla.Categories.DoHeadings', false, false); } trace($this->deliveryMethod(), 'delivery method'); trace($this->deliveryType(), 'delivery type'); trace($this->SyndicationMethod, 'syndication'); if ($this->SyndicationMethod != SYNDICATION_NONE) { // RSS can't show a category list so just tell it to expand all categories. saveToConfig('Vanilla.ExpandCategories', true, false); } else { // This category is an overview style category and displays as a category list. switch ($Layout) { case 'mixed': $this->View = 'discussions'; $this->Discussions($CategoryIdentifier); break; case 'table': $this->table($CategoryIdentifier); break; default: $this->View = 'all'; $this->All($CategoryIdentifier); break; } return; } } Gdn_Theme::section('DiscussionList'); // Figure out which discussions layout to choose (Defined on "Homepage" settings page). $Layout = c('Vanilla.Discussions.Layout'); switch ($Layout) { case 'table': if ($this->SyndicationMethod == SYNDICATION_NONE) { $this->View = 'table'; } break; default: // $this->View = 'index'; break; } // Load the subtree. $Categories = CategoryModel::GetSubtree($CategoryIdentifier, false); $this->setData('Categories', $Categories); // Setup head $this->Menu->highlightRoute('/discussions'); if ($this->Head) { $this->addJsFile('discussions.js'); $this->Head->AddRss($this->SelfUrl . '/feed.rss', $this->Head->title()); } // Set CategoryID $CategoryID = val('CategoryID', $Category); $this->setData('CategoryID', $CategoryID, true); // Add modules $this->addModule('NewDiscussionModule'); $this->addModule('DiscussionFilterModule'); $this->addModule('CategoriesModule'); $this->addModule('BookmarkedModule'); // Get a DiscussionModel $DiscussionModel = new DiscussionModel(); $CategoryIDs = array($CategoryID); if (c('Vanilla.ExpandCategories')) { $CategoryIDs = array_merge($CategoryIDs, array_column($this->data('Categories'), 'CategoryID')); } $Wheres = array('d.CategoryID' => $CategoryIDs); $this->setData('_ShowCategoryLink', count($CategoryIDs) > 1); // Check permission $this->permission('Vanilla.Discussions.View', true, 'Category', val('PermissionCategoryID', $Category)); // Set discussion meta data. $this->EventArguments['PerPage'] = c('Vanilla.Discussions.PerPage', 30); $this->fireEvent('BeforeGetDiscussions'); list($Offset, $Limit) = offsetLimit($Page, $this->EventArguments['PerPage']); if (!is_numeric($Offset) || $Offset < 0) { $Offset = 0; } $Page = PageNumber($Offset, $Limit); // Allow page manipulation $this->EventArguments['Page'] =& $Page; $this->EventArguments['Offset'] =& $Offset; $this->EventArguments['Limit'] =& $Limit; $this->fireEvent('AfterPageCalculation'); // We want to limit the number of pages on large databases because requesting a super-high page can kill the db. $MaxPages = c('Vanilla.Categories.MaxPages'); if ($MaxPages && $Page > $MaxPages) { throw notFoundException(); } $CountDiscussions = $DiscussionModel->getCount($Wheres); if ($MaxPages && $MaxPages * $Limit < $CountDiscussions) { $CountDiscussions = $MaxPages * $Limit; } $this->setData('CountDiscussions', $CountDiscussions); $this->setData('_Limit', $Limit); // We don't wan't child categories in announcements. $Wheres['d.CategoryID'] = $CategoryID; $AnnounceData = $Offset == 0 ? $DiscussionModel->GetAnnouncements($Wheres) : new Gdn_DataSet(); $this->setData('AnnounceData', $AnnounceData, true); $Wheres['d.CategoryID'] = $CategoryIDs; $this->DiscussionData = $this->setData('Discussions', $DiscussionModel->getWhere($Wheres, $Offset, $Limit)); // Build a pager $PagerFactory = new Gdn_PagerFactory(); $this->EventArguments['PagerType'] = 'Pager'; $this->fireEvent('BeforeBuildPager'); $this->Pager = $PagerFactory->GetPager($this->EventArguments['PagerType'], $this); $this->Pager->ClientID = 'Pager'; $this->Pager->configure($Offset, $Limit, $CountDiscussions, array('CategoryUrl')); $this->Pager->Record = $Category; PagerModule::Current($this->Pager); $this->setData('_Page', $Page); $this->setData('_Limit', $Limit); $this->fireEvent('AfterBuildPager'); // Set the canonical Url. $this->canonicalUrl(CategoryUrl($Category, PageNumber($Offset, $Limit))); // Change the controller name so that it knows to grab the discussion views $this->ControllerName = 'DiscussionsController'; // Pick up the discussions class $this->CssClass = 'Discussions Category-' . GetValue('UrlCode', $Category); // Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); $this->View = 'discussions'; } // Render default view. $this->fireEvent('BeforeCategoriesRender'); $this->render(); } }
/** * Editing a category. * * @since 2.0.0 * @access public * * @param int $CategoryID Unique ID of the category to be updated. */ public function editCategory($CategoryID = '') { // Check permission $this->permission('Garden.Community.Manage'); // Set up models $RoleModel = new RoleModel(); $PermissionModel = Gdn::permissionModel(); $this->Form->setModel($this->CategoryModel); if (!$CategoryID && $this->Form->authenticatedPostBack()) { if ($ID = $this->Form->getFormValue('CategoryID')) { $CategoryID = $ID; } } // Get category data $this->Category = $this->CategoryModel->getID($CategoryID); if (!$this->Category) { throw notFoundException('Category'); } $this->Category->CustomPermissions = $this->Category->CategoryID == $this->Category->PermissionCategoryID; // Set up head $this->addJsFile('jquery.alphanumeric.js'); $this->addJsFile('categories.js'); $this->addJsFile('jquery.gardencheckboxgrid.js'); $this->title(t('Edit Category')); $this->addSideMenu('vanilla/settings/managecategories'); // Make sure the form knows which item we are editing. $this->Form->addHidden('CategoryID', $CategoryID); $this->setData('CategoryID', $CategoryID); // Load all roles with editable permissions $this->RoleArray = $RoleModel->getArray(); $this->fireEvent('AddEditCategory'); if ($this->Form->authenticatedPostBack()) { $this->setupDiscussionTypes($this->Category); $Upload = new Gdn_Upload(); $TmpImage = $Upload->validateUpload('PhotoUpload', false); if ($TmpImage) { // Generate the target image name $TargetImage = $Upload->generateTargetName(PATH_UPLOADS); $ImageBaseName = pathinfo($TargetImage, PATHINFO_BASENAME); // Save the uploaded image $Parts = $Upload->saveAs($TmpImage, $ImageBaseName); $this->Form->setFormValue('Photo', $Parts['SaveName']); } $this->Form->setFormValue('CustomPoints', (bool) $this->Form->getFormValue('CustomPoints')); if ($this->Form->save()) { $Category = CategoryModel::categories($CategoryID); $this->setData('Category', $Category); if ($this->deliveryType() == DELIVERY_TYPE_ALL) { redirect('vanilla/settings/managecategories'); } } } else { $this->Form->setData($this->Category); $this->setupDiscussionTypes($this->Category); $this->Form->setValue('CustomPoints', $this->Category->PointsCategoryID == $this->Category->CategoryID); } // Get all of the currently selected role/permission combinations for this junction. $Permissions = $PermissionModel->getJunctionPermissions(array('JunctionID' => $CategoryID), 'Category', '', array('AddDefaults' => !$this->Category->CustomPermissions)); $Permissions = $PermissionModel->unpivotPermissions($Permissions, true); if ($this->deliveryType() == DELIVERY_TYPE_ALL) { $this->setData('PermissionData', $Permissions, true); } // Render default view $this->render(); }
/** * Set the icon for an addon. * * @param int $AddonID Specified addon id. * @throws Exception Addon not found. */ public function icon($AddonID = '') { $Session = Gdn::session(); if (!$Session->isValid()) { $this->Form->addError('You must be authenticated in order to use this form.'); } $Addon = $this->AddonModel->getID($AddonID); if (!$Addon) { throw notFoundException('Addon'); } if ($Session->UserID != $Addon['InsertUserID']) { $this->permission('Addons.Addon.Manage'); } $this->addModule('AddonHelpModule', 'Panel'); $this->Form->setModel($this->AddonModel); $this->Form->addHidden('AddonID', $AddonID); if ($this->Form->authenticatedPostBack()) { $UploadImage = new Gdn_UploadImage(); try { // Validate the upload $imageLocation = $UploadImage->validateUpload('Icon'); $TargetImage = $this->saveIcon($imageLocation); } catch (Exception $ex) { $this->Form->addError($ex); } // If there were no errors, remove the old picture and insert the picture if ($this->Form->errorCount() == 0) { if ($Addon['Icon']) { $UploadImage->delete($Addon['Icon']); } $this->AddonModel->save(array('AddonID' => $AddonID, 'Icon' => $TargetImage)); } // If there were no problems, redirect back to the addon if ($this->Form->errorCount() == 0) { $this->RedirectUrl = Url('/addon/' . AddonModel::slug($Addon)); } } $this->render(); }
/** * Serve all CSS files. * * @param $themeType * @param $filename * @throws Exception */ public function serveCss($themeType, $filename) { // Split the filename into filename and etag. if (preg_match('`([\\w-]+?)-(\\w+).css$`', $filename, $matches)) { $basename = $matches[1]; $eTag = $matches[2]; } else { throw notFoundException(); } $basename = strtolower($basename); $this->EventArguments['Basename'] = $basename; $this->EventArguments['ETag'] = $eTag; $this->fireEvent('BeforeServeCss'); if (function_exists('header_remove')) { header_remove('Set-Cookie'); } // Get list of anchor files $anchors = $this->getAnchors(); safeHeader("Content-Type: text/css"); $anchorFileName = "{$basename}.css"; if (!in_array($anchorFileName, $anchors)) { safeHeader("HTTP/1.0 404", true, 404); echo "/* Could not find {$basename}/{$eTag} */"; die; } $requestETags = val('HTTP_IF_NONE_MATCH', $_SERVER); $requestETags = explode(',', $requestETags); foreach ($requestETags as $requestETag) { if ($requestETag == $eTag) { safeHeader("HTTP/1.0 304", true, 304); die; } } safeHeader("Cache-Control:public, max-age=14400"); $currentETag = self::eTag(); safeHeader("ETag: {$currentETag}"); $cachePath = PATH_CACHE . '/css/' . CLIENT_NAME . '-' . $themeType . '-' . "{$basename}-{$currentETag}.css"; if (!Debug() && file_exists($cachePath)) { readfile($cachePath); die; } // Include minify... set_include_path(PATH_LIBRARY . "/vendors/Minify/lib" . PATH_SEPARATOR . get_include_path()); require_once PATH_LIBRARY . "/vendors/Minify/lib/Minify/CSS.php"; ob_start(); echo "/* CSS generated for etag: {$currentETag}.\n *\n"; $notFound = []; $paths = $this->getCssFiles($themeType, $basename, $eTag, $notFound); // First, do a pass through the files to generate some information. foreach ($paths as $info) { list($path, $urlPath) = $info; echo " * {$urlPath}\n"; } // Echo the paths that weren't found to help debugging. foreach ($notFound as $info) { list($filename, $folder) = $info; echo " * {$folder}/{$filename} NOT FOUND.\n"; } echo " */\n\n"; // Now that we have all of the paths we want to serve them. foreach ($paths as $info) { list($path, $urlPath, $options) = $info; echo "/* File: {$urlPath} */\n"; $css = val('Css', $options); if (!$css) { $css = file_get_contents($path); } $css = Minify_CSS::minify($css, ['preserveComments' => true, 'prependRelativePath' => $this->UrlPrefix . asset(dirname($urlPath) . '/'), 'currentDir' => dirname($path), 'minify' => true]); echo $css; echo "\n\n"; } // Create a cached copy of the file. $css = ob_get_flush(); if (!file_exists(dirname($cachePath))) { mkdir(dirname($cachePath), 0775, true); } file_put_contents($cachePath, $css); }
/** * Default all discussions view: chronological by most recent comment. * * @since 2.0.0 * @access public * * @param int $Page Multiplied by PerPage option to determine offset. */ public function index($Page = false) { // Figure out which discussions layout to choose (Defined on "Homepage" settings page). $Layout = c('Vanilla.Discussions.Layout'); switch ($Layout) { case 'table': if ($this->SyndicationMethod == SYNDICATION_NONE) { $this->View = 'table'; } break; default: // $this->View = 'index'; break; } Gdn_Theme::section('DiscussionList'); // Check for the feed keyword. if ($Page === 'feed' && $this->SyndicationMethod != SYNDICATION_NONE) { $Page = 'p1'; } // Determine offset from $Page list($Offset, $Limit) = offsetLimit($Page, c('Vanilla.Discussions.PerPage', 30), true); $Page = PageNumber($Offset, $Limit); // Allow page manipulation $this->EventArguments['Page'] =& $Page; $this->EventArguments['Offset'] =& $Offset; $this->EventArguments['Limit'] =& $Limit; $this->fireEvent('AfterPageCalculation'); // Set canonical URL $this->canonicalUrl(url(ConcatSep('/', 'discussions', PageNumber($Offset, $Limit, true, false)), true)); // We want to limit the number of pages on large databases because requesting a super-high page can kill the db. $MaxPages = c('Vanilla.Discussions.MaxPages'); if ($MaxPages && $Page > $MaxPages) { throw notFoundException(); } // Setup head. if (!$this->data('Title')) { $Title = c('Garden.HomepageTitle'); $DefaultControllerRoute = val('Destination', Gdn::router()->GetRoute('DefaultController')); if ($Title && $DefaultControllerRoute == 'discussions') { $this->title($Title, ''); } else { $this->title(t('Recent Discussions')); } } if (!$this->Description()) { $this->Description(c('Garden.Description', null)); } if ($this->Head) { $this->Head->AddRss(url('/discussions/feed.rss', true), $this->Head->title()); } // Add modules $this->addModule('DiscussionFilterModule'); $this->addModule('NewDiscussionModule'); $this->addModule('CategoriesModule'); $this->addModule('BookmarkedModule'); $this->setData('Breadcrumbs', array(array('Name' => t('Recent Discussions'), 'Url' => '/discussions'))); // Set criteria & get discussions data $this->setData('Category', false, true); $DiscussionModel = new DiscussionModel(); // Check for individual categories. $categoryIDs = $this->getCategoryIDs(); $where = array(); if ($categoryIDs) { $where['d.CategoryID'] = CategoryModel::filterCategoryPermissions($categoryIDs); } else { $DiscussionModel->Watching = true; } // Get Discussion Count $CountDiscussions = $DiscussionModel->getCount($where); if ($MaxPages) { $CountDiscussions = min($MaxPages * $Limit, $CountDiscussions); } $this->setData('CountDiscussions', $CountDiscussions); // Get Announcements $this->AnnounceData = $Offset == 0 ? $DiscussionModel->GetAnnouncements($where) : false; $this->setData('Announcements', $this->AnnounceData !== false ? $this->AnnounceData : array(), true); // Get Discussions $this->DiscussionData = $DiscussionModel->getWhere($where, $Offset, $Limit); $this->setData('Discussions', $this->DiscussionData, true); $this->setJson('Loading', $Offset . ' to ' . $Limit); // Build a pager $PagerFactory = new Gdn_PagerFactory(); $this->EventArguments['PagerType'] = 'Pager'; $this->fireEvent('BeforeBuildPager'); if (!$this->data('_PagerUrl')) { $this->setData('_PagerUrl', 'discussions/{Page}'); } $this->Pager = $PagerFactory->GetPager($this->EventArguments['PagerType'], $this); $this->Pager->ClientID = 'Pager'; $this->Pager->configure($Offset, $Limit, $this->data('CountDiscussions'), $this->data('_PagerUrl')); PagerModule::Current($this->Pager); $this->setData('_Page', $Page); $this->setData('_Limit', $Limit); $this->fireEvent('AfterBuildPager'); // Deliver JSON data if necessary if ($this->_DeliveryType != DELIVERY_TYPE_ALL) { $this->setJson('LessRow', $this->Pager->toString('less')); $this->setJson('MoreRow', $this->Pager->toString('more')); $this->View = 'discussions'; } $this->render(); }
/** * Temporarily enable a locale pack without installing it/ * * @param string $LocaleKey The key of the folder. * @throws NotFoundException */ public function testLocale($LocaleKey) { $Available = $this->availableLocalePacks(); if (!isset($Available[$LocaleKey])) { throw notFoundException('Locale'); } // Grab all of the definition files from the locale. $Paths = SafeGlob(PATH_ROOT . "/locales/{$LocaleKey}/*.php"); // Unload the dynamic config Gdn::locale()->unload(); // Load each locale file, checking for errors foreach ($Paths as $Path) { Gdn::locale()->load($Path, false); } }
/** * Retrieve the user to be manipulated. Defaults to current user. * * @since 2.0.0 * @access public * @param mixed $User Unique identifier, possibly username or ID. * @param string $Username . * @param int $UserID Unique ID. * @param bool $CheckPermissions Whether or not to check user permissions. * @return bool Always true. */ public function getUserInfo($UserReference = '', $Username = '', $UserID = '', $CheckPermissions = false) { if ($this->_UserInfoRetrieved) { return; } if (!c('Garden.Profile.Public') && !Gdn::session()->isValid()) { throw permissionException(); } // If a UserID was provided as a querystring parameter, use it over anything else: if ($UserID) { $UserReference = $UserID; $Username = '******'; // Fill this with a value so the $UserReference is assumed to be an integer/userid. } $this->Roles = array(); if ($UserReference == '') { if ($Username) { $this->User = $this->UserModel->getByUsername($Username); } else { $this->User = $this->UserModel->getID(Gdn::session()->UserID); } } elseif (is_numeric($UserReference) && $Username != '') { $this->User = $this->UserModel->getID($UserReference); } else { $this->User = $this->UserModel->getByUsername($UserReference); } $this->fireEvent('UserLoaded'); if ($this->User === false) { throw notFoundException('User'); } elseif ($this->User->Deleted == 1) { redirect('dashboard/home/deleted'); } else { $this->RoleData = $this->UserModel->getRoles($this->User->UserID); if ($this->RoleData !== false && $this->RoleData->numRows(DATASET_TYPE_ARRAY) > 0) { $this->Roles = array_column($this->RoleData->resultArray(), 'Name'); } // Hide personal info roles if (!checkPermission('Garden.PersonalInfo.View')) { $this->Roles = array_filter($this->Roles, 'RoleModel::FilterPersonalInfo'); } $this->setData('Profile', $this->User); $this->setData('UserRoles', $this->Roles); if ($CssClass = val('_CssClass', $this->User)) { $this->CssClass .= ' ' . $CssClass; } } if ($CheckPermissions && Gdn::session()->UserID != $this->User->UserID) { $this->permission(array('Garden.Users.Edit', 'Moderation.Profiles.Edit'), false); } $this->addSideMenu(); $this->_UserInfoRetrieved = true; return true; }
/** * Load discussions for a specific tag. * @param DiscussionsController $Sender */ public function discussionsController_Tagged_create($Sender) { Gdn_Theme::section('DiscussionList'); $Args = $Sender->RequestArgs; $Get = array_change_key_case($Sender->Request->get()); if ($UseCategories = c('Plugins.Tagging.UseCategories')) { // The url is in the form /category/tag/p1 $CategoryCode = val(0, $Args); $Tag = val(1, $Args); $Page = val(2, $Args); } else { // The url is in the form /tag/p1 $CategoryCode = ''; $Tag = val(0, $Args); $Page = val(1, $Args); } // Look for explcit values. $CategoryCode = val('category', $Get, $CategoryCode); $Tag = val('tag', $Get, $Tag); $Page = val('page', $Get, $Page); $Category = CategoryModel::categories($CategoryCode); $Tag = stringEndsWith($Tag, '.rss', true, true); list($Offset, $Limit) = offsetLimit($Page, c('Vanilla.Discussions.PerPage', 30)); $MultipleTags = strpos($Tag, ',') !== false; $Sender->setData('Tag', $Tag, true); $TagModel = TagModel::instance(); $RecordCount = false; if (!$MultipleTags) { $Tags = $TagModel->getWhere(array('Name' => $Tag))->resultArray(); if (count($Tags) == 0) { throw notFoundException('Page'); } if (count($Tags) > 1) { foreach ($Tags as $TagRow) { if ($TagRow['CategoryID'] == val('CategoryID', $Category)) { break; } } } else { $TagRow = array_pop($Tags); } $Tags = $TagModel->getRelatedTags($TagRow); $RecordCount = $TagRow['CountDiscussions']; $Sender->setData('CountDiscussions', $RecordCount); $Sender->setData('Tags', $Tags); $Sender->setData('Tag', $TagRow); $ChildTags = $TagModel->getChildTags($TagRow['TagID']); $Sender->setData('ChildTags', $ChildTags); } $Sender->title(htmlspecialchars($TagRow['FullName'])); $UrlTag = rawurlencode($Tag); if (urlencode($Tag) == $Tag) { $Sender->canonicalUrl(url(ConcatSep('/', "/discussions/tagged/{$UrlTag}", PageNumber($Offset, $Limit, true)), true)); $FeedUrl = url(ConcatSep('/', "/discussions/tagged/{$UrlTag}/feed.rss", PageNumber($Offset, $Limit, true, false)), '//'); } else { $Sender->canonicalUrl(url(ConcatSep('/', 'discussions/tagged', PageNumber($Offset, $Limit, true)) . '?Tag=' . $UrlTag, true)); $FeedUrl = url(ConcatSep('/', 'discussions/tagged', PageNumber($Offset, $Limit, true, false), 'feed.rss') . '?Tag=' . $UrlTag, '//'); } if ($Sender->Head) { $Sender->addJsFile('discussions.js'); $Sender->Head->addRss($FeedUrl, $Sender->Head->title()); } if (!is_numeric($Offset) || $Offset < 0) { $Offset = 0; } // Add Modules $Sender->addModule('NewDiscussionModule'); $Sender->addModule('DiscussionFilterModule'); $Sender->addModule('BookmarkedModule'); $Sender->setData('Category', false, true); $Sender->AnnounceData = false; $Sender->setData('Announcements', array(), true); $DiscussionModel = new DiscussionModel(); $TagModel->setTagSql($DiscussionModel->SQL, $Tag, $Limit, $Offset, $Sender->Request->get('op', 'or')); $Sender->DiscussionData = $DiscussionModel->get($Offset, $Limit, array('Announce' => 'all')); $Sender->setData('Discussions', $Sender->DiscussionData, true); $Sender->setJson('Loading', $Offset . ' to ' . $Limit); // Build a pager. $PagerFactory = new Gdn_PagerFactory(); $Sender->Pager = $PagerFactory->GetPager('Pager', $Sender); $Sender->Pager->ClientID = 'Pager'; $Sender->Pager->configure($Offset, $Limit, $RecordCount, ''); $Sender->View = c('Vanilla.Discussions.Layout'); /* // If these don't equal, then there is a category that should be inserted. if ($UseCategories && $Category && $TagRow['FullName'] != val('Name', $Category)) { $Sender->Data['Breadcrumbs'][] = array('Name' => $Category['Name'], 'Url' => TagUrl($TagRow)); } $Sender->Data['Breadcrumbs'][] = array('Name' => $TagRow['FullName'], 'Url' => ''); */ // Render the controller. $this->View = c('Vanilla.Discussions.Layout') == 'table' ? 'table' : 'index'; $Sender->render($this->View, 'discussions', 'vanilla'); }
/** * * * @param $ConversationID * @param null $LastMessageID * @throws Exception */ public function getNew($ConversationID, $LastMessageID = null) { $this->RecipientData = $this->ConversationModel->getRecipients($ConversationID); $this->setData('Recipients', $this->RecipientData); // Check permissions on the recipients. $InConversation = false; foreach ($this->RecipientData->result() as $Recipient) { if ($Recipient->UserID == Gdn::session()->UserID) { $InConversation = true; break; } } if (!$InConversation) { // Conversation moderation must be enabled and they must have permission if (!c('Conversations.Moderation.Allow', false)) { throw permissionException(); } $this->permission('Conversations.Moderation.Manage'); } $this->Conversation = $this->ConversationModel->getID($ConversationID); $this->setData('Conversation', $this->Conversation); // Bad conversation? Redirect if ($this->Conversation === false) { throw notFoundException('Conversation'); } $Where = array(); if ($LastMessageID) { if (strpos($LastMessageID, '_') !== false) { $LastMessageID = array_pop(explode('_', $LastMessageID)); } $Where['MessageID >='] = $LastMessageID; } // Fetch message data $this->setData('MessageData', $this->ConversationMessageModel->get($ConversationID, Gdn::session()->UserID, 0, 50, $Where), true); $this->render('Messages'); }
/** * Insert a SPAM Queue entry for the specified record and delete the record, if possible. * * @param string $recordType The type of record we're flagging: Discussion or Comment. * @param int $id ID of the record we're flagging. * @param object|array $data Properties used for updating/overriding the record's current values. * * @throws Exception If an invalid record type is specified, throw an exception. */ protected static function flagForReview($recordType, $id, $data) { // We're planning to purge the spammy record. $deleteRow = true; /** * We're only handling two types of content: discussions and comments. Both need some special setup. * Error out if we're not dealing with a discussion or comment. */ switch ($recordType) { case 'Comment': $model = new CommentModel(); $row = $model->getID($id, DATASET_TYPE_ARRAY); break; case 'Discussion': $model = new DiscussionModel(); $row = $model->getID($id, DATASET_TYPE_ARRAY); /** * If our discussion has more than three comments, it might be worth saving. Hold off on deleting and * just flag it. If we have between 0 and 3 comments, save them along with the discussion. */ if ($row['CountComments'] > 3) { $deleteRow = false; } elseif ($row['CountComments'] > 0) { $comments = Gdn::database()->sql()->getWhere('Comment', array('DiscussionID' => $id))->resultArray(); if (!array_key_exists('_Data', $row)) { $row['_Data'] = array(); } $row['_Data']['Comment'] = $comments; } break; default: throw notFoundException($recordType); } $overrideFields = array('Name', 'Body'); foreach ($overrideFields as $fieldName) { if (($fieldValue = val($fieldName, $data, false)) !== false) { $row[$fieldName] = $fieldValue; } } $logOptions = array('GroupBy' => array('RecordID')); if ($deleteRow) { // Remove the record to the log. $model->deleteID($id); } LogModel::insert('Spam', $recordType, $row, $logOptions); }
/** * * * @param $sender controller instance. * @param int|string $discussionID Identifier of the discussion. * * @throws notFoundException */ protected function _discussionOptions($sender, $discussionID) { $sender->Form = new Gdn_Form(); $Discussion = $sender->DiscussionModel->getID($discussionID); if (!$Discussion) { throw notFoundException('Discussion'); } $sender->permission('Vanilla.Discussions.Edit', true, 'Category', val('PermissionCategoryID', $Discussion)); // Both '' and 'Discussion' denote a discussion type of discussion. if (!val('Type', $Discussion)) { setValue('Type', $Discussion, 'Discussion'); } if ($sender->Form->isPostBack()) { $sender->DiscussionModel->setField($discussionID, 'Type', $sender->Form->getFormValue('Type')); // Update the QnA field. Default to "Unanswered" for questions. Null the field for other types. $qna = val('QnA', $Discussion); switch ($sender->Form->getFormValue('Type')) { case 'Question': $sender->DiscussionModel->setField($discussionID, 'QnA', $qna ? $qna : 'Unanswered'); break; default: $sender->DiscussionModel->setField($discussionID, 'QnA', null); } // $Form = new Gdn_Form(); $sender->Form->setValidationResults($sender->DiscussionModel->validationResults()); // if ($sender->DeliveryType() == DELIVERY_TYPE_ALL || $Redirect) // $sender->RedirectUrl = Gdn::Controller()->Request->PathAndQuery(); Gdn::controller()->jsonTarget('', '', 'Refresh'); } else { $sender->Form->setData($Discussion); } $sender->setData('Discussion', $Discussion); $sender->setData('_Types', array('Question' => '@' . t('Question Type', 'Question'), 'Discussion' => '@' . t('Discussion Type', 'Discussion'))); $sender->setData('Title', t('Q&A Options')); $sender->render('DiscussionOptions', '', 'plugins/QnA'); }
/** * * * @param EntryController $Sender * @param string $Code * @param string $State * @throws Gdn_UserException */ public function entryController_googlePlus_create($Sender, $Code = false, $State = false) { if ($Error = $Sender->Request->get('error')) { throw new Gdn_UserException($Error); } // Get an access token. Gdn::session()->stash(self::ProviderKey); // remove any old google plus. $AccessToken = $this->getAccessToken($Code); $this->accessToken($AccessToken); // Get the user's information. $Profile = $this->api('/userinfo'); if ($State) { parse_str($State, $State); } else { $State = array('r' => 'entry', 'uid' => null); } switch ($State['r']) { case 'profile': // This is a connect request from the user's profile. $User = Gdn::userModel()->getID($State['uid']); if (!$User) { throw notFoundException('User'); } // Save the authentication. Gdn::userModel()->saveAuthentication(array('UserID' => $User->UserID, 'Provider' => self::ProviderKey, 'UniqueID' => $Profile['id'])); // Save the information as attributes. $Attributes = array('AccessToken' => $AccessToken, 'Profile' => $Profile); Gdn::userModel()->saveAttribute($User->UserID, self::ProviderKey, $Attributes); $this->EventArguments['Provider'] = self::ProviderKey; $this->EventArguments['User'] = $Sender->User; $this->fireEvent('AfterConnection'); redirect(userUrl($User, '', 'connections')); break; case 'entry': default: // This is an sso request, we need to redispatch to /entry/connect/googleplus Gdn::session()->stash(self::ProviderKey, array('AccessToken' => $AccessToken, 'Profile' => $Profile)); $url = '/entry/connect/googleplus'; if ($target = val('target', $State)) { $url .= '?Target=' . urlencode($target); } redirect($url); break; } }
/** * Create and display a thumbnail of an uploaded file. */ public function utilityController_mediaThumbnail_create($sender, $media_id) { // When it makes it into core, it will be available in // functions.general.php require 'generate_thumbnail.php'; $model = new Gdn_Model('Media'); $media = $model->getID($media_id, DATASET_TYPE_ARRAY); if (!$media) { throw notFoundException('File'); } // Get actual path to the file. $local_path = Gdn_Upload::copyLocal($media['Path']); if (!file_exists($local_path)) { throw notFoundException('File'); } $file_extension = pathinfo($local_path, PATHINFO_EXTENSION); // Generate new path for thumbnail $thumb_path = $this->getBaseUploadDestinationDir() . '/' . 'thumb'; // Grab full path with filename, and validate it. $thumb_destination_path = $this->getAbsoluteDestinationFilePath($local_path, $file_extension, $thumb_path); // Create thumbnail, and grab debug data from whole process. $thumb_payload = generate_thumbnail($local_path, $thumb_destination_path, array('height' => c('Plugins.FileUpload.ThumbnailHeight', 128))); if ($thumb_payload['success'] === true) { // Thumbnail dimensions $thumb_height = round($thumb_payload['result_height']); $thumb_width = round($thumb_payload['result_width']); // Move the thumbnail to its proper location. Calling SaveAs with // cloudfiles enabled will trigger the move to cloudfiles, so use // same path for each arg in SaveAs. The file will be removed from the local filesystem. $parsed = Gdn_Upload::parse($thumb_destination_path); $target = $thumb_destination_path; // $parsed['Name']; $Upload = new Gdn_Upload(); $filepath_parsed = $Upload->saveAs($thumb_destination_path, $target, array('source' => 'content')); // Save thumbnail information to DB. $model->save(array('MediaID' => $media_id, 'StorageMethod' => $filepath_parsed['Type'], 'ThumbWidth' => $thumb_width, 'ThumbHeight' => $thumb_height, 'ThumbPath' => $filepath_parsed['SaveName'])); // Remove cf scratch copy, typically in cftemp, if there was actually a file pulled in from CF. if (strpos($local_path, 'cftemp') !== false) { if (!unlink($local_path)) { // Maybe add logging for local cf copies not deleted. } } $url = $filepath_parsed['Url']; } else { // Fix the thumbnail information so this isn't requested again and again. $model->save(array('MediaID' => $media_id, 'ImageWidth' => 0, 'ImageHeight' => 0, 'ThumbPath' => '')); $url = asset('/plugins/FileUpload/images/file.png'); } redirect($url, 301); }
/** * Move a category to a different parent. * * @param int $categoryID Unique ID for the category to move. * @throws Exception if category is not found. */ public function moveCategory($categoryID) { // Check permission $this->permission(['Garden.Community.Manage', 'Garden.Settings.Manage'], false); $category = CategoryModel::categories($categoryID); if (!$category) { throw notFoundException(); } $this->Form->setModel($this->CategoryModel); $this->Form->addHidden('CategoryID', $categoryID); $this->setData('Category', $category); $parentCategories = CategoryModel::getAncestors($categoryID); array_pop($parentCategories); if (!empty($parentCategories)) { $this->setData('ParentCategories', array_column($parentCategories, 'Name', 'CategoryID')); } if ($this->Form->authenticatedPostBack()) { // Verify we're only attempting to save specific values. $this->Form->formValues(['CategoryID' => $this->Form->getValue('CategoryID'), 'ParentCategoryID' => $this->Form->getValue('ParentCategoryID')]); $this->Form->save(); } else { $this->Form->setData($category); } $this->render(); }
public function deleteComment($ID, $TK, $Target = '') { $session = Gdn::session(); if (!$session->validateTransientKey($TK)) { throw permissionException(); } if (!is_numeric($ID)) { throw Gdn_UserException('Invalid ID'); } $comment = $this->ActivityModel->getComment($ID); if (!$comment) { throw notFoundException('Comment'); } $activity = $this->ActivityModel->getID(val('ActivityID', $comment)); if (!$activity) { throw notFoundException('Activity'); } if (!$this->ActivityModel->canDelete($activity)) { throw permissionException(); } $this->ActivityModel->deleteComment($ID); if ($this->deliveryType() === DELIVERY_TYPE_ALL) { redirect($Target); } $this->render('Blank', 'Utility', 'Dashboard'); }
/** * * * @param $UserID * @param $Verified * @throws Exception */ public function verify($UserID, $Verified) { $this->permission('Garden.Moderation.Manage'); if (!$this->Request->isAuthenticatedPostBack()) { throw permissionException('Javascript'); } // First, set the field value. Gdn::userModel()->setField($UserID, 'Verified', $Verified); $User = Gdn::userModel()->getID($UserID); if (!$User) { throw notFoundException('User'); } // Send back the verified button. require_once $this->fetchViewLocation('helper_functions', 'Profile', 'Dashboard'); $this->jsonTarget('.User-Verified', userVerified($User), 'ReplaceWith'); $this->render('Blank', 'Utility', 'Dashboard'); }
/** * Send the confirmation email. * * @param null $User * @param bool $Force * @throws Exception */ public function sendEmailConfirmationEmail($User = null, $Force = false) { if (!$User) { $User = Gdn::session()->User; } elseif (is_numeric($User)) { $User = $this->getID($User); } elseif (is_string($User)) { $User = $this->getByEmail($User); } if (!$User) { throw notFoundException('User'); } $User = (array) $User; if (is_string($User['Attributes'])) { $User['Attributes'] = @unserialize($User['Attributes']); } // Make sure the user needs email confirmation. if ($User['Confirmed'] && !$Force) { $this->Validation->addValidationResult('Role', 'Your email doesn\'t need confirmation.'); // Remove the email key. if (isset($User['Attributes']['EmailKey'])) { unset($User['Attributes']['EmailKey']); $this->saveAttribute($User['UserID'], $User['Attributes']); } return; } // Make sure there is a confirmation code. $Code = valr('Attributes.EmailKey', $User); if (!$Code) { $Code = RandomString(8); $Attributes = $User['Attributes']; if (!is_array($Attributes)) { $Attributes = array('EmailKey' => $Code); } else { $Attributes['EmailKey'] = $Code; } $this->saveAttribute($User['UserID'], $Attributes); } $AppTitle = Gdn::config('Garden.Title'); $Email = new Gdn_Email(); $Email->subject(sprintf(t('[%s] Confirm Your Email Address'), $AppTitle)); $Email->to($User['Email']); $EmailFormat = t('EmailConfirmEmail', self::DEFAULT_CONFIRM_EMAIL); $Data = array(); $Data['EmailKey'] = $Code; $Data['User'] = arrayTranslate((array) $User, array('UserID', 'Name', 'Email')); $Data['Title'] = $AppTitle; $Message = formatString($EmailFormat, $Data); $Message = $this->_addEmailHeaderFooter($Message, $Data); $Email->message($Message); $Email->send(); }
/** * Send the confirmation email. * * @param int|string|null $User * @param bool $Force * @throws Exception */ public function sendEmailConfirmationEmail($User = null, $Force = false) { if (!$User) { $User = Gdn::session()->User; } elseif (is_numeric($User)) { $User = $this->getID($User); } elseif (is_string($User)) { $User = $this->getByEmail($User); } if (!$User) { throw notFoundException('User'); } $User = (array) $User; if (is_string($User['Attributes'])) { $User['Attributes'] = dbdecode($User['Attributes']); } // Make sure the user needs email confirmation. if ($User['Confirmed'] && !$Force) { $this->Validation->addValidationResult('Role', 'Your email doesn\'t need confirmation.'); // Remove the email key. if (isset($User['Attributes']['EmailKey'])) { unset($User['Attributes']['EmailKey']); $this->saveAttribute($User['UserID'], $User['Attributes']); } return; } // Make sure there is a confirmation code. $Code = valr('Attributes.EmailKey', $User); if (!$Code) { $Code = randomString(8); $Attributes = $User['Attributes']; if (!is_array($Attributes)) { $Attributes = ['EmailKey' => $Code]; } else { $Attributes['EmailKey'] = $Code; } $this->saveAttribute($User['UserID'], $Attributes); } $AppTitle = Gdn::config('Garden.Title'); $Email = new Gdn_Email(); $Email->subject(sprintf(t('[%s] Confirm Your Email Address'), $AppTitle)); $Email->to($User['Email']); $EmailUrlFormat = '{/entry/emailconfirm,exurl,domain}/{User.UserID,rawurlencode}/{EmailKey,rawurlencode}'; $Data = []; $Data['EmailKey'] = $Code; $Data['User'] = arrayTranslate((array) $User, ['UserID', 'Name', 'Email']); $url = formatString($EmailUrlFormat, $Data); $message = formatString(t('Hello {User.Name}!'), $Data) . ' ' . t('You need to confirm your email address before you can continue.'); $emailTemplate = $Email->getEmailTemplate()->setTitle(t('Confirm Your Email Address'))->setMessage($message)->setButton($url, t('Confirm My Email Address')); $Email->setEmailTemplate($emailTemplate); try { $Email->send(); } catch (Exception $e) { if (debug()) { throw $e; } } }
/** * Confirm email address is valid via sent code. * * @access public * @since 2.0.0 * * @param int $UserID * @param string $EmailKey Authenticate with unique, 1-time code sent via email. */ public function emailConfirm($UserID, $EmailKey = '') { $User = $this->UserModel->getID($UserID); if (!$User) { throw notFoundException('User'); } $EmailConfirmed = $this->UserModel->confirmEmail($User, $EmailKey); $this->Form->setValidationResults($this->UserModel->validationResults()); if ($EmailConfirmed) { $UserID = val('UserID', $User); Gdn::session()->start($UserID); } $this->setData('EmailConfirmed', $EmailConfirmed); $this->setData('Email', $User->Email); $this->render(); }
/** * Re-fetch a discussion's content based on its foreign url. * @param type $DiscussionID */ public function refetchPageInfo($DiscussionID) { // Make sure we are posting back. if (!$this->Request->isAuthenticatedPostBack(true)) { throw permissionException('Javascript'); } // Grab the discussion. $Discussion = $this->DiscussionModel->getID($DiscussionID); if (!$Discussion) { throw notFoundException('Discussion'); } // Make sure the user has permission to edit this discussion. $this->permission('Vanilla.Discussions.Edit', true, 'Category', $Discussion->PermissionCategoryID); $ForeignUrl = valr('Attributes.ForeignUrl', $Discussion); if (!$ForeignUrl) { throw new Gdn_UserException(t("This discussion isn't associated with a url.")); } $Stub = $this->DiscussionModel->fetchPageInfo($ForeignUrl, true); // Save the stub. $this->DiscussionModel->setField($DiscussionID, (array) $Stub); // Send some of the stuff back. if (isset($Stub['Name'])) { $this->jsonTarget('.PageTitle h1', Gdn_Format::text($Stub['Name'])); } if (isset($Stub['Body'])) { $this->jsonTarget("#Discussion_{$DiscussionID} .Message", Gdn_Format::to($Stub['Body'], $Stub['Format'])); } $this->informMessage('The page was successfully fetched.'); $this->render('Blank', 'Utility', 'Dashboard'); }
/** * * * @param string|unknown_type $InvitationID * @return bool * @throws Exception */ public function delete($InvitationID) { $Session = Gdn::session(); $UserID = $Session->UserID; // Validate that this user can delete this invitation: $Invitation = $this->getID($InvitationID, DATASET_TYPE_ARRAY); // Does the invitation exist? if (!$Invitation) { throw notFoundException('Invitation'); } // Does this user own the invitation? if ($UserID != $Invitation['InsertUserID'] && !$Session->checkPermission('Garden.Moderation.Manage')) { throw permissionException('@' . t('InviteErrorPermission', t('ErrorPermission'))); } // Delete it. $this->SQL->delete($this->Name, array('InvitationID' => $InvitationID)); // Add the invitation back onto the user's account if the invitation has not been accepted. if (!$Invitation->AcceptedUserID) { Gdn::userModel()->IncreaseInviteCount($UserID); } return true; }
public function notifyNewDiscussion($DiscussionID) { if (!c('Vanilla.QueueNotifications')) { throw forbiddenException('NotifyNewDiscussion'); } if (!$this->Request->isPostBack()) { throw forbiddenException('GET'); } // Grab the discussion. $Discussion = $this->DiscussionModel->getID($DiscussionID); if (!$Discussion) { throw notFoundException('Discussion'); } if (val('Notified', $Discussion) != ActivityModel::SENT_PENDING) { die('Not pending'); } // Mark the notification as in progress. $this->DiscussionModel->setField($DiscussionID, 'Notified', ActivityModel::SENT_INPROGRESS); $discussionType = val('Type', $Discussion); if ($discussionType) { $Code = "HeadlineFormat.Discussion.{$discussionType}"; } 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', $Discussion)); $Activity = array('ActivityType' => 'Discussion', 'ActivityUserID' => $Discussion->InsertUserID, 'HeadlineFormat' => $HeadlineFormat, 'RecordType' => 'Discussion', 'RecordID' => $DiscussionID, 'Route' => DiscussionUrl($Discussion), 'Data' => array('Name' => $Discussion->Name, 'Category' => val('Name', $Category))); $ActivityModel = new ActivityModel(); $this->DiscussionModel->NotifyNewDiscussion($Discussion, $ActivityModel, $Activity); $ActivityModel->SaveQueue(); $this->DiscussionModel->setField($DiscussionID, 'Notified', ActivityModel::SENT_OK); die('OK'); }
/** * Delete a single draft. * * Redirects user back to Index unless DeliveryType is set. * * @since 2.0.0 * @access public * * @param int $DraftID Unique ID of draft to be deleted. * @param string $TransientKey Single-use hash to prove intent. */ public function delete($DraftID = '', $TransientKey = '') { $Form = Gdn::factory('Form'); $Session = Gdn::session(); if (is_numeric($DraftID) && $DraftID > 0) { $Draft = $this->DraftModel->getID($DraftID); } if ($Draft) { if ($Session->validateTransientKey($TransientKey) && (val('InsertUserID', $Draft) == $Session->UserID || checkPermission('Garden.Community.Manage'))) { // Delete the draft if (!$this->DraftModel->deleteID($DraftID)) { $Form->addError('Failed to delete draft'); } } else { throw permissionException('Garden.Community.Manage'); } } else { throw notFoundException('Draft'); } // Redirect if ($this->_DeliveryType === DELIVERY_TYPE_ALL) { $Target = GetIncomingValue('Target', '/drafts'); redirect($Target); } // Return any errors if ($Form->errorCount() > 0) { $this->setJson('ErrorMessage', $Form->errors()); } // Render default view $this->render(); }