Silverstripe - assign Template to Controller manually - content-management-system

at the moment I´m working with a custom Silverstripe Controller with a Director rule:
---
Name: myroutes
After: framework/routes#coreroutes
---
Director:
rules:
'category/$Action/$Slug': 'Category_Controller'
The Controller looks like that:
class Category_Controller extends Page_Controller {
public function show($arguments) {
echo "Slug: " . $arguments->param("Slug");
}
}
When I open in the browser the URL http://mysite.com/category/show/mobile
then the output look fine like this: "Slug: mobile".
I just wonder how I can use a Category.ss Template from the Folder "themes/templates/Layout" to render the Output. Then of course the container html (with header/footer) from Page.ss should be included as well. Just as usual when you have a custom Page Controller/Class and a corresponding Template in the Layout Folder.
I just tried this:
public function show($arguments) {
echo $this->renderWith("Category");
}
It uses Category.ss for rendering the output, but there is no container html...
Thx for any help.
Regards,
Florian

you can also pass an array to renderWith(), and it will try through the array until it finds a template.
so lets say $this->renderWith(array('Category', 'Page'));
it will first look for a template called Category.ss, but will not find it (in the templates folder, not layout folder), it will then find Page.ss and use it.
Now it hits $Layout inside Page.ss and it checks the array again inside the Layout folder, it will now find the Category.ss, which is exactly what you where looking for if I got the question right.
if you do not want to do return $this->renderWith(); you can also just do return $this; and silverstripe will get the action you called and the class hierarchy of $this and use that as array for renderWith()
So if your classes are Category_Controller > Page_Controller > ContentController the array will look like this:
array(
'Category_show', // because your action is show
'Category',
'Page_show',
'Page',
'ContentController_show',
'ContentController',
)
(I am not a 100% sure if it also includes Page_show and ContentController_show.)

Related

Retrieve content element field from within a plugin template?

I am modifying the template of a plugin, and I want to retrieve a field from the content element.
Using f:debug I see the only data available is from the plugin itself, and none from the content element.
Is there any way I can perhaps insert the field I need in the plugin settings?
eg. something like:
plugin.tx_plugin.settings {
contentUid = TEXT
contentUid.field = uid
}
The best way I can think of to do this is with a custom ViewHelper. Something like:
namespace MyVendor\MyExtension\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class ContentUidViewHelper extends AbstractViewHelper
{
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
return $configurationManager->getContentObject()->data['uid'];
}
}
In your Fluid template:
<mynamespace:contentUid />
This will get the uid of the content element, but you can get any field this way. Just change the key of the data array to the field you need.
In the corresponding method (like the listAction or showAction) of the controller you can get the data of the content element in the following way:
$contentObject = $this->configurationManager->getContentObject();
$this->view->assign('contentObjectData', $contentObject->data);
As far as I know, you can't get to that data using typoscript, but I've never needed it anyway since I've been using the above code in the controller.
settings do not have stdWrap-type per default, but only string. So you can not use cObjects as values.
For one (or a few) settings, you could do the stdWrap-processing in your method/class yourself:
$settingsAsTypoScriptArray = $this->objectManager->get(TypoScriptService::class)->convertPlainArrayToTypoScriptArray($this->settings);
$contentObj = $this->configurationManager->getContentObject();
if ($contentObj === null) {
$contentObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
}
// now override the values in the settings array with the processed value
$contentUid = (int)$contentObj->stdWrap($settingsAsTypoScriptArray['contentUid'], $settingsAsTypoScriptArray['contentUid.']);
If you wanna have many settings to be stdWraped, have a look into EXT:news. Georg implemented an interesting concept via useStdWrap configuration.

Wordpress Plugins / Custom Class / get_option() / shortcode

I'm having an issue displaying information returned from a custom class defined within a plugin's files, when using a shortcode. I'll write up some mock files that showcase my issue.
/wp-content/plugins/my-plugin/classes/my_class.php
<?php
class People {
public $api_url = "https://www.external-service.com/api";
private $api_key;
function __construct($key = null) {
if $(key) {
$this->api_key = $key;
}
function get_response() {
$path = $this->api_url . "?my_api_token=" . $this->api_key;
}
}
?>
/wp-content/plugins/my-plugin/my-plugin.php
<?php
/**
* all of the wordpress plugin comments
* ...
*/
require "myplg_options.php";
require "myplg_shortcodes.php";
The options page and menu is generated from myplg_options; it is functioning correctly (including using get_option to retrieve the saved option (in this case, the api key).
/wp-content/plugins/my-plugin/myplg_shortcodes.php
<?php
require "classes/my_class.php";
$options = get_option('myplg_settings');
$myplg = new People($options['myplg_api_key']);
$response = $myplg->get_response();
function myplg_list_result(){
echo "the shortcode is working!";
var_dump($options, $myplg, $respnose);
}
add_shortcode('myplg_list', 'myplg_list_result');
?>
Testing externally from wordpress, the class works and everything is fine and dandy. The plugin's option page sets and retains the single option perfectly; the shortcode actually registers and is usable from within a WordPress page/portfolio/etc.
The issue I'm having is that using var_dump, all three of those variables are dumped as NULL.
After doing some homework, I was able to determine that moving the three variable declarations inside the shortcode makes it work. It would seem to me, however, that doing that is not the best workflow, as I'd need to re-grab the option, instantiate a new class, and call the class' function for every shortcode.
Is there a way around this?
As mentioned in the comment it's because variables are function scoped. You may be better off using a Closure.
<?php
require "classes/my_class.php";
$options = get_option('myplg_settings');
$myplg = new People($options['myplg_api_key']);
$response = $myplg->get_response();
add_shortcode('myplg_list', function() use ($options, $response, $myplg) {
echo "the shortcode is working!";
var_dump($options, $myplg, $respnose);
});

How to access a method and pass an argument within the template?

In my template I want check whether an entity has a relation to another one. Meaning one object is in an attached Object Storage of another one.
In the controller I can simply call:
if ($product->getCategory()->offsetExists($category) {
print 'In category ' . $category->getName();
}
But I can't figure out the correct syntax in the template. I tried those without luck (both evaluate to true everytime):
<f:if condition="{product.category.offsetExists(category)}">true</f:if>
<f:if condition="{product.category.offsetExists({category})}">true</f:if>
Is this even possible within the template?
You can only access properties via Getter from Fluid with no parameters, but you can implement an own ViewHelper to check that. As parameters you can use your Product and the Category. Then you can call your ViewHelper from Fluid this way:
<vh:checkOffset product="{product}" category="{category}" />
or inline
{vh:checkOffset(product: product, category: category)}
Within your ViewHelper you can check this in the way you've done it in your Controller:
public function render($product, $category){
return ($product->getCategory()->offsetExists($category));
}
Additionally to sretuer's answer, I'll only mention that you can create VH which will display block conditionally like:
File typo3conf/ext/your_ext/ViewHelpers/CheckOffsetViewHelper.php
<?php
namespace VENDORNAME\YourExt\ViewHelpers;
class CheckOffsetViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
public function render() {
return ($product->getCategory()->offsetExists($category))
? $this->renderChildren()
: '';
}
}
?>
So you can use it in the view:
{namespace vh=VENDORNAME\YourExt\ViewHelpers}
<vh:checkOffset product="{product}" category="{category}" >
Display this only if product is in category
</vh:checkOffset>
Of course you need to fix VENDORNAME and YourExt according to your extension, can be found at the beginning of every controller, model, repository etc.
You may consider https://fluidtypo3.org/viewhelpers/vhs/master/Condition/Iterator/ContainsViewHelper.html which is designed for creating conditions in Fluid that check if an array or Iterator contains another object and works exactly like f:if regarding then and else arguments and f:then and f:else child nodes.

TYPO3 Extension: Generate a PDF

Im trying to make an extension with Kickstarter that overrides the normal rendering of the page, and renders a PDF file. For this im using FPDF. But im not sure how to do it. I tried doing this, but it didnt work:
<?php
// require_once(PATH_tslib . 'class.tslib_pibase.php');
class tx_ishurkunde_pi1 extends tslib_pibase {
public $prefixId = 'tx_ishurkunde_pi1';
public $scriptRelPath = 'pi1/class.tx_ishurkunde_pi1.php';
public $extKey = 'ish_urkunde';
public $pi_checkCHash = TRUE;
public function main($content, array $conf) {
if (!t3lib_extMgm::isLoaded('fpdf')) return "Error!";
$pdf = new FPDF();
$pdf->AddPage();
$content = $pdf->Output('', 'S');
return $content;
}
}
?>
It still keeps rendering the normal web template. What am I missing?
FYI, Im not trying to render the HTML as PDF. Im trying to generate a PDF from scratch, using the URL parameters are text variables.
As far as I understood, your aim is to render a PDF instead of page elements.
Your current approach will not work since you are inserting a plugin onto the page. The plugin's return value is then given back to the TYPO3 content parser, and if the page has finished parsing, it is displayed. There is no part in it where you can throw over the whole page rendering; At least it is not intended to do, and you shouldn't to (albeit there are extensions that do this).
The eID approach would be to either create an eID script (have a look at dd_googlesitemap) which is called via GET param and renders only what you need. There you basically can do everything you want to.
In your extension's ext_localconf.php you register the eID script, like this:
$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'][$_EXTKEY] = "EXT:{$_EXTKEY}/path/to/my/EIDHandler.php";
An Example eID handler structure:
class Tx_MyExt_EIDHandler
{
public function main()
{
// Your code here
}
}
$output = t3lib_div::makeInstance('Tx_MyExt_EIDHandler');
$output->main();
To call your eID script in the frontend, you append the appropriate GET params, like http://example.com/index.php?eID=tx_myext. This is the array key you defined in your ext_localconf.php (in my example, it is $_EXTKEY, but it can basically be any string).
The plugin/typoscript approach would be like e.g. TemplaVoila does it: You create a PAGE type and call a user_func which does your things. This would be the fastest approach because you already have a plugin. Important is that you render your own page type with only your plugin in it.
Example TypoScript:
specialPage = PAGE
specialPage {
type = 2
10 = USER
10.userFunc = tx_myextension_pi1->myFunc
}
After that, you can call your new page with http://example.com/index.php?type=2. However, headers etc are still rendered and you may need to remove them.

How to call custom filters in Zend?

I want to use htmlpurifier on my website, but can't figure out how to load my filter in the view. I've added my filter the way described in the first answer here.
I want to be able to call it from my view with something like $this->filter($content) Any suggestions how I do that?
It is a two step process:
Write an actual Zend_Filter implementation of HTMLPurifier (done, answer in the question you mentioned)
Write a view helper
It will look like this:
class My_View_Helper_Purify extends Zend_View_Helper_Abstract
{
public function purify($value)
{
$filter = new My_Filter_HtmlPurifier();
return $filter->filter($value);
}
}
Don't forget to add your custom view helper path:
$view->addHelperPath(
APPLICATION_PATH . '/../library/My/View/Helper',
'My_View_Helper_'
);
And later in any of your view scripts:
<?= $this->purify($text) ?>