mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge remote-tracking branch 'origin/3.0' into 3.1
Conflicts: dev/CsvBulkLoader.php
This commit is contained in:
commit
3b02d22989
@ -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;
|
||||
|
@ -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'])) {
|
||||
|
@ -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() {
|
||||
|
5
docs/en/changelogs/3.0.6.md
Normal file
5
docs/en/changelogs/3.0.6.md
Normal 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.
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 %>
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
||||
…
|
||||
@ -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>
|
||||
|
@ -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 %>
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -378,7 +378,7 @@ Template:
|
||||
|
||||
:::ss
|
||||
<ul>
|
||||
<% loop Results %>
|
||||
<% loop $Results %>
|
||||
<li id="Result-$ID">$Title</li>
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
|
@ -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> »<% end_if %>
|
||||
<% if $Pages %>
|
||||
<% loop $Pages %>
|
||||
<% if $Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> »<% 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
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
||||
|
@ -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">→</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">→</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">→</span>
|
||||
|
@ -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 "{$Title}"">$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 "{$Title}"">$Title</a></h2>
|
||||
$Photo.SetWidth(150)
|
||||
|
@ -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%"> </div>
|
||||
|
@ -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 "{$Query}"</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>
|
||||
|
@ -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
|
||||
|
@ -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 |
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
MoneyTest_DataObject:
|
||||
test1:
|
||||
MyMoneyCurrency: EUR
|
||||
MyMoneyAmount: 1.23
|
||||
MyMoneyAmount: 1.23
|
||||
MoneyTest_SubClass:
|
||||
test2:
|
||||
MyOtherMoneyCurrency: GBP
|
||||
MyOtherMoneyAmount: 2.46
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user