pluginManager() public static method

Get the plugin manager for the application.
public static pluginManager ( ) : Gdn_PluginManager
return Gdn_PluginManager
 /**
  * Find available social plugins.
  *
  * @return array|mixed
  * @throws Exception
  */
 protected function getConnections()
 {
     $this->fireEvent('GetConnections');
     $connections = [];
     $addons = Gdn::addonManager()->lookupAllByType(\Vanilla\Addon::TYPE_ADDON);
     foreach ($addons as $addonName => $addon) {
         $addonInfo = $addon->getInfo();
         // Limit to designated social addons.
         if (!array_key_exists('socialConnect', $addonInfo)) {
             continue;
         }
         // See if addon is enabled.
         $isEnabled = Gdn::addonManager()->isEnabled($addonName, \Vanilla\Addon::TYPE_ADDON);
         setValue('enabled', $addonInfo, $isEnabled);
         // See if we can detect whether connection is configured.
         $isConfigured = null;
         if ($isEnabled) {
             $pluginInstance = Gdn::pluginManager()->getPluginInstance($addonName, Gdn_PluginManager::ACCESS_PLUGINNAME);
             if (method_exists($pluginInstance, 'isConfigured')) {
                 $isConfigured = $pluginInstance->isConfigured();
             }
         }
         setValue('configured', $addonInfo, $isConfigured);
         // Add the connection.
         $connections[$addonName] = $addonInfo;
     }
     return $connections;
 }
 /**
  * Override the default dashboard page with the new stats one.
  */
 public function gdn_dispatcher_beforeDispatch_handler($Sender)
 {
     $Enabled = c('Garden.Analytics.Enabled', true);
     if ($Enabled && !Gdn::pluginManager()->hasNewMethod('SettingsController', 'Index')) {
         Gdn::pluginManager()->registerNewMethod('VanillaStatsPlugin', 'StatsDashboard', 'SettingsController', 'Index');
     }
 }
 /**
  * Is the application/plugin/theme removable?
  *
  * @param string $Type self::TYPE_APPLICATION or self::TYPE_PLUGIN or self::TYPE_THEME
  * @param string $Name
  * @return boolean
  */
 public static function isRemovable($Type, $Name)
 {
     switch ($Type) {
         case self::TYPE_APPLICATION:
             $ApplicationManager = Gdn::Factory('ApplicationManager');
             if ($IsRemovable = !array_key_exists($Name, $ApplicationManager->EnabledApplications())) {
                 $ApplicationInfo = arrayValue($Name, $ApplicationManager->AvailableApplications(), array());
                 $ApplicationFolder = arrayValue('Folder', $ApplicationInfo, '');
                 $IsRemovable = IsWritable(PATH_APPLICATIONS . DS . $ApplicationFolder);
             }
             break;
         case self::TYPE_PLUGIN:
             if ($IsRemovable = !array_key_exists($Name, Gdn::pluginManager()->EnabledPlugins())) {
                 $PluginInfo = arrayValue($Name, Gdn::pluginManager()->AvailablePlugins(), false);
                 $PluginFolder = arrayValue('Folder', $PluginInfo, false);
                 $IsRemovable = IsWritable(PATH_PLUGINS . DS . $PluginFolder);
             }
             break;
         case self::TYPE_THEME:
             // TODO
             $IsRemovable = false;
             break;
     }
     return $IsRemovable;
 }
 /**
  * Reload the locale system.
  */
 public function refresh()
 {
     $LocalName = $this->current();
     $ApplicationWhiteList = Gdn::applicationManager()->enabledApplicationFolders();
     $PluginWhiteList = Gdn::pluginManager()->enabledPluginFolders();
     $ForceRemapping = true;
     $this->set($LocalName, $ApplicationWhiteList, $PluginWhiteList, $ForceRemapping);
 }
 /**
  * Remove plugins that are not mobile friendly!
  */
 public function gdn_dispatcher_afterAnalyzeRequest_handler($Sender)
 {
     // Remove plugins so they don't mess up layout or functionality.
     $inPublicDashboard = $Sender->application() == 'dashboard' && in_array($Sender->controller(), array('Activity', 'Profile', 'Search'));
     if (in_array($Sender->application(), array('vanilla', 'conversations')) || $inPublicDashboard) {
         Gdn::pluginManager()->removeMobileUnfriendlyPlugins();
     }
     saveToConfig('Garden.Format.EmbedSize', '240x135', false);
 }
Example #6
0
 /**
  * Looks through the themes directory for valid themes.
  *
  * The themes are returned as an associative array of "Theme Name" => "Theme Info Array".
  *
  * @param bool $force Deprecated.
  * @return array Returns the available themes in an array.
  */
 public function availableThemes($force = false)
 {
     $addons = $this->addonManager->lookupAllByType(Addon::TYPE_THEME);
     $result = [];
     /* @var Addon $addon */
     foreach ($addons as $addon) {
         $result[$addon->getRawKey()] = Gdn::pluginManager()->calcOldInfoArray($addon);
     }
     return $result;
 }
Example #7
0
 /**
  * Fires an event for initializing static members in the calling class.
  *
  * @return bool Whether the event was fired.
  * @throws Exception
  */
 public static function initStatic()
 {
     if (!self::$initStaticFired) {
         self::$initStaticFired = true;
         Gdn::pluginManager()->fireAs(get_called_class());
         Gdn::pluginManager()->fireEvent('InitStatic');
         return true;
     }
     return false;
 }
Example #8
0
 /**
  * Remove mobile rendering from our custom non-forum pages.
  *
  * @param $sender
  */
 public function base_render_before($sender)
 {
     $userFacing = $sender->MasterView == 'default' || $sender->MasterView == '';
     $onForumPage = !in_array(strtolower($sender->Application), array('vanilla', 'conversations', 'dashboard'));
     if ($userFacing && isMobile() && $onForumPage) {
         // Use the main theme instead of mobile
         $sender->Theme = c('Garden.Theme');
         Gdn::pluginManager()->unregisterPlugin('MobileThemeHooks');
     }
 }
 public function gdn_dispatcher_afterAnalyzeRequest_handler($sender)
 {
     $inPublicDashboard = in_array($sender->controller(), array('Activity', 'Profile', 'Search'));
     if (in_array($sender->application(), array('vanilla', 'conversations')) || $inPublicDashboard) {
         Gdn::pluginManager()->removeMobileUnfriendlyPlugins();
     }
     saveToConfig('Garden.Format.EmbedSize', '240x135', false);
     saveToConfig('Vanilla.AdminCheckboxes.Use', false, false);
     //the table discussions layout takes up too much space on small screens
     saveToConfig('Vanilla.Discussions.Layout', 'modern', false);
 }
Example #10
0
 /**
  *
  *
  * @since 2.0.18
  * @access public
  * @return array Category IDs.
  */
 public static function categoryWatch($AllDiscussions = true)
 {
     $Categories = self::categories();
     $AllCount = count($Categories);
     $Watch = array();
     foreach ($Categories as $CategoryID => $Category) {
         if ($AllDiscussions && val('HideAllDiscussions', $Category)) {
             continue;
         }
         if ($Category['PermsDiscussionsView'] && $Category['Following']) {
             $Watch[] = $CategoryID;
         }
     }
     Gdn::pluginManager()->EventArguments['CategoryIDs'] =& $Watch;
     Gdn::pluginManager()->fireEvent('CategoryWatch');
     if ($AllCount == count($Watch)) {
         return true;
     }
     return $Watch;
 }
Example #11
0
 /**
  * Validate captcha.
  *
  * @param mixed $value
  * @return boolean validity of captcha submission
  */
 public static function validate($value = null)
 {
     if (is_null($value)) {
         // Get captcha text
         $captchaText = null;
         Gdn::pluginManager()->EventArguments['captchatext'] =& $captchaText;
         Gdn::pluginManager()->fireAs('captcha')->fireEvent('get', ['captcha' => $value]);
         $value = $captchaText;
     }
     if (is_null($value)) {
         return false;
     }
     // Validate captcha text
     // Assume invalid submission
     $valid = false;
     Gdn::pluginManager()->EventArguments['captchavalid'] =& $valid;
     Gdn::pluginManager()->fireAs('captcha')->fireEvent('validate', ['captcha' => $value]);
     $isValid = $valid ? true : false;
     unset(Gdn::pluginManager()->EventArguments['captchavalid']);
     return $isValid;
 }
Example #12
0
 /**
  * If passed path leads to an image, return size
  *
  * @param string $Path Path to file.
  * @return array [0] => Height, [1] => Width.
  */
 public static function getImageSize($Path)
 {
     // Static FireEvent for intercepting non-local files.
     $Sender = new stdClass();
     $Sender->Returns = array();
     $Sender->EventArguments = array();
     $Sender->EventArguments['Path'] =& $Path;
     $Sender->EventArguments['Parsed'] = Gdn_Upload::parse($Path);
     Gdn::pluginManager()->callEventHandlers($Sender, 'Gdn_Upload', 'CopyLocal');
     if (!in_array(strtolower(pathinfo($Path, PATHINFO_EXTENSION)), array('gif', 'jpg', 'jpeg', 'png'))) {
         return array(0, 0);
     }
     $ImageSize = @getimagesize($Path);
     if (is_array($ImageSize)) {
         if (!in_array($ImageSize[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG))) {
             return array(0, 0);
         }
         return array($ImageSize[0], $ImageSize[1]);
     }
     return array(0, 0);
 }
Example #13
0
 /**
  * Attach mappings for vanilla extension folders.
  *
  * @param string $ExtensionType The type of extension to map.
  * This should be one of: CONTEXT_THEME, CONTEXT_PLUGIN, CONTEXT_APPLICATION.
  */
 public static function attach($ExtensionType)
 {
     switch ($ExtensionType) {
         case self::CONTEXT_APPLICATION:
             if (Gdn::applicationManager() instanceof Gdn_ApplicationManager) {
                 $EnabledApplications = Gdn::applicationManager()->enabledApplicationFolders();
                 foreach ($EnabledApplications as $EnabledApplication) {
                     self::attachApplication($EnabledApplication);
                 }
             }
             break;
         case self::CONTEXT_PLUGIN:
             if (Gdn::pluginManager() instanceof Gdn_PluginManager) {
                 foreach (Gdn::pluginManager()->searchPaths() as $SearchPath => $SearchPathName) {
                     if ($SearchPathName === true || $SearchPathName == 1) {
                         $SearchPathName = md5($SearchPath);
                     }
                     // If we have already loaded the plugin manager, use its internal folder list
                     if (Gdn::pluginManager()->started()) {
                         $Folders = Gdn::pluginManager()->enabledPluginFolders($SearchPath);
                         foreach ($Folders as $PluginFolder) {
                             $FullPluginPath = combinePaths(array($SearchPath, $PluginFolder));
                             self::registerMap(self::MAP_LIBRARY, self::CONTEXT_PLUGIN, $FullPluginPath, array('SearchSubfolders' => true, 'Extension' => $SearchPathName, 'Structure' => Gdn_Autoloader_Map::STRUCTURE_SPLIT, 'SplitTopic' => strtolower($PluginFolder), 'PreWarm' => true));
                         }
                         $PluginMap = self::getMap(self::MAP_LIBRARY, self::CONTEXT_PLUGIN);
                         if ($PluginMap && !$PluginMap->mapIsOnDisk()) {
                             Gdn::pluginManager()->forceAutoloaderIndex();
                         }
                     }
                 }
             }
             break;
         case self::CONTEXT_THEME:
             break;
     }
 }
 /**
  * Manage list of plugins.
  *
  * @since 2.0.0
  * @access public
  * @param string $Filter 'enabled', 'disabled', or 'all' (default)
  * @param string $PluginName Unique ID of plugin to be modified.
  * @param string $TransientKey Security token.
  */
 public function plugins($Filter = '', $PluginName = '', $TransientKey = '')
 {
     $this->permission('Garden.Settings.Manage');
     // Page setup
     $this->addJsFile('addons.js');
     $this->title(t('Plugins'));
     $this->addSideMenu('dashboard/settings/plugins');
     // Validate and set properties
     $Session = Gdn::session();
     if ($PluginName && !$Session->validateTransientKey($TransientKey)) {
         $PluginName = '';
     }
     if (!in_array($Filter, array('enabled', 'disabled'))) {
         $Filter = 'all';
     }
     $this->Filter = $Filter;
     // Retrieve all available plugins from the plugins directory
     $this->EnabledPlugins = Gdn::pluginManager()->enabledPlugins();
     self::sortAddons($this->EnabledPlugins);
     $this->AvailablePlugins = Gdn::pluginManager()->availablePlugins();
     self::sortAddons($this->AvailablePlugins);
     if ($PluginName != '') {
         try {
             $this->EventArguments['PluginName'] = $PluginName;
             if (array_key_exists($PluginName, $this->EnabledPlugins) === true) {
                 Gdn::pluginManager()->disablePlugin($PluginName);
                 Gdn_LibraryMap::clearCache();
                 $this->fireEvent('AfterDisablePlugin');
             } else {
                 $Validation = new Gdn_Validation();
                 if (!Gdn::pluginManager()->enablePlugin($PluginName, $Validation)) {
                     $this->Form->setValidationResults($Validation->results());
                 } else {
                     Gdn_LibraryMap::ClearCache();
                 }
                 $this->EventArguments['Validation'] = $Validation;
                 $this->fireEvent('AfterEnablePlugin');
             }
         } catch (Exception $e) {
             $this->Form->addError($e);
         }
         if ($this->Form->errorCount() == 0) {
             redirect('/settings/plugins/' . $this->Filter);
         }
     }
     $this->render();
 }
 /**
  * Handle toggling this version of embedding on and off. Take care of disabling the other version of embed (the old plugin).
  *
  * @param string $Toggle
  * @param string $TransientKey
  * @return boolean
  * @throws Gdn_UserException
  */
 private function toggle($Toggle = '', $TransientKey = '')
 {
     if (in_array($Toggle, array('enable', 'disable')) && Gdn::session()->validateTransientKey($TransientKey)) {
         if ($Toggle == 'enable' && array_key_exists('embedvanilla', Gdn::pluginManager()->enabledPlugins())) {
             throw new Gdn_UserException('You must disable the "Embed Vanilla" plugin before continuing.');
         }
         // Do the toggle
         saveToConfig('Garden.Embed.Allow', $Toggle == 'enable' ? true : false);
         return true;
     }
     return false;
 }
Example #16
0
 /**
  *
  *
  * @param null $AddonCode
  * @param bool $Explicit
  * @param bool $Drop
  * @throws Exception
  */
 public function runStructure($AddonCode = null, $Explicit = false, $Drop = false)
 {
     // Get the structure files for all of the enabled applications.
     $ApplicationManager = new Gdn_ApplicationManager();
     $Apps = $ApplicationManager->EnabledApplications();
     $AppNames = consolidateArrayValuesByKey($Apps, 'Folder');
     $Paths = array();
     foreach ($Apps as $Key => $AppInfo) {
         $Path = PATH_APPLICATIONS . "/{$AppInfo['Folder']}/settings/structure.php";
         if (file_exists($Path)) {
             $Paths[] = $Path;
         }
         Gdn::ApplicationManager()->RegisterPermissions($Key, $this->Validation);
     }
     // Execute the structures.
     $Database = Gdn::database();
     $SQL = Gdn::sql();
     $Structure = Gdn::structure();
     foreach ($Paths as $Path) {
         include $Path;
     }
     // Execute the structures for all of the plugins.
     $PluginManager = Gdn::pluginManager();
     $Registered = $PluginManager->RegisteredPlugins();
     foreach ($Registered as $ClassName => $Enabled) {
         if (!$Enabled) {
             continue;
         }
         try {
             $Plugin = $PluginManager->GetPluginInstance($ClassName, Gdn_PluginManager::ACCESS_CLASSNAME);
             if (method_exists($Plugin, 'Structure')) {
                 trace("{$ClassName}->Structure()");
                 $Plugin->Structure();
             }
         } catch (Exception $Ex) {
             // Do nothing, plugin wouldn't load/structure.
             if (Debug()) {
                 throw $Ex;
             }
         }
     }
     $this->fireEvent('AfterStructure');
 }
Example #17
0
 /**
  * Takes the path to an asset (image, js file, css file, etc) and prepends the web root.
  *
  * @param string $Destination The path to the asset.
  * @param boolean $WithDomain Whether or not to include the domain.
  * @param boolean $AddVersion Whether or not to add a cache-busting querystring parameter to the URL.
  * @param string $Version Forced version, skips auto-lookup.
  * @return string Returns the URL to the asset.
  */
 function asset($Destination = '', $WithDomain = false, $AddVersion = false, $Version = null)
 {
     $Destination = str_replace('\\', '/', $Destination);
     if (IsUrl($Destination)) {
         $Result = $Destination;
     } else {
         $Result = Gdn::request()->urlDomain($WithDomain) . Gdn::request()->assetRoot() . '/' . ltrim($Destination, '/');
     }
     if ($AddVersion) {
         if (strpos($Result, '?') === false) {
             $Result .= '?';
         } else {
             $Result .= '&';
         }
         // Figure out which version to put after the asset.
         if (is_null($Version)) {
             $Version = APPLICATION_VERSION;
             if (preg_match('`^/([^/]+)/([^/]+)/`', $Destination, $Matches)) {
                 $Type = $Matches[1];
                 $Key = $Matches[2];
                 static $ThemeVersion = null;
                 switch ($Type) {
                     case 'plugins':
                         $PluginInfo = Gdn::pluginManager()->getPluginInfo($Key);
                         $Version = val('Version', $PluginInfo, $Version);
                         break;
                     case 'applications':
                         $AppInfo = Gdn::applicationManager()->getApplicationInfo(ucfirst($Key));
                         $Version = val('Version', $AppInfo, $Version);
                         break;
                     case 'themes':
                         if ($ThemeVersion === null) {
                             $ThemeInfo = Gdn::themeManager()->getThemeInfo(Theme());
                             if ($ThemeInfo !== false) {
                                 $ThemeVersion = val('Version', $ThemeInfo, $Version);
                             } else {
                                 $ThemeVersion = $Version;
                             }
                         }
                         $Version = $ThemeVersion;
                         break;
                 }
             }
         }
         $Result .= 'v=' . urlencode($Version);
     }
     return $Result;
 }
Example #18
0
 * Extension Startup
 *
 * Allow installed addons to execute startup and bootstrap procedures that they may have, here.
 */
// Bootstrapping.
foreach (Gdn::addonManager()->getEnabled() as $addon) {
    /* @var Addon $addon */
    if ($bootstrapPath = $addon->getSpecial('bootstrap')) {
        $bootstrapPath = $addon->path($bootstrapPath);
        include $bootstrapPath;
    }
}
// Themes startup
Gdn::themeManager()->start();
// Plugins startup
Gdn::pluginManager()->start();
/**
 * Locales
 *
 * Install any custom locales provided by applications and plugins, and set up
 * the locale management system.
 */
// Load the Garden locale system
$gdnLocale = new Gdn_Locale(c('Garden.Locale', 'en'), Gdn::addonManager());
Gdn::factoryInstall(Gdn::AliasLocale, 'Gdn_Locale', null, Gdn::FactorySingleton, $gdnLocale);
unset($gdnLocale);
require_once PATH_LIBRARY_CORE . '/functions.validation.php';
// Start Authenticators
Gdn::authenticator()->startAuthenticator();
/**
 * Bootstrap After
Example #19
0
 /**
  *
  */
 protected function __construct()
 {
     // Initialize the canonical list. (emoji)
     $this->emoji = array('smile' => 'smile.png', 'smiley' => 'smiley.png', 'wink' => 'wink.png', 'blush' => 'blush.png', 'neutral' => 'neutral.png', 'relaxed' => 'relaxed.png', 'grin' => 'grin.png', 'joy' => 'joy.png', 'sweat_smile' => 'sweat_smile.png', 'lol' => 'lol.png', 'innocent' => 'innocent.png', 'naughty' => 'naughty.png', 'yum' => 'yum.png', 'relieved' => 'relieved.png', 'love' => 'love.png', 'sunglasses' => 'sunglasses.png', 'smirk' => 'smirk.png', 'expressionless' => 'expressionless.png', 'unamused' => 'unamused.png', 'sweat' => 'sweat.png', 'pensive' => 'pensive.png', 'confused' => 'confused.png', 'confounded' => 'confounded.png', 'kissing' => 'kissing.png', 'kissing_heart' => 'kissing_heart.png', 'kissing_smiling_eyes' => 'kissing_smiling_eyes.png', 'kissing_closed_eyes' => 'kissing_closed_eyes.png', 'tongue' => 'tongue.png', 'disappointed' => 'disappointed.png', 'worried' => 'worried.png', 'angry' => 'angry.png', 'rage' => 'rage.png', 'cry' => 'cry.png', 'persevere' => 'persevere.png', 'triumph' => 'triumph.png', 'frowning' => 'frowning.png', 'anguished' => 'anguished.png', 'fearful' => 'fearful.png', 'weary' => 'weary.png', 'sleepy' => 'sleepy.png', 'tired_face' => 'tired_face.png', 'grimace' => 'grimace.png', 'bawling' => 'bawling.png', 'open_mouth' => 'open_mouth.png', 'hushed' => 'hushed.png', 'cold_sweat' => 'cold_sweat.png', 'scream' => 'scream.png', 'astonished' => 'astonished.png', 'flushed' => 'flushed.png', 'sleeping' => 'sleeping.png', 'dizzy' => 'dizzy.png', 'no_mouth' => 'no_mouth.png', 'mask' => 'mask.png', 'star' => 'star.png', 'cookie' => 'cookie.png', 'warning' => 'warning.png', 'mrgreen' => 'mrgreen.png', 'heart' => 'heart.png', 'heartbreak' => 'heartbreak.png', 'kiss' => 'kiss.png', '+1' => '+1.png', '-1' => '-1.png', 'grey_question' => 'grey_question.png', 'trollface' => 'trollface.png');
     // Some aliases self-referencing the canonical list. Use this syntax.
     // This is used in cases where emoji image cannot be found.
     $this->emoji['error'] =& $this->emoji['grey_question'];
     // Initialize the alias list. (emoticons)
     $this->aliases = array(':)' => 'smile', ':D' => 'lol', '=)' => 'smiley', ':(' => 'frowning', ';)' => 'wink', ':\\' => 'confused', ':/' => 'confused', ':o' => 'open_mouth', ':s' => 'confounded', ':p' => 'stuck_out_tongue', ":'(" => 'cry', ':|' => 'neutral', 'D:' => 'anguished', 'B)' => 'sunglasses', ':#' => 'grimace', ':*' => 'kiss', ':3' => 'blush', 'o:)' => 'innocent', '<3' => 'heart', '>:)' => 'naughty');
     $this->archive = array('disappointed_relieved' => 'disappointed_relieved.png', 'dizzy_face' => 'dizzy.png', 'broken_heart' => 'heartbreak.png', 'grinning' => 'grin.png', 'heart_eyes' => 'love.png', 'neutral_face' => 'neutral.png', 'smiling_imp' => 'naughty.png', 'sob' => 'bawling.png', 'stuck_out_tongue' => 'tongue.png', 'stuck_out_tongue_winking_eye' => 'stuck_out_tongue_winking_eye.png', 'stuck_out_tongue_closed_eyes' => 'stuck_out_tongue_closed_eyes.png');
     $this->editorList = array(':)' => 'smile', ':D' => 'lol', ':(' => 'disappointed', ';)' => 'wink', ':/' => 'confused', ':o' => 'open_mouth', ':s' => 'confounded', ':p' => 'stuck_out_tongue', ":'(" => 'cry', ':|' => 'neutral', 'B)' => 'sunglasses', ':#' => 'grimace', ':*' => 'kiss', '<3' => 'heart', 'o:)' => 'innocent', '>:)' => 'naughty');
     if (C('Garden.EmojiSet') === 'none') {
         $this->enabled = false;
     }
     Gdn::pluginManager()->callEventHandlers($this, 'Emoji', 'Init', 'Handler');
     // Add emoji to definition list for whole site. This used to be in the
     // advanced editor plugin, but since moving atmentions to core, had to
     // make sure they were still being added. This will make sure that
     // emoji autosuggest works. Note: emoji will not be core yet, so the only
     // way that this gets called is by the editor when it instantiates. Core
     // does not instantiate this class anywhere, so there will not be any
     // suggestions for emoji yet, but keep here for whenever Advanced Editor
     // is running.
     $c = Gdn::controller();
     if ($c && $this->enabled) {
         $emojis = $this->getEmoji();
         $emojiAssetPath = $this->getAssetPath();
         $emoji = array();
         foreach ($emojis as $name => $data) {
             $emoji[] = array("name" => "" . $name . "", "url" => Asset($emojiAssetPath . '/' . $data));
         }
         $emoji = array('assetPath' => Asset($this->getAssetPath()), 'format' => $this->getFormat(), 'emoji' => $this->getEmoji());
         $c->addDefinition('emoji', $emoji);
     }
 }
 /**
  * Returns the url prefix for a given type.
  * If there is a plugin that wants to store uploads at a different location or in a different way then they register themselves by subscribing to the Gdn_Upload_GetUrls_Handler event.
  * After that they will be available here.
  *
  * @param string $Type The type of upload to get the prefix for.
  * @return string The url prefix.
  */
 public static function urls($Type = null)
 {
     static $Urls = null;
     if ($Urls === null) {
         $Urls = array('' => asset('/uploads', true));
         $Sender = new stdClass();
         $Sender->Returns = array();
         $Sender->EventArguments = array();
         $Sender->EventArguments['Urls'] =& $Urls;
         Gdn::pluginManager()->callEventHandlers($Sender, 'Gdn_Upload', 'GetUrls');
     }
     if ($Type === null) {
         return $Urls;
     }
     if (isset($Urls[$Type])) {
         return $Urls[$Type];
     }
     return false;
 }
Example #21
0
 /**
  * The summary of all settings available.
  *
  * The menu items displayed here are collected from each application's
  * application controller and all plugin's definitions.
  *
  * @since 2.0.0
  * @access public
  */
 public function index()
 {
     $this->ApplicationFolder = 'dashboard';
     $this->MasterView = 'setup';
     // Fatal error if Garden has already been installed.
     $Installed = c('Garden.Installed');
     if ($Installed) {
         throw new Gdn_UserException('Vanilla is installed!', 409);
     }
     if (!$this->_checkPrerequisites()) {
         $this->View = 'prerequisites';
     } else {
         $this->View = 'configure';
         // Make sure the user has copied the htaccess file over.
         if (!file_exists(PATH_ROOT . '/.htaccess')) {
             $this->setData('NoHtaccess', true);
             if ($this->Form->isPostBack()) {
                 $htaccessAction = $this->Form->getFormValue('HtaccessAction');
                 switch ($htaccessAction) {
                     case 'skip':
                         break;
                     case 'dist':
                         $htaccessCopied = copy(PATH_ROOT . '/.htaccess.dist', PATH_ROOT . '/.htaccess');
                         if ($htaccessCopied === false) {
                             $this->Form->addError(t('Unable to copy .htaccess.dist to .htaccess.', 'Unable to copy .htaccess.dist to .htaccess. You may need to manually copy this file.'));
                         }
                         break;
                     default:
                         $this->Form->addError(t('You are missing Vanilla\'s .htaccess file.', 'You are missing an <b>.htaccess</b> file. This file can be automatically created from Vanilla\'s <b>.htaccess.dist</b>.  However, it may not have been copied if you are using FTP to upload your files because this file is hidden. Make sure you\'ve copied the <b>.htaccess.dist</b> file before continuing.'));
                 }
             }
         }
         $ApplicationManager = Gdn::applicationManager();
         // Need to go through all of the setups for each application. Garden,
         if ($this->configure() && $this->Form->isPostBack()) {
             // Get list of applications to enable during install
             // Override by creating the config and adding this setting before install begins
             $AppNames = c('Garden.Install.Applications', array('Conversations', 'Vanilla'));
             try {
                 // Step through the available applications, enabling each of them.
                 foreach ($AppNames as $AppName) {
                     $Validation = new Gdn_Validation();
                     $ApplicationManager->RegisterPermissions($AppName, $Validation);
                     $ApplicationManager->EnableApplication($AppName, $Validation);
                 }
                 Gdn::pluginManager()->start(true);
             } catch (Exception $ex) {
                 $this->Form->addError($ex);
             }
             if ($this->Form->errorCount() == 0) {
                 // Save a variable so that the application knows it has been installed.
                 // Now that the application is installed, select a more user friendly error page.
                 $Config = array('Garden.Installed' => true);
                 saveToConfig($Config);
                 $this->setData('Installed', true);
                 $this->fireAs('UpdateModel')->fireEvent('AfterStructure');
                 $this->fireEvent('Installed');
                 // Go to the dashboard.
                 if ($this->deliveryType() === DELIVERY_TYPE_ALL) {
                     redirect('/settings/gettingstarted');
                 }
             } elseif ($this->deliveryType() === DELIVERY_TYPE_DATA) {
                 $maxCode = 0;
                 $messages = array();
                 foreach ($this->Form->errors() as $row) {
                     list($code, $message) = $row;
                     $maxCode = max($maxCode, $code);
                     $messages[] = $message;
                 }
                 throw new Gdn_UserException(implode(' ', $messages), $maxCode);
             }
         }
     }
     $this->render();
 }
Example #22
0
 /**
  * Saves the specified image at $Target in the specified format with the
  * specified dimensions (or the existing dimensions if height/width are not provided.
  *
  * @param string The path to the source image. Typically this is the tmp file name returned by $this->ValidateUpload();
  * @param string The full path to where the image should be saved, including image name.
  * @param int An integer value indicating the maximum allowed height of the image (in pixels).
  * @param int An integer value indicating the maximum allowed width of the image (in pixels).
  * @param array Options additional options for saving the image.
  *  - <b>Crop</b>: Image proportions will always remain constrained. The Crop parameter is a boolean value indicating if the image should be cropped when one dimension (height or width) goes beyond the constrained proportions.
  *  - <b>OutputType</b>: The format in which the output image should be saved. Options are: jpg, png, and gif. Default is jpg.
  *  - <b>ImageQuality</b>: An integer value representing the qualityof the saved image. Ranging from 0 (worst quality, smaller file) to 100 (best quality, biggest file).
  *  - <b>SourceX, SourceY</b>: If you want to create a thumbnail that is a crop of the image these are the coordinates of the thumbnail.
  *  - <b>SourceHeight. SourceWidth</b>: If you want to create a thumbnail that is a crop of the image these are it's dimensions.
  */
 public static function saveImageAs($Source, $Target, $Height = '', $Width = '', $Options = array())
 {
     $Crop = false;
     $OutputType = '';
     $ImageQuality = c('Garden.UploadImage.Quality', 100);
     // Make function work like it used to.
     $Args = func_get_args();
     $SaveGif = false;
     if (count($Args) > 5) {
         $Crop = val(4, $Args, $Crop);
         $OutputType = val(5, $Args, $OutputType);
         $ImageQuality = val(6, $Args, $ImageQuality);
     } elseif (is_bool($Options)) {
         $Crop = $Options;
     } else {
         $Crop = val('Crop', $Options, $Crop);
         $OutputType = val('OutputType', $Options, $OutputType);
         $ImageQuality = val('ImageQuality', $Options, $ImageQuality);
         $SaveGif = val('SaveGif', $Options);
     }
     // Set some boundaries for $ImageQuality
     if ($ImageQuality < 10) {
         $ImageQuality = 10;
     }
     if ($ImageQuality > 100 || !is_numeric($ImageQuality)) {
         $ImageQuality = 100;
     }
     // Make sure type, height & width are properly defined.
     if (!function_exists('gd_info')) {
         throw new Exception(T('The uploaded file could not be processed because GD is not installed.'));
     }
     $GdInfo = gd_info();
     $Size = getimagesize($Source);
     list($WidthSource, $HeightSource, $Type) = $Size;
     $WidthSource = val('SourceWidth', $Options, $WidthSource);
     $HeightSource = val('SourceHeight', $Options, $HeightSource);
     if ($Height == '' || !is_numeric($Height)) {
         $Height = $HeightSource;
     }
     if ($Width == '' || !is_numeric($Width)) {
         $Width = $WidthSource;
     }
     if (!$OutputType) {
         $OutputTypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png', 17 => 'ico');
         $OutputType = val($Type, $OutputTypes, 'jpg');
     } elseif ($Type == 17 && $OutputType != 'ico') {
         // Icons cannot be converted
         throw new Exception(T('Upload cannot convert icons.'));
     }
     // Figure out the target path.
     $TargetParsed = Gdn_Upload::parse($Target);
     $TargetPath = PATH_UPLOADS . '/' . ltrim($TargetParsed['Name'], '/');
     if (!file_exists(dirname($TargetPath))) {
         mkdir(dirname($TargetPath), 0777, true);
     }
     // Don't resize if the source dimensions are smaller than the target dimensions or an icon
     $XCoord = val('SourceX', $Options, 0);
     $YCoord = val('SourceY', $Options, 0);
     if (($HeightSource > $Height || $WidthSource > $Width) && $Type != 17) {
         $AspectRatio = (double) $WidthSource / $HeightSource;
         if ($Crop === false) {
             if (round($Width / $AspectRatio) > $Height) {
                 $Width = round($Height * $AspectRatio);
             } else {
                 $Height = round($Width / $AspectRatio);
             }
         } else {
             $HeightDiff = $HeightSource - $Height;
             $WidthDiff = $WidthSource - $Width;
             if ($WidthDiff > $HeightDiff) {
                 // Crop the original width down
                 $NewWidthSource = round($Width * $HeightSource / $Height);
                 // And set the original x position to the cropped start point.
                 if (!isset($Options['SourceX'])) {
                     $XCoord = round(($WidthSource - $NewWidthSource) / 2);
                 }
                 $WidthSource = $NewWidthSource;
             } else {
                 // Crop the original height down
                 $NewHeightSource = round($Height * $WidthSource / $Width);
                 // And set the original y position to the cropped start point.
                 if (!isset($Options['SourceY'])) {
                     $YCoord = 0;
                     // crop to top because most portraits show the face at the top.
                 }
                 $HeightSource = $NewHeightSource;
             }
         }
     } else {
         // Neither target dimension is larger than the original, so keep the original dimensions.
         $Height = $HeightSource;
         $Width = $WidthSource;
     }
     $Process = true;
     if ($WidthSource <= $Width && $HeightSource <= $Height && $Type == 1 && $SaveGif) {
         $Process = false;
     }
     // Never process icons
     if ($Type == 17) {
         $Process = false;
     }
     if ($Process) {
         // Create GD image from the provided file, but first check if we have the necessary tools
         $SourceImage = false;
         switch ($Type) {
             case 1:
                 if (val('GIF Read Support', $GdInfo) || val('GIF Write Support', $GdInfo)) {
                     $SourceImage = imagecreatefromgif($Source);
                 }
                 break;
             case 2:
                 if (val('JPG Support', $GdInfo) || val('JPEG Support', $GdInfo)) {
                     $SourceImage = imagecreatefromjpeg($Source);
                 }
                 break;
             case 3:
                 if (val('PNG Support', $GdInfo)) {
                     $SourceImage = imagecreatefrompng($Source);
                     imagealphablending($SourceImage, true);
                 }
                 break;
         }
         if (!$SourceImage) {
             throw new Exception(sprintf(T('You cannot save images of this type (%s).'), $Type));
         }
         // Create a new image from the raw source
         if (function_exists('imagecreatetruecolor')) {
             $TargetImage = imagecreatetruecolor($Width, $Height);
             // Only exists if GD2 is installed
         } else {
             $TargetImage = imagecreate($Width, $Height);
             // Always exists if any GD is installed
         }
         if ($OutputType == 'png') {
             imagealphablending($TargetImage, false);
             imagesavealpha($TargetImage, true);
         }
         imagecopyresampled($TargetImage, $SourceImage, 0, 0, $XCoord, $YCoord, $Width, $Height, $WidthSource, $HeightSource);
         imagedestroy($SourceImage);
         // Check for EXIF rotation tag, and rotate the image if present
         if (function_exists('exif_read_data') && ($Type == IMAGETYPE_JPEG || $Type == IMAGETYPE_TIFF_II || $Type == IMAGETYPE_TIFF_MM)) {
             $ImageExif = exif_read_data($Source);
             if (!empty($ImageExif['Orientation'])) {
                 switch ($ImageExif['Orientation']) {
                     case 3:
                         $TargetImage = imagerotate($TargetImage, 180, 0);
                         break;
                     case 6:
                         $TargetImage = imagerotate($TargetImage, -90, 0);
                         list($Width, $Height) = array($Height, $Width);
                         break;
                     case 8:
                         $TargetImage = imagerotate($TargetImage, 90, 0);
                         list($Width, $Height) = array($Height, $Width);
                         break;
                 }
             }
         }
         // No need to check these, if we get here then whichever function we need will be available
         if ($OutputType == 'gif') {
             imagegif($TargetImage, $TargetPath);
         } elseif ($OutputType == 'png') {
             imagepng($TargetImage, $TargetPath, 10 - (int) ($ImageQuality / 10));
         } elseif ($OutputType == 'ico') {
             self::imageIco($TargetImage, $TargetPath);
         } else {
             imagejpeg($TargetImage, $TargetPath, $ImageQuality);
         }
     } else {
         copy($Source, $TargetPath);
     }
     // Allow a plugin to move the file to a differnt location.
     $Sender = new stdClass();
     $Sender->EventArguments = array();
     $Sender->EventArguments['Path'] = $TargetPath;
     $Parsed = self::parse($TargetPath);
     $Parsed['Width'] = $Width;
     $Parsed['Height'] = $Height;
     $Sender->EventArguments['Parsed'] =& $Parsed;
     $Sender->Returns = array();
     Gdn::pluginManager()->callEventHandlers($Sender, 'Gdn_Upload', 'SaveAs');
     return $Sender->EventArguments['Parsed'];
 }
Example #23
0
 /**
  * Defines & retrieves the view and master view. Renders all content within
  * them to the screen.
  *
  * @param string $View
  * @param string $ControllerName
  * @param string $ApplicationFolder
  * @param string $AssetName The name of the asset container that the content should be rendered in.
  */
 public function xRender($View = '', $ControllerName = false, $ApplicationFolder = false, $AssetName = 'Content')
 {
     // Remove the deliver type and method from the query string so they don't corrupt calls to Url.
     $this->Request->setValueOn(Gdn_Request::INPUT_GET, 'DeliveryType', null);
     $this->Request->setValueOn(Gdn_Request::INPUT_GET, 'DeliveryMethod', null);
     Gdn::pluginManager()->callEventHandlers($this, $this->ClassName, $this->RequestMethod, 'Render');
     if ($this->_DeliveryType == DELIVERY_TYPE_NONE) {
         return;
     }
     // Handle deprecated StatusMessage values that may have been added by plugins
     $this->informMessage($this->StatusMessage);
     // If there were uncontrolled errors above the json data, wipe them out
     // before fetching it (otherwise the json will not be properly parsed
     // by javascript).
     if ($this->_DeliveryMethod == DELIVERY_METHOD_JSON) {
         if (ob_get_level()) {
             ob_clean();
         }
         $this->contentType('application/json; charset=' . c('Garden.Charset', 'utf-8'));
         $this->setHeader('X-Content-Type-Options', 'nosniff');
         // Cross-Origin Resource Sharing (CORS)
         $this->setAccessControl();
     }
     if ($this->_DeliveryMethod == DELIVERY_METHOD_TEXT) {
         $this->contentType('text/plain');
     }
     // Send headers to the browser
     $this->sendHeaders();
     // Make sure to clear out the content asset collection if this is a syndication request
     if ($this->SyndicationMethod !== SYNDICATION_NONE) {
         $this->Assets['Content'] = '';
     }
     // Define the view
     if (!in_array($this->_DeliveryType, array(DELIVERY_TYPE_BOOL, DELIVERY_TYPE_DATA))) {
         $View = $this->fetchView($View, $ControllerName, $ApplicationFolder);
         // Add the view to the asset container if necessary
         if ($this->_DeliveryType != DELIVERY_TYPE_VIEW) {
             $this->addAsset($AssetName, $View, 'Content');
         }
     }
     // Redefine the view as the entire asset contents if necessary
     if ($this->_DeliveryType == DELIVERY_TYPE_ASSET) {
         $View = $this->getAsset($AssetName);
     } elseif ($this->_DeliveryType == DELIVERY_TYPE_BOOL) {
         // Or as a boolean if necessary
         $View = true;
         if (property_exists($this, 'Form') && is_object($this->Form)) {
             $View = $this->Form->errorCount() > 0 ? false : true;
         }
     }
     if ($this->_DeliveryType == DELIVERY_TYPE_MESSAGE && $this->Form) {
         $View = $this->Form->errors();
     }
     if ($this->_DeliveryType == DELIVERY_TYPE_DATA) {
         $ExitRender = $this->renderData();
         if ($ExitRender) {
             return;
         }
     }
     if ($this->_DeliveryMethod == DELIVERY_METHOD_JSON) {
         // Format the view as JSON with some extra information about the
         // success status of the form so that jQuery knows what to do
         // with the result.
         if ($this->_FormSaved === '') {
             // Allow for override
             $this->_FormSaved = property_exists($this, 'Form') && $this->Form->errorCount() == 0 ? true : false;
         }
         $this->setJson('FormSaved', $this->_FormSaved);
         $this->setJson('DeliveryType', $this->_DeliveryType);
         $this->setJson('Data', base64_encode($View instanceof Gdn_IModule ? $View->toString() : $View));
         $this->setJson('InformMessages', $this->_InformMessages);
         $this->setJson('ErrorMessages', $this->_ErrorMessages);
         $this->setJson('RedirectUrl', $this->RedirectUrl);
         // Make sure the database connection is closed before exiting.
         $this->finalize();
         if (!check_utf8($this->_Json['Data'])) {
             $this->_Json['Data'] = utf8_encode($this->_Json['Data']);
         }
         $Json = json_encode($this->_Json);
         $this->_Json['Data'] = $Json;
         exit($this->_Json['Data']);
     } else {
         if (count($this->_InformMessages) > 0 && $this->SyndicationMethod === SYNDICATION_NONE) {
             $this->addDefinition('InformMessageStack', base64_encode(json_encode($this->_InformMessages)));
         }
         if ($this->RedirectUrl != '' && $this->SyndicationMethod === SYNDICATION_NONE) {
             $this->addDefinition('RedirectUrl', $this->RedirectUrl);
         }
         if ($this->_DeliveryMethod == DELIVERY_METHOD_XHTML && debug()) {
             $this->addModule('TraceModule');
         }
         // Render
         if ($this->_DeliveryType == DELIVERY_TYPE_BOOL) {
             echo $View ? 'TRUE' : 'FALSE';
         } elseif ($this->_DeliveryType == DELIVERY_TYPE_ALL) {
             // Render
             $this->renderMaster();
         } else {
             if ($View instanceof Gdn_IModule) {
                 $View->render();
             } else {
                 echo $View;
             }
         }
     }
 }
Example #24
0
 /**
  * Authenticates the user with the provided Authenticator class.
  *
  * @param int $UserID The UserID to start the session with.
  * @param bool $SetIdentity Whether or not to set the identity (cookie) or make this a one request session.
  * @param bool $Persist If setting an identity, should we persist it beyond browser restart?
  */
 public function start($UserID = false, $SetIdentity = true, $Persist = false)
 {
     if (!c('Garden.Installed', false)) {
         return;
     }
     // Retrieve the authenticated UserID from the Authenticator module.
     $UserModel = Gdn::authenticator()->getUserModel();
     $this->UserID = $UserID !== false ? $UserID : Gdn::authenticator()->getIdentity();
     $this->User = false;
     // Now retrieve user information
     if ($this->UserID > 0) {
         // Instantiate a UserModel to get session info
         $this->User = $UserModel->getSession($this->UserID);
         if ($this->User) {
             if ($SetIdentity) {
                 Gdn::authenticator()->setIdentity($this->UserID, $Persist);
                 Logger::event('session_start', Logger::INFO, 'Session started for {username}.');
                 Gdn::pluginManager()->callEventHandlers($this, 'Gdn_Session', 'Start');
             }
             $UserModel->EventArguments['User'] =& $this->User;
             $UserModel->fireEvent('AfterGetSession');
             $this->_Permissions = Gdn_Format::unserialize($this->User->Permissions);
             $this->_Preferences = Gdn_Format::unserialize($this->User->Preferences);
             $this->_Attributes = Gdn_Format::unserialize($this->User->Attributes);
             $this->_TransientKey = is_array($this->_Attributes) ? val('TransientKey', $this->_Attributes) : false;
             if ($this->_TransientKey === false) {
                 $this->_TransientKey = $UserModel->setTransientKey($this->UserID);
             }
             // Save any visit-level information.
             if ($SetIdentity) {
                 $UserModel->updateVisit($this->UserID);
             }
         } else {
             $this->UserID = 0;
             $this->User = false;
             $this->_TransientKey = getAppCookie('tk');
             if ($SetIdentity) {
                 Gdn::authenticator()->setIdentity(null);
             }
         }
     } else {
         // Grab the transient key from the cookie. This doesn't always get set but we'll try it here anyway.
         $this->_TransientKey = getAppCookie('tk');
     }
     // Load guest permissions if necessary
     if ($this->UserID == 0) {
         $this->_Permissions = Gdn_Format::unserialize($UserModel->definePermissions(0));
     }
 }
 public static function discussionTypes()
 {
     if (!self::$_DiscussionTypes) {
         $DiscussionTypes = array('Discussion' => array('Singular' => 'Discussion', 'Plural' => 'Discussions', 'AddUrl' => '/post/discussion', 'AddText' => 'New Discussion'));
         Gdn::pluginManager()->EventArguments['Types'] =& $DiscussionTypes;
         Gdn::pluginManager()->FireAs('DiscussionModel')->fireEvent('DiscussionTypes');
         self::$_DiscussionTypes = $DiscussionTypes;
         unset(Gdn::pluginManager()->EventArguments['Types']);
     }
     return self::$_DiscussionTypes;
 }
Example #26
0
 /**
  * Gets the short name of the currently active cache.
  *
  * This method retrieves the name of the active cache according to the config file.
  * It fires an event thereafter, allowing that value to be overridden
  * by loaded plugins.
  *
  * @return string shortname of current auto active cache
  */
 public static function activeCache()
 {
     /*
      * There is a catch 22 with caching the config file. We need
      * an external way to define the cache layer before needing it
      * in the config.
      */
     if (defined('CACHE_METHOD_OVERRIDE')) {
         $ActiveCache = CACHE_METHOD_OVERRIDE;
     } else {
         $ActiveCache = C('Cache.Method', false);
     }
     // This should only fire when cache is loading automatically
     if (!func_num_args() && Gdn::pluginManager() instanceof Gdn_PluginManager) {
         Gdn::pluginManager()->EventArguments['ActiveCache'] =& $ActiveCache;
         Gdn::pluginManager()->fireEvent('BeforeActiveCache');
     }
     return $ActiveCache;
 }
 /**
  *
  *
  * @return Smarty The smarty object used for rendering.
  */
 public function smarty()
 {
     if (is_null($this->_Smarty)) {
         $Smarty = Gdn::factory('Smarty');
         $Smarty->cache_dir = PATH_CACHE . DS . 'Smarty' . DS . 'cache';
         $Smarty->compile_dir = PATH_CACHE . DS . 'Smarty' . DS . 'compile';
         $Smarty->plugins_dir[] = PATH_LIBRARY . DS . 'vendors' . DS . 'SmartyPlugins';
         //         Gdn::PluginManager()->Trace = TRUE;
         Gdn::pluginManager()->callEventHandlers($Smarty, 'Gdn_Smarty', 'Init');
         $this->_Smarty = $Smarty;
     }
     return $this->_Smarty;
 }
Example #28
0
 /**
  *
  *
  * @param $Sender
  * @throws Exception
  */
 public function pluginController_dismissGettingStarted_create($Sender)
 {
     Gdn::pluginManager()->disablePlugin('GettingStarted');
     echo 'TRUE';
 }
 /**
  * When enabled, disable other known editors that may clash with this one.
  *
  * If editor is loaded, then the other editors loaded after, there are CSS rules that hide them.
  * This way, the editor plugin always takes precedence.
  */
 public function setup()
 {
     $pluginEditors = array('cleditor', 'ButtonBar', 'Emotify', 'FileUpload');
     foreach ($pluginEditors as $pluginName) {
         Gdn::pluginManager()->disablePlugin($pluginName);
     }
     touchConfig(array('Garden.MobileInputFormatter' => 'TextEx', 'Plugins.editor.ForceWysiwyg' => false));
     $this->structure();
 }
Example #30
0
 /**
  *
  *
  * @return bool|null
  * @throws Exception
  */
 public function save()
 {
     if (!$this->Dirty) {
         return null;
     }
     $this->EventArguments['ConfigDirty'] =& $this->Dirty;
     $this->EventArguments['ConfigNoSave'] = false;
     $this->EventArguments['ConfigType'] = $this->Type;
     $this->EventArguments['ConfigSource'] = $this->Source;
     $this->EventArguments['ConfigData'] = $this->Settings;
     $this->fireEvent('BeforeSave');
     if ($this->EventArguments['ConfigNoSave']) {
         $this->Dirty = false;
         return true;
     }
     // Check for and fire callback if one exists
     if ($this->Callback && is_callable($this->Callback)) {
         $CallbackOptions = array();
         if (!is_array($this->CallbackOptions)) {
             $this->CallbackOptions = array();
         }
         $CallbackOptions = array_merge($CallbackOptions, $this->CallbackOptions, array('ConfigDirty' => $this->Dirty, 'ConfigType' => $this->Type, 'ConfigSource' => $this->Source, 'ConfigData' => $this->Settings, 'SourceObject' => $this));
         $ConfigSaved = call_user_func($this->Callback, $CallbackOptions);
         if ($ConfigSaved) {
             $this->Dirty = false;
             return true;
         }
     }
     switch ($this->Type) {
         case 'file':
             if (empty($this->Source)) {
                 trigger_error(errorMessage('You must specify a file path to be saved.', 'Configuration', 'Save'), E_USER_ERROR);
             }
             $CheckWrite = $this->Source;
             if (!file_exists($CheckWrite)) {
                 $CheckWrite = dirname($CheckWrite);
             }
             if (!is_writable($CheckWrite)) {
                 throw new Exception(sprintf(t("Unable to write to config file '%s' when saving."), $this->Source));
             }
             $Group = $this->Group;
             $Data =& $this->Settings;
             ksort($Data);
             // Check for the case when the configuration is the group.
             if (is_array($Data) && count($Data) == 1 && array_key_exists($Group, $Data)) {
                 $Data = $Data[$Group];
             }
             // Do a sanity check on the config save.
             if ($this->Source == Gdn::config()->defaultPath()) {
                 // Log root config changes
                 try {
                     $LogData = $this->Initial;
                     $LogData['_New'] = $this->Settings;
                     LogModel::insert('Edit', 'Configuration', $LogData);
                 } catch (Exception $Ex) {
                 }
                 if (!isset($Data['Database'])) {
                     if ($Pm = Gdn::pluginManager()) {
                         $Pm->EventArguments['Data'] = $Data;
                         $Pm->EventArguments['Backtrace'] = debug_backtrace();
                         $Pm->fireEvent('ConfigError');
                     }
                     return false;
                 }
             }
             // Write config data to string format, ready for saving
             $FileContents = Gdn_Configuration::format($Data, array('VariableName' => $Group, 'WrapPHP' => true, 'ByLine' => true));
             if ($FileContents === false) {
                 trigger_error(errorMessage('Failed to define configuration file contents.', 'Configuration', 'Save'), E_USER_ERROR);
             }
             // Save to cache if we're into that sort of thing
             $FileKey = sprintf(Gdn_Configuration::CONFIG_FILE_CACHE_KEY, $this->Source);
             if ($this->Configuration && $this->Configuration->caching() && Gdn::cache()->type() == Gdn_Cache::CACHE_TYPE_MEMORY && Gdn::cache()->activeEnabled()) {
                 $CachedConfigData = Gdn::cache()->store($FileKey, $Data, array(Gdn_Cache::FEATURE_NOPREFIX => true, Gdn_Cache::FEATURE_EXPIRY => 3600));
             }
             $TmpFile = tempnam(PATH_CONF, 'config');
             $Result = false;
             if (file_put_contents($TmpFile, $FileContents) !== false) {
                 chmod($TmpFile, 0775);
                 $Result = rename($TmpFile, $this->Source);
             }
             if ($Result) {
                 if (function_exists('apc_delete_file')) {
                     // This fixes a bug with some configurations of apc.
                     @apc_delete_file($this->Source);
                 } elseif (function_exists('opcache_invalidate')) {
                     @opcache_invalidate($this->Source);
                 }
             }
             $this->Dirty = false;
             return $Result;
             break;
         case 'json':
         case 'array':
         case 'string':
             /**
              * How would these even save? String config data must be handled by
              * an event hook or callback, if at all.
              */
             $this->Dirty = false;
             return false;
             break;
     }
 }