Getting the values from a repeated Embedded Schema using TOM.NET API in an event handler in SDL Tridion 2011 SP1 - tridion

I am working on the Event Handler for saving a component.
My objective is to perform some validations when the user creates and component based on a schema.
I have a schema with the name "Employee".
Employee has an embedded schema with the name "Experience" and it is multivalued.
Experience has 3 fields.
Role : Drop down with the values Manager, Lead.
Company: Text field
Years: Text field
When the user enters some data in these fields, I want to do some validations before save.
The high level design would look like this.
Load the instance of the Component
Navigate to embedded field "Experience"
For every "Experience". I need to get the value of the "Role", and check that appropriate value is entered in other two fields(By writing Component Save event)
For( all the repeated "Experience")
{
If (Role=="Manager")
check the values in the other two fields and do some validation
If (Role=="Lead")
check the values in the other two fields and do some validation
}
I am stuck at extracting the value and Names of subfields at the embeddded field.
I have tried:
Tridion.ContentManager.Session mySession = sourcecomp.Session;
Schema schema= sourcecomp.Schema;
if(schema.Title.Equals("Employee"))
{
var compFields = new ItemFields(sourcecomp.Content, sourcecomp.Schema);
var embeddefield = (EmbeddedSchemaField)compFields["Experience"];
var embeddedfields = (IList<EmbeddedSchemaField>)embeddefield.Values;
foreach(var a in embeddedfields)
{
if(a.Name.Equals("Role"))
{
string value=a.Value.ToString();
}
}
}
Actually I am stuck how to retrieve the values in the other fields at the same time.
Can any one explain how it can be done?

What you need to understand on a EmbeddedSchemaField class is that it represents both a schema and a field (as the name implies...)
I always find it helpful to look at the source XML of the component when writing code that targets its fields, you get a good visual representation of what your classes must do. If you look at a component XML like this:
<Content>
<Title>Some Title</Title>
<Body>
<ParagraphTitle>Title 1</ParagraphTitle>
<ParagraphContent>Some Content</ParagraphContent>
</Body>
<Body>
<ParagraphTitle>Title 2</ParagraphTitle>
<ParagraphContent>Some more Content</ParagraphContent>
</Body>
</Content>
Body is your embedded Schema field, which is multivalued, and contains 2 single-valued fields within it.
Addressing these fields in TOM.NET then:
// The Component
Component c = (Component)engine.GetObject(package.GetByName(Package.ComponentName));
// The collection of fields in this component
ItemFields content = new ItemFields(c.Content, c.Schema);
// The Title field:
TextField contentTitle = (TextField)content["Title"];
// contentTitle.Value = "Some Title"
// Get the Embedded Schema Field "Body"
EmbeddedSchemaField body = (EmbeddedSchemaField)content["Body"];
// body.Value is NOT a field, it's a collection of fields.
// Since this happens to be a multi-valued field, we'll use body.Values
foreach(ItemFields bodyFields in body.Values)
{
SingleLineTextField bodyParagraphTitle = (SingleLineTextField)bodyFields["ParagraphTitle"];
XhtmlField bodyParagraphContent = (XhtmlField) bodyFields["ParagraphContent"];
}
Hope this gets you started.

Related

2sxc Prefill datetime value in Manage Content Data

I created a Entity in Content-Types and Data and this entity have a field that is a datetime
When I click in the Plus button (+) I want to hide the field from user (this I got) and that default value be the current date (to be filled behind the scene)
Is there a way to achieve this ?
I read about prefill but I dont got how to do that with the default "Manage Content / Data"
I´m using 2sxc 10.24.0
Welcome to StackOverflow, #Alexandre ;)
The special buttons can only be done in the view. The internal manage-data doesn't provide options to customize. You can set default texts, but there is currently no mechanism to provide tokens, JS or something to get dynamic values as an initial value from the normal Admin-dialogs.
I got if I create a view and then use
#Edit.Toolbar(toolbar: new object[] { new { command = new { action = "new", contentType = "Articles", prefill = new { CreationDate = DateTime.Now } }}}
I believe that to do that in Manage Content / Data will be need to modify the "view" from the 2sxc

Datasource Paging Issue (Revised Again)

See Datasource Paging Issue (Revised)
for the original question.
Markus, you were kind enough to help with out with the issue of incorporating a record count into a query using a calculated datasource. I have a search form with 15 widgets - a mix of date ranges, dropdowns, text values and ._contains, ._equals, ._greaterThanOrEquals, ._lessThanOrEquals, etc.
I have tested this extensively against mySQL SQL code and it works fine.
I have now added a 16th parameter PropertyNames, which is a list with binding #datasource.query.filters.Property.PropertyName._in and Options blank. The widget on the form is hidden because it is only used for additional filtering.
Logic such as the following is used, such that a particular logged-in user can only view their own properties. So if they perform a search and the Property is not specified we do:-
if (params.param_Property === null && canViewAllRecords === false) {
console.log(params.param_PropertyNames); // correct output
ds.filters.Property.PropertyName._in = params.param_PropertyNames;
}
The record count (records.length) is correct, and if I for loop through the array of records the record set is correct.
However, on the results page the table displays a larger resultset which omits the PropertyNames filter. So if I was to search on Status 'Open' (mySQL results 50) and then I add a single value ['Property Name London SW45'] for params.param_PropertyNames the record count is 6, the records array is 6 but the datasource display is 50. So the datasource is not filtering on the property array.
Initially I tried without adding the additional parameter and form widget and just using code such as
if (params.param_Property === null && canViewAllRecords === false) {
console.log(params.param_PropertyNames); // correct output
ds.filters.Property.PropertyName._in = properties; // an array of
properties to filter out
}
But this didn't work, hence the idea of adding a form widget and an additional parameter to the calculated recordcount datasource.
If I inspect at query.parameters then I see:-
"param_Status": "Open",
"param_PropertyNames": ["Property Name London SW45"],
If I inspect query.filters:-
name=param_Status, value=Open
name=param_PropertyNames, value=[]}]}
It looks as though the filter isn't set. Even hard coding
ds.filters.Property.PropertyName._in = ['Property Name London SW45'],
I get the same reuslt.
Have you got any idea what would be causing this issue and what I can do for a workaround ?
Using a server side solution I would suggest editing both your SQL datasource query script (server side) that is supposed to filter by this property list and including the same code in your server side script for your calculated Count datasource. The code would look something like this, not knowing your exact details:
var subquery = app.models.Directory.newQuery();
subquery.filters.PrimaryEmail._equals = Session.getActiveUser().getEmail();
subquery.prefetch.Property._add();
var results = subquery.run();
if(!results[0].CanViewAllRecords) {
query.filters.Property.PropertyName._in = results[0].Property.map(function(i) {return i.PropertyName;});
}
By adding this code you are filtering your directory by your current user and prefetching the Property relation table, then you set the filter only if your user canviewallRecords is false and use JS map function to create an array of the PropertyName field in the Property table. As I stated, your code may not be exactly the same depending on how you have to retrieve your user canviewallrecords property and then of course I don't know your relation between user and Property table either, is it one-to-many or other. But this should give you an idea how to implement this on server side.

Custom AEM Form field not persisted

I attempt to create a custom field for AEM form (AEM6.0 SP3) following how text field does it: /libs/fd/af/components/guidetextbox
I created init.jsp and widget.jsp with same content.
On widget.jsp, I then add some jQuery to autopopulate text field on focus out.
<script>
var thisField = '${guideid}${'_widget'}';
$(thisField).focusout(function() {
$(this).val('date ' + new Date());
});
</script>
On focus in, I type text 'ABC' then when focus-out I get text 'date ' however when submitting the data, text 'ABC' gets submitted.
Is there any AEM API I need to invoke (instead of just jQuery .val() function) in order for the changes to be recorded ?
Not the best solution, but we managed to get it work by first calling focus(), eg.
$(this).focus().val('date ' + new Date());
Better solution:
create a custom function eg
function initDatePicker(thisObj) { $(this).focusout(function() {thisObj.value = $(this).val();});}
Update .content.xml to call this within initScript eg.
<cq:template guideNodeClass="guideTextBox" jcr:primaryType="nt:unstructured" jcr:title="Datepicker input field" initScript="initDatePicker(this)"/>
the function will be immediately included when the widget is added to canvas.
In AEM Forms a Javascript model is maintained that stores the value and that model is used to submit the data. Now to pass on the value from the ui to the model XFA_EXIT_EVENT[1] has to be triggered. So after setting the value you must add this line of code to persist the value
$(this).trigger(xfalib.ut.XfaUtil.prototype.XFA_EXIT_EVENT)
Also a better way would be to create your own widget for this specific scenario. See [2] for more details. The article is for AEM Form 6.1 but it will work for AEM 6.0 as well.
[1] https://helpx.adobe.com/aem-forms/6/html5-forms/introduction-widgets.html
[2] https://helpx.adobe.com/aem-forms/6-1/custom-appearance-widget-adaptive-form.html

How to get the complete set of Embedded field values in a popup window in the Tridion Web GUI?

I implemented a ribbon tool bar button for Tridion 2011 SP1, which opens an aspx page and populates a drop down list based on a look-up component. The look-up component comprises of different embedded schemas. To filter out the values based on embedded schema name I need to get the Embedded schema field values of component creation page on button click in button JavaScript.
Because in my component creation page consists of multivalued Embedded schema field has the info, which helps look up value filtering process. I am unaware of the command need to be used for the requirement. I know about a command to get the complete component XML, that is: $display.getView().getItemFields().
To get the present RTF field content I am going for the command: target.editor.getHTML(). To get the complete set of Embedded schema field values only,
which command I need to use?
My sample component source:
<root>
<a>sample a</a>
<b>sample b</b>
<c>
<ca>ca 1</ca>
<cb>cb 1</cb>
<cc>cc 1</cc>
</c>
<c>
<ca>ca 2</ca>
<cb>cb 2</cb>
<cc>cc 2</cc>
</c>
<c>
<ca>ca 1</ca>
<cb>cb 1</cb>
<cc>cc 1</cc>
</c>
</root>
I don't think there are public API for that. But you could use component data xml and then parse it by yourself:
var item = $display.getItem();
var xml = item.getContent(); // OR $display.getView().getItemFields();
var xmlDoc = $xml.getNewXmlDocument(xml);
var schema = item.getSchema();
if(schema.isLoaded())
{
var xpath = "/custom:{0}/custom:embeddedFieldName".format(schema.getRootElementName());
var fields = $xml.selectNodes(xmlDoc, xpath, { custom: schema.getNamespaceUri() });
// loop fields and get values ...
}

Looping through a multivalued field and embedded schema field in .NET Assembly TBB

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

Resources