Silverstripe tumblr-like Post Types - silverstripe

I am trying to create a back-end interface for silverstripe that gives the CMS user the option to choose between a set of Post Types (like tumblr) in Silverstripe3. So they can choose to create a News Post, Video Post, Gallery Post, etc.
I initially started off giving all Posts the necessary fields for each Type and adding an enum field that allowed the user to choose the Post Type. I then used the forTemplate method to set the template dependent upon which Post Type was chosen.
class Post extends DataObject {
static $db = array(
'Title' => 'Varchar(255),
'Entry' => 'HTMLText',
'Type' => 'enum('Video, Photo, Gallery, Music')
);
static $many_many = array(
'Videos' => 'SiteVideo',
'Photos' => 'SitePhoto,
'Songs' => 'SiteMp3'
);
public function forTemplate() {
switch ($this->Type) {
case 'Video':
return $this->renderWith('VideoPost');
break;
case 'Photo':
return $this->renderWith('ImagePost');
break;
etc...
}
function getCMSFields($params=null) {
$fields = parent::getCMSFields($params);
...
$videosField = new GridField(
'Videos',
'Videos',
$this->Videos()->sort('SortOrder'),
$gridFieldConfig
);
$fields->addFieldToTab('Root.Videos', $photosField);
$photosField = new GridField(
'Photos',
'Photos',
$this->Photos()->sort('SortOrder'),
$gridFieldConfig
);
$fields->addFieldToTab('Root.Videos', $photosField);
return $fields;
}
}
I would rather the user be able to choose the Post Type in the backend and only the appropriate tabs show up. So if you choose Video, only the Video GridField tab would show up. If you choose Photo Type only the Photo's GridField would show.Then I would like to be able to call something like
public function PostList() {
Posts::get()
}
and be able to output all PostTypes sorted by date.
Does anyone know how this might be accomplished? Thanks.

Well the first part can be accomplished using javascript. Check out this tutorial and the docs let me know if you have questions on it.
The second part would be trickier but I think you could do something with the page controller. Include a method that outputs a different template based on the enum value but you would have to set links somewhere.
I managed this with DataObjectManager in 2.4.7 as I had numerous DataObjects and all were included in one page but I'm not sure if that is feasible in SS3.
return $this->renderWith(array('CustomTemplate'));
This line of code will output the page using a different template. You need to include it in a method and then call that method when the appropriate link is clicked.

Related

Silverstripe 4 save variable in database

Is posible to save SS template variable in database from CMS and after execute it in template?
Okay lets see example:
In CMS i have settings where i put social media links and contact informatios.
Also in CMS i have module where i create HTML block-s which after that i loop in website.
In that html block i want to put existing $SiteConfig.Email variable.
I Try that but that is rendered in template like $SiteConfig.Email not show real email?
Is this posible to do or i need some extra modification?
Check photo
The question you have written makes no sense to me, but I understand the screenshot.
So, SilverStripe renders .ss files with a class called SSViewer. Basically it reads the file as string and then runs it through SSViewer to generate the HTML output.
But, as you saw, the output of variables is not processed.
I can think of 3 ways to get what you want:
Run the variables through SSViewer aswell (in this example, use $RenderedHTMLContent in the template)
class MyDataObject extends DataObject {
private static array $db = [
'Title' => DBVarchar::class,
'HTMLContent' => DBText::class,
];
public function Foobar() { return "hello from foobar"; }
public function RenderedHTMLContent() {
$template = \SilverStripe\View\SSViewer::fromString($this->HTMLContent);
// using $this->renderWith() will allow you access to all things of $this in the template. so eg $ID, $Title or $Foobar. Probably also $SiteConfig because it's global
return $this->renderWith($template);
// if you want to add extra variables that are not part of $this, you can also do:
return $this->renderWith($template, ["ExtraVariable" => "Hello from extra variable"]);
// if you do not want $this, you can do:
return (new ArrayData(["MyVariable" => "my value"]))->renderWith($template);
}
}
Please be aware of the security implications this thing brings. SilverStripe is purposely built to not allow content authors to write template files. A content author can not only call the currently scoped object but also all global template variables. This includes $SiteConfig, $List, .... Therefore a "bad" content author can write a template like <% loop $List('SilverStripe\Security\Member') %>$ID $FirstName $LastName $Email $Salt $Password<% end_loop %> or perhaps might access methods that have file access. So only do this if you trust your content authors
Use shortcodes instead of variables. But I never liked shortcodes, so I don't remember how they work. You'll have to lookup the docs for that.
Build your own mini template system with str_replace.
class MyDataObject extends DataObject {
private static array $db = [
'Title' => DBVarchar::class,
'HTMLContent' => DBText::class,
];
public function Foobar() { return "hello from foobar"; }
public function RenderedHTMLContent() {
return str_replace(
[
'$SiteConfig.Title',
'$SiteConfig.Tagline',
'$Title',
'$Foobar',
],
[
SiteConfig::current_site_config()->Title,
SiteConfig::current_site_config()->Tagline,
$this->Title,
$this->Foobar(),
],
$this->HTMLContent
);
}
}

WordPress publish_{$post_type} hook works only for posts and not for custom post types or pages

I'm trying to send push notification when any of posts, custom post types or pages is published. I'm getting enabled post types from the plugin settings and adding action via foreach loop in my class __construct method. The problem is that it only works for posts and not for any of custom post types or pages. Here is my function and action:
foreach ((array)get_option('PushPostTypes') as $postType) {
add_action("publish_{$postType}", array($this, 'doNewPostPush'), 10, 2);
}
public function doNewPostPush($id, $post) {
$pushData = array(
'title' => $post->post_title,
'body' => strip_tags($post->post_content),
'data' => array(
'url' => trailingslashit(get_permalink($id)),
),
);
if (has_post_thumbnail($id)) {
$pushData['image'] = get_the_post_thumbnail_url($id);
}
$this->sendNotification($pushData);
}
get_option('PushPostTypes') is an array of post types that user choose, for example: array('post', 'page', 'custom_post');
Any idea why it only works for post and not for pages or custom post types?
Your code worked for me, assuming your get_option('PushPostTypes') is working as intended (Obviously I had to mock that).
Try a different approach that does not rely on get_option('PushPostTypes') to see if you get the same result;
add_action('transition_post_status', function ($new_status, $old_status, $post) {
if ($new_status !== 'publish') {
return;
}
// do something
}, 10, 3);
Try the 'transition_post_status' hook that works for all posts without specifically defining them. Put that somewhere just to see if it runs. Do whatever debugging statement that suits you. Then if that works, then move it into Class code and see if that works. I'm debugging by trying to isolate where the first thing goes wrong. Keeping it very simple to get it to work, then gradually adding complexity until it breaks.

How to put an Elemental field under a tab in admin CMS form

Starting out with the Elemental module for Silverstripe 4 and by default it lists the Elemental area(s) under the Main "Content" tab. I'd like to put them under their own tab.
How do I do that in my Page class getCMSField function?
What I have is:
A specific page (ElementPage) for using the module
ElementPage:
extensions:
- DNADesign\Elemental\Extensions\ElementalPageExtension
In ElementPage.php I have two $has_one like this:
private static $has_one = [
'LeftElemental' => ElementalArea::class,
'RightElemental' => ElementalArea::class
];
Those work fine, fields display and can render them in the template.
Trying to put them under their own tab, the getCMSFields:
public function getCMSFields()
{
$fields = parent::getCMSFields();
// To remove the default added one
$fields->removeByName('ElementalArea');
$fields->addFieldToTab('Root.LeftContentBlocks', ElementalArea::create('LeftElementalID'));
return $fields;
}
Resulting error:
[User Warning] DataObject::__construct passed The value
'LeftElementalID'. It's supposed to be passed an array, taken straight
from the database. Perhaps you should use DataList::create()->First();
instead?
I didn't really expect that to work but I can't see the create signature it needs.
EDIT:
This seems to get it done:
public function getCMSFields()
{
$fields = parent::getCMSFields();
// To remove the default added one
$fields->removeByName('ElementalArea');
$fields->addFieldToTab('Root.LeftContentBlocks', ElementalAreaField::create('LeftElemental', $this->LeftElemental(), $this->getElementalTypes()));
$fields->addFieldToTab('Root.RightContentBlocks', ElementalAreaField::create('RightElemental', $this->RightElemental(), $this->getElementalTypes()));
return $fields;
}
I'm not entirely sure $this->getElementalTypes() is what I should be doing. Any improvements/corrections are welcomed.

Treat the node nids as a field (for display only in a content type) in drupal 7

I need to use the nid of a node as field in a content type: i need to choose where to print it (put it before some fields but after others) and format it as i wish. the only thing i could think about is create a "fake" custom field with no widget to insert it buth with a theme formatter to display it but it seems to me that this is a little to complicated. How should i do it?
If I understand correctly, you just want to expose data to the node view. Could it be as easy as using hook_node_view() from a module?
With that, you can set a 'fake' field to be sent out to the content array of the node, which you can access in the node template.
From drupal.org:
<?php
function hook_node_view($node, $view_mode, $langcode) {
$node->content['my_additional_field'] = array(
'#markup' => $additional_field,
'#weight' => 10,
'#theme' => 'mymodule_my_additional_field',
);
}
?>

Drupal: Dynamic View using Arguments

For a current project i need to setup a specific view to display a gallery detailpage. It should work like this:
1. User clicked a node (costum-post-type: gallery)
2. User received an overview page with linked images
3. User clicked an image
4. User received the gallery page (gallerific view)
Step 1-3 are done. But how can I get Drupal to build a detail page using the data of the overview page?
For Example something like this: http://example.com/gallery-1/detail or http://example.com/gallery-2/detail.
/gallery-n is the overview page with linked images and detail is the detailpage of /gallery-n.
Hope you'll understand what i mean?!
EDIT
On the overview page i have a bunch of thumbails which each are linked to the detail gallery (jquery galleriffic) page.
If I'm correct understand your problem you should do this things.
1. Create view1 for page with linked images. It should be page display with http://example.com/images/%nid
where %nid is nid argument of gallery.
2. Create view2 for gallery detailed page. it should be page display with http://example.com/%nid/detail
3. Theme that views as you want.
4. For view1 for image field use override output in field settings to make it links to %nid/detail
P.S. Use relationships where needed. If description is not clear, fill free to ask.
You can try something like this, in a custom module you make (or maybe already have):
where you set the path to the page you want in the menu and set it as a callback that calls a function and then you can render whatever you want, or call whatever you want.
function MODULENAME_menu() {
$items = array();
$items['gallery/%/detail'] = array(
'title' => 'Gallery Detail',
'page callback' => 'MODULENAME_gallery_detail_page',
'page arguments' => array(1),
'access callback' => TRUE,
'type' => MENU_CALLBACK
);
return $items;
}
function MODULENAME_gallery_detail_page($gallery_id) {
// Here you can render the view as a page, using the gallery
// id which you passed as a parameter to this function.
// So Change MYCUSTOMVIEW to the view you want to render
$view = views_get_view('MYCUSTOMVIEW');
print views_build_view('page', $view, array(), false, false);
}
Just change MODULENAME with the name of your module. You might need to do some work when calling the views_build_view, but it should be a start, you can ask some more questions if you like and I'll help out.

Resources