Merge remote-tracking branch 'origin/3.1' into 3

Conflicts:
	control/HTTP.php
	control/HTTPResponse.php
	docs/en/05_Contributing/01_Code.md
	forms/CompositeField.php
	forms/FormAction.php
	forms/FormField.php
	forms/InlineFormAction.php
	forms/NumericField.php
	forms/TreeDropdownField.php
	forms/TreeMultiselectField.php
	templates/forms/TreeDropdownField.ss
	tests/core/CoreTest.php
	tests/forms/NumericFieldTest.php
	tests/model/DataDifferencerTest.php
This commit is contained in:
Damian Mooyman 2015-02-20 10:17:19 +13:00
commit dff65867cc
90 changed files with 1508 additions and 446 deletions

View File

@ -281,7 +281,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
$firstCrumb = $crumbs->shift(); $firstCrumb = $crumbs->shift();
if($params['FieldName'] == 'Groups') { if($params['FieldName'] == 'Groups') {
$crumbs->unshift(new ArrayData(array( $crumbs->unshift(new ArrayData(array(
'Title' => singleton('Group')->plural_name(), 'Title' => singleton('Group')->i18n_plural_name(),
'Link' => $this->Link('groups') 'Link' => $this->Link('groups')
))); )));
} elseif($params['FieldName'] == 'Users') { } elseif($params['FieldName'] == 'Users') {

View File

@ -194,8 +194,8 @@
// For embedded pages, remove the dialog hash key as in getFilePath(), // For embedded pages, remove the dialog hash key as in getFilePath(),
// otherwise the Data Url won't match the id of the embedded Page. // otherwise the Data Url won't match the id of the embedded Page.
return u.hash.split( dialogHashKey )[0].replace( /^#/, "" ); return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
} else if ( path.isSameDomain( u, documentBase ) ) { } else if ( path.isSameDomain( u, document ) ) {
return u.hrefNoHash.replace( documentBase.domain, "" ); return u.hrefNoHash.replace( document.domain, "" );
} }
return absUrl; return absUrl;
}, },
@ -227,7 +227,7 @@
//return a url path with the window's location protocol/hostname/pathname removed //return a url path with the window's location protocol/hostname/pathname removed
clean: function( url ) { clean: function( url ) {
return url.replace( documentBase.domain, "" ); return url.replace( document.domain, "" );
}, },
//just return the url without an initial # //just return the url without an initial #
@ -244,7 +244,7 @@
//could be mailto, etc //could be mailto, etc
isExternal: function( url ) { isExternal: function( url ) {
var u = path.parseUrl( url ); var u = path.parseUrl( url );
return u.protocol && u.domain !== documentUrl.domain ? true : false; return u.protocol && u.domain !== document.domain ? true : false;
}, },
hasProtocol: function( url ) { hasProtocol: function( url ) {

View File

@ -182,7 +182,7 @@ class RestfulService extends ViewableData implements Flushable {
// Check for unexpired cached feed (unless flush is set) // Check for unexpired cached feed (unless flush is set)
//assume any cache_expire that is 0 or less means that we dont want to //assume any cache_expire that is 0 or less means that we dont want to
// cache // cache
if($this->cache_expire > 0 && self::$flush if($this->cache_expire > 0 && !self::$flush
&& @file_exists($cache_path) && @file_exists($cache_path)
&& @filemtime($cache_path) + $this->cache_expire > time()) { && @filemtime($cache_path) + $this->cache_expire > time()) {

View File

@ -97,7 +97,7 @@ Once you have done that, run 'composer install' or './framework/sake dev/build'
an empty database. an empty database.
For more information, please read this page in our docs: For more information, please read this page in our docs:
http://doc.silverstripe.org/framework/en/topics/environment-management http://docs.silverstripe.org/en/getting_started/environment_management/
ENVCONTENT; ENVCONTENT;
@ -110,7 +110,7 @@ DB::connect($databaseConfig);
$url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : null; $url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : null;
if(!$url) { if(!$url) {
echo 'Please specify an argument to cli-script.php/sake. For more information, visit' echo 'Please specify an argument to cli-script.php/sake. For more information, visit'
. ' http://doc.silverstripe.org/framework/en/topics/commandline'; . ' http://docs.silverstripe.org/en/developer_guides/cli';
die(); die();
} }

View File

@ -24,6 +24,11 @@ class HTTP {
*/ */
protected static $etag = null; protected static $etag = null;
/**
* @config
*/
private static $cache_ajax_requests = true;
/** /**
* Turns a local system filename into a URL by comparing it to the script * Turns a local system filename into a URL by comparing it to the script
* filename. * filename.
@ -325,7 +330,9 @@ class HTTP {
// Popuplate $responseHeaders with all the headers that we want to build // Popuplate $responseHeaders with all the headers that we want to build
$responseHeaders = array(); $responseHeaders = array();
if(function_exists('apache_request_headers')) { $config = Config::inst();
// currently using a config setting to cancel this, seems to be so taht the CMS caches ajax requests
if(function_exists('apache_request_headers') && $config->get(get_called_class(), 'cache_ajax_requests')) {
$requestHeaders = apache_request_headers(); $requestHeaders = apache_request_headers();
if(isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With']=='XMLHttpRequest') { if(isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With']=='XMLHttpRequest') {
$cacheAge = 0; $cacheAge = 0;

View File

@ -68,7 +68,7 @@ class SS_HTTPRequest implements ArrayAccess {
protected $body; protected $body;
/** /**
* @var array $allParams Contains an assiciative array of all * @var array $allParams Contains an associative array of all
* arguments matched in all calls to {@link RequestHandler->handleRequest()}. * arguments matched in all calls to {@link RequestHandler->handleRequest()}.
* It's a "historical record" that's specific to the current call of * It's a "historical record" that's specific to the current call of
* {@link handleRequest()}, and is only complete once the "last call" to that method is made. * {@link handleRequest()}, and is only complete once the "last call" to that method is made.

View File

@ -161,6 +161,7 @@ class SS_HTTPResponse {
*/ */
public function setBody($body) { public function setBody($body) {
$this->body = $body ? (string)$body : $body; // Don't type-cast false-ish values, eg null is null not '' $this->body = $body ? (string)$body : $body; // Don't type-cast false-ish values, eg null is null not ''
return $this;
} }
/** /**
@ -191,7 +192,6 @@ class SS_HTTPResponse {
public function getHeader($header) { public function getHeader($header) {
if(isset($this->headers[$header])) if(isset($this->headers[$header]))
return $this->headers[$header]; return $this->headers[$header];
return null;
} }
/** /**
@ -238,13 +238,17 @@ class SS_HTTPResponse {
$url = Director::absoluteURL($this->headers['Location'], true); $url = Director::absoluteURL($this->headers['Location'], true);
$urlATT = Convert::raw2htmlatt($url); $urlATT = Convert::raw2htmlatt($url);
$urlJS = Convert::raw2js($url); $urlJS = Convert::raw2js($url);
echo $title = Director::isDev()
"<p>Redirecting to <a href=\"$urlATT\" title=\"Click this link if your browser does not redirect you\">" ? "{$urlATT}... (output started on {$file}, line {$line})"
. "$urlATT... (output started on $file, line $line)</a></p> : "{$urlATT}...";
<meta http-equiv=\"refresh\" content=\"1; url=$urlATT\" /> echo <<<EOT
<script type=\"text/javascript\">setTimeout(function(){ <p>Redirecting to <a href="{$urlATT}" title="Click this link if your browser does not redirect you">{$title}</a></p>
window.location.href = \"$urlJS\"; <meta http-equiv="refresh" content="1; url={$urlATT}" />
}, 50);</script>"; <script type="text/javascript">setTimeout(function(){
window.location.href = "{$urlJS}";
}, 50);</script>";
EOT
;
} else { } else {
$line = $file = null; $line = $file = null;
if(!headers_sent($file, $line)) { if(!headers_sent($file, $line)) {

View File

@ -49,14 +49,12 @@ if ($dirsToCheck[0] == $dirsToCheck[1]) {
foreach ($dirsToCheck as $dir) { foreach ($dirsToCheck as $dir) {
//check this dir and every parent dir (until we hit the base of the drive) //check this dir and every parent dir (until we hit the base of the drive)
// or until we hit a dir we can't read // or until we hit a dir we can't read
do { while(true) {
//add the trailing slash we need to concatenate properly
$dir .= DIRECTORY_SEPARATOR;
//if it's readable, go ahead //if it's readable, go ahead
if (@is_readable($dir)) { if (@is_readable($dir)) {
//if the file exists, then we include it, set relevant vars and break out //if the file exists, then we include it, set relevant vars and break out
if (file_exists($dir . $envFile)) { if (file_exists($dir . DIRECTORY_SEPARATOR . $envFile)) {
define('SS_ENVIRONMENT_FILE', $dir . $envFile); define('SS_ENVIRONMENT_FILE', $dir . DIRECTORY_SEPARATOR . $envFile);
include_once(SS_ENVIRONMENT_FILE); include_once(SS_ENVIRONMENT_FILE);
//break out of BOTH loops because we found the $envFile //break out of BOTH loops because we found the $envFile
break(2); break(2);
@ -66,11 +64,14 @@ foreach ($dirsToCheck as $dir) {
//break out of the while loop, we can't read the dir //break out of the while loop, we can't read the dir
break; break;
} }
if (dirname($dir) == $dir) {
// here we need to check that the path of the last dir and the next one are
// not the same, if they are, we have hit the root of the drive
break;
}
//go up a directory //go up a directory
$dir = dirname($dir); $dir = dirname($dir);
//here we need to check that the path of the last dir and the next one are }
// not the same, if they are, we have hit the root of the drive
} while (dirname($dir) != $dir);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -96,7 +96,7 @@ Injector::set_inst($injector);
// The coupling is a hack, but it removes an annoying bug where new classes // The coupling is a hack, but it removes an annoying bug where new classes
// referenced in _config.php files can be referenced during the build process. // referenced in _config.php files can be referenced during the build process.
$requestURL = isset($_REQUEST['url']) ? trim($_REQUEST['url'], '/') : false; $requestURL = isset($_REQUEST['url']) ? trim($_REQUEST['url'], '/') : false;
$flush = (isset($_GET['flush']) || $requestURL == 'dev/build' || $requestURL == BASE_URL . '/dev/build'); $flush = (isset($_GET['flush']) || $requestURL === trim(BASE_URL . '/dev/build', '/'));
global $manifest; global $manifest;
$manifest = new SS_ClassManifest(BASE_PATH, false, $flush); $manifest = new SS_ClassManifest(BASE_PATH, false, $flush);

View File

@ -64,8 +64,9 @@ function getTempParentFolder($base = null) {
} }
// failing the above, try finding a namespaced silverstripe-cache dir in the system temp // failing the above, try finding a namespaced silverstripe-cache dir in the system temp
$cacheFolder = DIRECTORY_SEPARATOR . 'silverstripe-cache' . str_replace(array(' ', '/', ':', '\\'), '-', $base); $tempPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR .
$tempPath = sys_get_temp_dir() . $cacheFolder; 'silverstripe-cache-php' . preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION) .
str_replace(array(' ', '/', ':', '\\'), '-', $base);
if(!@file_exists($tempPath)) { if(!@file_exists($tempPath)) {
$oldUMask = umask(0); $oldUMask = umask(0);
$worked = @mkdir($tempPath, 0777); $worked = @mkdir($tempPath, 0777);

View File

@ -31,60 +31,11 @@ if(function_exists('session_start') && !session_id()) {
session_start(); session_start();
} }
/** require_once FRAMEWORK_NAME . '/core/Constants.php'; // this also includes TempPath.php;
* Include _ss_environment.php file
*/
$usingEnv = false;
$envFileExists = false;
//define the name of the environment file
$envFile = '_ss_environment.php';
//define the dirs to start scanning from (have to add the trailing slash)
// we're going to check the realpath AND the path as the script sees it
$dirsToCheck = array(
realpath('.'),
dirname($_SERVER['SCRIPT_FILENAME'])
);
//if they are the same, remove one of them
if($dirsToCheck[0] == $dirsToCheck[1]) {
unset($dirsToCheck[1]);
}
foreach($dirsToCheck as $dir) {
//check this dir and every parent dir (until we hit the base of the drive)
// or until we hit a dir we can't read
do {
//add the trailing slash we need to concatenate properly
$dir .= DIRECTORY_SEPARATOR;
//if it's readable, go ahead
if(@is_readable($dir)) {
//if the file exists, then we include it, set relevant vars and break out
if(file_exists($dir . $envFile)) {
include_once($dir . $envFile);
$envFileExists = true;
//legacy variable assignment
$usingEnv = true;
//break out of BOTH loops because we found the $envFile
break(2);
}
} else {
//break out of the while loop, we can't read the dir
break;
}
//go up a directory
$dir = dirname($dir);
//here we need to check that the path of the last dir and the next one are
// not the same, if they are, we have hit the root of the drive
} while(dirname($dir) != $dir);
}
if($envFileExists) { $envFileExists = defined('SS_ENVIRONMENT_FILE');
if(!empty($_REQUEST['useEnv'])) { $usingEnv = $envFileExists && !empty($_REQUEST['useEnv']);
$usingEnv = true;
} else {
$usingEnv = false;
}
}
require_once FRAMEWORK_NAME . '/core/Constants.php'; // this also includes TempPath.php
require_once FRAMEWORK_NAME . '/dev/install/DatabaseConfigurationHelper.php'; require_once FRAMEWORK_NAME . '/dev/install/DatabaseConfigurationHelper.php';
require_once FRAMEWORK_NAME . '/dev/install/DatabaseAdapterRegistry.php'; require_once FRAMEWORK_NAME . '/dev/install/DatabaseAdapterRegistry.php';
@ -1554,7 +1505,7 @@ HTML;
if($base != '.') $baseClause = "RewriteBase '$base'\n"; if($base != '.') $baseClause = "RewriteBase '$base'\n";
else $baseClause = ""; else $baseClause = "";
if(strpos(strtolower(php_sapi_name()), "cgi") !== false) $cgiClause = "RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]\n"; if(strpos(strtolower(php_sapi_name()), "cgi") !== false) $cgiClause = "RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]\n";
else $cgiClause = ""; else $cgiClause = "";
$modulePath = FRAMEWORK_NAME; $modulePath = FRAMEWORK_NAME;
$rewrite = <<<TEXT $rewrite = <<<TEXT

View File

@ -25,31 +25,51 @@ Servers - this is so our changes to the php.ini take effect.
## Installing SilverStripe ## Installing SilverStripe
###Composer ### Composer
[Composer (a dependancy manager for PHP)](http://getcomposer.org) is the prefered way to install SilverStripe and ensure you get the correct set of files for your project. [Composer (a dependancy manager for PHP)](http://getcomposer.org) is the preferred way to install SilverStripe and ensure you get the correct set of files for your project.
Composer uses your MAMP PHP executible to run and also requires [git](http://git-scm.com) (so it can automatically download the required files from GitHub). Composer uses your MAMP PHP executable to run and also requires [git](http://git-scm.com) (so it can automatically download the required files from GitHub).
Process to install is as follows: #### Install composer using MAMP
1. First create an alias for our bash profile, using your preferred terminal text editor (nano, vim, etc) open `~/.bash_profile`.
1. Install [git for mac](http://git-scm.com/download/mac). 2. Add the following line (adjusting the version number of PHP to your installation of MAMP): `alias phpmamp='/Applications/MAMP/bin/php/php5.4.10/bin/php'`.
2. Install composer using the instructions at https://gist.github.com/kkirsche/5710272
3. Run the following command to get a fresh copy of SilverStripe via composer:
composer create-project silverstripe/installer /Applications/MAMP/htdocs/silverstripe/ 3. The run `. ~/.bash_profile` to reload the bash profile and make it accessible.
4. You can now [use composer](http://doc.silverstripe.org/framework/en/installation/composer) to manage future SilverStripe updates and adding modules with a few easy commands. 4. This will create an alias, `phpmamp`, allowing you to use the MAMP installation of PHP. Please take note of the PHP version, in this case 5.4.10, as with different versions of MAMP this may be different. Check your installation and see what version you have, and replace the number accordingly (this was written with MAMP version 2.1.2).
###Package Download 5. With that setup, we are ready to install `composer`. This is a two step process if we would like this to be installed globally (only do the first step if you would like `composer` installed to the local working directory only).
- First, run the following command in the terminal: `curl -sS https://getcomposer.org/installer | phpmamp`
[Download](http://silverstripe.org/download) the latest SilverStripe installer package. Copy the tar.gz file to the 'Document Root' for MAMP - By Default its `/Applications/MAMP/htdocs`. We are using `phpmamp` so that we correctly use the MAMP installation of PHP from above.
- Second, if you want to make composer available globally, we need to move the file to '/usr/local/bin/composer'. To do this, run the following command:
`sudo mv composer.phar /usr/local/bin/composer`
Terminal will ask you for your root password, after entering it and pressing the 'return' (or enter) key, you'll have a working global installation of composer on your mac that uses MAMP.
6. You can verify your installation worked by typing the following command:
`composer`
It'll show you the current version and a list of commands you can use.
7. Run the following command to get a fresh copy of SilverStripe via composer:
`composer create-project silverstripe/installer /Applications/MAMP/htdocs/silverstripe/`
8. You can now [use composer](http://doc.silverstripe.org/framework/en/getting_started/composer/) to manage future SilverStripe updates and adding modules with a few easy commands.
### Package Download
[Download](http://silverstripe.org/software/download/) the latest SilverStripe installer package. Copy the tar.gz or zip file to the 'Document Root' for MAMP - By Default its `/Applications/MAMP/htdocs`.
Don't know what your Document Root is? Open MAMP Click `Preferences -> Apache`. Don't know what your Document Root is? Open MAMP Click `Preferences -> Apache`.
Extract the tar.gz file to a folder, e.g. `silverstripe/` (you always move the tar.gz file first and not the other way Extract the tar.gz file to a folder, e.g. `silverstripe/` (you always move the tar.gz file first and not the other way
around as SilverStripe uses a '.htaccess' file which is hidden from OSX so if you move SilverStripe the .htaccess file around as SilverStripe uses a '.htaccess' file which is hidden from OSX so if you move SilverStripe the .htaccess file
won't come along. won't come along.
###Run the installation wizard ### Run the installation wizard
Once you have a copy of the required code (by either of the above methods), open your web browser and go to `http://localhost:8888/silverstripe/`. Enter your database details - by default with MAMP its user `root` and password `root` and select your account details. Click "Check Details". Once you have a copy of the required code (by either of the above methods), open your web browser and go to `http://localhost:8888/silverstripe/`. Enter your database details - by default with MAMP its user `root` and password `root` and select your account details. Click "Check Details".
Once everything is sorted hit "Install!" and Voila, you have SilverStripe installed Once everything is sorted hit "Install!" and Voila, you have SilverStripe installed

View File

@ -40,8 +40,10 @@ $ composer create-project silverstripe/installer ./silverstripe
* Unpack the archive into `C:\wamp\www` * Unpack the archive into `C:\wamp\www`
* Rename the unpacked directory from `C:\wamp\www\silverstripe-vX.X.X` to `C:\wamp\www\silverstripe` * Rename the unpacked directory from `C:\wamp\www\silverstripe-vX.X.X` to `C:\wamp\www\silverstripe`
### Install and configure ## Install and configure
* Visit `http://localhost/silverstripe` - you will see SilverStripe's installation screen. * Option 1: Environment file - Set up a file named _ss_environment.php either in the webroot or a directory above webroot and setup as per the [Environment Management process](/getting_started/environment_management).
* Option 2: Installer - Visit `http://localhost/silverstripe` - you will see SilverStripe's installation screen.
* You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two. * You should be able to click "Install SilverStripe" and the installer will do its thing. It takes a minute or two.
* Once the installer has finished, visit `http://localhost/silverstripe`. You should see your new SilverStripe site's * Once the installer has finished, visit `http://localhost/silverstripe`. You should see your new SilverStripe site's
home page. home page.
@ -61,4 +63,16 @@ alternatives for incoming connection". Make sure that it is de-selected.
Right clicked on the installation folder and go to Permissions > Security > Users > Advanced and give the user full Right clicked on the installation folder and go to Permissions > Security > Users > Advanced and give the user full
control. control.
3. If you find you are having issues with URL rewriting. Remove the index.php file that is bundled with SilverStripe. As we are using Apache web server's URL rewriting this file is not required (and in fact can result in problems when using apache 2.4+ as in the latest versions of WAMP). The other option is to enable the mod_access_compat module for apache which improves compatibility of newer versions of Apache with SilverStripe. 3. Apache rewrite (mod_rewrite) isn't working and it's installed (prior to SilverStripe 3.1.11)
Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/current/mod/mod_dir.html#DirectoryCheckHandler) (precedence of handlers), index.php gets added to the URLs as soon as you navigate to the homepage of your site. Further requests are then handled by index.php rather than `mod_rewrite` (framework/main.php). To fix this place the following within the `mod_rewrite` section of your .htaccess file:
```
<IfModule mod_rewrite.c>
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
<IfModule mod_dir.c>
DirectoryIndex disabled
</IfModule>
# ------ #
</IfModule>
```

View File

@ -25,6 +25,20 @@ On "live" environments, the `?isDev=1` solution is preferred, as it means that y
(and potentially security sensitive) PHP errors as well. (and potentially security sensitive) PHP errors as well.
</div> </div>
## mod_rewrite isn't working but it's installed (prior to SilverStripe 3.1.11)
Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/current/mod/mod_dir.html#DirectoryCheckHandler) (precedence of handlers), index.php gets added to the URLs as soon as you navigate to the homepage of your site. Further requests are then handled by index.php rather than `mod_rewrite` (framework/main.php). To fix this place the following within the `mod_rewrite` section of your .htaccess file:
```
<IfModule mod_rewrite.c>
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
<IfModule mod_dir.c>
DirectoryIndex disabled
</IfModule>
# ------ #
</IfModule>
```
## My templates don't update on page refresh ## My templates don't update on page refresh
Putting ?flush=1 on the end of any SilverStripe URL will clear out all cached content; this is a pretty common solution Putting ?flush=1 on the end of any SilverStripe URL will clear out all cached content; this is a pretty common solution

View File

@ -1,7 +1,7 @@
# Installation # Installation
These instructions show you how to install SilverStripe on any web server. These instructions show you how to install SilverStripe on any web server.
The best way to install from the source code is to use [Composer](composer). The best way to install from the source code is to use [Composer](../composer).
Check out our operating system specific guides for [Linux](linux_unix), Check out our operating system specific guides for [Linux](linux_unix),
[Windows Server](windows-pi) and [Mac OSX](mac-osx). [Windows Server](windows-pi) and [Mac OSX](mac-osx).

View File

@ -1,6 +1,9 @@
title: Building a basic site title: Building a basic site
summary: An overview of the SilverStripe installation and an introduction to creating a web page. summary: An overview of the SilverStripe installation and an introduction to creating a web page.
<div class="alert" markdown="1">
This tutorial is deprecated, and has been replaced by Lessons 1, 2, 3, and 4 in the [Lessons section](http://www.silverstripe.org/learn/lessons)
</div>
# Tutorial 1 - Building a Basic Site # Tutorial 1 - Building a Basic Site
## Overview ## Overview

View File

@ -1,6 +1,10 @@
title: Extending a basic site title: Extending a basic site
summary: Building on tutorial 1, a look at storing data in SilverStripe and creating a latest news feed. summary: Building on tutorial 1, a look at storing data in SilverStripe and creating a latest news feed.
<div class="alert" markdown="1">
This tutorial is deprecated, and has been replaced by Lessons 4, 5, and 6 in the [Lessons section](http://www.silverstripe.org/learn/lessons)
</div>
# Tutorial 2 - Extending a basic site # Tutorial 2 - Extending a basic site
## Overview ## Overview
@ -246,7 +250,7 @@ page layout.
### ArticlePage Template ### ArticlePage Template
First, the template for displaying a single article: First, the template for displaying a single article:
**themes/simple/templates/Layout/ArticlePage.ss** **themes/simple/templates/Layout/ArticlePage.ss**
:::ss :::ss
@ -409,6 +413,9 @@ An RSS feed is something that no news section should be without. SilverStripe ma
} }
Ensure that when you have input the code to implement an RSS feed; flush the webpage afterwards
(add ?flush=all on the end of your URL). This is because allowed_actions has changed.
This function creates an RSS feed of all the news articles, and outputs it to the browser. If we go to [http://localhost/your_site_name/news/rss](http://localhost/your_site_name/news/rss) we should see our RSS feed. When there is more to a URL after a page's base URL, "rss" in this case, SilverStripe will call the function with that name on the controller if it exists. This function creates an RSS feed of all the news articles, and outputs it to the browser. If we go to [http://localhost/your_site_name/news/rss](http://localhost/your_site_name/news/rss) we should see our RSS feed. When there is more to a URL after a page's base URL, "rss" in this case, SilverStripe will call the function with that name on the controller if it exists.
Depending on your browser, you should see something like the picture below. If your browser doesn't support RSS, you will most likely see the XML output instead. For more on RSS, see `[api:RSSFeed]` Depending on your browser, you should see something like the picture below. If your browser doesn't support RSS, you will most likely see the XML output instead. For more on RSS, see `[api:RSSFeed]`

View File

@ -1,6 +1,10 @@
title: DataObject Relationship Management title: DataObject Relationship Management
summary: Learn how to create custom DataObjects and how to build interfaces for managing that data. summary: Learn how to create custom DataObjects and how to build interfaces for managing that data.
<div class="alert" markdown="1">
This tutorial is deprecated, and has been replaced by Lessons 7, 8, 9, and 10 in the [Lessons section](http://www.silverstripe.org/learn/lessons)
</div>
# Tutorial 5 - Dataobject Relationship Management # Tutorial 5 - Dataobject Relationship Management
## Overview ## Overview

View File

@ -2,18 +2,24 @@ title: Tutorials
introduction: The tutorials below take a step by step look at how to build a SilverStripe application. introduction: The tutorials below take a step by step look at how to build a SilverStripe application.
## Written Tutorials ## Written Tutorials
<div class="alert" markdown="1">
[CHILDREN] These tutorials are deprecated, and have been replaced by the new [Lessons](http://silverstripe.org/learn/lessons) section.
</div>
[CHIDLREN]
## Video lessons ## Video lessons
These include video screencasts, written tutorials and code examples to get you started working with SilverStripe websites. These include video screencasts, written tutorials and code examples to get you started working with SilverStripe websites.
* [How to set up a local development environment in SilverStripe](https://vimeo.com/108861537) * [How to set up a local development environment in SilverStripe](https://vimeo.com/108861537)
* [Lesson 1: Creating your first theme](http://www.silverstripe.org/learn/lessons/lesson-1-creating-your-first-theme/) * [Lesson 1: Creating your first theme](http://www.silverstripe.org/learn/lessons/lesson-1-creating-your-first-theme/)
* [Lesson 2: Migrating static templates into your theme](http://www.silverstripe.org/learn/lessons/lesson-2-migrating-static-templates-into-your-theme/) * [Lesson 2: Migrating static templates into your theme](http://www.silverstripe.org/learn/lessons/lesson-2-migrating-static-templates-into-your-theme/)
* [Lesson 3: Adding dynamic content](http://www.silverstripe.org/learn/lessons/lesson-3/) * [Lesson 3: Adding dynamic content](http://www.silverstripe.org/learn/lessons/lesson-3-adding-dynamic-content/)
* [Lesson 4: Working with multiple templates](http://www.silverstripe.org/learn/lessons/lesson-4-working-with-multiple-templates/) * [Lesson 4: Working with multiple templates](http://www.silverstripe.org/learn/lessons/lesson-4-working-with-multiple-templates/)
* [Lesson 5: The holder/page pattern](http://www.silverstripe.org/learn/lessons/lesson-5-the-holderpage-pattern/) * [Lesson 5: The holder/page pattern](http://www.silverstripe.org/learn/lessons/lesson-5-the-holderpage-pattern/)
* [Lesson 6: Adding Custom Fields to a Page](http://www.silverstripe.org/learn/lessons/lesson-6-adding-custom-fields-to-a-page/)
* [Lesson 7: Working with Files and Images](http://www.silverstripe.org/learn/lessons/lesson-7-working-with-files-and-images/)
* [Lesson 8: Introduction to the ORM](http://www.silverstripe.org/learn/lessons/lesson-8-introduction-to-the-orm)
* [Lesson 9: Data Relationships - $has_many](http://www.silverstripe.org/learn/lessons/lesson-9-working-with-data-relationships-has-many)
* [Lesson 10: Introduction to the ORM](http://www.silverstripe.org/learn/lessons/lesson-10-working-with-data-relationships-many-many)
## Help: If you get stuck ## Help: If you get stuck

View File

@ -30,7 +30,7 @@ The extension is automatically applied to `SiteTree` class. For more information
## Database Structure ## Database Structure
Depending on how many stages you configured, two or more new tables will be created for your records. In the above, this Depending on how many stages you configured, two or more new tables will be created for your records. In the above, this
will create a new `MyClass_Live` table once you've rebuilt the database. will create a new `MyRecord_Live` table once you've rebuilt the database.
<div class="notice" markdown="1"> <div class="notice" markdown="1">
Note that the "Stage" naming has a special meaning here, it will leave the original table name unchanged, rather than Note that the "Stage" naming has a special meaning here, it will leave the original table name unchanged, rather than

View File

@ -26,7 +26,7 @@ An example is `DataObject`, SilverStripe will automatically create your CMS inte
public function getCMSFields() { public function getCMSFields() {
// parent::getCMSFields() does all the hard work and creates the fields for Title, IsActive and Content. // parent::getCMSFields() does all the hard work and creates the fields for Title, IsActive and Content.
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->fieldByName('IsActive')->setTitle('Is active?'); $fields->dataFieldByName('IsActive')->setTitle('Is active?');
return $fields; return $fields;
} }
@ -39,7 +39,7 @@ To fully customise your form fields, start with an empty FieldList.
public function getCMSFields() { public function getCMSFields() {
$fields = FieldList::create( $fields = FieldList::create(
TabSet::create("Root", TabSet::create("Root.Main",
CheckboxSetField::create('IsActive','Is active?'), CheckboxSetField::create('IsActive','Is active?'),
TextField::create('Title'), TextField::create('Title'),
TextareaField::create('Content') TextareaField::create('Content')

View File

@ -67,16 +67,14 @@ It is standard in SilverStripe for your controller actions to be `lowercasewithn
Action methods can return one of four main things: Action methods can return one of four main things:
* an array. In this case the values in the array are available in the templates and the controller completes as * an array. In this case the values in the array are available in the templates and the controller completes as usual by returning a [api:SS_HTTPResponse] with the body set to the current template.
usual by returning a [api:SS_HTTPResponse] with the body set to the current template. * `HTML`. SilverStripe will wrap the `HTML` into a `SS_HTTPResponse` and set the status code to 200.
* `HTML`. SilverStripe will wrap the `HTML` into a `SS_HTTPResponse` and set the status code to 200. * an [api:SS_HTTPResponse] containing a manually defined `status code` and `body`.
* an [api:SS_HTTPResponse] containing a manually defined `status code` and `body`. * an [api:SS_HTTPResponse_Exception]. A special type of response which indicates a error. By returning the exception, the execution pipeline can adapt and display any error handlers.
* an [api:SS_HTTPResponse_Exception]. A special type of response which indicates a error. By returning the
exception, the execution pipeline can adapt and display any error handlers.
:::php
**mysite/code/controllers/TeamController.php** **mysite/code/controllers/TeamController.php**
:::php
/** /**
* Return some additional data to the current response that is waiting to go out, this makes $Title set to * Return some additional data to the current response that is waiting to go out, this makes $Title set to
* 'MyTeamName' and continues on with generating the response. * 'MyTeamName' and continues on with generating the response.
@ -160,9 +158,8 @@ Each controller should define a `Link()` method. This should be used to avoid ha
return Controller::join_links('teams', $action); return Controller::join_links('teams', $action);
} }
<div class="info"> <div class="info" markdown="1">
The [api:Controller::join_links] is optional, but makes `Link()` more flexible by allowing an `$action` argument, and The [api:Controller::join_links] is optional, but makes `Link()` more flexible by allowing an `$action` argument, and concatenates the path segments with slashes. The action should map to a method on your controller.
concatenates the path segments with slashes. The action should map to a method on your controller.
</div> </div>
## Related Documentation ## Related Documentation

View File

@ -156,7 +156,7 @@ information on the CMS interface.
Each [api:FormField] subclass has a number of methods you can call on it to customize its' behavior or HTML markup. The Each [api:FormField] subclass has a number of methods you can call on it to customize its' behavior or HTML markup. The
default `FormField` object has several methods for doing common operations. default `FormField` object has several methods for doing common operations.
<div class="notice"> <div class="notice" markdown="1">
Most of the `set` operations will return the object back so methods can be chained. Most of the `set` operations will return the object back so methods can be chained.
</div> </div>
@ -238,14 +238,14 @@ with the particular button. In the previous example, clicking the 'Another Butto
* The `Form` instance. * The `Form` instance.
* The `Controller` instance. * The `Controller` instance.
<div class="notice"> <div class="notice" markdown="1">
If the `$action` method cannot be found on any of those or is marked as `private` or `protected`, an error will be If the `$action` method cannot be found on any of those or is marked as `private` or `protected`, an error will be
thrown. thrown.
</div> </div>
The `$action` method takes two arguments: The `$action` method takes two arguments:
* `$data` an array containing the values of the form mapped from `$name` => '$value' * `$data` an array containing the values of the form mapped from `$name => $value`
* `$form` the submitted [api:Form] instance. * `$form` the submitted [api:Form] instance.
:::php :::php

View File

@ -102,7 +102,7 @@ the same validation logic applied to it throughout.
} }
else if($this->value > 5 || $this->value < 2) { else if($this->value > 5 || $this->value < 2) {
$validator->validationError( $validator->validationError(
$this->name, "Your number must be between 2 and 5, "validation", false $this->name, "Your number must be between 2 and 5", "validation", false
); );
return false; return false;

View File

@ -57,7 +57,7 @@ First we need to define a callback for the shortcode.
); );
public function MyShortCodeMethod($arguments, $content = null, $parser = null, $tagName) { public function MyShortCodeMethod($arguments, $content = null, $parser = null, $tagName) {
return str_replace($content, "<em>$content</em>", $this->Content); return "<em>" . $tagName . "</em> " . $content . "; " . count($arguments) . " arguments.";
} }
} }

View File

@ -78,9 +78,9 @@ You will generally write two different kinds of test classes.
Tutorials and recipes for creating tests using the SilverStripe framework: Tutorials and recipes for creating tests using the SilverStripe framework:
* [Creating a SilverStripe test](creating-a-silverstripe-test): Writing tests to check core data objects * [Creating a SilverStripe test](how_tos/write_a_sapphiretest): Writing tests to check core data objects
* [Creating a functional test](creating-a-functional-test): An overview of functional tests and how to write a functional test * [Creating a functional test](how_tos/write_a_functionaltest): An overview of functional tests and how to write a functional test
* [Testing Outgoing Email](testing-email): An overview of the built-in email testing code * [Testing Outgoing Email](how_tos/testing_email): An overview of the built-in email testing code
## Running Tests ## Running Tests

View File

@ -41,10 +41,13 @@ You can indicate a log file relative to the site root.
:::php :::php
if(!Director::isDev()) { if(!Director::isDev()) {
// log errors and warnings // log errors and warnings
SS_Log::add_writer(new SS_LogFileWriter('/my/logfile/path'), SS_Log::WARN, '<='); SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors-warnings.log'), SS_Log::WARN, '<=');
// or just errors // or just errors
SS_Log::add_writer(new SS_LogFileWriter('/my/logfile/path'), SS_Log::ERR); SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors.log'), SS_Log::ERR);
// or notices (e.g. for Deprecation Notifications)
SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors-notices.log'), SS_Log::NOTICE);
} }
<div class="info" markdown="1"> <div class="info" markdown="1">
@ -62,7 +65,7 @@ You can send both fatal errors and warnings in your code to a specified email-ad
:::php :::php
if(!Director::isDev()) { if(!Director::isDev()) {
// log errors and warnings // log errors and warnings
SS_Log::add_writer(new SS_LogEmailWriter('admin@domain.com'), SS_Log::WARN, '<='); SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors-warnings.log'), SS_Log::WARN, '<=');
// or just errors // or just errors
SS_Log::add_writer(new SS_LogEmailWriter('admin@domain.com'), SS_Log::ERR); SS_Log::add_writer(new SS_LogEmailWriter('admin@domain.com'), SS_Log::ERR);

View File

@ -159,7 +159,7 @@ To cache the contents of a page for all anonymous users, but dynamically calcula
## Uncached ## Uncached
Yhe template tag 'uncached' can be used - it is the exact equivalent of a cached block with an if condition that always The template tag 'uncached' can be used - it is the exact equivalent of a cached block with an if condition that always
returns false. The key and conditionals in an uncached tag are ignored, so you can easily temporarily disable a returns false. The key and conditionals in an uncached tag are ignored, so you can easily temporarily disable a
particular cache block by changing just the tag, leaving the key and conditional intact. particular cache block by changing just the tag, leaving the key and conditional intact.

View File

@ -51,7 +51,7 @@ The returned object is of type `Zend_Cache`.
$cache = SS_Cache::factory('foo'); $cache = SS_Cache::factory('foo');
if (!($result = $cache->load($cachekey))) { if (!($result = $cache->load($cachekey))) {
$result = caluate some how; $result = caluate some how;
$cache->save($result); $cache->save($result, $cachekey);
} }
return $result; return $result;
@ -103,7 +103,7 @@ which can provide better performance, including APC, Xcache, ZendServer, Memcach
If `?flush=1` is requested in the URL, e.g. http://mysite.com?flush=1, this will trigger a call to `flush()` on If `?flush=1` is requested in the URL, e.g. http://mysite.com?flush=1, this will trigger a call to `flush()` on
any classes that implement the `Flushable` interface. Using this, you can trigger your caches to clean. any classes that implement the `Flushable` interface. Using this, you can trigger your caches to clean.
See [reference documentation on Flushable](/reference/flushable) for implementation details. See [reference documentation on Flushable](/developer_guides/execution_pipeline/flushable/) for implementation details.
### Memcached ### Memcached

View File

@ -91,7 +91,7 @@ and another subclass for the same email-address in the address-database.
Using inheritance to add extra behaviour or data fields to a member is limiting, because you can only inherit from 1 Using inheritance to add extra behaviour or data fields to a member is limiting, because you can only inherit from 1
class. A better way is to use role extensions to add this behaviour. Add the following to your class. A better way is to use role extensions to add this behaviour. Add the following to your
`[config.yml](/topics/configuration)`. `[config.yml](/developer_guides/configuration/configuration/#configuration-yaml-syntax-and-rules)`.
:::yml :::yml
Member: Member:

View File

@ -8,21 +8,24 @@ authentication system.
The main login system uses these controllers to handle the various security requests: The main login system uses these controllers to handle the various security requests:
* `[api:Security]` Which is the controller which handles most front-end security requests, including `[api:Security]` Which is the controller which handles most front-end security requests, including
Logging in, logging out, resetting password, or changing password. This class also provides an interface Logging in, logging out, resetting password, or changing password. This class also provides an interface
to allow configured `[api:Authenticator]` classes to each display a custom login form. to allow configured `[api:Authenticator]` classes to each display a custom login form.
* `[api:CMSSecurity]` Which is the controller which handles security requests within the CMS, and allows
`[api:CMSSecurity]` Which is the controller which handles security requests within the CMS, and allows
users to re-login without leaving the CMS. users to re-login without leaving the CMS.
## Member Authentication ## Member Authentication
The default member authentication system is implemented in the following classes: The default member authentication system is implemented in the following classes:
* `[api:MemberAuthenticator]` Which is the default member authentication implementation. This uses the email `[api:MemberAuthenticator]` Which is the default member authentication implementation. This uses the email
and password stored internally for each member to authenticate them. and password stored internally for each member to authenticate them.
* `[api:MemberLoginForm]` Is the default form used by `MemberAuthenticator`, and is displayed on the public site
`[api:MemberLoginForm]` Is the default form used by `MemberAuthenticator`, and is displayed on the public site
at the url `Security/login` by default. at the url `Security/login` by default.
* `[api:CMSMemberLoginForm]` Is the secondary form used by `MemberAuthenticator`, and will be displayed to the
`[api:CMSMemberLoginForm]` Is the secondary form used by `MemberAuthenticator`, and will be displayed to the
user within the CMS any time their session expires or they are logged out via an action. This form is user within the CMS any time their session expires or they are logged out via an action. This form is
presented via a popup dialog, and can be used to re-authenticate that user automatically without them having presented via a popup dialog, and can be used to re-authenticate that user automatically without them having
to lose their workspace. E.g. if editing a form, the user can login and continue to publish their content. to lose their workspace. E.g. if editing a form, the user can login and continue to publish their content.
@ -32,10 +35,11 @@ The default member authentication system is implemented in the following classes
Additional authentication methods (oauth, etc) can be implemented by creating custom implementations of each of the Additional authentication methods (oauth, etc) can be implemented by creating custom implementations of each of the
following base classes: following base classes:
* `[api:Authenticator]` The base class for authentication systems. This class also acts as the factory `[api:Authenticator]` The base class for authentication systems. This class also acts as the factory
to generate various login forms for parts of the system. If an authenticator supports in-cms to generate various login forms for parts of the system. If an authenticator supports in-cms
reauthentication then it will be necessary to override the `supports_cms` and `get_cms_login_form` methods. reauthentication then it will be necessary to override the `supports_cms` and `get_cms_login_form` methods.
* `[api:LoginForm]` which is the base class for a login form which links to a specific authenticator. At the very
`[api:LoginForm]` which is the base class for a login form which links to a specific authenticator. At the very
least, it will be necessary to implement a form class which provides a default login interface. If in-cms least, it will be necessary to implement a form class which provides a default login interface. If in-cms
re-authentication is desired, then a specialised subclass of this method may be necessary. For example, this form re-authentication is desired, then a specialised subclass of this method may be necessary. For example, this form
could be extended to require confirmation of username as well as password. could be extended to require confirmation of username as well as password.

View File

@ -97,7 +97,7 @@ For output of an image tag with the image automatically resized to 80px width, y
$Image.SetSize(80,80) // returns a 80x80px padded image $Image.SetSize(80,80) // returns a 80x80px padded image
$Image.SetRatioSize(80,80) // Returns an image scaled proportional, with its greatest diameter scaled to 80px $Image.SetRatioSize(80,80) // Returns an image scaled proportional, with its greatest diameter scaled to 80px
$Image.CroppedImage(80,80) // Returns an 80x80 image cropped from the center. $Image.CroppedImage(80,80) // Returns an 80x80 image cropped from the center.
$Image.PaddedImage(80, 80) // Returns an 80x80 image. Unused space is padded white. No crop. No stretching $Image.PaddedImage(80, 80, FFFFFF) // Returns an 80x80 image. Unused space is padded white. No crop. No stretching
$Image.Width // returns width of image $Image.Width // returns width of image
$Image.Height // returns height of image $Image.Height // returns height of image
$Image.Orientation // returns Orientation $Image.Orientation // returns Orientation

View File

@ -150,7 +150,7 @@ The results are shown in a tabular listing, powered by the [GridField](../forms/
the [api:GridFieldDataColumns] component. This component looks for a [api:DataObject::$summary_fields] static on your the [api:GridFieldDataColumns] component. This component looks for a [api:DataObject::$summary_fields] static on your
model class, where you can add or remove columns. To change the title, use [api:DataObject::$field_labels]. model class, where you can add or remove columns. To change the title, use [api:DataObject::$field_labels].
**mysite/code/Page.php** **mysite/code/Product.php**
:::php :::php
<?php <?php

View File

@ -72,9 +72,9 @@ Load the file in the CMS via setting adding 'mysite/javascript/MyLeftAndMain.Pre
to the `LeftAndMain.extra_requirements_javascript` [configuration value](/topics/configuration) to the `LeftAndMain.extra_requirements_javascript` [configuration value](/topics/configuration)
:::yml :::yml
LeftAndMain LeftAndMain:
extra_requirements_javascript: extra_requirements_javascript:
- mysite/javascript/MyLeftAndMain.Preview.js: - mysite/javascript/MyLeftAndMain.Preview.js
In order to find out which configuration values are available, the source code In order to find out which configuration values are available, the source code
is your best reference at the moment - have a look in `framework/admin/javascript/LeftAndMain.Preview.js`. is your best reference at the moment - have a look in `framework/admin/javascript/LeftAndMain.Preview.js`.

View File

@ -31,7 +31,7 @@ This example uses `[api:SS_Cache]` in some custom code, and the same cache is cl
$something = $cache->load('mykey'); $something = $cache->load('mykey');
if(!$something) { if(!$something) {
$something = 'value to be cached'; $something = 'value to be cached';
$cache->save($something); $cache->save($something, 'mykey');
} }
return $something; return $something;
} }

View File

@ -76,7 +76,7 @@ The chapter on [configuration](/topics/configuration) has more details.
## Flushing ## Flushing
If a `?flush=1` query parameter is added to a URL, a call to `flush()` will be triggered If a `?flush=1` query parameter is added to a URL, a call to `flush()` will be triggered
on any classes that implement the [Flushable](/reference/flushable) interface. on any classes that implement the [Flushable](flushable) interface.
This enables developers to clear [manifest caches](manifests), This enables developers to clear [manifest caches](manifests),
for example when adding new templates or PHP classes. for example when adding new templates or PHP classes.
Note that you need to be in [dev mode](/getting_started/environment_management) Note that you need to be in [dev mode](/getting_started/environment_management)

View File

@ -130,7 +130,7 @@ The ["Request Filters" documentation](../controller/request_filters) shows you h
## Flushing Manifests ## Flushing Manifests
If a `?flush=1` query parameter is added to a URL, a call to `flush()` will be triggered If a `?flush=1` query parameter is added to a URL, a call to `flush()` will be triggered
on any classes that implement the [Flushable](/reference/flushable) interface. on any classes that implement the [Flushable](flushable) interface.
This enables developers to clear [manifest caches](manifests), This enables developers to clear [manifest caches](manifests),
for example when adding new templates or PHP classes. for example when adding new templates or PHP classes.
Note that you need to be in [dev mode](/getting_started/environment_management) Note that you need to be in [dev mode](/getting_started/environment_management)

View File

@ -0,0 +1,26 @@
# 3.0.10
## Upgrading
* If relying on partial caching of content between logged in users, be aware that the cache is now automatically
segmented based on both the current member ID, and the versioned reading mode. If this is not an appropriate
method (such as if the same content is served to logged in users within partial caching) then it is necessary
to adjust the config value of `SSViewer.global_key` to something more or less sensitive.
## Security
* [BUG Fix issue with versioned dataobjects being cached between stages](https://github.com/silverstripe/silverstripe-framework/commit/4415a75d9304a3930b9c28763fc092299640c685) - See [announcement SS-2014-007](http://www.silverstripe.org/ss-2014-007-confidentiality-breach-can-occur-between-draft-and-live-modes/)
* [BUG Fix encoding of JS redirection script](https://github.com/silverstripe/silverstripe-framework/commit/f8e3bbe3ae3f29f22d85abb73cea033659511168) - See [announcement SS-2014-006](http://www.silverstripe.org/ss-2014-006-xss-in-returnurl-redirection/)
* [Amends solution to SS-2014-006](https://github.com/silverstripe/silverstripe-framework/commit/5b0a96979484fad12e11ce69aef98feda57b321f)
* [FIX Prevent SQLi when no URL filters are applied](https://github.com/silverstripe/silverstripe-cms/commit/114df8a3a5e4800ef7586c5d9c8d79798fd2a11d) - See [announcement SS-2014-004](http://www.silverstripe.org/ss-2014-004-sql-injection-in-sitetree-with-custom-urlsegmentfilter-rules/)
* [FIX Do now allow arbitary class creation in CMS](https://github.com/silverstripe/silverstripe-cms/commit/bf9b22fd4331a6f78cec12a75262f570b025ec2d) - See [announcement SS-2014-005](http://www.silverstripe.org/ss-2014-005-arbitrary-class-creation-in-cms-backend/)
## General
* [Rewrote usages of error suppression operator](https://github.com/silverstripe/silverstripe-framework/commit/6d5d3d8cb7e69e0b37471b1e34077211b0f631fe)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.10)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.0.10)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.0.10)

View File

@ -0,0 +1,19 @@
# 3.0.11
Minor security release
## Security
* 2014-04-16 [9d74bc4](https://github.com/silverstripe/sapphire/commit/9d74bc4) Potential DoS exploit in TinyMCE - See [announcement SS-2014-009](http://www.silverstripe.org/ss-2014-009-potential-dos-exploit-in-tinymce/)
* 2014-05-05 [9bfeffd](https://github.com/silverstripe/silverstripe-framework/commit/9bfeffd) Injection / Filesystem vulnerability in generatesecuretoken - See [announcement SS-2014-010](http://www.silverstripe.org/ss-2014-010-injection-filesystem-vulnerability-in-generatesecuretoken/)
* 2014-05-07 [0099a18](https://github.com/silverstripe/silverstripe-framework/commit/0099a18) Folder filename injection - See [announcement SS-2014-011](http://www.silverstripe.org/ss-2014-011-folder-filename-injection/)
### Bugfixes
* 2013-06-20 [f2c4a62](https://github.com/silverstripe/sapphire/commit/f2c4a62) ConfirmedPasswordField used to expose existing hash (Hamish Friedlander)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.11)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.0.11)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.0.11)

View File

@ -0,0 +1,57 @@
# 3.1.10
## Upgrading
### Form Actions
Form action titles are now safely XML encoded, although this was an implicit assumption, it is now explicitly enforced.
XML encoding titles will not cause an error, but is deprecated at framework 4.0. FormAction buttons with custom HTML
content should be assigned using the `FormAction::setButtonContent` method instead.
## Security
Several medium and some low level security XSS (cross site scripting) vulnerabilites have been closed in this release.
All users of SilverStripe framework 3.1.9 and below are advised to upgrade.
* 2015-02-10 [1db08ba](https://github.com/silverstripe/sapphire/commit/1db08ba) Fix FormAction title encoding (Damian Mooyman) -
See announcement [ss-2015-007](http://www.silverstripe.org/software/download/security-releases/ss-2015-007)
* 2015-02-10 [1db08ba](https://github.com/silverstripe/sapphire/commit/1db08ba) Core CMS XSS Vulnerability Fixes (Damian Mooyman) -
See announcements
[ss-2015-003](http://www.silverstripe.org/software/download/security-releases/ss-2015-003),
[ss-2015-004](http://www.silverstripe.org/software/download/security-releases/ss-2015-004),
[ss-2015-006](http://www.silverstripe.org/software/download/security-releases/ss-2015-006)
* 2015-01-22 [7733c43](https://github.com/silverstripe/silverstripe-cms/commit/7733c43) Correctly sanitise Title (Michael Strong) -
See announcement [SS-2015-005](http://www.silverstripe.org/software/download/security-releases/ss-2015-005)
* 2015-02-05 [70e0d60](https://github.com/silverstripe/sapphire/commit/70e0d60) Fix developer output in redirection script (Damian Mooyman) -
See announcement [SS-2015-001](http://www.silverstripe.org/software/download/security-releases/ss-2015-001)
### Features and Enhancements
* 2015-01-22 [2e4bf9a](https://github.com/silverstripe/sapphire/commit/2e4bf9a) Update sake to reference new docs (Cam Findlay)
### Bugfixes
* 2015-02-17 [aa77e12](https://github.com/silverstripe/sapphire/commit/aa77e12) Fixed infinity loop when searching _ss_environment (Zauberfish)
* 2015-02-12 [047fe3a](https://github.com/silverstripe/sapphire/commit/047fe3a) Include php version in default cache folder name Update CoreTest.php (JorisDebonnet)
* 2015-02-08 [a530085](https://github.com/silverstripe/silverstripe-cms/commit/a530085) External redirects shouldnt show in preview pane (Daniel Hensby)
* 2015-02-06 [d68435e](https://github.com/silverstripe/sapphire/commit/d68435e) SelectionGroup no longer shows empty FieldLists (Daniel Hensby)
* 2015-02-06 [a0f9535](https://github.com/silverstripe/sapphire/commit/a0f9535) issue where empty composite fields created a fieldlist with empty items (Daniel Hensby)
* 2015-02-03 [abd1e6b](https://github.com/silverstripe/sapphire/commit/abd1e6b) GridFieldExportButton should honour can method. (Will Rossiter)
* 2015-01-22 [eed7093](https://github.com/silverstripe/sapphire/commit/eed7093) dev/build not flushing manifests if site is in a subfolder (Loz Calver)
* 2015-01-19 [77ebdc2](https://github.com/silverstripe/sapphire/commit/77ebdc2) DataObject::db returned fields in incorrect order, with incorrect data types (Loz Calver)
* 2015-01-15 [32ce85d](https://github.com/silverstripe/sapphire/commit/32ce85d) . Summary fields can't be translated (Elvinas L.)
* 2015-01-13 [2e6e8af](https://github.com/silverstripe/sapphire/commit/2e6e8af) insert media trims whitespace - fixes #845 (Emma O'Keefe)
* 2015-01-13 [2861e7c](https://github.com/silverstripe/sapphire/commit/2861e7c) insert media trims whitespace fixes #845 (Emma O'Keefe)
* 2015-01-09 [ef237f6](https://github.com/silverstripe/sapphire/commit/ef237f6) Expands the CMS' centre-pane when collapsed and it's clicked. (Russell Michell)
* 2014-10-24 [9d78eb7](https://github.com/silverstripe/sapphire/commit/9d78eb7) Fix BasicAuth not resetting failed login counts on authentication (Damian Mooyman)
* 2014-10-16 [e4ddb4b](https://github.com/silverstripe/sapphire/commit/e4ddb4b) Ensure query string in X-Backurl is encoded (fixes #3563) (Loz Calver)
* 2014-08-25 [f823831](https://github.com/silverstripe/sapphire/commit/f823831) making minify javascript fail-safe (Igor Nadj)
* 2014-04-03 [5180452](https://github.com/silverstripe/sapphire/commit/5180452) Fixed handling of numbers in certain locales. Fixes #2161 (Damian Mooyman)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.10)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.10)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.10)

View File

@ -0,0 +1,29 @@
# 3.1.3
## Overview
* Security: Require ADMIN for ?flush=1&isDev=1 ([SS-2014-001](http://www.silverstripe.org/ss-2014-001-require-admin-for-flush1-and-isdev1))
* Security: XSS in third party library (SWFUpload) ([SS-2014-002](http://www.silverstripe.org/ss-2014-002-xss-in-third-party-library-swfupload/))
* Security: SiteTree.ExtraMeta allows JavaScript for malicious CMS authors ([SS-2014-003](http://www.silverstripe.org/ss-2014-003-extrameta-allows-javascript-for-malicious-cms-authors-/))
* Better loading performance when using multiple `UploadField` instances
* Option for `force_js_to_bottom` on `Requirements` class (ignoring inline `<script>` tags)
* Added `ListDecorator->filterByCallback()` for more sophisticated filtering
* New `DataList` filters: `LessThanOrEqualFilter` and `GreaterThanOrEqualFilter`
* "Cancel" button on "Add Page" form
* Better code hinting on magic properties (for IDE autocompletion)
* Increased Behat test coverage (editing HTML content, managing page permissions)
* Support for PHPUnit 3.8
## Upgrading
### SiteTree.ExtraMeta allows JavaScript for malicious CMS authors
If you have previously used the `SiteTree.ExtraMeta` field for `<head>` markup
other than its intended use case (`<meta>` and `<link>`), please consult
[SS-2014-003](http://www.silverstripe.org/ss-2014-003-extrameta-allows-javascript-for-malicious-cms-authors-/).
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.3)
* [cms](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.3)
* [installer](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.3)

View File

@ -0,0 +1,46 @@
# 3.1.4
## Upgrading
* If relying on partial caching of content between logged in users, be aware that the cache is now automatically
segmented based on both the current member ID, and the versioned reading mode. If this is not an appropriate
method (such as if the same content is served to logged in users within partial caching) then it is necessary
to adjust the config value of `SSViewer.global_key` to something more or less sensitive.
## Security
* [BUG Fix issue with versioned dataobjects being cached between stages](https://github.com/silverstripe/silverstripe-framework/commit/4415a75d9304a3930b9c28763fc092299640c685) - See [announcement SS-2014-007](http://www.silverstripe.org/ss-2014-007-confidentiality-breach-can-occur-between-draft-and-live-modes/)
* [BUG Fix encoding of JS redirection script](https://github.com/silverstripe/silverstripe-framework/commit/f8e3bbe3ae3f29f22d85abb73cea033659511168) - See [announcement SS-2014-006](http://www.silverstripe.org/ss-2014-006-xss-in-returnurl-redirection/)
* [Amends solution to SS-2014-006](https://github.com/silverstripe/silverstripe-framework/commit/5b0a96979484fad12e11ce69aef98feda57b321f)
* [FIX Prevent SQLi when no URL filters are applied](https://github.com/silverstripe/silverstripe-cms/commit/114df8a3a5e4800ef7586c5d9c8d79798fd2a11d) - See [announcement SS-2014-004](http://www.silverstripe.org/ss-2014-004-sql-injection-in-sitetree-with-custom-urlsegmentfilter-rules/)
* [FIX Do now allow arbitary class creation in CMS](https://github.com/silverstripe/silverstripe-cms/commit/bf9b22fd4331a6f78cec12a75262f570b025ec2d) - See [announcement SS-2014-005](http://www.silverstripe.org/ss-2014-005-arbitrary-class-creation-in-cms-backend/)
## Bugfixes
* [Fix Versioned::augmentSQL() when the data query was null.](https://github.com/silverstripe/silverstripe-framework/commit/deb1bfbcbaaa62acb2263ba797b5068e142a6353)
* [FIX UploadField validation error and styles](https://github.com/silverstripe/silverstripe-framework/commit/02bceca9b478358bdd569c16818d3be2467beb64)
* [FIX Overriding of theme templates in project folder](https://github.com/silverstripe/silverstripe-framework/commit/5f87d344f11c382dbee3fae8edfc00bb9a5a0265)
* [BUG Ensure TreeMultiSelectField doesn't populate menus with "unchanged".](https://github.com/silverstripe/silverstripe-framework/commit/9e2c7b657221c336137e07985bd5994682216d65)
* [BUG: #2503 Fixes performReadonlyTransformation for OptionSetField](https://github.com/silverstripe/silverstripe-framework/commit/44a8537f68872f0587cdf4cceadd433817dfdf60)
* [FIX: Rewrite Member getCMSFields to ensure updateCMSFields is only run once](https://github.com/silverstripe/silverstripe-framework/commit/d91c7d14b84d8b3caed948b0bbab94d254ea2b96)
* [FIX: Ensure valid CSS classes for GridField header](https://github.com/silverstripe/silverstripe-framework/commit/90952e7bd4bf7a278959ff320b3a71d30596f5d8)
* [BUG Fix case where setFolder('/') would break UploadField::fileexists](https://github.com/silverstripe/silverstripe-framework/commit/c1e0f98f87fa58edf7967d818732c7467cf47d80)
* [BUG Prevent unnecessary reconstruction of ClassName field after default records are generated](https://github.com/silverstripe/silverstripe-framework/commit/53b5adbcd98ff4d0e3947f4472b7b7b62a2b064a)
* [BUG Fix DataObject::loadLazyFields discarding original query parameters](https://github.com/silverstripe/silverstripe-framework/commit/23f5f08eda4201e0d3d4c28b81805da10b55bdb1)
* [Upload: retrieve existing File if an object without an ID is given and replaceFile=true](https://github.com/silverstripe/silverstripe-framework/commit/3c1e82b42c282ab64dfe7f5a68a50f59d8ebcc69)
* [BUG Fix Date and SS_DateTime::FormatFromSettings](https://github.com/silverstripe/silverstripe-framework/commit/84d8022b326e3938753430678cfc3dfa50770d83)
## API
* [Add support for many_many_extraField in YAML](https://github.com/silverstripe/silverstripe-framework/commit/8b923006227b0177983c96b949edaa6df18fbbf8)
* [Allow vetoing forgot password requests](https://github.com/silverstripe/silverstripe-framework/commit/9afcf8f01ac6b5c3c054b9a49f1731d35aa868ed)
## General
* [Rewrote usages of error suppression operator](https://github.com/silverstripe/silverstripe-framework/commit/6d5d3d8cb7e69e0b37471b1e34077211b0f631fe)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.4)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.4)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.4)

View File

@ -0,0 +1,67 @@
# 3.1.5
## Upgrading
* If running an application in an environment where user security is critical, it may be necessary to
assign the config value `Security.remember_username` to false. This will disable persistence of
user login name between sessions, and disable browser auto-completion on the username field.
Note that users of certain browsers who have previously autofilled and saved login credentials
will need to clear their password autofill history before this setting is properly respected.
* Test cases that rely on updating and restoring `[api:Injector]` services may now take advantage
of the new `Injector::nest()` and `Injector::unnest()` methods to sandbox their alterations.
* If errors could potentially be raised by any `[api:RequestHandler]` class such as a `[api:Form]` or
`[api:Controller]`, you may now add the new `[api:ErrorPageControllerExtension]` to this class to
transform plain text error messages into `ErrorPage` rendered HTML errors. In the past this
behaviour was limited to subclasses of `[api:ContentController]`. By default this extension is now
added to the `Security` controller, and if this is not desirable then it should be removed
explicitly via the Config system.
## Security
* 2014-04-16 [bde16f0](https://github.com/silverstripe/sapphire/commit/bde16f0) Potential DoS exploit in TinyMCE - See [announcement SS-2014-009](http://www.silverstripe.org/ss-2014-009-potential-dos-exploit-in-tinymce/)
* 2014-05-05 [d9bc352](https://github.com/silverstripe/silverstripe-framework/commit/d9bc352) Injection / Filesystem vulnerability in generatesecuretoken - See [announcement SS-2014-010](http://www.silverstripe.org/ss-2014-010-injection-filesystem-vulnerability-in-generatesecuretoken/)
* 2014-05-02 [8e841cc](https://github.com/silverstripe/sapphire/commit/8e841cc) Folder filename injection - See [announcement SS-2014-011](http://www.silverstripe.org/ss-2014-011-folder-filename-injection/)
* 2014-05-05 [df28ccb](https://github.com/silverstripe/sapphire/commit/df28ccb) Upload fileexists vulnerability - See [announcement SS-2014-013](http://www.silverstripe.org/ss-2014-013-upload-fileexists-vulnerability/)
### API Changes
* 2014-05-02 [f9cb880](https://github.com/silverstripe/silverstripe-cms/commit/f9cb880) Error page support for Security controller errors (Damian Mooyman)
* 2014-05-01 [3162d0e](https://github.com/silverstripe/silverstripe-cms/commit/3162d0e) Update ErrorPage to respect new HTTP Error codes (Damian Mooyman)
* 2014-04-28 [0285322](https://github.com/silverstripe/silverstripe-cms/commit/0285322) Ability to configure paging for assets / pages (Damian Mooyman)
* 2014-04-22 [d06d5c1](https://github.com/silverstripe/sapphire/commit/d06d5c1) Injector supports nesting BUG Resolve issue with DirectorTest breaking RequestProcessor Injector::nest and Injector::unnest are introduced to better support sandboxing of testings. Injector and Config ::nest and ::unnest support chaining Test cases for both Injector::nest and Config::nest (Damian Mooyman)
* 2014-04-17 [a6017a0](https://github.com/silverstripe/sapphire/commit/a6017a0) HTTP 429 Allowed for use with rate limiting methods (Damian Mooyman)
* 2014-04-11 [892b440](https://github.com/silverstripe/sapphire/commit/892b440) Make default gridfield paging configurable Documentation improved (Damian Mooyman)
* 2014-04-09 [997077a](https://github.com/silverstripe/sapphire/commit/997077a) Security.remember_username to disable login form autocompletion (Damian Mooyman)
### Features and Enhancements
* 2014-03-28 [a502c9d](https://github.com/silverstripe/silverstripe-cms/commit/a502c9d) Fixes #966. Ability to filter pages on page status. - New filters for statuses normally found through SiteTree::getStatusFlags(). - Refactored menu sorting. Now alphabetical, as it wasn't previously. (Russell Michell)
* 2014-04-11 [3765030](https://github.com/silverstripe/silverstripe-cms/commit/3765030) Filter by date created for files Added test cases Do not merge before https://github.com/silverstripe-labs/silverstripe-behat-extension/pull/32 (Damian Mooyman)
### Bugfixes
* 2014-05-05 [c5d5d10](https://github.com/silverstripe/silverstripe-cms/commit/c5d5d10) Behat now uses explicit radio button behaviour (Damian Mooyman)
* 2014-05-01 [bd5abb6](https://github.com/silverstripe/sapphire/commit/bd5abb6) parent::init is not called first (Michael Parkhill)
* 2014-05-01 [4fd3015](https://github.com/silverstripe/sapphire/commit/4fd3015) corrected link to CMS Alternating Button Page (James Pluck)
* 2014-04-29 [8673b11](https://github.com/silverstripe/sapphire/commit/8673b11) Fix ImageTest Image test would erroneously reset the Image::$backend to null if the test was skipped, breaking subsequent test cases (Damian Mooyman)
* 2014-04-29 [89fbae2](https://github.com/silverstripe/silverstripe-cms/commit/89fbae2) Fix encoding of SiteTree.MetaTags (Damian Mooyman)
* 2014-04-25 [ff5f607](https://github.com/silverstripe/sapphire/commit/ff5f607) Docs for DataList::filter() (Daniel Hensby)
* 2014-04-24 [5e9ae57](https://github.com/silverstripe/sapphire/commit/5e9ae57) Fix edge case IE8 / dev / ssl / download file crash Prevents issue at http://support.microsoft.com/kb/323308 appearing on dev (Damian Mooyman)
* 2014-04-17 [bec8927](https://github.com/silverstripe/sapphire/commit/bec8927) Allow PHPUnit installation with composer / Fix travis (Will Morgan)
* 2014-04-16 [396fd9a](https://github.com/silverstripe/silverstripe-cms/commit/396fd9a) Broken file link tracking (fixes #996) (Loz Calver)
* 2014-04-14 [0b4f62d](https://github.com/silverstripe/sapphire/commit/0b4f62d) Fix jstree when duplicating subtrees (Damian Mooyman)
* 2014-04-11 [a261f22](https://github.com/silverstripe/sapphire/commit/a261f22) Delete Character \x01 (Stevie Mayhew)
* 2014-04-09 [91034d1](https://github.com/silverstripe/sapphire/commit/91034d1) HTMLText whitelist considers text nodes Minor improvement to #2853. If a list of whitelisted elements are specified, text nodes no longer evade the whitelist (Damian Mooyman)
* 2014-04-09 [a3c8a59](https://github.com/silverstripe/sapphire/commit/a3c8a59) Fix data query not always joining necessary tables Fixes #2846 (Damian Mooyman)
* 2014-04-08 [a060784](https://github.com/silverstripe/sapphire/commit/a060784) - missing link url for composer (camfindlay)
* 2014-04-07 [3204ab5](https://github.com/silverstripe/silverstripe-cms/commit/3204ab5) Fix orphaned pages reporting they can be viewed (Damian Mooyman)
* 2014-04-01 [84d8022](https://github.com/silverstripe/sapphire/commit/84d8022) Fix Date and SS_DateTime::FormatFromSettings This issue is caused by the odd default behaviour of Zend_Date, which attempts to parse yyyy-mm-dd format date and times as though they were yyyy-dd-mm. (Damian Mooyman)
* 2014-03-12 [b4a1aa4](https://github.com/silverstripe/silverstripe-cms/commit/b4a1aa4) Fixes #965. Allow user date-settings to show on GridField Page admin (Russell Michell)
* 2014-03-04 [ae573f8](https://github.com/silverstripe/sapphire/commit/ae573f8) Fix Versioned stage not persisting in Session. Fixes #962 BUG Disabled disruptive test case in DirectorTest API RequestProcessor and VersionedRequestFilter now both correctly implement RequestFilter Better PHPDoc on RequestFilter and implementations (Damian Mooyman)
* 2013-06-20 [f2c4a62](https://github.com/silverstripe/sapphire/commit/f2c4a62) ConfirmedPasswordField used to expose existing hash (Hamish Friedlander)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.5)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.5)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.5)

View File

@ -0,0 +1,25 @@
# 3.0.10-rc1
## Upgrading
* If relying on partial caching of content between logged in users, be aware that the cache is now automatically
segmented based on both the current member ID, and the versioned reading mode. If this is not an appropriate
method (such as if the same content is served to logged in users within partial caching) then it is necessary
to adjust the config value of `SSViewer.global_key` to something more or less sensitive.
## Security
* [BUG Fix issue with versioned dataobjects being cached between stages](https://github.com/silverstripe/silverstripe-framework/commit/4415a75d9304a3930b9c28763fc092299640c685) - See [announcement SS-2014-007](http://www.silverstripe.org/ss-2014-007-confidentiality-breach-can-occur-between-draft-and-live-modes/)
* [BUG Fix encoding of JS redirection script](https://github.com/silverstripe/silverstripe-framework/commit/f8e3bbe3ae3f29f22d85abb73cea033659511168) - See [announcement SS-2014-006](http://www.silverstripe.org/ss-2014-006-xss-in-returnurl-redirection/)
* [FIX Prevent SQLi when no URL filters are applied](https://github.com/silverstripe/silverstripe-cms/commit/114df8a3a5e4800ef7586c5d9c8d79798fd2a11d) - See [announcement SS-2014-004](http://www.silverstripe.org/ss-2014-004-sql-injection-in-sitetree-with-custom-urlsegmentfilter-rules/)
* [FIX Do now allow arbitary class creation in CMS](https://github.com/silverstripe/silverstripe-cms/commit/bf9b22fd4331a6f78cec12a75262f570b025ec2d) - See [announcement SS-2014-005](http://www.silverstripe.org/ss-2014-005-arbitrary-class-creation-in-cms-backend/)
## General
* [Rewrote usages of error suppression operator](https://github.com/silverstripe/silverstripe-framework/commit/6d5d3d8cb7e69e0b37471b1e34077211b0f631fe)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.10-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.0.10-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.0.10-rc1)

View File

@ -0,0 +1,19 @@
# 3.0.11-rc1
Minor security release
## Security
* 2014-04-16 [9d74bc4](https://github.com/silverstripe/sapphire/commit/9d74bc4) Potential DoS exploit in TinyMCE - See [announcement SS-2014-009](http://www.silverstripe.org/ss-2014-009-potential-dos-exploit-in-tinymce/)
* 2014-05-05 [9bfeffd](https://github.com/silverstripe/silverstripe-framework/commit/9bfeffd) Injection / Filesystem vulnerability in generatesecuretoken - See [announcement SS-2014-010](http://www.silverstripe.org/ss-2014-010-injection-filesystem-vulnerability-in-generatesecuretoken/)
* 2014-05-07 [0099a18](https://github.com/silverstripe/silverstripe-framework/commit/0099a18) Folder filename injection - See [announcement SS-2014-011](http://www.silverstripe.org/ss-2014-011-folder-filename-injection/)
### Bugfixes
* 2013-06-20 [f2c4a62](https://github.com/silverstripe/sapphire/commit/f2c4a62) ConfirmedPasswordField used to expose existing hash (Hamish Friedlander)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.11-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.0.11-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.0.11-rc1)

View File

@ -0,0 +1,12 @@
# 3.0.9-rc1 (2014-02-19)
## Overview
* Security: Require ADMIN for ?flush=1&isDev=1 ([SS-2014-001](http://www.silverstripe.org/ss-2014-001-require-admin-for-flush1-and-isdev1))
* Security: XSS in third party library (SWFUpload) ([SS-2014-002](http://www.silverstripe.org/ss-2014-002-xss-in-third-party-library-swfupload/))
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.9-rc1)
* [cms](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.9-rc1)
* [installer](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.0.9-rc1)

View File

@ -0,0 +1,40 @@
# 3.1.10-rc1
## Upgrading
### Form Actions
Form action titles are now safely XML encoded, although this was an implicit assumption, it is now explicitly enforced.
XML encoding titles will not cause an error, but is deprecated at framework 4.0. FormAction buttons with custom HTML
content should be assigned using the `FormAction::setButtonContent` method instead.
### Features and Enhancements
* 2015-01-22 [2e4bf9a](https://github.com/silverstripe/sapphire/commit/2e4bf9a) Update sake to reference new docs (Cam Findlay)
### Bugfixes
* 2015-02-12 [047fe3a](https://github.com/silverstripe/sapphire/commit/047fe3a) Include php version in default cache folder name Update CoreTest.php (JorisDebonnet)
* 2015-02-10 [1db08ba](https://github.com/silverstripe/sapphire/commit/1db08ba) Fix FormAction title encoding BUG Fix TreeMultiSelectField using the wrong label BUG Fix encoding of selected title on TreeDropdownField BUG Fix DataDifferencer trying to compare non-comparable fields (non-dbfield objects) BUG: Fix issue with TreeMultiSelectField not saving BUG: Fix issue with GridFieldPrintButton ENHANCEMENT Instead of using multiple api calls to encode dbfield values, delegate this operation to the individual fields via forTemplate Instead of using a new API to communicate html encoding to treeselect, just ensure all content is HTML encoded, and enable html_titles in jstree. (Damian Mooyman)
* 2015-02-08 [a530085](https://github.com/silverstripe/silverstripe-cms/commit/a530085) External redirects shouldnt show in preview pane (Daniel Hensby)
* 2015-02-06 [d68435e](https://github.com/silverstripe/sapphire/commit/d68435e) SelectionGroup no longer shows empty FieldLists (Daniel Hensby)
* 2015-02-06 [a0f9535](https://github.com/silverstripe/sapphire/commit/a0f9535) issue where empty composite fields created a fieldlist with empty items (Daniel Hensby)
* 2015-02-05 [70e0d60](https://github.com/silverstripe/sapphire/commit/70e0d60) Fix developer output in redirection script (Damian Mooyman)
* 2015-02-03 [abd1e6b](https://github.com/silverstripe/sapphire/commit/abd1e6b) GridFieldExportButton should honour can method. (Will Rossiter)
* 2015-01-22 [eed7093](https://github.com/silverstripe/sapphire/commit/eed7093) dev/build not flushing manifests if site is in a subfolder (Loz Calver)
* 2015-01-22 [7733c43](https://github.com/silverstripe/silverstripe-cms/commit/7733c43) Correctly sanitise Title (Michael Strong)
* 2015-01-19 [77ebdc2](https://github.com/silverstripe/sapphire/commit/77ebdc2) DataObject::db returned fields in incorrect order, with incorrect data types (Loz Calver)
* 2015-01-15 [32ce85d](https://github.com/silverstripe/sapphire/commit/32ce85d) . Summary fields can't be translated (Elvinas L.)
* 2015-01-13 [2861e7c](https://github.com/silverstripe/sapphire/commit/2861e7c) insert media trims whitespace fixes #845 (Emma O'Keefe)
* 2015-01-13 [2e6e8af](https://github.com/silverstripe/sapphire/commit/2e6e8af) insert media trims whitespace - fixes #845 (Emma O'Keefe)
* 2015-01-09 [ef237f6](https://github.com/silverstripe/sapphire/commit/ef237f6) Expands the CMS' centre-pane when collapsed and it's clicked. (Russell Michell)
* 2014-10-24 [9d78eb7](https://github.com/silverstripe/sapphire/commit/9d78eb7) Fix BasicAuth not resetting failed login counts on authentication (Damian Mooyman)
* 2014-10-16 [e4ddb4b](https://github.com/silverstripe/sapphire/commit/e4ddb4b) Ensure query string in X-Backurl is encoded (fixes #3563) (Loz Calver)
* 2014-08-25 [f823831](https://github.com/silverstripe/sapphire/commit/f823831) making minify javascript fail-safe (Igor Nadj)
* 2014-04-03 [5180452](https://github.com/silverstripe/sapphire/commit/5180452) Fixed handling of numbers in certain locales. Fixes #2161 (Damian Mooyman)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.10-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.10-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.10-rc1)

View File

@ -0,0 +1,11 @@
# 3.1.10-rc2
## Bugfixes
* 2015-02-17 [aa77e12](https://github.com/silverstripe/sapphire/commit/aa77e12) Fixed infinity loop when searching _ss_environment (Zauberfish)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.10-rc2)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.10-rc2)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.10-rc2)

View File

@ -0,0 +1,29 @@
# 3.1.3-rc1
## Overview
* Security: Require ADMIN for ?flush=1&isDev=1 ([SS-2014-001](http://www.silverstripe.org/ss-2014-001-require-admin-for-flush1-and-isdev1))
* Security: XSS in third party library (SWFUpload) ([SS-2014-002](http://www.silverstripe.org/ss-2014-002-xss-in-third-party-library-swfupload/))
* Security: SiteTree.ExtraMeta allows JavaScript for malicious CMS authors ([SS-2014-003](http://www.silverstripe.org/ss-2014-003-extrameta-allows-javascript-for-malicious-cms-authors-/))
* Better loading performance when using multiple `UploadField` instances
* Option for `force_js_to_bottom` on `Requirements` class (ignoring inline `<script>` tags)
* Added `ListDecorator->filterByCallback()` for more sophisticated filtering
* New `DataList` filters: `LessThanOrEqualFilter` and `GreaterThanOrEqualFilter`
* "Cancel" button on "Add Page" form
* Better code hinting on magic properties (for IDE autocompletion)
* Increased Behat test coverage (editing HTML content, managing page permissions)
* Support for PHPUnit 3.8
## Upgrading
### SiteTree.ExtraMeta allows JavaScript for malicious CMS authors
If you have previously used the `SiteTree.ExtraMeta` field for `<head>` markup
other than its intended use case (`<meta>` and `<link>`), please consult
[SS-2014-003](http://www.silverstripe.org/ss-2014-003-extrameta-allows-javascript-for-malicious-cms-authors-/).
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.3-rc1)
* [cms](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.3-rc1)
* [installer](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.3-rc1)

View File

@ -0,0 +1,12 @@
# 3.1.3-rc2
# Overview
* Fixed regression around CMS loading in IE8
* Fixed regression in folder creation on upload
### Bugfixes
* 2014-02-20 [ebeb663](https://github.com/silverstripe/sapphire/commit/ebeb663) Fixed critical issue with Folder::find_or_make failing to handle invalid filename characters BUG Fix UploadField duplicate checking with invalid folderName (Damian Mooyman)
* 2014-02-19 [a681bd7](https://github.com/silverstripe/sapphire/commit/a681bd7) IE8 support in jquery.ondemand.js (fixes #2872) (Loz Calver)

View File

@ -0,0 +1,44 @@
# 3.1.4-rc1
## Upgrading
* If relying on partial caching of content between logged in users, be aware that the cache is now automatically
segmented based on both the current member ID, and the versioned reading mode. If this is not an appropriate
method (such as if the same content is served to logged in users within partial caching) then it is necessary
to adjust the config value of `SSViewer.global_key` to something more or less sensitive.
## Security
* [BUG Fix issue with versioned dataobjects being cached between stages](https://github.com/silverstripe/silverstripe-framework/commit/4415a75d9304a3930b9c28763fc092299640c685) - See [announcement SS-2014-007](http://www.silverstripe.org/ss-2014-007-confidentiality-breach-can-occur-between-draft-and-live-modes/)
* [BUG Fix encoding of JS redirection script](https://github.com/silverstripe/silverstripe-framework/commit/f8e3bbe3ae3f29f22d85abb73cea033659511168) - See [announcement SS-2014-006](http://www.silverstripe.org/ss-2014-006-xss-in-returnurl-redirection/)
* [FIX Prevent SQLi when no URL filters are applied](https://github.com/silverstripe/silverstripe-cms/commit/114df8a3a5e4800ef7586c5d9c8d79798fd2a11d) - See [announcement SS-2014-004](http://www.silverstripe.org/ss-2014-004-sql-injection-in-sitetree-with-custom-urlsegmentfilter-rules/)
* [FIX Do now allow arbitary class creation in CMS](https://github.com/silverstripe/silverstripe-cms/commit/bf9b22fd4331a6f78cec12a75262f570b025ec2d) - See [announcement SS-2014-005](http://www.silverstripe.org/ss-2014-005-arbitrary-class-creation-in-cms-backend/)
## Bugfixes
* [Fix Versioned::augmentSQL() when the data query was null.](https://github.com/silverstripe/silverstripe-framework/commit/deb1bfbcbaaa62acb2263ba797b5068e142a6353)
* [FIX UploadField validation error and styles](https://github.com/silverstripe/silverstripe-framework/commit/02bceca9b478358bdd569c16818d3be2467beb64)
* [FIX Overriding of theme templates in project folder](https://github.com/silverstripe/silverstripe-framework/commit/5f87d344f11c382dbee3fae8edfc00bb9a5a0265)
* [BUG Ensure TreeMultiSelectField doesn't populate menus with "unchanged".](https://github.com/silverstripe/silverstripe-framework/commit/9e2c7b657221c336137e07985bd5994682216d65)
* [BUG: #2503 Fixes performReadonlyTransformation for OptionSetField](https://github.com/silverstripe/silverstripe-framework/commit/44a8537f68872f0587cdf4cceadd433817dfdf60)
* [FIX: Rewrite Member getCMSFields to ensure updateCMSFields is only run once](https://github.com/silverstripe/silverstripe-framework/commit/d91c7d14b84d8b3caed948b0bbab94d254ea2b96)
* [FIX: Ensure valid CSS classes for GridField header](https://github.com/silverstripe/silverstripe-framework/commit/90952e7bd4bf7a278959ff320b3a71d30596f5d8)
* [BUG Fix case where setFolder('/') would break UploadField::fileexists](https://github.com/silverstripe/silverstripe-framework/commit/c1e0f98f87fa58edf7967d818732c7467cf47d80)
* [BUG Prevent unnecessary reconstruction of ClassName field after default records are generated](https://github.com/silverstripe/silverstripe-framework/commit/53b5adbcd98ff4d0e3947f4472b7b7b62a2b064a)
* [BUG Fix DataObject::loadLazyFields discarding original query parameters](https://github.com/silverstripe/silverstripe-framework/commit/23f5f08eda4201e0d3d4c28b81805da10b55bdb1)
* [Upload: retrieve existing File if an object without an ID is given and replaceFile=true](https://github.com/silverstripe/silverstripe-framework/commit/3c1e82b42c282ab64dfe7f5a68a50f59d8ebcc69)
## API
* [Add support for many_many_extraField in YAML](https://github.com/silverstripe/silverstripe-framework/commit/8b923006227b0177983c96b949edaa6df18fbbf8)
* [Allow vetoing forgot password requests](https://github.com/silverstripe/silverstripe-framework/commit/9afcf8f01ac6b5c3c054b9a49f1731d35aa868ed)
## General
* [Rewrote usages of error suppression operator](https://github.com/silverstripe/silverstripe-framework/commit/6d5d3d8cb7e69e0b37471b1e34077211b0f631fe)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.4-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.4-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.4-rc1)

View File

@ -0,0 +1,67 @@
# 3.1.5-rc1
## Upgrading
* If running an application in an environment where user security is critical, it may be necessary to
assign the config value `Security.remember_username` to false. This will disable persistence of
user login name between sessions, and disable browser auto-completion on the username field.
Note that users of certain browsers who have previously autofilled and saved login credentials
will need to clear their password autofill history before this setting is properly respected.
* Test cases that rely on updating and restoring `[api:Injector]` services may now take advantage
of the new `Injector::nest()` and `Injector::unnest()` methods to sandbox their alterations.
* If errors could potentially be raised by any `[api:RequestHandler]` class such as a `[api:Form]` or
`[api:Controller]`, you may now add the new `[api:ErrorPageControllerExtension]` to this class to
transform plain text error messages into `ErrorPage` rendered HTML errors. In the past this
behaviour was limited to subclasses of `[api:ContentController]`. By default this extension is now
added to the `Security` controller, and if this is not desirable then it should be removed
explicitly via the Config system.
## Security
* 2014-04-16 [bde16f0](https://github.com/silverstripe/sapphire/commit/bde16f0) Potential DoS exploit in TinyMCE - See [announcement SS-2014-009](http://www.silverstripe.org/ss-2014-009-potential-dos-exploit-in-tinymce/)
* 2014-05-05 [d9bc352](https://github.com/silverstripe/silverstripe-framework/commit/d9bc352) Injection / Filesystem vulnerability in generatesecuretoken - See [announcement SS-2014-010](http://www.silverstripe.org/ss-2014-010-injection-filesystem-vulnerability-in-generatesecuretoken/)
* 2014-05-02 [8e841cc](https://github.com/silverstripe/sapphire/commit/8e841cc) Folder filename injection - See [announcement SS-2014-011](http://www.silverstripe.org/ss-2014-011-folder-filename-injection/)
* 2014-05-05 [df28ccb](https://github.com/silverstripe/sapphire/commit/df28ccb) Upload fileexists vulnerability - See [announcement SS-2014-013](http://www.silverstripe.org/ss-2014-013-upload-fileexists-vulnerability/)
### API Changes
* 2014-05-02 [f9cb880](https://github.com/silverstripe/silverstripe-cms/commit/f9cb880) Error page support for Security controller errors (Damian Mooyman)
* 2014-05-01 [3162d0e](https://github.com/silverstripe/silverstripe-cms/commit/3162d0e) Update ErrorPage to respect new HTTP Error codes (Damian Mooyman)
* 2014-04-28 [0285322](https://github.com/silverstripe/silverstripe-cms/commit/0285322) Ability to configure paging for assets / pages (Damian Mooyman)
* 2014-04-22 [d06d5c1](https://github.com/silverstripe/sapphire/commit/d06d5c1) Injector supports nesting BUG Resolve issue with DirectorTest breaking RequestProcessor Injector::nest and Injector::unnest are introduced to better support sandboxing of testings. Injector and Config ::nest and ::unnest support chaining Test cases for both Injector::nest and Config::nest (Damian Mooyman)
* 2014-04-17 [a6017a0](https://github.com/silverstripe/sapphire/commit/a6017a0) HTTP 429 Allowed for use with rate limiting methods (Damian Mooyman)
* 2014-04-11 [892b440](https://github.com/silverstripe/sapphire/commit/892b440) Make default gridfield paging configurable Documentation improved (Damian Mooyman)
* 2014-04-09 [997077a](https://github.com/silverstripe/sapphire/commit/997077a) Security.remember_username to disable login form autocompletion (Damian Mooyman)
### Features and Enhancements
* 2014-03-28 [a502c9d](https://github.com/silverstripe/silverstripe-cms/commit/a502c9d) Fixes #966. Ability to filter pages on page status. - New filters for statuses normally found through SiteTree::getStatusFlags(). - Refactored menu sorting. Now alphabetical, as it wasn't previously. (Russell Michell)
* 2014-04-11 [3765030](https://github.com/silverstripe/silverstripe-cms/commit/3765030) Filter by date created for files Added test cases Do not merge before https://github.com/silverstripe-labs/silverstripe-behat-extension/pull/32 (Damian Mooyman)
### Bugfixes
* 2014-05-05 [c5d5d10](https://github.com/silverstripe/silverstripe-cms/commit/c5d5d10) Behat now uses explicit radio button behaviour (Damian Mooyman)
* 2014-05-01 [bd5abb6](https://github.com/silverstripe/sapphire/commit/bd5abb6) parent::init is not called first (Michael Parkhill)
* 2014-05-01 [4fd3015](https://github.com/silverstripe/sapphire/commit/4fd3015) corrected link to CMS Alternating Button Page (James Pluck)
* 2014-04-29 [8673b11](https://github.com/silverstripe/sapphire/commit/8673b11) Fix ImageTest Image test would erroneously reset the Image::$backend to null if the test was skipped, breaking subsequent test cases (Damian Mooyman)
* 2014-04-29 [89fbae2](https://github.com/silverstripe/silverstripe-cms/commit/89fbae2) Fix encoding of SiteTree.MetaTags (Damian Mooyman)
* 2014-04-25 [ff5f607](https://github.com/silverstripe/sapphire/commit/ff5f607) Docs for DataList::filter() (Daniel Hensby)
* 2014-04-24 [5e9ae57](https://github.com/silverstripe/sapphire/commit/5e9ae57) Fix edge case IE8 / dev / ssl / download file crash Prevents issue at http://support.microsoft.com/kb/323308 appearing on dev (Damian Mooyman)
* 2014-04-17 [bec8927](https://github.com/silverstripe/sapphire/commit/bec8927) Allow PHPUnit installation with composer / Fix travis (Will Morgan)
* 2014-04-16 [396fd9a](https://github.com/silverstripe/silverstripe-cms/commit/396fd9a) Broken file link tracking (fixes #996) (Loz Calver)
* 2014-04-14 [0b4f62d](https://github.com/silverstripe/sapphire/commit/0b4f62d) Fix jstree when duplicating subtrees (Damian Mooyman)
* 2014-04-11 [a261f22](https://github.com/silverstripe/sapphire/commit/a261f22) Delete Character \x01 (Stevie Mayhew)
* 2014-04-09 [91034d1](https://github.com/silverstripe/sapphire/commit/91034d1) HTMLText whitelist considers text nodes Minor improvement to #2853. If a list of whitelisted elements are specified, text nodes no longer evade the whitelist (Damian Mooyman)
* 2014-04-09 [a3c8a59](https://github.com/silverstripe/sapphire/commit/a3c8a59) Fix data query not always joining necessary tables Fixes #2846 (Damian Mooyman)
* 2014-04-08 [a060784](https://github.com/silverstripe/sapphire/commit/a060784) - missing link url for composer (camfindlay)
* 2014-04-07 [3204ab5](https://github.com/silverstripe/silverstripe-cms/commit/3204ab5) Fix orphaned pages reporting they can be viewed (Damian Mooyman)
* 2014-04-01 [84d8022](https://github.com/silverstripe/sapphire/commit/84d8022) Fix Date and SS_DateTime::FormatFromSettings This issue is caused by the odd default behaviour of Zend_Date, which attempts to parse yyyy-mm-dd format date and times as though they were yyyy-dd-mm. (Damian Mooyman)
* 2014-03-12 [b4a1aa4](https://github.com/silverstripe/silverstripe-cms/commit/b4a1aa4) Fixes #965. Allow user date-settings to show on GridField Page admin (Russell Michell)
* 2014-03-04 [ae573f8](https://github.com/silverstripe/sapphire/commit/ae573f8) Fix Versioned stage not persisting in Session. Fixes #962 BUG Disabled disruptive test case in DirectorTest API RequestProcessor and VersionedRequestFilter now both correctly implement RequestFilter Better PHPDoc on RequestFilter and implementations (Damian Mooyman)
* 2013-06-20 [f2c4a62](https://github.com/silverstripe/sapphire/commit/f2c4a62) ConfirmedPasswordField used to expose existing hash (Hamish Friedlander)
## Changelog
* [framework](https://github.com/silverstripe/silverstripe-framework/releases/tag/3.1.5-rc1)
* [cms](https://github.com/silverstripe/silverstripe-cms/releases/tag/3.1.5-rc1)
* [installer](https://github.com/silverstripe/silverstripe-installer/releases/tag/3.1.5-rc1)

View File

@ -16,11 +16,11 @@ well written bug reports can be half of the solution already!
Before submitting a bug: Before submitting a bug:
* Ask for assistance on the [forums](http://silverstripe.org/forums), [core mailinglist](http://groups.google.com/group/silverstripe-dev) or on [IRC](http://silverstripe.org/irc) if you're unsure if its really a bug. * Ask for assistance on the [forums](http://www.silverstripe.org/community/forums/), [core mailinglist](http://groups.google.com/group/silverstripe-dev) or on [IRC](http://irc.silverstripe.org/) if you're unsure if its really a bug.
* Search for similar, existing tickets * Search for similar, existing tickets
* Is this a security issue? Please follow our separate reporting guidelines below. * Is this a security issue? Please follow our separate reporting guidelines below.
* Is this a issue with the core framework or cms? Modules have their own issue trackers (see [silverstripe.org/modules](http://www.silverstripe.org/modules)) * Is this a issue with the core framework or cms? Modules have their own issue trackers (see [silverstripe.org/modules](http://www.silverstripe.org/modules/))
* Try to reproduce your issue on a [clean installation](http://doc.silverstripe.org/framework/en/installation/composer#using-development-versions), maybe the bug has already been fixed on an unreleased branch? * Try to reproduce your issue on a [clean installation](/getting_started/composer#using-development-versions), maybe the bug has already been fixed on an unreleased branch?
* The bugtracker is not the place to discuss enhancements, please use the forums or mailinglist. * The bugtracker is not the place to discuss enhancements, please use the forums or mailinglist.
Only log enhancement tickets if they gather a large interest in the community Only log enhancement tickets if they gather a large interest in the community
and the enhancement is likely to be implemented in the next couple of months. and the enhancement is likely to be implemented in the next couple of months.
@ -30,7 +30,7 @@ If the issue does look like a new bug:
* [Create a new ticket](https://github.com/silverstripe/silverstripe-framework/issues/new) * [Create a new ticket](https://github.com/silverstripe/silverstripe-framework/issues/new)
* Describe the steps required to reproduce your issue, and the expected outcome. Unit tests, screenshots and screencasts can help here. * Describe the steps required to reproduce your issue, and the expected outcome. Unit tests, screenshots and screencasts can help here.
* Describe your environment as detailed as possible: SilverStripe version, Browser, PHP version, Operating System, any installed SilverStripe modules. * Describe your environment as detailed as possible: SilverStripe version, Browser, PHP version, Operating System, any installed SilverStripe modules.
* *(optional)* [Submit a pull request](/misc/contributing/code) which fixes the issue. * *(optional)* [Submit a pull request](/contributing/code/#step-by-step-from-forking-to-sending-the-pull-request) which fixes the issue.
Lastly, don't get your hopes up too high. Unless your issue is a blocker Lastly, don't get your hopes up too high. Unless your issue is a blocker
affecting a large number of users, don't expect SilverStripe developers to jump affecting a large number of users, don't expect SilverStripe developers to jump
@ -42,24 +42,24 @@ problem can collaborate with you to develop a fix.
<div class="warning" markdown='1'> <div class="warning" markdown='1'>
Please don't file "feature requests" as Github issues. If there's a new feature Please don't file "feature requests" as Github issues. If there's a new feature
you'd like to see in SilverStripe, you either need to write it yourself (and you'd like to see in SilverStripe, you either need to write it yourself (and
[submit a pull request](/misc/contributing/code)) or convince somebody else to [submit a pull request](/contributing/code/#step-by-step-from-forking-to-sending-the-pull-request) or convince somebody else to
write it for you. Any "wishlist" type issues without code attached can be write it for you. Any "wishlist" type issues without code attached can be
expected to be closed as soon as they're reviewed. expected to be closed as soon as they're reviewed.
</div> </div>
In order to gain interest and feedback in your feature, we encourage you to In order to gain interest and feedback in your feature, we encourage you to
present it to the community through the [forums](http://silverstripe.org/forums), present it to the community through the [forums](http://www.silverstripe.org/community/forums),
[core mailinglist](http://groups.google.com/group/silverstripe-dev) or on [core mailinglist](http://groups.google.com/group/silverstripe-dev) or on
[IRC](http://silverstripe.org/irc). [IRC](http://irc.silverstripe.org/).
## Reporting Security Issues ## Reporting Security Issues
Report security issues to [security@silverstripe.com](mailto:security@silverstripe.com). Report security issues to [security@silverstripe.org](mailto:security@silverstripe.org).
See our "[Release Process](release-process)" documentation for more info, and See our "[Release Process](/contributing/release_process/#security-releases)" documentation for more info, and
read our guide on [how to write secure code](/topics/security). read our guide on [how to write secure code](/developer_guides/security/secure_coding/).
## Sharing your Opinion ## Sharing your Opinion
* [silverstripe.org/forums](http://silverstripe.org/forums): Forums on silverstripe.org * [silverstripe.org/forums](http://www.silverstripe.org/community/forums/): Forums on silverstripe.org
* [silverstripe-dev](http://groups.google.com/group/silverstripe-dev): Core development mailinglist * [silverstripe-dev](http://groups.google.com/group/silverstripe-dev/): Core development mailinglist
* [silverstripe-documentation](http://groups.google.com/group/silverstripe-translators): Translation team mailing list * [silverstripe-documentation](http://groups.google.com/group/silverstripe-translators/): Translation team mailing list

View File

@ -0,0 +1,60 @@
# SilverStripe community code of conduct
These guidelines aim to be an aspirational ideal for how we should behave when interacting in the SilverStripe developer community and to aid in building great open source software.
This code of conduct should be applied to all discussions and interactions involving SilverStripe and its related resources.
Honour the code of conduct whenever you participate formally and informally in our community and follow it in spirit as much as in letter.
## **Be open, collaborative and curious**
* Experiment with your code and share the results.
* Ask questions if you get stuck.
* If you can answer questions be responsive and help guide others towards solutions
* Share code and knowledge that might help others.
* The community is first and foremost about people sharing their knowledge to build something great together.
## Be empathetic, friendly and considerate.
* Welcome and encourage participation from everyone that wishes to join our community.
* Remember growing the community socially is as important as code related matters.
* Be respectful and honour diversity in our community. Sexist, racist, and other exclusionary jokes can offend those around you and are not appropriate.
* Keep your actions lawful, non-discriminatory, and unthreatening towards others.
## Respect the readers time.
* Be concise and read over things before you post.
* Mind your words, you write something once that will be read by many - being abusive, mean spirited and swearing at others does nothing to help get points across and drive constructive contributions to open source.
* Be clear when responding who you are representing, are you speaking on behalf of another business or acting in your capacity as an individual community member? Either is fine and encouraged and you should aim to make it clear to the reader.
## Respect different levels of participation.
* Welcome newcomers, spend a little time helping orientate them in our community.
* All contributors and core committers, module maintainers and knowledge sharers are participating in the community in a voluntary nature. Have respect for them and their time when making requests. You may need to exercise some patience.
* Whenever possible reference your comments to others with example code or links to documentation. This helps people learn and become more experienced community members and developers.
* If you manage to solve your own problem, tell others how you solved it. This will help people in the future that find they have the same problem as you.
* If you are reducing your input and potentially stepping away from the community please remember others might rely on your contributions. Be prepared to ensure these are kept open and available, spend some time handing over to another community member or core contributor to help continuity of SilverStripe open source.
## Resolve conflicts directly
* Conflict may eventuate from time to time, we should view it as a constructive process. Understanding others positions without descending into ad hominem attacks is valuable.
* Involve a 3rd party or if necessary inform a core committer if conflicts continue to escalate.
* Breaches of the code of conduct by community members may first result in a reminder about the code of conduct (we realise we're all human and sometimes we have bad days).
* Repeated and deliberate breaching after this will be referred to the core committers team and may result in members being asked to leave the community channels.
* While we have a policy of not removing postings and comments from our digital channels, there may be times in which items are removed if deemed blatantly discriminatory towards individuals, groups or cultures.
* Call others on their behaviour within the community and remind them of this code where necessary.
* Refer to [these helpful guides on conflict resolution](http://www.crnhq.org/pages.php?pID=10) to aid you.
## A living document
* This is a living document, as core committers we don't have all the answers and we attempt to make the community an innovative and valuable space for everyone that wishes to participate.
* If you would like to improve the wording, add additional items, or simply fix typos, each contribution to the code of conduct will be considered on it's merit and agreed upon unanimously by the core committers. Any changes will be included in future revisions of the SilverStripe code of conduct.
## Core committers house rules
Core committers are also bound by their [own set of house rules](core_committers#house_rules_for_the_core_committer_team).
_The SilverStripe code of conduct has been adapted from the following sources:_
http://www.apache.org/foundation/policies/conduct.html
https://www.djangoproject.com/conduct/
http://web.archive.org/web/20141109123859/http://speakup.io/coc.html
http://www.crnhq.org/pages.php?pID=10

View File

@ -2,6 +2,7 @@ title: Contributing
introduction: Any open source product is only as good as the community behind it. You can participate by sharing code, ideas, or simply helping others. No matter what your skill level is, every contribution counts. introduction: Any open source product is only as good as the community behind it. You can participate by sharing code, ideas, or simply helping others. No matter what your skill level is, every contribution counts.
## House rules for everybody contributing to SilverStripe ## House rules for everybody contributing to SilverStripe
* Read over the SilverStripe Community [Code of Conduct](code_of_conduct)
* Ask questions on the [forum](http://silverstripe.org/community/forums), and stick to more high-level discussions on the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev) * Ask questions on the [forum](http://silverstripe.org/community/forums), and stick to more high-level discussions on the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev)
* Make sure you know how to [raise good bug reports](issues_and_bugs) * Make sure you know how to [raise good bug reports](issues_and_bugs)
* Everybody can contribute to SilverStripe! If you do, ensure you can [submit solid pull requests](code) * Everybody can contribute to SilverStripe! If you do, ensure you can [submit solid pull requests](code)

View File

@ -1,62 +0,0 @@
# Todo
This folder is a work in progress for the new SilverStripe.org documentation
project run by Cameron Findlay and Will Rossiter. If you want to contribute
we'd love you too so flick us a message via email or twitter.
The docsviewer module issue tracker has a set of functional requirements that
we need to make as part of this work.
At the current point, the existing docs have just been dropped into the correct
sections. Index files need to be written as well as perhaps files merged or
reworked within sections.
## How-tos
How-tos should be each of the learning categories under a `howto` folder which
is visible within the section. This separates the context of reference
documentation to more tutorial style steps.
## Review *
Below where we say 'review' this relates to writing new index folders,
organizing the existing pages into a cohesive structure, how-tos out to
individual files and rewriting documentation pages in a standard and agreed upon
language style.
We are also looking at using a consistent example across all the documentation
and releasing this code on Github so that it gives developers a great reference
of what a beautiful SilverStripe project looks like.
## Writing and Language notes
Todo
## Developer Guide notes
The developer guides are a new concept. Each guide is broken into 2 sections
- How tos (stored within a how-to folder)
- Reference documentation
How-tos should be short, sweet and full of code. The style of these is for users
to basically copy and paste to get a solution. An example of this would be
`How to add a custom action to a GridField row`.
Everything else in the developer guide should be written as a reference manual.
Each section should contain an index.md file which summaries the topic, provides
the *entry level* user an introduction guide to the feature and any background
then it can go down into more detailed explanation into detailed references.
If you cannot place a how-to within a single developer guide, that would be an
indication that it should be a tutorial rather than part of a guide. Tutorials
should cover a full working case of a problem, the thought behind the problem
and a annotated implementation. An example of a new tutorial would be
'Building a Website without the CMS'. 'Building a contact form' would still sit
under 'Forms' as while it may have templates and controllers involved, as a user
'Form' is the action word.
## The plan
See our plan and progress at https://trello.com/b/y32uSVM1/silverstripe-documentation

View File

@ -4,20 +4,20 @@ introduction: Welcome to the SilverStripe Developer Documentation. This website
## Getting Started with SilverStripe ## Getting Started with SilverStripe
Before you start developing your first web application, you'll need to install the latest version of SilverStripe onto Before you start developing your first web application, you'll need to install the latest version of SilverStripe onto
a web server. The [Getting Started](getting_started/) section will show you what a web server. The [Getting Started](/getting_started) section will show you what
[server requirements](/installation/server-requirements) you will need to meet and how to [server requirements](/getting_started/server_requirements) you will need to meet and how to
[download and install SilverStripe](installation/). [download and install SilverStripe](/getting_started/installation).
To check out the features that SilverStripe offers without installing it, read the [Feature Overview](http://silverstripe.org/introduction/) To check out the features that SilverStripe offers without installing it, read the [Feature Overview](http://www.silverstripe.org/software/)
and play with the interactive [demo website](http://demo.silverstripe.org/). and play with the interactive [demo website](http://demo.silverstripe.org/).
## Getting support ## Getting support
SilverStripe has an wide range of options for getting support. The [forums](http://www.silverstripe.org/forums/) SilverStripe has an wide range of options for getting support. The [forums](http://www.silverstripe.org/community/forums/)
and [IRC channel](http://silverstripe.org/irc) are the best places to talk and discuss questions and problems with the and [IRC channel](http://irc.silverstripe.org/) are the best places to talk and discuss questions and problems with the
community. There are also several other websites with SilverStripe documentation to make use of. community. There are also several other websites with SilverStripe documentation to make use of.
* The [API Documentation](http://api.silverstripe.org/current) contains technical reference and class information. * The [API Documentation](http://api.silverstripe.org/) contains technical reference and class information.
* The [User Help](http://userhelp.silverstripe.com) website contains documentation related to working within the CMS. * The [User Help](http://userhelp.silverstripe.com) website contains documentation related to working within the CMS.
New features, API changes and the development [roadmap](http://www.silverstripe.org/roadmap/) for the product are New features, API changes and the development [roadmap](http://www.silverstripe.org/roadmap/) for the product are
@ -27,18 +27,16 @@ discussed on the [core mailinglist](https://groups.google.com/forum/#!forum/silv
## Building your first SilverStripe Web application ## Building your first SilverStripe Web application
Once you have completed the [Getting Started](http://silverstripe.org/getting-started/) guide and have got SilverStripe Once you have completed the [Getting Started](/getting_started) guide and have got SilverStripe
installed and running, the following [Tutorials](tutorials/) will lead through the basics and core concepts of installed and running, the following [Tutorials](http://silverstripe.org/learn/lessons) will lead through the basics and core concepts of
SilverStripe. SilverStripe.
Make sure you know the basic concepts of PHP5 before attempting to follow the tutorials. If you have not programmed with Make sure you know the basic concepts of PHP5 before attempting to follow the tutorials. If you have not programmed with PHP5 be sure to read the [Introduction to PHP5 (zend.com)](http://devzone.zend.com/6/php-101--php-for-the-absolute-beginner).
PHP5 be sure to read the [Introduction to PHP5 (zend.com)](http://devzone.zend.com/6/php-101--php-for-the-absolute-beginner).
[CHILDREN Folder=01_Tutorials]
## SilverStripe Concepts ## SilverStripe Concepts
The [Developer Gudes](developer_guides/) contain more detailed documentation on certain SilverStripe topics, how-to The [Developer Gudes](/developer_guides) contain more detailed documentation on certain SilverStripe topics, 'how to'
examples and reference documentation. examples and reference documentation.
[CHILDREN Folder=02_Developer_Guides] [CHILDREN Folder=02_Developer_Guides]

View File

@ -6,7 +6,7 @@
* the same. * the same.
* *
* Example: * Example:
* <code php> * <code>
* new CheckboxSetField( * new CheckboxSetField(
* $name = "topics", * $name = "topics",
* $title = "I am interested in the following topics", * $title = "I am interested in the following topics",
@ -17,7 +17,7 @@
* "4" => "Sports" * "4" => "Sports"
* ), * ),
* $value = "1" * $value = "1"
* ) * );
* </code> * </code>
* *
* <b>Saving</b> * <b>Saving</b>

View File

@ -49,7 +49,8 @@ class CompositeField extends FormField {
} elseif(is_array($children)) { } elseif(is_array($children)) {
$this->children = new FieldList($children); $this->children = new FieldList($children);
} else { } else {
$children = is_array(func_get_args()) ? func_get_args() : array(); //filter out null/empty items
$children = array_filter(func_get_args());
$this->children = new FieldList($children); $this->children = new FieldList($children);
} }
$this->children->setContainerField($this); $this->children->setContainerField($this);

View File

@ -17,31 +17,47 @@
*/ */
class FormAction extends FormField { class FormAction extends FormField {
/**
* Action name, normally prefixed with 'action_'
*
* @var string
*/
protected $action; protected $action;
/** /**
* Enables the use of <button> instead of <input> * Enables the use of <button> instead of <input>
* in {@link Field()} - for more customizeable styling. * in {@link Field()} - for more customizeable styling.
* *
* @var boolean $useButtonTag * @var boolean
*/ */
public $useButtonTag = false; public $useButtonTag = false;
/**
* Literal button content, used when useButtonTag is true.
*
* @var string
*/
protected $buttonContent = null; protected $buttonContent = null;
/** /**
* Create a new action button. * Create a new action button.
* *
* @param action The method to call when the button is clicked * @param string $action The method to call when the button is clicked
* @param title The label on the button * @param string $title The label on the button. This should be plain text, not escaped as HTML.
* @param form The parent form, auto-set when the field is placed inside a form * @param Form form The parent form, auto-set when the field is placed inside a form
*/ */
public function __construct($action, $title = "", $form = null) { public function __construct($action, $title = "", $form = null) {
$this->action = "action_$action"; $this->action = "action_$action";
$this->setForm($form);
parent::__construct($this->action, $title, null, $form); parent::__construct($this->action, $title);
} }
/**
* Get the action name
*
* @return string
*/
public function actionName() { public function actionName() {
return substr($this->name, 7); return substr($this->name, 7);
} }
@ -49,6 +65,9 @@ class FormAction extends FormField {
/** /**
* Set the full action name, including action_ * Set the full action name, including action_
* This provides an opportunity to replace it with something else * This provides an opportunity to replace it with something else
*
* @param string $fullAction
* @return $this
*/ */
public function setFullAction($fullAction) { public function setFullAction($fullAction) {
$this->action = $fullAction; $this->action = $fullAction;
@ -76,6 +95,22 @@ class FormAction extends FormField {
return 'action'; return 'action';
} }
public function Title() {
$title = parent::Title();
// Remove this method override in 4.0
$decoded = Convert::xml2raw($title);
if($decoded !== $title) {
Deprecation::notice(
'4.0',
'The FormAction title field should not be html encoded. Use buttonContent to set custom html instead'
);
return $decoded;
}
return $title;
}
public function getAttributes() { public function getAttributes() {
$type = (isset($this->attributes['src'])) ? 'image' : 'submit'; $type = (isset($this->attributes['src'])) ? 'image' : 'submit';
@ -92,6 +127,9 @@ class FormAction extends FormField {
/** /**
* Add content inside a button field. * Add content inside a button field.
*
* @param string $content
* @return $this
*/ */
public function setButtonContent($content) { public function setButtonContent($content) {
$this->buttonContent = (string) $content; $this->buttonContent = (string) $content;
@ -99,14 +137,19 @@ class FormAction extends FormField {
} }
/** /**
* @return String * Gets the content inside the button field
*
* @return string
*/ */
public function getButtonContent() { public function getButtonContent() {
return $this->buttonContent; return $this->buttonContent;
} }
/** /**
* @param Boolean * Enable or disable the rendering of this action as a <button />
*
* @param boolean
* @return $this
*/ */
public function setUseButtonTag($bool) { public function setUseButtonTag($bool) {
$this->useButtonTag = $bool; $this->useButtonTag = $bool;
@ -114,7 +157,9 @@ class FormAction extends FormField {
} }
/** /**
* @return Boolean * Determine if this action is rendered as a <button />
*
* @return boolean
*/ */
public function getUseButtonTag() { public function getUseButtonTag() {
return $this->useButtonTag; return $this->useButtonTag;

View File

@ -524,8 +524,8 @@ class FormField extends RequestHandler {
* Set the field value. * Set the field value.
* *
* @param mixed $value * @param mixed $value
* * @param mixed $data Optional data source passed in by {@see Form::loadDataFrom}
* @return FormField. * @return FormField Self reference
*/ */
public function setValue($value) { public function setValue($value) {
$this->value = $value; $this->value = $value;

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Render a button that will submit the form its contained in through ajax. * Render a button that will submit the form its contained in through ajax.
* If you want to add custom behaviour, please set {@link inlcudeDefaultJS()} to FALSE * If you want to add custom behaviour, please set {@link includeDefaultJS()} to FALSE
* *
* @see framework/javascript/InlineFormAction.js * @see framework/javascript/InlineFormAction.js
* *

View File

@ -2,13 +2,63 @@
/** /**
* Text input field with validation for numeric values. Supports validating * Text input field with validation for numeric values. Supports validating
* the numeric value as to the {@link i18n::get_locale()} value. * the numeric value as to the {@link i18n::get_locale()} value, or an
* overridden locale specific to this field.
* *
* @package forms * @package forms
* @subpackage fields-formattedinput * @subpackage fields-formattedinput
*/ */
class NumericField extends TextField { class NumericField extends TextField {
/**
* Override locale for this field
*
* @var string
*/
protected $locale = null;
public function setValue($value, $data = array()) {
require_once "Zend/Locale/Format.php";
// If passing in a non-string number, or a value
// directly from a dataobject then localise this number
if ((is_numeric($value) && !is_string($value)) ||
($value && $data instanceof DataObject)
){
$locale = new Zend_Locale($this->getLocale());
$this->value = Zend_Locale_Format::toNumber($value, array('locale' => $locale));
} else {
// If an invalid number, store it anyway, but validate() will fail
$this->value = $this->clean($value);
}
return $this;
}
/**
* In some cases and locales, validation expects non-breaking spaces
*
* @param string $input
* @return string The input value, with all spaces replaced with non-breaking spaces
*/
protected function clean($input) {
$nbsp = html_entity_decode('&nbsp;', null, 'UTF-8');
return str_replace(' ', $nbsp, trim($input));
}
/**
* Determine if the current value is a valid number in the current locale
*
* @return bool
*/
protected function isNumeric() {
require_once "Zend/Locale/Format.php";
$locale = new Zend_Locale($this->getLocale());
return Zend_Locale_Format::isNumber(
$this->clean($this->value),
array('locale' => $locale)
);
}
public function Type() { public function Type() {
return 'numeric text'; return 'numeric text';
} }
@ -31,14 +81,8 @@ class NumericField extends TextField {
return true; return true;
} }
require_once THIRDPARTY_PATH."/Zend/Locale/Format.php"; if($this->isNumeric()) return true;
$valid = Zend_Locale_Format::isNumber(
trim($this->value),
array('locale' => i18n::get_locale())
);
if(!$valid) {
$validator->validationError( $validator->validationError(
$this->name, $this->name,
_t( _t(
@ -47,15 +91,23 @@ class NumericField extends TextField {
), ),
"validation" "validation"
); );
return false; return false;
} }
return true; /**
} * Extracts the number value from the localised string value
*
* @return string number value
*/
public function dataValue() { public function dataValue() {
return (is_numeric($this->value)) ? $this->value : 0; require_once "Zend/Locale/Format.php";
if(!$this->isNumeric()) return 0;
$locale = new Zend_Locale($this->getLocale());
$number = Zend_Locale_Format::getNumber(
$this->clean($this->value),
array('locale' => $locale)
);
return $number;
} }
/** /**
@ -67,6 +119,26 @@ class NumericField extends TextField {
return $field; return $field;
} }
/**
* Gets the current locale this field is set to
*
* @return string
*/
public function getLocale() {
return $this->locale ?: i18n::get_locale();
}
/**
* Override the locale for this field
*
* @param string $locale
* @return $this
*/
public function setLocale($locale) {
$this->locale = $locale;
return $this;
}
} }
class NumericField_Readonly extends ReadonlyField { class NumericField_Readonly extends ReadonlyField {

View File

@ -71,13 +71,15 @@ class TreeDropdownField extends FormField {
* *
* @param string $name the field name * @param string $name the field name
* @param string $title the field label * @param string $title the field label
* @param sourceObject The object-type to list in the tree. Must be a * @param string|array $sourceObject The object-type to list in the tree. This could
* {@link Hierarchy} subclass. Alternatively, you can set this to an * be one of the following:
* array of key/value pairs, like a {@link DropdownField} source. In * - A DataObject class name with the {@link Hierarchy} extension.
* - An array of key/value pairs, like a {@link DropdownField} source. In
* this case, the field will act like show a flat list of tree items, * this case, the field will act like show a flat list of tree items,
* without any hierarchy. This is most useful in conjunction with * without any hierarchy. This is most useful in conjunction with
* {@link TreeMultiselectField}, for presenting a set of checkboxes in * {@link TreeMultiselectField}, for presenting a set of checkboxes in
* a compact view. * a compact view. Note, that all value strings must be XML encoded
* safely prior to being passed in.
* *
* @param string $keyField to field on the source class to save as the * @param string $keyField to field on the source class to save as the
* field value (default ID). * field value (default ID).
@ -95,6 +97,8 @@ class TreeDropdownField extends FormField {
$this->labelField = $labelField; $this->labelField = $labelField;
$this->showSearch = $showSearch; $this->showSearch = $showSearch;
$this->addExtraClass('single');
parent::__construct($name, $title); parent::__construct($name, $title);
} }
@ -193,15 +197,15 @@ class TreeDropdownField extends FormField {
Requirements::css(FRAMEWORK_DIR . '/css/TreeDropdownField.css'); Requirements::css(FRAMEWORK_DIR . '/css/TreeDropdownField.css');
$record = $this->Value() ? $this->objectForKey($this->Value()) : null; $record = $this->Value() ? $this->objectForKey($this->Value()) : null;
if($record) { if($record instanceof ViewableData) {
$title = $record->{$this->labelField}; $title = $record->obj($this->labelField)->forTemplate();
} else { } elseif($record) {
if($this->showSearch) { $title = Convert::raw2xml($record->{$this->labelField});
} else if($this->showSearch) {
$title = _t('DropdownField.CHOOSESEARCH', '(Choose or Search)', 'start value of a dropdown'); $title = _t('DropdownField.CHOOSESEARCH', '(Choose or Search)', 'start value of a dropdown');
} else { } else {
$title = _t('DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown'); $title = _t('DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown');
} }
}
// TODO Implement for TreeMultiSelectField // TODO Implement for TreeMultiSelectField
$metadata = array( $metadata = array(
@ -213,8 +217,7 @@ class TreeDropdownField extends FormField {
$properties, $properties,
array( array(
'Title' => $title, 'Title' => $title,
'TitleURLEncoded' => rawurlencode($title), 'Metadata' => ($metadata) ? Convert::raw2json($metadata) : null,
'Metadata' => ($metadata) ? Convert::raw2att(Convert::raw2json($metadata)) : null
) )
); );
@ -286,8 +289,7 @@ class TreeDropdownField extends FormField {
} }
$self = $this; $self = $this;
$escapeLabelField = ($obj->escapeTypeForField($this->labelField) != 'xml'); $titleFn = function(&$child) use(&$self) {
$titleFn = function(&$child) use(&$self, $escapeLabelField) {
$keyField = $self->keyField; $keyField = $self->keyField;
$labelField = $self->labelField; $labelField = $self->labelField;
return sprintf( return sprintf(
@ -299,7 +301,7 @@ class TreeDropdownField extends FormField {
Convert::raw2xml($child->markingClasses()), Convert::raw2xml($child->markingClasses()),
($self->nodeIsDisabled($child)) ? 'disabled' : '', ($self->nodeIsDisabled($child)) ? 'disabled' : '',
(int)$child->ID, (int)$child->ID,
$escapeLabelField ? Convert::raw2xml($child->$labelField) : $child->$labelField $child->obj($labelField)->forTemplate()
); );
}; };

View File

@ -46,6 +46,8 @@
class TreeMultiselectField extends TreeDropdownField { class TreeMultiselectField extends TreeDropdownField {
public function __construct($name, $title=null, $sourceObject="Group", $keyField="ID", $labelField="Title") { public function __construct($name, $title=null, $sourceObject="Group", $keyField="ID", $labelField="Title") {
parent::__construct($name, $title, $sourceObject, $keyField, $labelField); parent::__construct($name, $title, $sourceObject, $keyField, $labelField);
$this->removeExtraClass('single');
$this->addExtraClass('multiple');
$this->value = 'unchanged'; $this->value = 'unchanged';
} }
@ -98,19 +100,20 @@ class TreeMultiselectField extends TreeDropdownField {
Requirements::css(FRAMEWORK_DIR . '/css/TreeDropdownField.css'); Requirements::css(FRAMEWORK_DIR . '/css/TreeDropdownField.css');
$value = ''; $value = '';
$itemList = ''; $titleArray = array();
$idArray = array();
$items = $this->getItems(); $items = $this->getItems();
if($items && count($items)) { if($items && count($items)) {
foreach($items as $id => $item) { foreach($items as $item) {
$titleArray[] = $item->Title;
$idArray[] = $item->ID; $idArray[] = $item->ID;
$titleArray[] = ($item instanceof ViewableData)
? $item->obj($this->labelField)->forTemplate()
: Convert::raw2xml($item->{$this->labelField});
} }
if(isset($titleArray)) {
$title = implode(", ", $titleArray); $title = implode(", ", $titleArray);
$value = implode(",", $idArray); $value = implode(",", $idArray);
}
} else { } else {
$title = _t('DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown'); $title = _t('DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown');
} }
@ -118,30 +121,19 @@ class TreeMultiselectField extends TreeDropdownField {
$dataUrlTree = ''; $dataUrlTree = '';
if ($this->form){ if ($this->form){
$dataUrlTree = $this->Link('tree'); $dataUrlTree = $this->Link('tree');
if (isset($idArray) && count($idArray)){ if (!empty($idArray)){
$dataUrlTree = Controller::join_links($dataUrlTree, '?forceValue='.implode(',',$idArray)); $dataUrlTree = Controller::join_links($dataUrlTree, '?forceValue='.implode(',',$idArray));
} }
} }
return FormField::create_tag( $properties = array_merge(
'div', $properties,
array ( array(
'id' => "TreeDropdownField_{$this->id()}", 'Title' => $title,
'class' => 'TreeDropdownField multiple' . ($this->extraClass() ? " {$this->extraClass()}" : '') 'Link' => $dataUrlTree,
. ($this->showSearch ? " searchable" : ''), 'Value' => $value
'data-url-tree' => $dataUrlTree,
'data-title' => $title,
'title' => $this->getDescription()
),
FormField::create_tag(
'input',
array (
'id' => $this->id(),
'type' => 'hidden',
'name' => $this->name,
'value' => $value
)
) )
); );
return $this->customise($properties)->renderWith('TreeDropdownField');
} }
/** /**

View File

@ -132,6 +132,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
} }
foreach($items->limit(null) as $item) { foreach($items->limit(null) as $item) {
if($item->hasMethod('canView') && $item->canView()) {
$columnData = array(); $columnData = array();
foreach($csvColumns as $columnSource => $columnHeader) { foreach($csvColumns as $columnSource => $columnHeader) {
@ -148,10 +149,11 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
} }
$value = str_replace(array("\r", "\n"), "\n", $value); $value = str_replace(array("\r", "\n"), "\n", $value);
$columnData[] = '"' . str_replace('"', '\"', $value) . '"'; $columnData[] = '"' . str_replace('"', '""', $value) . '"';
} }
$fileData .= implode($separator, $columnData); $fileData .= implode($separator, $columnData);
$fileData .= "\n"; $fileData .= "\n";
}
$item->destroy(); $item->destroy();
} }

View File

@ -192,6 +192,10 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr
foreach($printColumns as $field => $label) { foreach($printColumns as $field => $label) {
$value = $gridField->getDataFieldValue($item, $field); $value = $gridField->getDataFieldValue($item, $field);
if($item->escapeTypeForField($field) != 'xml') {
$value = Convert::raw2xml($value);
}
$itemRow->push(new ArrayData(array( $itemRow->push(new ArrayData(array(
"CellString" => $value, "CellString" => $value,
))); )));

View File

@ -56,7 +56,7 @@
var linkTitle = strings.openLink; var linkTitle = strings.openLink;
if(linkTitle) this.find("treedropdownfield-toggle-panel-link a").attr('title', linkTitle); if(linkTitle) this.find("treedropdownfield-toggle-panel-link a").attr('title', linkTitle);
if(this.data('title')) this.setTitle(decodeURIComponent(this.data('title'))); if(this.data('title')) this.setTitle(this.data('title'));
this.getPanel().hide(); this.getPanel().hide();
this._super(); this._super();
@ -138,10 +138,10 @@
this[this.getPanel().is(':visible') ? 'closePanel' : 'openPanel'](); this[this.getPanel().is(':visible') ? 'closePanel' : 'openPanel']();
}, },
setTitle: function(title) { setTitle: function(title) {
if(!title) title = strings.fieldTitle; title = title || this.data('title') || strings.fieldTitle;
this.find('.treedropdownfield-title').html(title); this.find('.treedropdownfield-title').html(title);
this.data('title', encodeURIComponent(title)); // separate view from storage (important for search cancellation) this.data('title', title); // separate view from storage (important for search cancellation)
}, },
getTitle: function() { getTitle: function() {
return this.find('.treedropdownfield-title').text(); return this.find('.treedropdownfield-title').text();
@ -161,7 +161,7 @@
if(title) { if(title) {
self.setTitle(title); self.setTitle(title);
self.data('title', title) self.data('title', title);
} }
if(node) tree.jstree('select_node', node); if(node) tree.jstree('select_node', node);
} }
@ -235,6 +235,7 @@
var self = this; var self = this;
return { return {
'core': { 'core': {
'html_titles': true,
// 'initially_open': ['record-0'], // 'initially_open': ['record-0'],
'animation': 0 'animation': 0
}, },

View File

@ -1,4 +1,16 @@
id: id:
AssetAdmin:
NEWFOLDER: Berkas baru
AssetTableField:
FILENAME: Nama berkas
FOLDER: Map
TITLE: Judul
AssetUploadField:
EDITALL: 'Ubah seluruhnya'
EDITANDORGANIZE: 'Ubah dan atur'
EDITINFO: 'Ubah berkas'
FILES: Berkas
TOTAL: Total
BBCodeParser: BBCodeParser:
ALIGNEMENT: Penjajaran barisan ALIGNEMENT: Penjajaran barisan
ALIGNEMENTEXAMPLE: 'jajar kanan' ALIGNEMENTEXAMPLE: 'jajar kanan'
@ -44,18 +56,50 @@ id:
DataObject: DataObject:
PLURALNAME: 'Objek-objek Data' PLURALNAME: 'Objek-objek Data'
SINGULARNAME: 'Objek Data' SINGULARNAME: 'Objek Data'
Date:
DAY: hari
DAYS: hari
HOUR: jam
HOURS: jam
LessThanMinuteAgo: 'kurang dari semenit'
MIN: menit
MINS: menit
MONTH: bulan
MONTHS: bulan
SEC: detik
SECS: detik
TIMEDIFFAGO: '{difference} lalu'
YEAR: tahun
YEARS: tahun
DateField: DateField:
NOTSET: 'tidak diset' NOTSET: 'tidak diset'
TODAY: hari ini TODAY: hari ini
VALIDDATEFORMAT2: 'Mohon masukkan format tanggal yang valid ({format})'
DatetimeField:
NOTSET: 'Tidak diset'
DropdownField: DropdownField:
CHOOSE: (Pilih) CHOOSE: (Pilih)
CHOOSESEARCH: '(Pilih or Cari)'
EmailField:
VALIDATION: 'Mohon masukkan alamat surel'
Enum:
ANY: Lain
File: File:
Content: Isi Content: Isi
CssType: 'Berkas CSS'
DocType: 'Dokumen Word'
Filename: Nama File Filename: Nama File
HtlType: 'Berkas HTML'
HtmlType: 'Berkas HTML'
IcoType: 'Ikon gambar'
NOFILESIZE: 'Ukuran file adalah nol bytes.' NOFILESIZE: 'Ukuran file adalah nol bytes.'
Name: Nama Name: Nama
PLURALNAME: File-file PLURALNAME: File-file
SINGULARNAME: Berkas
Title: Judul Title: Judul
Folder:
PLURALNAME: Map
SINGULARNAME: Map
ForgotPasswordEmail_ss: ForgotPasswordEmail_ss:
HELLO: Hai HELLO: Hai
TEXT1: 'Inilah' TEXT1: 'Inilah'
@ -68,6 +112,16 @@ id:
VALIDATOR: Pengesah VALIDATOR: Pengesah
FormField: FormField:
NONE: tidak ada NONE: tidak ada
GridAction:
DELETE_DESCRIPTION: Hapus
Delete: Hapus
GridField:
Add: 'Tambah {name}'
Filter: Saring
FilterBy: 'Saring dengan'
Find: Temukan
LinkExisting: 'Tautan tersedia'
NewRecord: 'Baru %s'
Group: Group:
Code: 'Kode Grup' Code: 'Kode Grup'
DefaultGroupTitleAdministrators: Pengurus DefaultGroupTitleAdministrators: Pengurus
@ -133,12 +187,39 @@ id:
db_NumVisit: 'Jumlah Kunjungan' db_NumVisit: 'Jumlah Kunjungan'
db_Password: Kata sandi db_Password: Kata sandi
db_PasswordExpiry: 'Tanggal Kata Sandi Berakhir' db_PasswordExpiry: 'Tanggal Kata Sandi Berakhir'
MemberDatetimeOptionsetField:
TWODIGITDAY: 'Dua digit bulan'
TWODIGITHOUR: 'Dua angka jam (00 ke 23)'
TWODIGITYEAR: 'Dua-angka tahun'
MemberImportForm:
ResultCreated: 'Membuat {count} anggota'
ResultDeleted: 'Menghapus %d anggota'
ResultNone: 'Tidak ada perubahan'
MemberPassword:
PLURALNAME: 'Kata kunci anggota'
MemberTableField:
APPLY_FILTER: 'Terapkan saring'
ModelAdmin:
DELETE: Hapus
DELETEDRECORDS: 'Menghapus {count} rekod'
Title: 'Model Data'
ModelAdmin_ImportSpec_ss:
IMPORTSPECFIELDS: 'Kolom database'
IMPORTSPECRELATIONS: Hubungan
ModelAdmin_Tools_ss:
FILTER: Saring
IMPORT: Impor
ModelSidebar_ss:
IMPORT_TAB_HEADER: Impor
SEARCHLISTINGS: Cari
MoneyField: MoneyField:
FIELDLABELAMOUNT: Jumlah FIELDLABELAMOUNT: Jumlah
FIELDLABELCURRENCY: Mata Uang FIELDLABELCURRENCY: Mata Uang
Permission: Permission:
AdminGroup: Pengurus AdminGroup: Pengurus
FULLADMINRIGHTS: 'Hak-hak administratif yang penuh' FULLADMINRIGHTS: 'Hak-hak administratif yang penuh'
PermissionRole:
Title: Judul
PhoneNumberField: PhoneNumberField:
VALIDATION: 'Harap masukkan nomor telepon yang valid' VALIDATION: 'Harap masukkan nomor telepon yang valid'
Security: Security:
@ -162,5 +243,18 @@ id:
ToggleField: ToggleField:
LESS: kurang LESS: kurang
MORE: lebih MORE: lebih
UploadField:
DOEDIT: Simpan
EDIT: Ubah
EDITINFO: 'Ubah berkas ini'
FIELDNOTSET: 'Informasi berkas tidak ditemukan'
FROMCOMPUTER: 'Dari komputermu'
FROMCOMPUTERINFO: 'Pilih dari berkas'
FROMFILES: 'Dari berkas'
OVERWRITEWARNING: 'Berkas dengan nama yang sama sudah ada'
REMOVE: Memindahkan
STARTALL: 'Jalankan semua'
Saved: Disimpan
UPLOADSINTO: 'simpan ke /{path}'
Versioned: Versioned:
has_many_Versions: Versi-versi has_many_Versions: Versi-versi

View File

@ -75,7 +75,7 @@ lt:
ChangePasswordEmail_ss: ChangePasswordEmail_ss:
CHANGEPASSWORDTEXT1: 'Jūs pakeitėte slaptažodį' CHANGEPASSWORDTEXT1: 'Jūs pakeitėte slaptažodį'
CHANGEPASSWORDTEXT2: 'Nuo šiol galite naudoti šiuos prisijungimo duomenis:' CHANGEPASSWORDTEXT2: 'Nuo šiol galite naudoti šiuos prisijungimo duomenis:'
EMAIL: E. paštas EMAIL: El. paštas
HELLO: Sveiki HELLO: Sveiki
PASSWORD: Slaptažodis PASSWORD: Slaptažodis
ConfirmedPasswordField: ConfirmedPasswordField:
@ -173,7 +173,7 @@ lt:
Form: Form:
CSRF_FAILED_MESSAGE: "Iškilo techninė problema. Prašome paspausti mygtuką Atgal,\nperkraukite naršyklės langą ir bandykite vėl." CSRF_FAILED_MESSAGE: "Iškilo techninė problema. Prašome paspausti mygtuką Atgal,\nperkraukite naršyklės langą ir bandykite vėl."
FIELDISREQUIRED: '{name} yra privalomas' FIELDISREQUIRED: '{name} yra privalomas'
SubmitBtnLabel: Eiti SubmitBtnLabel: Vykdyti
VALIDATIONCREDITNUMBER: 'Prašome įsitikinti, ar teisingai suvedėte kreditinės kortelės numerį {number}' VALIDATIONCREDITNUMBER: 'Prašome įsitikinti, ar teisingai suvedėte kreditinės kortelės numerį {number}'
VALIDATIONNOTUNIQUE: 'Įvesta reikšmė nėra unikali' VALIDATIONNOTUNIQUE: 'Įvesta reikšmė nėra unikali'
VALIDATIONPASSWORDSDONTMATCH: 'Nesutampa slaptažodžiai' VALIDATIONPASSWORDSDONTMATCH: 'Nesutampa slaptažodžiai'
@ -184,14 +184,14 @@ lt:
CSRF_EXPIRED_MESSAGE: 'Jūsų prisijungimas nebegalioja. Prašome iš naujo išsaugoti duomenis.' CSRF_EXPIRED_MESSAGE: 'Jūsų prisijungimas nebegalioja. Prašome iš naujo išsaugoti duomenis.'
FormField: FormField:
Example: 'pvz. %s' Example: 'pvz. %s'
NONE: niekas NONE: nėra
GridAction: GridAction:
DELETE_DESCRIPTION: Ištrinti DELETE_DESCRIPTION: Ištrinti
Delete: Ištrinti Delete: Ištrinti
UnlinkRelation: Atkabinti UnlinkRelation: Atkabinti
GridField: GridField:
Add: 'Sukurti {name}' Add: 'Sukurti {name}'
Filter: Filtras Filter: Filtruoti
FilterBy: 'Filtruoti pagal ' FilterBy: 'Filtruoti pagal '
Find: Rasti Find: Rasti
LEVELUP: 'Aukštyn' LEVELUP: 'Aukštyn'
@ -303,10 +303,10 @@ lt:
DELETED: Ištrinta. DELETED: Ištrinta.
DropdownBatchActionsDefault: Veiksmai DropdownBatchActionsDefault: Veiksmai
HELP: Pagalba HELP: Pagalba
PAGETYPE: 'Puslapio tipas:' PAGETYPE: 'Puslapio tipas: '
PERMAGAIN: 'Jūs atsijungėte. Norėdami vėl prisijungti, įveskite savo duomenis į žemiau esančius laukelius.' PERMAGAIN: 'Jūs atsijungėte. Norėdami vėl prisijungti, įveskite savo duomenis į žemiau esančius laukelius.'
PERMALREADY: 'Deja, Jūs negalite patekti į šią TVS dalį. Jeigu norite prisijungti kitu vartotoju, tai atlikite žemiau' PERMALREADY: 'Deja, Jūs negalite patekti į šią TVS dalį. Jeigu norite prisijungti kitu vartotoju, tai atlikite žemiau'
PERMDEFAULT: 'Pasirinkite prisijungimo būda ir suveskite prisijungimo duomenis' PERMDEFAULT: 'Pasirinkite prisijungimo būdą ir suveskite prisijungimo duomenis'
PLEASESAVE: 'Prašome išsaugoti puslapį: Šis puslapis negali būti atnaujintas, nes jis dar nėra išsaugotas.' PLEASESAVE: 'Prašome išsaugoti puslapį: Šis puslapis negali būti atnaujintas, nes jis dar nėra išsaugotas.'
PreviewButton: Peržiūra PreviewButton: Peržiūra
REORGANISATIONSUCCESSFUL: 'Puslapių medis pertvarkytas sėkmingai.' REORGANISATIONSUCCESSFUL: 'Puslapių medis pertvarkytas sėkmingai.'
@ -474,7 +474,7 @@ lt:
LOGGEDOUT: 'Jūs atsijungėte. Norėdami vėl prisijungti, įveskite savo duomenis į žemiau esančius laukelius.' LOGGEDOUT: 'Jūs atsijungėte. Norėdami vėl prisijungti, įveskite savo duomenis į žemiau esančius laukelius.'
LOGIN: 'Prisijungti' LOGIN: 'Prisijungti'
LOSTPASSWORDHEADER: 'Slaptažodžio atstatymas' LOSTPASSWORDHEADER: 'Slaptažodžio atstatymas'
NOTEPAGESECURED: 'Šis puslapis yra apsaugotas. Įveskite savo prisijungimo duomenis, esančius žemiau.' NOTEPAGESECURED: 'Šis puslapis yra apsaugotas. Įveskite savo duomenis į žemiau esančius laukelius.'
NOTERESETLINKINVALID: '<p>Neteisinga arba negaliojanti slaptažodžio atstatymo nuoroda.</p><p>Galite atsisiųsti naują <a href="{link1}">čia</a> arba pasikeisti slaptažodį po to, kai <a href="{link2}">prisijungsite</a>.</p>' NOTERESETLINKINVALID: '<p>Neteisinga arba negaliojanti slaptažodžio atstatymo nuoroda.</p><p>Galite atsisiųsti naują <a href="{link1}">čia</a> arba pasikeisti slaptažodį po to, kai <a href="{link2}">prisijungsite</a>.</p>'
NOTERESETPASSWORD: 'Įveskite savo e. pašto adresą ir atsiųsime slaptažodžio atstatymui skirtą nuorodą' NOTERESETPASSWORD: 'Įveskite savo e. pašto adresą ir atsiųsime slaptažodžio atstatymui skirtą nuorodą'
PASSWORDSENTHEADER: 'Slaptažodžio atstatymo nuoroda nusiųsta į ''{email}''' PASSWORDSENTHEADER: 'Slaptažodžio atstatymo nuoroda nusiųsta į ''{email}'''
@ -490,7 +490,7 @@ lt:
IMPORTUSERS: 'Importuoti vartotojus' IMPORTUSERS: 'Importuoti vartotojus'
MEMBERS: Nariai MEMBERS: Nariai
MENUTITLE: Saugumas MENUTITLE: Saugumas
MemberListCaution: 'Dėmesio: Pašalinus vartotojus ir šio sąrašo, jie bus pašalinti iš visų grupių ir duomenų bazės.' MemberListCaution: 'Dėmesio: Pašalinus vartotojus iš šio sąrašo, jie bus pašalinti ir iš visų grupių, bei duomenų bazės.'
NEWGROUP: 'Nauja grupė' NEWGROUP: 'Nauja grupė'
PERMISSIONS: Leidimai PERMISSIONS: Leidimai
ROLES: Rolės ROLES: Rolės
@ -499,9 +499,9 @@ lt:
Users: Vartotojai Users: Vartotojai
SecurityAdmin_MemberImportForm: SecurityAdmin_MemberImportForm:
BtnImport: 'Importuoti iš CSV' BtnImport: 'Importuoti iš CSV'
FileFieldLabel: 'CSV byla <small>(Leidžiami plėtiniai: *.csv)</small>' FileFieldLabel: 'CSV byla <small>(leidžiami plėtiniai: *.csv)</small>'
SilverStripeNavigator: SilverStripeNavigator:
Auto: Automatiškai Auto: Automatinis
ChangeViewMode: 'Pakeisti peržiūros rėžimą' ChangeViewMode: 'Pakeisti peržiūros rėžimą'
Desktop: Kompiuteris Desktop: Kompiuteris
DualWindowView: 'Du langai' DualWindowView: 'Du langai'

View File

@ -85,10 +85,25 @@ class DataDifferencer extends ViewableData {
if(in_array($field, $this->ignoredFields)) continue; if(in_array($field, $this->ignoredFields)) continue;
if(in_array($field, array_keys($hasOnes))) continue; if(in_array($field, array_keys($hasOnes))) continue;
// Check if a field from-value is comparable
$toField = $this->toRecord->obj($field);
if(!($toField instanceof DBField)) continue;
$toValue = $toField->forTemplate();
// Show only to value
if(!$this->fromRecord) { if(!$this->fromRecord) {
$diffed->setField($field, "<ins>" . $this->toRecord->$field . "</ins>"); $diffed->setField($field, "<ins>{$toValue}</ins>");
} else if($this->fromRecord->$field != $this->toRecord->$field) { continue;
$diffed->setField($field, Diff::compareHTML($this->fromRecord->$field, $this->toRecord->$field)); }
// Check if a field to-value is comparable
$fromField = $this->fromRecord->obj($field);
if(!($fromField instanceof DBField)) continue;
$fromValue = $fromField->forTemplate();
// Show changes between the two, if any exist
if($fromValue != $toValue) {
$diffed->setField($field, Diff::compareHTML($fromValue, $toValue));
} }
} }

View File

@ -277,7 +277,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
* @example $list = $list->sort('Name'); // default ASC sorting * @example $list = $list->sort('Name'); // default ASC sorting
* @example $list = $list->sort('Name DESC'); // DESC sorting * @example $list = $list->sort('Name DESC'); // DESC sorting
* @example $list = $list->sort('Name', 'ASC'); * @example $list = $list->sort('Name', 'ASC');
* @example $list = $list->sort(array('Name'=>'ASC,'Age'=>'DESC')); * @example $list = $list->sort(array('Name'=>'ASC', 'Age'=>'DESC'));
* *
* @param String|array Escaped SQL statement. If passed as array, all keys and values are assumed to be escaped. * @param String|array Escaped SQL statement. If passed as array, all keys and values are assumed to be escaped.
* @return DataList * @return DataList

View File

@ -1817,9 +1817,14 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
*/ */
public function db($fieldName = null) { public function db($fieldName = null) {
$classes = ClassInfo::ancestry($this, true); $classes = ClassInfo::ancestry($this, true);
$items = array();
foreach(array_reverse($classes) as $class) { // If we're looking for a specific field, we want to hit subclasses first as they may override field types
if($fieldName) {
$classes = array_reverse($classes);
}
$items = array();
foreach($classes as $class) {
if(isset(self::$_cache_db[$class])) { if(isset(self::$_cache_db[$class])) {
$dbItems = self::$_cache_db[$class]; $dbItems = self::$_cache_db[$class];
} else { } else {
@ -3539,6 +3544,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @var array * @var array
*/ */
private static $casting = array( private static $casting = array(
"ID" => 'Int',
"ClassName" => 'Varchar',
"LastEdited" => "SS_Datetime", "LastEdited" => "SS_Datetime",
"Created" => "SS_Datetime", "Created" => "SS_Datetime",
"Title" => 'Text', "Title" => 'Text',

View File

@ -97,9 +97,9 @@ class Member extends DataObject implements TemplateGlobalProvider {
); );
private static $summary_fields = array( private static $summary_fields = array(
'FirstName' => 'First Name', 'FirstName',
'Surname' => 'Last Name', 'Surname',
'Email' => 'Email', 'Email',
); );
/** /**

View File

@ -99,7 +99,7 @@ class PermissionCheckboxSetField extends FormField {
if(!isset($uninheritedCodes[$permission->Code])) $uninheritedCodes[$permission->Code] = array(); if(!isset($uninheritedCodes[$permission->Code])) $uninheritedCodes[$permission->Code] = array();
$uninheritedCodes[$permission->Code][] = _t( $uninheritedCodes[$permission->Code][] = _t(
'PermissionCheckboxSetField.AssignedTo', 'assigned to "{title}"', 'PermissionCheckboxSetField.AssignedTo', 'assigned to "{title}"',
array('title' => $record->Title) array('title' => $record->dbObject('Title')->forTemplate())
); );
} }
@ -115,7 +115,7 @@ class PermissionCheckboxSetField extends FormField {
'PermissionCheckboxSetField.FromRole', 'PermissionCheckboxSetField.FromRole',
'inherited from role "{title}"', 'inherited from role "{title}"',
'A permission inherited from a certain permission role', 'A permission inherited from a certain permission role',
array('title' => $role->Title) array('title' => $role->dbObject('Title')->forTemplate())
); );
} }
} }
@ -134,7 +134,7 @@ class PermissionCheckboxSetField extends FormField {
'PermissionCheckboxSetField.FromRoleOnGroup', 'PermissionCheckboxSetField.FromRoleOnGroup',
'inherited from role "%s" on group "%s"', 'inherited from role "%s" on group "%s"',
'A permission inherited from a role on a certain group', 'A permission inherited from a role on a certain group',
array('roletitle' => $role->Title, 'grouptitle' => $parent->Title) array('roletitle' => $role->dbObject('Title')->forTemplate(), 'grouptitle' => $parent->dbObject('Title')->forTemplate())
); );
} }
} }
@ -149,7 +149,7 @@ class PermissionCheckboxSetField extends FormField {
'PermissionCheckboxSetField.FromGroup', 'PermissionCheckboxSetField.FromGroup',
'inherited from group "{title}"', 'inherited from group "{title}"',
'A permission inherited from a certain group', 'A permission inherited from a certain group',
array('title' => $parent->Title) array('title' => $parent->dbObject('Title')->forTemplate())
); );
} }
} }

View File

@ -1,6 +1,6 @@
<% if $UseButtonTag %> <% if $UseButtonTag %>
<button $AttributesHTML> <button $AttributesHTML>
<% if $ButtonContent %>$ButtonContent<% else %>$Title<% end_if %> <% if $ButtonContent %>$ButtonContent<% else %>$Title.XML<% end_if %>
</button> </button>
<% else %> <% else %>
<input $AttributesHTML /> <input $AttributesHTML />

View File

@ -13,7 +13,10 @@
<ul class="SelectionGroup<% if extraClass %> $extraClass<% end_if %>"> <ul class="SelectionGroup<% if extraClass %> $extraClass<% end_if %>">
<% loop $FieldSet %> <% loop $FieldSet %>
<li$Selected> <li$Selected>
{$RadioButton}{$RadioLabel}{$FieldHolder} {$RadioButton}{$RadioLabel}
<% if $FieldList %>
$FieldHolder
<% end_if %>
</li> </li>
<% end_loop %> <% end_loop %>
</ul> </ul>

View File

@ -1,8 +1,8 @@
<div id="$HolderID" <div id="$HolderID"
class="TreeDropdownField single<% if extraClass %> $extraClass<% end_if %><% if ShowSearch %> searchable<% end_if %>" class="TreeDropdownField <% if $extraClass %> $extraClass<% end_if %><% if $ShowSearch %> searchable<% end_if %>"
data-url-tree="$Link(tree)" data-url-tree="$Link('tree')"
data-title="$TitleURLEncoded" data-title="$Title.ATT"
<% if $Description %>title="$Description"<% end_if %> <% if $Description %>title="$Description.ATT"<% end_if %>
<% if $Metadata %>data-metadata="$Metadata"<% end_if %> tabindex="0"> <% if $Metadata %>data-metadata="$Metadata.ATT"<% end_if %> tabindex="0">
<input id="$ID" type="hidden" name="$Name" value="$Value" /> <input id="$ID" type="hidden" name="$Name.ATT" value="$Value.ATT" />
</div> </div>

View File

@ -22,20 +22,22 @@ class CoreTest extends SapphireTest {
$this->assertEquals(getTempFolder(BASE_PATH), $this->tempPath . DIRECTORY_SEPARATOR . $user); $this->assertEquals(getTempFolder(BASE_PATH), $this->tempPath . DIRECTORY_SEPARATOR . $user);
} else { } else {
$user = getTempFolderUsername(); $user = getTempFolderUsername();
$base = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'silverstripe-cache-php' .
preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION);
// A typical Windows location for where sites are stored on IIS // A typical Windows location for where sites are stored on IIS
$this->assertEquals(sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->assertEquals(
'silverstripe-cacheC--inetpub-wwwroot-silverstripe-test-project' . DIRECTORY_SEPARATOR . $user, $base . 'C--inetpub-wwwroot-silverstripe-test-project' . DIRECTORY_SEPARATOR . $user,
getTempFolder('C:\\inetpub\\wwwroot\\silverstripe-test-project')); getTempFolder('C:\\inetpub\\wwwroot\\silverstripe-test-project'));
// A typical Mac OS X location for where sites are stored // A typical Mac OS X location for where sites are stored
$this->assertEquals(sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->assertEquals(
'silverstripe-cache-Users-joebloggs-Sites-silverstripe-test-project' . DIRECTORY_SEPARATOR . $user, $base . '-Users-joebloggs-Sites-silverstripe-test-project' . DIRECTORY_SEPARATOR . $user,
getTempFolder('/Users/joebloggs/Sites/silverstripe-test-project')); getTempFolder('/Users/joebloggs/Sites/silverstripe-test-project'));
// A typical Linux location for where sites are stored // A typical Linux location for where sites are stored
$this->assertEquals(sys_get_temp_dir() . DIRECTORY_SEPARATOR . $this->assertEquals(
'silverstripe-cache-var-www-silverstripe-test-project' . DIRECTORY_SEPARATOR . $user, $base . '-var-www-silverstripe-test-project' . DIRECTORY_SEPARATOR . $user,
getTempFolder('/var/www/silverstripe-test-project')); getTempFolder('/var/www/silverstripe-test-project'));
} }
} }
@ -43,12 +45,14 @@ class CoreTest extends SapphireTest {
public function tearDown() { public function tearDown() {
parent::tearDown(); parent::tearDown();
$user = getTempFolderUsername(); $user = getTempFolderUsername();
$base = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'silverstripe-cache-php' .
preg_replace('/[^\w-\.+]+/', '-', PHP_VERSION);
foreach(array( foreach(array(
'silverstripe-cacheC--inetpub-wwwroot-silverstripe-test-project', 'C--inetpub-wwwroot-silverstripe-test-project',
'silverstripe-cache-Users-joebloggs-Sites-silverstripe-test-project', '-Users-joebloggs-Sites-silverstripe-test-project',
'silverstripe-cache-var-www-silverstripe-test-project' '-cache-var-www-silverstripe-test-project'
) as $dir) { ) as $dir) {
$path = sys_get_temp_dir().DIRECTORY_SEPARATOR.$dir; $path = $base . $dir;
if(file_exists($path)) { if(file_exists($path)) {
rmdir($path . DIRECTORY_SEPARATOR . $user); rmdir($path . DIRECTORY_SEPARATOR . $user);
rmdir($path); rmdir($path);

View File

@ -8,33 +8,202 @@ class NumericFieldTest extends SapphireTest {
protected $usesDatabase = false; protected $usesDatabase = false;
public function testValidator() { /**
i18n::set_locale('en_US'); * In some cases and locales, validation expects non-breaking spaces.
*
* Duplicates non-public NumericField::clean method
*
* @param string $input
* @return string The input value, with all spaces replaced with non-breaking spaces
*/
protected function clean($input) {
$nbsp = html_entity_decode('&nbsp;', null, 'UTF-8');
return str_replace(' ', $nbsp, trim($input));
}
protected function checkInputValidation($locale, $tests) {
i18n::set_locale($locale);
$field = new NumericField('Number'); $field = new NumericField('Number');
$field->setValue('12.00'); $validator = new RequiredFields('Number');
$validator = new RequiredFields(); foreach($tests as $input => $output) {
$this->assertTrue($field->validate($validator)); // Both decimal and thousands B
$field->setValue($input);
$field->setValue('12,00'); if($output === false) {
$this->assertFalse($field->validate($validator)); $this->assertFalse(
$field->validate($validator),
"Expect validation to fail for input $input in locale $locale"
);
$this->assertEquals(
0,
$field->dataValue(),
"Expect invalid value to be rewritten to 0 in locale $locale"
);
// Even invalid values shouldn't be rewritten
$this->assertEquals(
$this->clean($input),
$field->Value(),
"Expected input $input to be saved in the field in locale $locale"
);
} else {
$this->assertTrue(
$field->validate($validator),
"Expect validation to succeed for $input in locale $locale"
);
$this->assertEquals(
$output,
$field->dataValue(),
"Expect value $input to be mapped to $output in locale $locale"
);
}
}
}
/**
* Test that data loaded in via Form::loadDataFrom(DataObject) will populate the field correctly,
* and can format the database value appropriately for the frontend
*
* @param string $locale
* @param array $tests
*/
public function checkDataFormatting($locale, $tests) {
i18n::set_locale($locale);
$field = new NumericField('Number');
$form = new Form(new Controller(), 'Form', new FieldList($field), new FieldList());
$dataObject = new NumericFieldTest_Object();
foreach($tests as $input => $output) {
// Given a dataobject as a context, the field should assume the field value is not localised
$dataObject->Number = (string)$input;
$form->loadDataFrom($dataObject, Form::MERGE_CLEAR_MISSING);
// Test value
$this->assertEquals(
$input,
$field->dataValue(),
"Expected $input loaded via dataobject to be left intact in locale $locale"
);
// Test expected formatted value (Substitute nbsp for spaces)
$this->assertEquals(
$this->clean($output),
$field->Value(),
"Expected $input to be formatted as $output in locale $locale"
);
}
}
/**
* German locale values (same as dutch)
*/
public function testGermanLocales() {
$this->checkDataFormatting('de_DE', $formatting = array(
'13000' => "13.000",
'15' => '15',
'12.0' => '12,0',
'12.1' => '12,1',
'14000.5' => "14.000,5",
));
$this->checkDataFormatting('nl_NL', $formatting);
$this->checkInputValidation('de_DE', $validation = array(
'13000' => 13000,
'12,00' => 12.00,
'12.00' => false,
'11 000' => false,
'11.000' => 11000,
'11,000' => 11.0,
'15 000,5' => false,
'15 000.5' => false,
'15.000,5' => 15000.5,
'15,000.5' => false,
));
$this->checkInputValidation('nl_NL', $validation);
}
/**
* French locale values
*/
public function testFrenchLocales() {
$this->checkDataFormatting('fr_FR', array(
'13000' => "13 000",
'15' => '15',
'12.0' => '12,0',
'12.1' => '12,1',
'14000.5' => "14 000,5",
));
$this->checkInputValidation('fr_FR', array(
'13000' => 13000,
'12,00' => 12.00,
'12.00' => false,
'11 000' => 11000,
'11.000' => false,
'11,000' => 11.000,
'15 000,5' => 15000.5,
'15 000.5' => false,
'15.000,5' => false,
'15,000.5' => false,
));
}
/**
* US locale values
*/
public function testUSLocales() {
$this->checkDataFormatting('en_US', array(
'13000' => "13,000",
'15' => '15',
'12.0' => '12.0',
'12.1' => '12.1',
'14000.5' => "14,000.5",
));
$this->checkInputValidation('en_US', array(
'13000' => 13000,
'12,00' => false,
'12.00' => 12.00,
'11 000' => false,
'11.000' => 11.0,
'11,000' => 11000,
'15 000,5' => false,
'15 000.5' => false,
'15.000,5' => false,
'15,000.5' => 15000.5,
));
}
/**
* Test empty values
*/
public function testEmptyValidator() {
i18n::set_locale('en_US');
$field = new NumericField('Number');
// Treats '0' as given for the sake of required fields
$field->setValue('0'); $field->setValue('0');
$this->assertTrue($field->validate($validator)); $this->assertTrue($field->validate($validator));
$this->assertEquals(0, $field->dataValue());
$field->setValue(false); // Treat literal 0
$field->setValue(0);
$this->assertTrue($field->validate($validator)); $this->assertTrue($field->validate($validator));
$this->assertEquals(0, $field->dataValue());
i18n::set_locale('de_DE'); // Should fail the 'required but not given' test
$field->setValue('12,00'); $field->setValue('');
$validator = new RequiredFields();
$this->assertTrue($field->validate($validator));
$field->setValue('12.00');
$this->assertFalse($field->validate($validator)); $this->assertFalse($field->validate($validator));
$field->setValue(0); $field->setValue(false);
}
public function testReadonly() {
i18n::set_locale('en_US');
$field = new NumericField('Number');
$this->assertRegExp("#<span[^>]+>\s*0\s*<\/span>#", "".$field->performReadonlyTransformation()->Field().""); $this->assertRegExp("#<span[^>]+>\s*0\s*<\/span>#", "".$field->performReadonlyTransformation()->Field()."");
} }
@ -47,3 +216,10 @@ class NumericFieldTest extends SapphireTest {
} }
} }
class NumericFieldTest_Object extends DataObject implements TestOnly {
private static $db = array(
'Number' => 'Float'
);
}

View File

@ -1,4 +1,9 @@
<?php <?php
/**
* @package framework
* @subpackage tests
*/
class GridFieldExportButtonTest extends SapphireTest { class GridFieldExportButtonTest extends SapphireTest {
protected $list; protected $list;
@ -10,7 +15,8 @@ class GridFieldExportButtonTest extends SapphireTest {
protected static $fixture_file = 'GridFieldExportButtonTest.yml'; protected static $fixture_file = 'GridFieldExportButtonTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = array(
'GridFieldExportButtonTest_Team' 'GridFieldExportButtonTest_Team',
'GridFieldExportButtonTest_NoView'
); );
public function setUp() { public function setUp() {
@ -22,6 +28,21 @@ class GridFieldExportButtonTest extends SapphireTest {
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config); $this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
} }
public function testCanView() {
$list = new DataList('GridFieldExportButtonTest_NoView');
$button = new GridFieldExportButton();
$button->setExportColumns(array('Name' => 'My Name'));
$config = GridFieldConfig::create()->addComponent(new GridFieldExportButton());
$gridField = new GridField('testfield', 'testfield', $list, $config);
$this->assertEquals(
"\"My Name\"\n",
$button->generateExportFileData($gridField)
);
}
public function testGenerateFileDataBasicFields() { public function testGenerateFileDataBasicFields() {
$button = new GridFieldExportButton(); $button = new GridFieldExportButton();
$button->setExportColumns(array('Name' => 'My Name')); $button->setExportColumns(array('Name' => 'My Name'));
@ -94,8 +115,12 @@ class GridFieldExportButtonTest extends SapphireTest {
$button->generateExportFileData($this->gridField) $button->generateExportFileData($this->gridField)
); );
} }
} }
/**
* @package framework
* @subpackage tests
*/
class GridFieldExportButtonTest_Team extends DataObject implements TestOnly { class GridFieldExportButtonTest_Team extends DataObject implements TestOnly {
private static $db = array( private static $db = array(
@ -109,3 +134,20 @@ class GridFieldExportButtonTest_Team extends DataObject implements TestOnly {
} }
/**
* @package framework
* @subpackage tests
*/
class GridFieldExportButtonTest_NoView extends DataObject implements TestOnly {
private static $db = array(
'Name' => 'Varchar',
'City' => 'Varchar'
);
public function canView($member = null) {
return false;
}
}

View File

@ -6,3 +6,6 @@ GridFieldExportButtonTest_Team:
Name: Test2 Name: Test2
City: City2 City: City2
GridFieldExportButtonTest_NoView:
item1:
Name: Foo

View File

@ -17,7 +17,7 @@ class DataDifferencerTest extends SapphireTest {
public function testArrayValues() { public function testArrayValues() {
$obj1 = $this->objFromFixture('DataDifferencerTest_Object', 'obj1'); $obj1 = $this->objFromFixture('DataDifferencerTest_Object', 'obj1');
// create a new version // create a new version
$obj1->Choices = array('a'); $obj1->Choices = 'a';
$obj1->write(); $obj1->write();
$obj1v1 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version-1); $obj1v1 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version-1);
$obj1v2 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version); $obj1v2 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version);
@ -88,14 +88,6 @@ class DataDifferencerTest_Object extends DataObject implements TestOnly {
return $fields; return $fields;
} }
public function getChoices() {
return explode(',', $this->getField('Choices'));
}
public function setChoices($val) {
$this->setField('Choices', (is_array($val)) ? implode(',', $val) : $val);
}
} }
class DataDifferencerTest_HasOneRelationObject extends DataObject implements TestOnly { class DataDifferencerTest_HasOneRelationObject extends DataObject implements TestOnly {

View File

@ -40,9 +40,19 @@ class DataObjectTest extends SapphireTest {
$this->assertEquals('Text', $obj->db('Comment')); $this->assertEquals('Text', $obj->db('Comment'));
$obj = new DataObjectTest_ExtendedTeamComment(); $obj = new DataObjectTest_ExtendedTeamComment();
$dbFields = $obj->db();
// Assert overloaded fields have correct data type // Assert overloaded fields have correct data type
$this->assertEquals('HTMLText', $obj->db('Comment')); $this->assertEquals('HTMLText', $obj->db('Comment'));
$this->assertEquals('HTMLText', $dbFields['Comment'],
'Calls to DataObject::db without a field specified return correct data types');
// assertEquals doesn't verify the order of array elements, so access keys manually to check order:
// expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText')
reset($dbFields);
$this->assertEquals('Name', key($dbFields), 'DataObject::db returns fields in correct order');
next($dbFields);
$this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order');
} }
public function testValidObjectsForBaseFields() { public function testValidObjectsForBaseFields() {

View File

@ -106,6 +106,54 @@ class SSViewerTest extends SapphireTest {
$this->assertFalse((bool)trim($template), "Should be no content in this return."); $this->assertFalse((bool)trim($template), "Should be no content in this return.");
} }
public function testRequirementsCombine(){
$oldBackend = Requirements::backend();
$testBackend = new Requirements_Backend();
Requirements::set_backend($testBackend);
$combinedTestFilePath = BASE_PATH . '/' . $testBackend->getCombinedFilesFolder() . '/testRequirementsCombine.js';
$jsFile = FRAMEWORK_DIR . '/tests/view/themes/javascript/bad.js';
$jsFileContents = file_get_contents(BASE_PATH . '/' . $jsFile);
Requirements::combine_files('testRequirementsCombine.js', array($jsFile));
require_once('thirdparty/jsmin/jsmin.php');
// first make sure that our test js file causes an exception to be thrown
try{
$content = JSMin::minify($content);
Requirements::set_backend($oldBackend);
$this->fail('JSMin did not throw exception on minify bad file: ');
}catch(Exception $e){
// exception thrown... good
}
// secondly, make sure that requirements combine throws the correct warning, and only that warning
@unlink($combinedTestFilePath);
try{
Requirements::process_combined_files();
}catch(PHPUnit_Framework_Error_Warning $e){
if(strstr($e->getMessage(), 'Failed to minify') === false){
Requirements::set_backend($oldBackend);
$this->fail('Requirements::process_combined_files raised a warning, which is good, but this is not the expected warning ("Failed to minify..."): '.$e);
}
}catch(Exception $e){
Requirements::set_backend($oldBackend);
$this->fail('Requirements::process_combined_files did not catch exception caused by minifying bad js file: '.$e);
}
// and make sure the combined content matches the input content, i.e. no loss of functionality
if(!file_exists($combinedTestFilePath)){
Requirements::set_backend($oldBackend);
$this->fail('No combined file was created at expected path: '.$combinedTestFilePath);
}
$combinedTestFileContents = file_get_contents($combinedTestFilePath);
$this->assertContains($jsFileContents, $combinedTestFileContents);
// reset
Requirements::set_backend($oldBackend);
}
public function testComments() { public function testComments() {
$output = $this->render(<<<SS $output = $this->render(<<<SS
This is my template<%-- this is a comment --%>This is some content<%-- this is another comment --%>Final content This is my template<%-- this is a comment --%>This is some content<%-- this is another comment --%>Final content

View File

@ -0,0 +1 @@
this is a javascript file which should cause an exception to be thrown when minified by jsmin '

View File

@ -12,14 +12,14 @@
$frameworkPath = rtrim(dirname(dirname(dirname(__FILE__))), DIRECTORY_SEPARATOR); $frameworkPath = rtrim(dirname(dirname(dirname(__FILE__))), DIRECTORY_SEPARATOR);
$basePath = rtrim(dirname($frameworkPath), DIRECTORY_SEPARATOR); $basePath = rtrim(dirname($frameworkPath), DIRECTORY_SEPARATOR);
require_once $frameworkPath . '/core/TempPath.php'; require_once $frameworkPath . '/core/Constants.php';
// Handle incoming request if it's a script call // Handle incoming request if it's a script call
if (TinyMCE_Compressor::getParam("js")) { if (TinyMCE_Compressor::getParam("js")) {
// Default settings // Default settings
$tinyMCECompressor = new TinyMCE_Compressor(array( $tinyMCECompressor = new TinyMCE_Compressor(array(
// CUSTOM SilverStripe // CUSTOM SilverStripe
'cache_dir' => getTempFolder($basePath) 'cache_dir' => TEMP_FOLDER
// CUSTOM END // CUSTOM END
)); ));

View File

@ -1139,10 +1139,16 @@ class Requirements_Backend {
if(!$refresh) continue; if(!$refresh) continue;
$failedToMinify = false;
$combinedData = ""; $combinedData = "";
foreach(array_diff($fileList, $this->blocked) as $file) { foreach(array_diff($fileList, $this->blocked) as $file) {
$fileContent = file_get_contents($base . $file); $fileContent = file_get_contents($base . $file);
try{
$fileContent = $this->minifyFile($file, $fileContent); $fileContent = $this->minifyFile($file, $fileContent);
}catch(Exception $e){
$failedToMinify = true;
}
if ($this->write_header_comment) { if ($this->write_header_comment) {
// write a header comment for each file for easier identification and debugging // write a header comment for each file for easier identification and debugging
@ -1161,6 +1167,12 @@ class Requirements_Backend {
unset($fh); unset($fh);
} }
if($failedToMinify){
// Failed to minify, use unminified. This warning is raised at the end to allow code execution
// to complete in case this warning is caught inside a try-catch block.
user_error('Failed to minify '.$file.', exception: '.$e->getMessage(), E_USER_WARNING);
}
// Unsuccessful write - just include the regular JS files, rather than the combined one // Unsuccessful write - just include the regular JS files, rather than the combined one
if(!$successfulWrite) { if(!$successfulWrite) {
user_error("Requirements_Backend::process_combined_files(): Couldn't create '$combinedFilePath'", user_error("Requirements_Backend::process_combined_files(): Couldn't create '$combinedFilePath'",

View File

@ -791,12 +791,14 @@ class SSViewer implements Flushable {
if(!$this->chosenTemplates) { if(!$this->chosenTemplates) {
$templateList = (is_array($templateList)) ? $templateList : array($templateList); $templateList = (is_array($templateList)) ? $templateList : array($templateList);
user_error( $message = 'None of the following templates could be found';
"None of these templates can be found in theme '" if(!$theme) {
. Config::inst()->get('SSViewer', 'theme') . "': " $message .= ' (no theme in use)';
. implode(".ss, ", $templateList) . ".ss", } else {
E_USER_WARNING $message .= ' in theme "' . $theme . '"';
); }
user_error($message . ': ' . implode(".ss, ", $templateList) . ".ss", E_USER_WARNING);
} }
} }

View File

@ -280,9 +280,7 @@ class ViewableData extends Object implements IteratorAggregate {
* @return string 'xml'|'raw' * @return string 'xml'|'raw'
*/ */
public function escapeTypeForField($field) { public function escapeTypeForField($field) {
if(!$class = $this->castingClass($field)) { $class = $this->castingClass($field) ?: $this->config()->default_cast;
$class = self::$default_cast;
}
return Config::inst()->get($class, 'escape_type', Config::FIRST_SET); return Config::inst()->get($class, 'escape_type', Config::FIRST_SET);
} }
@ -374,7 +372,7 @@ class ViewableData extends Object implements IteratorAggregate {
if(!is_object($value) && ($this->castingClass($fieldName) || $forceReturnedObject)) { if(!is_object($value) && ($this->castingClass($fieldName) || $forceReturnedObject)) {
if(!$castConstructor = $this->castingHelper($fieldName)) { if(!$castConstructor = $this->castingHelper($fieldName)) {
$castConstructor = $this->stat('default_cast'); $castConstructor = $this->config()->default_cast;
} }
$valueObject = Object::create_from_string($castConstructor, $fieldName); $valueObject = Object::create_from_string($castConstructor, $fieldName);
@ -389,7 +387,7 @@ class ViewableData extends Object implements IteratorAggregate {
} }
if(!is_object($value) && $forceReturnedObject) { if(!is_object($value) && $forceReturnedObject) {
$default = Config::inst()->get('ViewableData', 'default_cast', Config::FIRST_SET); $default = $this->config()->default_cast;
$castedValue = new $default($fieldName); $castedValue = new $default($fieldName);
$castedValue->setValue($value); $castedValue->setValue($value);
$value = $castedValue; $value = $castedValue;