How iterate all Property of a node and update the value of each property and save in repository in cq5 - iterator

My cq5 Content Structure is....
Content
---mywebsite
------base
-----us
--- en
----pageOne
----pageTwo
----pageThree
----pageFour
----cq:content
----par
----pageFourNew
"pageFourNew" has around 500 Properties.
Now I need to get all the properties of "pageFourNew" and to update their value.
For example if I have:
property=prop1
value = prop1 value
I want to do value = value+"some string value append" and save it on the repository.
I want to do this in a programmatically way.
Please share if you have any solution or idea.

You can use PropertyIterator to iterate through all the properties, setProperty() method of node api to set the new value and jcr session to persist the value to get this done. Sample code:
PropertyIterator propertyIterator = pageFourNew.getProperties();
while (propertyIterator.hasNext()) {
Property property = propertyIterator.nextProperty();
pageFourNew.setProperty(property.getName(),
property.getValue().getString() + "");
jcrSession.save();}

You can easily do this as suggested above at JCR level. But as per CQ practices and this blog
It is better practice to operate at Sling level and NOT JCR level, just to avoid overhead of managing the resources. You can use below code which works:
Resource resource = pageFourNew; // assuming you are getting sling resource properly
ModifiableValueMap valueMap = resource.adaptTo(ModifiableValueMap.class);
for(String key : valueMap.keySet()) {
String value = valueMap.get(key, String.class);
value = value + "additional texts";
valueMap.put(key, value);
}
resource.getResourceResolver().commit();
This is cleaner approach.

Related

Ektron: How to change the Framework API's default page size?

I've noticed that when pulling content form the framework API that there is a default page size of 50. I've tried adjusting the "ek_PageSize" AppSetting, but that doesn't seem to affect the API.
Basically in all my code I need to create a new PaginInfo object to update the number of items being returned.
var criteria = new ContentTaxonomyCriteria(ContentProperty.Id, EkEnumeration.OrderByDirection.Descending);
criteria.PagingInfo = new PagingInfo(100);
Does anyone know if there's a way to change that default value (for the entire site) without having to modify the PagingInfo object on the criteria on each call?
You could create a factory method that creates your criteria object. Then instead of instantiating the criteria object, you would call this factory method. From here, you can define an AppSetting that is unique to your code. There are several types of criteria objects used by the ContentManager, so you could even make the factory method generic.
private T GetContentCriteria<T>() where T : ContentCriteria, new()
{
// Sorting by Id descending will ensure newer content blocks are favored over older content.
var criteria = new T
{
OrderByField = ContentProperty.Id,
OrderByDirection = EkEnumeration.OrderByDirection.Descending
};
int maxRecords;
int.TryParse(ConfigurationManager.AppSettings["CmsContentService_PageSize"], out maxRecords);
// Only set the PagingInfo if a valid value exists in AppSettings.
// The Framework API's default page size of 50 will be used otherwise.
if (maxRecords > 0)
{
criteria.PagingInfo = new PagingInfo(maxRecords);
}
return criteria;
}
There's a page size App Setting in the web.config that I believe controls this default page size as well. But be warned as this will also change page sizes within the Workarea.
So if you set it to 100, then you'll see 100 users, content items, aliases, etc. per page instead of the default 50.

Creating new smartform data using Ektron ContentTypes

Ektron 8.0.1 SP1
I am using SmartForms and Content Types to read (and hopefully write) data. I can read data but now I am attempting to write a new record similar to the following.
ContentTypeManager<member> contentTypeManager = new ContentTypeManager<member>();
ContentType<member> newmem = new ContentType<member>();
newmem.SmartForm.details.field1 = "Chuck"; // This line throws 'Object reference not set to an instance of an object.' error
newmem.SmartForm.details.field2 = "Norris";
contentTypeManager.Update(newmem);
I get the error "Object reference not set to an instance of an object." for that first assignment line. What am I missing?
I am having trouble finding good documentation on ContentTypes for 8.0.1 now that the Ektron website has been redesigned.
Thx.
Thanks for clarifying, to ADD content to a folder that has a smartform assigned to it, the basic code block should get you started: (Note: the Html attribute of the content is simply the xml matched to the schema you created)
Ektron.Cms.Framework.Content.ContentManager cmanager = new Cms.Framework.Content.ContentManager();
Ektron.Cms.ContentData cdata = new ContentData();
cdata.FolderId = 0;
cdata.XmlConfiguration.Id = 0; //SMARTFORM ID HERE
cdata.Html = "<root><field1>field1 value</field1><field2>field2 value</field2></root>";
cmanager.Add(cdata);
You could update ContentTypes.cs to include an Add method. Just copy the Update method and change contentManager.Update to contentManager.Add.
public void Add(ContentType<T> contentType)
{
Initialize();
contentType.Content.Html = Ektron.Cms.EkXml.Serialize(typeof(T), contentType.SmartForm);
contentManager.Add(contentType.Content);
}
Unfortunately, contentManager.Add returns void. Ideally it should return the new content ID.

An interesting Restlet Attribute behavior

Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.

Custom wcf data provider and debugging a relationship error

I'm implementing a custom data provider, I have gotten it to the point that it returns data and can be filtered, but am having some trouble getting relationships to work.
When querying the metadata the relationships look correct, and when querying a table the related property links appear, but when attempting to access a ResourceReference property I get the following exception:
Object reference not set to an instance of an object.
System.NullReferenceException
stacktrace at System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
at System.Data.Services.Providers.DataServiceProviderWrapper.GetContainer(ResourceSetWrapper sourceContainer, ResourceType sourceResourceType, ResourceProperty navigationProperty)
at System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceProperties(ResourceSetWrapper resourceSet, ResourceType resourceType)
at System.Data.Services.Serializers.SyndicationSerializer.WriteObjectProperties(IExpandedResult expanded, Object customObject, ResourceType resourceType, Uri absoluteUri, String relativeUri, SyndicationItem item, DictionaryContent content, EpmSourcePathSegment currentSourceRoot)
at System.Data.Services.Serializers.SyndicationSerializer.WriteEntryElement(IExpandedResult expanded, Object element, ResourceType expectedType, Uri absoluteUri, String relativeUri, SyndicationItem target)
at System.Data.Services.Serializers.SyndicationSerializer.WriteTopLevelElement(IExpandedResult expanded, Object element)
at System.Data.Services.Serializers.Serializer.WriteRequest(IEnumerator queryResults, Boolean hasMoved)
at System.Data.Services.ResponseBodyWriter.Write(Stream stream)
Here's a sample of how I create the relationships:
var sourceReference = new ResourceProperty(
relatedType.ResourceTypeName,
ResourcePropertyKind.ResourceReference,
relatedType.ResourceType);
sourceReference.CanReflectOnInstanceTypeProperty = false;
compoundType.ResourceType.AddProperty(sourceReference);
var destinationReference = new ResourceProperty(
compoundType.ResourceSetName,
ResourcePropertyKind.ResourceSetReference,
compoundType.ResourceType);
destinationReference.CanReflectOnInstanceTypeProperty = false;
source.ResourceType.AddProperty(destinationReference);
var sourceAssociation = new ResourceAssociationSet(
"source",
new ResourceAssociationSetEnd(compoundType.ResourceSet, compoundType.ResourceType, sourceReference),
new ResourceAssociationSetEnd(relatedType.ResourceSet, relatedType.ResourceType, null));
var destinationAssociation = new ResourceAssociationSet(
"destination",
new ResourceAssociationSetEnd(relatedType.ResourceSet, relatedType.ResourceType, destinationReference),
new ResourceAssociationSetEnd(compoundType.ResourceSet, compoundType.ResourceType, null));
From looking at the sample code on the OData website I thought I'd done it all correctly, and cannot determine my error. Any ideas? or tips on debugging a custom WCF Data service?
Update:
Here's what happens just before the null exception.
Have a resource set Collars with a relationship to Projects so I do this query:
blah.svc/Collars(1)/Project
My override of GetResourceAssociationSet in my IDataServiceMetadataProvider gets called with the parameters ResourceSet = Collars, ResourceType = Collar, Property = Project and I return the association set specified above.
GetResourceAssociationSet is then called again with ResourceSet = Projects, ResourceType = Collar, Property = Project and I return the same association set.
Then in System.Data.Services.Providers.GetResourceAssociationSetEnd the variables passed in are resourceSet = Projects, resourceType = Collar, resourceProperty = Project, this function returns null.
Which makes thisEnd in System.Data.Services.Providers.DataServiceProviderWrapper.GetResourceAssociationSet equal to null.
Then GetRelatedResourceAssociationSetEnd is called with the same variables and also returns null.
So it then crashes with the call
ResourceSetWrapper relatedSet = this.ValidateResourceSet(relatedEnd.ResourceSet);
because relatedEnd is null.
Well, in my debugging I noticed the last time GetResourceAssociationSet was called before the error occurred was for a case where the resourceSet and resourceType parameters had different values (in the case of a derived type).
So, I looked and found this
WCF data services (OData), query with inheritance limitation?
...and lo and behold this uninformative null reference exception (at least in my case) is caused by that same issue. I removed the offending property (and then moved it to the base resource set, even though it doesn't exist there in practice), and the issue was resolved.
Interesting side note (in case this helps the original poster): ResourceType.Properties includes properties from Base Types. Had to change some code to use PropertiesDeclaredOnThisType instead...
The solution for me was that I had made a mistake in my implementation of GetResourceType in my IDataServiceQueryProvider.
When querying a Resource Property that was a ResourceSetReference I was returning the ResourceType of the parent resource, and not the type of the related resource.

Return Entire field from GetBestFragment in FastVectorHighlighter

In Highlighter.Net, we can use NullFragmenter to return the entire field content. Is there any way we can do this in FastVectorHighlighter.Net?
If you use SimpleFragListBuilder-fragmenter for FastVectorHighlighter then need to modify a public static properties of fragmenter to manage fragment size:
var fieldContent = "some data";
SimpleFragListBuilder.MARGIN = fieldContent.Length;
SimpleFragListBuilder.MIN_FRAG_CHAR_SIZE = SimpleFragListBuilder.MARGIN*3;
var result = highlighter.GetBestFragment(.., fragCharSize: SimpleFragListBuilder.MIN_FRAG_CHAR_SIZE);
(see source for details - 'Lucene.Net 3.0.3 SimpleFragListBuilder.cs' [http://lucenenet.apache.org/docs/3.0.3/dd/d38/_simple_frag_list_builder_8cs_source.html])
Isn't it an option to just use document.Get("field_name") and return the entire field content in such a way? You probably have you document somewhere in the context anyway (as you need doc id to GetBestFragment()), so why not just use it?
There is a patch for java FVH that claims to do this. I haven't personally tested it.
https://issues.apache.org/jira/browse/LUCENE-2464