How do I programatically copy Taxonomy Terms in Orchard 1.9? - orchardcms

I am trying to programmatically clone content items. The built-in clone() method isn't sufficient, I need to interfere with part of process. The content items I'm trying to clone have a Category Taxonomy Field. I was hoping I could do something along the lines of:
var sourceContentItemCategory = sourceContentItem.ContentItem.Category as TaxonomyField;
var targetContentItemCategory = targetContentItem.ContentItem.Category as TaxonomyField;
targetContentItemCategory.Terms = sourceContentItemCategory.Terms;
But I understand why that doesn't work. Terms are readonly and if I'm not mistaken, every term is also its own content item, so I can't just wholesale stuff that into another taxonomy. What's the easiest way to go about this? Can the TaxonomyService help me make this painless?

I figured out that this can pretty easily be done with the help of the Taxonomy Service:
var sourceTerms = sourceItem.Item.Category.Terms;
_taxonomyService.UpdateTerms(targetItem, sourceTerms, "[Name of Taxonomy Field = Category]");

Related

How to sort the OrderItems in the cart by its category on Broadleaf Commerce?

Broadleaf Commerce site
I need to sort the OrderItems in the cart by its product category. Currently, I am using the comparator to sort the OrderItems.
What is the best or right way to customize the default sort for Cart OrderItems?
I assume that you are currently doing something like this:
List<OrderItem> items = cart.getOrderItems();
Collections.sort(items, comparator);
and I also assume that by "default sort" you mean that you want to change the behavior when you call cart.getOrderItems().
Unfortunately, there is no way to change the behavior of the query that occurs when you call getOrderItems() as this is a JPA-generated query on the OrderItem collection within OrderImpl:
#OneToMany(mappedBy = "order", targetEntity = OrderItemImpl.class, cascade = {CascadeType.ALL})
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region="blOrderElements")
#AdminPresentationCollection(friendlyName="OrderImpl_Order_Items",
tab = TabName.General)
protected List<OrderItem> orderItems = new ArrayList<>();
Because of this you really only have 3 options:
Override the OrderImpl class and change the implementation of the getOrderItems() method. Unless you really needed to extend this (like to add another column to BLC_ORDER) then this isn't a great solution since it will add another table to the database that isn't actually used. See the Broadleaf extending entities tutorial. You can also technically avoid the multi-table inheritance by switching to single-table inheritance which is detailed at the bottom of that doc
Add your own query somewhere in a service method that manually queries for OrderItem for the order with a JPA query
Do what you are currently doing and sort the order items manually after retrieval with your comparator. You might consider adding a Thymeleaf utility method to have it available to you in your templates

How can I read the translation labels from profile documents in XPages?

I am xpages enabling an old Notes application which is using profile documents to store translated labels. The translated lables in the notes form are read from the profile document using #GetProfileField depending on which language the user have selected in their profile.
I have read that profile documents are not recommended to use with xpages so I need a better solution for my xpages users. but it is important that users using Notes client still use the "old" profile document solution.
How can I provide these translation lables to my xpages users?
Thanks
Thomas
In addition to Knut's answer there is also the option to "double" your translated labels via the way to prefer in XPages dev by using the localization options as described here: http://www-10.lotus.com/ldd/ddwiki.nsf/dx/UsingLocalizationOptions.htm
You need to split the task into two. First have a function that is called inside the XPage to get the label you are looking for, secondly have a way to provide that value inside the function.
Making a direct call to the profile isn't a good idea since it fixes the way you provide the data (besides potentially creating a memory leak if you don't recycle dilligently). I would see 4 potential solutions:
Define your profile document as additional data source and simply bind the labels to items in the document. Saves you most of the recycling work, but couples tight
Use a SsJS function: getLabel(name). It would check for a scope variable (a Map) and if not found load it - currently from your profile. If application scope is good enough, you touch the profile once only- speed. If you change the loader later on - you don't need to change anything in the XPage.
Use a managed bean. Same approach as #2, only now you can use el data binding. Your bean needs to implement Map
If the labels hardly change do a design time conversion and write the profile doc out into properties files (works nicely with ODP) and use XPages internal mechanism for internationalization
Let us know how it goes
You can use profile documents for this use case as the content gets changed only with new versions of your project probably. So, you can easily live with profile document's caching.
You get the label translation from a profile document with
var doc = database.getProfileDocument("LabelsEnglish", "");
var label = doc.getItemValueString("label1");
doc.recycle();
return label;
You could read all labels in an application scope variable Map too and do your own caching. This way profile documents would get read only once.
if (!applicationScope.labels) {
var map = new java.util.HashMap();
var doc = database.getProfileDocument("LabelsEnglish", "");
var allItems = doc.getItems();
for (var i = 0; i < allItems.size(); i++) {
var item = allItems.elementAt(i);
item.getName();
map.put(item.getName(), item.getValueString());
item.recycle();
}
doc.recycle();
applicationScope.labels = map;
}
Execute the SSJS code above in a custom control which is included in every XPage (e.g. application layout custom control) in before page load event so you can be sure application scope variable "labels" is initialized when you want to use it. You can access the labels easily with EL
applicationScope.labels.label1

Orchard getting the field from type ContentItem

I have a content item called Event, which has a taxonomy field called Section attached via the content definition area.
What is the easiest way to retrieve the Section field from the content within an alternate? My alternate is not overriding an Event, so Model.ContentItem is not possible. Within my alternate my Event object instance is of type ContentItem which I am retrieving through the ContentManager.
This is what i'm doing at the moment:
ContentItem content = WorkContext.Resolve<IContentManager>().Get(id);
var = content.Parts.ElementAt(13).Fields.ElementAt(0);
I realise that in the above code the index's could change, the only other way I can think of doing this is by inserting Lambda expressions in the place of the integers.
content.Parts.ElementAt(13) returns object of type ContentPart
content.Parts.ElementAt(13).Fields.ElementAt(0) returns object of type TaxonomyField. Whereas I believe I need the TermPart?
If it cannot be achieved in a simple way, why is it so difficult to perform such a simple task?
Thank you in advance.
First you do not need the ContentManager on the template.
On the Model you have the ContentItem. You can retrive the field like this:
var contentItem = Model.ContentItem;
var terms = contentItem.Event.TaxonomyFieldName.Terms;
On terms you have the terms of the ContentItem.

New ContentItem from code, when is it saved?

I can't get my head around this.
If I have a ContentType called Contacts. The ContentType has two fields attached to it.
FirstName (textfield) and LastName (textfield).
If I want to create a new contentitem of this type then I can write code like this.
dynamic contact = _services.ContentManager.New("Contacts");
contact.Contacts.FirstName.Value = "John";
contact.Contacts.LastName.Value = "Doe";
_services.ContentManager.Create(contact, VersionOptions.Published);
This does not work. The Contentitem gets created but the fields are empty.
However, if I write it like this it works. Why is that? Must I set the fields values after ContentManager.Create is called?
dynamic contact = _services.ContentManager.New("Contacts");
_services.ContentManager.Create(contact, VersionOptions.Published);
contact.Contacts.FirstName.Value = "John";
contact.Contacts.LastName.Value = "Doe";
What you observed is indeed the intended behaviour and is by design. I've come across this before too and also created an issue about it. As you can see there it was closed by Sebastien, the lead developer stating that this is by design but unfortunately not explaining why.
FYI the standard workflow for managing content items is the following:
Instantiate item.
Create it.
Update its values.
If the update happened through the model binder then check if the ModelState is valid. If not, cancel the transaction and return.
If everything's OK and the content type is set to be draftable, publish the item.
You can see an explained example of this in the Orchard Training Demo module (ContentsAdminController.PersonListDashboardPost()).

Programmatically adding columns to Sharepoint List Views

For a project I am working on, I am trying to do the following thing.
There's a Sharepoint 2010 environment, with a few Custom Lists created in Visual Studio. I am adding some fields to them using background code in the FeatureActivated function in the EventReceiver.
What I am doing there is adding a lookup field to the Sharepoint List, and setting the properties to allow it to lookup values from the other list I am adding to the Sharepoint Site.
However, I can't find a function to add it to one of the views. I've tried modifying the Schema Xml, but I can't seem to find a function to reinsert it to the List, and when using the Xml file from the View, I can't seem to make it work.
Is there an easy way to programatically add a field to a view? This would help me out, since there seems to be no way to do this correctly.
This can also be solved if one could explain my other question I have.
I would like to know how one could make Lookup fields in the Schema XML file. I have a Custom Content Type, and Custom Fields, and I am currently trying to look up the Naam field in the Intermediairs List. (This one is also created when deploying this solution). When searching Google, it seems I have to use either a name / the GUID of a List Instance here, but I don't know the GUID of the List Instance beforehand.
<Field ID="{7CC49D9D-F6F5-4A4A-851F-3152AAAAB158}" Type="Lookup"
List="Intermediairs" Name="IntermediairLookup" DisplayName="Intermediair"
StaticName="IntermediairLookup" Group="Onboarding" ShowField="Naam" />
You should know that this code seems to work:
SPWeb web = null;
SPSite site = null;
if (properties.Feature.Parent is SPWeb)
{
web = properties.Feature.Parent as SPWeb;
site = web.Site;
}
if (properties.Feature.Parent is SPSite)
{
site = properties.Feature.Parent as SPSite;
web = site.RootWeb;
}
web.AllowUnsafeUpdates = true;
SPList changeList = web.Lists.TryGetList("Onboarding");
SPList sourceList = web.Lists.TryGetList("Intermediairs");
if (changeList != null && sourceList != null)
{
changeList.Fields.Delete("IntermediairLookup");
var PrimaryColumnStr = changeList.Fields.AddLookup("Intermediair", sourceList.ID, true);
var PrimaryColumn = changeList.Fields.GetFieldByInternalName(PrimaryColumnStr) as SPFieldLookup;
PrimaryColumn.LookupField = sourceList.Fields["Naam"].InternalName;
PrimaryColumn.Update();
}
But yeah. I can't figure out how to do it in XML form. Anyone has any ideas? A solution to either of the questions would solve my core issue.
Greetings,
Mats
EDIT: Well, the question has now been answered, thanks again!
One thing though. I would really like to know at some point how to do something like this in XML / CAML. Does anyone know how to do that? Anyone who's still reading this thread?
Take a look at SPView.ViewFields
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spview.viewfields.aspx

Resources