I have a $has_many relation on my DashboardPage to my PastEvent dataobject. In my DashboardPage.ss template I want to be able to loop through all the PastEvents. I believe because of the $has_many relationship I should be able to do the following on the DashboardPage.ss template:
DashboardPage.ss
<% loop $PastEvents %>
<div>
<div>$EventName</div>
<div>$ClassType</div>
etc...
</div>
<% end_loop %>
However nothing is showing. What am I missing?
DashboardPage.php
<?php
class DashboardPage extends Page implements TemplateGlobalProvider {
private static $db = array(
'Testing' => 'Varchar(255)'
);
private static $has_many = array(
'PastEvents' => 'PastEvent'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Content');
return $fields;
}
public static function get_template_global_variables() {
return array('DashboardLink' => 'getDashboardLink');
}
public static function getDashboardLink($action='') {
$page = DashboardPage::get()->first();
return $page ? $page->Link($action) : '';
}
}
class DashboardPage_Controller extends Page_Controller {
private static $allowed_actions = array(
'show'
);
public function init() {
parent::init();
Requirements::css('themes/' . SSViewer::current_theme() . '/owl-carousel/owl.carousel.css');
Requirements::css('themes/' . SSViewer::current_theme() . '/owl-carousel/owl.theme.css');
Requirements::css('themes/' . SSViewer::current_theme() . '/owl-carousel/owl.transitions.css');
}
public function show(){
dd('Coming here');
}
}
PastEvent.php
<?php
class PastEvent extends DataObject {
private static $db = array(
'EventName' => 'Varchar(255)',
'ClassType' => 'Varchar(255)',
'Instructor' => 'Varchar(255)',
'EmbedCode' => 'Text',
'EventDate' => 'Date',
'Time' => 'Time'
);
private static $has_one = array(
'BranchLocation' => 'BranchLocation',
'DashboardPage' => 'DashboardPage'
);
private static $summary_fields = array(
'EventName' => 'EventName',
'BranchLocation.Name' => 'Branch Location',
'ClassType' => 'ClassType',
'Instructor' => 'Instructor',
'EventDate' => 'Event Date',
'Time' => 'Time',
);
public function getCMSFields() {
$fields = new FieldList(
TextField::create('EventName'),
TextField::create('ClassType'),
TextField::create('Instructor'),
DropdownField::create(
'BranchLocationID',
'Branch Location',
BranchLocation::get()->map('ID', 'Name')->toArray()
),
TextareaField::create('EmbedCode'),
DateField::create('EventDate')->setConfig('showcalendar', true)->setDescription('Click inside textbox to open calender'),
TimePickerField::create('Time')
);
return $fields;
}
public function Link() {
return $this->DashboardPage()->Link('show/'.$this->ID);
}
}
You need to manage PastEvent on the DashboardPage
public function getCMSFields() {
...
$fields->push(GridField::create('PastEvents', 'Past Events',
$this->PastEvents(),
singleton('PastEvent')->canEdit()
? GridFieldConfig_RecordEditor::create()
: GridFieldConfig_RecordViewer::create()
));
...
}
You can add past events from another place, assuming that you created your page with '/dashboard' url.
$dashboard = SiteTree::get_by_link('dashboard');
$events = $dashboard->PastEvents();
$events->add($this->createNewEvent());
You don't need your show() action to display the DashboardPage (implicit index() action is used by default). Page actions extend page url, so your action will be called when you request /dashboard/show, that seems is your intention.
Improve your template to show that there are no events:
<% if $PastEvents.Count %>
<ul>
<% loop $PastEvents %>
<li>
$EventName
<div>$ClassType</div>
...
</li>
<% end_loop %>
</ul>
<% else %>
<p>There are no past events.</p>
<% end_if %>
Since your page provides proper link to show action
public function Link() {
return $this->DashboardPage()->Link('show/'.$this->ID);
}
Your show action might be like this
public function show($eventID = 0) {
$event = $this->PastEvents()->byID($eventID);
if (!$event) {
$this->httpError(404);
}
// render with templates/Page.ss and templates/Layout/PastEventDetails.ss
return $event->renderWith(array('PastEventDetails', 'Page'));
}
Related
I created a module that will allow certain user groups to create a request in the CMS.Any logged in user can access the module.
When user visit 'RequestModeule' in in CMS section in GRID CMS FIELDS I want to show him the requests he only sent not another customer requests. The system displays the requirements of all users and he see all.
How do I set the CMS FIELDS results to be displayed for the currently logged in user.
This is a ecommerce site and some customers want to return the product, some want to replace the product, some want to complain about the quality. I want to allow customers to submit a request themselves. I started with a simple model, so I will expand it when I solve big problems.
Here is my Model.
class Request extends DataObject
{
private static $db = [
'FullName' => 'Varchar',
'RequestText' => 'Text'
];
private static $has_one = [
'Photo' => Image::class
];
public function canView($member = null)
{
return Permission::check('VIEW_REQUEST', 'any', $member);
}
public function canEdit($member = null)
{
return Permission::check('EDIT_REQUEST', 'any', $member);
}
public function canDelete($member = null)
{
return Permission::check('DELETE_REQUEST', 'any', $member);
}
public function canCreate($member = null, $context = [])
{
return Permission::check('CREATE_REQUEST', 'any', $member);
}
/**
* #return \SilverStripe\Forms\FieldList
*/
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', TextField::create('FullName'));
$fields->addFieldToTab('Root.Main', TextField::create('RequestText'));
$fields->addFieldToTab('Root.Photos', $img = UploadField::create('Photo', 'Main photo'));
$img->setFolderName("products");
$img->setAllowedExtensions(["png", "jpg", "jpeg", "gif", "webp"]);
return $fields;
}
}
Request Controller
class Request_Controller extends ContentController implements PermissionProvider
{
function providePermissions(){
return array(
"CREATE_REQUEST" => "Create REQUEST",
"EDIT_REQUEST" => "Edit REQUEST",
"VIEW_REQUEST" => "View REQUEST",
"DELTE_REQUEST" => "Delete REQUEST",
);
}
First of all, I don't see anything about connecting your Request class with a Member. If you want to only show the things a member has created, you need to somehow store the Members ID or something.
The most obvious way to do this would be a private static $has_one = ['CreatedBy' => Member::class].
(and during creation you'll have to put the member ID into CreatedBy. For example you can do protected function onBeforeWrite() { parent::onBeforeWrite(); $this->CreatedByID = Security::getCurrentUser()->ID; })
As for the GridField:
There are 2 ways to control what is displayed in a GridField:
GridFields 3rd argument is any SS_List instance, so you can filter or customize that any way you want. For example you can use Request::get()->filter('CreatedByID' => 13) to only show the objects created by member ID 13.
You can use canView on each Request object. If it returns false, it will not be shown. So you can do public function canView($member = null) { return $member && $member->ID === $this->CreatedByID && Permission::check('VIEW_REQUEST', 'any', $member); }
I have a dataobject that I use on my IndexPage page which works fine in its footer but it does not render on my other pages in the footer.
This is what I have tried so far in the default CWP PageController page and has made the data disappear from the index page (my IndexPage is extended by the CWP Page template):
```<?php
namespace SilverStripe\IndexPage;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
use SilverStripe\Forms\GridField\GridFieldDeleteAction;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\Assets\Image;
use SilverStripe\Assets\File;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\DropdownField;
use Symbiote\GridFieldExtensions\GridFieldOrderableRows;
class IndexPage extends Page {
private static $description = 'Custom homepage';
private static $icon = 'cwp/cwp:images/icons/sitetree_images/home.png';
private static $has_many = [
'FooterFeedback' => Footer::class,
];
private static $owns = [
'FooterFeedback',
];
private static $table_name = 'IndexPageTB';
$fields->addFieldToTab('Root.FooterFeedback',
$gridfield = GridField::create('FooterFeedback', 'FooterFeedback', $this->FooterFeedback(),
GridFieldConfig_RecordEditor::create()));
$gridConfigE = $gridfield->getConfig();
$gridConfigE->addComponent(new GridFieldOrderableRows('SortOrder'));
$gridConfigE->addComponent(new GridFieldDeleteAction);
return $fields;
}
}```
```<?php
namespace SilverStripe\IndexPage;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBEnum;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
//use SilverStripe\Assets\Image;
//use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Assets\Image;
use SilverStripe\Assets\File;
use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\Forms\DropdownField;
class Footer extends DataObject {
private static $db = [
'Feedback' => 'HTMLText',
'ClientAlias' => 'Varchar(255)',
'SortOrder' => 'Int'
];
private static $has_one = [
'Project' => IndexPage::class,
'Avatar' => Image::class,
];
private static $owns = [
'Avatar',
];
private static $summary_fields = [
'GridThumbnail' => '',
'Feedback' => 'Feedback',
'ClientAlias' => 'Client alias',
];
public function getGridThumbnail() {
if($this->Avatar()->exists()) {
return $this->Avatar()->ScaleWidth(120);
}
return "(no image)";
}
private static $table_name = 'Footer';
public function getCMSFields() {
$fields = FieldList::create(
$uploader = UploadField::create('Avatar'),
TextField::create('ClientAlias', 'Client name or alias')->setMaxLength(300)->setDescription('Max 300 characters'),
HTMLEditorField::create('Feedback', 'Feedback')->setDescription('Client feedback')
);
return $fields;
}
}
<div class="col-md-7">
<h1 class="footerbrand">Client feedback</h1>
<div id="quotes">
<% if $FooterFeedback %>
<% loop $FooterFeedback %>
<div class="textItem">
<div class="avatar">
<img src="$Avatar.URL" alt="avatar">
</div>
$Feedback
<p><b> $ClientAlias </b></p>
</div>
<% end_loop %>
<% end_if %>
</div>
<div class="clearfix">
</div>
</div>
<?php
use CWP\CWP\PageTypes\BasePageController;
class PageController extends BasePageController
{
/*public function FooterFeedback()
{
return Footer::get();
}*/
public function FooterFeedback()
{
$Footer = \SilverStripe\IndexPage\IndexPage::get()->first();
return $Footer;
}
}
I added a few different examples which might help you decide on how you want to setup the relations to the footer.
// ----- Current situation: -----
class IndexPage extends Page
{
// ...
private static $has_many = [
'FooterFeedback' => Footer::class,
];
// ...
}
class Footer extends DataObject
{
// ...
}
// Does your other pages extend IndexPage or Page?
class MyOtherPageWhichExtendsIndexPage extends IndexPage
{
// As this page extends IndexPage you are able to loop $FooterFeedback in the templates for this page.
}
class MyOtherPageWhichExtendsPage extends Page
{
// As this page extends Page you can not loop $FooterFeedback in the templates, as only the IndexPage has the relations to footer feedback.
}
// ----- Alternative 1 solution: -----
class Page extends SiteTree
{
// ... Add your footer relation on the Page model, or on SiteTree directly through an extension.
private static $has_many = [
'FooterFeedback' => Footer::class,
];
// ...
}
class Footer extends DataObject
{
// ...
}
// Now you can extend any Page which has extended root Page class and have access to the footer feedback.
class MyOtherPageWhichExtendsIndexPage extends IndexPage
{
// As this page extends IndexPage which extends Page, you are able to loop $FooterFeedback in the templates for this page.
}
class MyOtherPageWhichExtendsPage extends Page
{
// As this page extends Page, you are able to loop $FooterFeedback in the templates for this page.
}
// ----- Alternative 2 solution: -----
// Footer has no relations to any pages (create a separate model admin for it).
class Footer extends DataObject
{
// ...
private static $has_many = [
'Feedback' => Feedback::class,
];
}
class Feedback extends DataObject
{
private static $db = [
'Feedback' => 'HTMLText',
'ClientAlias' => 'Varchar(255)',
'SortOrder' => 'Int'
];
private static $has_one = [
'Footer' => Footer::class,
'Avatar' => Image::class,
// 'Page' => Page::class, <- If you need to display different feedback for different pages.
];
private static $owns = [
'Avatar',
];
}
// Then in your base page controller you can add a getFooter method which is available to all your controllers which extends PageController.
class PageController extends ContentController
{
public function getFooter()
{
return Footer::get()->first();
}
}
// Page.ss
// <% if $Footer %> <% with $Footer %> <% if $Feedback %> <% loop $Feedback %> $Feedback $ClientAlias ... <% end_.. %>
It's also possible to have a relation between the Footer and SiteConfig through an extension. So you would be able to do $SiteConfig.Footeror $SiteConfig.FooterFeedback in your templates.
Also, I don't see why the feedback needs to be related to the footer model, you could just define the feedback relation on the page model directly:
class Page extends SiteTree
{
private static $has_many = [
'Feedback' => Feedback::class,
];
}
class Feedback extends DataObject
{
private static $has_one = [
'Page' => Page::class,
];
}
// AnyPage.ss <% if $Feedback %> <% loop $feedback %> ...
I am very new in SilverStripe thats why, facing so many problem. I want to retrieve data from table and show them in a page template.I have created a modeladmin and from there i can insert records but i cant figure out how could i retrieve those data and show them in a page template? basically my codes are bellow....
mysite/code/SerialsCollection.php
<?php
class SerialsCollection extends DataObject {
private static $db = array(
'Title' => 'Varchar',
'Author' => 'Varchar',
'Publisher' => 'Varchar',
'PublicationYear' => 'Date',
);
private static $searchable_fields = array(
'Title',
'Author'
);
private static $field_labels = array(
'Title' => 'Title' // renames the column to "Cost"
);
private static $summary_fields = array(
'Title',
'Author',
'Publisher',
'PublicationYear',
);
public function canView($member = null) {
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
}
public function canEdit($member = null) {
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
}
public function canDelete($member = null) {
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
}
public function canCreate($member = null) {
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
}
}
and mysite/code/SerialsCollectionAdmin.php ...
<?php
class SerialsCollectionAdmin extends ModelAdmin {
private static $managed_models = array(
'SerialsCollection'
);
private static $url_segment = 'serials-collection';
private static $menu_title = 'Serials Collection';
public function getList() {
$list = parent::getList();
return $list;
}
now, I am able to insert new records, view all and edit particular record. But what i want to create a page template and show these records on that page. I have tried it this way....
mysite/code/SerialsCollectionPage.php
<?php
Class SerialsCollectionPage extends Page{
}
Class Serials_Collection_Page_Controller extends Page_controller{
public function SerialsCollections()
{
return DataObject::get("SerialsCollection");
}
}
themes/SLIS/templates/SerialsCollectionPage.ss
<% include Header %>
<div id="Content">
<h1>This is SerialsCollection Page</h1>
<% control SerialsCollection %>
<p>Title: $Title</p>
<p>Author: $Author</p>
<p>Publication Year:$PublicationYear</p>
<% end_control %>
</div>
<% include Footer %>
But I got nothing only if i write something into content on the page then the contents are coming.
Any help would be really appreciated.
in the Page_Controller:
if SerialsCollection has only one instance make sure you get that instance alone, and make sure you use a get function:
public function getSerials(){
return SerialsCollection::get()->First();// for one item
return SerialsCollection::get();//for an array (use only one of those)
}
the get() alone will return a collection (ArrayList) which you will have to loop
in the Template
if you used the First(one item):
$Serials.Title
will print the title or if you used the array:
<% loop $Serials %>
$Title
<% end_loop %>
Read Querying Data from http://doc.silverstripe.org/en/developer_guides/model/data_model_and_orm/ .
In your case you would get data from the dataobjects with:
$sc = SerialsCollection::get();
Also you should read this: http://doc.silverstripe.org/en/developer_guides/model/lists/
Multimedia, Gallery and GalleryMultimedia.
Both Multimedia and Gallery have a One-to-Many relationship with GalleryMultimedia which holds multimedia_id, gallery_id and position.
In my MultimediaAdmin, I added list of Galleries as below:
->with('Thematic Galleries')
->add('gallery', 'entity', array(
'class' => 'ACME\MyBundle\Entity\Gallery',
'property' => 'name',
'multiple' => true,
'expanded' => true,
'mapped' => false,))
->end()
Now I am stuck at persisiting the selected Gallery as a GalleryMultimedia object. In my Multimedia Model, I have the function below which I would love to pass the GalleryMultimedia object for persisiting but just can't figure out how.
public function setGalleries($galleries)
{
if (count($galleries) > 0) {
foreach ($galleries as $gallery)
{
$this->addGallery($gallery);
}
}
return $this;
}
Out of desparation, I added the following code in my MultimediaAdmin.php
public function prePersist($multimedia)
{
$this->preUpdate($multimedia);
}
public function preUpdate($multimedia)
{
$multimedia->setFiles($multimedia->getFiles());
$this->saveGalleries($multimedia);
}
public function saveGalleries($multimedia)
{
$galleryies = $this->getForm()->get('gallery')->getData();
$container = $this->getConfigurationPool()->getContainer();
$em = $container->get('doctrine')->getManager();
$existing_arr = array();
$existing = $em->getRepository('ACMEMyBundle:GalleryMultimedia')->findBy(array('multimedia' => $multimedia));
$gals = array();
foreach($existing as $exist)
{
$existing_arr[] = $exist->getGallery()->getId();
}
foreach($galleryies as $gallery)
{
if(in_array($gallery->getId(),$existing_arr))
{
continue;
}
else
{
$gm = new \ACME\MyBundle\Entity\GalleryMultimedia();
$gm->setGallery($gallery);
$gals[] = $gm;
$multimedia->setGalleries($gals);
}
}
}
Can someone please assist my poor soul?
I solved this by making the persisting in postPersist like this
public function postPersist($multimedia)
{
$this->postUpdate($multimedia);
}
public function postUpdate($multimedia)
{
$this->saveGalleries($multimedia);
}
Hope this will help someone out there
My way of doing it with objects:
public function preUpdate($object)
{
$this->updateCountries($object);
}
public function updateCountries(\AppBundle\Entity\Product $object){
$container = $this->getConfigurationPool()->getContainer();
$em = $container->get('doctrine')->getManager();
$form_countries = $this->getForm()->get('Country')->getData();
//form_countries is a Doctrine\Common\Collections\ArrayCollection
$object_countries = $object->getCountries();
//object_countries is a collection of \AppBundle\Entity\CountryProduct
$list = array();
foreach($object_countries as $country){
if(!$form_countries->contains($country->getCountry())){
//remove objects not in the form
$em->remove($country);
} else {
//save the others to not re add them
$list[] = $country->getCountry();
}
}
foreach($form_countries as $country){
if(!in_array($country, $list)){
//add everyone in the form but not in the list
$countryProduct = new \AppBundle\Entity\CountryProduct();
$countryProduct->setProduct($object);
$countryProduct->setCountry($country);
$countryProduct->setValuation(1);
$em->persist($countryProduct);
}
}
$em->flush();
}
How do I add a button to the backend of the CMS that fires an action? I can display the button where I want using:
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldsToTab("Root.ButtonTest", array(
FormAction::create('doAction', 'Action button')
)
);
return $fields;
}
public function doAction()
{
//Do something
}
However the button added does nothing when clicked.
I've seen one example of how to put a button on the main action bar (next to save/publish) but that's not what I'm trying to do.
Looking at the only page of documentation I can find, do I need to do something within:
public function getCMSActions()
{
$actions = parent::getCMSActions();
//Something here?
}
It isn't very clear how to create the action that the button calls.
You'll have to extend/decorate LeftAndMain with your own extension and the action you want to call. Here's an example:
<?php
class MyExtension extends LeftAndMainExtension
{
private static $allowed_actions = array(
'doAction'
);
public function doAction($data, $form){
$className = $this->owner->stat('tree_class');
$SQL_id = Convert::raw2sql($data['ID']);
$record = DataObject::get_by_id($className, $SQL_id);
if(!$record || !$record->ID){
throw new SS_HTTPResponse_Exception(
"Bad record ID #" . (int)$data['ID'], 404);
}
// at this point you have a $record,
// which is your page you can work with!
// this generates a message that will show up in the CMS
$this->owner->response->addHeader(
'X-Status',
rawurlencode('Success message!')
);
return $this->owner->getResponseNegotiator()
->respond($this->owner->request);
}
}
Once you have written an extension like this, you'll have to apply it to LeftAndMain by adding the following to your mysite/_config/config.yml:
LeftAndMain:
extensions:
- MyExtension
That's it. Your doAction button should now actually do something!
Not sure if this is helpful, but here's how you can add action-buttons to a ModelAdmin.
(does reload the page)
...in the admin class:
public function getEditForm($id = null, $fields = null)
{
$form = parent::getEditForm($id, $fields);
$form
->Fields()
->fieldByName($this->sanitiseClassName($this->modelClass))
->getConfig()
->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('MyGridFieldDetailForm_ItemRequest');
return $form;
}
MyGridFieldDetailForm_ItemRequest.php
class MyGridFieldDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
function ItemEditForm()
{
$form = parent::ItemEditForm();
$formActions = $form->Actions();
$button = FormAction::create('myAction');
$button->setTitle('button label');
$button->addExtraClass('ss-ui-action-constructive');
$formActions->push($button);
$form->setActions($formActions);
return $form;
}
public function myAction(){ //do things }
}