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

Conflicts:
	dev/CsvBulkLoader.php
This commit is contained in:
Ingo Schommer 2013-05-09 10:34:20 +02:00
commit 3b02d22989
38 changed files with 243 additions and 132 deletions

View File

@ -97,7 +97,7 @@ class JSONDataFormatter extends DataFormatter {
$innerParts[] = ArrayData::array_to_object(array(
"className" => $relClass,
"href" => "$href.json",
"id" => $obj->$fieldName
"id" => $item->$fieldName
));
}
$serobj->$relName = $innerParts;
@ -118,7 +118,7 @@ class JSONDataFormatter extends DataFormatter {
$innerParts[] = ArrayData::array_to_object(array(
"className" => $relClass,
"href" => "$href.json",
"id" => $obj->$fieldName
"id" => $item->$fieldName
));
}
$serobj->$relName = $innerParts;

View File

@ -94,22 +94,22 @@ class CsvBulkLoader extends BulkLoader {
$obj->{"{$relationName}ID"} = $relationObj->ID;
//write if we are not previewing
if (!$preview) {
$obj->write();
$obj->flushCache(); // avoid relation caching confusion
$obj->write();
$obj->flushCache(); // avoid relation caching confusion
}
} elseif(strpos($fieldName, '.') !== false) {
// we have a relation column with dot notation
list($relationName, $columnName) = explode('.', $fieldName);
list($relationName,$columnName) = explode('.', $fieldName);
// always gives us an component (either empty or existing)
$relationObj = $obj->getComponent($relationName);
if (!$preview) $relationObj->write();
$obj->{"{$relationName}ID"} = $relationObj->ID;
//write if we are not previewing
if (!$preview) {
$obj->write();
$obj->flushCache(); // avoid relation caching confusion
}
$obj->write();
$obj->flushCache(); // avoid relation caching confusion
}
}
}
@ -166,10 +166,12 @@ class CsvBulkLoader extends BulkLoader {
foreach($this->duplicateChecks as $fieldName => $duplicateCheck) {
if(is_string($duplicateCheck)) {
$SQL_fieldName = Convert::raw2sql($duplicateCheck);
if(!isset($record[$SQL_fieldName]) || empty($record[$SQL_fieldName])) { //skip current duplicate check if field value is empty
continue;
if(!isset($record[$SQL_fieldName])) {
return false;
//user_error("CsvBulkLoader:processRecord: Couldn't find duplicate identifier '{$fieldName}'
//in columns", E_USER_ERROR);
}
$SQL_fieldValue = Convert::raw2sql($record[$fieldName]);
$SQL_fieldValue = Convert::raw2sql($record[$SQL_fieldName]);
$existingRecord = DataObject::get_one($this->objectClass, "\"$SQL_fieldName\" = '{$SQL_fieldValue}'");
if($existingRecord) return $existingRecord;
} elseif(is_array($duplicateCheck) && isset($duplicateCheck['callback'])) {

View File

@ -177,15 +177,23 @@ class DevelopmentAdmin extends Controller {
$generator = Injector::inst()->create('RandomGenerator');
$token = $generator->randomToken('sha1');
echo <<<TXT
Token: $token
Please add this to your mysite/_config.php with the following code:
Config::inst()->update('Security', 'token', '$token');
TXT;
$path = $this->request->getVar('path');
if($path) {
if(file_exists(BASE_PATH . '/' . $path)) {
echo sprintf(
"Configuration file '%s' exists, can't merge. Please choose a new file.\n",
BASE_PATH . '/' . $path
);
exit(1);
}
$yml = "Security:\n token: $token";
file_put_contents(BASE_PATH . '/' . $path, $yml);
echo "Configured token in $path\n";
} else {
echo "Generated new token. Please add the following code to your YAML configuration:\n\n";
echo "Security:\n";
echo " token: $token\n";
}
}
public function errors() {

View File

@ -0,0 +1,5 @@
# 3.0.6 (Not yet released)
## Upgrading
* If you have created your own composite database fields, then you shoulcd amend the setValue() to allow the passing of an object (usually DataObject) as well as an array.

View File

@ -126,7 +126,7 @@ and replace it with the following:
:::ss
<ul>
<% loop BookmarkedPages %>
<% loop $BookmarkedPages %>
<li><a href="admin/pages/edit/show/$ID">Edit "$Title"</a></li>
<% end_loop %>
</ul>

View File

@ -74,10 +74,10 @@ In this case, the `getTitleFirstLetter()` method defined earlier is used to brea
:::ss
<%-- Modules list grouped by TitleFirstLetter --%>
<h2>Modules</h2>
<% loop GroupedModules.GroupedBy(TitleFirstLetter) %>
<% loop $GroupedModules.GroupedBy(TitleFirstLetter) %>
<h3>$TitleFirstLetter</h3>
<ul>
<% loop Children %>
<% loop $Children %>
<li>$Title</li>
<% end_loop %>
</ul>
@ -133,10 +133,10 @@ The final step is the render this into the template using the [api:GroupedList->
:::ss
// Modules list grouped by the Month Posted
<h2>Modules</h2>
<% loop GroupedModulesByDate.GroupedBy(MonthCreated) %>
<% loop $GroupedModulesByDate.GroupedBy(MonthCreated) %>
<h3>$MonthCreated</h3>
<ul>
<% loop Children %>
<% loop $Children %>
<li>$Title ($Created.Nice)</li>
<% end_loop %>
</ul>

View File

@ -9,7 +9,7 @@ located in `themes/<mytheme>/templates/Page.ss`.
:::ss
<ul>
<% loop Menu(1) %>
<% loop $Menu(1) %>
<li>
<a href="$Link" title="Go to the $Title page" class="$LinkingMode">
<span>$MenuTitle</span>

View File

@ -36,7 +36,7 @@ The first step is to simply list the objects in the template:
:::ss
<ul>
<% loop PaginatedPages %>
<% loop $PaginatedPages %>
<li><a href="$Link">$Title</a></li>
<% end_loop %>
</ul>
@ -45,22 +45,22 @@ By default this will display 10 pages at a time. The next step is to add paginat
controls below this so the user can switch between pages:
:::ss
<% if PaginatedPages.MoreThanOnePage %>
<% if PaginatedPages.NotFirstPage %>
<% if $PaginatedPages.MoreThanOnePage %>
<% if $PaginatedPages.NotFirstPage %>
<a class="prev" href="$PaginatedPages.PrevLink">Prev</a>
<% end_if %>
<% loop PaginatedPages.Pages %>
<% if CurrentBool %>
<% loop $PaginatedPages.Pages %>
<% if $CurrentBool %>
$PageNum
<% else %>
<% if Link %>
<% if $Link %>
<a href="$Link">$PageNum</a>
<% else %>
...
<% end_if %>
<% end_if %>
<% end_loop %>
<% if PaginatedPages.NotLastPage %>
<% if $PaginatedPages.NotLastPage %>
<a class="next" href="$PaginatedPages.NextLink">Next</a>
<% end_if %>
<% end_if %>

View File

@ -66,7 +66,16 @@ Composer isn't only used to download SilverStripe CMS, it can also be used to ma
composer require silverstripe/forum:*
This command has two parts. First is `silverstripe/forum`. This is the name of the package. You can find other packages with the following command:
This will install the forum module in the latest compatible version.
By default, Composer updates other existing modules (like `framework` and `cms`),
and installs "dev" dependencies like PHPUnit. In case you don't need those dependencies,
use the following command instead:
composer require --no-update silverstripe/forum:*
composer update --no-dev
The `require` command has two parts. First is `silverstripe/forum`. This is the name of the package.
You can find other packages with the following command:
composer search silverstripe
@ -84,7 +93,7 @@ Except for the control code of the Voyager space probe, every piece of code in t
To get the latest updates of the modules in your project, run this command:
composer update
composer update --no-dev
Updates to the required modules will be installed, and the `composer.lock` file will get updated with the specific commits of each of those.

View File

@ -427,7 +427,7 @@ Put code into the classes in the following order (where applicable).
* Commonly used methods like `getCMSFields()`
* Accessor methods (`getMyField()` and `setMyField()`)
* Controller action methods
* Template data-access methods (methods that will be called by a `$MethodName` or `<% loop MethodName %>` construct in a template somewhere)
* Template data-access methods (methods that will be called by a `$MethodName` or `<% loop $MethodName %>` construct in a template somewhere)
* Object methods
### SQL Format

View File

@ -215,7 +215,7 @@ Failing example:
:::ss
<% cached LastEdited %>
<% loop Children %>
<% loop $Children %>
<% cached LastEdited %>
$Name
<% end_cached %>
@ -231,7 +231,7 @@ Can be re-written as:
<% cached LastEdited %>
<% cached Children.max(LastEdited) %>
<% loop Children %>
<% loop $Children %>
$Name
<% end_loop %>
<% end_cached %>

View File

@ -139,9 +139,9 @@ Results.PaginationSummary(4) defines how many pages the search will show in the
:::ss
<% if Results %>
<% if $Results %>
<ul>
<% loop Results %>
<% loop $Results %>
<li>$Title, $Autor</li>
<% end_loop %>
</ul>
@ -149,19 +149,19 @@ Results.PaginationSummary(4) defines how many pages the search will show in the
<p>Sorry, your search query did not return any results.</p>
<% end_if %>
<% if Results.MoreThanOnePage %>
<% if $Results.MoreThanOnePage %>
<div id="PageNumbers">
<p>
<% if Results.NotFirstPage %>
<% if $Results.NotFirstPage %>
<a class="prev" href="$Results.PrevLink" title="View the previous page">Prev</a>
<% end_if %>
<span>
<% loop Results.PaginationSummary(4) %>
<% if CurrentBool %>
<% loop $Results.PaginationSummary(4) %>
<% if $CurrentBool %>
$PageNum
<% else %>
<% if Link %>
<% if $Link %>
<a href="$Link" title="View page number $PageNum">$PageNum</a>
<% else %>
&hellip;
@ -170,7 +170,7 @@ Results.PaginationSummary(4) defines how many pages the search will show in the
<% end_loop %>
</span>
<% if Results.NotLastPage %>
<% if $Results.NotLastPage %>
<a class="next" href="$Results.NextLink" title="View the next page">Next</a>
<% end_if %>
</p>

View File

@ -15,7 +15,7 @@ You can access `[api:SiteConfig]` options from any SS template by using the func
// or
<% loop SiteConfig %>
<% loop $SiteConfig %>
$Title $AnotherField
<% end_loop %>

View File

@ -4,9 +4,9 @@ These are the main changes to the SiverStripe 3 template language.
## Control blocks: Loops vs. Scope
The `<% control var %>...<% end_control %>` in SilverStripe prior to version 3 has two different meanings. Firstly, if the control variable is a collection (e.g. DataList), then `<% control %>` iterates over that set. If it's a non-iteratable object, however, `<% control %>` introduces a new scope, which is used to render the inner template code. This dual-use is confusing to some people, and doesn't allow a collection of objects to be used as a scope.
The `<% control $var %>...<% end_control %>` in SilverStripe prior to version 3 has two different meanings. Firstly, if the control variable is a collection (e.g. DataList), then `<% control %>` iterates over that set. If it's a non-iteratable object, however, `<% control %>` introduces a new scope, which is used to render the inner template code. This dual-use is confusing to some people, and doesn't allow a collection of objects to be used as a scope.
In SilverStripe 3, the first usage (iteration) is replaced by `<% loop var %>`. The second usage (scoping) is replaced by `<% with var %>`
In SilverStripe 3, the first usage (iteration) is replaced by `<% loop $var %>`. The second usage (scoping) is replaced by `<% with $var %>`
## Literals in Expressions

View File

@ -26,9 +26,9 @@ Here is a very simple template:
<p>Welcome $FirstName $Surname.</p>
<% end_with %>
<% if Dishes %>
<% if $Dishes %>
<ul>
<% loop Dishes %>
<% loop $Dishes %>
<li>$Title ($Price.Nice)</li>
<% end_loop %>
</ul>
@ -106,7 +106,7 @@ The "include" tag can be particularly helpful for nested functionality. In this
a variable is true
:::ss
<% if CurrentMember %>
<% if $CurrentMember %>
<% include MembersOnlyInclude %>
<% end_if %>
@ -114,7 +114,7 @@ Includes can't directly access the parent scope of the scope active when the inc
pass arguments to the include, which are available on the scope top within the include
:::ss
<% with CurrentMember %>
<% with $CurrentMember %>
<% include MemberDetails PageTitle=$Top.Title, PageID=$Top.ID %>
<% end_with %>
@ -182,7 +182,7 @@ the markup in the `else` clause is used, if that clause is present.
This example shows the use of `not` to negate the test.
:::ss
<% if not $DinnerInOven %>
<% if $not $DinnerInOven %>
I'm going out for dinner tonight.
<% end_if %>
@ -221,12 +221,18 @@ collection of items. For example:
<% end_loop %>
</ul>
This loops over the children of a page, and generates an unordered list showing
the `Title` property from each one. Note that `$Title` *inside* the loop refers
to the `Title` property on each object that is looped over, not the current page.
To refer to the current page's `Title` property inside the loop, you can do
This loops over the children of a page, and generates an unordered list showing
the `Title` property from each one. Note that `$Title` *inside* the loop refers
to the `Title` property on each object that is looped over, not the current page.
This is know as the `Scope` of the template. For more information about `Scope`
see the section below.
To refer to the current page's `Title` property inside the loop, you can do
`$Up.Title`. More about `Up` later.
`$Me` can be used to refer to the current object context the template is rendered
with.
### Position Indicators
Inside the loop scope, there are many variables at your disposal to determine the
@ -239,6 +245,50 @@ current position in the list and iteration:
* `$Pos`: The current position in the list (integer). Will start at 1.
* `$TotalItems`: Number of items in the list (integer)
### Altering the list
`<% loop %>` statements iterate over a `[api:DataList]` instance. As the
template has access to the list object, templates can call `[api:DataList]`
functions. For instance, see the following examples:
Providing a custom sort.
:::ss
<ul>
<% loop $Children.Sort(Title) %>
<li>$Title</li>
<% end_loop %>
</ul>
Limiting the number of items displayed.
:::ss
<ul>
<% loop $Children.Limit(10) %>
<li>$Title</li>
<% end_loop %>
</ul>
Reversing the loop.
:::ss
<ul>
<% loop $Children.Reverse %>
<li>$Title</li>
<% end_loop %>
</ul>
The `DataList` class also supports chaining methods. For example, to reverse
the list and output the last 3 items we would write:
:::ss
<ul>
<% loop $Children.Reverse.Limit(3) %>
<li>$Title</li>
<% end_loop %>
</ul>
### Modulus and MultipleOf
$Modulus and $MultipleOf can help to build column layouts.
@ -252,7 +302,7 @@ custom column names based on your loop statement. Note that this works for any
control statement (not just children).
:::ss
<% loop Children %>
<% loop $Children %>
<div class="column-{$Modulus(4)}">
...
</div>
@ -265,8 +315,8 @@ You can also use $MultipleOf(value, offset) to help build columned layouts. In
this case we want to add a <br> after every 3th item.
:::ss
<% loop Children %>
<% if MultipleOf(3) %>
<% loop $Children %>
<% if $MultipleOf(3) %>
<br>
<% end_if %>
<% end_loop %>
@ -289,11 +339,11 @@ the scope back to the previous level. Take the following example:
:::ss
$Title
--
<% loop Children %>
<% loop $Children %>
$Title
$Up.Title
--
<% loop Children %>
<% loop $Children %>
$Title
$Up.Title
<% end_loop %>
@ -321,12 +371,12 @@ include `$Top`:
:::ss
$Title
--
<% loop Children %>
<% loop $Children %>
$Title
$Up.Title
$Top.Title
--
<% loop Children %>
<% loop $Children %>
$Title
$Up.Title
$Top.Title
@ -459,11 +509,11 @@ It renders in the template as `<base href="http://www.mydomain.com" /><!--[if lt
Returns the currently logged in member, if there is one.
All of their details or any special Member page controls can be called on this.
Alternately, you can use `<% if CurrentMember %>` to detect whether someone has logged
Alternately, you can use `<% if $CurrentMember %>` to detect whether someone has logged
in.
:::ss
<% if CurrentMember %>
<% if $CurrentMember %>
Welcome Back, $CurrentMember.FirstName
<% end_if %>
@ -498,7 +548,7 @@ Your function could return a single value as above or it could be a subclass of
And now you could call these values by using
:::ss
<% with MyCustomValues %>
<% with $MyCustomValues %>
$Hi , $Name
<% end_with %>
// output "Kia Ora , John Smith"

View File

@ -83,7 +83,7 @@ WARNING: Currently the UploadField doesn't fully support has_many relations, so
### Overview
The field can either be configured on an instance level through `setConfig(<key>, <value>)`,
or globally by overriding the YAML defaults.
or globally by overriding the YAML defaults. See the [Configuration Reference](#configuration-reference) section for possible values.
Example: mysite/_config/uploadfield.yml

View File

@ -79,7 +79,7 @@ In templates, casting helpers are available without the need for an `obj()` call
Example: Flagging an object of type `MyObject` (see above) if it's date is in the past.
:::ss
<% if MyObjectInstance.MyDate.InPast %>Outdated!<% end_if %>
<% if $MyObjectInstance.MyDate.InPast %>Outdated!<% end_if %>
## Casting HTML Text

View File

@ -419,7 +419,7 @@ but using the *obj()*-method or accessing through a template will cast the value
// $myPlayer->MembershipFee() returns a float (e.g. 123.45)
// $myPlayer->obj('MembershipFee') returns a object of type Currency
// In a template: <% loop MyPlayer %>MembershipFee.Nice<% end_loop %> returns a casted string (e.g. "$123.45")
// In a template: <% loop $MyPlayer %>MembershipFee.Nice<% end_loop %> returns a casted string (e.g. "$123.45")
public function getMembershipFee() {
return $this->Team()->BaseFee * $this->MembershipYears;
}

View File

@ -264,7 +264,7 @@ basic customisation:
:::ss
<form $FormAttributes>
<% if Message %>
<% if $Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
@ -284,9 +284,9 @@ basic customisation:
$Fields.dataFieldByName(SecurityID)
</fieldset>
<% if Actions %>
<% if $Actions %>
<div class="Actions">
<% loop Actions %>$Field<% end_loop %>
<% loop $Actions %>$Field<% end_loop %>
</div>
<% end_if %>
</form>
@ -297,7 +297,7 @@ for the type of field. Pass in the name of the field as the first parameter, as
template.
To find more methods, have a look at the `[api:Form]` class and `[api:FieldList]` class as there is a lot of different
methods of customising the form templates. An example is that you could use `<% loop Fields %>` instead of specifying
methods of customising the form templates. An example is that you could use `<% loop $Fields %>` instead of specifying
each field manually, as we've done above.
### Custom form field templates

View File

@ -378,7 +378,7 @@ Template:
:::ss
<ul>
<% loop Results %>
<% loop $Results %>
<li id="Result-$ID">$Title</li>
<% end_loop %>
</ul>

View File

@ -95,15 +95,15 @@ Note that pages with the `ShowInMenus` property set to FALSE will be filtered ou
### Children Loops
:::ss
<% loop Children %>...<% end_loop %>
<% loop $Children %>...<% end_loop %>
Will loop over all children of the current page context.
Helpful to create page-specific subnavigations.
Most likely, you'll want to use `<% loop Menu %>` for your main menus,
Most likely, you'll want to use `<% loop $Menu %>` for your main menus,
since its independent of the page context.
:::ss
<% loop ChildrenOf(<my-page-url>) %>...<% end_loop %>
<% loop $ChildrenOf(<my-page-url>) %>...<% end_loop %>
Will create a list of the children of the given page,
as identified by its `URLSegment` value. This can come in handy because its not dependent
@ -111,7 +111,7 @@ on the context of the current page. For example, it would allow you to list all
underneath a "staff" holder on any page, regardless if its on the top level or elsewhere.
:::ss
<% loop allChildren %>...<% end_loop %>
<% loop $allChildren %>...<% end_loop %>
This will show all children of a page even if the `ShowInMenus` property is set to FALSE.
@ -137,7 +137,7 @@ To simply retrieve the parent page of the current context (if existing), use the
### Access to a specific Page
:::ss
<% loop Page(my-page) %>...<% end_loop %>`
<% with $Page(my-page) %>...<% end_with %>`
"Page" will return a single page from the site tree, looking it up by URL. You can use it in the `<% loop %>` format.
Can't be called using `$Page(my-page).Title`.
@ -180,7 +180,7 @@ More common uses:
Example: Only show the menu item linked if its the current one:
:::ss
<% if LinkOrCurrent = current %>
<% if $LinkOrCurrent = current %>
$Title
<% else %>
<a href="$Link">$Title</a>
@ -199,9 +199,9 @@ Simply place a file with the same name in your `themes/<your-theme>/templates`
folder to customize its output. Here's the default template:
:::ss
<% if Pages %>
<% loop Pages %>
<% if Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> &raquo;<% end_if %>
<% if $Pages %>
<% loop $Pages %>
<% if $Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> &raquo;<% end_if %>
<% end_loop %>
<% end_if %>
@ -282,4 +282,4 @@ we recommend that you dive into the API docs for the following classes:
* `[api:ContentController]`: The main controller responsible for handling pages
* `[api:Controller]`: Generic controller (not specific to pages)
* `[api:DataObject]`: Underlying model class for page objects
* `[api:ViewableData]`: Underlying object class for pretty much anything displayable
* `[api:ViewableData]`: Underlying object class for pretty much anything displayable

View File

@ -253,7 +253,7 @@ of the CMS you have to take care of instanciation yourself:
:::ss
// File: mysite/templates/MyController.ss
$Form
<% with EditorToolbar %>
<% with $EditorToolbar %>
$MediaForm
$LinkForm
<% end_with %>

View File

@ -72,9 +72,9 @@ our theme in action. The code for mine is below.
</div>
<div id="Navigation">
<% if Menu(1) %>
<% if $Menu(1) %>
<ul>
<% loop Menu(1) %>
<% loop $Menu(1) %>
<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
<% end_loop %>
</ul>
@ -182,9 +182,9 @@ Next is a division for the main navigation. This may contain something like:
:::ss
<div id="Navigation">
<% if Menu(1) %>
<% if $Menu(1) %>
<ul>
<% loop Menu(1) %>
<% loop $Menu(1) %>
<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
<% end_loop %>
</ul>
@ -198,8 +198,8 @@ Before stepping into a loop it's good practise to check if it exists first. This
important in manipulating SilverStripe templates, but in any programming language!
:::ss
<% if MyFunction %>
<% loop MyFunction %>
<% if $MyFunction %>
<% loop $MyFunction %>
$Title
<% end_loop %>
<% end_if %>

View File

@ -158,7 +158,7 @@ Open up *themes/simple/templates/Includes/Navigation.ss*
The Menu for our site is created using a **loop**. Loops allow us to iterate over a data set, and render each item using a sub-template.
:::ss
<% loop Menu(1) %>
<% loop $Menu(1) %>
returns a set of first level menu items. We can then use the template variable
*$MenuTitle* to show the title of the page we are linking to, *$Link* for the URL of the page and *$LinkingMode* to help style our menu with CSS (explained in more detail shortly).
@ -168,7 +168,7 @@ returns a set of first level menu items. We can then use the template variable
:::ss
<ul>
<% loop Menu(1) %>
<% loop $Menu(1) %>
<li class="$LinkingMode">
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
</li>
@ -225,7 +225,7 @@ Adding a second level menu is very similar to adding the first level menu. Open
:::ss
<ul>
<% loop Menu(2) %>
<% loop $Menu(2) %>
<li class="$LinkingMode">
<a href="$Link" title="Go to the $Title.XML page">
<span class="arrow">&rarr;</span>
@ -245,10 +245,10 @@ Look again in the *Sidebar.ss* file and you will see that the menu is surrounded
like this:
:::ss
<% if Menu(2) %>
<% if $Menu(2) %>
...
<ul>
<% loop Menu(2) %>
<% loop $Menu(2) %>
<li class="$LinkingMode">
<a href="$Link" title="Go to the $Title.XML page">
<span class="arrow">&rarr;</span>
@ -269,7 +269,7 @@ Now that we have two levels of navigation, it would also be useful to include so
Open up */themes/simple/templates/Includes/BreadCrumbs.ss* template and look at the following code:
:::ss
<% if Level(2) %>
<% if $Level(2) %>
<div id="Breadcrumbs">
$Breadcrumbs
</div>
@ -295,12 +295,12 @@ The following example runs an if statement, and a loop on *Children*, checking t
:::ss
<ul>
<% loop Menu(1) %>
<% loop $Menu(1) %>
<li class="$LinkingMode">
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
<% if Children %>
<% if $Children %>
<ul>
<% loop Children %>
<% loop $Children %>
<li class="$LinkingMode">
<a href="$Link" title="Go to the $Title.XML page">
<span class="arrow">&rarr;</span>

View File

@ -285,7 +285,7 @@ We'll now create a template for the article holder. We want our news section to
$Content
<div class="content">$Content</div>
</article>
<% loop Children %>
<% loop $Children %>
<article>
<h2><a href="$Link" title="Read more on &quot;{$Title}&quot;">$Title</a></h2>
<p>$Content.FirstParagraph</p>
@ -313,7 +313,7 @@ Cut the code between "loop Children" in *ArticleHolder.ss** and replace it with
:::ss
...
<% loop Children %>
<% loop $Children %>
<% include ArticleTeaser %>
<% end_loop %>
...
@ -370,7 +370,7 @@ This function simply runs a database query that gets the latest news articles fr
<!-- ... -->
<div class="content">$Content</div>
</article>
<% loop LatestNews %>
<% loop $LatestNews %>
<% include ArticleTeaser %>
<% end_loop %>
@ -486,7 +486,7 @@ The staff section templates aren't too difficult to create, thanks to the utilit
$Content
<div class="content">$Content</div>
</article>
<% loop Children %>
<% loop $Children %>
<article>
<h2><a href="$Link" title="Read more on &quot;{$Title}&quot;">$Title</a></h2>
$Photo.SetWidth(150)

View File

@ -325,11 +325,11 @@ The final step is to create the template to display our data. Change the 'Browse
:::ss
<div id="BrowserPoll">
<h2>Browser Poll</h2>
<% if BrowserPollForm %>
<% if $BrowserPollForm %>
$BrowserPollForm
<% else %>
<ul>
<% loop BrowserPollResults %>
<% loop $BrowserPollResults %>
<li>
<div class="browser">$Browser: $Percentage%</div>
<div class="bar" style="width:$Percentage%">&nbsp;</div>

View File

@ -33,7 +33,7 @@ To add the search form, we can add `$SearchForm` anywhere in our templates. In t
:::ss
...
<% if SearchForm %>
<% if $SearchForm %>
<span class="search-dropdown-icon">L</span>
<div class="search-bar">
$SearchForm
@ -98,16 +98,16 @@ class.
<div id="Content" class="searchResults">
<h1>$Title</h1>
<% if Query %>
<% if $Query %>
<p class="searchQuery"><strong>You searched for &quot;{$Query}&quot;</strong></p>
<% end_if %>
<% if Results %>
<% if $Results %>
<ul id="SearchResults">
<% loop Results %>
<% loop $Results %>
<li>
<a class="searchResultHeader" href="$Link">
<% if MenuTitle %>
<% if $MenuTitle %>
$MenuTitle
<% else %>
$Title
@ -124,17 +124,17 @@ class.
<p>Sorry, your search query did not return any results.</p>
<% end_if %>
<% if Results.MoreThanOnePage %>
<% if $Results.MoreThanOnePage %>
<div id="PageNumbers">
<% if Results.NotLastPage %>
<% if $Results.NotLastPage %>
<a class="next" href="$Results.NextLink" title="View the next page">Next</a>
<% end_if %>
<% if Results.NotFirstPage %>
<% if $Results.NotFirstPage %>
<a class="prev" href="$Results.PrevLink" title="View the previous page">Prev</a>
<% end_if %>
<span>
<% loop Results.Pages %>
<% if CurrentBool %>
<% loop $Results.Pages %>
<% if $CurrentBool %>
$PageNum
<% else %>
<a href="$Link" title="View page number $PageNum">$PageNum</a>

View File

@ -293,19 +293,19 @@ a named list of object.
</tr>
</thead>
<tbody>
<% loop Children %>
<% loop $Children %>
<tr>
<td>
<a href="$Link">$Title</a>
</td>
<td>
<% loop Students %>
$Name ($University)<% if Last !=1 %>,<% end_if %>
<% loop $Students %>
$Name ($University)<% if $Last !=1 %>,<% end_if %>
<% end_loop %>
</td>
<td>
<% loop Mentor %>
$Name<% if Last !=1 %>,<% end_if %>
<% loop $Mentor %>
$Name<% if $Last !=1 %>,<% end_if %>
<% end_loop %>
</td>
</tr>
@ -343,9 +343,9 @@ we can access the "Students" and "Mentors" relationships directly in the templat
<div class="content">
$Content
<h2>Students</h2>
<% if Students %>
<% if $Students %>
<ul>
<% loop Students %>
<% loop $Students %>
<li>$Name ($University)</li>
<% end_loop %>
</ul>
@ -353,9 +353,9 @@ we can access the "Students" and "Mentors" relationships directly in the templat
<p>No students found</p>
<% end_if %>
<h2>Mentors</h2>
<% if Mentors %>
<% if $Mentors %>
<ul>
<% loop Mentors %>
<% loop $Mentors %>
<li>$Name</li>
<% end_loop %>
</ul>
@ -392,7 +392,7 @@ To use this template, we need to add a new method to our student class:
Replace the student template code in both `Project.ss`
and `ProjectHolder.ss` templates with the new placeholder, `$Info`.
That's the code enclosed in `<% loop Students %>` and `<% end_loop %>`.
That's the code enclosed in `<% loop $Students %>` and `<% end_loop %>`.
With this pattern, you can increase code reuse across templates.
## Summary

View File

@ -936,7 +936,7 @@ class File extends DataObject {
if(!is_array($exts)) $exts = array($exts);
foreach($exts as $ext) {
if(!is_subclass_of($ext, 'File')) {
if(!is_subclass_of($class, 'File')) {
throw new InvalidArgumentException(
sprintf('Class "%s" (for extension "%s") is not a valid subclass of File', $class, $ext)
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 B

After

Width:  |  Height:  |  Size: 372 B

View File

@ -749,6 +749,18 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
return $this->record;
}
/**
* Return all currently fetched database fields.
*
* This function is similar to toMap() but doesn't trigger the lazy-loading of all unfetched fields.
* Obviously, this makes it a lot faster.
*
* @return array The data as a map.
*/
public function getQueriedDatabaseFields() {
return $this->record;
}
/**
* Update a number of fields on this object, given a map of the desired changes.
*

View File

@ -118,7 +118,7 @@ interface CompositeDBField {
* parameter.
*
* @param DBField|array $value
* @param array $record Map of values loaded from the database
* @param DataObject|array $record An array or object that this field is part of
* @param boolean $markChanged Indicate wether this field should be marked changed.
* Set to FALSE if you are initializing this field after construction, rather
* than setting a new value.

View File

@ -103,6 +103,11 @@ class Money extends DBField implements CompositeDBField {
}
public function setValue($value, $record = null, $markChanged = true) {
// Convert an object to an array
if($record && $record instanceof DataObject) {
$record = $record->getQueriedDatabaseFields();
}
// @todo Allow resetting value to NULL through Money $value field
if ($value instanceof Money && $value->exists()) {
$this->setCurrency($value->getCurrency(), $markChanged);

View File

@ -17,6 +17,7 @@ class MoneyTest extends SapphireTest {
protected $extraDataObjects = array(
'MoneyTest_DataObject',
'MoneyTest_SubClass',
);
public function testMoneyFieldsReturnedAsObjects() {
@ -268,6 +269,15 @@ class MoneyTest extends SapphireTest {
))->value()
);
}
public function testMoneyLazyLoading() {
// Get the object, ensuring that MyOtherMoney will be lazy loaded
$id = $this->idFromFixture('MoneyTest_SubClass', 'test2');
$obj = MoneyTest_DataObject::get()->byID($id);
$this->assertEquals('£2.46', $obj->obj('MyOtherMoney')->Nice());
}
}
class MoneyTest_DataObject extends DataObject implements TestOnly {
@ -277,3 +287,9 @@ class MoneyTest_DataObject extends DataObject implements TestOnly {
);
}
class MoneyTest_SubClass extends MoneyTest_DataObject implements TestOnly {
static $db = array(
'MyOtherMoney' => 'Money',
);
}

View File

@ -1,4 +1,8 @@
MoneyTest_DataObject:
test1:
MyMoneyCurrency: EUR
MyMoneyAmount: 1.23
MyMoneyAmount: 1.23
MoneyTest_SubClass:
test2:
MyOtherMoneyCurrency: GBP
MyOtherMoneyAmount: 2.46

View File

@ -371,7 +371,7 @@ class ViewableData extends Object implements IteratorAggregate {
}
$valueObject = Object::create_from_string($castConstructor, $fieldName);
$valueObject->setValue($value, ($this->hasMethod('toMap') ? $this->toMap() : null));
$valueObject->setValue($value, $this);
$value = $valueObject;
}