Merge branch '3.0'

This commit is contained in:
Sam Minnee 2012-08-23 12:39:41 +12:00
commit 824afffd2e
12 changed files with 78 additions and 82 deletions

View File

@ -285,7 +285,8 @@
}
// Replace inner content
node.addClass(origClasses).html(newNode.html());
var origChildren = node.children('ul').detach();
node.addClass(origClasses).html(newNode.html()).append(origChildren);
if (nextNode && nextNode.length) {
this.jstree('move_node', node, nextNode, 'before');
@ -354,23 +355,25 @@
return;
}
var correctStateFn = function(node) {
self.jstree('deselect_all');
self.jstree('select_node', node);
// Similar to jstree's correct_state, but doesn't remove children
var hasChildren = (node.children('ul').length > 0);
node.toggleClass('jstree-leaf', !hasChildren);
if(!hasChildren) node.removeClass('jstree-closed jstree-open');
};
// Check if node exists, create if necessary
if(node.length) {
self.updateNode(node, nodeData.html, nodeData);
setTimeout(function() {
self.jstree('deselect_all');
self.jstree('select_node', node);
// Manually correct state, which checks for children and
// removes toggle arrow (should really be done by jstree internally)
self.jstree('correct_state', node);
correctStateFn(node) ;
}, 500);
} else {
includesNewNode = true;
self.createNode(nodeData.html, nodeData, function(newNode) {
self.jstree('deselect_all');
self.jstree('select_node', newNode);
// Manually remove toggle node, see above
self.jstree('correct_state', newNode);
correctStateFn(newNode);
});
}
});

View File

@ -1,4 +1,4 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<!doctype html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
@ -244,8 +244,8 @@
<h3 class="sectionHeading">Theme selection <small>Step 4 of 5</small></h3>
<p class="helpText">You can change the theme or <a href="http://silverstripe.org/themes">download</a> another from the SilverStripe website after installation.</p>
<ul id="Themes">
<li><input type="radio" name="template" value="simple" id="Simple" <?php if(!isset($_POST['template']) || $_POST['template'] == 'simple') {?>checked="checked"<?php }?>><label for="Simple"><a href="https://github.com/silverstripe-themes/silverstripe-simple">Simple</a> - our default theme ready to use.</label></li>
<li><input type="radio" name="template" value="tutorial" id="EmptyTemplate" <?php if(isset($_POST['template']) && $_POST['template'] == 'tutorial') {?>checked="checked"<?php }?>><label for="EmptyTemplate">Empty template - ready to begin the <a href="http://doc.silverstripe.org/sapphire/en/tutorials" target="_blank">tutorial</a>.</label></li>
<li><input type="radio" name="template" value="simple" id="Simple" <?php if(!isset($_POST['template']) || $_POST['template'] == 'simple') {?>checked="checked"<?php }?>><label for="Simple"><a href="https://github.com/silverstripe-themes/silverstripe-simple">Simple</a> - our default theme ready to use, or begin the <a href="http://doc.silverstripe.org/sapphire/en/tutorials" target="_blank">tutorial</a>.</label></li>
<li><input type="radio" name="template" value="tutorial" id="EmptyTemplate" <?php if(isset($_POST['template']) && $_POST['template'] == 'tutorial') {?>checked="checked"<?php }?>><label for="EmptyTemplate">Empty template</label></li>
</ul>
<h3 class="sectionHeading" id="install">Confirm Install <small>Step 5 of 5</small></h3>

View File

@ -246,44 +246,6 @@ use case could be when you want to find all the members that does not exist in a
// ... Finding all members that does not belong to $group.
$otherMembers = Member::get()->subtract($group->Members());
### Relation filters
So far we have only filtered a data list by fields on the object that you're requesting. For simple cases, this might
be okay, but often, a data model is made up of a number of related objects. For example, in SilverStripe each member
can be placed in a number of groups, and each group has a number of permissions.
For this, the SilverStripe ORM supports **Relation Filters**. Any ORM request can be filtered by fields on a related
object by specifying the filter key as `<relation-name>.<field-in-related-object>`. You can chain relations together
as many times as is necessary.
For example, this will return all members assigned to a group that has a permission record with the code "ADMIN". In
other words, it will return all administrators.
:::php
$members = Member::get()->filter(array(
'Groups.Permissions.Code:ExactMatch' => 'ADMIN',
));
Note that we are just joining these tables to filter the records. Even if a member is in more than 1 administrator
group, unique members will still be returned by this query.
The other features of filters can be applied to relation filters as well. This will return all members in groups whose
names start with 'A' or 'B'.
:::php
$members = Member::get()->filter(array(
'Groups.Title:StartsWith' => array('A', 'B'),
));
You can even follow a relation back to the original model class! This will return all members are in at least 1 group
that also has a member called Sam.
:::php
$members = Member::get()->filter(array(
'Groups.Members.FirstName' => 'Sam'
));
### Raw SQL options for advanced users
Occasionally, the system described above won't let you do exactly what you need to do. In these situtations, we have

View File

@ -133,7 +133,11 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
if($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
$gridField->getList()->remove($item);
if($actionName == 'deleterecord') {
$item->delete();
} else {
$gridField->getList()->remove($item);
}
}
}
}

View File

@ -656,6 +656,7 @@ function max($field) {
$fieldExpression = $this->expressionForField($field, $subSelect);
$subSelect->setSelect(array());
$subSelect->selectField($fieldExpression, $field);
$subSelect->setOrderBy(null);
$this->where($this->expressionForField($field, $this).' NOT IN ('.$subSelect->sql().')');
return $this;

View File

@ -575,22 +575,9 @@ class Hierarchy extends DataExtension {
if(!$showAll) $children = $children->where('"ShowInMenus" = 1');
// Query the live site
$children->dataQuery()->setQueryParam('Versioned.mode', 'stage');
$children->dataQuery()->setQueryParam('Versioned.mode', $onlyDeletedFromStage ? 'stage_unique' : 'stage');
$children->dataQuery()->setQueryParam('Versioned.stage', 'Live');
if($onlyDeletedFromStage) {
// Note that this makes a second query, and could be optimised to be a join
$stageChildren = DataObject::get($baseClass)
->where("\"{$baseClass}\".\"ID\" != $id");
$stageChildren->dataQuery()->setQueryParam('Versioned.mode', 'stage');
$stageChildren->dataQuery()->setQueryParam('Versioned.stage', '');
$ids = $stageChildren->column("ID");
if($ids) {
$children = $children->where("\"$baseClass\".\"ID\" NOT IN (" . implode(',',$ids) . ")");
}
}
return $children;
}

View File

@ -191,8 +191,28 @@ class Versioned extends DataExtension {
}
}
break;
// Reading a specific stage, but only return items that aren't in any other stage
case 'stage_unique':
$stage = $dataQuery->getQueryParam('Versioned.stage');
// Recurse to do the default stage behavior (must be first, we rely on stage renaming happening before below)
$dataQuery->setQueryParam('Versioned.mode', 'stage');
$this->augmentSQL($query, $dataQuery);
// Now exclude any ID from any other stage. Note that we double rename to avoid the regular stage rename
// renaming all subquery references to be Versioned.stage
foreach($this->stages as $excluding) {
if ($excluding == $stage) continue;
$tempName = 'ExclusionarySource_'.$excluding;
$excludingTable = $baseTable . ($excluding && $excluding != $this->defaultStage ? "_$excluding" : '');
$query->addWhere('"'.$baseTable.'"."ID" NOT IN (SELECT ID FROM "'.$tempName.'")');
$query->renameTable($tempName, $excludingTable);
}
break;
// Return all version instances
case 'all_versions':
case 'latest_versions':
@ -208,6 +228,7 @@ class Versioned extends DataExtension {
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name);
}
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID");
$query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version'));
// latest_version has one more step
// Return latest version instances, regardless of whether they are on a particular stage
@ -363,7 +384,7 @@ class Versioned extends DataExtension {
$versionIndexes = array_merge(
array(
'RecordID_Version' => array('type' => 'unique', 'value' => 'RecordID,Version'),
'RecordID_Version' => array('type' => 'unique', 'value' => '"RecordID","Version"'),
'RecordID' => true,
'Version' => true,
),

View File

@ -14,11 +14,12 @@ class GreaterThanFilter extends SearchFilter {
*/
public function apply(DataQuery $query) {
$this->model = $query->applyRelation($this->relation);
return $query->where(sprintf(
"%s > '%s'",
$this->getDbName(),
Convert::raw2sql($this->getDbFormattedValue())
));
$value = $this->getDbFormattedValue();
if(is_numeric($value)) $filter = sprintf("%s > %s", $this->getDbName(), Convert::raw2sql($value));
else $filter = sprintf("%s > '%s'", $this->getDbName(), Convert::raw2sql($value));
return $query->where($filter);
}
public function isEmpty() {

View File

@ -18,8 +18,10 @@ class RequirementsTest extends SapphireTest {
$backend->javascript('http://www.mydomain.com/test.js');
$backend->javascript('https://www.mysecuredomain.com/test.js');
$backend->javascript('//scheme-relative.example.com/test.js');
$backend->css('http://www.mydomain.com/test.css');
$backend->css('https://www.mysecuredomain.com/test.css');
$backend->css('//scheme-relative.example.com/test.css');
$html = $backend->includeInHTML(false, self::$html_template);
@ -31,6 +33,10 @@ class RequirementsTest extends SapphireTest {
(strpos($html, 'https://www.mysecuredomain.com/test.js') !== false),
'Load external secure javascript URL'
);
$this->assertTrue(
(strpos($html, '//scheme-relative.example.com/test.js') !== false),
'Load external scheme-relative javascript URL'
);
$this->assertTrue(
(strpos($html, 'http://www.mydomain.com/test.css') !== false),
'Load external CSS URL'
@ -39,6 +45,10 @@ class RequirementsTest extends SapphireTest {
(strpos($html, 'https://www.mysecuredomain.com/test.css') !== false),
'Load external secure CSS URL'
);
$this->assertTrue(
(strpos($html, '//scheme-relative.example.com/test.css') !== false),
'Load scheme-relative CSS URL'
);
}
protected function setupCombinedRequirements($backend) {

View File

@ -28,8 +28,10 @@ class DataObjectLazyLoadingTest extends SapphireTest {
$expected = 'SELECT DISTINCT "DataObjectTest_Team"."ClassName", "DataObjectTest_Team"."Created", ' .
'"DataObjectTest_Team"."LastEdited", "DataObjectTest_Team"."ID", CASE WHEN '.
'"DataObjectTest_Team"."ClassName" IS NOT NULL THEN "DataObjectTest_Team"."ClassName" ELSE ' .
$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" WHERE ' .
'("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').'))';
$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName", "DataObjectTest_Team"."Title" '.
'FROM "DataObjectTest_Team" ' .
'WHERE ("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').'))' .
' ORDER BY "DataObjectTest_Team"."Title" ASC';
$this->assertEquals($expected, $playerList->sql());
}
@ -43,7 +45,8 @@ class DataObjectLazyLoadingTest extends SapphireTest {
'"DataObjectTest_Team"."ClassName" IS NOT NULL THEN "DataObjectTest_Team"."ClassName" ELSE ' .
$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" LEFT JOIN ' .
'"DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = "DataObjectTest_Team"."ID" WHERE ' .
'("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').'))';
'("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').')) ' .
'ORDER BY "DataObjectTest_Team"."Title" ASC';
$this->assertEquals($expected, $playerList->sql());
}
@ -55,7 +58,8 @@ class DataObjectLazyLoadingTest extends SapphireTest {
'"DataObjectTest_Team"."LastEdited", "DataObjectTest_Team"."Title", "DataObjectTest_Team"."ID", ' .
'CASE WHEN "DataObjectTest_Team"."ClassName" IS NOT NULL THEN "DataObjectTest_Team"."ClassName" ELSE ' .
$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM "DataObjectTest_Team" WHERE ' .
'("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').'))';
'("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').')) ' .
'ORDER BY "DataObjectTest_Team"."Title" ASC';
$this->assertEquals($expected, $playerList->sql());
}
@ -66,9 +70,10 @@ class DataObjectLazyLoadingTest extends SapphireTest {
$expected = 'SELECT DISTINCT "DataObjectTest_Team"."ClassName", "DataObjectTest_Team"."Created", ' .
'"DataObjectTest_Team"."LastEdited", "DataObjectTest_SubTeam"."SubclassDatabaseField", ' .
'"DataObjectTest_Team"."ID", CASE WHEN "DataObjectTest_Team"."ClassName" IS NOT NULL THEN ' .
'"DataObjectTest_Team"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName" FROM ' .
'"DataObjectTest_Team"."ClassName" ELSE '.$db->prepStringForDB('DataObjectTest_Team').' END AS "RecordClassName", "DataObjectTest_Team"."Title" FROM ' .
'"DataObjectTest_Team" LEFT JOIN "DataObjectTest_SubTeam" ON "DataObjectTest_SubTeam"."ID" = ' .
'"DataObjectTest_Team"."ID" WHERE ("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').'))';
'"DataObjectTest_Team"."ID" WHERE ("DataObjectTest_Team"."ClassName" IN ('.$db->prepStringForDB('DataObjectTest_SubTeam').')) ' .
'ORDER BY "DataObjectTest_Team"."Title" ASC';
$this->assertEquals($expected, $playerList->sql());
}

View File

@ -1111,6 +1111,8 @@ class DataObjectTest_Team extends DataObject implements TestOnly {
)
);
static $default_sort = "Title";
function MyTitle() {
return 'Team ' . $this->Title;
}

View File

@ -781,7 +781,7 @@ class Requirements_Backend {
* @return string|boolean
*/
protected function path_for_file($fileOrUrl) {
if(preg_match('/^http[s]?/', $fileOrUrl)) {
if(preg_match('{^//|http[s]?}', $fileOrUrl)) {
return $fileOrUrl;
} elseif(Director::fileExists($fileOrUrl)) {
$prefix = Director::baseURL();