Is there a better way to write the function for my carousel dataobject in my footer - silverstripe-4

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 %> ...

Related

How to add a description to a model managed via ModelAdmin?

I have a model managed via model admin, I want to add a description below the gridfield. Usually this is accomplished by setting ->setDescription('Note in here')
How do you do this when it is managed via the ModelAdmin?
<?php
class FormDropdownModelAdmin extends ModelAdmin {
private static $managed_models = array(
'HearAboutUsItem'
);
private static $url_segment = 'form-dropdown-items';
private static $menu_title = 'Form Dropdown Items';
}
You can overload the getEditForm method on your ModelAdmin and apply a description to the field.
public function getEditForm($id = NULL, $fields = NULL) {
$form = parent::getEditForm($id, $fields);
$form->Fields()->fieldByName('HearAboutUsItem')
->setDescription('This is my description');
return $form;
}

Looping through a dataobject with a $has_many relationship?

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'));
}

Showing records from Database table to a page template SilverStripe

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/

Orchard CMS 1.7 - Extend Media Content Type to get Tag type functionality

Problem:
I have situation where I have to tag the media items with geoIDs. I have been trying to replicate the functionalists of Tags Module and have created the models, Views, Drives and Handler for GeoObject. My problem is that when I load the edit view of an Image, I don't get my GeoObject edit view.
Here's my Handler:
class GeoObjectsPartHandler:ContentHandler {
public GeoObjectsPartHandler(IRepository<GeoObjectsPartRecord> repository, IGeoObjectService geoObjectService)
{
Filters.Add(StorageFilter.For(repository));
OnIndexing<GeoObjectsPart>(
(context, geoObjectsPart) =>
{
foreach (var geoObject in geoObjectsPart.CurrentGeoObjects)
{
context.DocumentIndex.Add("geoObjects", geoObject.GeoObjectName).Analyze();
}
});
}
}
Driver:
[UsedImplicitly]
class GeoObjectsPartDriver: ContentPartDriver<GeoObjectsPart>
{
private static readonly char[] _disalowedChars = new[] { '<', '>', '*', '%', ':', '&', '\\', '"', '|' };
private const string TemplateName = "Parts/GeoObjects";
private readonly INotifier _notifier;
private readonly IGeoObjectService _geoObjectService;
public GeoObjectsPartDriver(IGeoObjectService geoObjectService, INotifier notifier)
{
_geoObjectService = geoObjectService;
_notifier = notifier;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
protected override string Prefix
{
get { return "GeoObjects"; }
}
protected override DriverResult Editor(GeoObjectsPart part, dynamic shapeHelper)
{
return ContentShape("Parts_GeoObjects_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: BuildEditorViewModel(part), Prefix: Prefix));
}
protected override DriverResult Editor(GeoObjectsPart part, IUpdateModel updater, dynamic shapeHelper)
{
var model = new EditGeoObjectsViewModel();
return ContentShape("Parts_GeoObjects_Edit",
() => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
private static EditGeoObjectsViewModel BuildEditorViewModel(GeoObjectsPart part)
{
return new EditGeoObjectsViewModel
{
GeoObjects = string.Join(", ", part.CurrentGeoObjects.Select((t, i) => t.GeoObjectName).ToArray())
};
}
protected override void Importing(GeoObjectsPart part, ImportContentContext context)
{
var geoObjectString = context.Attribute(part.PartDefinition.Name, "GeoObjects");
}
protected override void Exporting(GeoObjectsPart part, ExportContentContext context)
{
context.Element(part.PartDefinition.Name).SetAttributeValue("GeoObjects", String.Join(",", part.CurrentGeoObjects.Select(t => t.GeoObjectName)));
}
}
Migration:
using Orchard.Data.Migration;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
namespace ePageo.TUI.MediaManager
{
public class MediaManagerDataMigration : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("GeoObjectsPartRecord",
table => table
.ContentPartRecord()
);
SchemaBuilder.CreateTable("GeoObjectRecord",
table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<string>("GeoObjectName")
);
SchemaBuilder.CreateTable("ContentGeoObjectRecord",
table => table
.Column<int>("Id", column => column.PrimaryKey().Identity())
.Column<int>("GeoObjectRecord_Id")
.Column<int>("GeoObjectsPartRecord_Id")
);
ContentDefinitionManager.AlterPartDefinition("GeoObjectsPart", builder => builder.Attachable());
return 1;
}
public int UpdateFrom1()
{
ContentDefinitionManager.AlterPartDefinition("GeoObjectsPart", builder => builder
.WithDescription("Allows to add Geo-object ids to the particular media Item."));
return 2;
}
public int UpdateFrom2()
{
ContentDefinitionManager.AlterTypeDefinition("Image", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("Video", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("Audio", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("Document", td => td
.WithPart("GeoObjectsPart")
);
ContentDefinitionManager.AlterTypeDefinition("OEmbed", td => td
.WithPart("GeoObjectsPart")
);
return 3;
}
}
}
Placement.info:
<Placement>
<Place Parts_GeoObjects_Edit="Content:12"/>
</Placement>
I don't think I have problem in my model since its the exact replication of Orchard Tags Models. In fact all of the above files are just that.
I just cannot get the Geo Object edit view to show up in Image (Media) edit view.
I need help!
I got the code to work.
Turns out I had to declare the Driver and Handler classes to public.
I just switched from PHP to ASP.NET few months ago so since if we did not declare any scope PHP would take it as public, I thought it'd be the same with ASP.NET.
The code itself had no other problem except for that.

Symfony2 + Twig: Translate label into a new twig extension

I have implemented a new twig extension and I have some text which had to be translated.
Unfortunately when I use a code label it appears as a sample text.
I mean when twig render this following extension, it displays: 5 entity.years instead of 5 years for example:
class MyExtension extends \Twig_Extension {
public function getFilters()
{
return array(
'myextension' => new \Twig_Filter_Method($this, 'myextension'),
);
}
public function myextension ($myId)
{
// ....
// Some operations concerning $myId...
// ....
if($myId!=0) {
$res = $myId. ' '.'entity.year';
} else {
$res = ($months == 0 ? $days.'entity.days' : $months.'entity.months');
}
return $res;
}
}
Where entity.years, entity.months, entity.days is defined into my translations folder.
Inject the translator service into your extension and use it. For example:
class MyExtension extends \Twig_Extension
{
private $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
// ...
public function myMethod()
{
return $this->translator->trans('my_string');
}
}

Resources