YouTube id contents in TYPO3 Flux FAL Object - typo3

I have a YouTube Video as a FAL Object and i need the YouTube id in my Fluid Template.
In a FileReference Object it is possible to get the id via {file.contents}.
But i can't find the YouTube id in the FAL Array from a flux:field.inline.fal
In my flux Content Template i get the Video FAL Array like this:
{v:content.resources.fal(field: 'settings.falimage', record:record) -> v:iterator.first() -> v:variable.set(name: 'image')}
In the recieved Array i only find the url to the YouTube Video. Is there a getter or ViewHelper for that?
My Setup:
TYPO3 9.5.9
Flux 9.2.0
VHS 5.2.0

After the failed attempt to use the VHS PregMatchViewHelper (https://fluidtypo3.org/viewhelpers/vhs/development/Variable/PregMatchViewHelper.html) in conjunction with the pattern https://gist.github.com/ghalusa/6c7f3a00fd2383e5ef33 i ended up creating an own ViewHelper that finds the YouTube id from an url:
<?php
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* A view helper for rendering the YouTube Id from an url
*
* = Examples =
*
* <code>
* {nc:youTubeId(url: 'https://www.youtube.com/watch?v=zpOVYePk6mM')}
* </code>
* <output>
* zpOVYePk6mM
* </output>
*/
class YouTubeIdViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('url', 'string', 'YouTube url', true);
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return string youtube id
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$url = $arguments['url'];
preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $url, $match);
$youtube_id = $match[1];
return $youtube_id;
}
}

Related

TYPO3 FileReference does not save the tablename on the DB. Uploading file from frontend on TYPO3

In my custom extension on TYPO3 10.4 I'm trying to upload a file (image) from the frontend. The file gets uploaded just fine, the rows on the DB seemed to be inserted just fine but there is some data missing.
This is my form:
<f:form method="post" action="create" name="blackboard"
object="{blackboard}" enctype="multipart/form-data">
<f:form.textfield placeholder="Titel*" required="true" property="title"></f:form.textfield>
<f:form.upload property="image" name="image" />
<f:form.submit class="btn btn-primary" value="{f:translate(key: 'submit', default: 'Absenden')}"></f:form.submit>
</f:form>
The model:
/**
* image
*
* #var \TYPO3\CMS\Extbase\Domain\Model\FileReference
* #TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
*/
protected $image = null;
/**
* Returns the image
*
* #return \TYPO3\CMS\Extbase\Domain\Model\FileReference $image
*/
public function getImage()
{
return $this->image;
}
/**
* Sets the image
*
* #param \TYPO3\CMS\Extbase\Domain\Model\FileReference $image
* #return void
*/
public function setImage(\TYPO3\CMS\Extbase\Domain\Model\FileReference $image)
{
$this->image = $image;
}
The controller:
/**
* action create
* #param Blackboard
*/
public function createAction(Blackboard $blackboard)
{
$blackboard->setPid($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['hebo_blackboards']['BlackboardsStoragePId']);
$blackboard->setUser($GLOBALS['TSFE']->fe_user->user['uid']);
$this->blackboardRepository->add($blackboard);
}
Surprisingly, just that easy, this seems to work just fine. I get the image uploaded on the server, the correct UID of that sys_file_reference on my custom table, the sys_file_reference gets the correct UID of that sys_file... but as you can see in the pic that follows there are a few data missing, "tablename" and "table_local" and as soon as I add that data manually the relationships work (the first rows, where this data is not missing is from rows created from the backend, working fine)
My question is, why? What do I do to fix that?
The problem is that extbase doesn't know those values, therefore you need to state those in the TCA. Given this example
'extra_files' => [
'label' => 'A file',
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
'extra_files',
[
'foreign_match_fields' => [
'tablenames' => 'tx_yourtable_domain_model_fo',
'table_local' => 'sys_file'
]
],
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
),
],
The foreign_match_fields part is the relevant one which is not needed if you don't handle file uploads in the Frontend.

Check TYPO3 link type in fluid

I want to render a typolink in fluid, but i need to check if it is a download file (t3://file?uid=1), a page link (t3://page?uid=1) or an external link (https://www.abc.de _blank).
Is there a way or viewhelper to check the linktype in fluid?
What i found were only via typoscript or a hacky way with VHS like
<f:if condition="{target -> v:math.round()} > 0">
It's for TYPO3 9.x
$linkService = $this->objectManager->get(LinkService::class);
$result = $linkService->resolve($linkValue);
That could help you in a custom ViewHelper
Possible Values for $linkValue:
t3://page?uid=1 => [string (pageuid), 'page']
info#example.com => [string (email), 'email']
https://typo3.org => [string (url), 'url']
t3://file?uid=226 => [TYPO3\CMS\Core\Resource\File, 'file']
$result returns an array. Every case has the argument "type". Depending on the type, another value or object is returned. I have listed this above.
The class is available from TYPO3 Version 8.
You could also check the linktype with the vhs extension, e.g. to set a different target:
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
...
<f:variable name="target">_self</f:variable>
<v:condition.string.contains haystack="{url}" needle="t3://file?uid">
<f:variable name="target">_blank</f:variable>
</v:condition.string.contains>
<v:condition.string.contains haystack="{url}" needle="http">
<f:variable name="target">_blank</f:variable>
</v:condition.string.contains>
<v:condition.string.contains haystack="{url}" needle="www">
<f:variable name="target">_blank</f:variable>
</v:condition.string.contains>
<f:link.typolink parameter="{url}" target="{target}">the link</f:link.typolink>
This is the ViewHelper i am using now:
/**
* A view helper for rendering the linktype
*
* = Examples =
*
* <code>
* {nc:linkType(parameter: link)}
* </code>
* <output>
* page, file, url, email, folder, unknown
* </output>
*/
class LinkTypeViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initialize arguments
*/
public function initializeArguments()
{
$this->registerArgument('parameter', 'string', 'stdWrap.typolink style parameter string', true);
}
/**
* #param array $arguments
* #param \Closure $renderChildrenClosure
* #param RenderingContextInterface $renderingContext
* #return string Linktype (page, file, url, email, folder, unknown)
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$parameter = $arguments['parameter'];
// workaround if parameter has _blank or other additional params
$arr = explode(' ',trim($parameter));
$firstparameter = $arr[0];
$linkService = GeneralUtility::makeInstance(LinkService::class);
$linkDetails = $linkService->resolve($firstparameter);
return $linkDetails['type'];
}
}

TYPO3: Uploads in backend module

I have to implement a "Book" management in the Backend. Each book has a PDF preview, a title, description and so on...
The BE user should be able to upload a PDF and set a title, description etc. through a Backend module.
The created Book should be selectable in a plugin (or content element ?) so it can be display in the frontend.
Also, the uploaded PDF should only be downloadable by a certain group of FE users.
I don't know how to handle the upload part in the backend. I didn't find much info online beside this upload example: https://github.com/helhum/upload_example it seems quite complex and I'm not sure if it's the best solution for me.
What would be the best way to proceed for my task ?
Use File Abstraction Layer (FAL). You don't need the example in backend, but it's great for frontend upload.
Domain/Model/Book.php
...
/**
* File (file references)
*
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference>
* #lazy
*/
protected $files = NULL;
/**
* Construct
*
*
*/
public function __construct() {
//Do not remove the next line: It would break the functionality
$this->initStorageObjects();
}
/**
* Initializes all ObjectStorage properties
* Do not modify this method!
* It will be rewritten on each save in the extension builder
* You may modify the constructor of this class instead
*
* #return void
*/
protected function initStorageObjects() {
$this->files = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
}
/**
* Set files (file references)
*
* #param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $files
* #return void
*/
public function setFiles(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $files) {
$this->files = $files;
}
/**
* Get files (file references)
*
* #return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FileReference> $files
*/
public function getFiles() {
return $this->files;
}
...
TCA/tx_yourextension_domain_model_book.php
...
'files' => [
'label' => 'LLL:EXT:werkhandkunst/Resources/Private/Language/locallang_db.xlf:file',
'config' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getFileFieldTCAConfig(
'files', ['
maxitems' => 25,
],
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
),
],
...
ext_tables.sql
CREATE TABLE tx_yourextension_domain_model_book (
...
files int(11) unsigned DEFAULT '0' NOT NULL,
...
)

Typo3 Extbase AJAX without page typenum

Is there any way to create AJAX calls in Extbase extension without using of page typeNum?
Edit:
Helmut Hummel, a member of the TYPO3 CMS team, measured that using EID with Extbase is slower than using the typeNum approach. But since the typeNum approach is cumbersome to configure, there is a third way developed by him.
The extension typoscript_rendering provides a way to call Extbase actions directly without additional configuration. It contains a ViewHelper that generates such links and can be used like this in a Fluid template:
{namespace h=Helhum\TyposcriptRendering\ViewHelpers}
<script>
var getParticipationsUri = '<h:uri.ajaxAction controller="Participation" action="listByCompetition" arguments="{competition:competition}" />';
</script>
This generates an URI that calls the action "listByCompetition" of my "ParticipationController". You can pass arguments normally.
The only downside is that for security reasons, the extension uses the cHash to validate the request arguments. The cHash is submitted by GET but you cannot pass additional arguments by GET at the same time because it would invalidate the cHash. So if you want to pass form data in such a request, you need to mix GET (for a valid AJAX call) and POST (for submitting user data):
<script>
var createAddressUri = '<h:uri.ajaxAction controller="Address" action="create" />';
$body.on('submit', '#myForm', function(e) {
e.preventDefault();
emailAddress = $('#myForm').find('#email');
if (typeof(emailAddress) === 'string') {
$.ajax({
url: createAddressUri,
type: 'POST',
data: { 'tx_myext_pluginname[address][email]' : emailAddress},
success: function() {
// things to do on success
}
})
}
});
</script>
(Of course this is only a very basic example. You might post whole models etc.)
The EID way:
Yes, you can use the EID (Extension ID) mechanism for that. There is no official statement which way (pageType or eID) should be used for Extbase AJAX calls and it seems to be just a matter of taste.
There is a nice tutorial that can be found here and I copy the source code in here:
<?php
/** *************************************************************
*
* Extbase Dispatcher for Ajax Calls TYPO3 6.1 namespaces
*
* IMPORTANT Use this script only in Extensions with namespaces
*
* Klaus Heuer <klaus.heuer#t3-developer.com>
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
* ************************************************************* */
/** ************************************************************
* Usage of this script:
*
* - Copy this script in your Extension Dir in the Folder Classes
* - Set the Vendor and Extension Name in Line 82 + 83
* - Include the next line in the ext_localconf.php, change the ext name!
* - $TYPO3_CONF_VARS['FE']['eID_include']['ajaxDispatcher'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('myExtension').'Classes/EidDispatcher.php';
*
* Use for Ajax Calls in your jQuery Code:
*
* $('.jqAjax').click(function(e) {
* var uid = $(this).find('.uid').html();
* var storagePid = '11';
*
* $.ajax({
* async: 'true',
* url: 'index.php',
* type: 'POST',
*
* data: {
* eID: "ajaxDispatcher",
* request: {
* pluginName: 'patsystem',
* controller: 'Todo',
* action: 'findTodoByAjax',
* arguments: {
* 'uid': uid,
* 'storagePid': storagePid
* }
* }
* },
* dataType: "json",
*
* success: function(result) {
* console.log(result);
* },
* error: function(error) {
* console.log(error);
* }
* });
*************************************************************** */
/**
* Gets the Ajax Call Parameters
*/
$ajax = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('request');
/**
* Set Vendor and Extension Name
*
* Vendor Name like your Vendor Name in namespaces
* ExtensionName in upperCamelCase
*/
$ajax['vendor'] = 'T3Developer';
$ajax['extensionName'] = 'ProjectsAndTasks';
/**
* #var $TSFE \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
*/
$TSFE = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController', $TYPO3_CONF_VARS, 0, 0);
\TYPO3\CMS\Frontend\Utility\EidUtility::initLanguage();
// Get FE User Information
$TSFE->initFEuser();
// Important: no Cache for Ajax stuff
$TSFE->set_no_cache();
//$TSFE->checkAlternativCoreMethods();
$TSFE->checkAlternativeIdMethods();
$TSFE->determineId();
$TSFE->initTemplate();
$TSFE->getConfigArray();
\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadConfigurationAndInitialize();
$TSFE->cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer');
$TSFE->settingLanguage();
$TSFE->settingLocale();
/**
* Initialize Database
*/
\TYPO3\CMS\Frontend\Utility\EidUtility::connectDB();
/**
* #var $objectManager \TYPO3\CMS\Extbase\Object\ObjectManager
*/
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Extbase\Object\ObjectManager');
/**
* Initialize Extbase bootstap
*/
$bootstrapConf['extensionName'] = $ajax['extensionName'];
$bootstrapConf['pluginName'] = $ajax['pluginName'];
$bootstrap = new TYPO3\CMS\Extbase\Core\Bootstrap();
$bootstrap->initialize($bootstrapConf);
$bootstrap->cObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('tslib_cObj');
/**
* Build the request
*/
$request = $objectManager->get('TYPO3\CMS\Extbase\Mvc\Request');
$request->setControllerVendorName($ajax['vendor']);
$request->setcontrollerExtensionName($ajax['extensionName']);
$request->setPluginName($ajax['pluginName']);
$request->setControllerName($ajax['controller']);
$request->setControllerActionName($ajax['action']);
$request->setArguments($ajax['arguments']);
$response = $objectManager->create('TYPO3\CMS\Extbase\Mvc\ResponseInterface');
$dispatcher = $objectManager->get('TYPO3\CMS\Extbase\Mvc\Dispatcher');
$dispatcher->dispatch($request, $response);
echo $response->getContent();
//die();
?>
Have a look at the "usage of this script" section that explains how to register the eID. The script works with TYPO3 6.1 and higher.
For TYPO3 6.2 change the following line:
\TYPO3\CMS\Core\Core\Bootstrap::getInstance()->loadConfigurationAndInitialize();
to
\TYPO3\CMS\Core\Core\Bootstrap::getInstance()
For Test Extension.
Include EID in the ext_localconf.php file
## Ajax configuration
$TYPO3_CONF_VARS['FE']['eID_include']['Test'] = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('test').'Classes/Ajax/EidDispatcher.php';
Create directory in classes – Classes/Ajax/EidDispatcher.php
namespace TYPO3\Test\Ajax;
class EidDispatcher {
/**
* #var \array
*/
protected $configuration;
/**
* #var \array
*/
protected $bootstrap;
/**
* The main Method
*
* #return \string
*/
public function run() {
return $this->bootstrap->run( '', $this->configuration );
}
/**
* Initialize Extbase
*
* #param \array $TYPO3_CONF_VARS
*/
public function __construct($TYPO3_CONF_VARS) {
$ajaxRequest = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('tx_Test_addhours');
// create bootstrap
$this->bootstrap = new \TYPO3\CMS\Extbase\Core\Bootstrap();
// get User
$feUserObj = \TYPO3\CMS\Frontend\Utility\EidUtility::initFeUser();
// set PID
$pid = (\TYPO3\CMS\Core\Utility\GeneralUtility::_GET( 'id' )) ? \TYPO3\CMS\Core\Utility\GeneralUtility::_GET('id') : 1;
// Create and init Frontend
$GLOBALS['TSFE'] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance( 'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController', $TYPO3_CONF_VARS, $pid, 0, TRUE );
$GLOBALS['TSFE']->connectToDB();
$GLOBALS['TSFE']->fe_user = $feUserObj;
$GLOBALS['TSFE']->id = $pid;
$GLOBALS['TSFE']->determineId();
$GLOBALS['TSFE']->getCompressedTCarray(); //Comment this line when used for TYPO3 7.6.0 on wards
$GLOBALS['TSFE']->initTemplate();
$GLOBALS['TSFE']->getConfigArray();
$GLOBALS['TSFE']->includeTCA(); //Comment this line when used for TYPO3 7.6.0 on wards
// Get Plugins TypoScript
$TypoScriptService = new \TYPO3\CMS\Extbase\Service\TypoScriptService();
$pluginConfiguration = $TypoScriptService->convertTypoScriptArrayToPlainArray($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_Test.']);
// Set configuration to call the plugin
$this->configuration = array (
'pluginName' => $ajaxRequest['pluginName'],
'vendorName' => 'TYPO3',
'extensionName' => 'Test',
'controller' => $ajaxRequest['controller'],
'action' => $ajaxRequest['action'],
'mvc' => array (
'requestHandlers' => array (
'TYPO3\CMS\Extbase\Mvc\Web\FrontendRequestHandler' => 'TYPO3\CMS\Extbase\Mvc\Web\FrontendRequestHandler'
)
),
'settings' => $pluginConfiguration['settings'],
'persistence' => array (
'storagePid' => $pluginConfiguration['persistence']['storagePid']
)
);
}
}
global $TYPO3_CONF_VARS;
// make instance of bootstrap and run
$eid = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance( 'TYPO3\Test\Ajax\EidDispatcher', $TYPO3_CONF_VARS );
echo $eid->run();
Call From Script
$.ajax({
async: 'true',
url: 'index.php',
type: 'GET',
data: {
eID: "Test",
tx_ExtName_PluginName: {
pluginName: 'Plugin_Name',
controller: 'Controller_Name',
action: 'Action_Name',
}
},
success:function(data){
// code
}
});
I had to change the first 0 of the makeInstance to the id of the page for it to work.
$id = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('id');
$TSFE = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController', $TYPO3_CONF_VARS, $id, 0);
I used a quick and probably dirty way without typeNum.
I used jQuery ajax call the common way.
My target action ended with the following.
$headers = DivUtilities::createHeader();
foreach ($headers as $header => $data) {
$this->response->setHeader($header, $data);
}
$this->response->sendHeaders();
echo $this->view->render();
exit;
The createHeader Method
/**
*
* #param string $type
* #return array
*/
public static function createHeader($type = 'html')
{
switch ($type) {
case 'txt':
$cType = 'text/plain';
break;
case 'html':
$cType = 'text/html';
break;
case 'json':
$cType = 'application/json';
break;
case 'pdf':
$cType = 'application/pdf';
break;
}
$headers = array(
'Pragma' => 'public',
'Expires' => 0,
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Cache-Control' => 'public',
'Content-Type' => $cType
);
return $headers;
}
The output is the template of the called action. This can be html, json or whatever u need.

Zend Controller Action: _redirect() vs getHelper('Redirector')->gotoUrl()

I've read that $this->getHelper('[helper_name]') is preferable to $this->_helper->[helper_name]. What I haven't been able to find any documentation of is which of these is better/preferred: $this->_redirect($url) or $this->getHelper('Redirector')->gotoUrl($url).
Use whatever one suits you, they do exactly the same thing:
/**
* Redirect to another URL
*
* Proxies to {#link Zend_Controller_Action_Helper_Redirector::gotoUrl()}.
*
* #param string $url
* #param array $options Options to be used when redirecting
* #return void
*/
protected function _redirect($url, array $options = array())
{
$this->_helper->redirector->gotoUrl($url, $options);
}