setLeftTitle in Silverstripe 3.1 - silverstripe

I have just jumped up the Silverstripe bandwagon.
I have been trying to get the following effect of static text in front of a text field on the getCMSfields form:
Telephone number: +36 [__________]
where "telephone number:" is obviously the field title (which can be altered via ->setTitle() and +36 is a static prefix prepended to the left of the input field.
I have been trying with ->setLeftTitle('+36') but it does not seem to have any effect.
BTW. ->setRightTitle('something') - which I expected to append a label to the right of input field, works identically to ->setDescription().
I'm confused. So is there any way to achieve what I want?

Use the FieldGroup class
public function getCMSFields($fields) {
$fields = parent::getCMSFields($fields);
$fields->addFieldToTab('Root.Main',
FieldGroup::create(
new HeaderField('+36'),
new TextField('PhoneNumber','')
)->setTitle('Telephone number')
);
return $fields;
}
Personally I'd use two TextFields for cosmetic reasons, and enforce some validation on the TelephonePrefix.

The default template does not render the left title. To do this, you will probably need to create a custom form field template (called FormFieldLeftTitle.ss) or something, and include $LeftTitle in the appropriate spot:
<div id="$Name" class="field<% if $extraClass %> $extraClass<% end_if %>">
<% if $Title %><label class="left" for="$ID">$Title</label><% end_if %>
<div class="middleColumn">
$LeftTitle
$Field
</div>
<% if $RightTitle %><label class="right" for="$ID">$RightTitle</label><% end_if %>
<% if $Message %><span class="message $MessageType">$Message</span><% end_if %>
<% if $Description %><span class="description">$Description</span><% end_if %>
</div>
You can then call $myFormField->setTemplate('FormFieldLeftTitle') to use this template.

Related

Access a controller function when inside a loop (ChildrenOf)

I have a page type (ProducerReport) which acts like a summary page - it gets data from individual pages (Shows) and lists them all on one page with the main info from each article.
Using ChildrenOf() made it really easy and simple, I thought wow SilverStripe will do all the work for me all I have to do is structure and style it!
But then tragedy struck...
In Shows I have a DataObject linked by $has_many which allows users to add key people such as contacts etc for each individual article, this is done via GridField (ShowsContacts).
At first I kind of assumed that simply adding the necessary variables would get the data from ShowContacts - this didn't work.
Then in the view I took a shot in the dark and tried using $ID which actually worked and returned the ID of the post.
So I went ahead I added this into the ProducerReport controller which I hoped would get the job done, allowing me to perform a query to get the relevant contacts and loop it within in the ChildrenOf loop.
However, the controller doesn't do anything while in the loop. The only time it outputs anything is when I put the outside of the loop.
ProducerReport.php
class ProducerReport_Controller extends Page_Controller {
# Get the Show Contacts for the Show, based on ShowsID
public function something($SiteID){
# Needs to be cast to int as param comes in as string
$x = (int)$SiteID;
var_dump(ShowsContact::get()->find('ShowID', $x)->Role);
}
}
ProducerReport.ss
<div class="producer-report">
<% loop ChildrenOf(current-shows).sort('PercentageComplete', 'DESC') %>
<div class="show">
<div class="banner">
<% if $ReportImage %>
$ReportImage
<% else %>
<img src="/assets/_placeholders/producer_report_cover.png" />
<% end_if %>
<h2>$Title <span>($ProjectCode)</span></h2>
</div><!-- . banner -->
<a class="hub-link" target="_blank" href="http://website.com?job=$ProjectCode">Hub</a><!-- . hub-link -->
<div class="stats">
<h3>Show Statistics</h3>
<dl>
<dt>Client</dt>
<% if $Client %>
<dd>$Client</dd>
<% else %>
<dd>None set</dd>
<% end_if %>
</dl>
<dl>
<dt>Percentage Complete</dt>
<% if $PercentageComplete %>
<dd>$PercentageComplete%</dd>
<% else %>
<dd>-</dd>
<% end_if %>
</dl>
</div><!-- . stats -->
</div><!-- . show -->
<!-- Here I need to retrieve info of the contacts belonging to the page -->
<!-- Inside the ChildrenOf loop, this DOESNT output anything -->
$something($ID)
<% end_loop %>
<!-- This outside the loop DOES output a job role -->
$something(84)
</div><!-- . producer report -->
EDIT - Additional code
This here is what the ProducerReport gets data from, all direct data for this Model appears in that ChildrenOf loop; The ShowsContact data isn't accessible and if I try to query using the $something($ID) functionality it doesn't work when used inside the loop.
Shows.php
class Shows extends Page {
private static $db = array(
'ProjectCode' => 'Varchar(4)',
'Client' => 'Varchar(255)',
'PercentageComplete' => 'Int'
);
private static $has_one = array(
'ReportImage' => 'Image'
);
private static $has_many = array(
'ShowsContacts' => 'ShowsContact'
);
public function getCMSFields(){
# GridField / Show Contacts
$conf = GridFieldConfig_RelationEditor::create();
$gridField = new GridField('ShowsContact',
'Show Contact List',
$this->ShowsContacts(), $conf);
$fields->addFieldsToTab('Root.Content.ShowContact', array(
$gridField
));
return $fields;
}
}
class Shows_Controller extends Page_Controller {
# Get key people from ShowsContact class // input via ShowsContact GridField
# THIS HERE is the data that I need displayed on ProducerReport
public function getKeyPeople(){
if($this->ShowsContacts()->exists()){
$result = new ArrayList();
foreach($this->ShowsContacts()->column('MemberID') as $teamMemberID){
$member = Member::get()->byID($teamMemberID);
$result->add(new ArrayData(array(
'PictureURL' => $member->ImageURL,
'Role' => $this->ShowsContacts()->find('MemberID', $teamMemberID)->Role,
'Firstname' => $member->FirstName,
'Surname' => $member->Surname,
'Nickname' => $member->Nickname,
'Email' => $member->Email,
'Ext' => $member->Extension,
'Site' => Site::get()->byID($member->SiteID)->Title
)
));
}
return $result;
}
else
return null;
}
}
My question is - how would I get data from another page type as well as data that's linked to it by relationship if I am unable to do a loop within a loop - or am I doing something wrong?
AHHHH!! omfg!!!! Okay, so after taking many many shots at this issue I found out how to get this working.
The solution to this is to use $Top.[Method] inside the loop. I assume its because SilverStripe is doing it's own thing within the loop and for whatever reason becomes a bit blind. From what I understand - you have to tell the method to go outside the loop in order for it to see / use the Controller
Going from the code above in ProducerReport.ss and trimming it down for relevance to the question...
<% loop ChildrenOf(current-shows).sort('PercentageComplete', 'DESC') %>
...
$Top.something($ID) <!-- works perfectly, ID is passed. Output is as expected!
...
# Or when in a loop it'll be like so...
<% loop $Top.something($ID) %>
# ArrayList / ArrayData output...
<% end_loop %>
<% end_loop %>
I can't believe this took me so long to find a solution!
May anyone who gets stuck on something like this or anything similar find their way to this answer because I was honestly about to give up and rewrite everything for this page type and use queries to get all the data to put in the template...
Edit / Note: I've found that if you have a method called getUserName (for example) you cannot omit the 'get' when using it with $Top ... so in the template you need to put $Top.getUserName when using it
Not sure it's relevant if you think you've fixed your problem, but the something controller method (is that even used?) will never dump what you think it will as any DataObject::get() or DataList::create() ORM calls always return an instance of DataList, not an individual DataObject.
If you're trying to see what SS has from that query, try using ->first() instead, which as you might guess, returns the first DataObject (or DataObject subclass) in your list:
...
var_dump(ShowsContact::get()->find('ShowID', $x)->first()->Role);
For your template you could do this:
ProducerReport.ss
<% loop Shows %>
<% loop ShowsContacts %>
<%-- contact details here --%>
<% end_loop %>
<% end_loop %>
Where 'ShowsContacts ' is the name of the relation from shows to contacts

SilverStripe 3 - Extending Comments Form

I'd like to extend AddCommentForm so I can output field labels with custom classes. I want to remove 'left' class from label as it conflicts with Foundation but ideally I want to have full control over outputted HTML.
I tried to extend CommentingController in mysite/code but no luck there...
Can I set it up so every field type has it's own .ss template?
How do I do this?
In SilverStripe you can overwrite templates just by creating a file of the same name in your mysite or theme folder.
so, lets say you have a Form of the class SomeForm all you need to do is create a file called SomeForm.ss and SilverStripe will use that instead of the default Form.ss.
However, the css class 'left' is not added in Form.ss, in that template there is only the form html, the fields are added in a loop.
(Also, the comment module does not use a class for the Form, so that won't work here anyway.)
Each field has its own template (actually 2 templates). MyField.ss and MyField_holder.ss.
If one of the 2 templates does not exist, it will fall back to the default files FormField.ss and FormField_holder.ss.
so lets take for example the class TextField, it will use:
FormField_holder.ss because there is no TextField_holder.ss
TextField.ss
the <label> you seek is inside FormField_holder.ss.
You could of course just create a FormField_holder.ss and copy the Content from the original file, but this would also effect the CMS. So you need some way to only effect the frontend.
Unfortunately, I don't really have nice and clean a solution for this problem.
Usually I would recommend sub classing that form and overwriting the template for the Fields in a loop.
But I think you are using this module here: https://github.com/silverstripe/silverstripe-comments/ which does no use a class for that form, so we have to use a work around and hook into that controller.
File MyCommentControllerExntesion:
class MyCommentControllerExntesion extends Extension {
public function alterCommentForm($form) {
foreach($form->Fields() as $field) {
if (!$field->is_a('HiddenField') {
// skip hidden fields
$field->setFieldHolderTemplate('MyFrontEndField_holder');
}
}
}
}
File config.yml:
CommentingController:
extensions:
- 'MyCommentControllerExntesion'
File MyFrontEndField_holder.ss:
<div id="$Name" class="field<% if $extraClass %> $extraClass<% end_if %>">
<% if $Title %><label class="not-left" for="$ID">$Title</label><% end_if %>
<div class="middleColumn">
$Field
</div>
<% if $RightTitle %><label class="right" for="$ID">$RightTitle</label><% end_if %>
<% if $Message %><span class="message $MessageType">$Message</span><% end_if %>
<% if $Description %><span class="description">$Description</span><% end_if %>
</div>

Creating article pagination

Hi I'm using silverstripe 2.4.7 and I'm having difficulty getting the pagination to work. I've created a function in my page.php to get the latest articles like so
function AllNewsPosts($num=1) {
$news = DataObject::get_one("NewsHolder");
return ($news) ? DataObject::get("NewsEntry", "ParentID > 0", "Date DESC", "", $num) : false;
}
Then when i put this function into the control and pagination tags one article shows up however the links to the concurrent articles do not work - essentially the pagination is not working and I'm not sure how to fix it
<% if AllNewsPosts %>
<% control AllNewsPosts %>
<div class="event">
<h2>$MenuTitle |<span class="date"> $Date.Time $Date.Long</span></h2>
<p>$Content.FirstParagraph</p>
See more about this event
</div>
<% end_control %>
<% else %>
<div class="no-entry">'There are no entries'</div>
<% end_if %>
<% if AllNewsPosts.MoreThanOnePage %>
<div id="PageNumbers">
<p>
<% if AllNewsPosts.NotFirstPage %>
<a class="prev" href="$AllNewsPosts.PrevLink" title="View the previous page"><span class="yellow-background">Prev</span></a>
<% end_if %>
<span>
<% control AllNewsPosts.PaginationSummary(0) %>
<% if CurrentBool %>
<span class="current">$PageNum</span>
<% else %>
<% if Link %>
$PageNum
<% else %>
…
<% end_if %>
<% end_if %>
<% end_control %>
</span>
<% if AllNewsPosts.NotLastPage %>
<a class="next" href="$AllNewsPosts.NextLink" title="View the next page"><span class="yellow-background">Next</span></a>
<% end_if %>
</p>
</div>
<% end_if %>
Any help is much appreciated
Note: The following answer is for Silverstripe 2.4. This should not be used for Silverstripe 3.0+ sites. From 3.0 and onwards the PaginatedList object makes pagination much easier.
You are not setting a limit on how many entries to retrieve in your query, or where to start from.
The following tutorial explains how to apply pagination to a set of data objects exactly as you are trying to do:
http://www.ssbits.com/tutorials/2010/paginating-a-filtered-dataobjectset/
Here is an attempt at altering your function to include limit and start as needed for pagination:
PHP
function AllNewsPosts() {
if(!isset($_GET['start']) || !is_numeric($_GET['start']) || (int)$_GET['start'] < 1)
{
$_GET['start'] = 0;
}
$SQL_start = (int)$_GET['start'];
$newsEntries = DataObject::get('NewsEntry', '', 'Date DESC');
$doSet = new DataObjectSet();
foreach ($newsEntries as $newsEntry) {
if ($newsEntry->canView()) {
$doSet->push($newsEntry);
}
}
$doSet->setPageLimits($SQL_start, 10, $doSet->Count());
return $doSet;
}
Note the above will display 10 items per page. You can change this to however you need per page.

Silverstripe: Excluding current page from list of the parent's children

Using Silverstripe's "ChildrenOf" syntax, I've been successfully able to list all children of a page's parent. It's being used in a "see also" style list on a page.
I'd like to exclude the current page from the list but unsure how to determine which is the same as the current page, as within the control loop I'm in the parent's scope. Any ideas? Here's a pseudocode of what I'm doing:
<% control ChildrenOf(page-url) %>
<!-- Output some stuff, like the page's $Link and $Title -->
<% end_control %>
there's a built-in page control for this, so to exclude the current page from your list:
<% control ChildrenOf(page-url) %>
<% if LinkOrCurrent = current %>
<!-- exclude me -->
<% else %>
<!-- Output some stuff, like the page's $Link and $Title -->
<% end_if %>
<% end_control %>
see http://doc.silverstripe.org/sapphire/en/reference/built-in-page-controls#linkingmode-linkorcurrent-and-linkorsection
UPDATE
as you mentioned in your comment below that you'd like to use the $Pos control, you need to filter the dataobjectset before iterating over it.
add the following to your Page_Controller class:
function FilteredChildrenOf($pageUrl) {
$children = $this->ChildrenOf($pageUrl);
if($children) {
$filteredChildren = new DataObjectSet();
foreach($children as $child) {
if(!$child->isCurrent()) $filteredChildren->push($child);
}
return $filteredChildren;
}
}
then replace 'ChildrenOf' in your template by 'FilteredChildrenOf':
<% control FilteredChildrenOf(page-url) %>
//use $Pos here
<% end_control
In Silverstripe 3.1 you can use a method like this -
<% loop $Parent.Children %>
<% if $LinkingMode != current %>
<!-- Output some stuff, like the page's $Link and $Title , $Pos etc -->
<% end_if %>
<% end_loop %>
This way you can list all parent's children pages.
See https://docs.silverstripe.org/en/3.1/developer_guides/templates/common_variables/

silverstripe function to return menu level

I'm trying to write a function that returns what menu levels are visible on the page...at the moment I'm using <% if %> statements in the template, ie:
<div class="<% if Menu(1) %>navA<% end_if %> <% if Menu(2) %>navB<% end_if %> <% if Menu(3) %>navC<% end_if %>">...</div>
Which, if there are 3 menu levels on a page, returns <div class="navA navB navC">
What I want is a function that returns just the lowest level menu on the current page, ie <div class="navC">
Thanks
that's perfectly possible.
just add the following to your Page_Controller class:
function LowestLevel() {
$i = 1;
while($this->getMenu($i)->count() > 0) $i++;
return 'level'.($i-1);
}
now you can call it in your template like so:
<div>lowest level: $LowestLevel</div>
$LowestLevel will print 'level1', 'level2' etc.
in case your class names have to be like 'navA', 'navB'... you need to do some matching like 'level1'->'navA', which shouldn't be too hard - come back to me if you need any help on this.
What about the following (untested):
<div class="<% if Menu(3) %>navC<% else_if Menu(2) %>navB<% else %>navA<% end_if %>">...</div>
You might want to consider using some custom code in the Controller for logic-heavy stuff, but this should get you going...

Resources