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

Conflicts:
	admin/javascript/LeftAndMain.FieldHelp.js
	lang/en.yml
	model/URLSegmentFilter.php
This commit is contained in:
Ingo Schommer 2012-12-21 11:48:42 +01:00
commit 8ec3641e60
14 changed files with 89 additions and 20 deletions

View File

@ -20,7 +20,7 @@
descriptionEl.remove();
}
}
});
});
$(".cms .field.cms-description-tooltip :input").entwine({
onfocusin: function(e) {
@ -28,8 +28,8 @@
},
onfocusout: function(e) {
this.closest('.field').tooltip('close');
}
}
});
});
});
}(jQuery));

View File

@ -683,7 +683,7 @@ jQuery.noConflict();
this._super();
},
onremove: function() {
this.button('destroy');
if(this.data('button')) this.button('destroy');
this._super();
}
});

View File

@ -30,7 +30,7 @@ class SS_LogErrorFileFormatter implements Zend_Log_Formatter_Interface {
$urlSuffix = '';
$relfile = Director::makeRelative($errfile);
if($relfile[0] == '/') $relfile = substr($relfile, 1);
if(strlen($relfile) && $relfile[0] == '/') $relfile = substr($relfile, 1);
if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] && isset($_SERVER['REQUEST_URI'])) {
$urlSuffix = " (http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI])";
}

View File

@ -85,8 +85,15 @@ class RequiredFields extends Validator {
}
if($formField && $error) {
$errorMessage = sprintf(_t('Form.FIELDISREQUIRED', '%s is required'),
strip_tags('"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"'));
$errorMessage = _t(
'Form.FIELDISREQUIRED',
'{name} is required',
array(
'name' => strip_tags(
'"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"'
)
)
);
if($msg = $formField->getCustomValidationMessage()) {
$errorMessage = $msg;

View File

@ -478,10 +478,16 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
// TODO Save this item into the given relationship
$message = sprintf(
_t('GridFieldDetailForm.Saved', 'Saved %s %s'),
$this->record->singular_name(),
'<a href="' . $this->Link('edit') . '">"' . htmlspecialchars($this->record->Title, ENT_QUOTES) . '"</a>'
$link = '<a href="' . $this->Link('edit') . '">"'
. htmlspecialchars($this->record->Title, ENT_QUOTES)
. '"</a>';
$message = _t(
'GridFieldDetailForm.Saved',
'Saved {name} {link}',
array(
'name' => $this->record->singular_name(),
'link' => $link
)
);
$form->sessionMessage($message, 'good');

View File

@ -1536,7 +1536,13 @@ class i18n extends Object implements TemplateGlobalProvider {
// Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order.
// Fail silently in case the translation is outdated
$replaced = @vsprintf($returnValue, array_values($injectionArray));
preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs);
if($returnValueArgs) foreach($returnValueArgs[0] as $i => $returnValueArg) {
if($i >= count($injectionArray)) {
$injectionArray[] = '';
}
}
$replaced = vsprintf($returnValue, array_values($injectionArray));
if($replaced) $returnValue = $replaced;
} else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found,

View File

@ -289,7 +289,7 @@ class i18nTextCollector extends Object {
*
* @todo Why the type juggling for $this->collectFromTemplate()? It always returns an array.
*/
public function collectFromTemplate($content, $fileName, $module) {
public function collectFromTemplate($content, $fileName, $module, &$parsedFiles = array()) {
$entities = array();
// Search for included templates
@ -299,11 +299,12 @@ class i18nTextCollector extends Object {
$includeFileName = "{$includeName}.ss";
$filePath = SSViewer::getTemplateFileByType($includeName, 'Includes');
if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
if($filePath) {
if($filePath && !in_array($filePath, $parsedFiles)) {
$parsedFiles[] = $filePath;
$includeContent = file_get_contents($filePath);
$entities = array_merge(
$entities,
(array)$this->collectFromTemplate($includeContent, $module, $includeFileName)
(array)$this->collectFromTemplate($includeContent, $module, $includeFileName, $parsedFiles)
);
}
// @todo Will get massively confused if you include the includer -> infinite loop

View File

@ -176,7 +176,7 @@ en:
TEXT2: 'password reset link'
TEXT3: for
Form:
FIELDISREQUIRED: '%s is required'
FIELDISREQUIRED: '{name} is required'
SubmitBtnLabel: Go
VALIDATIONCREDITNUMBER: 'Please ensure you have entered the {number} credit card number correctly'
VALIDATIONNOTUNIQUE: 'The value entered is not unique'
@ -215,7 +215,9 @@ en:
DeletePermissionsFailure: 'No delete permissions'
Deleted: 'Deleted %s %s'
Save: Save
Saved: 'Saved %s %s'
Saved: 'Saved {name} {link}'
GridFieldEditButton.ss:
EDIT: Edit
GridFieldItemEditView.ss:
'Go back': 'Go back'
Group:
@ -344,6 +346,7 @@ en:
NEWPASSWORD: 'New Password'
PASSWORD: Password
PLURALNAME: Members
PROFILESAVESUCCESS: 'Successfully saved.'
REMEMBERME: 'Remember me next time?'
SINGULARNAME: Member
SUBJECTPASSWORDCHANGED: 'Your password has been changed'

View File

@ -566,6 +566,7 @@ class SQLQuery {
if($this->orderby) {
$i = 0;
foreach($this->orderby as $clause => $dir) {
// public function calls and multi-word columns like "CASE WHEN ..."
if(strpos($clause, '(') !== false || strpos($clause, " ") !== false ) {
// remove the old orderby

View File

@ -39,7 +39,7 @@ class XMLDataFormatterTest extends SapphireTest {
$this->assertEquals(
'<a href="http://mysite.com">mysite.com</a> is a link in this HTML content.'
. ' <![CDATA[this is some nested CDATA]]>',
(string) $xml->Content
(string)$xml->Content
);
}

View File

@ -300,6 +300,15 @@ class i18nTest extends SapphireTest {
$translated, "Testing sprintf placeholders with named injections"
);
$translated = i18n::_t(
'i18nTestModule.INJECTIONSLEGACY', // has %s placeholders
array("Cat", "meow"/*, "meow" */) // remove third arg
);
$this->assertContains(
"TRANS Hello Cat meow. But it is late, ",
$translated, "Testing sprintf placeholders with unnamed injections and too few args"
);
$translated = i18n::_t(
'i18nTestModule.INJECTIONS', // has {name} placeholders
array("Cat", "meow", "meow")

View File

@ -368,12 +368,38 @@ class SQLQueryTest extends SapphireTest {
}
}
/**
* Test that "_SortColumn0" is added for an aggregate in the ORDER BY
* clause, in combination with a LIMIT and GROUP BY clause.
* For some databases, like MSSQL, this is a complicated scenario
* because a subselect needs to be done to query paginated data.
*/
public function testOrderByContainingAggregateAndLimitOffset() {
$query = new SQLQuery();
$query->setSelect(array('"Name"', '"Meta"'));
$query->setFrom('"SQLQueryTest_DO"');
$query->setOrderBy(array('MAX(Date)'));
$query->setGroupBy(array('"Name"', '"Meta"'));
$query->setLimit('1', '1');
$records = array();
foreach($query->execute() as $record) {
$records[] = $record;
}
$this->assertCount(1, $records);
$this->assertEquals('Object 2', $records[0]['Name']);
$this->assertEquals('2012-05-01 09:00:00', $records['0']['_SortColumn0']);
}
}
class SQLQueryTest_DO extends DataObject implements TestOnly {
static $db = array(
"Name" => "Varchar",
"Meta" => "Varchar",
"Date" => "SS_Datetime"
);
}

View File

@ -2,6 +2,8 @@ SQLQueryTest_DO:
test1:
Name: 'Object 1'
Meta: 'Details 1'
Date: 2012-01-01 10:00:00
test2:
Name: 'Object 2'
Meta: 'Details 2'
Date: 2012-05-01 09:00:00

View File

@ -23,6 +23,14 @@ class URLSegmentFilterTest extends SapphireTest {
);
}
public function testReplacesCommonNonAsciiCharacters() {
$f = new URLSegmentFilter();
$this->assertEquals(
urlencode('aa1-.'),
$f->filter('Aa1~!@#$%^*()_`-=;\':"[]\{}|,./<>?')
);
}
public function testRetainsNonAsciiUrlsWithAllowMultiByteOption() {
$f = new URLSegmentFilter();
$f->setAllowMultibyte(true);