diff --git a/ORM/Connect/MySQLiConnector.php b/ORM/Connect/MySQLiConnector.php
index 38fa654e0..2be7a74a6 100644
--- a/ORM/Connect/MySQLiConnector.php
+++ b/ORM/Connect/MySQLiConnector.php
@@ -102,7 +102,7 @@ class MySQLiConnector extends DBConnector {
}
public function __destruct() {
- if ($this->dbConn) {
+ if (is_resource($this->dbConn)) {
mysqli_close($this->dbConn);
$this->dbConn = null;
}
diff --git a/ORM/FieldType/DBField.php b/ORM/FieldType/DBField.php
index 0d47866b8..43ff6e888 100644
--- a/ORM/FieldType/DBField.php
+++ b/ORM/FieldType/DBField.php
@@ -256,19 +256,19 @@ abstract class DBField extends ViewableData {
}
public function HTMLATT() {
- return Convert::raw2htmlatt($this->value);
+ return Convert::raw2htmlatt($this->RAW());
}
public function URLATT() {
- return urlencode($this->value);
+ return urlencode($this->RAW());
}
public function RAWURLATT() {
- return rawurlencode($this->value);
+ return rawurlencode($this->RAW());
}
public function ATT() {
- return Convert::raw2att($this->value);
+ return Convert::raw2att($this->RAW());
}
public function RAW() {
@@ -276,7 +276,7 @@ abstract class DBField extends ViewableData {
}
public function JS() {
- return Convert::raw2js($this->value);
+ return Convert::raw2js($this->RAW());
}
/**
@@ -284,15 +284,15 @@ abstract class DBField extends ViewableData {
* @return string
*/
public function JSON() {
- return Convert::raw2json($this->value);
+ return Convert::raw2json($this->RAW());
}
public function HTML(){
- return Convert::raw2xml($this->value);
+ return Convert::raw2xml($this->RAW());
}
public function XML(){
- return Convert::raw2xml($this->value);
+ return Convert::raw2xml($this->RAW());
}
/**
diff --git a/ORM/FieldType/DBHTMLText.php b/ORM/FieldType/DBHTMLText.php
index 528669803..73efceb1f 100644
--- a/ORM/FieldType/DBHTMLText.php
+++ b/ORM/FieldType/DBHTMLText.php
@@ -111,7 +111,7 @@ class DBHTMLText extends DBText {
// Catch warnings thrown by loadHTML and turn them into a failure boolean rather than a SilverStripe error
set_error_handler(create_function('$no, $str', 'throw new Exception("HTML Parse Error: ".$str);'), E_ALL);
// Nonbreaking spaces get converted into weird characters, so strip them
- $value = str_replace(' ', ' ', $this->value);
+ $value = str_replace(' ', ' ', $this->RAW());
try {
$res = $doc->loadHTML('' . $value);
}
@@ -186,6 +186,15 @@ class DBHTMLText extends DBText {
return $this->Summary();
}
+ public function RAW() {
+ if ($this->processShortcodes) {
+ return ShortcodeParser::get_active()->parse($this->value);
+ }
+ else {
+ return $this->value;
+ }
+ }
+
/**
* Return the value of the field with relative links converted to absolute urls (with placeholders parsed).
* @return string
@@ -195,12 +204,7 @@ class DBHTMLText extends DBText {
}
public function forTemplate() {
- if ($this->processShortcodes) {
- return ShortcodeParser::get_active()->parse($this->value);
- }
- else {
- return $this->value;
- }
+ return $this->RAW();
}
public function prepValueForDB($value) {
@@ -248,14 +252,16 @@ class DBHTMLText extends DBText {
return false;
}
+ $value = $this->RAW();
+
// If it's got a content tag
- if(preg_match('/<(img|embed|object|iframe|meta|source|link)[^>]*>/i', $this->value)) {
+ if(preg_match('/<(img|embed|object|iframe|meta|source|link)[^>]*>/i', $value)) {
return true;
}
// If it's just one or two tags on its own (and not the above) it's empty.
// This might be
or or whatever.
- if(preg_match('/^[\\s]*(<[^>]+>[\\s]*){1,2}$/', $this->value)) {
+ if(preg_match('/^[\\s]*(<[^>]+>[\\s]*){1,2}$/', $value)) {
return false;
}
diff --git a/ORM/FieldType/DBHTMLVarchar.php b/ORM/FieldType/DBHTMLVarchar.php
index 131429930..f01f357ee 100644
--- a/ORM/FieldType/DBHTMLVarchar.php
+++ b/ORM/FieldType/DBHTMLVarchar.php
@@ -28,16 +28,21 @@ class DBHTMLVarchar extends DBVarchar {
}
public function forTemplate() {
+ return $this->RAW();
+ }
+
+ public function RAW() {
if ($this->processShortcodes) {
return ShortcodeParser::get_active()->parse($this->value);
}
else {
return $this->value;
}
+
}
public function exists() {
- return parent::exists() && $this->value != '';
+ return parent::exists() && $this->RAW() != '';
}
public function scaffoldFormField($title = null, $params = null) {
diff --git a/ORM/FieldType/DBString.php b/ORM/FieldType/DBString.php
index fd6018960..a28e1fcff 100644
--- a/ORM/FieldType/DBString.php
+++ b/ORM/FieldType/DBString.php
@@ -88,9 +88,10 @@ abstract class DBString extends DBField {
* @see core/model/fieldtypes/DBField#exists()
*/
public function exists() {
- return $this->getValue() // All truthy values exist
- || (is_string($this->getValue()) && strlen($this->getValue())) // non-empty strings exist ('0' but not (int)0)
- || (!$this->getNullifyEmpty() && $this->getValue() === ''); // Remove this stupid exemption in 4.0
+ $value = $this->RAW();
+ return $value // All truthy values exist
+ || (is_string($value) && strlen($value)) // non-empty strings exist ('0' but not (int)0)
+ || (!$this->getNullifyEmpty() && $value === ''); // Remove this stupid exemption in 4.0
}
/**
@@ -122,7 +123,7 @@ abstract class DBString extends DBField {
* @return string
*/
public function LimitCharacters($limit = 20, $add = '...') {
- $value = trim($this->value);
+ $value = trim($this->RAW());
if($this->stat('escape_type') == 'xml') {
$value = strip_tags($value);
$value = html_entity_decode($value, ENT_COMPAT, 'UTF-8');
@@ -146,13 +147,13 @@ abstract class DBString extends DBField {
*/
public function LimitCharactersToClosestWord($limit = 20, $add = '...') {
// Strip HTML tags if they exist in the field
- $this->value = strip_tags($this->value);
+ $value = strip_tags($this->RAW());
// Determine if value exceeds limit before limiting characters
- $exceedsLimit = mb_strlen($this->value) > $limit;
+ $exceedsLimit = mb_strlen($value) > $limit;
// Limit to character limit
- $value = $this->LimitCharacters($limit, '');
+ $value = DBField::create_field(get_class($this), $value)->LimitCharacters($limit, '');
// If value exceeds limit, strip punctuation off the end to the last space and apply ellipsis
if($exceedsLimit) {
@@ -178,11 +179,11 @@ abstract class DBString extends DBField {
* @return string
*/
public function LimitWordCount($numWords = 26, $add = '...') {
- $this->value = trim(Convert::xml2raw($this->value));
- $ret = explode(' ', $this->value, $numWords + 1);
+ $value = trim(Convert::xml2raw($this->RAW()));
+ $ret = explode(' ', $value, $numWords + 1);
if(count($ret) <= $numWords - 1) {
- $ret = $this->value;
+ $ret = $value;
} else {
array_pop($ret);
$ret = implode(' ', $ret) . $add;
@@ -213,7 +214,7 @@ abstract class DBString extends DBField {
* @return string
*/
public function LowerCase() {
- return mb_strtolower($this->value);
+ return mb_strtolower($this->RAW());
}
/**
@@ -221,7 +222,7 @@ abstract class DBString extends DBField {
* @return string
*/
public function UpperCase() {
- return mb_strtoupper($this->value);
+ return mb_strtoupper($this->RAW());
}
/**
@@ -230,6 +231,6 @@ abstract class DBString extends DBField {
* @return string
*/
public function NoHTML() {
- return strip_tags($this->value);
+ return strip_tags($this->RAW());
}
}
diff --git a/ORM/FieldType/DBText.php b/ORM/FieldType/DBText.php
index d753590bc..bb79fecdb 100644
--- a/ORM/FieldType/DBText.php
+++ b/ORM/FieldType/DBText.php
@@ -71,7 +71,7 @@ class DBText extends DBString {
* @return string
*/
public function AbsoluteLinks() {
- return HTTP::absoluteURLs($this->value);
+ return HTTP::absoluteURLs($this->RAW());
}
/**
@@ -86,7 +86,7 @@ class DBText extends DBString {
}
$output = array();
- $data = trim(Convert::xml2raw($this->value));
+ $data = trim(Convert::xml2raw($this->RAW()));
$sentences = explode('.', $data);
if ($sentCount == 0) return '';
@@ -106,7 +106,7 @@ class DBText extends DBString {
* Caution: Not XML/HTML-safe - does not respect closing tags.
*/
public function FirstSentence() {
- $paragraph = Convert::xml2raw( $this->value );
+ $paragraph = Convert::xml2raw( $this->RAW() );
if( !$paragraph ) return "";
$words = preg_split('/\s+/', $paragraph);
@@ -127,7 +127,7 @@ class DBText extends DBString {
public function Summary($maxWords = 50) {
// get first sentence?
// this needs to be more robust
- $value = Convert::xml2raw( $this->value /*, true*/ );
+ $value = Convert::xml2raw( $this->RAW() /*, true*/ );
if(!$value) return '';
// grab the first paragraph, or, failing that, the whole content
@@ -169,7 +169,7 @@ class DBText extends DBString {
// get first sentence?
// this needs to be more robust
- $data = $plain ? Convert::xml2raw($this->value, true) : $this->value;
+ $data = $plain ? Convert::xml2raw($this->RAW(), true) : $this->RAW();
if(!$data) return '';
@@ -209,8 +209,9 @@ class DBText extends DBString {
public function FirstParagraph($plain = 1) {
// get first sentence?
// this needs to be more robust
+ $value = $this->RAW();
if($plain && $plain != 'html') {
- $data = Convert::xml2raw($this->value);
+ $data = Convert::xml2raw($value);
if(!$data) return "";
// grab the first paragraph, or, failing that, the whole content
@@ -219,12 +220,12 @@ class DBText extends DBString {
return $data;
} else {
- if(strpos($this->value, "") === false) return $this->value;
+ if(strpos($value, "") === false) return $value;
- $data = substr($this->value, 0, strpos($this->value, "") + 4);
+ $data = substr($value, 0, strpos($value, "") + 4);
- if(strlen($data) < 20 && strpos($this->value, "", strlen($data))) {
- $data = substr($this->value, 0, strpos( $this->value, "", strlen($data)) + 4 );
+ if(strlen($data) < 20 && strpos($value, "", strlen($data))) {
+ $data = substr($value, 0, strpos( $value, "", strlen($data)) + 4 );
}
return $data;
@@ -253,7 +254,7 @@ class DBText extends DBString {
}
// Remove HTML tags so we don't have to deal with matching tags
- $text = $striphtml ? $this->NoHTML() : $this->value;
+ $text = $striphtml ? $this->NoHTML() : $this->RAW();
// Find the search string
$position = (int) stripos($text, $string);
@@ -285,7 +286,7 @@ class DBText extends DBString {
$summary = trim($summary);
if($position > 0) $summary = $prefix . $summary;
- if(strlen($this->value) > ($characters + $position)) $summary = $summary . $suffix;
+ if(strlen($this->RAW()) > ($characters + $position)) $summary = $summary . $suffix;
return $summary;
}
@@ -299,14 +300,14 @@ class DBText extends DBString {
*/
public function Parse($parser = "TextParser") {
if($parser == "TextParser" || is_subclass_of($parser, "TextParser")) {
- $obj = new $parser($this->value);
+ $obj = new $parser($this->RAW());
return $obj->parse();
} else {
// Fallback to using raw2xml and show a warning
// TODO Don't kill script execution, we can continue without losing complete control of the app
user_error("Couldn't find an appropriate TextParser sub-class to create (Looked for '$parser')."
. "Make sure it sub-classes TextParser and that you've done ?flush=1.", E_USER_WARNING);
- return Convert::raw2xml($this->value);
+ return Convert::raw2xml($this->RAW());
}
}
diff --git a/ORM/FieldType/DBVarchar.php b/ORM/FieldType/DBVarchar.php
index ef8645629..a0193fe8e 100644
--- a/ORM/FieldType/DBVarchar.php
+++ b/ORM/FieldType/DBVarchar.php
@@ -80,15 +80,19 @@ class DBVarchar extends DBString {
* Return the first letter of the string followed by a .
*/
public function Initial() {
- if($this->exists()) return $this->value[0] . '.';
+ if($this->exists()) {
+ $value = $this->RAW();
+ return $value[0] . '.';
+ }
}
/**
* Ensure that the given value is an absolute URL.
*/
public function URL() {
- if(preg_match('#^[a-zA-Z]+://#', $this->value)) return $this->value;
- else return "http://" . $this->value;
+ $value = $this->RAW();
+ if(preg_match('#^[a-zA-Z]+://#', $value)) return $value;
+ else return "http://" . $value;
}
/**
@@ -96,7 +100,7 @@ class DBVarchar extends DBString {
* @return string
*/
public function RTF() {
- return str_replace("\n", '\par ', $this->value);
+ return str_replace("\n", '\par ', $this->RAW());
}
/**
diff --git a/Security/MemberLoginForm.php b/Security/MemberLoginForm.php
index 1dc376509..121930d86 100644
--- a/Security/MemberLoginForm.php
+++ b/Security/MemberLoginForm.php
@@ -129,7 +129,7 @@ class MemberLoginForm extends LoginForm {
FormAction::create('dologin', _t('Member.BUTTONLOGIN', "Log in")),
LiteralField::create(
'forgotPassword',
- ''
+ ''
. _t('Member.BUTTONLOSTPASSWORD', "I've lost my password") . '
'
)
);
diff --git a/Security/Permission.php b/Security/Permission.php
index 48a7b9e4b..652feb8c9 100644
--- a/Security/Permission.php
+++ b/Security/Permission.php
@@ -201,10 +201,9 @@ class Permission extends DataObject implements TemplateGlobalProvider {
}
}
}
- elseif (substr($permCode, 0, 11) === 'CMS_ACCESS_') {
+ elseif (substr($permCode, 0, 11) === 'CMS_ACCESS_' && !in_array('CMS_ACCESS_LeftAndMain', $code)) {
//cms_access_leftandmain means access to all CMS areas
$code[] = 'CMS_ACCESS_LeftAndMain';
- break;
}
}
diff --git a/Security/Security.php b/Security/Security.php
index 4afb6e9db..100495da5 100644
--- a/Security/Security.php
+++ b/Security/Security.php
@@ -155,6 +155,15 @@ class Security extends Controller implements TemplateGlobalProvider {
*/
private static $logout_url = "Security/logout";
+ /**
+ * The default lost password URL
+ *
+ * @config
+ *
+ * @var string
+ */
+ private static $lost_password_url = "Security/lostpassword";
+
/**
* Get location of word list file
*
@@ -1159,7 +1168,7 @@ class Security extends Controller implements TemplateGlobalProvider {
* @return string
*/
public static function login_url() {
- return self::config()->login_url;
+ return Controller::join_links(Director::baseURL(), self::config()->login_url);
}
@@ -1171,9 +1180,19 @@ class Security extends Controller implements TemplateGlobalProvider {
* @return string
*/
public static function logout_url() {
- return self::config()->logout_url;
+ return Controller::join_links(Director::baseURL(), self::config()->logout_url);
}
+ /**
+ * Get the URL of the logout page.
+ *
+ * To update the logout url use the "Security.logout_url" config setting.
+ *
+ * @return string
+ */
+ public static function lost_password_url() {
+ return Controller::join_links(Director::baseURL(), self::config()->lost_password_url);
+ }
/**
* Defines global accessible templates variables.
@@ -1184,6 +1203,7 @@ class Security extends Controller implements TemplateGlobalProvider {
return array(
"LoginURL" => "login_url",
"LogoutURL" => "logout_url",
+ "LostPasswordURL" => "lost_password_url",
);
}
diff --git a/docs/en/02_Developer_Guides/00_Model/02_Relations.md b/docs/en/02_Developer_Guides/00_Model/02_Relations.md
index b3cb2bd1c..83ddbbae3 100644
--- a/docs/en/02_Developer_Guides/00_Model/02_Relations.md
+++ b/docs/en/02_Developer_Guides/00_Model/02_Relations.md
@@ -147,7 +147,7 @@ you will get an instance of [api:HasManyList] rather than the object.
echo $player->FirstName;
}
-To specify multiple $has_manys to the same object you can use dot notation to distinguish them like below:
+To specify multiple `$has_many` to the same object you can use dot notation to distinguish them like below:
:::php
removeByName(array('ManagerID', 'CleanerID'));
+ return $fields;
+ }
+
## belongs_to
Defines a 1-to-1 relationship with another object, which declares the other end of the relationship with a
-corresponding $has_one. A single database column named `ID` will be created in the object with the
+corresponding `$has_one`. A single database column named `ID` will be created in the object with the
`$has_one`, but the $belongs_to by itself will not create a database field. This field will hold the ID of the object
declaring the `$belongs_to`.
-Similarly with $has_many, dot notation can be used to explicitly specify the `$has_one` which refers to this relation.
+Similarly with `$has_many`, dot notation can be used to explicitly specify the `$has_one` which refers to this relation.
This is not mandatory unless the relationship would be otherwise ambiguous.
:::php
diff --git a/docs/en/02_Developer_Guides/14_Files/02_Images.md b/docs/en/02_Developer_Guides/14_Files/02_Images.md
index ead5221b5..ccccb5b51 100644
--- a/docs/en/02_Developer_Guides/14_Files/02_Images.md
+++ b/docs/en/02_Developer_Guides/14_Files/02_Images.md
@@ -75,6 +75,19 @@ Image methods are chainable. Example:
:::ss
+### Padded Image Resize
+
+The Pad method allows you to resize an image with existing ratio and will
+pad any surplus space. You can specify the color of the padding using a hex code such as FFFFFF or 000000.
+
+You can also specify a level of transparency to apply to the padding color in a fourth param. This will only effect
+png images.
+
+ :::php
+ $Image.Pad(80, 80, FFFFFF, 50) // white padding with 50% transparency
+ $Image.Pad(80, 80, FFFFFF, 100) // white padding with 100% transparency
+ $Image.Pad(80, 80, FFFFFF) // white padding with no transparency
+
### Manipulating images in PHP
The image manipulation functions can be used in your code with the same names, example: `$image->Fill(150,150)`.
diff --git a/filesystem/ImagickBackend.php b/filesystem/ImagickBackend.php
index 522030f39..efe787fa0 100644
--- a/filesystem/ImagickBackend.php
+++ b/filesystem/ImagickBackend.php
@@ -150,19 +150,61 @@ class ImagickBackend extends Imagick implements Image_Backend {
return $this->resize( $scale * $geometry["width"], $height );
}
- public function paddedResize($width, $height, $backgroundColor = "FFFFFF") {
+ /**
+ * paddedResize
+ *
+ * @param int $width
+ * @param int $height
+ * @param int $transparencyPercent
+ * @return Image_Backend
+ */
+ public function paddedResize($width, $height, $backgroundColor = "FFFFFF", $transparencyPercent = 0) {
if(!$this->valid()) {
return null;
}
+
+ //keep the % within bounds of 0-100
+ $transparencyPercent = min(100, max(0, $transparencyPercent));
$new = $this->resizeRatio($width, $height);
- $new->setImageBackgroundColor("#".$backgroundColor);
+ if($transparencyPercent) {
+ $alphaHex = $this->calculateAlphaHex($transparencyPercent);
+ $new->setImageBackgroundColor("#{$backgroundColor}{$alphaHex}");
+ } else {
+ $new->setImageBackgroundColor("#{$backgroundColor}");
+ }
$w = $new->getImageWidth();
$h = $new->getImageHeight();
$new->extentImage($width,$height,($w-$width)/2,($h-$height)/2);
return $new;
}
+ /**
+ * Convert a percentage (or 'true') to a two char hex code to signifiy the level of an alpha channel
+ *
+ * @param $percent
+ * @return string
+ */
+ public function calculateAlphaHex($percent) {
+ if($percent > 100) {
+ $percent = 100;
+ }
+ // unlike GD, this uses 255 instead of 127, and is reversed. Lower = more transparent
+ $alphaHex = dechex(255 - floor(255 * bcdiv($percent, 100, 2)));
+ if(strlen($alphaHex) == 1) {
+ $alphaHex = '0' .$alphaHex;
+ }
+ return $alphaHex;
+ }
+
+
+ /**
+ * croppedResize
+ *
+ * @param int $width
+ * @param int $height
+ * @return Image_Backend
+ */
public function croppedResize($width, $height) {
if(!$this->valid()) {
return null;
diff --git a/forms/CheckboxSetField.php b/forms/CheckboxSetField.php
index 3ff403041..f07a7abdd 100644
--- a/forms/CheckboxSetField.php
+++ b/forms/CheckboxSetField.php
@@ -53,9 +53,7 @@ class CheckboxSetField extends MultiSelectField {
'Options' => $this->getOptions()
));
- return $this->customise($properties)->renderWith(
- $this->getTemplates()
- );
+ return FormField::Field($properties);
}
/**
diff --git a/forms/ListboxField.php b/forms/ListboxField.php
index 8bd9cd64d..abe56e98d 100644
--- a/forms/ListboxField.php
+++ b/forms/ListboxField.php
@@ -65,11 +65,10 @@ class ListboxField extends MultiSelectField {
*/
public function Field($properties = array()) {
$properties = array_merge($properties, array(
- 'Options' => $this->getOptions()
+ 'Options' => $this->getOptions(),
));
- return $this
- ->customise($properties)
- ->renderWith($this->getTemplates());
+
+ return FormField::Field($properties);
}
/**
diff --git a/forms/OptionsetField.php b/forms/OptionsetField.php
index f3790e8af..aa46b3ecd 100644
--- a/forms/OptionsetField.php
+++ b/forms/OptionsetField.php
@@ -121,9 +121,7 @@ class OptionsetField extends SingleSelectField {
'Options' => new ArrayList($options)
));
- return $this->customise($properties)->renderWith(
- $this->getTemplates()
- );
+ return FormField::Field($properties);
}
/**
diff --git a/forms/TreeDropdownField.php b/forms/TreeDropdownField.php
index 9d5c3985b..673d3c6c6 100644
--- a/forms/TreeDropdownField.php
+++ b/forms/TreeDropdownField.php
@@ -261,7 +261,7 @@ class TreeDropdownField extends FormField {
)
);
- return $this->customise($properties)->renderWith('TreeDropdownField');
+ return parent::Field($properties);
}
public function extraClass() {
diff --git a/forms/TreeMultiselectField.php b/forms/TreeMultiselectField.php
index 6accad2cc..1c5c9a4e1 100644
--- a/forms/TreeMultiselectField.php
+++ b/forms/TreeMultiselectField.php
@@ -145,7 +145,7 @@ class TreeMultiselectField extends TreeDropdownField {
'Value' => $value
)
);
- return $this->customise($properties)->renderWith('TreeDropdownField');
+ return FormField::Field($properties);
}
/**
diff --git a/forms/UploadField.php b/forms/UploadField.php
index b7b76de9c..b19595973 100644
--- a/forms/UploadField.php
+++ b/forms/UploadField.php
@@ -1016,11 +1016,11 @@ class UploadField extends FileField {
}
$mergedConfig = array_merge($config, $this->ufConfig);
- return $this->customise(array(
+ return parent::Field(array(
'configString' => str_replace('"', """, Convert::raw2json($mergedConfig)),
'config' => new ArrayData($mergedConfig),
'multiple' => $allowedMaxFileNumber !== 1
- ))->renderWith($this->getTemplates());
+ ));
}
/**
diff --git a/parsers/ShortcodeParser.php b/parsers/ShortcodeParser.php
index 354d34c36..2ab935089 100644
--- a/parsers/ShortcodeParser.php
+++ b/parsers/ShortcodeParser.php
@@ -270,11 +270,18 @@ class ShortcodeParser extends Object {
preg_match_all(static::attrrx(), $match['attrs'][0], $attrmatches, PREG_SET_ORDER);
foreach ($attrmatches as $attr) {
- list($whole, $name, $value) = array_values(array_filter($attr, function($attrPart) {
- return $attrPart !== '';
- }));
+ $name = '';
+ $value = '';
+ $parts = array_values(array_filter($attr));
+ //the first element in the array is the complete delcaration (`id=1`) - we don't need this
+ array_shift($parts);
+
+ //the next two parts are what we care about (id and 1 from `id=1`)
+ $name = array_shift($parts) ?: $name;
+ $value = array_shift($parts) ?: $value;
+
$attrs[$name] = $value;
- }
+ }
}
// And store the indexes, tag details, etc
@@ -553,6 +560,9 @@ class ShortcodeParser extends Object {
// If no content, don't try and parse it
if (!trim($content)) return $content;
+ // If no shortcode tag, don't try and parse it
+ if (strpos($content, '[') === false) return $content;
+
// First we operate in text mode, replacing any shortcodes with marker elements so that later we can
// use a proper DOM
list($content, $tags) = $this->replaceElementTagsWithMarkers($content);
diff --git a/tests/core/manifest/ConfigStaticManifestTest/ConfigStaticManifestTestClassKeyword.php b/tests/core/manifest/ConfigStaticManifestTest/ConfigStaticManifestTestClassKeyword.php
new file mode 100644
index 000000000..cfd8e740b
--- /dev/null
+++ b/tests/core/manifest/ConfigStaticManifestTest/ConfigStaticManifestTestClassKeyword.php
@@ -0,0 +1,11 @@
+inst = Injector::inst()->get(static::class);
+ }
+
+}
diff --git a/tests/model/DBHTMLTextTest.php b/tests/model/DBHTMLTextTest.php
index aa2617aa8..f1f53dddf 100644
--- a/tests/model/DBHTMLTextTest.php
+++ b/tests/model/DBHTMLTextTest.php
@@ -199,4 +199,126 @@ class DBHTMLTextTest extends SapphireTest {
'Removes any elements not in whitelist including text elements'
);
}
+
+ public function testShortCodeParsedInRAW() {
+ $parser = ShortcodeParser::get('HTMLTextTest');
+ $parser->register('shortcode', function($arguments, $content, $parser, $tagName, $extra) {
+ return 'replaced';
+ });
+ ShortcodeParser::set_active('HTMLTextTest');
+ /** @var DBHTMLText $field */
+ $field = DBField::create_field('HTMLText', '[shortcode]
');
+ $this->assertEquals('replaced
', $field->RAW());
+ $this->assertEquals('replaced
', (string)$field);
+
+ $field->setOptions(array(
+ 'shortcodes' => false,
+ ));
+
+ $this->assertEquals('[shortcode]
', $field->RAW());
+ $this->assertEquals('[shortcode]
', (string)$field);
+
+
+ ShortcodeParser::set_active('default');
+ }
+
+ public function testShortCodeParsedInTemplateHelpers() {
+ $parser = ShortcodeParser::get('HTMLTextTest');
+ $parser->register('shortcode', function($arguments, $content, $parser, $tagName, $extra) {
+ return 'Replaced short code with this. home';
+ });
+ ShortcodeParser::set_active('HTMLTextTest');
+ /** @var DBHTMLText $field */
+ $field = DBField::create_field('HTMLText', '[shortcode]
');
+
+ $this->assertEquals(
+ '<p>Replaced short code with this. <a href="home">home</a></p>',
+ $field->HTMLATT()
+ );
+ $this->assertEquals(
+ '%3Cp%3EReplaced+short+code+with+this.+%3Ca+href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E',
+ $field->URLATT()
+ );
+ $this->assertEquals(
+ '%3Cp%3EReplaced%20short%20code%20with%20this.%20%3Ca%20href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E',
+ $field->RAWURLATT()
+ );
+ $this->assertEquals(
+ '<p>Replaced short code with this. <a href="home">home</a></p>',
+ $field->ATT()
+ );
+ $this->assertEquals(
+ 'Replaced short code with this. home
',
+ $field->RAW()
+ );
+ $this->assertEquals(
+ '\x3cp\x3eReplaced short code with this. \x3ca href=\"home\"\x3ehome\x3c/a\x3e\x3c/p\x3e',
+ $field->JS()
+ );
+ $this->assertEquals(
+ '<p>Replaced short code with this. <a href="home">home</a></p>',
+ $field->HTML()
+ );
+ $this->assertEquals(
+ '<p>Replaced short code with this. <a href="home">home</a></p>',
+ $field->XML()
+ );
+ $this->assertEquals(
+ 'Repl...',
+ $field->LimitCharacters(4, '...')
+ );
+ $this->assertEquals(
+ 'Replaced...',
+ $field->LimitCharactersToClosestWord(10, '...')
+ );
+ $this->assertEquals(
+ 'Replaced...',
+ $field->LimitWordCount(1, '...')
+ );
+ $this->assertEquals(
+ 'replaced short code with this. home
',
+ $field->LowerCase()
+ );
+ $this->assertEquals(
+ 'REPLACED SHORT CODE WITH THIS. HOME
',
+ $field->UpperCase()
+ );
+ $this->assertEquals(
+ 'Replaced short code with this. home',
+ $field->NoHTML()
+ );
+ Config::nest();
+ Config::inst()->update('Director', 'alternate_base_url', 'http://example.com/');
+ $this->assertEquals(
+ 'Replaced short code with this. home
',
+ $field->AbsoluteLinks()
+ );
+ Config::unnest();
+ $this->assertEquals(
+ 'Replaced short code with this.',
+ $field->LimitSentences(1)
+ );
+ $this->assertEquals(
+ 'Replaced short code with this.',
+ $field->FirstSentence()
+ );
+ $this->assertEquals(
+ 'Replaced short...',
+ $field->Summary(2)
+ );
+ $this->assertEquals(
+ 'Replaced short code with...',
+ $field->BigSummary(4)
+ );
+ $this->assertEquals(
+ 'Replaced short code with this. home[home]',
+ $field->FirstParagraph()
+ );
+ $this->assertEquals(
+ 'Replaced short code with this. home',
+ $field->ContextSummary(500, 'short code')
+ );
+
+ ShortcodeParser::set_active('default');
+ }
}
diff --git a/tests/parsers/ShortcodeParserTest.php b/tests/parsers/ShortcodeParserTest.php
index 8a64bfc5e..85dcaa3b4 100644
--- a/tests/parsers/ShortcodeParserTest.php
+++ b/tests/parsers/ShortcodeParserTest.php
@@ -15,6 +15,12 @@ class ShortcodeParserTest extends SapphireTest {
parent::setUp();
}
+ public function tearDown() {
+ ShortcodeParser::get('test')->unregister('test_shortcode');
+
+ parent::tearDown();
+ }
+
/**
* Tests that valid short codes that have not been registered are not replaced.
*/
@@ -218,6 +224,14 @@ class ShortcodeParserTest extends SapphireTest {
);
}
+ public function testFalseyArguments() {
+ $this->parser->parse('[test_shortcode falsey=0]');
+
+ $this->assertEquals(array(
+ 'falsey' => '',
+ ), $this->arguments);
+ }
+
public function testNumericShortcodes() {
$this->assertEqualsIgnoringWhitespace(
'[2]',
@@ -240,6 +254,8 @@ class ShortcodeParserTest extends SapphireTest {
'',
$this->parser->parse('')
);
+
+ $this->parser->unregister('2');
}
public function testExtraContext() {
@@ -250,6 +266,18 @@ class ShortcodeParserTest extends SapphireTest {
$this->assertEquals($this->extra['element']->tagName, 'a');
}
+ public function testNoParseAttemptIfNoCode() {
+ $stub = $this->getMock('ShortcodeParser', array('replaceElementTagsWithMarkers'));
+ $stub->register('test', function() {
+ return '';
+ });
+
+ $stub->expects($this->never())
+ ->method('replaceElementTagsWithMarkers')->will($this->returnValue(array('', '')));
+
+ $stub->parse('
test
');
+ }
+
// -----------------------------------------------------------------------------------------------------------------
/**
diff --git a/tests/security/PermissionTest.php b/tests/security/PermissionTest.php
index 63eb0df7e..d40712681 100644
--- a/tests/security/PermissionTest.php
+++ b/tests/security/PermissionTest.php
@@ -31,6 +31,8 @@ class PermissionTest extends SapphireTest {
$members = Member::get()->byIDs($this->allFixtureIDs('SilverStripe\\Security\\Member'));
foreach ($members as $member) {
$this->assertTrue(Permission::checkMember($member, 'CMS_ACCESS'));
+ $this->assertTrue(Permission::checkMember($member, array('CMS_ACCESS', 'CMS_ACCESS_Security')));
+ $this->assertTrue(Permission::checkMember($member, array('CMS_ACCESS_Security', 'CMS_ACCESS')));
}
$member = new Member();
@@ -41,6 +43,8 @@ class PermissionTest extends SapphireTest {
));
$member->write();
$this->assertFalse(Permission::checkMember($member, 'CMS_ACCESS'));
+ $this->assertFalse(Permission::checkMember($member, array('CMS_ACCESS', 'CMS_ACCESS_Security')));
+ $this->assertFalse(Permission::checkMember($member, array('CMS_ACCESS_Security', 'CMS_ACCESS')));
}
public function testLeftAndMainAccessAll() {