public function actionGenerate() { $sectionUrls = $this->_generateSections(); $categoryUrls = $this->_generateCategories(); $productTypeUrls = $this->_generateProductTypes(); HeaderHelper::setContentTypeByExtension('xml'); HeaderHelper::setHeader(array('charset' => 'utf-8')); $path = craft()->path->getPluginsPath() . 'seo/templates'; craft()->templates->setTemplatesPath($path); $this->renderTemplate('_sitemap', array('sectionUrls' => $sectionUrls, 'categoryUrls' => $categoryUrls, 'productTypeUrls' => $productTypeUrls, 'customUrls' => array_key_exists('customUrls', $this->sitemap) ? $this->sitemap['customUrls'] : [])); }
/** * Forgot password action */ public function actionResetPassword() { try { $this->requirePostRequest(); $username = craft()->request->getRequiredPost('username'); $user = craft()->users->getUserByUsernameOrEmail($username); if ($user) { craft()->users->sendPasswordResetEmail($user); } $this->returnJson(array('message' => Craft::t('Email has been sent if address exists'))); } catch (HttpException $e) { HeaderHelper::setHeader('HTTP/ ' . $e->statusCode); $this->returnErrorJson($e->getMessage()); } }
/** * Sends a resource back to the browser. * * @param string $path * * @throws HttpException * @return null */ public function sendResource($path) { if (PathHelper::ensurePathIsContained($path) === false) { throw new HttpException(404); } $cachedPath = $this->getCachedResourcePath($path); if ($cachedPath) { if ($cachedPath == ':(') { // 404 $realPath = false; } else { // We've got it already $realPath = $cachedPath; } } else { // We don't have a cache of the file system path, so let's get it $realPath = $this->getResourcePath($path); // Now cache it $this->cacheResourcePath($path, $realPath); } if ($realPath === false || !IOHelper::fileExists($realPath)) { throw new HttpException(404); } // If there is a timestamp and HTTP_IF_MODIFIED_SINCE exists, check the timestamp against requested file's last // modified date. If the last modified date is less than the timestamp, return a 304 not modified and let the // browser serve it from cache. $timestamp = craft()->request->getParam($this->dateParam, null); if ($timestamp !== null && array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) { $requestDate = DateTime::createFromFormat('U', $timestamp); $lastModifiedFileDate = IOHelper::getLastTimeModified($realPath); if ($lastModifiedFileDate && $lastModifiedFileDate <= $requestDate) { // Let the browser serve it from cache. HeaderHelper::setHeader('HTTP/1.1 304 Not Modified'); craft()->end(); } } // Note that $content may be empty -- they could be requesting a blank text file or something. It doens't matter. // No need to throw a 404. $content = IOHelper::getFileContents($realPath); // Normalize URLs in CSS files $mimeType = IOHelper::getMimeTypeByExtension($realPath); if (mb_strpos($mimeType, 'css') !== false) { $content = preg_replace_callback('/(url\\(([\'"]?))(.+?)(\\2\\))/', array(&$this, '_normalizeCssUrl'), $content); } if (!craft()->config->get('useXSendFile')) { $options['forceDownload'] = false; if (craft()->request->getQuery($this->dateParam)) { $options['cache'] = true; } craft()->request->sendFile($realPath, $content, $options); } else { craft()->request->xSendFile($realPath); } // You shall not pass. craft()->end(); }
/** * Processes the request. * * @throws HttpException * @return null */ public function processRequest() { // If this is a resource request, we should respond with the resource ASAP. $this->_processResourceRequest(); $configService = $this->config; // If we're not in devMode, or it's a 'dontExtendSession' request, we're going to remove some logging routes. if (!$configService->get('devMode') || craft()->isInstalled() && !$this->userSession->shouldExtendSession()) { $this->log->removeRoute('WebLogRoute'); $this->log->removeRoute('ProfileLogRoute'); } // Additionally, we don't want these in the log files at all. if (craft()->isInstalled() && !$this->userSession->shouldExtendSession()) { $this->log->removeRoute('FileLogRoute'); } // If this is a CP request, prevent robots from indexing/following the page // (see https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag) if ($this->request->isCpRequest()) { HeaderHelper::setHeader(array('X-Robots-Tag' => 'none')); HeaderHelper::setHeader(array('X-Frame-Options' => 'SAMEORIGIN')); HeaderHelper::setHeader(array('X-Content-Type-Options' => 'nosniff')); } // Send the X-Powered-By header? if ($configService->get('sendPoweredByHeader')) { HeaderHelper::setHeader(array('X-Powered-By' => 'Craft CMS')); } else { // In case PHP is already setting one HeaderHelper::removeHeader('X-Powered-By'); } // Validate some basics on the database configuration file. $this->validateDbConfigFile(); // Process install requests $this->_processInstallRequest(); // If the system in is maintenance mode and it's a site request, throw a 503. if ($this->isInMaintenanceMode() && $this->request->isSiteRequest()) { throw new HttpException(503); } // Check if the app path has changed. If so, run the requirements check again. $this->_processRequirementsCheck(); // Makes sure that the uploaded files are compatible with the current database schema if (!$this->updates->isSchemaVersionCompatible()) { if ($this->request->isCpRequest()) { $version = $this->getVersion(); $build = $this->getBuild(); $url = "https://download.craftcdn.com/craft/{$version}/{$version}.{$build}/Craft-{$version}.{$build}.zip"; throw new HttpException(200, Craft::t('Craft does not support backtracking to this version. Please upload Craft {url} or later.', array('url' => '[' . $build . '](' . $url . ')'))); } else { throw new HttpException(503); } } // isCraftDbMigrationNeeded will return true if we're in the middle of a manual or auto-update for Craft itself. // If we're in maintenance mode and it's not a site request, show the manual update template. if ($this->updates->isCraftDbMigrationNeeded() || $this->isInMaintenanceMode() && $this->request->isCpRequest() || $this->request->getActionSegments() == array('update', 'cleanUp') || $this->request->getActionSegments() == array('update', 'rollback')) { $this->_processUpdateLogic(); } // If there's a new version, but the schema hasn't changed, just update the info table if ($this->updates->hasCraftBuildChanged()) { $this->updates->updateCraftVersionInfo(); } // If the system is offline, make sure they have permission to be here $this->_enforceSystemStatusPermissions(); // Load the plugins $this->plugins->loadPlugins(); // Check if a plugin needs to update the database. if ($this->updates->isPluginDbUpdateNeeded()) { $this->_processUpdateLogic(); } // If this is a non-login, non-validate, non-setPassword CP request, make sure the user has access to the CP if ($this->request->isCpRequest() && !($this->request->isActionRequest() && $this->_isSpecialCaseActionRequest())) { // Make sure the user has access to the CP $this->userSession->requireLogin(); $this->userSession->requirePermission('accessCp'); // If they're accessing a plugin's section, make sure that they have permission to do so $firstSeg = $this->request->getSegment(1); if ($firstSeg) { $plugin = $plugin = $this->plugins->getPlugin($firstSeg); if ($plugin) { $this->userSession->requirePermission('accessPlugin-' . $plugin->getClassHandle()); } } } // If this is an action request, call the controller $this->_processActionRequest(); // If we're still here, finally let UrlManager do it's thing. parent::processRequest(); }
/** * Attempts to closes the connection with the HTTP client, without ending PHP script execution. * * This method relies on [flush()](http://php.net/manual/en/function.flush.php), which may not actually work if * mod_deflate or mod_gzip is installed, or if this is a Win32 server. * * @param string|null $content Any content that should be included in the response body. * * @see http://stackoverflow.com/a/141026 * @throws Exception An exception will be thrown if content has already been output. * @return null */ public function close($content = '') { // Make sure nothing has been output yet if (headers_sent()) { throw new Exception(Craft::t('HttpRequestService::close() cannot be called after content has been output.')); } // Prevent the script from ending when the browser closes the connection ignore_user_abort(true); // Prepend any current OB content while (ob_get_length() !== false) { // If ob_start() didn't have the PHP_OUTPUT_HANDLER_CLEANABLE flag, ob_get_clean() will cause a PHP notice // and return false. $obContent = @ob_get_clean(); if ($obContent !== false) { $content = $obContent . $content; } else { break; } } // Send the content ob_start(); echo $content; $size = ob_get_length(); // Tell the browser to close the connection HeaderHelper::setHeader(array('Connection' => 'close', 'Content-Length' => $size)); // Output the content, flush it to the browser, and close out the session ob_end_flush(); flush(); // Close the session. craft()->session->close(); // In case we're running on php-fpm (https://secure.php.net/manual/en/book.fpm.php) if (function_exists("fastcgi_finish_request")) { fastcgi_finish_request(); } }
/** * Attempts to closes the connection with the HTTP client, without ending PHP script execution. * * This method relies on [flush()](http://php.net/manual/en/function.flush.php), which may not actually work if * mod_deflate or mod_gzip is installed, or if this is a Win32 server. * * @param string|null $content Any content that should be included in the response body. * * @see http://stackoverflow.com/a/141026 * @throws Exception An exception will be thrown if content has already been output. * @return null */ public function close($content = '') { // Make sure nothing has been output yet if (headers_sent()) { throw new Exception(Craft::t('HttpRequestService::close() cannot be called after content has been output.')); } // Prevent the script from ending when the browser closes the connection ignore_user_abort(true); // Prepend any current OB content while (ob_get_length() !== false) { // If ob_start() didn't have the PHP_OUTPUT_HANDLER_CLEANABLE flag, ob_get_clean() will cause a PHP notice // and return false. $obContent = @ob_get_clean(); if ($obContent !== false) { $content = $obContent . $content; } else { break; } } // Send the content ob_start(); echo $content; $size = ob_get_length(); // Tell the browser to close the connection HeaderHelper::setHeader(array('Connection' => 'close', 'Content-Length' => $size)); // Output the content, flush it to the browser, and close out the session ob_end_flush(); flush(); // Borrowed from CHttpSession->close() because session_write_close can cause PHP notices in some situations. if (session_id() !== '') { @session_write_close(); } }
/** * Processes the request. * * @throws HttpException * @return null */ public function processRequest() { // If this is a resource request, we should respond with the resource ASAP $this->_processResourceRequest(); // If we're not in devMode, or it's a 'dontExtendSession' request, we're going to remove some logging routes. if (!$this->config->get('devMode') || craft()->isInstalled() && !$this->userSession->shouldExtendSession()) { $this->log->removeRoute('WebLogRoute'); $this->log->removeRoute('ProfileLogRoute'); } // Additionally, we don't want these in the log files at all. if (craft()->isInstalled() && !$this->userSession->shouldExtendSession()) { $this->log->removeRoute('FileLogRoute'); } // If this is a CP request, prevent robots from indexing/following the page // (see https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag) if ($this->request->isCpRequest()) { HeaderHelper::setHeader(array('X-Robots-Tag' => 'none')); } // Validate some basics on the database configuration file. $this->validateDbConfigFile(); // Process install requests $this->_processInstallRequest(); // If the system in is maintenance mode and it's a site request, throw a 503. if ($this->isInMaintenanceMode() && $this->request->isSiteRequest()) { throw new HttpException(503); } // Check if the app path has changed. If so, run the requirements check again. $this->_processRequirementsCheck(); // These have been deprecated in PHP 6 in favor of default_charset, which defaults to 'UTF-8' // http://php.net/manual/en/migration56.deprecated.php if (version_compare(PHP_VERSION, '6.0.0') < 0) { // Now that we've ran the requirements checker, set MB to use UTF-8 mb_internal_encoding('UTF-8'); mb_http_input('UTF-8'); mb_http_output('UTF-8'); } mb_detect_order('auto'); // Makes sure that the uploaded files are compatible with the current DB schema if (!$this->updates->isSchemaVersionCompatible()) { if ($this->request->isCpRequest()) { $version = $this->getVersion(); $build = $this->getBuild(); $url = "http://download.buildwithcraft.com/craft/{$version}/{$version}.{$build}/Craft-{$version}.{$build}.zip"; throw new HttpException(200, Craft::t('Craft does not support backtracking to this version. Please upload Craft {url} or later.', array('url' => '[' . $build . '](' . $url . ')'))); } else { throw new HttpException(503); } } // Set the edition components $this->_setEditionComponents(); // isCraftDbMigrationNeeded will return true if we're in the middle of a manual or auto-update for Craft itself. // If we're in maintenance mode and it's not a site request, show the manual update template. if ($this->updates->isCraftDbMigrationNeeded() || $this->isInMaintenanceMode() && $this->request->isCpRequest() || $this->request->getActionSegments() == array('update', 'cleanUp') || $this->request->getActionSegments() == array('update', 'rollback')) { $this->_processUpdateLogic(); } // If there's a new version, but the schema hasn't changed, just update the info table if ($this->updates->hasCraftBuildChanged()) { $this->updates->updateCraftVersionInfo(); } // If the system is offline, make sure they have permission to be here $this->_enforceSystemStatusPermissions(); // Load the plugins $this->plugins->loadPlugins(); // Check if a plugin needs to update the database. if ($this->updates->isPluginDbUpdateNeeded()) { $this->_processUpdateLogic(); } // If this is a non-login, non-validate, non-setPassword CP request, make sure the user has access to the CP if ($this->request->isCpRequest() && !($this->request->isActionRequest() && $this->_isSpecialCaseActionRequest())) { // Make sure the user has access to the CP $this->userSession->requireLogin(); $this->userSession->requirePermission('accessCp'); // If they're accessing a plugin's section, make sure that they have permission to do so $firstSeg = $this->request->getSegment(1); if ($firstSeg) { $plugin = $plugin = $this->plugins->getPlugin($firstSeg); if ($plugin) { $this->userSession->requirePermission('accessPlugin-' . $plugin->getClassHandle()); } } } // If this is an action request, call the controller $this->_processActionRequest(); // If we're still here, finally let UrlManager do it's thing. parent::processRequest(); }
/** * Handles DB connection errors. * * @param DbConnectException $exception * * @return null */ protected function handleDbConnectionError(DbConnectException $exception) { $this->_error = $data = array('code' => 'error', 'type' => get_class($exception), 'errorCode' => null, 'message' => $exception->getMessage(), 'file' => null, 'line' => null, 'trace' => '', 'traces' => null); if (!headers_sent()) { HeaderHelper::setHeader('HTTP/1.0 500 ' . $this->getHttpHeader(500, get_class($exception))); } $this->render('error', $data); }
/** * Renders a template, and either outputs or returns it. * * @param mixed $template The name of the template to load in a format supported by * {@link TemplatesService::findTemplate()}, or a {@link StringTemplate} object. * @param array $variables The variables that should be available to the template. * @param bool $return Whether to return the results, rather than output them. (Default is `false`.) * @param bool $processOutput Whether the output should be processed by {@link processOutput()}. * * @throws HttpException * @return mixed The rendered template if $return is set to `true`. */ public function renderTemplate($template, $variables = array(), $return = false, $processOutput = false) { if (($output = craft()->templates->render($template, $variables)) !== false) { if ($processOutput) { $output = $this->processOutput($output); } if ($return) { return $output; } else { // Set the MIME type for the request based on the matched template's file extension (unless the // Content-Type header was already set, perhaps by the template via the {% header %} tag) if (!HeaderHelper::isHeaderSet('Content-Type')) { // Safe to assume that findTemplate() will return an actual template path here, and not `false`. // If the template didn't exist, a TemplateLoaderException would have been thrown when calling // craft()->templates->render(). $templateFile = craft()->templates->findTemplate($template); $extension = IOHelper::getExtension($templateFile, 'html'); if ($extension == 'twig') { $extension = 'html'; } HeaderHelper::setContentTypeByExtension($extension); } // Set the charset header HeaderHelper::setHeader(array('charset' => 'utf-8')); // Are we serving HTML or XHTML? if (in_array(HeaderHelper::getMimeType(), array('text/html', 'application/xhtml+xml'))) { // Are there any head/foot nodes left in the queue? $headHtml = craft()->templates->getHeadHtml(); $footHtml = craft()->templates->getFootHtml(); if ($headHtml) { if (($endHeadPos = mb_stripos($output, '</head>')) !== false) { $output = mb_substr($output, 0, $endHeadPos) . $headHtml . mb_substr($output, $endHeadPos); } else { $output .= $headHtml; } } if ($footHtml) { if (($endBodyPos = mb_stripos($output, '</body>')) !== false) { $output = mb_substr($output, 0, $endBodyPos) . $footHtml . mb_substr($output, $endBodyPos); } else { $output .= $footHtml; } } } // Output it into a buffer, in case TasksService wants to close the connection prematurely ob_start(); echo $output; // End the request craft()->end(); } } else { throw new HttpException(404); } }
/** * @codeCoverageIgnore * * @param string $key * @param string $value */ protected function setHeader($key, $value) { HeaderHelper::setHeader($key . ': ' . $value); }