/**
  * Get the MIME type based on a file's extension. If the finfo class exists in PHP, and the file
  * exists relative to the project root, then use that extension, otherwise fallback to a list of
  * commonly known MIME types.
  *
  * @param string $filename
  *
  * @return string
  */
 public static function get_mime_type($filename)
 {
     // If the finfo module is compiled into PHP, use it.
     $path = BASE_PATH . DIRECTORY_SEPARATOR . $filename;
     if (class_exists('finfo') && file_exists($path)) {
         $finfo = new finfo(FILEINFO_MIME_TYPE);
         return $finfo->file($path);
     }
     // Fallback to use the list from the HTTP.yml configuration and rely on the file extension
     // to get the file mime-type
     $ext = File::get_file_extension($filename);
     // Get the mime-types
     $mimeTypes = HTTP::config()->get('MimeTypes');
     // The mime type doesn't exist
     if (!isset($mimeTypes[$ext])) {
         return 'application/unknown';
     }
     return $mimeTypes[$ext];
 }
 /**
  * Returns a link to the previous page, if the first page is not currently
  * active.
  *
  * @return string
  */
 public function PrevLink()
 {
     if ($this->NotFirstPage()) {
         return HTTP::setGetVar($this->getPaginationGetVar(), $this->getPageStart() - $this->getPageLength());
     }
 }
 /**
  * Return the attributes of the form tag - used by the templates.
  *
  * @param array $attrs Custom attributes to process. Falls back to {@link getAttributes()}.
  * If at least one argument is passed as a string, all arguments act as excludes by name.
  *
  * @return string HTML attributes, ready for insertion into an HTML tag
  */
 public function getAttributesHTML($attrs = null)
 {
     $exclude = is_string($attrs) ? func_get_args() : null;
     // Figure out if we can cache this form
     // - forms with validation shouldn't be cached, cos their error messages won't be shown
     // - forms with security tokens shouldn't be cached because security tokens expire
     $needsCacheDisabled = false;
     if ($this->getSecurityToken()->isEnabled()) {
         $needsCacheDisabled = true;
     }
     if ($this->FormMethod() != 'GET') {
         $needsCacheDisabled = true;
     }
     if (!$this->validator instanceof RequiredFields || count($this->validator->getRequired())) {
         $needsCacheDisabled = true;
     }
     // If we need to disable cache, do it
     if ($needsCacheDisabled) {
         HTTP::set_cache_age(0);
     }
     $attrs = $this->getAttributes();
     // Remove empty
     $attrs = array_filter((array) $attrs, create_function('$v', 'return ($v || $v === 0);'));
     // Remove excluded
     if ($exclude) {
         $attrs = array_diff_key($attrs, array_flip($exclude));
     }
     // Prepare HTML-friendly 'method' attribute (lower-case)
     if (isset($attrs['method'])) {
         $attrs['method'] = strtolower($attrs['method']);
     }
     // Create markup
     $parts = array();
     foreach ($attrs as $name => $value) {
         $parts[] = $value === true ? "{$name}=\"{$name}\"" : "{$name}=\"" . Convert::raw2att($value) . "\"";
     }
     return implode(' ', $parts);
 }
 /**
  * Construct a new DataObject.
  *
  * @param array|null $record This will be null for a new database record.  Alternatively, you can pass an array of
  * field values.  Normally this constructor is only used by the internal systems that get objects from the database.
  * @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods.
  *                             Singletons don't have their defaults set.
  * @param DataModel $model
  * @param array $queryParams List of DataQuery params necessary to lazy load, or load related objects.
  */
 public function __construct($record = null, $isSingleton = false, $model = null, $queryParams = array())
 {
     parent::__construct();
     // Set query params on the DataObject to tell the lazy loading mechanism the context the object creation context
     $this->setSourceQueryParams($queryParams);
     // Set the fields data.
     if (!$record) {
         $record = array('ID' => 0, 'ClassName' => static::class, 'RecordClassName' => static::class);
     }
     if (!is_array($record) && !is_a($record, "stdClass")) {
         if (is_object($record)) {
             $passed = "an object of type '" . get_class($record) . "'";
         } else {
             $passed = "The value '{$record}'";
         }
         user_error("DataObject::__construct passed {$passed}.  It's supposed to be passed an array," . " taken straight from the database.  Perhaps you should use DataList::create()->First(); instead?", E_USER_WARNING);
         $record = null;
     }
     if (is_a($record, "stdClass")) {
         $record = (array) $record;
     }
     // Set $this->record to $record, but ignore NULLs
     $this->record = array();
     foreach ($record as $k => $v) {
         // Ensure that ID is stored as a number and not a string
         // To do: this kind of clean-up should be done on all numeric fields, in some relatively
         // performant manner
         if ($v !== null) {
             if ($k == 'ID' && is_numeric($v)) {
                 $this->record[$k] = (int) $v;
             } else {
                 $this->record[$k] = $v;
             }
         }
     }
     // Identify fields that should be lazy loaded, but only on existing records
     if (!empty($record['ID'])) {
         // Get all field specs scoped to class for later lazy loading
         $fields = static::getSchema()->fieldSpecs(static::class, DataObjectSchema::INCLUDE_CLASS | DataObjectSchema::DB_ONLY);
         foreach ($fields as $field => $fieldSpec) {
             $fieldClass = strtok($fieldSpec, ".");
             if (!array_key_exists($field, $record)) {
                 $this->record[$field . '_Lazy'] = $fieldClass;
             }
         }
     }
     $this->original = $this->record;
     // Keep track of the modification date of all the data sourced to make this page
     // From this we create a Last-Modified HTTP header
     if (isset($record['LastEdited'])) {
         HTTP::register_modification_date($record['LastEdited']);
     }
     // this must be called before populateDefaults(), as field getters on a DataObject
     // may call getComponent() and others, which rely on $this->model being set.
     $this->model = $model ? $model : DataModel::inst();
     // Must be called after parent constructor
     if (!$isSingleton && (!isset($this->record['ID']) || !$this->record['ID'])) {
         $this->populateDefaults();
     }
     // prevent populateDefaults() and setField() from marking overwritten defaults as changed
     $this->changed = array();
 }
 /**
  * Change the password
  *
  * @param array $data The user submitted data
  * @return HTTPResponse
  */
 public function doChangePassword(array $data)
 {
     if ($member = Member::currentUser()) {
         // The user was logged in, check the current password
         if (empty($data['OldPassword']) || !$member->checkPassword($data['OldPassword'])->valid()) {
             $this->clearMessage();
             $this->sessionMessage(_t('Member.ERRORPASSWORDNOTMATCH', "Your current password does not match, please try again"), "bad");
             // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
             return $this->controller->redirect($this->controller->Link('changepassword'));
         }
     }
     if (!$member) {
         if (Session::get('AutoLoginHash')) {
             $member = Member::member_from_autologinhash(Session::get('AutoLoginHash'));
         }
         // The user is not logged in and no valid auto login hash is available
         if (!$member) {
             Session::clear('AutoLoginHash');
             return $this->controller->redirect($this->controller->Link('login'));
         }
     }
     // Check the new password
     if (empty($data['NewPassword1'])) {
         $this->clearMessage();
         $this->sessionMessage(_t('Member.EMPTYNEWPASSWORD', "The new password can't be empty, please try again"), "bad");
         // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
         return $this->controller->redirect($this->controller->Link('changepassword'));
     } else {
         if ($data['NewPassword1'] == $data['NewPassword2']) {
             $isValid = $member->changePassword($data['NewPassword1']);
             if ($isValid->valid()) {
                 // Clear locked out status
                 $member->LockedOutUntil = null;
                 $member->FailedLoginCount = null;
                 $member->write();
                 if ($member->canLogIn()->valid()) {
                     $member->logIn();
                 }
                 // TODO Add confirmation message to login redirect
                 Session::clear('AutoLoginHash');
                 if (!empty($_REQUEST['BackURL']) && Director::is_site_url($_REQUEST['BackURL'])) {
                     $url = Director::absoluteURL($_REQUEST['BackURL']);
                     return $this->controller->redirect($url);
                 } else {
                     // Redirect to default location - the login form saying "You are logged in as..."
                     $redirectURL = HTTP::setGetVar('BackURL', Director::absoluteBaseURL(), $this->controller->Link('login'));
                     return $this->controller->redirect($redirectURL);
                 }
             } else {
                 $this->clearMessage();
                 $this->sessionMessage(_t('Member.INVALIDNEWPASSWORD', "We couldn't accept that password: {password}", array('password' => nl2br("\n" . Convert::raw2xml($isValid->starredList())))), "bad", false);
                 // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
                 return $this->controller->redirect($this->controller->Link('changepassword'));
             }
         } else {
             $this->clearMessage();
             $this->sessionMessage(_t('Member.ERRORNEWPASSWORD', "You have entered your new password differently, try again"), "bad");
             // redirect back to the form, instead of using redirectBack() which could send the user elsewhere.
             return $this->controller->redirect($this->controller->Link('changepassword'));
         }
     }
 }
 /**
  * Construct an HTTPResponse that will deliver a file to the client.
  * Caution: Since it requires $fileData to be passed as binary data (no stream support),
  * it's only advisable to send small files through this method.
  *
  * @static
  * @param $fileData
  * @param $fileName
  * @param null $mimeType
  * @return HTTPResponse
  */
 public static function send_file($fileData, $fileName, $mimeType = null)
 {
     if (!$mimeType) {
         $mimeType = HTTP::get_mime_type($fileName);
     }
     $response = new HTTPResponse($fileData);
     $response->addHeader("Content-Type", "{$mimeType}; name=\"" . addslashes($fileName) . "\"");
     // Note a IE-only fix that inspects this header in HTTP::add_cache_headers().
     $response->addHeader("Content-Disposition", "attachment; filename=\"" . addslashes($fileName) . "\"");
     $response->addHeader("Content-Length", strlen($fileData));
     return $response;
 }
 public function testFilename2url()
 {
     $this->withBaseURL('http://www.silverstripe.org/', function ($test) {
         $frameworkTests = ltrim(FRAMEWORK_DIR . '/tests', '/');
         $test->assertEquals("http://www.silverstripe.org/{$frameworkTests}/control/HTTPTest.php", HTTP::filename2url(__FILE__));
     });
 }
 /**
  * Redirect back. Uses either the HTTP-Referer or a manually set request-variable called "BackURL".
  * This variable is needed in scenarios where HTTP-Referer is not sent (e.g when calling a page by
  * location.href in IE). If none of the two variables is available, it will redirect to the base
  * URL (see {@link Director::baseURL()}).
  *
  * @uses redirect()
  *
  * @return bool|HTTPResponse
  */
 public function redirectBack()
 {
     // Don't cache the redirect back ever
     HTTP::set_cache_age(0);
     $url = null;
     // In edge-cases, this will be called outside of a handleRequest() context; in that case,
     // redirect to the homepage - don't break into the global state at this stage because we'll
     // be calling from a test context or something else where the global state is inappropraite
     if ($this->getRequest()) {
         if ($this->getRequest()->requestVar('BackURL')) {
             $url = $this->getRequest()->requestVar('BackURL');
         } else {
             if ($this->getRequest()->isAjax() && $this->getRequest()->getHeader('X-Backurl')) {
                 $url = $this->getRequest()->getHeader('X-Backurl');
             } else {
                 if ($this->getRequest()->getHeader('Referer')) {
                     $url = $this->getRequest()->getHeader('Referer');
                 }
             }
         }
     }
     if (!$url) {
         $url = Director::baseURL();
     }
     // absolute redirection URLs not located on this site may cause phishing
     if (Director::is_site_url($url)) {
         $url = Director::absoluteURL($url, true);
         return $this->redirect($url);
     } else {
         return false;
     }
 }
 /**
  * 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 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;
 }
 /**
  * Return the value of the field with relative links converted to absolute urls (with placeholders parsed).
  * @return string
  */
 public function AbsoluteLinks()
 {
     return HTTP::absoluteURLs($this->forTemplate());
 }
 /**
  * Encode the contents of a file for emailing, including headers
  *
  * $file can be an array, in which case it expects these members:
  *   'filename'        - the filename of the file
  *   'contents'        - the raw binary contents of the file as a string
  *  and can optionally include these members:
  *   'mimetype'        - the mimetype of the file (calculated from filename if missing)
  *   'contentLocation' - the 'Content-Location' header value for the file
  *
  * $file can also be a string, in which case it is assumed to be the filename
  *
  * h5. contentLocation
  *
  * Content Location is one of the two methods allowed for embedding images into an html email.
  * It's also the simplest, and best supported.
  *
  * Assume we have an email with this in the body:
  *
  *   <img src="http://example.com/image.gif" />
  *
  * To display the image, an email viewer would have to download the image from the web every time
  * it is displayed. Due to privacy issues, most viewers will not display any images unless
  * the user clicks 'Show images in this email'. Not optimal.
  *
  * However, we can also include a copy of this image as an attached file in the email.
  * By giving it a contentLocation of "http://example.com/image.gif" most email viewers
  * will use this attached copy instead of downloading it. Better,
  * most viewers will show it without a 'Show images in this email' conformation.
  *
  * Here is an example of passing this information through Email.php:
  *
  *   $email = new Email();
  *   $email->attachments[] = array(
  *     'filename' => BASE_PATH . "/themes/mytheme/images/header.gif",
  *     'contents' => file_get_contents(BASE_PATH . "/themes/mytheme/images/header.gif"),
  *     'mimetype' => 'image/gif',
  *     'contentLocation' => Director::absoluteBaseURL() . "/themes/mytheme/images/header.gif"
  *   );
  *
  * @param array|string $file
  * @param bool $destFileName
  * @param string $disposition
  * @param string $extraHeaders
  * @return string
  */
 protected function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $extraHeaders = "")
 {
     if (!$file) {
         throw new InvalidArgumentException("Not passed a filename and/or data");
     }
     if (is_string($file)) {
         $file = array('filename' => $file);
         $fh = fopen($file['filename'], "rb");
         if ($fh) {
             $file['contents'] = "";
             while (!feof($fh)) {
                 $file['contents'] .= fread($fh, 10000);
             }
             fclose($fh);
         }
     }
     // Build headers, including content type
     if (!$destFileName) {
         $base = basename($file['filename']);
     } else {
         $base = $destFileName;
     }
     $mimeType = !empty($file['mimetype']) ? $file['mimetype'] : HTTP::get_mime_type($file['filename']);
     if (!$mimeType) {
         $mimeType = "application/unknown";
     }
     if (empty($disposition)) {
         $disposition = isset($file['contentLocation']) ? 'inline' : 'attachment';
     }
     // Encode for emailing
     if (substr($mimeType, 0, 4) != 'text') {
         $encoding = "base64";
         $file['contents'] = chunk_split(base64_encode($file['contents']));
     } else {
         // This mime type is needed, otherwise some clients will show it as an inline attachment
         $mimeType = 'application/octet-stream';
         $encoding = "quoted-printable";
         $file['contents'] = quoted_printable_encode($file['contents']);
     }
     $headers = "Content-type: {$mimeType};\n\tname=\"{$base}\"\n" . "Content-Transfer-Encoding: {$encoding}\n" . "Content-Disposition: {$disposition};\n\tfilename=\"{$base}\"\n";
     if (isset($file['contentLocation'])) {
         $headers .= 'Content-Location: ' . $file['contentLocation'] . "\n";
     }
     $headers .= $extraHeaders . "\n";
     // Return completed packet
     return $headers . $file['contents'];
 }
 /**
  * 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 HTTPResponse instance.
  *
  * @return DBHTMLText
  */
 public function outputToBrowser()
 {
     $prevState = SSViewer::config()->get('source_file_comments');
     SSViewer::config()->update('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");
     }
     SSViewer::config()->update('source_file_comments', $prevState);
     return $this->renderWith($this->getTemplates());
 }
Esempio n. 13
0
 /**
  * Load all the template variables into the internal variables, including
  * the template into body.	Called before send() or debugSend()
  * $isPlain=true will cause the template to be ignored, otherwise the GenericEmail template will be used
  * and it won't be plain email :)
  *
  * @param bool $isPlain
  * @return $this
  */
 protected function parseVariables($isPlain = false)
 {
     $origState = SSViewer::config()->get('source_file_comments');
     SSViewer::config()->update('source_file_comments', false);
     if (!$this->parseVariables_done) {
         $this->parseVariables_done = true;
         // Parse $ variables in the base parameters
         $this->templateData();
         // Process a .SS template file
         $fullBody = $this->body;
         if ($this->ss_template && !$isPlain) {
             // Requery data so that updated versions of To, From, Subject, etc are included
             $data = $this->templateData();
             $candidateTemplates = [$this->ss_template, ['type' => 'email', $this->ss_template]];
             $template = new SSViewer($candidateTemplates);
             if ($template->exists()) {
                 $fullBody = $template->process($data);
             }
         }
         // Rewrite relative URLs
         $this->body = HTTP::absoluteURLs($fullBody);
     }
     SSViewer::config()->update('source_file_comments', $origState);
     return $this;
 }