I've been working on a .NET C# TBB to retrieve fields from a schema, which in turns parses through every embedded field. I use Razor Mediator in my templates to output JSON. I'm very new to C# and .NET and I'm therefore using and referring code my colleagues wrote in other templates.The goal of the TBB is to retrieve, after publish, the exact url of an image within one of these embedded fields, and push it back to the package as plain text.
This eventually seemed to work, up until I changed one of the fields of the embedded schema's to a multimedialink. As soon as I set the Embeddedschemafields to loop through, template builder serves an error with the o so descriptive
Operation is not supported on a new item or on a null URI.
I have no clue why this is happening, since the code actually worked . That's why I believe changing one of the embedded schema's fields could be the culprit, but the error occurs even before looking this particular field up. my code stops at retrieving the component from the embeddedschemafield:
CM.Schema schema = (engine.GetObject(comp.GetAsSource().GetValue("ID")) as CM.Component).Schema;
ItemFields fields = new ItemFields(schema);
foreach (ItemField field in fields)
{
_log.Info("top level: " + field.Name);
if (field is EmbeddedSchemaField)
{
CM.Component c = (CM.Component)engine.GetObject(package.GetByName(Package.ComponentName));
_log.Info(" c = " + c + " content: " + c.Content + " schema: " + c.Schema);
ItemFields content = new ItemFields(c.Content, c.Schema);
EmbeddedSchemaField embeddedFields = (EmbeddedSchemaField)content[field.Name]; //boom
MultimediaLinkField mmValue = null;
ItemFields currentFields = null;
foreach (ItemFields embeddedField in embeddedFields.Values)
{
Anyone have an idea?
This is the log I got from template builder:
GetComponentImageUrl: top level: name
GetComponentImageUrl: top level: division
GetComponentImageUrl: top level: theme
GetComponentImageUrl: top level: product
GetComponentImageUrl: field.Name = Embeddedschemafield
GetComponentImageUrl: c = Component tcm:11-1963 content: System.Xml.XmlElement schema: Schema tcm:11-1842-8
GetComponentImageUrl: c. Content = System.Xml.XmlElement
Engine: Error in Engine.Transform
Engine: Error in Engine.Transform
Operation is not supported on a new item or on a null URI.
at Tridion.ContentManager.Session.GetTcmUri(String uri)
at Tridion.ContentManager.Session.GetObject(String uri)
at Tridion.ContentManager.Session.GetObject(XmlElement linkElement)
I would do the following:
CM.Schema schema = (engine.GetObject(comp.GetAsSource().GetValue("ID")) as CM.Component).Schema;
ItemFields fields = new ItemFields(schema);
foreach (ItemField field in fields)
{
_log.Info("top level: " + field.Name);
if (field is EmbeddedSchemaField)
{
CM.Component c = (CM.Component)engine.GetObject(package.GetByName(Package.ComponentName));
_log.Info(" c = " + c + " content: " + c.Content + " schema: " + c.Schema);
ItemFields content = new ItemFields(c.Content, c.Schema);
if (content != null && content.Contains(field.Name)) {
EmbeddedSchemaField embeddedFields = (EmbeddedSchemaField)content[field.Name]; //boom
MultimediaLinkField mmValue = null;
ItemFields currentFields = null;
foreach (ItemFields embeddedField in embeddedFields.Values)
{
Notice the added check that the ItemFields object is created, and that it contains the field you're looking for.
Related
I am creating an app using Google AppMaker and wish to create a boolean search option (text box where I can use AND/OR options to search multiple combinations of strings). This is similar to how I can search currently on Gmail or LinkedIn etc. How can I create it?
Here is one way to accomplish this:
Make a new datasource for the model you want to search in.
Select Query Script as the query type.
Add a string parameter called searchCriteria
Add a bunch of parameters called parameter0, parameter1, parameter2, etc.
Enter this code as the Server Script (assuming the field you want to search in is called Name):
var searchCriteria = query.parameters.searchCriteria;
if(searchCriteria === null) return query.run();
var searchArray = searchCriteria.split(/( and | or )/i);
var searchString = '';
for(var i = 0; i < searchArray.length; i++){
if(i % 2 === 0 ){
searchString += 'Name contains :parameter' + i.toString() + ' ';
query.parameters['parameter' + i.toString()] = searchArray[i];
}else{
searchString += searchArray[i];
}
}
query.where = searchString;
return query.run();
Then on your page bind the text box you want to search with to #datasource.query.parameters.searchCriteria
Finally modify the onValueEdit of the search box to Reload Datasource
This technique is limited by how many numbered parameters you create and doesn't allow using parenthesis.
I am currently using the following code to search within Tridion. It is fetching the Items (Components and Pages) based on the input.
Question: In rich text field we have Design,Source and Preview tabs. The below code is searching for content present in Design tab only. I need the Source tab content also
to be considered while performing search.
CoreServiceSession client = new CoreServiceSession();
SessionAwareCoreServiceClient csClient = client.GetClient();
var find = new SearchQueryData
{
Description = "Universe"
ItemTypes = new ItemType[] { ItemType.Page, ItemType.Component }
);
IdentifiableObjectData[] foundItems = csClient.GetSearchResults(find);
Did you try with FullTextQuery
CoreServiceSession client = new CoreServiceSession();
SessionAwareCoreServiceClient csClient = client.GetClient();
ReadOptions readoption = new ReadOptions();
var find = new SearchQueryData
{
Description = "Universe"
FullTextQuery= "Universe"
ItemTypes = new ItemType[] { ItemType.Component }
);
IdentifiableObjectData[] foundItems = csClient.GetSearchResults(find);
As FullTextQuery can impact on your CMS performance, you may want it to restrict to particular schema components field only.
BasedOnSchemaData basedSchemaNote = new BasedOnSchemaData();
basedSchemaNote.Schema = new LinkToSchemaData() { IdRef = "tcm:XX-xxxx-8" };
basedSchemaNote.Field = "FieldName";
basedSchemaNote.FieldValue = "*SeachText*";
I would like to set the Multimedia link fied for the component metadata using core service.
I am trying like below, am getting xml validation error. could you pleaes help on this?
ComponentData comp = client.Read(compid, readoption) as ComponentData;
comp = client.TryCheckOut(compid, readoption) as ComponentData;
string newxml = #"<Metadata xmlns=""uuid:5880d67f-13f7-4632-8c33-dcfd9c1437ed"">
<meta>
<mmlink>tcm:22-5678</mmlink>
</metad>
</Metadata>";
comp.Metadata = newxml;
client.Save(comp, readoption);
client.CheckIn(comp.Id, readoption);
You should set xlink:href like here:
<mmlink xlink:type="simple" xlink:href="tcm:2-146"
xmlns:xlink="http://www.w3.org/1999/xlink"></mmlink>
The easiest way to solve problems like this is to create a component schema with the field in question and corresponding component. You will then find the answer by exploring component XML
The approach for Multimedia Links is the same than for Component Links. And it also applies for both, content and metadata fields: This example is setting a mm component link in a folder metadata where the md schema constains a embeddable field called "versioned_component" containing a field called "component" which is a multimedia component link field:
this.OpenSession();
try
{
//itemUri is the MM Component uri
var currentItem = (ComponentData)session.Read(itemUri, new ReadOptions());
LinkToRepositoryData ltrd = currentItem.LocationInfo.ContextRepository;
var pd = (PublicationData)session.Read(ltrd.IdRef, new ReadOptions());
String currentPublicationWebdavURL = pd.LocationInfo.WebDavUrl;
String schemaUri = string.Format(FOLDER_MD_SCHEMA_WEBDAVURL,
HttpUtility.UrlDecode(currentPublicationWebdavURL));
//schemaUri = HttpUtility.UrlEncode(schemaUri);
var sd = (SchemaData)session.Read(schemaUri, new ReadOptions());
FolderData folder = new FolderData();
folder.Id = TcmUri.UriNull;
folder.Title = "hidden_" + Guid.NewGuid().ToString();
var rootFolder = (FolderData)session.Read(
currentItem.LocationInfo.OrganizationalItem.IdRef,
new ReadOptions());
folder.LocationInfo= new LocationInfo()
{
OrganizationalItem = new LinkToOrganizationalItemData(){
IdRef = rootFolder.Id
}
};
folder.MetadataSchema = new LinkToSchemaData()
{
IdRef = sd.Id,
};
string sMetadata = "<Metadata xmlns=\"{0}\" xmlns:xlink=\"{1}\"> "
+ " <version_component>"
+ " <component xlink:type=\"simple\" "
+ " xlink:href=\"{2}\" xlink:title=\"{3}\" />"
+ " </version_component> "
+ "</Metadata>";
sMetadata = string.Format(sMetadata, sd.NamespaceUri,
Tridion.Constants.XlinkNamespace,
currentItem.Id.ToString(), currentItem.Title);
folder.Metadata = sMetadata;
folderUri = session.Save(folder, new ReadOptions()).Id.ToString();
return folderUri;
}
finally {
this.CloseSession();
}
Hope this helps too,
I am trying to retrieve the Binary Url of a multimedia component's file that is published as a dynamic Component Presentation.
I can see the Url in the Binaries table within the Broker database but I can't seem to get the binary url using either of the following bits of code:
using SQLBinaryMetaHome:
using (var sqlBinMetaHome = new Com.Tridion.Broker.Binaries.Meta.SQLBinaryMetaHome())
{
int componentItemId = int.Parse(queryStringId.Split('-')[1]);
var binaryMeta = sqlBinMetaHome.FindByPrimaryKey(new TCDURI(publicationId, 16, componentItemId));
if (binaryMeta != null)
{
VideoBinaryUrl = binaryMeta.GetURLPath();
}
else
{
Logger.Log.ErrorFormat("Failed ot load via SQL Binary Meta {0}", queryStringId);
}
}
Using Binary Meta factory:
using (var b = new BinaryMetaFactory())
{
var binaryMeta = b.GetMeta(queryStringId);
if (binaryMeta != null)
{
VideoBinaryUrl = binaryMeta.UrlPath;
}
else
{
Logger.Log.ErrorFormat("Failed to load binary meta {0}", queryStringId);
}
}
I can load the Component Meta data using the ComponentMetaFactory.
Any ideas on why I can't load the Binary Meta? Am I on the right track?
Rob
It looks like your first example is importing (auto-generated) methods from an internal DLL (Tridion.ContentDelivery.Interop.dll). Please don't use those and stick to the ones in the Tridion.ContentDelivery namespace (Tridion.ContentDelivery.dll).
You can find the official documentation for the Content Delivery .NET API in CHM format on SDL Tridion World (click the link, log in to the site and click the link again). From that documentation comes this example:
//create a new BinaryMetaFactory instance:
BinaryMetaFactory binaryMetaFactory = new BinaryMetaFactory();
//find the metadata for the specified binary
BinaryMeta binaryMeta = binaryMetaFactory.GetBinaryMeta("tcm:1-123");
//print the path to the output stream:
if(binaryMeta!=null) {
Response.Write("Path of the binary: " + binaryMeta.UrlPath);
}
//Dispose the BinaryMetaFactory
binaryMetaFactory.Dispose();
The factory class is Tridion.ContentDelivery.Meta.BinaryMetaFactory from Tridion.ContentDelivery.dll. I indeed also can't find a GetBinaryMeta method in that class, so it seems there is a mistake in the code sample. The most likely method that you should use is GetMeta.
Is there a reason you are not using a Binary Link to get a Link object to the specific Variant of the binary you want? Keep in mind that any DCP may render multiple variations of your multimedia component. From the Link object you can then get the URL to the binary.
Look for BinaryLink in the documentation for more details.
Try this:-
BinaryMeta binaryMeta = b.GetBinaryMeta(queryStringId);
if(binaryMeta != null) {
VideoBinaryUrl = binaryMeta.URLPath;
}
I did a SQL Profiler on the code and noticed that it was because I deployed my test app it wasn't calling the broker. Running the code within the actual Tridion Published site did hit the database but it was passing the value "[#def#]" for the variantId column.
I have now got it working with the following code:
IComponentMeta cm = cmf.GetMeta(queryStringId);
if (cm != null)
{
TcmId = queryStringId;
Title = cm.TryGetValue("title");
Summary = cm.TryGetValue("summary");
Product = cm.TryGetValue("product");
if (cm.SchemaId == StreamingContentSchemaId)
{
VideoId = cm.TryGetValue("video_url");
IsVimeo = true;
}
else if (cm.SchemaId == WebcastSchemaId)
{
using (var b = new BinaryMetaFactory())
{
var binaryMeta = b.GetMeta(queryStringId, "tcm:0-" + cm.OwningPublicationId + "-1");
if (binaryMeta != null)
{
VideoBinaryUrl = binaryMeta.UrlPath;
}
else
{
Logger.Log.ErrorFormat("Failed to load binary meta {0}", queryStringId);
}
}
}
I am using Adobe Air to get data from SalesForce, and present it in a datagrid.
I am using a query to get the data, and then put it into an arraycollection that is bound to the datagrid, this works correctly and the data is displayed.
The problem is that I want to convert the Account Id in the Event to show the account Name. To do this I am using the following code:-
_serviceWrapper.query( "Select * From Event order by StartDateTime asc", new mx.rpc.Responder( eventQueryHandler, faultHandler ))}
protected function eventQueryHandler(qr:ArrayCollection):void {
var acctIdss:String = "";
for each(var contact:DynamicEntity in qr) {
if (contact.AccountId != null && acctIdss.indexOf(contact.AccountId) == -1) {
acctIdss += "'" + contact.AccountId + "',";
}
//contact.AccountName = ""; // Add field to contact for account name
TempGridProvider.addItem(contact); // Add contact to temp grid data data provider
//TempGridProvider.contact.AccountName = "";
}
acctIdss = acctIdss.substr(0, acctIdss.length - 1);
// Query for the accounts based on the account ids found in the contact list
_serviceWrapper.query("Select Id, Name, BillingCity From Account Where Id in (" + acctIdss + ")",
new SfdcAsyncResponder(Event2QueryHandler, faultHandler));
}
protected function Event2QueryHandler(accounts:ArrayCollection):void {
for each (var account:DynamicEntity in accounts) {
for each(var contact:DynamicEntity in TempGridProvider) {
if (contact.AccountId == account.Id) {
contact.AccountName = account.Name + " - " + account.BillingCity;
}
}
}
onQueryResult(TempGridProvider);
private function onQueryResult( rows : ArrayCollection ) : void {
// release previous query results
_serviceWrapper.releaseQueryResults( _gridDataProvider );
// populate datagrid
_gridDataProvider = rows;
// show message in status bar
var status : F3Message = new F3Message( F3Message.STATUS_INFO, "Query came back with " + ( _gridDataProvider == null ? 0 : _gridDataProvider.length ) + " " + _selectedEntity + "s" );
showStatus( status );
TempGridProvider = new ArrayCollection();;
}
This works and displays the Account Name, the problem is that when I use this script and then Sync Changes to SalesForce all the records that have been displayed are identified as needing to be syncronised even if they have only been displayed.
If I skip the function eventQueryHandler, and link my query to the OnQueryResult function then there is no problem, but only the Account Id can be displayed.
How can I stop Air marking these records as having changed, or is there a better way to achieve this??
Thanks in advance, any help is greatly appreciated.
Roy
I think you need to not manipulate the underlying object. There are two options for getting the data to render in a DataGrid.
Option 1 - Create another non-managed ValueObject that holds the values you need to display in the DataGrid. Copy the values when you receive them into the new ValueObject.
Option 2 - Use a labelFunction on the DataGridColumn to fetch the data externally when each cell in a given column is rendered.