Prestashop 1.5.6 - Add a custom field in Admin Product Page - prestashop

I have been trying to introduce a new custom field in Admin Product page but got a weird error. Actually this field has the same text area type as the existing ones. If I enter a plain text, it works fine but if I enter anything starts with an opening angle bracket (<) it gives me a 403 forbidden error. I already checked permission of folders and files.
This is what I have done.
New db field
ProductCore class. Made changes wherever 'description' is referenced because they are pretty much the same thing.
Information.tpl modified to display this new field.
This is how Admin Product page looks like after the change. As you can see, a plain text can be saved without any issue.
If I type in any html tag, which starts with an angle bracket, then save, I get a 403 error, whereas an existing textarea such as Description in the screen is totally fine.
I have checked permission of files and folders and error logs but couldn't find any clue. Anyone got an idea? Please help!
information.tpl
<tr>
<td class="col-left">
{include file="controllers/products/multishop/checkbox.tpl" field="description" type="tinymce" multilang="true"}
<label>{l s='Description:'}<br /></label>
<p class="product_description">({l s='Appears in the body of the product page'})</p>
</td>
<td style="padding-bottom:5px;">
{include file="controllers/products/textarea_lang.tpl" languages=$languages
input_name='description'
input_value=$product->description
}
<p class="clear"></p>
</td>
</tr>
<tr>
<td class="col-left">
{include file="controllers/products/multishop/checkbox.tpl" field="alternate_item" type="tinymce" multilang="true"}
<label>{l s='Alternate Item:'}<br /></label>
</td>
<td style="padding-bottom:5px;">
{include file="controllers/products/textarea_lang.tpl"
languages=$languages
input_name='alternate_item'
input_value=$product->alternate_item}
<p class="clear"></p>
</td>
</tr>
textarea_lang.tpl
<div class="translatable">
{foreach from=$languages item=language}
<div class="lang_{$language.id_lang}" style="{if !$language.is_default}display:none;{/if}float: left;">
<textarea cols="100" rows="10" id="{$input_name}_{$language.id_lang}"
name="{$input_name}_{$language.id_lang}"
class="autoload_rte" >{if isset($input_value[$language.id_lang])}{$input_value[$language.id_lang]|htmlentitiesUTF8}{/if}</textarea>
<span class="counter" max="{if isset($max)}{$max}{else}none{/if}"></span>
<span class="hint">{$hint|default:''}<span class="hint-pointer"> </span></span>
</div>
{/foreach}
</div>
<script type="text/javascript">
var iso = '{$iso_tiny_mce}';
var pathCSS = '{$smarty.const._THEME_CSS_DIR_}';
var ad = '{$ad}';
</script>

You must override the ProductCore class NOT modify ProductCore class
class Product extends ProductCore
{
public $alternate_item;
public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null)
{
self::$definition['fields']['alternate_item'] = array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml');
parent::__construct($id_product, $full, $id_lang, $id_shop, $context);
}
}
The override class must be placed in override/classes folder or yourmodule/override/classes
When you override a class you must delete cache/class_index.php

Related

Checkbox is not retaining its state while using ViewBag in .net core

I am using ViewBag to store the state of checkbox. It is always showing false. When I remove value="isCurrentFilter"then It is showing enabled when I check the checkbox.
Can anyone guide me where I am going wrong?
Please advise me.
Controller
Public async Task<IActionResult> Index(bool searchText, bool currentFilter, int? page)
{
int selectedPage = page ?? 1;
int bypassCount = (selectedPage - 1) * _pagingOptions.PageSize;
if (searchText != false)
{
page = 1;
}
else
{
searchText = currentFilter;
}
ViewBag.CurrentFilter = searchText;
}
Index.cshtml
<form asp-action="Index" method="get">
#{ bool isCurrentFilter = ViewBag.CurrentFilter; }
<input type="checkbox" asp-for="searchText" value="isCurrentFilter" class="form-control" />
<div class="col-md-12">
<button class="btn btn-primary" type="submit">Search</button>
</div>
</form>
<table class="table">
<thead>
<tr >
<th>Message Id</th>
<th>Status</th>
<th>Resent</th>
<th>Resent Date</th>
<th>Created Date</th>
</tr>
</thead>
<tbody>
#if (Model.Items.TotalItemCount > 0)
{
#foreach (var item in Model.Items.ToList())
{
<td>#Html.DisplayFor(modelItem => item.MessageId)</td>
<td>#Html.DisplayFor(modelItem => item.Status)</td>
<td>#Html.DisplayFor(modelItem => resentString)</td>
<td>#Html.DisplayFor(modelItem => resentDateString)</td>
<td>#Html.DisplayFor(modelItem => createdDateString)</td>
</tr>
}
}
</tbody>
</table>
</div>
using ViewBag to store the state of checkbox. It is always showing false.
If you check the source code of checkbox in browser, you would find that it might be rendered as below, and the value property of checkbox is false, so you always get false even if you checked it and submit the form.
When I remove value="#isCurrentFilter" then It is showing enabled when I check the checkbox.
Without explicitly set value property for your checkbox <input type="checkbox" asp-for="searchText" class="form-control"/>, the value property would be true.
And the rendered HTML as above will also include two fields (checkbox and hidden) for the searchText property. If the checkbox is checked, the posted value will be searchText=true&searchText=false. The model binder will correctly extract true from the value. Otherwise it will be false. SO as you said, removing value="#isCurrentFilter" will work.
You may use #ViewBag.CurrentFilter instead
Like this
<input type="checkbox" asp-for="searchText" value="#ViewBag.CurrentFilter" class="form-control" />

View is posting a empty object on submit when expecting the modified object

I have a View that lets users edit data that is fetched from a database and converted into a DataTable (for simplicity since the data can get really complicated and deep).
The issue is that when I POST the data back to the responsible Controller the controller receives the DataTable object but it's empty, e.g. the changes made by a user never get back to the Controller and cannot be saved to Database.
I am at most intermediate at web programming so I appreciate straight answers or direct pointers.
View:
#model System.Data.DataTable
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_LayoutAdminlte.cshtml";
}
#using (Html.BeginForm("EditSave", "Recipe", FormMethod.Post, new { #id = "Properties-Form" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<h2>#Html.DisplayFor(m => Model.TableName)</h2>
</div>
<div class="box box-primary">
<table id="Properties" class="table table-condensed">
<thead>
<tr>
#foreach (System.Data.DataColumn col in Model.Columns)
{
<th>
#Html.DisplayFor(m => col.Caption)
</th>
}
</tr>
</thead>
<tbody>
#foreach (System.Data.DataRow row in Model.Rows)
{
<tr>
#foreach (var cell in row.ItemArray)
{
<td>
#if (row.ItemArray.ToList().IndexOf(cell) == 0)
{
#cell.ToString()
}
else
{
<input asp-for="#cell" />
}
</td>
}
</tr>
}
</tbody>
</table>
</div>
<div class="box-footer">
<!-- id="Save" -->
<input class="btn btn-primary pull-right" type="submit" value="Spara" id="Save" />
<i class="fa fa-show"></i> Visa
<i class="fa fa-show"></i> Avbryt
</div>
</div>
}
#section Scripts{
#await Html.PartialAsync("_ValidationScriptsPartial")
}
Controller:
[HttpPost]
public ActionResult EditSave(DataTable model)
{
Debugger.Break();
return View("Edit", model);
}
This <input asp-for="#cell" /> is your problem.
If you run your program and use the developer tools to inspect the html generated for the input fields, you will notice the generated html attributes.
the particular one that you should pay attention to is the name attribute.
The way model binding works in .net core when you send data to the controller.
public IActionResult DoSomething(Model model) { ... }
you would have match the name attribute to the property of the object. Example:
<input name="model.Level" value="8999" />
so then you would get var level = model.Level and level will be 8999.
So you have to take care when you use the asp-for it's not doing as much heavy lifting as you think. Always check the html generated.
PS
Don't use DataTables, other developers will throw rocks at you. Don't be lazy map to an actual object or use an ORM, programs quickly become unmaintainable if you use them to dynamically store data.

How do you open a kendo drop down popup on page load?

I have several uses for kendo drop-downs in my application (DDL, ComboBox, etc.).
I want them to open up on page load, but Kendo's documentation doesn't indicate that is possible.
I am using the MVC server variables.
This is my view coding:
<script id="itemTemplate" type="text/x-kendo-template">
# var index=FullName.indexOf(" *****");
if (index > 0)
{
#
<span style="font-weight:bold;">
#: FullName.substring(0, index)#
</span>
#
} else {
#
<span style="font-weight:normal;">
#: FullName#
</span>
#
}
#
</script>
<table class="form-horizontal table-condensed" style="width:100%;">
<tr style="height:400px;">
<td style="width:40%;vertical-align:top;">
<h4 style="width:100%;text-align:center;">Available Members</h4>
<h4 style="width:100%;text-align:center;font-size:smaller;">Current Cancer Center Members are highlighted in Bold.</h4>
#(Html.Kendo()
.MultiSelect()
.Name("AvailableWGMembers")
.DataTextField("FullName")
.DataValueField("id")
.ItemTemplateId("itemTemplate")
.TagTemplateId("itemTemplate")
.BindTo((System.Collections.IEnumerable)ViewBag.AvailableWGMembers)
.AutoBind(true)
.Placeholder("Click here to select one or more members to add, ...")
.AutoClose(false)
.HtmlAttributes(new { style = "width:100%;", #class = "Roles" })
.Events(events => { events.Change("doRoles");})
.Value(new int[0])
.Height(650)
)
</td>
<td style="width:20%;text-align:center;vertical-align:top;">
<input id="btnAdd" type="submit" value="Select" class="btn btn-default" disabled="disabled" />
</td>
<td style="width:40%;vertical-align:top;">
<h4 style="width:100%;text-align:center;">#Model.WGTitle</h4>
<h4 style="width:100%;text-align:center;font-size:smaller;">Current Cancer Center Members are highlighted in Bold.</h4>
#(Html.Kendo()
.MultiSelect()
.Name("ExistingWGMembers")
.AutoBind(false)
.DataTextField("FullName")
.DataValueField("id")
.ItemTemplateId("itemTemplate")
.TagTemplateId("itemTemplate")
.BindTo((System.Collections.IEnumerable)ViewBag.ExistingWGMembers)
.Placeholder("Click here to select one or more members to remove, ...")
.AutoClose(true)
.HtmlAttributes(new { style = "width:100%;", #class = "UnusedRoles" })
.Events(events => { events.Change("doRoles"); })
.Value(new int[0])
.Height(650)
)
</td>
</tr>
</table>
I want the lists to both be open when the page loads, and I want to be able to use unobstrusive jQuery or javascript to control it if I have to.
Does anyone have any suggestions?
It took a little digging, but I finally figured out the answer. It was actually pretty simple.
The following should be added to the unobstrusive javascript code file:
function openPopup(e)
{
if (e.sender.list[0].childNodes['1'].childNodes['0'].childElementCount > 0) {
e.sender.popup.open();
}
}
You add the following code to your event listing:
.Events(events => { ...; events.DataBound("openPopup"); })
This can be done with any of the lists that have popups like Kendo DropDownList or ComboBox or MultiSelect.
I would check for the list length to make sure the list has members so you don't get an ugly empty list shown, but otherwise the result is actually pretty simple.
This answer is dependent upon the code example at: http://demos.telerik.com/aspnet-mvc/window/index
I took that example from the Index.cshtml version of their example and simply replaced the Content value of the # with your table template from the original question:
#(Html.Kendo().Window()
.Name("window")
.Title("Your modal popup with dropdown menus")
.Content(#<text>
<table class="form-horizontal table-condensed" style="width:100%;">
<tr style="height:400px;">
<td style="width:40%;vertical-align:top;">
<h4 style="width:100%;text-align:center;">Available Members</h4>
<h4 style="width:100%;text-align:center;font-size:smaller;">Current Cancer Center Members are highlighted in Bold.</h4>
#(Html.Kendo()
.MultiSelect()
.Name("AvailableWGMembers")
.DataTextField("FullName")
.DataValueField("id")
.ItemTemplateId("itemTemplate")
.TagTemplateId("itemTemplate")
.BindTo((System.Collections.IEnumerable)ViewBag.AvailableWGMembers)
.AutoBind(true)
.Placeholder("Click here to select one or more members to add, ...")
.AutoClose(false)
.HtmlAttributes(new { style = "width:100%;", #class = "Roles" })
.Events(events => { events.Change("doRoles");})
.Value(new int[0])
.Height(650)
)
</td>
<td style="width:20%;text-align:center;vertical-align:top;">
<input id="btnAdd" type="submit" value="Select" class="btn btn-default" disabled="disabled" />
</td>
<td style="width:40%;vertical-align:top;">
<h4 style="width:100%;text-align:center;">#Model.WGTitle</h4>
<h4 style="width:100%;text-align:center;font-size:smaller;">Current Cancer Center Members are highlighted in Bold.</h4>
#(Html.Kendo()
.MultiSelect()
.Name("ExistingWGMembers")
.AutoBind(false)
.DataTextField("FullName")
.DataValueField("id")
.ItemTemplateId("itemTemplate")
.TagTemplateId("itemTemplate")
.BindTo((System.Collections.IEnumerable)ViewBag.ExistingWGMembers)
.Placeholder("Click here to select one or more members to remove, ...")
.AutoClose(true)
.HtmlAttributes(new { style = "width:100%;", #class = "UnusedRoles" })
.Events(events => { events.Change("doRoles"); })
.Value(new int[0])
.Height(650)
)
</td>
</tr>
</table>
</text>)
.Draggable()
.Resizable()
.Width(600)
.Actions(actions => actions.Pin().Minimize().Maximize().Close())
.Events(ev => ev.Close("onClose"))
)
I hope this helps!

Call post edit action from Razor view mvc4

This question might have been asked several times, however it is not working in my case, so please bear with me.
I have the below actions in my controller:
[HttpPost]
public ActionResult Edit(Organization obj)
{
if (ModelState.IsValid)
{
OrgRepo.Update(obj);
return RedirectToAction("Details");
}
else
return View();
}
public ActionResult Edit(int id)
{
return View();
}
I am trying to update the data into database by calling post edit action.
For this purpose, I am calling the edit action as below:
#foreach (var item in Model) {
var test = item.PartyId;
<tr id="#test">
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Caption)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.NameInUse)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Description )"/>
</td>
<td>
#using (Html.BeginForm())
{
#Html.ActionLink("Edit", "Edit", "Org", null, new { #obj = item })
}
</td>
</tr>
However when I click on edit I am getting exception:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Edit(Int32)' in 'Dwiza.Controllers.OrgController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
My questions:
How can I fix this?
Why it get edit action is getting invoked instead of post edit?
What are better ways to invoke edit action among invoke through jQuery, or ajax or any others, if you can suggest better ways of doing it?
The #Html.ActionLink produces an a tag which can only be used to call GET. Change to a submit button to get a POST.
Normally with an Edit, you are only editing a sinble model rather than a collection on a page but going with what you have, change the cshtml to:
#model ICollection<Organization>
<table>
#foreach (var item in Model)
{
using (Html.BeginForm())
{
var test = item.PartyId;
<tr id="#test">
<td class="txt">
<input type="text" name="Caption" class="txt" value="#item.Caption"/>
</td>
<td class="txt">
<input type="text" name="NameInUse" class="txt" value="#item.NameInUse"/>
</td>
<td class="txt">
<input type="text" name="Description" class="txt" value="#item.Description" />
</td>
<td>
<input type="hidden" name="PartyId" value="#item.PartyId"/>
<button type="submit">Edit</button>
</td>
</tr>
}
}
</table>
Now each table row is wrapped by a form meaning the submit button will post that data. The name attribute on the inputs will cause the MVC model binders to bind your posted values to your model correctly.
This hidden input at the end will ensure your PartyId value gets posted back. The fact that it is in int (and not nullable) was giving the exception with your initial code I think.
HTH
EDIT
Adding controller code (note - I still think this is a little/lot strange as you should be editing only the one Organisation...
public ActionResult Edit(int id)
{
// get your organisations from your orgRepo... I'm mocking that out.
var orgs = new List<Organization> { new Organization { PartyId = 1, Description = "Org 1", Caption = "Caption 1", NameInUse = "Name 1"},
new Organization { PartyId = 2, Description = "Org 2", Caption = "Caption 2", NameInUse = "Name 2"}};
return View(orgs);
}
lordy, that is a mess dude. your form only has a link in it, and that link is to the edit action, that will invoke a get, the form will never post back. are you trying to do a form inside a table row?
#foreach (var item in Model) {
var test = item.PartyId;
<tr>
<td colspan ="4>
#using (Html.BeginForm("Edit", "Org", FormMethod.Post))
{
#Html.HiddenFor(modelItem => item.PartyId)
<table>
<tr id="#test">
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Caption)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.NameInUse)"/>
</td>
<td class ="txt">
<input type="text"class="txt" value="#Html.DisplayFor(modelItem => item.Description )"/>
</td>
<td>
<input type="submit" value="edit" />
</td>
</tr>
</table>
}
</td>
</tr>
}
That code will do an edit inside a row, but i am just guessing at the structure from the code you posted.

Dynamically added rows to table does not the get the Dojo style

I am working in ASP.Net MVC4 with Dojo. I have a strange problem when I am adding new rows to html table. I need to have a table which supports adding and removing rows and textboxes inside should have dojo style. I referred the post http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ ad tried to create the same thing. Let me explain my code. Below is my view
#section scripts {
<script type="text/javascript">
require(["dojo/parser", "dijit/form/TextBox", "dijit/form/Button"]);
</script>
}
<table id="servicesTable">
<tr>
<th>
<label id="lblSelectService">
Select</label>
</th>
<th>
<label id="lblServiceName">
Name</label>
</th>
<th>
<label id="lblTitle">
AE Title</label>
</th>
</tr>
#for (int i = 0; i < Model.Services.Count; i++)
{
#Html.Partial("_ServiceRow", Model.Services[i])
}
</table>
My partial view for rendering each row is like below
#model WebUI.Models.RemDevServiceViewModel
<tr>
<td>
#Html.CheckBoxFor(x => x.IsMarked,
new { id = #String.Format("Service[{0}]IsMarked", #Model.ID) })
</td>
<td>
<input style="width: 100px; id="Service[#Model.ID]Name"
name="Service[#Model.ID].Name" type="text"
data-dojo-type="dijit/form/TextBox" value="#Model.Name"/>
</td>
<td>
<input style="width: 100px;" id="Service[#Model.ID]Title"
data-dojo-type="dijit/form/TextBox"
name="Service[#Model.ID].Title" type="text"
value="#Model.Title" />
</td>
</tr>
I have an add button which will add new row to the table. The problem that I am facing is, new row does not get the dojo style and they are rendered as normal textboxes. Initially loaded rows are rendered as dojo style text boxes, but the newly added are not like that. My jquery function to add new rows is like below
function addNewService() {
$.ajax({
url: rootPath + "MyController/AddNewService",
type: "get",
cache: false,
success: function (html) {
$("#servicesTable tbody").append(html);
},
error: function (ts) {
alert("Error while adding services");
}
});
}
Can any one tell me how to solve this? Please note that I am aware about the existence of Dojo grid. I want to take the advantage of model binding and hence following this style
Try running the parser over the added nodes. Are you seriously loading jquery on the page as well? Anyway, with the code you've got, it's kind of tricky to select the just added nodes, but if you put the following inside the success method it might work.
var newNodes = $(html).appendTo("#servicesTable tbody");
require(["dojo/parser"], function(parser){
newNodes.forEach(function(node){
parser.parse(node);
});
});
If you parse over nodes that were already parsed, the parser will throw an error. Also, if you need to support older browsers you'll want to replace the forEach with a standard for loop.
Thanks a lot David for your reply. Your code did not work exactly, but I got the idea and below code is working for me. Please dont feel that I am answering my own question. I want to format the code properly and hence using this view. Once again thanks a lot
success: function (html) {
var newNodes = $(html).appendTo("#servicesTable tbody");
require(["dojo/_base/array", "dojo/parser"], function (array, parser) {
array.forEach(newNodes, function (node, i) {
parser.parse(node);
});
});