/** * Constructor * * @param string $TableName * @param string $style The style key to use. */ public function __construct($TableName = '', $style = '') { if ($TableName != '') { $TableModel = new Gdn_Model($TableName); $this->setModel($TableModel); } if ($style === '') { $themeInfo = Gdn::themeManager()->getThemeInfo(Gdn::themeManager()->currentTheme()); $style = val('ControlStyle', $themeInfo); } $this->setStyles($style); // Get custom error class $this->ErrorClass = c('Garden.Forms.InlineErrorClass', 'Error'); parent::__construct(); }
/** * 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; }
/** * Test to see if a plugin throws fatal errors. */ public function testPlugin($PluginName, &$Validation, $Setup = false) { // Make sure that the plugin's requirements are met // Required Plugins $PluginInfo = $this->getPluginInfo($PluginName); $RequiredPlugins = val('RequiredPlugins', $PluginInfo, false); CheckRequirements($PluginName, $RequiredPlugins, $this->enabledPlugins(), 'plugin'); // Required Themes $EnabledThemes = Gdn::themeManager()->enabledThemeInfo(); $RequiredThemes = ArrayValue('RequiredTheme', $PluginInfo, false); CheckRequirements($PluginName, $RequiredThemes, $EnabledThemes, 'theme'); // Required Applications $EnabledApplications = Gdn::applicationManager()->enabledApplications(); $RequiredApplications = ArrayValue('RequiredApplications', $PluginInfo, false); CheckRequirements($PluginName, $RequiredApplications, $EnabledApplications, 'application'); // Include the plugin, instantiate it, and call its setup method $PluginClassName = ArrayValue('ClassName', $PluginInfo, false); $PluginFolder = ArrayValue('Folder', $PluginInfo, false); if ($PluginFolder == '') { throw new Exception(T('The plugin folder was not properly defined.')); } $this->pluginHook($PluginName, self::ACTION_ENABLE, $Setup); // If setup succeeded, register any specified permissions $PermissionName = val('RegisterPermissions', $PluginInfo, false); if ($PermissionName != false) { $PermissionModel = Gdn::permissionModel(); $PermissionModel->define($PermissionName); } return true; }
Gdn::factoryInstall('Dummy', 'Gdn_Dummy'); /** * 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();
/** * Show a preview of a theme. * * @since 2.0.0 * @access public * @param string $ThemeName Unique ID. */ public function previewTheme($ThemeName = '') { $this->permission('Garden.Settings.Manage'); $ThemeInfo = Gdn::themeManager()->getThemeInfo($ThemeName); $PreviewThemeName = $ThemeName; $PreviewThemeFolder = val('Folder', $ThemeInfo); $IsMobile = val('IsMobile', $ThemeInfo); // If we failed to get the requested theme, cancel preview if ($ThemeInfo === false) { $PreviewThemeName = ''; $PreviewThemeFolder = ''; } Gdn::session()->setPreference(array('PreviewThemeName' => $PreviewThemeName, 'PreviewThemeFolder' => $PreviewThemeFolder, 'PreviewIsMobile' => $IsMobile)); redirect('/'); }
/** * Generate an e-tag for the application from the versions of all of its enabled applications/plugins. * * @return string etag **/ public static function eTag() { $data = []; $data['vanilla-core-' . APPLICATION_VERSION] = true; $plugins = Gdn::pluginManager()->enabledPlugins(); foreach ($plugins as $info) { $data[strtolower("{$info['Index']}-plugin-{$info['Version']}")] = true; } $applications = Gdn::applicationManager()->enabledApplications(); foreach ($applications as $info) { $data[strtolower("{$info['Index']}-app-{$info['Version']}")] = true; } // Add the desktop theme version. $info = Gdn::themeManager()->getThemeInfo(Gdn::themeManager()->desktopTheme()); if (!empty($info)) { $version = val('Version', $info, 'v0'); $data[strtolower("{$info['Index']}-theme-{$version}")] = true; if (Gdn::controller()->Theme && Gdn::controller()->ThemeOptions) { $filenames = valr('Styles.Value', Gdn::controller()->ThemeOptions); $data[$filenames] = true; } } // Add the mobile theme version. $info = Gdn::themeManager()->getThemeInfo(Gdn::themeManager()->mobileTheme()); if (!empty($info)) { $version = val('Version', $info, 'v0'); $data[strtolower("{$info['Index']}-theme-{$version}")] = true; } Gdn::pluginManager()->EventArguments['ETagData'] =& $data; $suffix = ''; Gdn::pluginManager()->EventArguments['Suffix'] =& $suffix; Gdn::pluginManager()->fireAs('AssetModel')->fireEvent('GenerateETag'); unset(Gdn::pluginManager()->EventArguments['ETagData']); ksort($data); $result = substr(md5(implode(',', array_keys($data))), 0, 8) . $suffix; return $result; }
/** * Closes theme preview. * * @since 2.0.0 * @access public * * @param string $previewThemeFolder * @param string $transientKey */ public function cancelPreview($previewThemeFolder = '', $transientKey = '') { $this->permission('Garden.Settings.Manage'); $isMobile = false; if (Gdn::session()->validateTransientKey($transientKey)) { $themeInfo = Gdn::themeManager()->getThemeInfo($previewThemeFolder); $isMobile = val('IsMobile', $themeInfo); if ($isMobile) { Gdn::session()->setPreference(['PreviewMobileThemeFolder' => '', 'PreviewMobileThemeName' => '']); } else { Gdn::session()->setPreference(['PreviewThemeFolder' => '', 'PreviewThemeName' => '']); } } if ($isMobile) { redirect('settings/mobilethemes'); } else { redirect('settings/themes'); } }
/** * Resolve relative static resources into full paths * * This method is used to translate CSS, Js and Template relative file lists * into absolute paths. * * Element values should conform to the following format: * * [] => array( * 'FileName' => // filename (relative, absolute, or URL) * 'AppFolder' => // optional application folder to target (default controller app) * ); * * @param array $resourceList * @param string $stub * @param array $options Optional. List of check options. * - 'GlobalLibrary' // Check $Stub/library in global section * - 'StripRoot' // Strip PATH_ROOT from final results * - 'CDNS' // List of external CDN replacements * @param array $checkLocations Optional. List of locations to check. * - 'themes' * - 'plugins' * - 'applications' * - 'global' */ public static function resolveStaticResources($resourceList, $stub, $options = null, $checkLocations = null) { // All locations by default if (!is_array($checkLocations)) { $checkLocations = array('themes', 'plugins', 'applications', 'global'); } // Default options $defaultOptions = array('GlobalLibrary' => true, 'StripRoot' => true, 'CDNS' => array(), 'AutoVersion' => true); if (!is_array($options)) { $options = array(); } $options = array_merge($defaultOptions, $options); // Parse options $checkGlobalLibrary = val('GlobalLibrary', $options); $stripRoot = val('StripRoot', $options); $autoDetectVersion = val('AutoVersion', $options); // See if we're allowing any CDN replacements $CDNs = val('CDNS', $options, array()); // Pre-get controller info $controllerAppFolder = false; $controllerTheme = false; if (Gdn::Controller() instanceof Gdn_Controller) { $controllerAppFolder = Gdn::controller()->ApplicationFolder; $controllerTheme = Gdn::controller()->Theme; } $fileList = array(); foreach ($resourceList as $index => $resourceInfo) { $resourceFile = $resourceInfo['FileName']; $resourceFolder = val('AppFolder', $resourceInfo); $resourceOptions = (array) val('Options', $resourceInfo, false); if ($resourceFile === false) { if (!$resourceOptions) { continue; } $rawCSS = val('Css', $resourceOptions, false); if (!$rawCSS) { continue; } $cssHash = md5($rawCSS); $fileList[$resourceFolder] = array('options' => $resourceOptions); continue; } $skipFileCheck = false; // Resolve CDN resources if (array_key_exists($resourceFile, $CDNs)) { $resourceFile = $CDNs[$resourceFile]; } if (strpos($resourceFile, '//') !== false) { // This is a link to an external file. $skipFileCheck = true; $testPaths = array($resourceFile); } elseif (strpos($resourceFile, '/') === 0) { // A direct path to the file was given. $testPaths = array(paths(PATH_ROOT, $resourceFile)); } elseif (strpos($resourceFile, '~') === 0) { $skipFileCheck = true; $resourceFile = substr($resourceFile, 1); $testPaths = array(paths(PATH_ROOT, $resourceFile)); } else { // Relative path $appFolder = val('AppFolder', $resourceInfo, false); if ($appFolder == '') { $appFolder = $controllerAppFolder; } if ($appFolder == 'false') { $appFolder = false; } // Resources can come from: // - a theme // - an application // - a plugin // - global garden resource-specific folder // - global garden resource-specific library folder $testPaths = array(); // Theme if (in_array('themes', $checkLocations) && $controllerTheme) { // Application-specific theme override if ($appFolder) { $testPaths[] = paths(PATH_THEMES, $controllerTheme, $appFolder, $stub, $resourceFile); } // Garden-wide theme override $testPaths[] = paths(PATH_THEMES, $controllerTheme, $stub, $resourceFile); } // Application or plugin $isPluginFolder = stringBeginsWith(trim($appFolder, '/'), 'plugins/', true, false); if ($isPluginFolder) { $pluginFolder = stringBeginsWith(trim($appFolder, '/'), 'plugins/', true, true); } if (in_array('plugins', $checkLocations) && $isPluginFolder) { // Plugin $testPaths[] = paths(PATH_PLUGINS, $pluginFolder, $stub, $resourceFile); $testPaths[] = paths(PATH_PLUGINS, $pluginFolder, $resourceFile); } if (in_array('applications', $checkLocations) && !$isPluginFolder) { // Application if ($appFolder) { $testPaths[] = paths(PATH_APPLICATIONS, $appFolder, $stub, $resourceFile); } // Dashboard app is added by default if ($appFolder != 'dashboard') { $testPaths[] = paths(PATH_APPLICATIONS, 'dashboard', $stub, $resourceFile); } } if (in_array('global', $checkLocations)) { // Global folder. eg. root/js/ $testPaths[] = paths(PATH_ROOT, $stub, $resourceFile); if ($checkGlobalLibrary) { // Global library folder. eg. root/js/library/ $testPaths[] = paths(PATH_ROOT, $stub, 'library', $resourceFile); } } } // Find the first file that matches the path. $resourcePath = false; if (!$skipFileCheck) { foreach ($testPaths as $glob) { $paths = safeGlob($glob); if (is_array($paths) && count($paths) > 0) { $resourcePath = $paths[0]; break; } } // Get version $version = val('Version', $resourceInfo, false); // If a path was matched, make sure it has a version if ($resourcePath && !$version && $autoDetectVersion) { // Theme file if (!$version && preg_match('`themes/([^/]+)/`i', $resourcePath, $matches)) { $themeName = $matches[1]; $themeInfo = Gdn::themeManager()->getThemeInfo($themeName); $version = val('Version', $themeInfo); $versionSource = "theme {$themeName}"; } // Plugin file if (!$version && preg_match('`plugins/([^/]+)/`i', $resourcePath, $matches)) { $pluginName = $matches[1]; $pluginInfo = Gdn::pluginManager()->getPluginInfo($pluginName, Gdn_PluginManager::ACCESS_PLUGINNAME); $version = val('Version', $pluginInfo); $versionSource = "plugin {$pluginName}"; } // Application file if (!$version && preg_match('`applications/([^/]+)/`i', $resourcePath, $matches)) { $applicationName = $matches[1]; $applicationInfo = Gdn::applicationManager()->getApplicationInfo($applicationName); $version = val('Version', $applicationInfo); $versionSource = "app {$applicationName}"; } } } else { $version = null; } // Global file if (!$version) { $version = APPLICATION_VERSION; } // If a path was succesfully matched if ($resourcePath !== false || $skipFileCheck) { // We enact SkipFileCheck for virtual paths, targeting controllers // perhaps, or full URLs from the CDN resolver. if ($skipFileCheck) { $resourcePath = array_pop($testPaths); } // Strip PATH_ROOT from absolute path $resourceResolved = $resourcePath; if ($stripRoot) { $resourceResolved = str_replace(array(PATH_ROOT, DS), array('', '/'), $resourcePath); } // Bring options into response structure $resource = array('path' => $resourcePath); $resourceOptions = (array) val('Options', $resourceInfo, array()); touchValue('version', $resource, $version); if ($resourceOptions) { touchValue('options', $resource, $resourceOptions); } $fileList[$resourceResolved] = $resource; } } return $fileList; }
/** * Lookup the path to a JS file and return its info array * * @param string $filename name/relative path to js file * @param string $folder optional. app or plugin folder to search * @param string $themeType mobile or desktop * @return array|bool */ public static function jsPath($filename, $folder = '', $themeType = '') { if (!$themeType) { $themeType = isMobile() ? 'mobile' : 'desktop'; } // 1. Check for a url. if (isUrl($filename)) { return array($filename, $filename); } $paths = array(); // 2. Check for a full path. if (strpos($filename, '/') === 0) { $filename = ltrim($filename, '/'); // Direct path was given $filename = "/{$filename}"; $path = PATH_ROOT . $filename; if (file_exists($path)) { deprecated("AssetModel::JsPath() with direct paths"); return array($path, $filename); } return false; } // 3. Check the theme. $theme = Gdn::themeManager()->themeFromType($themeType); if ($theme) { $path = "/{$theme}/js/{$filename}"; $paths[] = array(PATH_THEMES . $path, "/themes{$path}"); } // 4. Static, Plugin, or App relative file if ($folder) { if (in_array($folder, array('resources', 'static'))) { $path = "/resources/js/{$filename}"; $paths[] = array(PATH_ROOT . $path, $path); // A plugin-relative path was given } elseif (stringBeginsWith($folder, 'plugins/')) { $folder = substr($folder, strlen('plugins/')); $path = "/{$folder}/js/{$filename}"; $paths[] = array(PATH_PLUGINS . $path, "/plugins{$path}"); // Allow direct-to-file links for plugins $paths[] = array(PATH_PLUGINS . "/{$folder}/{$filename}", "/plugins/{$folder}/{$filename}", true); // deprecated // An app-relative path was given } else { // App-relative path under the theme if ($theme) { $path = "/{$theme}/{$folder}/js/{$filename}"; $paths[] = array(PATH_THEMES . $path, "/themes{$path}"); } $path = "/{$folder}/js/{$filename}"; $paths[] = array(PATH_APPLICATIONS . $path, "/applications{$path}"); } } // 5. Check the global js folder. $paths[] = array(PATH_ROOT . "/js/{$filename}", "/js/{$filename}"); $paths[] = array(PATH_ROOT . "/js/library/{$filename}", "/js/library/{$filename}"); foreach ($paths as $info) { if (file_exists($info[0])) { if (!empty($info[2])) { // This path is deprecated. unset($info[2]); deprecated("The js file '{$filename}' in folder '{$folder}'"); } return $info; } } if (!stringEndsWith($filename, 'custom.js')) { trace("Could not find file '{$filename}' in folder '{$folder}'."); } return false; }
/** * Generate an e-tag for the application from the versions of all of its enabled applications/plugins. * * @return string etag **/ public static function eTag() { $data = []; $data['vanilla-core-' . APPLICATION_VERSION] = true; // Look through the enabled addons. /* @var Addon $addon */ foreach (Gdn::addonManager()->getEnabled() as $addon) { if ($addon->getType() == Addon::TYPE_THEME) { // Themes have to figured out separately. continue; } $key = $addon->getKey(); $version = $addon->getVersion(); $type = $addon->getType(); $data[strtolower("{$key}-{$type}-{$version}")] = true; } // Add the desktop theme version. $themes = ['' => Gdn::addonManager()->lookupTheme(Gdn::themeManager()->desktopTheme()), 'Mobile' => Gdn::addonManager()->lookupTheme(Gdn::themeManager()->mobileTheme())]; foreach ($themes as $optionsPx => $theme) { if (!$theme instanceof Addon) { continue; } $data[$theme->getKey() . '-theme-' . $theme->getVersion()] = true; // Look for theme options. $options = c("Garden.{$optionsPx}ThemeOptions"); if (!empty($options)) { $data[valr('Styles.Value', $options)] = true; } } $info = Gdn::themeManager()->getThemeInfo(Gdn::themeManager()->desktopTheme()); if (!empty($info)) { $version = val('Version', $info, 'v0'); $data[strtolower("{$info['Index']}-theme-{$version}")] = true; if (Gdn::controller()->Theme && Gdn::controller()->ThemeOptions) { $filenames = valr('Styles.Value', Gdn::controller()->ThemeOptions); $data[$filenames] = true; } } // Add the mobile theme version. $info = Gdn::themeManager()->getThemeInfo(Gdn::themeManager()->mobileTheme()); if (!empty($info)) { $version = val('Version', $info, 'v0'); $data[strtolower("{$info['Index']}-theme-{$version}")] = true; } Gdn::pluginManager()->EventArguments['ETagData'] =& $data; $suffix = ''; Gdn::pluginManager()->EventArguments['Suffix'] =& $suffix; Gdn::pluginManager()->fireAs('AssetModel')->fireEvent('GenerateETag'); unset(Gdn::pluginManager()->EventArguments['ETagData']); ksort($data); $result = substr(md5(implode(',', array_keys($data))), 0, 8) . $suffix; return $result; }
/** * Get a version string for a given asset. * * @param string $destination The path of the asset. * @param string|null $version A known version for the asset or **null** to grab it from the addon's info array. * @return string Returns a version string. */ function assetVersion($destination, $version = null) { static $gracePeriod = 90; // 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': $applicationInfo = Gdn::applicationManager()->getApplicationInfo(ucfirst($key)); $version = val('Version', $applicationInfo, $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; } } } // Add a timestamp component to the version if available. if ($timestamp = c('Garden.Deployed')) { $graced = $timestamp + $gracePeriod; if (time() >= $graced) { $timestamp = $graced; } $version .= '.' . dechex($timestamp); } return $version; }