/**
  * Tests {@link Convert::raw2att()}
  */
 public function testRaw2Att()
 {
     $val1 = '<input type="text">';
     $this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2att($val1), 'Special characters are escaped');
     $val2 = 'This is some normal text.';
     $this->assertEquals('This is some normal text.', Convert::raw2att($val2), 'Normal text is not escaped');
 }
 /**
  * @return string
  */
 public function getContent()
 {
     $doc = clone $this->getDocument();
     $xp = new DOMXPath($doc);
     // If there's no body, the content is empty string
     if (!$doc->getElementsByTagName('body')->length) {
         return '';
     }
     // saveHTML Percentage-encodes any URI-based attributes. We don't want this, since it interferes with
     // shortcodes. So first, save all the attribute values for later restoration.
     $attrs = array();
     $i = 0;
     foreach ($xp->query('//body//@*') as $attr) {
         $key = "__HTMLVALUE_" . $i++;
         $attrs[$key] = $attr->value;
         $attr->value = $key;
     }
     // Then, call saveHTML & extract out the content from the body tag
     $res = preg_replace(array('/^(.*?)<body>/is', '/<\\/body>(.*?)$/isD'), '', $doc->saveHTML());
     // Then replace the saved attributes with their original versions
     $res = preg_replace_callback('/__HTMLVALUE_(\\d+)/', function ($matches) use($attrs) {
         return Convert::raw2att($attrs[$matches[0]]);
     }, $res);
     // Prevent &nbsp; being encoded as literal utf-8 characters
     // Possible alternative solution: http://stackoverflow.com/questions/2142120/php-encoding-with-domdocument
     $from = mb_convert_encoding('&nbsp;', 'utf-8', 'html-entities');
     $res = str_replace($from, '&nbsp;', $res);
     return $res;
 }
 /**
  * Get raw HTML for image markup
  *
  * @param File $file
  * @return string
  */
 protected function getIconMarkup($file)
 {
     if (!$file) {
         return null;
     }
     $previewLink = Convert::raw2att($file->PreviewLink());
     return "<img src=\"{$previewLink}\" class=\"editor__thumbnail\" />";
 }
 /**
  * overloaded to display the correctly formated value for this datatype
  *
  * @param array $properties
  * @return string
  */
 public function Field($properties = array())
 {
     if ($this->value) {
         $val = Convert::raw2xml($this->value);
         $val = _t('CurrencyField.CURRENCYSYMBOL', '$') . number_format(preg_replace('/[^0-9.]/', "", $val), 2);
         $valforInput = Convert::raw2att($val);
     } else {
         $valforInput = '';
     }
     return "<input class=\"text\" type=\"text\" disabled=\"disabled\"" . " name=\"" . $this->name . "\" value=\"" . $valforInput . "\" />";
 }
 /**
  * Overloaded to display the correctly formated value for this datatype
  *
  * @param array $properties
  * @return string
  */
 public function Field($properties = array())
 {
     if ($this->value) {
         $val = Convert::raw2xml($this->value);
         $val = _t('CurrencyField.CURRENCYSYMBOL', '$') . number_format(preg_replace('/[^0-9.]/', "", $val), 2);
         $valforInput = Convert::raw2att($val);
     } else {
         $val = '<i>' . _t('CurrencyField.CURRENCYSYMBOL', '$') . '0.00</i>';
         $valforInput = '';
     }
     return "<span class=\"readonly " . $this->extraClass() . "\" id=\"" . $this->ID() . "\">{$val}</span>" . "<input type=\"hidden\" name=\"" . $this->name . "\" value=\"" . $valforInput . "\" />";
 }
 protected function getSpecsMarkup($record)
 {
     if (!$record || !$record->isInDB()) {
         return null;
     }
     /**
      * Can remove .label and .label-info when Bootstrap has been updated to BS4 Beta
      * .label is being replaced with .tag
      */
     $versionTag = sprintf('<span class="label label-info tag tag-info">v.%s</span>', $record->Version);
     $agoTag = sprintf('%s <time class="relative-time" title="%s">%s</time>', $record->WasPublished ? _t('SilverStripe\\AssetAdmin\\Forms\\FileHistoryFormFactory.PUBLISHED', 'Published') : _t('SilverStripe\\AssetAdmin\\Forms\\FileHistoryFormFactory.SAVED', 'Saved'), Convert::raw2att($record->LastEdited), Convert::raw2xml($record->dbObject('LastEdited')->Ago()));
     return sprintf('<div class="editor__specs">%s %s, %s %s</div>', $versionTag, $agoTag, $record->getSize(), $this->getStatusFlagMarkup($record));
 }
    /**
     * Redirects the user to the external login page
     *
     * @return HTTPResponse
     */
    protected function redirectToExternalLogin()
    {
        $loginURL = Security::create()->Link('login');
        $loginURLATT = Convert::raw2att($loginURL);
        $loginURLJS = Convert::raw2js($loginURL);
        $message = _t('CMSSecurity.INVALIDUSER', '<p>Invalid user. <a target="_top" href="{link}">Please re-authenticate here</a> to continue.</p>', 'Message displayed to user if their session cannot be restored', array('link' => $loginURLATT));
        $response = $this->getResponse();
        $response->setStatusCode(200);
        $response->setBody(<<<PHP
<!DOCTYPE html>
<html><body>
{$message}
<script type="application/javascript">
setTimeout(function(){top.location.href = "{$loginURLJS}";}, 0);
</script>
</body></html>
PHP
);
        $this->setResponse($response);
        return $response;
    }
 /**
  * The process() method handles the "meat" of the template processing.
  *
  * It takes care of caching the output (via {@link Cache}), as well as
  * replacing the special "$Content" and "$Layout" placeholders with their
  * respective subtemplates.
  *
  * The method injects extra HTML in the header via {@link Requirements::includeInHTML()}.
  *
  * Note: You can call this method indirectly by {@link ViewableData->renderWith()}.
  *
  * @param ViewableData $item
  * @param array|null $arguments Arguments to an included template
  * @param ViewableData $inheritedScope The current scope of a parent template including a sub-template
  * @return DBHTMLText Parsed template output.
  */
 public function process($item, $arguments = null, $inheritedScope = null)
 {
     SSViewer::$topLevel[] = $item;
     $template = $this->chosen;
     $cacheFile = TEMP_FOLDER . "/.cache" . str_replace(array('\\', '/', ':'), '.', Director::makeRelative(realpath($template)));
     $lastEdited = filemtime($template);
     if (!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited) {
         $content = file_get_contents($template);
         $content = $this->parseTemplateContent($content, $template);
         $fh = fopen($cacheFile, 'w');
         fwrite($fh, $content);
         fclose($fh);
     }
     $underlay = array('I18NNamespace' => basename($template));
     // Makes the rendered sub-templates available on the parent item,
     // through $Content and $Layout placeholders.
     foreach (array('Content', 'Layout') as $subtemplate) {
         $sub = null;
         if (isset($this->subTemplates[$subtemplate])) {
             $sub = $this->subTemplates[$subtemplate];
         } elseif (!is_array($this->templates)) {
             $sub = ['type' => $subtemplate, $this->templates];
         } elseif (!array_key_exists('type', $this->templates) || !$this->templates['type']) {
             $sub = array_merge($this->templates, ['type' => $subtemplate]);
         }
         if ($sub) {
             $subtemplateViewer = clone $this;
             // Disable requirements - this will be handled by the parent template
             $subtemplateViewer->includeRequirements(false);
             // Select the right template
             $subtemplateViewer->setTemplate($sub);
             if ($subtemplateViewer->exists()) {
                 $underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
             }
         }
     }
     $output = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay, $inheritedScope);
     if ($this->includeRequirements) {
         $output = Requirements::includeInHTML($output);
     }
     array_pop(SSViewer::$topLevel);
     // If we have our crazy base tag, then fix # links referencing the current page.
     $rewrite = SSViewer::config()->get('rewrite_hash_links');
     if ($this->rewriteHashlinks && $rewrite) {
         if (strpos($output, '<base') !== false) {
             if ($rewrite === 'php') {
                 $thisURLRelativeToBase = "<?php echo \\SilverStripe\\Core\\Convert::raw2att(preg_replace(\"/^(\\\\/)+/\", \"/\", \$_SERVER['REQUEST_URI'])); ?>";
             } else {
                 $thisURLRelativeToBase = Convert::raw2att(preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI']));
             }
             $output = preg_replace('/(<a[^>]+href *= *)"#/i', '\\1"' . $thisURLRelativeToBase . '#', $output);
         }
     }
     return DBField::create_field('HTMLFragment', $output);
 }
예제 #9
0
 /**
  * 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);
 }
 /**
  * Returns a version of a title suitable for insertion into an HTML attribute.
  *
  * @return string
  */
 public function attrValue()
 {
     return Convert::raw2att($this->value);
 }
 /**
  * HTML Content for external link
  *
  * @return string
  */
 public function getExternalLink()
 {
     $title = $this->file ? $this->file->getTitle() : $this->getName();
     return sprintf('<a href="%1$s" title="%2$s" target="_blank" rel="external" class="file-url">%1$s</a>', Convert::raw2att($this->url), Convert::raw2att($title));
 }
 /**
  * Get part of the current classes ancestry to be used as a CSS class.
  *
  * This method returns an escaped string of CSS classes representing the current classes ancestry until it hits a
  * stop point - e.g. "Page DataObject ViewableData".
  *
  * @param string $stopAtClass the class to stop at (default: ViewableData)
  * @return string
  * @uses ClassInfo
  */
 public function CSSClasses($stopAtClass = 'SilverStripe\\View\\ViewableData')
 {
     $classes = array();
     $classAncestry = array_reverse(ClassInfo::ancestry($this->class));
     $stopClasses = ClassInfo::ancestry($stopAtClass);
     foreach ($classAncestry as $class) {
         if (in_array($class, $stopClasses)) {
             break;
         }
         $classes[] = $class;
     }
     // optionally add template identifier
     if (isset($this->template) && !in_array($this->template, $classes)) {
         $classes[] = $this->template;
     }
     // Strip out namespaces
     $classes = preg_replace('#.*\\\\#', '', $classes);
     return Convert::raw2att(implode(' ', $classes));
 }
예제 #13
0
 /**
  * Regenerates "[image id=n]" shortcode with new src attribute prior to being edited within the CMS.
  *
  * @param array $args Arguments passed to the parser
  * @param string $content Raw shortcode
  * @param ShortcodeParser $parser Parser
  * @param string $shortcode Name of shortcode used to register this handler
  * @param array $extra Extra arguments
  * @return string Result of the handled shortcode
  */
 public static function regenerate_shortcode($args, $content, $parser, $shortcode, $extra = array())
 {
     // Check if there is a suitable record
     $record = static::find_shortcode_record($args);
     if ($record) {
         $args['src'] = $record->getURL();
     }
     // Rebuild shortcode
     $parts = array();
     foreach ($args as $name => $value) {
         $htmlValue = Convert::raw2att($value ?: $name);
         $parts[] = sprintf('%s="%s"', $name, $htmlValue);
     }
     return sprintf("[%s %s]", $shortcode, implode(' ', $parts));
 }
예제 #14
0
 /**
  * Gets the value appropriate for a HTML attribute string
  *
  * @return string
  */
 public function ATT()
 {
     return Convert::raw2att($this->RAW());
 }
 /**
  * @param array $attrs
  * @return DBHTMLText
  */
 public function getAttributesHTML($attrs = null)
 {
     $excludeKeys = is_string($attrs) ? func_get_args() : null;
     if (!$attrs || is_string($attrs)) {
         $attrs = $this->attributes;
     }
     // Remove empty or excluded values
     foreach ($attrs as $key => $value) {
         if ($excludeKeys && in_array($key, $excludeKeys) || !$value && $value !== 0 && $value !== '0') {
             unset($attrs[$key]);
             continue;
         }
     }
     // Create markkup
     $parts = array();
     foreach ($attrs as $name => $value) {
         $parts[] = $value === true ? "{$name}=\"{$name}\"" : "{$name}=\"" . Convert::raw2att($value) . "\"";
     }
     return DBField::create_field('HTMLFragment', implode(' ', $parts));
 }
 /**
  * Generate a CSV import form for a single {@link DataObject} subclass.
  *
  * @return Form|false
  */
 public function ImportForm()
 {
     $modelSNG = singleton($this->modelClass);
     $modelName = $modelSNG->i18n_singular_name();
     // check if a import form should be generated
     if (!$this->showImportForm || is_array($this->showImportForm) && !in_array($this->modelClass, $this->showImportForm)) {
         return false;
     }
     $importers = $this->getModelImporters();
     if (!$importers || !isset($importers[$this->modelClass])) {
         return false;
     }
     if (!$modelSNG->canCreate(Member::currentUser())) {
         return false;
     }
     $fields = new FieldList(new HiddenField('ClassName', _t('ModelAdmin.CLASSTYPE'), $this->modelClass), new FileField('_CsvFile', false));
     // get HTML specification for each import (column names etc.)
     $importerClass = $importers[$this->modelClass];
     /** @var BulkLoader $importer */
     $importer = new $importerClass($this->modelClass);
     $spec = $importer->getImportSpec();
     $specFields = new ArrayList();
     foreach ($spec['fields'] as $name => $desc) {
         $specFields->push(new ArrayData(array('Name' => $name, 'Description' => $desc)));
     }
     $specRelations = new ArrayList();
     foreach ($spec['relations'] as $name => $desc) {
         $specRelations->push(new ArrayData(array('Name' => $name, 'Description' => $desc)));
     }
     $specHTML = $this->customise(array('ClassName' => $this->sanitiseClassName($this->modelClass), 'ModelName' => Convert::raw2att($modelName), 'Fields' => $specFields, 'Relations' => $specRelations))->renderWith($this->getTemplatesWithSuffix('_ImportSpec'));
     $fields->push(new LiteralField("SpecFor{$modelName}", $specHTML));
     $fields->push(new CheckboxField('EmptyBeforeImport', _t('ModelAdmin.EMPTYBEFOREIMPORT', 'Replace data'), false));
     $actions = new FieldList(new FormAction('import', _t('ModelAdmin.IMPORT', 'Import from CSV')));
     $form = new Form($this, "ImportForm", $fields, $actions);
     $form->setFormAction(Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'ImportForm'));
     $this->extend('updateImportForm', $form);
     return $form;
 }
 /**
  * Get HTML for img containing the icon for this file
  *
  * @return DBHTMLText
  */
 public function IconTag()
 {
     return DBField::create_field('HTMLFragment', '<img src="' . Convert::raw2att($this->getIcon()) . '" />');
 }
 /**
  * Adds extra fields to this form
  *
  * @param FieldList $fields
  * @param Controller $controller
  * @param string $name
  * @param array $context
  */
 public function updateFormFields(FieldList &$fields, Controller $controller, $name, $context = [])
 {
     // Add preview link
     if (empty($context['Record'])) {
         return;
     }
     $record = $context['Record'];
     if ($record->hasExtension(Versioned::class)) {
         $link = $controller->Link('preview');
         $fields->unshift(new LiteralField("PreviewLink", sprintf('<a href="%s" rel="external" target="_blank">Preview</a>', Convert::raw2att($link))));
     }
 }
예제 #19
0
 /**
  * @return string
  */
 public function getTreeTitle()
 {
     return sprintf("<span class=\"jstree-foldericon\"></span><span class=\"item\">%s</span>", Convert::raw2att(preg_replace('~\\R~u', ' ', $this->Title)));
 }
    public function testRewriteHashlinks()
    {
        SSViewer::config()->update('rewrite_hash_links', true);
        $_SERVER['HTTP_HOST'] = 'www.mysite.com';
        $_SERVER['REQUEST_URI'] = '//file.com?foo"onclick="alert(\'xss\')""';
        // Emulate SSViewer::process()
        // Note that leading double slashes have been rewritten to prevent these being mis-interepreted
        // as protocol-less absolute urls
        $base = Convert::raw2att('/file.com?foo"onclick="alert(\'xss\')""');
        $tmplFile = TEMP_FOLDER . '/SSViewerTest_testRewriteHashlinks_' . sha1(rand()) . '.ss';
        // Note: SSViewer_FromString doesn't rewrite hash links.
        file_put_contents($tmplFile, '<!DOCTYPE html>
			<html>
				<head><% base_tag %></head>
				<body>
				<a class="external-inline" href="http://google.com#anchor">ExternalInlineLink</a>
				$ExternalInsertedLink
				<a class="inline" href="#anchor">InlineLink</a>
				$InsertedLink
				<svg><use xlink:href="#sprite"></use></svg>
				<body>
			</html>');
        $tmpl = new SSViewer($tmplFile);
        $obj = new ViewableData();
        $obj->InsertedLink = DBField::create_field('HTMLFragment', '<a class="inserted" href="#anchor">InsertedLink</a>');
        $obj->ExternalInsertedLink = DBField::create_field('HTMLFragment', '<a class="external-inserted" href="http://google.com#anchor">ExternalInsertedLink</a>');
        $result = $tmpl->process($obj);
        $this->assertContains('<a class="inserted" href="' . $base . '#anchor">InsertedLink</a>', $result);
        $this->assertContains('<a class="external-inserted" href="http://google.com#anchor">ExternalInsertedLink</a>', $result);
        $this->assertContains('<a class="inline" href="' . $base . '#anchor">InlineLink</a>', $result);
        $this->assertContains('<a class="external-inline" href="http://google.com#anchor">ExternalInlineLink</a>', $result);
        $this->assertContains('<svg><use xlink:href="#sprite"></use></svg>', $result, 'SSTemplateParser should only rewrite anchor hrefs');
        unlink($tmplFile);
    }
    /**
     * Redirect the user to the change password form.
     *
     * @return HTTPResponse
     */
    protected function redirectToChangePassword()
    {
        // Since this form is loaded via an iframe, this redirect must be performed via javascript
        $changePasswordForm = new ChangePasswordForm($this->controller, 'SilverStripe\\Security\\ChangePasswordForm');
        $changePasswordForm->sessionMessage(_t('Member.PASSWORDEXPIRED', 'Your password has expired. Please choose a new one.'), 'good');
        // Get redirect url
        $changePasswordURL = $this->getExternalLink('changepassword');
        if ($backURL = $this->controller->getRequest()->requestVar('BackURL')) {
            Session::set('BackURL', $backURL);
            $changePasswordURL = Controller::join_links($changePasswordURL, '?BackURL=' . urlencode($backURL));
        }
        $changePasswordURLATT = Convert::raw2att($changePasswordURL);
        $changePasswordURLJS = Convert::raw2js($changePasswordURL);
        $message = _t('CMSMemberLoginForm.PASSWORDEXPIRED', '<p>Your password has expired. <a target="_top" href="{link}">Please choose a new one.</a></p>', 'Message displayed to user if their session cannot be restored', array('link' => $changePasswordURLATT));
        // Redirect to change password page
        $this->controller->getResponse()->setStatusCode(200);
        $this->controller->getResponse()->setBody(<<<PHP
<!DOCTYPE html>
<html><body>
{$message}
<script type="application/javascript">
setTimeout(function(){top.location.href = "{$changePasswordURLJS}";}, 0);
</script>
</body></html>
PHP
);
        return $this->controller->getResponse();
    }