Adding Recent post in silverstripe blog - silverstripe

I am working with silverstripe with first time and I almost create a blog in silverstripe but now I stuck at one place where I need help you guys. If anybody have any idea about it then please help me.
I am trying to add recent posts in my blog. I am using the below code for this
public function latestBlog($num=10) {
/* return BlogEntry::get()->sort('PublishDate','desc')->limit($num); */
echo $blogPosts;
return $blogPosts = BlogPost::get()->filter('ParentID', $this->ID)->limit($num);
}
And in my ss page I am using html like this
<% loop $latestBlog %>
<li>$Title</li>
<% end_loop %>
this gives me titles of the each post but in href I want url too
If my title is "TEST POST" then I want href like "www.mydomain.com/TEST-POST";
Can Anybody have Idea about it?

You can use $Link which will return the relative url. Reference https://docs.silverstripe.org/en/3.2/developer_guides/templates/common_variables/#links
<ul>
<% loop $latestBlog %>
<li>$Title</li>
<% end_loop %>
</ul>

Related

Two separate menus in silverstripe

I recently started to use SilverStripe. RIght now i made a footer menu for my page where already have a left menu. Now i want to make that i can make pages in admin panel for foot and left menu. So my idea was to made a check box in admin panel, if you make a new page and check the checkbox the page go in foot menu, if leave empty then in left menu.
I add this code to my page.php sidetree
static $db = array( 'menuLocationHorizontal' => "Boolean" );
And this in getCMSFields in page.php
$fields->addFieldToTab('Root.Behaviour', new
CheckboxField('menuLocationHorizontal',"Show up in horizontal
menu?"),"ShowInSearch");
This is how looks my footnav template
<ul>
<% control Menu(2) %>
<% if menuLocationHorizontal == 1 %>
<li><span>$MenuTitle.XML</span></li>
<% end_if %>
<% end_control %>
</ul>
After i done this i just add an if statment in my foot menu and thats it. But it crashes the site. Version i use is 3.1. I guess its php foult.
Sorry guys for my English, it is not my native.
control entries are not supported on SS 3.1, what you're looking for is a <% loop %>, like so:
<ul>
<% loop Menu(2) %>
<% if menuLocationHorizontal == 1 %>
<li><span>$MenuTitle.XML</span></li>
<% end_if %>
<% end_loop %>
</ul>
More information available on Silverstripe docs website.
For me the answer was this -> https://github.com/i-lateral/silverstripe-custommenus
The only thing that actually worked.

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>

Partial Cache Members

I am using the DataObjectsAsPage module. It returns a Datalist ($Items) to the holder page which loops through each $Item. I am also trying to develop a partial caching strategy for the page. I read in the docs that you cannot place cache blocks inside of a loop, so in my DataObjectsAsPageHolder Page, I have the following:
<% cached 'items', LastEdited, CacheSegment %>
<% loop $Items %>
$Me
<% end_loop %>
<% end_cached %>
I checked the silverstripe-cache/cache directory and this seems to be caching the $Items list.
The problem is that I have added a DataExtension to each $Item that allows the admin to set whether or not an $Item is viewable based on the CurrentMember's group. So within each $Me template I have the following:
<% if HasAccess %>
<% end_if %>
I have two issues:
Given the cache key above, if an authorized member is the first to view a page, then the page gets cached and exclusive material gets shown to non-members in subsequent page views.
If I adjust the cache key to the following:
<% cached 'items', Items.max(Created), CacheSegment unless CurrentMember %>
<% loop $Items %>
$Me
<% end_loop %>
<% end_cached %>
Then the content in each $Me template is never cached for members - which is the largest portion of my sites viewers.
Is there a way I can cache the $Items list for members and non-members and still be able to use the HasAccess check on $Item within the loop?
The simplest solution is probably to add the current member's ID to the cache key.
<% cached 'items', LastEdited, CacheSegment, CurrentMember.ID %>
<% loop $Items %>
$Me
<% end_loop %>
<% end_cached %>
However, this will cache the block uniquely for each registered member. To make the caching a little more useful, you should use the current member's groups in the cache key. Unfortunately, there's no easy way that I know of to get a template cache key ready list of groups for a member.
The easiest way to get a round this issue is probably to add a GroupsCacheKey function to your Page class. It's a bit of a dirty solution, but it should work effectively.
// Untested function
public function GroupsCacheKey() {
if ($member = Member::currentUser()) {
return implode('', $member->Groups()->map('ID', 'ID')->toArray());
}
return 'nonmember';
}
Then in your template:
<% cached 'items', LastEdited, CacheSegment, GroupsCacheKey %>
<% loop $Items %>
$Me
<% end_loop %>
<% end_cached %>
There is a better solution than this out there somewhere, but it will work.

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