What is the background of allowAllProperties, why is it necessary here? - typo3

I have a form with a parameter that is mapped to an object storage. In a template I have form that sets this parameter with a list of checkboxes. Heres an overview of the action in the controller and the template.
Controller
/**
* action list
* #param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Redacted\Productfinder\Domain\Model\Category> $categories
* #return void
*/
public function searchAction($categories=NULL) {
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump(func_get_args());
...
}
Template
<f:for each="{categories}" as="option">
<li class="category-{option.uid}" data-filter="{option.uid}">
<label for="checkbox-category-{option.uid}">
<f:form.checkbox name="categories" multiple="true" id="checkbox-category-{option.uid}" value="{option.uid}" checked="{x:condition.iterator.contains(haystack:'{filter.categories}', needle:'{option}', then:'checked')}" />
<span class="title">{option.title}
</label>
</li>
</f:for>
This, so far, works like a charm. I'm now trying to set the $categories parameter with a link, but that doesn't work. I have a f:for loop with the categories.
If I set the Parameter to the single object like so, it is ignored. No error message, Extbase just ignores the value.
<f:link.action action="list" arguments="{categories:category}">{category.title}</f:link.action>
If I set the parameter to an array with that value, like so
<f:link.action action="list" arguments="{categories:{0:'{category}'}}">{category.title}</f:link.action>
Extbase tells me, that the Parameter cannot be mapped.
#1297759968: Exception while property mapping at property path "": It is not allowed to map property "0"
I've managed to figure out, that I can tell Extbase to allow the property mappging, and then resolve the raw uids into extbase objects, like so
$propertyMappingConfiguration = $this->arguments->getArgument($property)->getPropertyMappingConfiguration();
$propertyMappingConfiguration->allowAllProperties();
I'd like to understand the background of this, why is this necessary to allow this for GET parameters, when it just works for POST? Am I doing something wrong, or is this by design?

This is indeed by design. If you use <f:form.textfield..... it isn't needed to set it expliciet like you do.
This is to prevent that unwanted items are past/persisted because someone changed the form manually.
So it's a security thing

Related

angularjs2: setting fields to "dirty", especially datepicker

I am new to Angular2/Typescript, since I come from the Java world, I decided to learn Typescript and Angular2 directly.
I want to leave most of the logic on the server, thus I don't need complex validation management on the client. So all I want is the user to fill out forms, and post/put all the fields to the REST Service.The goal is to leave the client side as light as possible.
I have a form:
<form role="form" (ngSubmit)="onSubmit()" #ArbeitstagForm="ngForm">
and a field in it, some datepickers too: similar like this:
<input type="text" class="form-control pull-right" id="datepicker" [(ngModel)]="model.datum">
When I submit the form, I call this function:
model = new Arbeitstag();
onSubmit(form:any) {
alert(JSON.stringify(this.model));return false;
}
So that alerts me the the entered data as JSON, which I will after send to a REST Service. It works actually great, BUT only when I actually type something into the field, when I have a default value, or I set the field with a datepicker, the model object values will remain empty.
I've found out about the dirty setting of the fields, which are false by default and are getting true when I type something in and that's also what I see when I check firebug, but that's definitely not what I want to achieve.
Is there a way to set all the fields dirty in a form in Angular2? I've found many examples for Angular.js 1, but not for Angular2/Typescript.
Control has a markAsDirty() (and markAsTouched()) method
<input #datePicker="ngForm" type="text" class="form-control pull-right" id="datepicker" [(ngModel)]="model.datum">
<button (click)="datePicker.control.markAsDirty()">update dirty status</button>
Plunker example
What I usually do is get a reference to the form in my component, using ViewChild. With that reference I can mark to form dirty or touched when I need to. Like so:
export class MyComponent implements OnInit, OnDestroy {
#ViewChild('form') form: NgForm;
...
public methodWithFormChange(): void {
this.form.control.markAsDirty();
}
}
;-)

TYPO3 merge list and edit

I've got a TYPO3 backend module which lists a lot of elements. Now, I want to include in my list the edit form, but that doesn't work well at the moment.
Rendering is good, but if I send the form, I get the error:
Required argument "note" is not set.
My code looks like this:
<f:for each="{notes}" as="note">
<f:form action="update" name="note" object="{note}">
<textarea class="form-control gettooltip" rows="1" placeholder="Kommentar" title="Kommentar zur Note">{note.kommentar}</textarea>
</f:form>
</f:for>
How can I merge these two views correctly?
Your code cannot work because your textarea doesn't have a property (or you don't use the <f:form.textarea ViewHelper).
If you property map $note in your controller, the property must be passed to Fluid with the prefixed extension name and plugin name. This is done automatically when using the "property" argument of the textarea ViewHelper. The name attribute will then be:
<textarea name="tx_myext_myplugin[note]"...
Thîs will map to $note in the controller.
So if you don't use the ViewHelper, you need to manually prefix the name attribute to create an output like printed just above.
If you're planning to update multiple objects of the of the same kind in one request, this won't because because there is an Extbase limitation.
You could do the following:
Use a submit button for each note and save/reload the changes through AJAX.
<f:for each="{notes}" as="note">
<f:form action="update" name="note" object="{note}">
<f:form.textarea class="form-control gettooltip" placeholder="Kommentar" property="kommentar">{note.kommentar}</f:form.textarea>
<f:form.submit value="Update" />
</f:form>
</f:for>
Then you intercept the submit click, submit the form through AJAX and set the new content to the textarea.
If you want to have one form for all objects, you will need to prefix the fields
<f:form action="update" name="note">
<f:for each="{notes}" as="note">
<f:form.textarea class="form-control gettooltip" placeholder="Kommentar" name="note[note{note.uid}][kommentar]">{note.kommentar}</f:form.textarea>
</f:for>
<f:form.submit value="Update" />
</f:form>
You will then have an array of values and need to iterate in your controller and manually persist the changes.
For your problem - as #lorenz answered you need to use viewhelpers for rendering fields OR at least use valid name attributes for your fields...
Anyway, I'm wondering why do you want to reinvent the wheel - especially while creating BE modules, the fastest, easiest and most elegant way is... using TYPO3 forms. They handle many things, relations, localization, validation, RTE etc, etc. What's more you can also add own type of field to TCA and process with your own PHP and JS - very rare situation, but may be used i.e. for adding GoogleMap field,
#see: user type in TCA
Finally all you need to open the record from your BE module is creating proper link - which can be easily copied from List module (right click on the yellow pencil next to your record and copy the code), sample:
<a href="#" onclick="window.location.href='alt_doc.php?returnUrl='+T3_THIS_LOCATION+'&edit[fe_users][1234]=edit'; return false;" title="Edit user">
<span title="" class="t3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-open"> </span>
</a>
Where fe_users is table name and 1234 is record uid.
alt_doc.php?returnUrl='+T3_THIS_LOCATION part handles returning to the place from which edit was started, so it will be your module again including all GET params selected by admin before editing.
For creating new user
<a href="#" onclick="window.location.href='alt_doc.php?returnUrl='+T3_THIS_LOCATION+'&edit[fe_users][6789]=new'; return false;" title="New record">
<span class="t3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-new"> </span>
</a>
In this case 6789 is a PID (uid of the page where the user should be created...
You can even set some default values when creating records from your own module using params in your new link:
&defVals[table_name][field_name]=value
sample
<a href="#" onclick="window.location.href='alt_doc.php?returnUrl='+T3_THIS_LOCATION+'&edit[fe_users][6789]=new&defVals[fe_users][tx_extbase_type]=Tx_MyExt_People&defVals[fe_users][usergroup]=1'; return false;" title="New record">
<span class="t3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-new"> </span>
</a>

FLUID ViewHelper form in new window

I would like to add the target attribute to a FLUID form, so that after submitting the form, a new window is created.
This does not work, and produces an error:
<f:form target="_blank" action="..." name="..." id="..." pageUid="..." controller="..."></f:form>
Any ideas on how to make the to open in a new window?
I use TYPO3 6.2beta5
As the f:form ViewHelper inherits from the AbstractTagBasedViewHelper, it shares the same allowed attributes.
The fluid documentation inside the TYPO3 Flow documentation shows you all allowed attributes.
To answer your question, the correct way to use it would be:
<f:form additionalAttributes="{target:'_blank'}">FORMCONTENT</f:form>
Note that the additionalAttributes argument is an array. If you were to add more than 1 custom attribute, you would do it like that:
<f:form additionalAttributes="{target:'_blank', data-validate: 'foo'}">FORMCONTENT</f:form>
EDIT
The AbstractTagBasedViewHelper changed, so the answer as of today for TYPO3 CMS v7 would be: There's an attribute data for that which takes an array of keys and values.
<f:form data="{foo: 'bar', validate: 'baz'}" ....>
FORMCONTENT
</f:form>

MVC3 and Razor - How to place a dynamic value for hidden field?

I'm a beginner about Razor, and sometimes I get stuck with really simple things.
I have this foreach loop:
#foreach (dynamic item in ViewBag.EAList)
{
<li>
#using (#Html.BeginForm("Duplicate, "Daily"))
{
<p>#item.AuthorComment</p>
#Html.Hidden("EstadoDeAlmaID", #item.EAID)
#Html.Hidden("PosterID", Session["id"].ToString())
<input type="submit" value="Send" />
}
</li>
}
This line:
#Html.Hidden("EstadoDeAlmaID", #item.EAID)
Doesn't work, and I don't know how to make it work, I tried many ways, without #, with (--), with #(--)...
Could someone help me to display the dynamic value in my hidden field?
In addition, if someone know about a good Razor samples websites, I would be very thankful.
I had the same problem, found that a simple cast solved my problem.
#Html.Hidden("id", (string) ViewBag.ebook.isbn)
In Razor, once you are in "C# land", you no longer need to prefix values with # sign.
This should suffice:
#Html.Hidden("EstadoDeAlmaID", item.EAID)
Check out Scott Gu's article covering the syntax for more help.
Update
And I would also move your <li></li> within your using block, as Razor works better when you wrap HTML blocks inside of a code blocks.
Also, your Html.BeginForm should live outside of your loop.
#using (#Html.BeginForm("Duplicate, "Daily"))
{
<ul>
#foreach (? item in ViewBag.EAList)
{
<li>
<p>#item.AuthorComment</p>
#Html.Hidden("EstadoDeAlmaID", item.EAID)
#Html.Hidden("PosterID", Session["id"].ToString())
<input type="submit" value="Send" />
</li>
}
</ul>
}
Where ? in the foreach loop is the type of your items in EAList.
To avoid the Extension methods cannot be dynamically dispatched exception, use a model instead of ViewBag so you will not be using dynamic objects (this will avoid all the unnecessary casting in the View and is more in line with MVC style in general):
In your action when you return the view:
return View("ViewName", db.EAList.ToList());
In your view, the first line should be:
#model IEnumerable<EAListItem> //or whatever the type name is
Then just do:
#foreach(var item in Model)
You got the error, "Extension methods cannot be dynamically dispatched"... therein lies your trouble.
You should declare you loop variable not to be of type dynamic, but of the actual type in the collection. Then remove the # from the item.EAID call inside the #Html.Hidden() call.
The simple solution for me was to use ViewData instead of ViewBag. ViewBag is just a dynamic wrapper around ViewData anyway.
#Html.Hidden("ReportID", ViewData["ReportID"])
but I don't know if this will help in your case or not since you are creating dynamic items in your foreach loop.
I have found that when i want to use the view bag data in the HTML
Getting back to basics has often worked for me
<input type="hidden" name="Data" id="Data" value="#ViewBag.Data" />
this gave the same result.

Passing Complex object from View to Controller: one object is always null

I'm passing a complex object as a Model to the View as
but when I get the Model back from the View, one particular object comes always null while other complex types are normally passed through
my View is the default Edit Strongly Typed View
What am I missing?
The ModelState Error says
The parameter conversion from type 'System.String' to type 'Julekalender.Database.CalendarInfo' failed because no type converter can convert between these types.
Why don't I get the same for the other types? How is it automatically converted?
I have added 3 fields (as the T4 template does not append this types) but I still get null when POSTing
The green boxed below is the field
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Calendar.Guid)%>
</div>
Even renaming the Action to
[HttpPost]
public ActionResult General2(GeneralInfo model)
gives the same error
Make sure that when you use this wizard there are input fields generated in the view for each property of the Calendar object so that when you post the form they will be sent to the controller action. I am not sure this is the case (haven't verified if the wizard does it for complex objects, I've never used this wizard).
In the resulting HTML you should have:
<input type="text" name="Calendar.Prop1" value="prop1 value" />
<input type="text" name="Calendar.Prop2" value="prop2 value" />
... and so on for each property you expect to get back in the post action
... of course those could be hidden fields if you don't want them to be editable
UPDATE:
The problem comes from the fact that you have a string variable called calendar in your action method and an object which has a property called Calendar which is confusing. Try renaming it:
[HttpPost]
public ActionResult General2(string calendarModel, GeneralInfo model)
Also don't forget to rename it in your view.