mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge remote-tracking branch 'origin/3.1'
Conflicts: .travis.yml
This commit is contained in:
commit
8f31352039
@ -22,6 +22,8 @@ matrix:
|
||||
env: DB=MYSQL CORE_RELEASE=master
|
||||
- php: 5.5
|
||||
env: DB=MYSQL CORE_RELEASE=master
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=master
|
||||
- php: 5.4
|
||||
env: DB=MYSQL CORE_RELEASE=master BEHAT_TEST=1
|
||||
|
||||
|
2
cache/Cache.php
vendored
2
cache/Cache.php
vendored
@ -76,7 +76,7 @@
|
||||
* <h2>Using APC as a store</h2>
|
||||
*
|
||||
* <code>
|
||||
* SS_Cache::add_backend('two-level', 'TwoLevels', array(
|
||||
* SS_Cache::add_backend('two-level', 'Two-Levels', array(
|
||||
* 'slow_backend' => 'File',
|
||||
* 'fast_backend' => 'Apc',
|
||||
* 'slow_backend_options' => array(
|
||||
|
@ -26,5 +26,6 @@
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["tests/behat/features/bootstrap"]
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
* Represents a HTTP-request, including a URL that is tokenised for parsing, and a request method
|
||||
* (GET/POST/PUT/DELETE). This is used by {@link RequestHandler} objects to decide what to do.
|
||||
*
|
||||
* Caution: objects of this class are immutable, e.g. echo $request['a']; works as expected,
|
||||
* but $request['a'] = '1'; has no effect.
|
||||
*
|
||||
* The intention is that a single SS_HTTPRequest object can be passed from one object to another, each object calling
|
||||
* match() to get the information that they need out of the URL. This is generally handled by
|
||||
* {@link RequestHandler::handleRequest()}.
|
||||
|
@ -23,6 +23,13 @@ Used in side panels and action tabs
|
||||
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-error-text { color: red; font-weight: bold; width: 150px; }
|
||||
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-warning-text { color: #b7a403; }
|
||||
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status.ui-state-success-text { color: #1f9433; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-preview { width: auto; height: auto; margin-right: 15px; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-info { margin-left: 0; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name { float: left; width: 70%; height: auto; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name .name { float: left; width: 100%; margin-bottom: 5px; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-name .ss-uploadfield-item-status { float: left; width: 100%; padding: 0; text-align: left; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-actions { float: right; width: 5%; min-height: 0; margin: 0; }
|
||||
.ss-uploadfield .ss-uploadfield-item.ui-state-error .ss-uploadfield-item-info .ss-uploadfield-item-actions .ss-uploadfield-item-cancel { position: relative; top: auto; }
|
||||
.ss-uploadfield .ss-ui-button { display: block; float: left; margin: 0 10px 6px 0; }
|
||||
.ss-uploadfield .ss-ui-button.ss-uploadfield-fromcomputer { position: relative; overflow: hidden; }
|
||||
.ss-uploadfield .ss-uploadfield-files { margin: 0; padding: 0; overflow: auto; position: relative; }
|
||||
|
@ -144,8 +144,13 @@ class DebugView extends Object {
|
||||
public function writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
$errorType = isset(self::$error_types[$errno]) ? self::$error_types[$errno] : self::$unknown_error;
|
||||
$httpRequestEnt = htmlentities($httpRequest, ENT_COMPAT, 'UTF-8');
|
||||
if (ini_get('html_errors')) {
|
||||
$errstr = strip_tags($errstr);
|
||||
} else {
|
||||
$errstr = Convert::raw2xml($errstr);
|
||||
}
|
||||
echo '<div class="info ' . $errorType['class'] . '">';
|
||||
echo "<h1>[" . $errorType['title'] . '] ' . strip_tags($errstr) . "</h1>";
|
||||
echo "<h1>[" . $errorType['title'] . '] ' . $errstr . "</h1>";
|
||||
echo "<h3>$httpRequestEnt</h3>";
|
||||
echo "<p>Line <strong>$errline</strong> in <strong>$errfile</strong></p>";
|
||||
echo '</div>';
|
||||
|
@ -48,7 +48,7 @@ $dirsToCheck = array(
|
||||
if($dirsToCheck[0] == $dirsToCheck[1]) {
|
||||
unset($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)
|
||||
// or until we hit a dir we can't read
|
||||
do {
|
||||
@ -1061,12 +1061,16 @@ class InstallRequirements {
|
||||
$helperPath = $adapters[$databaseClass]['helperPath'];
|
||||
$class = str_replace('.php', '', basename($helperPath));
|
||||
}
|
||||
return (class_exists($class)) ? new $class() : new MySQLDatabaseConfigurationHelper();
|
||||
return (class_exists($class)) ? new $class() : false;
|
||||
}
|
||||
|
||||
function requireDatabaseFunctions($databaseConfig, $testDetails) {
|
||||
$this->testing($testDetails);
|
||||
$helper = $this->getDatabaseConfigurationHelper($databaseConfig['type']);
|
||||
if (!$helper) {
|
||||
$this->error("Couldn't load database helper code for ". $databaseConfig['type']);
|
||||
return false;
|
||||
}
|
||||
$result = $helper->requireDatabaseFunctions($databaseConfig);
|
||||
if($result) {
|
||||
return true;
|
||||
|
@ -110,6 +110,16 @@ For output of an image tag with the image automatically resized to 80px width, y
|
||||
For usage on a website form, see `[api:FileField]`.
|
||||
If you want to upload images within the CMS, see `[api:UploadField]`.
|
||||
|
||||
### Image Quality
|
||||
|
||||
To adjust the quality of the generated images when they are resized add the following to your mysite/config/config.yml file:
|
||||
|
||||
:::yml
|
||||
GDBackend:
|
||||
default_quality: 90
|
||||
|
||||
The default value is 75.
|
||||
|
||||
### Clearing Thumbnail Cache
|
||||
|
||||
Images are (like all other Files) synchronized with the SilverStripe database.
|
||||
|
@ -21,7 +21,7 @@ define an AbsoluteLink() method.
|
||||
RSSFeed::linkToFeed($link, $title)
|
||||
|
||||
This line should go in your `[api:Controller]` subclass in the action you want
|
||||
to include the HTML link.
|
||||
to include the HTML link. Not all arguments are required, see `[api:RSSFeed]` and example below. Last Modified Time is expected in seconds like time().
|
||||
|
||||
:::php
|
||||
$feed = new RSSFeed(
|
||||
@ -31,7 +31,9 @@ to include the HTML link.
|
||||
$description,
|
||||
$titleField,
|
||||
$descriptionField,
|
||||
$authorField
|
||||
$authorField,
|
||||
$lastModifiedTime,
|
||||
$etag
|
||||
);
|
||||
|
||||
Creates a new `[api:RSSFeed]` instance to be returned. The arguments notify
|
||||
|
@ -200,10 +200,12 @@ the date field will have the date format defined by your locale.
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->addFieldToTab('Root.Main', $dateField = new DateField('Date','Article Date (for example: 20/12/2010)'), 'Content');
|
||||
$dateField = new DateField('Date', 'Article Date (for example: 20/12/2010)');
|
||||
$dateField->setConfig('showcalendar', true);
|
||||
$dateField->setConfig('dateformat', 'dd/MM/YYYY');
|
||||
|
||||
$fields->addFieldToTab('Root.Main', $dateField, 'Content');
|
||||
$fields->addFieldToTab('Root.Main', new TextField('Author'), 'Content');
|
||||
$fields->addFieldToTab('Root.Main', new TextField('Author', 'Author Name'), 'Content');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
@ -211,7 +213,7 @@ the date field will have the date format defined by your locale.
|
||||
Let's walk through these changes.
|
||||
|
||||
:::php
|
||||
$fields->addFieldToTab('Root.Main', $dateField = new DateField('Date','Article Date (for example: 20/12/2010)'), 'Content');
|
||||
$dateField = new DateField('Date', 'Article Date (for example: 20/12/2010)');
|
||||
|
||||
*$dateField* is declared in order to change the configuration of the DateField.
|
||||
|
||||
@ -335,19 +337,23 @@ Now let's make a purely cosmetic change that nevertheless helps to make the info
|
||||
Add the following field to the *ArticleHolder* and *ArticlePage* classes:
|
||||
|
||||
:::php
|
||||
private static $icon = "framework/docs/en/tutorials/_images/treeicons/news-file.gif";
|
||||
private static $icon = "cms/images/treeicons/news-file.gif";
|
||||
|
||||
|
||||
And this one to the *HomePage* class:
|
||||
|
||||
:::php
|
||||
private static $icon = "framework/docs/en/tutorials/_images/treeicons/home-file.gif";
|
||||
private static $icon = "cms/images/treeicons/home-file.png";
|
||||
|
||||
|
||||
This will change the icons for the pages in the CMS.
|
||||
|
||||
![](_images/tutorial2_icons2.jpg)
|
||||
|
||||
<div class="hint" markdown="1">
|
||||
Note: The `news-file` icon may not exist in a default SilverStripe installation. Try adding your own image or choosing a different one from the `treeicons` collection.
|
||||
</div>
|
||||
|
||||
## Showing the latest news on the homepage
|
||||
|
||||
It would be nice to greet page visitors with a summary of the latest news when they visit the homepage. This requires a little more code though - the news articles are not direct children of the homepage, so we can't use the *Children* control. We can get the data for news articles by implementing our own function in *HomePage_Controller*.
|
||||
|
@ -40,11 +40,13 @@ class Folder extends File {
|
||||
|
||||
/**
|
||||
* Find the given folder or create it both as {@link Folder} database records
|
||||
* and on the filesystem. If necessary, creates parent folders as well.
|
||||
* and on the filesystem. If necessary, creates parent folders as well. If it's
|
||||
* unable to find or make the folder, it will return null (as /assets is unable
|
||||
* to be represented by a Folder DataObject)
|
||||
*
|
||||
* @param $folderPath string Absolute or relative path to the file.
|
||||
* If path is relative, its interpreted relative to the "assets/" directory.
|
||||
* @return Folder
|
||||
* @return Folder|null
|
||||
*/
|
||||
public static function find_or_make($folderPath) {
|
||||
// Create assets directory, if it is missing
|
||||
|
@ -134,7 +134,9 @@ class Upload extends Controller {
|
||||
$file = $nameFilter->filter($tmpFile['name']);
|
||||
$fileName = basename($file);
|
||||
|
||||
$relativeFilePath = $parentFolder ? $parentFolder->getRelativePath() . "$fileName" : $fileName;
|
||||
$relativeFilePath = $parentFolder
|
||||
? $parentFolder->getRelativePath() . "$fileName"
|
||||
: ASSETS_DIR . "/" . $fileName;
|
||||
|
||||
// Create a new file record (or try to retrieve an existing one)
|
||||
if(!$this->file) {
|
||||
|
@ -1473,7 +1473,7 @@ class Form extends RequestHandler {
|
||||
. " value=\"" . $this->FormAction() . "\" />\n";
|
||||
$content .= "<input type=\"hidden\" name=\"_form_name\" value=\"" . $this->FormName() . "\" />\n";
|
||||
$content .= "<input type=\"hidden\" name=\"_form_method\" value=\"" . $this->FormMethod() . "\" />\n";
|
||||
$content .= "<input type=\"hidden\" name=\"_form_enctype\" value=\"" . $this->FormEncType() . "\" />\n";
|
||||
$content .= "<input type=\"hidden\" name=\"_form_enctype\" value=\"" . $this->getEncType() . "\" />\n";
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ class OptionsetField extends DropdownField {
|
||||
public function performReadonlyTransformation() {
|
||||
// Source and values are DataObject sets.
|
||||
$field = $this->castedCopy('LookupField');
|
||||
$field->setValue($this->getSource());
|
||||
$field->setSource($this->getSource());
|
||||
$field->setReadonly(true);
|
||||
|
||||
return $field;
|
||||
|
@ -279,7 +279,7 @@ class TreeDropdownField extends FormField {
|
||||
if( isset($_REQUEST['forceValue']) || $this->value ) {
|
||||
$forceValue = ( isset($_REQUEST['forceValue']) ? $_REQUEST['forceValue'] : $this->value);
|
||||
if(($values = preg_split('/,\s*/', $forceValue)) && count($values)) foreach($values as $value) {
|
||||
if(!$value) continue;
|
||||
if(!$value || $value == 'unchanged') continue;
|
||||
|
||||
$obj->markToExpose($this->objectForKey($value));
|
||||
}
|
||||
|
@ -1300,7 +1300,9 @@ class UploadField extends FileField {
|
||||
// Resolve expected folder name
|
||||
$folderName = $this->getFolderName();
|
||||
$folder = Folder::find_or_make($folderName);
|
||||
$parentPath = BASE_PATH."/".$folder->getFilename();
|
||||
$parentPath = $folder
|
||||
? BASE_PATH."/".$folder->getFilename()
|
||||
: ASSETS_PATH."/";
|
||||
|
||||
// check if either file exists
|
||||
$exists = false;
|
||||
|
@ -92,6 +92,7 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
foreach($columns as $columnField) {
|
||||
$currentColumn++;
|
||||
$metadata = $gridField->getColumnMetadata($columnField);
|
||||
$fieldName = str_replace('.', '-', $columnField);
|
||||
$title = $metadata['title'];
|
||||
|
||||
if(isset($this->fieldSorting[$columnField]) && $this->fieldSorting[$columnField]) {
|
||||
@ -132,7 +133,7 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
}
|
||||
|
||||
$field = Object::create(
|
||||
'GridField_FormAction', $gridField, 'SetOrder'.$columnField, $title,
|
||||
'GridField_FormAction', $gridField, 'SetOrder'.$fieldName, $title,
|
||||
"sort$dir", array('SortColumn' => $columnField)
|
||||
)->addExtraClass('ss-gridfield-sort');
|
||||
|
||||
@ -148,10 +149,10 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
||||
if($currentColumn == count($columns)
|
||||
&& $gridField->getConfig()->getComponentByType('GridFieldFilterHeader')){
|
||||
|
||||
$field = new LiteralField($columnField,
|
||||
$field = new LiteralField($fieldName,
|
||||
'<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>');
|
||||
} else {
|
||||
$field = new LiteralField($columnField, '<span class="non-sortable">' . $title . '</span>');
|
||||
$field = new LiteralField($fieldName, '<span class="non-sortable">' . $title . '</span>');
|
||||
}
|
||||
}
|
||||
$forTemplate->Fields->push($field);
|
||||
|
@ -1,9 +1,11 @@
|
||||
window.tmpl.cache['ss-uploadfield-downloadtemplate'] = tmpl(
|
||||
'{% for (var i=0, files=o.files, l=files.length, file=files[0]; i<l; file=files[++i]) { %}' +
|
||||
'<li class="ss-uploadfield-item template-download{% if (file.error) { %} ui-state-error{% } %}" data-fileid="{%=file.id%}">' +
|
||||
'{% if (file.thumbnail_url) { %}' +
|
||||
'<div class="ss-uploadfield-item-preview preview"><span>' +
|
||||
'<img src="{%=file.thumbnail_url%}" alt="" />' +
|
||||
'</span></div>' +
|
||||
'{% } %}' +
|
||||
'<div class="ss-uploadfield-item-info">' +
|
||||
'{% if (!file.error) { %}' +
|
||||
'<input type="hidden" name="{%=file.fieldname%}[Files][]" value="{%=file.id%}" />' +
|
||||
|
@ -257,7 +257,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
$db = DB::getConn();
|
||||
if($db->hasField($class, 'ClassName')) {
|
||||
$existing = $db->query("SELECT DISTINCT \"ClassName\" FROM \"$class\"")->column();
|
||||
$classNames = array_unique(array_merge($existing, $classNames));
|
||||
$classNames = array_unique(array_merge($classNames, $existing));
|
||||
}
|
||||
|
||||
self::$classname_spec_cache[$class] = "Enum('" . implode(', ', $classNames) . "')";
|
||||
@ -2187,6 +2187,11 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
|
||||
$dataQuery = new DataQuery($tableClass);
|
||||
|
||||
// Reset query parameter context to that of this DataObject
|
||||
if($params = $this->getSourceQueryParams()) {
|
||||
foreach($params as $key => $value) $dataQuery->setQueryParam($key, $value);
|
||||
}
|
||||
|
||||
// TableField sets the record ID to "new" on new row data, so don't try doing anything in that case
|
||||
if(!is_numeric($this->record['ID'])) return false;
|
||||
|
||||
|
@ -43,6 +43,7 @@ class GroupedList extends SS_ListDecorator {
|
||||
$result = new ArrayList();
|
||||
|
||||
foreach ($grouped as $indVal => $list) {
|
||||
$list = GroupedList::create($list);
|
||||
$result->push(new ArrayData(array(
|
||||
$index => $indVal,
|
||||
$children => $list
|
||||
|
@ -182,13 +182,13 @@ class Versioned extends DataExtension {
|
||||
* @todo Should this all go into VersionedDataQuery?
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
|
||||
if(!$dataQuery || !$dataQuery->getQueryParam('Versioned.mode')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
|
||||
|
||||
switch($dataQuery->getQueryParam('Versioned.mode')) {
|
||||
// Noop
|
||||
case '':
|
||||
break;
|
||||
|
||||
// Reading a specific data from the archive
|
||||
case 'archive':
|
||||
$date = $dataQuery->getQueryParam('Versioned.date');
|
||||
@ -1203,6 +1203,7 @@ class Versioned extends DataExtension {
|
||||
$oldMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage($stage);
|
||||
|
||||
$this->owner->forceChange();
|
||||
$result = $this->owner->write(false, $forceInsert);
|
||||
Versioned::set_reading_mode($oldMode);
|
||||
|
||||
|
@ -38,7 +38,7 @@ class Enum extends StringField {
|
||||
public function __construct($name = null, $enum = NULL, $default = NULL) {
|
||||
if($enum) {
|
||||
if(!is_array($enum)) {
|
||||
$enum = preg_split("/ *, */", trim(trim($enum, ',')));
|
||||
$enum = preg_split("/ *, */", trim($enum));
|
||||
}
|
||||
|
||||
$this->enum = $enum;
|
||||
|
@ -18,7 +18,7 @@ class MultiEnum extends Enum {
|
||||
// Validate and assign the default
|
||||
$this->default = null;
|
||||
if($default) {
|
||||
$defaults = preg_split('/ *, */',trim(trim($default, ',')));
|
||||
$defaults = preg_split('/ *, */',trim($default));
|
||||
foreach($defaults as $thisDefault) {
|
||||
if(!in_array($thisDefault, $this->enum)) {
|
||||
user_error("Enum::__construct() The default value '$thisDefault' does not match "
|
||||
|
@ -85,6 +85,52 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Upload/Validation error
|
||||
&.ui-state-error
|
||||
{
|
||||
.ss-uploadfield-item-preview {
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.ss-uploadfield-item-info {
|
||||
margin-left: 0;
|
||||
|
||||
.ss-uploadfield-item-name {
|
||||
float: left;
|
||||
width: 70%;
|
||||
height: auto;
|
||||
|
||||
.name
|
||||
{
|
||||
float: left;
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.ss-uploadfield-item-status {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.ss-uploadfield-item-actions {
|
||||
float: right;
|
||||
width: 5%;
|
||||
min-height: 0;
|
||||
margin: 0;
|
||||
|
||||
.ss-uploadfield-item-cancel {
|
||||
position: relative;
|
||||
top: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ss-ui-button {
|
||||
display: block;
|
||||
|
@ -114,11 +114,20 @@ class CmsUiContext extends BehatContext {
|
||||
$table_element = null;
|
||||
foreach ($table_elements as $table) {
|
||||
$table_title_element = $table->find('css', '.title');
|
||||
if ($table_title_element->getText() === $title) {
|
||||
if ($table_title_element && $table_title_element->getText() === $title) {
|
||||
$table_element = $table;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Some {@link GridField} tables don't have a visible title, so look for a fieldset with data-name instead
|
||||
if(!$table_element) {
|
||||
$fieldset = $page->findAll('xpath', "//fieldset[@data-name='$title']");
|
||||
if(is_array($fieldset) && isset($fieldset[0])) {
|
||||
$table_element = $fieldset[0]->find('css', '.ss-gridfield-table');
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull($table_element, sprintf('Table `%s` not found', $title));
|
||||
|
||||
return $table_element;
|
||||
|
@ -24,4 +24,14 @@ class OptionsetFieldTest extends SapphireTest {
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
public function testReadonlyField() {
|
||||
$sourceArray = array(0 => 'No', 1 => 'Yes');
|
||||
$field = new OptionsetField('FeelingOk', 'are you feeling ok?', $sourceArray, 1);
|
||||
$field->setEmptyString('(Select one)');
|
||||
$field->setValue(1);
|
||||
$readonlyField = $field->performReadonlyTransformation();
|
||||
preg_match('/Yes/', $field->Field(), $matches);
|
||||
$this->assertEquals($matches[0], 'Yes');
|
||||
}
|
||||
}
|
||||
|
@ -677,6 +677,45 @@ class UploadFieldTest extends FunctionalTest {
|
||||
$this->assertNotContains($fileSubfolder->ID, $itemIDs, 'Does not contain file in subfolder');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that UploadField::fileexist works
|
||||
*/
|
||||
public function testFileExists() {
|
||||
$this->loginWithPermission('ADMIN');
|
||||
|
||||
// Check that fileexist works on subfolders
|
||||
$nonFile = uniqid().'.txt';
|
||||
$responseEmpty = $this->mockFileExists('NoRelationField', $nonFile);
|
||||
$responseEmptyData = json_decode($responseEmpty->getBody());
|
||||
$this->assertFalse($responseEmpty->isError());
|
||||
$this->assertFalse($responseEmptyData->exists);
|
||||
|
||||
// Check that filexists works on root folder
|
||||
$responseRoot = $this->mockFileExists('RootFolderTest', $nonFile);
|
||||
$responseRootData = json_decode($responseRoot->getBody());
|
||||
$this->assertFalse($responseRoot->isError());
|
||||
$this->assertFalse($responseRootData->exists);
|
||||
|
||||
// Check that uploaded files can be detected in the root
|
||||
$tmpFileName = 'testUploadBasic.txt';
|
||||
$response = $this->mockFileUpload('RootFolderTest', $tmpFileName);
|
||||
$this->assertFalse($response->isError());
|
||||
$this->assertFileExists(ASSETS_PATH . "/$tmpFileName");
|
||||
$responseExists = $this->mockFileExists('RootFolderTest', $tmpFileName);
|
||||
$responseExistsData = json_decode($responseExists->getBody());
|
||||
$this->assertFalse($responseExists->isError());
|
||||
$this->assertTrue($responseExistsData->exists);
|
||||
|
||||
// Check that uploaded files can be detected
|
||||
$response = $this->mockFileUpload('NoRelationField', $tmpFileName);
|
||||
$this->assertFalse($response->isError());
|
||||
$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
|
||||
$responseExists = $this->mockFileExists('NoRelationField', $tmpFileName);
|
||||
$responseExistsData = json_decode($responseExists->getBody());
|
||||
$this->assertFalse($responseExists->isError());
|
||||
$this->assertTrue($responseExistsData->exists);
|
||||
}
|
||||
|
||||
protected function getMockForm() {
|
||||
return new Form(new Controller(), 'Form', new FieldList(), new FieldList());
|
||||
}
|
||||
@ -751,6 +790,12 @@ class UploadFieldTest extends FunctionalTest {
|
||||
);
|
||||
}
|
||||
|
||||
protected function mockFileExists($fileField, $fileName) {
|
||||
return $this->get(
|
||||
"UploadFieldTest_Controller/Form/field/{$fileField}/fileexists?filename=".urlencode($fileName)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a physical file deletion
|
||||
*
|
||||
@ -807,7 +852,14 @@ class UploadFieldTest extends FunctionalTest {
|
||||
}
|
||||
|
||||
// Remove left over folders and any files that may exist
|
||||
if(file_exists('../assets/UploadFieldTest')) Filesystem::removeFolder('../assets/UploadFieldTest');
|
||||
if(file_exists(ASSETS_PATH.'/UploadFieldTest')) {
|
||||
Filesystem::removeFolder(ASSETS_PATH.'/UploadFieldTest');
|
||||
}
|
||||
|
||||
// Remove file uploaded to root folder
|
||||
if(file_exists(ASSETS_PATH.'/testUploadBasic.txt')) {
|
||||
unlink(ASSETS_PATH.'/testUploadBasic.txt');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -894,6 +946,9 @@ class UploadFieldTestForm extends Form implements TestOnly {
|
||||
$controller = new UploadFieldTest_Controller();
|
||||
}
|
||||
|
||||
$fieldRootFolder = UploadField::create('RootFolderTest')
|
||||
->setFolderName('/');
|
||||
|
||||
$fieldNoRelation = UploadField::create('NoRelationField')
|
||||
->setFolderName('UploadFieldTest');
|
||||
|
||||
@ -949,6 +1004,7 @@ class UploadFieldTestForm extends Form implements TestOnly {
|
||||
$fieldAllowedExtensions->getValidator()->setAllowedExtensions(array('txt'));
|
||||
|
||||
$fields = new FieldList(
|
||||
$fieldRootFolder,
|
||||
$fieldNoRelation,
|
||||
$fieldHasOne,
|
||||
$fieldHasOneMaxOne,
|
||||
|
@ -124,6 +124,57 @@ class DataObjectSchemaGenerationTest extends SapphireTest {
|
||||
// Restore old indexes
|
||||
Config::unnest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the generation of the ClassName spec and ensure it's not unnecessarily influenced
|
||||
* by the order of classnames of existing records
|
||||
*/
|
||||
public function testClassNameSpecGeneration() {
|
||||
|
||||
// Test with blank entries
|
||||
DataObject::clear_classname_spec_cache();
|
||||
$fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
|
||||
$this->assertEquals(
|
||||
"Enum('DataObjectSchemaGenerationTest_DO, DataObjectSchemaGenerationTest_IndexDO')",
|
||||
$fields['ClassName']
|
||||
);
|
||||
|
||||
// Test with instance of subclass
|
||||
$item1 = new DataObjectSchemaGenerationTest_IndexDO();
|
||||
$item1->write();
|
||||
DataObject::clear_classname_spec_cache();
|
||||
$fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
|
||||
$this->assertEquals(
|
||||
"Enum('DataObjectSchemaGenerationTest_DO, DataObjectSchemaGenerationTest_IndexDO')",
|
||||
$fields['ClassName']
|
||||
);
|
||||
$item1->delete();
|
||||
|
||||
// Test with instance of main class
|
||||
$item2 = new DataObjectSchemaGenerationTest_DO();
|
||||
$item2->write();
|
||||
DataObject::clear_classname_spec_cache();
|
||||
$fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
|
||||
$this->assertEquals(
|
||||
"Enum('DataObjectSchemaGenerationTest_DO, DataObjectSchemaGenerationTest_IndexDO')",
|
||||
$fields['ClassName']
|
||||
);
|
||||
$item2->delete();
|
||||
|
||||
// Test with instances of both classes
|
||||
$item1 = new DataObjectSchemaGenerationTest_IndexDO();
|
||||
$item1->write();
|
||||
$item2 = new DataObjectSchemaGenerationTest_DO();
|
||||
$item2->write();
|
||||
DataObject::clear_classname_spec_cache();
|
||||
$fields = DataObject::database_fields('DataObjectSchemaGenerationTest_DO');
|
||||
$this->assertEquals(
|
||||
"Enum('DataObjectSchemaGenerationTest_DO, DataObjectSchemaGenerationTest_IndexDO')",
|
||||
$fields['ClassName']
|
||||
);
|
||||
$item1->delete();
|
||||
$item2->delete();
|
||||
}
|
||||
}
|
||||
|
||||
class DataObjectSchemaGenerationTest_DO extends DataObject implements TestOnly {
|
||||
|
@ -49,4 +49,78 @@ class GroupedListTest extends SapphireTest {
|
||||
$this->assertEquals('CCC', $last->Name);
|
||||
}
|
||||
|
||||
public function testGroupedByChildren(){
|
||||
$list = GroupedList::create(
|
||||
ArrayList::create(
|
||||
array(
|
||||
ArrayData::create(array(
|
||||
'Name' => 'AAA',
|
||||
'Number' => '111',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'BBB',
|
||||
'Number' => '111',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'AAA',
|
||||
'Number' => '222',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'BBB',
|
||||
'Number' => '111',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'AAA',
|
||||
'Number' => '111',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'AAA',
|
||||
'Number' => '333',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'BBB',
|
||||
'Number' => '222',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'BBB',
|
||||
'Number' => '333',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'AAA',
|
||||
'Number' => '111',
|
||||
)),
|
||||
ArrayData::create(array(
|
||||
'Name' => 'AAA',
|
||||
'Number' => '333',
|
||||
))
|
||||
)
|
||||
)
|
||||
);
|
||||
$grouped = $list->GroupedBy('Name');
|
||||
|
||||
foreach($grouped as $group){
|
||||
$children = $group->Children;
|
||||
$childGroups = $children->GroupedBy('Number');
|
||||
|
||||
$this->assertEquals(3, count($childGroups));
|
||||
|
||||
$first = $childGroups->first();
|
||||
$last = $childGroups->last();
|
||||
|
||||
if($group->Name == 'AAA'){
|
||||
$this->assertEquals(3, count($first->Children));
|
||||
$this->assertEquals('111', $first->Number);
|
||||
$this->assertEquals(2, count($last->Children));
|
||||
$this->assertEquals('333', $last->Number);
|
||||
}
|
||||
|
||||
if($group->Name == 'BBB'){
|
||||
$this->assertEquals(2, count($first->Children));
|
||||
$this->assertEquals('111', $first->Number);
|
||||
$this->assertEquals(1, count($last->Children));
|
||||
$this->assertEquals('333', $last->Number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -550,6 +550,47 @@ class VersionedTest extends SapphireTest {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that publishing processes respects lazy loaded fields
|
||||
*/
|
||||
public function testLazyLoadFields() {
|
||||
$originalMode = Versioned::get_reading_mode();
|
||||
|
||||
// Generate staging record and retrieve it from stage in live mode
|
||||
Versioned::reading_stage('Stage');
|
||||
$obj = new VersionedTest_Subclass();
|
||||
$obj->Name = 'bob';
|
||||
$obj->ExtraField = 'Field Value';
|
||||
$obj->write();
|
||||
$objID = $obj->ID;
|
||||
$filter = sprintf('"VersionedTest_DataObject"."ID" = \'%d\'', Convert::raw2sql($objID));
|
||||
Versioned::reading_stage('Live');
|
||||
|
||||
// Check fields are unloaded prior to access
|
||||
$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
|
||||
$lazyFields = $objLazy->getQueriedDatabaseFields();
|
||||
$this->assertTrue(isset($lazyFields['ExtraField_Lazy']));
|
||||
$this->assertEquals('VersionedTest_Subclass', $lazyFields['ExtraField_Lazy']);
|
||||
|
||||
// Check lazy loading works when viewing a Stage object in Live mode
|
||||
$this->assertEquals('Field Value', $objLazy->ExtraField);
|
||||
|
||||
// Test that writeToStage respects lazy loaded fields
|
||||
$objLazy = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', $filter, false);
|
||||
$objLazy->writeToStage('Live');
|
||||
$objLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', $filter, false);
|
||||
$liveLazyFields = $objLive->getQueriedDatabaseFields();
|
||||
|
||||
// Check fields are unloaded prior to access
|
||||
$this->assertTrue(isset($liveLazyFields['ExtraField_Lazy']));
|
||||
$this->assertEquals('VersionedTest_Subclass', $liveLazyFields['ExtraField_Lazy']);
|
||||
|
||||
// Check that live record has original value
|
||||
$this->assertEquals('Field Value', $objLive->ExtraField);
|
||||
|
||||
Versioned::set_reading_mode($originalMode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user