SilverStripe 3 - Extending Comments Form - silverstripe

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>

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

Adding Recent post in silverstripe blog

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>

setLeftTitle in Silverstripe 3.1

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.

Rails - Error CSS styles

New to rails, and am experimenting with changing default layouts.
It seems that the .field_with_errors class is always being added to my forms, when a field causes a validation error. The default scaffold CSS defined field_with_errors as:
.field_with_errors {
padding: 2px;
background-color: red;
display: table;
}
My question is: Why even use this .field_with_errors? Where is it even coming from? Same with a div with ID of notice to print success messages. Where is this coming from?... From my research both of these coming somewhere from ActionView::Helpers.
But what if I wanted to use my own custom styles for these? Do I have to write my own .fields_with_errors and notice classes in my application.css.scss file? I tried this and it works... But why do I have to jail myself to those class names? What if I wanted to call them something else? Can I do this?
Secondly, let's say I have my own custom CSS classes now (assuming it's possible -- which I hope it is)... What if I wanted to apply a bootstrap style to them? For example, bootstrap would use <div class="alert alert-success"> where Rails' scaffold would default to using <div id="#notice">... How can I make such changes in the most elegant way without simply making my own style with the same CSS code as Twitter's alert alert-success.... Isn't there a way in SASS (or through Rails somehow) to say, Success messages are printed with XYZ style and error fields are printed with ABC style... Like maybe in some config file?
Thanks!
Can I do this? yes.
The extra code is being added by ActionView::Base.field_error_proc. If you want to call something else and you're not using field_with_errors to style your form, You should override it in config/application.rb
config.action_view.field_error_proc = Proc.new { |html_tag, instance|
"<div class='your class'>#{html_tag}</div>".html_safe
}
Restart your server
Secondly, If you want to apply a bootstrap style to them, You can save your selection style on application_helper.rb
module ApplicationHelper
def flash_class(level)
case level
when :notice then "alert alert-info"
when :success then "alert alert-success"
when :error then "alert alert-error"
when :alert then "alert alert-error"
end
end
end
create file layouts/_flash_message.html.erb and paste this :
<div>
<% flash.each do |key, value| %>
<div class="<%= flash_class(key) %> fade in">
×
<%= value %>
</div>
<% end %>
</div>
and to call the flash you just render in view
<%= render 'layouts/flash_messages' %>
Example
On accounts_controller.rb create action
def create
#account = Account.new(params[:account])
if #account.save
# using :success if #account.save
flash[:success] = "Success."
redirect_to accounts_url
else
flash[:alert] = "Failed."
render :new
end
end
Put on top of accounts/index.html.erb and on top of form in accounts/_form.html.erb
<%= render 'layouts/flash_messages' %>
Result on index :
<div class="alert alert-success">
<button type="button" class="close" data-dismiss="alert">×</button>
Success.
</div>
Result on form :
<div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">×</button>
Failed.
</div>
#anonymousxxx answer seems to be correct in my opinion.
I would recommend you to use the twitter-bootstrap-rails gem (https://github.com/seyhunak/twitter-bootstrap-rails) for your css. check out the readme on github, this gem is really convenient.

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