Render children of active branch with Zend Navigation view helper - zend-framework

What I am asking is similar to this issue, which is still unresolved.
I'm trying to create a good submenu solution, but I can't seem to get Zend_View_Helper_Navigation_Menu to work with me.
Here is my XML navigation config:
<configdata>
<nav>
<index>
<label>Index</label>
<uri>/</uri>
<pages>
<home>
<label>home</label>
<uri>/</uri>
</home>
<about>
<label>about</label>
<uri>/about</uri>
</about>
<works>
<label>works</label>
<uri>/works</uri>
<pages>
<music>
<label>music</label>
<uri>/works/music</uri>
</music>
</pages>
</works>
<posts>
<label>posts</label>
<uri>/posts</uri>
</posts>
<admin>
<label>admin</label>
<uri>/admin</uri>
<pages>
<login>
<label>log in</label>
<uri>/admin/login</uri>
</login>
<settings>
<label>settings</label>
<uri>/admin/settings</uri>
</settings>
<register>
<label>register</label>
<uri>/admin/register</uri>
</register>
<logout>
<label>log out</label>
<uri>/admin/logout</uri>
</logout>
</pages>
</admin>
</pages>
</index>
</nav>
</configdata>
And here is the relevant code in my layout:
echo $this->navigation()->menu()->renderMenu(
null,
array(
'minDepth' => 2,
'maxDepth' => 2,
'onlyActiveBranch' => true
)
);
When I navigate to 'admin', I don't see the submenu. If I change the minDepth to 1 I can see that that menu and its children are 'active' in the source. Why are they not being rendered when the minDepth is set to 2?
I did a workaround for this, but I have to copy/paste it into every controller for it to work (because I need the request object in order to do it):
$pages = $this->view->navigation()->current()->getPages();
foreach ($pages as $page) {
$this->_setChildrenInvisible($page);
}
the _setChildrenInvisible() function:
private function _setChildrenInvisible(Zend_Navigation_Container $container) {
foreach ($container as $child) {
if ($child->hasChildren()) {
$this->_setChildrenInvisible($child);
}
$child->setVisible(false);
}
}
Im just trying to get admin's children to render in the submenu. Does anyone know how to fix this?

In the layout, in the renderMenu method, the renderParents attribute should be set to false
echo $this->navigation()->menu()->renderMenu(
null,
array(
'minDepth' => 2,
'maxDepth' => 2,
'onlyActiveBranch' => true,
'renderParents' => false
)
);

I know this is an old question but it can still be relevant for some people stumbling across this. In ZF2 it can be done the same as in ZF1 using:
echo $this->navigation()->menu()->renderSubMenu();

Related

TYPO3 f:form with additional arguments, where submit not update the arguments

I am using TYPO3 10.4.15
My edit view:
f:section name="content">
<h1>Edit Album</h1>
<f:flashMessages />
<f:render partial="FormErrors" />
<f:form id='fNew' action="update" name="album" object="{album}" arguments="{mode:mode, disc:disc}" >
<f:render partial="Album/FormFields" arguments="{album:album, disc:disc}" />
<f:form.submit value="Save" />
</f:form>
</f:section>
</html>
This is the relevant part of the partial formfields.html:
<f:if condition='{disc}'>
<input type='text' name="disc[0][name][]" />
</f:if>
The error_log with the disc structure looks:
Update-Disc: array (
0 =>
array (
'name' => '',
'trackNum' => '1',
'track' =>
array (
0 =>
array (
'title' => '',
'duration' => '0',
'composer' => '',
'texter' => '',
'musicFile' => '',
'imageFile' => '',
),
),
),
)
And this is the "updateAction" part of the controller
/**
* action update
*
* #param \HGA\Album\Domain\Model\Album $album
* #param string $mode
* #param array $disc
* #return string|object|null|void
*/
public function updateAction(\HGA\Album\Domain\Model\Album $album, $mode, $disc)
{
error_log("Update-Disc: " . var_export($disc, true) . " Mode: " . $mode, 0);
if ($mode == 'tracks') {
$this->editAction($album, $mode, $disc);
}
error_log("Update: " . var_export($album, true) . " Mode: " . $mode, 0);
$this->addFlashMessage('The object was updated. Please be aware that this action is publicly accessible unless you implement an access check. See https://docs.typo3.org/typo3cms/extensions/extension_builder/User/Index.html', '', \TYPO3\CMS\Core\Messaging\AbstractMessage::WARNING);
$this->albumRepository->update($album);
$this->redirect('list');
}
If I write something into the text input field and execute submit, I get the error_log you can see above. The value I have typed in the input field is missing. It is only the array, as I have send it to the view.
The mode string will be transmitted correctly, but with the disc array is maybe something wrong!
The disc array is more complex, but I made it simple, because I need to understand how it works in general.
I also need this additional disc array and can not doing it with the album object!
Thanks in advance for your help.
You are ignoring your plugin's namespace in combination with misinterpretation of f:form arguments.
Each field for your plugin has a prefix like tx_hgaalbum... followed by your property's name in square brackets. So the fieldname for disc should look like tx_hgaalbum...[disc]. Have a look into the HTML code and see which names are generated for the other properties.
The second problem is using the arguments in the form-ViewHelper. This will only add the arguments to the action URI of your form. That's why you're getting your initial values for disc.

How to extend common content elements of TYPO3 with FlexForm through TCA overwrite?

I want to extend some content-elements of TYPO3 with a field to enter a custom CSS class. Therefore I thought, the pi_flexform field is the place to go as it gives the flexibility also to add more values later. (No, I don't want to create Layout-Options for all the CSS classes)
In a TCA overwrite for - lets say "texpic" for example - I added the following structure:
$GLOBALS['TCA']['tt_content']['types']['textpic'] = array_replace_recursive(
$GLOBALS['TCA']['tt_content']['types']['textpic'], [
'columns' => [
'pi_flexform' => [
'l10n_display' => 'hideDiff',
'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:pi_flexform',
'config' => [
'type' => 'flex',
'ds_pointerField' => 'list_type,CType',
'ds' => [
'default' => '
<T3DataStructure>
<ROOT>
<type>array</type>
<el>
<customClass>
<TCEforms type="array">
<label>My custom CSS classes</label>
<config type="array">
<type>input</type>
<eval>trim</eval>
</config>
</TCEforms>
</customClass>
</el>
</ROOT>
</T3DataStructure>
'
],
'search' => [
'andWhere' => '{#CType}=\'list\''
]
]
]
],
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.general;general,
--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.header;header_minimal,
pi_flexform,
[...]
']
);
The pi_flexform field is displayed in the backend, but my XML configuration from above is not evaluated. It shows Plugin Options [pi_flexform] The Title: which is the demo example value from the file typo3\sysext\frontend\Configuration\TCA\tt_content.php. So it seems my code is not entirely overwritten, only the section "showitem" works. What could be the issue here?
EDIT 1:
After I posted the question, I found my mistake: the definition for columns needs to go directly into the tt_content section:
$GLOBALS['TCA']['tt_content']['columns']['pi_flexform'] = [...]
Unfortunately this means that it will have an effect for ALL tt_content elements, except other plugins will overwrite this again. So the question remains: is there a simple way to extend default content-elements, without writing an own extension nor adding database-fields?
Found an easy solution! Just add the FlexForm XML directly to addPiFlexFormValue() and specify the content-element as 3rd parameter:
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue(
'*',
'<T3DataStructure>
<ROOT>
<type>array</type>
<el>
<customClass>
<TCEforms type="array">
<label>My custom CSS classes</label>
<config type="array">
<type>input</type>
<eval>trim</eval>
</config>
</TCEforms>
</customClass>
</el>
</ROOT>
</T3DataStructure>',
'textpic'
);
Its so wired: wasting hours of trying out, and suddenly after posting the question the solution comes just by itself.

Symfony 2 This form should not contain extra fields

I created a form using formBuilder in Symfony. I add some basic styling to the form inputs using an external stylesheet and referencing the tag id. The form renders correctly and processes information correctly.
However, it outputs an unwanted unordered list with a list item containing the following text: This form should not contain extra fields.
I am having a really hard time getting rid of this notice. I was wondering if someone can help me understand why it being rendered with my form and how to remove it?
Many thanks in advance!
Controller
$form = $this->createFormBuilder($search)
->add('searchinput', 'text', array('label'=>false, 'required' =>false))
->add('search', 'submit')
->getForm();
$form->handleRequest($request);
Twig Output (form is outputted and processed correctly
This form should not contain extra fields.
Rendered HTML
<form method="post" action="">
<div id="form">
<ul>
<li>This form should not contain extra fields.</li>
</ul>
<div>
<input type="text" id="form_searchinput" name="form[searchinput]" />
</div>
<div>
<button type="submit" id="form_search" name="form[search]">Search</button>
</div>
<input type="hidden" id="form__token" name="form[_token]" value="bb342d7ef928e984713d8cf3eda9a63440f973f2" />
</div>
</form>
It seems to me that you have the problem because of the token field. If it is so, try to add options to createFormBuilder():
$this->createFormBuilder($search, array(
'csrf_protection' => true,
'csrf_field_name' => '_token',
))
->add('searchinput', 'text', array('label'=>false, 'required' =>false))
->add('search', 'submit')
->getForm();
To find out the extra field use this code in controller, where you get the request:
$data = $request->request->all();
print("REQUEST DATA<br/>");
foreach ($data as $k => $d) {
print("$k: <pre>"); print_r($d); print("</pre>");
}
$children = $form->all();
print("<br/>FORM CHILDREN<br/>");
foreach ($children as $ch) {
print($ch->getName() . "<br/>");
}
$data = array_diff_key($data, $children);
//$data contains now extra fields
print("<br/>DIFF DATA<br/>");
foreach ($data as $k => $d) {
print("$k: <pre>"); print_r($d); print("</pre>");
}
$form->bind($data);
This message is also possible if you added/changed fields in your createFormBuilder() and press refresh in your browser...
In this case it's ok after sending the form again ;-)
I got the same message while having multiple forms on the same page. Turns out, symfony defaults to the name 'form' for all of them. Instead of using createFormBuilder, you can change the name of the form to avoid conflicts using
public FormBuilderInterface createNamedBuilder(string $name, string|FormTypeInterface $type = 'form', mixed $data = null, array $options = array(), FormBuilderInterface $parent = null)
See https://stackoverflow.com/a/13366086/1025437 for an example.
I ran into this error when creating a multi-step form.
When the step 1 form is submitted, $request->request contains acme_mybundle_myform array. This created a validation error and stopped the back, forward and form fields from populating correctly. Not to mention "this-form-should-not-contain-extra-fields"
I discovered this thanks to the code by nni6.
The solution in my case was inside the controller:
if ($form->isValid())
{
if($form->has('nextStep') && $form->get('nextStep')->isClicked())
{
$session->getFlashBag()->set('notice', 'Next clicked');
$registerType->incrementStep();
$request->request->remove('acme_mybundle_myform');
return $this->forward("AcmeMyBundle:Default:register", array($request));
}
....
}
I had the same error.
It was because I had a form which, by mistake, had a NULL name.
In the HTML, the name attribute would look like this:
<form name href="..." action"..."></form>
As simple as that.
Might not be the case for everyone, but worth to check.

Editable Breadcrumb for Dynamic Pages

Looking for a better Breadcrumb solution for a Zend Framework project.
Currently I have a navigation.xml like
<?xml version="1.0" encoding="UTF-8"?>
<configdata>
<nav>
<home>
<label>Home</label>
<module>default</module>
<controller>index</controller>
<action>index</action>
<pages>
<countryurl>
<label>Spain</label>
<module>default</module>
<controller>country</controller>
<action>index</action>
<route>country_url</route>
<params>
<country>spain</country>
</params>
<pages>
<provinceurl>
<label>Madrid</label>
<module>default</module>
<controller>country</controller>
<action>province</action>
<route>province_url</route>
<params>
<country>spain</country>
<province>madrid</province>
</params>
<pages>
<cityurl>
<label>City</label>
<module>default</module>
<controller>country</controller>
<action>city</action>
<route>city_url</route>
<params>
<country>spain</country>
<province>madrid</province>
<city>madrid</city>
</params>
<pages>
<producturl>
<label>Product</label>
<module>default</module>
<controller>country</controller>
<action>product</action>
<route>product_url</route>
<params>
<country>spain</country>
<province>madrid</province>
<city>madrid</city>
<product>product</product>
</params>
</producturl>
</pages>
</cityurl>
</pages>
</provinceurl>
</pages>
</countryurl>
</pages>
</home>
</nav>
</configdata>
and routes like
$router->addRoute(
'product_url',
new Zend_Controller_Router_Route(':lang/:country/:province/:city/:product', array(
'controller' => 'country',
'action' => 'product'
))
);
$router->addRoute(
'city_url',
new Zend_Controller_Router_Route(':lang/:country/:province/:city', array(
'controller' => 'country',
'action' => 'city'
))
);
$router->addRoute(
'province_url',
new Zend_Controller_Router_Route(':lang/:country/:province', array(
'controller' => 'country',
'action' => 'province'
))
);
$router->addRoute(
'country_url',
new Zend_Controller_Router_Route(':lang/:country', array(
'controller' => 'country',
'action' => 'index'
))
);
I am facing some issues / looking for some suggestions . Creating the Breadcrumbs with the help of Zend_Navigation
$config = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml', 'nav');
$container = new Zend_Navigation($config);
$view->navigation($container);
1 ) The request for http://example.com/en/spain/madrid/madrid/product shows the breadcrumb, with the help of
$this->navigation()
->breadcrumbs()
->setMinDepth(0)
->setLinkLast(true)
->setSeparator(" > ");
as Home > Spain > Madrid > City > Product
But the links pointing at Spain , Madrid , City all are to http://example.com . Which should be http://example.com/en/spain , http://example.com/en/spain/madrid , http://example.com/en/spain/madrid/madrid respectively.
2 ) Currently when the request for http://example.com/en/spain
the breadcrumb will show Home >> Spain
<label>Spain</label>
<module>default</module>
<controller>country</controller>
<action>index</action>
<route>country_url</route>
<params>
<country>spain</country>
</params>
But you can see the param country differs according to country. So do we want to add the labels for all countries ?
http://example.com/en/spain
Home >> Spain
http://example.com/en/india
Home >> India
I have provinces , city and product coming along, any suggestions how I can build for it ?
Also this is a multilingual website, so how can we make the necessary changes to the label? I guess if we are using Zend_Translate it will make the necessary changes.
You can create your own page class that treats params beginning with : as dynamic. You can reference it in the nav configuration like
...
<countryurl>
<label>:country</label> <!-- dynamic -->
<module>default</module>
<controller>index</controller>
<action>index</action>
<route>country_url</route>
<type>DynamicNavPage</type> <!-- page classname -->
<params>
<country>:country</country> <!-- dynamic -->
</params>
<pages>
...
and for example
<?php
class DynamicNavPage extends Zend_Navigation_Page_Mvc {
/**
* Params with ":" are read from request
*
* #param array $params
* #return Zend_Navigation_Page_Mvc
*/
public function setParams(array $params = null) {
$requestParams = Zend_Controller_Front::getInstance()
->getRequest()->getParams();
//searching for dynamic params (begining with :)
foreach ($params as $paramKey => $param) {
if (substr($param, 0, 1) == ':' &&
array_key_exists(substr($param, 1), $requestParams)) {
$params[$paramKey] = $requestParams[substr($param, 1)];
}
}
return parent::setParams($params);
}
/**
* If label begining with : manipulate (for example with Zend_Tanslate)
*/
public function setLabel($label) {
if (substr($label, 0, 1) == ':') {
//label modifications go here
//as an example reading it from page params and capitalizing
//can use Zend_Translate here
$requestParams = Zend_Controller_Front::getInstance()
->getRequest()->getParams();
$labelParamKey = substr($label, 1);
if (array_key_exists($labelParamKey, $requestParams)) {
$label = ucfirst($requestParams[$labelParamKey]);
}
}
return parent::setLabel($label);
}
}

Zend Naviagtion/Menu

Im trying to figure out the best way to keep track of my navigation for a site im working on.
I already use Zend Navigation to render a top menu with drop down submenus. This works fine. However I also want to keep track of my menu items which apply directly to a single item/model which the above menu displays. For instance my menu now.
<nav>
<job>
<label>Jobs</label>
<resource>job</resource>
<controller>job</controller>
<action>list</action>
<pages>
.....
</pages>
<job>
</nav>
This works good for handling "menus" which do not ally to a specific record in database. It is possible to keep track of the "actions" that are carried out on individual records. These extra items should not be rendered inside the main navigation as they only have context with a certain id, but could be rendered as a menu when viewing a record.
so maybe like:
<nav>
<resource>
<menu items to be rendered without specific context>
<possible submenu items>
</possible submenu items>
</menu items to be rendered without specific context>
<menu items to be rendered inside a specific context(like viewings a record)>
<view></view>
<edit></edit>
<invoice></invoice>
</menu items to be rendered inside a specific context(like viewings a record)>
</resource>
</nav>
I was thinking I could have a separate .xml for my context specific menu items but I would like to keep the resources grouped together.
So you want a drill down menu? You can do it easily with CSS. Example: (I prefer arrays but you can adjust).
Create your navigation as you normally would but ensure you set the subitems for each action:
$menu = array(
array(
'label' => 'Add Booking',
'module' => 'booking',
'controller' => 'admin',
'action' => 'addBooking',
'route' => 'default',
'pages'=>array(
array(
'label'=>'test',
'module' => 'booking',
'controller' => 'admin',
'action' => 'anotheraction',
)
)
)
);
CSS:
ul.navigation li ul li {
display:none;
}
ul.navigation li.active ul li {
display: block;
}
As long as you set the parameters for the parent page correctly it will should work fine.