I have a requirement to specify some values per-item per-sale. Imagine being able to add a gift message to each item in the basket individually.
How can this be achieved?
I'm using nopCommerce 1.6 (for .net 3.5 compatibility).
I have added three "Product Attributes" (Catalog > Products > Product Attributes). Created a product and in the default product variation, added the three attributes to the product.
The attributes are of type TextBox which, I believe will allow me to enter any value I like as a string.
How do I programatically set these values. From what I can tell ShoppingCartManager.AddToCart looks like it takes a string containing XML for the attributes as the fourth argument:
public static List<string> AddToCart(ShoppingCartTypeEnum shoppingCartType, int productVariantId, string selectedAttributes, decimal customerEnteredPrice, int quantity);
But I can't see anything that explains how the XML should be structured.
Please note: I'm integrating with another CMS so I'm not using the standard nopCommerce controls for the display of the products.
To manually set the value of product attributes on a product variant you can use the helper methods found in :
NopSolutions.NopCommerce.BusinessLogic.Products.ProductManager
NopSolutions.NopCommerce.BusinessLogic.Products.Attributes.ProductAttributeManager
NopSolutions.NopCommerce.BusinessLogic.Products.Attributes.ProductAttributeHelper
NopSolutions.NopCommerce.BusinessLogic.Orders.ShoppingCartManager
(this presumes your project is based on the normal nopCommerce example site.)
The process is fairly straight forward however; I assume the product attributes are of type TextBox in the nopCommerce catalog. This allows any string to be set as the value of the attribute.
Overview of process
Get the product variant, this assumes you already know the product Id and which variant of the product you want (if you have more than one).
Get the attributes for the variant.
Use ProductAttributeHelper to generate your attribute XML string
Save the product to the cart with these attributes.
Example code
private bool SaveProductToBasket()
{
var product = GetTheProduct();
int productId = product.ProductId;
var variants = ProductManager.GetProductVariantsByProductId(productId);
int variantId = GetDesiredVariantId();
var variant = variants[variantId];
var attributes =
ProductAttributeManager.GetProductVariantAttributesByProductVariantId(variant.ProductVariantId);
string data = string.Empty;
data = SetVariantAttribute(data, attributes, "Attribute1", value1.ToString());
data = SetVariantAttribute(data, attributes, "Attribute2", value2.ToString());
data = SetVariantAttribute(data, attributes, "Attributee", value3.ToString());
var addToCartWarnings =
ShoppingCartManager.AddToCart(ShoppingCartTypeEnum.ShoppingCart, variant.ProductVariantId, data, decimal.Zero, 1);
if (addToCartWarnings.Count == 0)
{
return true;
}
// TODO: Bind warnings.
return false;
}
private string SetVariantAttribute(string data, ProductVariantAttributeCollection attributes, string attributeName, string value)
{
var attribute = (from a in attributes
where a.ProductAttribute.Name == attributeName
select a).First();
return ProductAttributeHelper.AddProductAttribute(data, attribute, value);
}
Just to add to this string. The XML for the product attributes look like this...
<Attributes>
<ProductVariantAttribute ID="66">
<ProductVariantAttributeValue>
<Value>484</Value>
</ProductVariantAttributeValue>
</ProductVariantAttribute>
<ProductVariantAttribute ID="67">
<ProductVariantAttributeValue>
<Value>486</Value>
</ProductVariantAttributeValue>
</ProductVariantAttribute>
</Attributes>
Related
I am using Tridion Core Service to get all the Components published from a publication on a specific target. The code is as below.Since there can be lot of components and the result obtained using this code contains all items, Is there any way to narrow down the results (like providing ItemType and recursive as false).
var filter = new PublishTransactionsFilterData
{
PublicationTarget = new LinkToPublicationTargetData { IdRef = targetId },
ForRepository = new LinkToRepositoryData { IdRef = GetPublication(publicationId)},
BaseColumns = ListBaseColumns.IdAndTitle,
};
XElement t= Instance.GetSystemWideListXml(filter);
var v = t.Elements().Where(k => k.Attribute("ItemType").Value == "16");
First of all your code will always return null, as because of ListBaseColumns.IdAndTitle there will be no ItemType attribute. The only way to narrow down the results are properties you can find on PublishTransactionsFilterData, which are:
EndDate, StartDate to search only inside this timestamp
Priority, if you know it
PublishedBy, if you know who published it
All the rest is post filtering, which is also ok. You can filter on everything you have in XML. Sample XML looks like this:
<tcm:Item ID="tcm:0-241-66560" Title="page" Allow="24576" Deny="67108864" Icon="T64L0P0" ItemType="64" ItemID="tcm:2-72-64" State="4" StateChangeDate="2013-03-11T14:53:55" Publication="Test" PublicationTarget="Local" ItemPath="\Test\New Structure Group" Action="0" ScheduleDate="" UserId="tcm:0-11-65552" User="domain\user" Priority="4" Managed="0" />
You can postfilter on any of the attributes
I am working on creating .NET TBB for Tridion 2011 SP1. I have two fields in the component where one is single valued and other is embedded schema field.
To retrieve the single valued field, i have used
string singlefield= package.GetValue("Component.Fields.singlefield");
but to refer multivalued field i have used.
string multi= package.GetValue("Component.Fields.multi.values");
But its not fetching the values.
Please help a way of doing it.
I wouldn't use package.GetValue for any value in a component. Instead, use something like this:
// Get the component
Component c = (Component)engine.GetObject(package.GetByName(Package.ComponentName));
// Get the fields
ItemFields fields = new ItemFields(c.Content, c.Schema);
// get our Embedded schema field
EmbeddedSchemaField emb = (EmbeddedSchemaField)fields["emb"];
// Loop
foreach (ItemFields embeddedfields in emb.Values)
{
foreach (ItemField field in embeddedfields)
{
var tField = field as TextField;
if (tField != null)
{
string something = tField.Value;
}
}
}
You should do something like this:
package.GetValue("Component.Fields.emb.multi[0]");
Where emb is the name of the field in component schema and multi is the multiple value field from your embeddable schema. [0] is field index, but it's optional
Hey all, I was able to do this via a SELECT CASE statement, however I'm always trying to improve my code writing and was wondering if there was a better approach. Here's the scenario:
Each document has x custom fields on it.
There's y number of documents
However there's only 21 distinct custom fields, but they can obviously have n different combinations of them depending on the form.
So here's what I did, I created an object called CustomFields like so:
Private Class CustomFields
Public agentaddress As String
Public agentattorney As String
Public agentcity As String
Public agentname As String
Public agentnumber As String
Public agentstate As String
Public agentzip As String
... more fields here ....
End Class`
Then I went ahead and assigned the values I get from the user to each of those fields like so:
Set All of Our Custom Fields Accordingly
Dim pcc As New CustomFields()
pcc.agentaddress = agent.address1
pcc.agentattorney = cplinfo.attorneyname
pcc.agentcity = agent.city
pcc.agentname = agent.agencyName
pcc.agentnumber = agent.agentNumber
pcc.agentstate = agent.state
pcc.agentzip = agent.zip ....other values set to fields etc.
Now the idea is based upon what combo of fields come back based upon the document, we need to assign the value which matches up with that custom field's value. So if the form only needed agentaddress and agentcity:
'Now Let's Loop Through the Custom Fields for This Document
For Each cf As vCustomField In cc
Dim customs As New tblCustomValue()
Select Case cf.fieldname
Case "agentaddress"
customs.customfieldid = cf.customfieldid
customs.icsid = cpl.icsID
customs.value = pcc.additionalinfo
Case "agentcity"
customs.customfieldid = cf.customfieldid
customs.icsid = cpl.icsID
customs.value = pcc.additionalinfo
End Select
_db.tblCustomValues.InsertOnSubmit(customs)
_db.SubmitChanges()
This works, however we may end up having 100's of fields in the future so there a way to somehow "EVAL" (yes I know that doesn't exist in vb.net) the cf.fieldname and find it's corresponding value in the CustomFields object?
Just trying to write more efficient code and looking for some brainstorming here. Hopefully my code and description makes sense. If it doesn't let me know and I'll go hit my head up against the wall and try writing it again.
If I am reading your question correctly, you are trying to avoid setting the value of fields, when the field isn't used. If so, I would recommend you just go ahead and set the field to nothing in that case.
I have an ASP.NET page which takes a number of parameters in the query string:
search.aspx?q=123&source=WebSearch
This would display the first page of search results. Now within the rendering of that page, I want to display a set of links that allow the user to jump to different pages within the search results. I can do this simply by append &page=1 or &page=2 etc.
Where it gets complicated is that I want to preserve the input query string from the original page for every parameter except the one that I'm trying to change. There may be other parameters in the url used by other components and the value I'm trying to replace may or may not already be defined:
search.aspx?q=123&source=WebSearch&page=1&Theme=Blue
In this case to generate a link to the next page of results, I want to change page=1 to page=2 while leaving the rest of the query string unchanged.
Is there a builtin way to do this, or do I need to do all of the string parsing/recombining manually?
You can't modify the QueryString directly as it is readonly. You will need to get the values, modify them, then put them back together. Try this:
var nameValues = HttpUtility.ParseQueryString(Request.QueryString.ToString());
nameValues.Set("page", "2");
string url = Request.Url.AbsolutePath;
string updatedQueryString = "?" + nameValues.ToString();
Response.Redirect(url + updatedQueryString);
The ParseQueryString method returns a NameValueCollection (actually it really returns a HttpValueCollection which encodes the results, as I mention in an answer to another question). You can then use the Set method to update a value. You can also use the Add method to add a new one, or Remove to remove a value. Finally, calling ToString() on the name NameValueCollection returns the name value pairs in a name1=value1&name2=value2 querystring ready format. Once you have that append it to the URL and redirect.
Alternately, you can add a new key, or modify an existing one, using the indexer:
nameValues["temp"] = "hello!"; // add "temp" if it didn't exist
nameValues["temp"] = "hello, world!"; // overwrite "temp"
nameValues.Remove("temp"); // can't remove via indexer
You may need to add a using System.Collections.Specialized; to make use of the NameValueCollection class.
You can do this without all the overhead of redirection (which is not inconsiderable). My personal preference is to work with a NameValueCollection which a querystring really is, but using reflection:
// reflect to readonly property
PropertyInfo isReadOnly = typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
// make collection editable
isReadOnly.SetValue(this.Request.QueryString, false, null);
// remove
this.Request.QueryString.Remove("foo");
// modify
this.Request.QueryString.Set("bar", "123");
// make collection readonly again
isReadOnly.SetValue(this.Request.QueryString, true, null);
Using this QueryStringBuilder helper class, you can grab the current QueryString and call the Add method to change an existing key/value pair...
//before: "?id=123&page=1&sessionId=ABC"
string newQueryString = QueryString.Current.Add("page", "2");
//after: "?id=123&page=2&sessionId=ABC"
Use the URIBuilder Specifically the link textQuery property
I believe that does what you need.
This is pretty arbitrary, in .NET Core at least. And it all boils down to asp-all-route-data
Consider the following trivial example (taken from the "paginator" view model I use in virtually every project):
public class SomeViewModel
{
public Dictionary<string, string> NextPageLink(IQueryCollection query)
{
/*
* NOTE: how you derive the "2" is fully up to you
*/
return ParseQueryCollection(query, "page", "2");
}
Dictionary<string, string> ParseQueryCollection(IQueryCollection query, string replacementKey, string replacementValue)
{
var dict = new Dictionary<string, string>()
{
{ replacementKey, replacementValue }
};
foreach (var q in query)
{
if (!string.Equals(q.Key, replacementKey, StringComparison.OrdinalIgnoreCase))
{
dict.Add(q.Key, q.Value);
}
}
return dict;
}
}
Then to use in your view, simply pass the method the current request query collection from Context.Request:
<a asp-all-route-data="#Model.NextPageLink(Context.Request.Query)">Next</a>
Say for example if I have a business entity -> Customer, which has customerId, customerName and customerType. I have created an asp:Hidden Variable hdnCustomer to runat="server"
If I wanted to serialize the value of the customer business entity (in the code behind) to the hdnCustomer then how would I do that? Also once serialized how would I deserialize it?
// Pseudo code
Collection<Customer> customerList = new Collection<Customer>();
customerList = BusinessAccess.GetCustomerList();
hdnCustomer = serialize and assign the value of 'customerList' to hdnCustomer;
...
...
// Later on a select index change of one of the drop down lists
Inside the event handler for the drop down list:
{
Collection<Customer> customerList = new Collection<Customer>();
customerList = deserialize the value from hdnCustomer
int a = Convert.ToInt32(ddlDropDown.SelectedValue);
foreach(a in customerList)
{
// Do something
}
}
You can serialise to and from XML using XmlSerializer:
http://support.microsoft.com/kb/815813
However, if you just store the object in the ViewState[] collection that should work better:
ViewState["Customer"] = customerList;
It does the same thing: store the serialisable object in the page, hidden from the user: but it won't be in a human-readable format.
(edit: To deserialise, just get the value of ViewState["Customer"], checking for a null before using it!)
edit 2: a useful link about storing objects in ViewState:
http://www.beansoftware.com/ASP.NET-Tutorials/ViewState-In-ASP.NET.aspx
Hope that helps.
I think .net has already providing some classes to do so, look at this example