public function testAddCacheHeaders() { $body = "<html><head></head><body><h1>Mysite</h1></body></html>"; $response = new SS_HTTPResponse($body, 200); $this->assertEmpty($response->getHeader('Cache-Control')); HTTP::set_cache_age(30); HTTP::add_cache_headers($response); $this->assertNotEmpty($response->getHeader('Cache-Control')); // Ensure max-age is zero for development. Config::inst()->update('Director', 'environment_type', 'dev'); $response = new SS_HTTPResponse($body, 200); HTTP::add_cache_headers($response); $this->assertContains('max-age=0', $response->getHeader('Cache-Control')); // Ensure max-age setting is respected in production. Config::inst()->update('Director', 'environment_type', 'live'); $response = new SS_HTTPResponse($body, 200); HTTP::add_cache_headers($response); $this->assertContains('max-age=30', explode(', ', $response->getHeader('Cache-Control'))); $this->assertNotContains('max-age=0', $response->getHeader('Cache-Control')); // Still "live": Ensure header's aren't overridden if already set (using purposefully different values). $headers = array('Vary' => '*', 'Pragma' => 'no-cache', 'Cache-Control' => 'max-age=0, no-cache, no-store'); $response = new SS_HTTPResponse($body, 200); foreach ($headers as $name => $value) { $response->addHeader($name, $value); } HTTP::add_cache_headers($response); foreach ($headers as $name => $value) { $this->assertEquals($value, $response->getHeader($name)); } }
public function getJobStatus() { // Set headers HTTP::set_cache_age(0); HTTP::add_cache_headers($this->response); $this->response->addHeader('Content-Type', 'application/json')->addHeader('Content-Encoding', 'UTF-8')->addHeader('X-Content-Type-Options', 'nosniff'); // Format status $track = BrokenExternalPageTrackStatus::get_latest(); if ($track) { return json_encode(array('TrackID' => $track->ID, 'Status' => $track->Status, 'Completed' => $track->getCompletedPages(), 'Total' => $track->getTotalPages())); } }
public function handleRequest(SS_HTTPRequest $request, DataModel $model) { try { $this->pushCurrent(); $auth = $this->webserviceAuthenticator->authenticate($request); if (!$auth) { throw new WebServiceException(403, 'User not found'); } // borrowed from Controller $this->urlParams = $request->allParams(); $this->request = $request; $this->response = new SS_HTTPResponse(); $this->setDataModel($model); $this->extend('onBeforeInit'); $this->init(); $this->extend('onAfterInit'); if ($this->response->isFinished()) { $this->popCurrent(); return $this->response; } $response = $this->handleService($request, $model); if (self::has_curr()) { $this->popCurrent(); } if ($response instanceof SS_HTTPResponse) { $response->addHeader('Content-Type', 'application/' . $this->format); } HTTP::add_cache_headers($this->response); return $response; } catch (WebServiceException $exception) { $this->response = new SS_HTTPResponse(); $this->response->setStatusCode($exception->status); $this->response->setBody($this->ajaxResponse($exception->getMessage(), $exception->status)); } catch (SS_HTTPResponse_Exception $e) { $this->response = $e->getResponse(); $this->response->setBody($this->ajaxResponse($e->getMessage(), $e->getCode())); } catch (Exception $exception) { $code = 500; // check type explicitly in case the Restricted Objects module isn't installed if (class_exists('PermissionDeniedException') && $exception instanceof PermissionDeniedException) { $code = 403; } $this->response = new SS_HTTPResponse(); $this->response->setStatusCode($code); $this->response->setBody($this->ajaxResponse($exception->getMessage(), $code)); } return $this->response; }
public function testAddCacheHeaders() { $body = "<html><head></head><body><h1>Mysite</h1></body></html>"; $response = new SS_HTTPResponse($body, 200); $this->assertEmpty($response->getHeader('Cache-Control')); HTTP::set_cache_age(30); HTTP::add_cache_headers($response); $this->assertNotEmpty($response->getHeader('Cache-Control')); Config::inst()->update('Director', 'environment_type', 'dev'); HTTP::add_cache_headers($response); $this->assertContains('max-age=0', $response->getHeader('Cache-Control')); Config::inst()->update('Director', 'environment_type', 'live'); HTTP::add_cache_headers($response); $this->assertContains('max-age=30', explode(', ', $response->getHeader('Cache-Control'))); $this->assertNotContains('max-age=0', $response->getHeader('Cache-Control')); }
public function testConfigVary() { $body = "<html><head></head><body><h1>Mysite</h1></body></html>"; $response = new SS_HTTPResponse($body, 200); Config::inst()->update('Director', 'environment_type', 'live'); HTTP::set_cache_age(30); HTTP::add_cache_headers($response); $v = $response->getHeader('Vary'); $this->assertNotEmpty($v); $this->assertContains("Cookie", $v); $this->assertContains("X-Forwarded-Protocol", $v); $this->assertContains("User-Agent", $v); $this->assertContains("Accept", $v); Config::inst()->update('HTTP', 'vary', ''); $response = new SS_HTTPResponse($body, 200); HTTP::add_cache_headers($response); $v = $response->getHeader('Vary'); $this->assertEmpty($v); }
/** * @return SS_HTTPResponse */ public function onBeforeInit() { $config = SiteConfig::current_site_config(); // If Maintenance Mode is Off, skip processing if (!$config->MaintenanceMode) { return; } // Check if the visitor is Admin OR if they have an allowed IP. if (Permission::check('VIEW_SITE_MAINTENANCE_MODE') || Permission::check('ADMIN') || $this->hasAllowedIP()) { return; } // Are we already on the UtilityPage? If so, skip processing. if ($this->owner instanceof UtilityPage_Controller) { return; } // Fetch our utility page instance now. /** * @var Page $utilityPage */ $utilityPage = UtilityPage::get()->first(); if (!$utilityPage) { return; } // We need a utility page before we can do anything. // Are we configured to prevent redirection to the UtilityPage URL? if ($utilityPage->config()->DisableRedirect) { // Process the request internally to ensure that the URL is maintained // (instead of redirecting to the maintenance page's URL) and skip any further processing. $controller = ModelAsController::controller_for($utilityPage); $response = $controller->handleRequest(new SS_HTTPRequest('GET', ''), DataModel::inst()); HTTP::add_cache_headers($response); $response->output(); die; } // Default: Skip any further processing and immediately respond with a redirect to the UtilityPage. $response = new SS_HTTPResponse(); $response->redirect($utilityPage->AbsoluteLink(), 302); HTTP::add_cache_headers($response); $response->output(); die; }
/** * Output the feed to the browser. * * TODO: Pass $response object to ->outputToBrowser() to loosen dependence on global state for easier testing/prototyping so dev can inject custom SS_HTTPResponse instance. * * @return HTMLText */ public function outputToBrowser() { $prevState = Config::inst()->get('SSViewer', 'source_file_comments'); Config::inst()->update('SSViewer', 'source_file_comments', false); $response = Controller::curr()->getResponse(); if (is_int($this->lastModified)) { HTTP::register_modification_timestamp($this->lastModified); $response->addHeader("Last-Modified", gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT'); } if (!empty($this->etag)) { HTTP::register_etag($this->etag); } if (!headers_sent()) { HTTP::add_cache_headers(); $response->addHeader("Content-Type", "application/rss+xml; charset=utf-8"); } Config::inst()->update('SSViewer', 'source_file_comments', $prevState); return $this->renderWith($this->getTemplate()); }
/** * Output the feed to the browser */ public function outputToBrowser() { $prevState = SSViewer::get_source_file_comments(); SSViewer::set_source_file_comments(false); if (is_int($this->lastModified)) { HTTP::register_modification_timestamp($this->lastModified); header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT'); } if (!empty($this->etag)) { HTTP::register_etag($this->etag); } if (!headers_sent()) { HTTP::add_cache_headers(); header("Content-type: text/xml"); } SSViewer::set_source_file_comments($prevState); return $this->renderWith($this->getTemplate()); }
function run($requestParams) { if (isset($_GET['debug_profile'])) { Profiler::mark("Controller", "run"); } $this->pushCurrent(); $this->response = new HTTPResponse(); $this->requestParams = $requestParams; $this->action = isset($this->urlParams['Action']) ? str_replace("-", "_", $this->urlParams['Action']) : ""; if (!$this->action) { $this->action = 'index'; } // Check security on the controller if (!$this->checkAccessAction($this->action)) { user_error("Disallowed action: '{$this->action}' on controller '{$this->class}'", E_USER_ERROR); } // Init $this->baseInitCalled = false; $this->init(); if (!$this->baseInitCalled) { user_error("init() method on class '{$this->class}' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING); } // If we had a redirection or something, halt processing. if ($this->response->isFinished()) { $this->popCurrent(); return $this->response; } // Look at the action variables for forms $funcName = null; foreach ($this->requestParams as $paramName => $paramVal) { if (substr($paramName, 0, 7) == 'action_') { // Cleanup action_, _x and _y from image fields $funcName = preg_replace(array('/^action_/', '/_x$|_y$/'), '', $paramName); break; } } // Form handler if (isset($this->requestParams['executeForm']) && is_string($this->requestParams['executeForm'])) { if (isset($funcName)) { Form::set_current_action($funcName); } // Get the appropraite ocntroller: sometimes we want to get a form from another controller if (isset($this->requestParams['formController'])) { $formController = Director::getControllerForURL($this->requestParams['formController']); while (is_a($formController, 'NestedController')) { $formController = $formController->getNestedController(); } } else { $formController = $this; } // Create the form object $form = $formController; $formObjParts = explode('.', $this->requestParams['executeForm']); foreach ($formObjParts as $formMethod) { if (isset($_GET['debug_profile'])) { Profiler::mark("Calling {$formMethod}", "on {$form->class}"); } $form = $form->{$formMethod}(); if (isset($_GET['debug_profile'])) { Profiler::unmark("Calling {$formMethod}", "on {$form->class}"); } if (!$form) { break; } //user_error("Form method '" . $this->requestParams['executeForm'] . "' returns null in controller class '$this->class' ($_SERVER[REQUEST_URI])", E_USER_ERROR); } // Populate the form if (isset($_GET['debug_profile'])) { Profiler::mark("Controller", "populate form"); } if ($form) { $form->loadDataFrom($this->requestParams, true); // disregard validation if a single field is called if (!isset($_REQUEST['action_callfieldmethod'])) { $valid = $form->beforeProcessing(); if (!$valid) { $this->popCurrent(); return $this->response; } } else { $fieldcaller = $form->dataFieldByName($requestParams['fieldName']); if (is_a($fieldcaller, "TableListField")) { if ($fieldcaller->hasMethod('php')) { $valid = $fieldcaller->php($requestParams); if (!$valid) { exit; } } } } // If the action wasnt' set, choose the default on the form. if (!isset($funcName) && ($defaultAction = $form->defaultAction())) { $funcName = $defaultAction->actionName(); } if (isset($funcName)) { $form->setButtonClicked($funcName); } } else { user_error("No form (" . Session::get('CMSMain.currentPage') . ") returned by {$formController->class}->{$_REQUEST['executeForm']}", E_USER_WARNING); } if (isset($_GET['debug_profile'])) { Profiler::unmark("Controller", "populate form"); } if (!isset($funcName)) { user_error("No action button has been clicked in this form executon, and no default has been allowed", E_USER_ERROR); } // Protection against CSRF attacks if ($form->securityTokenEnabled()) { $securityID = Session::get('SecurityID'); if (!$securityID || !isset($this->requestParams['SecurityID']) || $securityID != $this->requestParams['SecurityID']) { // Don't show error on live sites, as spammers create a million of these if (!Director::isLive()) { trigger_error("Security ID doesn't match, possible CRSF attack.", E_USER_ERROR); } else { die; } } } // First, try a handler method on the controller if ($this->hasMethod($funcName) || !$form) { if (isset($_GET['debug_controller'])) { Debug::show("Found function {$funcName} on the controller"); } if (isset($_GET['debug_profile'])) { Profiler::mark("{$this->class}::{$funcName} (controller action)"); } $result = $this->{$funcName}($this->requestParams, $form); if (isset($_GET['debug_profile'])) { Profiler::unmark("{$this->class}::{$funcName} (controller action)"); } // Otherwise, try a handler method on the form object } else { if (isset($_GET['debug_controller'])) { Debug::show("Found function {$funcName} on the form object"); } if (isset($_GET['debug_profile'])) { Profiler::mark("{$form->class}::{$funcName} (form action)"); } $result = $form->{$funcName}($this->requestParams, $form); if (isset($_GET['debug_profile'])) { Profiler::unmark("{$form->class}::{$funcName} (form action)"); } } // Normal action } else { if (!isset($funcName)) { $funcName = $this->action; } if ($this->hasMethod($funcName)) { if (isset($_GET['debug_controller'])) { Debug::show("Found function {$funcName} on the {$this->class} controller"); } if (isset($_GET['debug_profile'])) { Profiler::mark("{$this->class}::{$funcName} (controller action)"); } $result = $this->{$funcName}($this->urlParams); if (isset($_GET['debug_profile'])) { Profiler::unmark("{$this->class}::{$funcName} (controller action)"); } } else { if (isset($_GET['debug_controller'])) { Debug::show("Running default action for {$funcName} on the {$this->class} controller"); } if (isset($_GET['debug_profile'])) { Profiler::mark("Controller::defaultAction({$funcName})"); } $result = $this->defaultAction($funcName, $this->urlParams); if (isset($_GET['debug_profile'])) { Profiler::unmark("Controller::defaultAction({$funcName})"); } } } // If your controller function returns an array, then add that data to the // default template if (is_array($result)) { $extended = $this->customise($result); $viewer = $this->getViewer($funcName); $result = $viewer->process($extended); } $this->response->setBody($result); if ($result) { ContentNegotiator::process($this->response); } // Set up HTTP cache headers HTTP::add_cache_headers($this->response); if (isset($_GET['debug_profile'])) { Profiler::unmark("Controller", "run"); } $this->popCurrent(); return $this->response; }
/** * Executes this controller, and return an {@link SS_HTTPResponse} object with the result. * * This method first does a few set-up activities: * - Push this controller ont to the controller stack - * see {@link Controller::curr()} for information about this. * - Call {@link init()} * - Defer to {@link RequestHandler->handleRequest()} to determine which action * should be executed * * Note: $requestParams['executeForm'] support was removed, * make the following change in your URLs: * "/?executeForm=FooBar" -> "/FooBar" * Also make sure "FooBar" is in the $allowed_actions of your controller class. * * Note: You should rarely need to overload run() - * this kind of change is only really appropriate for things like nested * controllers - {@link ModelAsController} and {@link RootURLController} * are two examples here. If you want to make more * orthodox functionality, it's better to overload {@link init()} or {@link index()}. * * Important: If you are going to overload handleRequest, * make sure that you start the method with $this->pushCurrent() * and end the method with $this->popCurrent(). * Failure to do this will create weird session errors. * * @param $request The {@link SS_HTTPRequest} object that is responsible * for distributing request parsing. * @return SS_HTTPResponse The response that this controller produces, * including HTTP headers such as redirection info */ public function handleRequest(SS_HTTPRequest $request, DataModel $model) { if (!$request) { user_error("Controller::handleRequest() not passed a request!", E_USER_ERROR); } $this->pushCurrent(); $this->urlParams = $request->allParams(); $this->request = $request; $this->response = new SS_HTTPResponse(); $this->setDataModel($model); $this->extend('onBeforeInit'); // Init $this->baseInitCalled = false; $this->init(); if (!$this->baseInitCalled) { user_error("init() method on class '{$this->class}' doesn't call Controller::init()." . "Make sure that you have parent::init() included.", E_USER_WARNING); } $this->extend('onAfterInit'); // If we had a redirection or something, halt processing. if ($this->response->isFinished()) { $this->popCurrent(); return $this->response; } $body = parent::handleRequest($request, $model); if ($body instanceof SS_HTTPResponse) { if (isset($_REQUEST['debug_request'])) { Debug::message("Request handler returned SS_HTTPResponse object to {$this->class} controller;" . "returning it without modification."); } $this->response = $body; } else { if ($body instanceof Object && $body->hasMethod('getViewer')) { if (isset($_REQUEST['debug_request'])) { Debug::message("Request handler {$body->class} object to {$this->class} controller;" . "rendering with template returned by {$body->class}::getViewer()"); } $body = $body->getViewer($request->latestParam('Action'))->process($body); } $this->response->setBody($body); } ContentNegotiator::process($this->response); HTTP::add_cache_headers($this->response); $this->popCurrent(); return $this->response; }
/** * Skip any further processing and immediately respond with a redirect to the passed URL. * * @param string $destURL - The URL to redirect to */ protected static function force_redirect($destURL) { $response = new SS_HTTPResponse("<h1>Your browser is not accepting header redirects</h1>" . "<p>Please <a href=\"{$destURL}\">click here</a>", 301); HTTP::add_cache_headers($response); $response->addHeader('Location', $destURL); // TODO: Use an exception - ATM we can be called from _config.php, before Director#handleRequest's try block $response->output(); die; }
public function rss() { $this->setDefaultView(); $events = $this->Events(); foreach ($events as $event) { $event->Title = strip_tags($event->DateRange()) . " : " . $event->getTitle(); $event->Description = $event->getContent(); } $rss_title = $this->RSSTitle ? $this->RSSTitle : sprintf(_t("Calendar.UPCOMINGEVENTSFOR", "Upcoming Events for %s"), $this->Title); $rss = new RSSFeed($events, $this->Link(), $rss_title, "", "Title", "Description"); if (is_int($rss->lastModified)) { HTTP::register_modification_timestamp($rss->lastModified); header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $rss->lastModified) . ' GMT'); } if (!empty($rss->etag)) { HTTP::register_etag($rss->etag); } $xml = str_replace(' ', ' ', $rss->renderWith('RSSFeed')); $xml = preg_replace('/<!--(.|\\s)*?-->/', '', $xml); $xml = trim($xml); HTTP::add_cache_headers(); $this->getResponse()->addHeader('Content-Type', 'application/rss+xml'); echo $xml; }
/** * Handles HTTP requests. * * If you are going to overload handleRequest, make sure that you start the method with $this->pushCurrent() * and end the method with $this->popCurrent(). Failure to do this will create weird session errors. * * @param $request The {@link HTTPRequest} object that is responsible for distributing request parsing. */ function handleRequest(HTTPRequest $request) { if(!$request) user_error("Controller::handleRequest() not passed a request!", E_USER_ERROR); $this->pushCurrent(); $this->urlParams = $request->allParams(); $this->request = $request; $this->response = new HTTPResponse(); // Init $this->baseInitCalled = false; $this->init(); if(!$this->baseInitCalled) user_error("init() method on class '$this->class' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING); // If we had a redirection or something, halt processing. if($this->response->isFinished()) { $this->popCurrent(); return $this->response; } $body = parent::handleRequest($request); if($body instanceof HTTPResponse) { if(isset($_REQUEST['debug_request'])) Debug::message("Request handler returned HTTPResponse object to $this->class controller; returning it without modification."); $this->response = $body; } else { if(is_object($body)) { if(isset($_REQUEST['debug_request'])) Debug::message("Request handler $body->class object to $this->class controller;, rendering with template returned by $body->class::getViewer()"); $body = $body->getViewer($request->latestParam('Action'))->process($body); } $this->response->setBody($body); } ContentNegotiator::process($this->response); HTTP::add_cache_headers($this->response); $this->popCurrent(); return $this->response; }
public function rss() { $events = $this->getModel()->UpcomingEvents(null, $this->DefaultEventDisplay); foreach ($events as $event) { $event->Title = strip_tags($event->_Dates()) . " : " . $event->EventTitle(); $event->Description = $event->EventContent(); } $rss = new RSSFeed($events, $this->Link(), sprintf(_t("Calendar.UPCOMINGEVENTSFOR", "Upcoming Events for %s"), $this->Title), "", "Title", "Description"); if (is_int($rss->lastModified)) { HTTP::register_modification_timestamp($rss->lastModified); header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $rss->lastModified) . ' GMT'); } if (!empty($rss->etag)) { HTTP::register_etag($rss->etag); } $xml = str_replace(' ', ' ', $rss->renderWith('RSSFeed')); $xml = preg_replace('/<!--(.|\\s)*?-->/', '', $xml); $xml = trim($xml); HTTP::add_cache_headers(); header("Content-type: text/xml"); echo $xml; }
public function handleRequest(SS_HTTPRequest $request, DataModel $model) { if (!$request) { user_error("Controller::handleRequest() not passed a request!", E_USER_ERROR); } $this->urlParams = $request->allParams(); $this->request = $request; $this->setDataModel($model); // Find our action or set to index if not found $action = $this->request->param("Action"); if (!$action) { $action = "index"; } $result = $this->{$action}($request); // Try to determine what response we are dealing with if ($result instanceof SS_HTTPResponse) { $this->response = $result; } else { $this->response = new SS_HTTPResponse(); $this->response->setBody($result); } // If we had a redirection or something, halt processing. if ($this->response->isFinished()) { return $this->response; } ContentNegotiator::process($this->response); HTTP::add_cache_headers($this->response); return $this->response; }
/** * Prepare the response (we can receive an assortment of response types (strings/objects/HTTPResponses) and * changes the controller response object appropriately * * @param SS_HTTPResponse|Object $response */ protected function prepareResponse($response) { if ($response instanceof SS_HTTPResponse) { if (isset($_REQUEST['debug_request'])) { Debug::message("Request handler returned SS_HTTPResponse object to {$this->class} controller;" . "returning it without modification."); } $this->setResponse($response); } else { if ($response instanceof Object && $response->hasMethod('getViewer')) { if (isset($_REQUEST['debug_request'])) { Debug::message("Request handler {$response->class} object to {$this->class} controller;" . "rendering with template returned by {$response->class}::getViewer()"); } $response = $response->getViewer($this->getAction())->process($response); } $this->getResponse()->setbody($response); } //deal with content if appropriate ContentNegotiator::process($this->getResponse()); //add cache headers HTTP::add_cache_headers($this->getResponse()); }
<?php global $project; $project = 'mysite'; // use the _ss_environment.php file for configuration require_once 'conf/ConfigureFromEnv.php'; // set default language i18n::set_locale('en_US'); define('PROJECT_THIRDPARTY_DIR', project() . '/thirdparty'); define('PROJECT_THIRDPARTY_PATH', BASE_PATH . '/' . PROJECT_THIRDPARTY_DIR); if (SS_IS_TEST_ENV) { BasicAuth::protect_entire_site(true); } if (Director::isLive()) { if (strpos(Director::absoluteBaseURL(), 'silverstripe-europe.org') !== false || strpos(Director::absoluteBaseURL(), 'www') !== false) { $response = new SS_HTTPResponse(); $response->redirect('https://stripecon.eu', 301); HTTP::add_cache_headers($response); $response->output(); die; } // we are in live mode, send errors per email, set cache and force WWW HTTP::set_cache_age(3600); // HTTP Header for CloudFlare Caching SS_Cache::set_cache_lifetime('any', 10800); // Serverside cache to 3 hours. SS_Log::add_writer(new SS_LogEmailWriter('*****@*****.**'), SS_Log::ERR); } Config::inst()->update('HtmlEditorField', 'use_gzip', false);
/** * Output the feed to the browser */ function outputToBrowser() { if (is_int($this->lastModified)) { HTTP::register_modification_timestamp($this->lastModified); header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT'); } if (!empty($this->etag)) { HTTP::register_etag($this->etag); } $body = str_replace(' ', ' ', $this->renderWith('RSSFeed')); HTTP::add_cache_headers($body); header("Content-type: text/xml"); echo $body; }
/** * Skip any further processing and immediately respond with a redirect to the passed URL. * * @param string $destURL */ protected static function force_redirect($destURL) { $response = new SS_HTTPResponse(); $response->redirect($destURL, 301); HTTP::add_cache_headers($response); // TODO: Use an exception - ATM we can be called from _config.php, before Director#handleRequest's try block $response->output(); die; }
/** * Get the RSS feed * * This method will output the RSS feed with the last 50 posts to the * browser. */ function rss() { HTTP::set_cache_age(3600); // cache for one hour $threadID = null; $forumID = null; // optionally allow filtering of the forum posts by the url in the format // rss/thread/$ID or rss/forum/$ID if (isset($this->urlParams['ID']) && ($action = $this->urlParams['ID'])) { if (isset($this->urlParams['OtherID']) && ($id = $this->urlParams['OtherID'])) { switch ($action) { case 'forum': $forumID = (int) $id; break; case 'thread': $threadID = (int) $id; } } else { // fallback is that it is the ID of a forum like it was in // previous versions $forumID = (int) $action; } } $data = array('last_created' => null, 'last_id' => null); if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !isset($_SERVER['HTTP_IF_NONE_MATCH'])) { // just to get the version data.. $this->getNewPostsAvailable(null, null, $forumID, $threadID, &$data); // No information provided by the client, just return the last posts $rss = new RSSFeed($this->getRecentPosts(50, $forumID, $threadID), $this->Link() . 'rss', sprintf(_t('Forum.RSSFORUMPOSTSTO'), $this->Title), "", "Title", "RSSContent", "RSSAuthor", $data['last_created'], $data['last_id']); $rss->outputToBrowser(); } else { // Return only new posts, check the request headers! $since = null; $etag = null; if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Split the If-Modified-Since (Netscape < v6 gets this wrong) $since = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']); // Turn the client request If-Modified-Since into a timestamp $since = @strtotime($since[0]); if (!$since) { $since = null; } } if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && is_numeric($_SERVER['HTTP_IF_NONE_MATCH'])) { $etag = (int) $_SERVER['HTTP_IF_NONE_MATCH']; } if ($this->getNewPostsAvailable($since, $etag, $forumID, $threadID, $data)) { HTTP::register_modification_timestamp($data['last_created']); $rss = new RSSFeed($this->getRecentPosts(50, $forumID, $threadID, $etag), $this->Link() . 'rss', sprintf(_t('Forum.RSSFORUMPOSTSTO'), $this->Title), "", "Title", "RSSContent", "RSSAuthor", $data['last_created'], $data['last_id']); $rss->outputToBrowser(); } else { if ($data['last_created']) { HTTP::register_modification_timestamp($data['last_created']); } if ($data['last_id']) { HTTP::register_etag($data['last_id']); } // There are no new posts, just output an "304 Not Modified" message HTTP::add_cache_headers(); header('HTTP/1.1 304 Not Modified'); } } exit; }
/** * Ensures the response has the correct headers */ protected function setHeaders() { // Set headers HTTP::set_cache_age(0); HTTP::add_cache_headers($this->response); $this->response->addHeader('Content-Type', 'application/json')->addHeader('Content-Encoding', 'UTF-8')->addHeader('X-Content-Type-Options', 'nosniff'); }
/** * Output the feed to the browser */ function outputToBrowser() { if(is_int($this->lastModified)) { HTTP::register_modification_timestamp($this->lastModified); header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $this->lastModified) . ' GMT'); } if(!empty($this->etag)) { HTTP::register_etag($this->etag); } $body = $this->feedContent(); HTTP::add_cache_headers(); header("Content-type: text/xml"); echo $body; }