Speaking url (realurl) links not rendering in extbase extension - typo3

I am developing an extbase extension to expose the site menu structure in json format. PageRepository gives me the relevant page uids and titles so the only remaining challenge is building correct URLs. I am using typoLink to do this but instead of getting speaking urls (which are rendered correctly if I browse the site) the output is in the form of index.php?id=[uid]. Here is my code:
$data = $this->pageRepository->getMenu(1);
$this->cObj = $GLOBALS['TSFE']->cObj;
$retval = array();
foreach ($data as $key => $row)
{
$pageUid = $row['uid'];
$conf = array('parameter'=>$pageUid, 'returnLast'=>'url');
$uri = $this->cObj->typoLink('', $conf);
array_push($retval, array('title' => $row['title'], 'url' => $uri));
}
Realurl 2.3.2
Typo3 8.7.11
Any ideas?

If you use a pageType to render your Extbase JSON response, have you checked if config.tx_realurl_enable = 1 is set for this pageType?

Related

How to access complex variables in page typoscript from content element via viewhelper?

I am using cobj_xpath object in my page typoscript as follows.
lib.xpath = XPATH
lib.xpath {
source = http://docsouth.unc.edu/southlit/poe/poe.xml
return = string
resultObj {
cObjNum = 1
1.current = 1
}
}
page.10 = FLUIDTEMPLATE
page.10.variables {
title < lib.xpath
title.expression = /TEI.2/text/front/titlePage/docTitle/titlePart
author < lib.xpath
author.expression = /TEI.2/text/front/titlePage/docAuthor
}
I can access the 'title' and 'author' variables in page template successfully via {title} and {author} viewhelpers but I cannot access them in the content element level. I cannot even find them in at CE level. Also I have the same problem with other COAs e.g.:
taleArgument = TEXT
taleArgument.data = GP:tale
MORE INFO:
I have created the CE via mask extension and configured it to create the required files in /Resources/Mask/ folder. In this folder there is a json file which contains the CE configuration and two folders named Backend and Frontend. Each of these folders contain Layout/Partial/Templates folders. I have inserted the CE created by mask in one of my pages. I manipulate the HTML file in Frontend/Templates as the template file and I can access the fields which I have created in the CE backend properly, so I suppose that my configuration is working well to this end.
Typo3 Version: 9.5.19
cobj_xpath and cobj_xslt version: 1.9.0
Further Investigations:
To get rid of external extensions, I installed a fresh Typo3. Then I developed a CE in my sitepackage from scratch. My configuration follows:
my_ext/Configuration/TsConfig/Page/Mod/Wizards/NewContentElement.tsconfig
mod.wizards.newContentElement.wizardItems.common {
elements {
my_ext_newcontentelement {
iconIdentifier = folder-open
title = Registration Example
description = Create a registration form
tt_content_defValues {
CType = my_ext_newcontentelement
}
}
}
show := addToList(my_ext_newcontentelement)
}
my_ext/Configuration/TCA/Overrides/tt_content.php
<?php
defined('TYPO3_MODE') or die();
call_user_func(function () {
// Adds the content element to the "Type" dropdown
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem(
'tt_content',
'CType',
[
'Registration Example',
'my_ext_newcontentelement',
'form-checkbox',
],
'textmedia',
'after'
);
// Configure the default backend fields for the content element
$GLOBALS['TCA']['tt_content']['types']['my_ext_newcontentelement'] = [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
--palette--;;headers,
bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel,
--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,
--palette--;;frames,
--palette--;;appearanceLinks,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
--palette--;;language,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
--palette--;;access,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories,
categories,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes,
rowDescription,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended,
',
'columnsOverrides' => [
'bodytext' => [
'config' => [
'enableRichtext' => true,
'richtextConfiguration' => 'default',
],
],
],
];
});
my_ext/Configuration/TypoScript/setup.typoscript
lib.contentElement {
templateRootPaths.200 = EXT:my_ext/Resources/Private/Templates/ContentElements/
}
tt_content {
my_ext_newcontentelement =< lib.contentElement
my_ext_newcontentelement {
templateName = NewContentElement
}
}
my_ext/Resources/Private/Templates/ContentElements/NewContentElement.html:
<div>{data.bodytext -> f:format.html()}</div>
I tested my CE after adding one to the backend and it works fine so far.
Then I create a new variable in my_ext/Configuration/TypoScript/setup.typoscript:
page.10.variables {
test = TEXT
test.value = test
}
I can see the variable when I add {_all} to my page template:
but no luck when I try to catch it in my CE template:
TLDR:
each fluid rendering has it's own variables. There are no global fluid-variables.
It is obvious that you can not access test from your content element as the definition of page.10.variables results in fluid variables used while you are renderend the page-template (page.10).
In your content element you have an independent rendering with it's own set of variables.
Meanwhile you often have fluid for some rendering, but each has its own definition and variable set.
The whole page has a page fluid rendering.
Each plugin has it's own fluid rendering, probably for each action. Although they share a common extension setting which results in some common fluid variables.
Each content element has a Fluid rendering, though they might share some definition as the result from the same kind of data (a tt_content record). The kind of CE defines which template is used to start with and there are different renderings.
Using TYPO9 and ext:bootstrap_package (and ext_fluid_styled_content) you can find:
The rendering of the CEs is defined below tt_content. with the name of the CE as next key. All definitions are based on lib.dynamicContent
if you want to access any data independent from context in your fluid you could use typoscript viewhelpers like:
lib.text = TEXT
lib.text.value = test
lib.getText = TEXT
lib.getText.data = GP:text
the calls in fluid:
<f:cObject typoscriptObjectPath="lib.text" />
{f:cObject(typoscriptObjectPath:'lib.getText')}

Zend Framework Router Hostname and Multi-Language support

Last two days I was fighting with Zend Framework Router and still didn't find solution.
Existing project has 2 different modules which work with the same domain e.g. www.domain1.com:
default - Contains public information, can be accessed via www.domain1.com
admin - Administrator interface, can be accessed via www.domain1.com/admin
Project is multi-langual and to keep language code it is transmitted as first parameter of every URL, e.g. www.domain1.com/en/ www.domain1.com/en/admin
Part of code which takes care is next plugin:
class Foo_Plugin_Language extends Zend_Controller_Plugin_Abstract
{
const LANGUAGE_KEY = 'lang';
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$languagesRegexp = implode('|', array_map('preg_quote', $this->_bootstrap->getLanguages()));
$routeLang = new Zend_Controller_Router_Route(
':' . self::LANGUAGE_KEY,
array(self::LANGUAGE_KEY => $this->_bootstrap->getLanguage()->toString()),
array(self::LANGUAGE_KEY => $languagesRegexp)
);
$router = $this->getFrontController()->getRouter();
$router->addDefaultRoutes();
$chainSeparator = '/';
foreach ($router->getRoutes() as $name => $route) {
$chain = new Zend_Controller_Router_Route_Chain();
$chain
->chain($routeLang, $chainSeparator)
->chain($route, $chainSeparator);
$new_name = $this->_formatLanguageRoute($name);
$router->addRoute($new_name, $chain);
}
protected function _formatLanguageRoute($name)
{
$suffix = '_' . self::LANGUAGE_KEY;
if (substr($name, -strlen($suffix)) == $suffix) return $name;
return $name . '_' . self::LANGUAGE_KEY;
}
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
$lang = $request->getParam(self::LANGUAGE_KEY, null);
$this->_bootstrap->setLanguage($lang);
$actual_lang = $this->_bootstrap->getLanguage()->toString();
$router = $this->getFrontController()->getRouter();
$router->setGlobalParam(self::LANGUAGE_KEY, $lang);
// Do not redirect image resize requests OR get js, css files
if (preg_match('/.*\.(jpg|jpeg|gif|png|bmp|js|css)$/i', $request->getPathInfo())) {
return true;
}
// redirect to appropriate language
if ($lang != $actual_lang) {
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$params = array(self::LANGUAGE_KEY => $actual_lang);
$route = $this->_formatLanguageRoute($router->getCurrentRouteName());
return $redirector->gotoRouteAndExit($params, $route, false);
}
}
}
One of the first question is what do you think about such way to provide multi-lang support. What I've noticed is that all this chains dramatically decrease operation speed of the server, response time from the server is about 4 seconds...
Main question is: Currently I have to implement such feature: I have domain www.domain2.com that should work just with single module e.g. "foobar"... and it should be available via second url... or, of course, it should work like www.domain1.com/en/foobar by default...
To provide this functionality in Bootstrap class I'be implemented such part of code
// Instantiate default module route
$routeDefault = new Zend_Controller_Router_Route_Module(
array(),
$front->getDispatcher(),
$front->getRequest()
);
$foobarHostname = new Zend_Controller_Router_Route_Hostname(
'www.domain2.com',
array(
'module' => 'foobar'
)
);
$router->addRoute("foobarHostname", $foobarHostname->chain($routeDefault));
And that is not working and as I've found routeDefault always rewrite found correct model name "foobar" with value "default"
Then I've implemented default router like this:
new Zend_Controller_Router_Route(
':controller/:action/*',
array(
'controller' => 'index',
'action' => 'index'
);
);
But that still didn't work, and started work without language only when I comment "routeStartup" method in Foo_Plugin_Language BUT I need language support, I've played a lot with all possible combinations of code and in the end made this to provide language support by default:
class Project_Controller_Router_Route extends Zend_Controller_Router_Route_Module
{
/**
* #param string $path
* #param bool $partial
* #return array
*/
public function match($path, $partial = false)
{
$result = array();
$languageRegexp = '%^\/([a-z]{2})(/.*)%i';
if (preg_match($languageRegexp, $path, $matches)) {
$result['lang'] = $matches[1];
$path = $matches[2];
}
$parentMatch = parent::match($path);
if (is_array($parentMatch)) {
$result = array_merge($result, $parentMatch);
}
return $result;
}
}
So language parameter was carefully extracted from path and regular processed left part as usual...
But when I did next code I was not able to get access to the foobar module via www.domain2.com url, because of module name in request was always "default"
$front = Zend_Controller_Front::getInstance();
/** #var Zend_Controller_Router_Rewrite $router */
$router = $front->getRouter();
$dispatcher = $front->getDispatcher();
$request = $front->getRequest();
$routerDefault = new Project_Controller_Router_Route(array(), $dispatcher, $request);
$router->removeDefaultRoutes();
$router->addRoute('default', $routerDefault);
$foobarHostname = new Zend_Controller_Router_Route_Hostname(
'www.domain2.com',
array(
'module' => 'foobar'
)
);
$router->addRoute("foobar", $foobarHostname->chain($routerDefault));
Instead of summary:
Problem is that I should implement feature that will provide access for the secondary domain to the specific module of ZendFramework, and I should save multi-language support. And I cannot find a way, how to manage all of this...
Secondary question is about performance of chain router, it makes site work very-very slow...
The way I have solved problem with multilanguage page is in this thread:
Working with multilanguage routers in Zend (last post).
Ofcourse my sollution need some caching to do, but I think it will solve your problem.
Cheers.

Authorization http header is not working at Zend Soap Client

I Used the below code to retrive the categories from the third party site using API, but unfortunately stream context is not able to requested at their API and resulting in the Internal Error.
FYI : It is used under zend framework.
$header = "Authorization: Bearer ".$accestoken."\r\n"."Content-Type:text/xml";//.'Content-Type:application/xml';
$wsdl = 'wsdl url';
$context = stream_context_create(array('http' => array('header' => $header,'method'=>'GET')));
$options = array('stream_context'=>$context,'encoding'=>'ISO-8859-1','exceptions'=>FALSE);
$params = array ('accessToken' => $accestoken);
$response = $client->getAdCategories($params);
print_r($response);
So please find the above code and provide some solution for this issue.
$httpHeaders = array(
'http'=>array(
'protocol_version' => 1.1,
'header' => "Authorization:Bearer ".$accestoken."\r\n" ,
"Connection: close"
));
$context = stream_context_create($httpHeaders);
$soapparams = array(
'stream_context' => $context,
);
$client = new SoapClient($wsdl, $soapparams);
$response = $client->getAdCategories($params);
print_r($response);
Please refer https://bugs.php.net/bug.php?id=49853
OK, I see in the title at least this is a SOAP service you are trying to work with. You should then be using something like the Zend_Soap_Client.
Looks like you have a WSDL... so,
$client = new Zend_Soap_Client("yourwsdl.wsdl");
and then make a request like
$retval = $client->method1(10);
Looking at your code I am not 100% sure what authentication approach is in use. If basic HTTP auth you can just pass username and password as options in the client's constructor.
Setting a header might look something like this:
$auth = new stdClass();
$auth->user = 'joe';
$auth->token = '12345';
$authHeader = new SoapHeader('authNamespace', 'authenticate', $auth);
$client->__setSoapHeaders(array($authHeader));
If you need more help post your WSDL.

silverstripe dataobject searchable

I´m trying to have certain DataObjects (News) displayed in the default SearchResult Page. So the result should display normal Pages and News.
Is there an easy way to accomplish that in Silverstripe 3?
Or is it recommended to code it completely custom - I mean a custom controller/action which handles the search request and creates a result list, which I display then in a custom template?
I found this, but obviously search is disabled right now:
https://github.com/arambalakjian/DataObjects-as-Pages
Thx and regards,
Florian
I usually but together a custom search function after enabling FulltextSearchable. So in _config.php I would have
FulltextSearchable::enable();
Object::add_extension('NewsStory', "FulltextSearchable('Name,Content')");
replacing Name and Content with whatever DBField you want to be searchable. And each searchable DataObject have this in their class to enable search indexes (pretty sure this needs to be added and run dev/build before enabling the extension, and only works on MySQL DB).
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
then in my PageController I have my custom searchForm and results functions.
Here is the search function that returns the search form, called with $search in the template:
public function search()
{
if($this->request && $this->request->requestVar('Search')) {
$searchText = $this->request->requestVar('Search');
}else{
$searchText = 'Search';
}
$f = new TextField('Search', false, $searchText);
$fields = new FieldList(
$f
);
$actions = new FieldList(
new FormAction('results', 'Go')
);
$form = new Form(
$this,
'search',
$fields,
$actions
);
//$form->disableSecurityToken();
$form->setFormMethod('GET');
$form->setTemplate('SearchForm');
return $form;
}
and here the custom results function to handle the queries...
function results($data, $form, $request)
{
$keyword = trim($request->requestVar('Search'));
$keyword = Convert::raw2sql($keyword);
$keywordHTML = htmlentities($keyword, ENT_NOQUOTES, 'UTF-8');
$pages = new ArrayList();
$news = new ArrayList();
$mode = ' IN BOOLEAN MODE';
//$mode = ' WITH QUERY EXPANSION';
//$mode = '';
$siteTreeClasses = array('Page');
$siteTreeMatch = "MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keyword'$mode)
+ MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keywordHTML'$mode)";
$newsItemMatch = "MATCH( Name, Content ) AGAINST ('$keyword'$mode)
+ MATCH( Name, Content ) AGAINST ('$keywordHTML'$mode)";
//Standard pages
foreach ( $siteTreeClasses as $c )
{
$query = DataList::create($c)
->where($siteTreeMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $siteTreeMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record )
{
if ( in_array($record['ClassName'], $siteTreeClasses) )
$objects[] = new $record['ClassName']($record);
}
$pages->merge($objects);
}
//news
$query = DataList::create('NewsStory')->where($newsItemMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $newsItemMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record ) $objects[] = new $record['ClassName']($record);
$news->merge($objects);
//sorting results
$pages->sort(array(
'Relevance' => 'DESC',
'Title' => 'ASC'
));
$news->sort(array(
'Relevance' => 'DESC',
'Date' => 'DESC'
));
//output
$data = array(
'Pages' => $pages,
'News' => $news,
'Query' => $keyword
);
return $this->customise($data)->renderWith(array('Search','Page'));
}
I add all the Page classes I want to be searched and that extend SiteTree in the $siteTreeClasses array, and the News parts can be pretty much copied for any other DataObjectI need searchable.
I am not saying this is the best solution and this can definitely be improved on, but it works for me and this might be a good stating point.
I have adapted #colymba's solution into a silverstripe module: https://github.com/burnbright/silverstripe-pagesearch
It allows setting the pagetype in the url.
You'll need to substantially overwrite SearchForm->getResults().
It uses Database->searchEngine(), but those are tailored towards SiteTree and Page classes.
The "proper" solution is to feed the data into a search engine like Solr or Sphinx.
We have the SS3-compatible "fulltextsearch" module for this purpose:
https://github.com/silverstripe-labs/silverstripe-fulltextsearch
It's going to take some upfront setup, and is only feasible if you can either host Solr yourself, or are prepared to pay for a SaaS provider. Once you've got it running though, the possibilities are endless, its a great tool!

TYPO3: Use t3lib_TCEforms in frontend plugin

I would like to use as much standard TYPO3 as possible to create a form to edit single records from tx_mytable.
In pi1 i load the tca for the table:
t3lib_div::loadTCA('tx_mytable');
Now I would like to use standard functions to create my form elements more or less like it is done in the backend...
I found this for the front end but cannot find any working examples:
t3lib_TCEforms_fe.php (that extends the normal t3lib_TCEforms)
Is this the right way to go or is there a better way?
I got something working but not really that nice code in the frontend
Here is a link that telss that TCA is not enough but two new entries in the array is needed
http://www.martin-helmich.de/?p=15
It is itemFormElName and itemFormElValue
// include tceforms_fe (place outside class where pipase is included)
require_once(PATH_t3lib.'class.t3lib_tceforms_fe.php');
// load TCA for table in frontend
t3lib_div::loadTCA('tx_ogcrmdb_tasks');
// init tceforms
$this->tceforms = t3lib_div::makeInstance("t3lib_TCEforms_FE");
$this->tceforms->initDefaultBEMode(); // is needed ??
$this->tceforms->backPath = $GLOBALS['BACK_PATH']; // is empty... may not be needed
//////////REPEAT FOR EACH INPUT FIELD/////////
// start create input fields, here just a single select for responsible
// conf used for tceforms similar to but not exactly like normal TCA
$conftest = array(
'itemFormElName' => $GLOBALS['TCA']['tx_ogcrmdb_tasks']['columns']['responsible']['label'],
'itemFormElValue' => 1,
'fieldConf' => array(
'config' => $GLOBALS['TCA']['tx_ogcrmdb_tasks']['columns']['responsible']['config']
)
);
// create input field
$this->content .= $this->tceforms->getSingleField_SW('','',array(),$conftest);
// wrap in form
$output = '<form action="" name="editform" method="post">';
$output .= $this->content;
$output .= '</form>';
// wrap and return output
return $output;
Still looking for a working example with custem template for input fields.