Tags
Share
In our previous article, we introduced you to content fragments. Today, we let you roll up your sleeves and hard code your way through the intricacies of AEM. We’ll start by defining the Video and Event schema models described in the previous article and then explore brightcove AEM video sync.
Video Schema Model
conf > site-com > settings > dam > cfm > models > video:
<!--?xml version="1.0" encoding="UTF-8"?-->
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primarytype="cq:Template" allowedpaths="[/content/entities(/.*)?]" ranking="{Long}100">
<jcr:content cq:scaffolding="/conf/site-com/settings/dam/cfm/models/video/jcr:content/model" cq:templatetype="/libs/settings/dam/cfm/model-types/fragment" jcr:primarytype="cq:PageContent" jcr:title="Video" sling:resourcesupertype="dam/cfm/models/console/components/data/entity" sling:resourcetype="dam/cfm/models/console/components/data/entity/default">
<model cq:targetpath="/content/entities" jcr:primarytype="cq:PageContent" sling:resourcetype="wcm/scaffolding/components/scaffolding" datatypesconfig="/mnt/overlay/settings/dam/cfm/models/formbuilderconfig/datatypes" maxgeneratedorder="20">
<cq:dialog jcr:primarytype="nt:unstructured" sling:resourcetype="cq/gui/components/authoring/dialog">
<content jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primarytype="nt:unstructured" maxgeneratedorder="20">
<brc_title jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/textfield" fieldlabel="Name" listorder="1" maxlength="255" required="on" metatype="text-single" name="brc_title" renderreadonly="false" showemptyinreadonly="true" valuetype="string">
<brc_poster jcr:primarytype="nt:unstructured" sling:resourcetype="dam/cfm/models/editor/components/contentreference" fieldlabel="Poster" filter="hierarchy" listorder="8" metatype="reference" name="brc_poster" namesuffix="contentReference" renderreadonly="false" rootpath="/content/dam" showemptyinreadonly="true" validation="cfm.validation.contenttype.image" valuetype="string/reference">
<brc_duration jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/textfield" disabled="{Boolean}true" fieldlabel="Duration" listorder="1" maxlength="255" metatype="text-single" name="brc_duration" renderreadonly="false" showemptyinreadonly="true" valuetype="string">
<brc_created_at jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/datepicker" disabled="{Boolean}true" displayedformat="YYYY-MM-DD HH:mm" emptytext="YYYY-MM-DD HH:mm" fieldlabel="Created" listorder="5" metatype="date" name="brc_created_at" renderreadonly="false" showemptyinreadonly="true" type="datetime" valueformat="YYYY-MM-DD[T]HH:mm:ss.000Z" valuetype="calendar">
<granite:data jcr:primarytype="nt:unstructured" typehint="Date">
</granite:data></brc_created_at>
<brc_id jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/hidden" fieldlabel="Id" listorder="1" maxlength="255" metatype="text-single" name="brc_id" renderreadonly="false" showemptyinreadonly="true" valuetype="string">
</brc_id></brc_duration></brc_poster></brc_title></items>
</content>
</cq:dialog>
</model>
</jcr:content>
</jcr:root>
Event Schema Model
This schema will serve both for upcoming Event and On-Demand webinars conf > site-com > settings > dam > cfm > models > event:
<!--?xml version="1.0" encoding="UTF-8"?-->
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" jcr:primarytype="cq:Template" allowedpaths="[/content/entities(/.*)?]" ranking="{Long}100">
<jcr:content cq:scaffolding="/conf/site-com/settings/dam/cfm/models/event/jcr:content/model" cq:templatetype="/libs/settings/dam/cfm/model-types/fragment" jcr:primarytype="cq:PageContent" jcr:title="Event" sling:resourcesupertype="dam/cfm/models/console/components/data/entity" sling:resourcetype="dam/cfm/models/console/components/data/entity/default">
<model cq:targetpath="/content/entities" jcr:primarytype="cq:PageContent" sling:resourcetype="wcm/scaffolding/components/scaffolding" datatypesconfig="/mnt/overlay/settings/dam/cfm/models/formbuilderconfig/datatypes" maxgeneratedorder="20">
<cq:dialog jcr:primarytype="nt:unstructured" sling:resourcetype="cq/gui/components/authoring/dialog">
<content jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/fixedcolumns">
<items jcr:primarytype="nt:unstructured" maxgeneratedorder="20">
<name jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/textfield" fieldlabel="Event Name" listorder="1" maxlength="255" metatype="text-single" name="eventName" renderreadonly="false" required="on" showemptyinreadonly="true" valuetype="string">
<start_date jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/datepicker" displayedformat="YYYY-MM-DD HH:mm" displaytimezonemessage="{Boolean}true" emptytext="YYYY-MM-DD HH:mm" fieldlabel="Start Date" listorder="5" metatype="date" name="startDate" renderreadonly="false" required="on" showemptyinreadonly="true" type="[datetime,datetime]" valueformat="YYYY-MM-DD[T]HH:mm:ss.000Z" valuetype="calendar/datetime">
<granite:data jcr:primarytype="nt:unstructured" typehint="Date">
</granite:data></start_date>
<end_date jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/datepicker" displayedformat="YYYY-MM-DD HH:mm" displaytimezonemessage="{Boolean}true" emptytext="YYYY-MM-DD HH:mm" fieldlabel="”End" date"="" listorder="5" metatype="date" name="endDate" renderreadonly="false" showemptyinreadonly="true" type="[datetime,datetime]" valueformat="YYYY-MM-DD[T]HH:mm:ss.000Z" valuetype="calendar/datetime">
<granite:data jcr:primarytype="nt:unstructured" typehint="Date">
</granite:data></end_date>
<duration jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/textfield" fielddescription="Please provide event duration in minutes." fieldlabel="Duration, minutes" listorder="1" maxlength="255" metatype="text-single" name="duration" renderreadonly="false" showemptyinreadonly="true" valuetype="string">
<form_id jcr:primarytype="nt:unstructured" sling:resourcetype="granite/ui/components/coral/foundation/form/textfield" fieldlabel="FORM - ID" listorder="1" maxlength="255" metatype="text-single" name="formID" renderreadonly="false" showemptyinreadonly="true" valuetype="string">
<webinarvideoreference jcr:primarytype="nt:unstructured" sling:resourcetype="dam/cfm/models/editor/components/fragmentreference" allownew="{Boolean}true" fieldlabel="Webinar Video" filter="hierarchy" fragmentmodelreference="/conf/site-com/settings/dam/cfm/models/video" listorder="21" metatype="fragment-reference" name="webinarVideoReference" namesuffix="contentReference" renderreadonly="false" rootpath="/content/dam/site-com/content-fragments/${psCFRegion}/webinars" showemptyinreadonly="true" subtype="content-fragment" valuetype="string/content-fragment">
<field jcr:primarytype="nt:unstructured" rootpath="/content/dam/site-com/content-fragments/${psCFRegion}/webinars">
<granite:data jcr:primarytype="nt:unstructured">
</granite:data></field></webinarvideoreference>
</form_id></duration></name></items>
</content>
</cq:dialog>
</model>
</jcr:content>
</jcr:root>
Video Sync Process: Using AEM Brightcove Connector
We’re using Brightcove AEM for videos, so now we’ll set a video sync-up using Adobe AEM Brightcove Connector (the Adobe-AEM-Brightcove Connector allows you to manage Brightcove Video Cloud videos and players within AEM and easily embed videos in AEM pages).
We’ll start by setting up the sync into ”/content/dam/brightcove/en” and then create a process that will create video content fragments based on synced videos.
First of all, we add a pom.xml configuration:
<dependency>
<groupid>com.coresecure.brightcove.cq5</groupid>
<artifactid>brightcove_connector</artifactid>
<type>zip</type>
<version>5.6</version>
</dependency>
And the sync config apps > site-com > osgiconfig > config.author > com.coresecure.brightcove.wrapper.sling.BrcServiceImpl~b1d15603-3b49-459c-844d-a15120ddebd8.cfg.json:
{
"client_secret": "{Enter your Brightcove client Secret API}",
"accountAlias": "{Enter a name for the account to be displayed in the Connector}",
"client_id": "{Enter your Brightcove client ID from the Brightcove API Authentication page }",
"allowed_groups": [
"{Specify the group that will access the Connector (required) be sure that it is a group you are included in}"
],
"key": "{Default Video Player Key}",
"asset_integration_path": "/content/dam/brightcove/en"
}
And apps > site-com > osgiconfig > config.author > com.coresecure.brightcove.wrapper.webservices.BrcReplicationHandler.cfg.json:
{
"target_directory":"/content/dam/brightcove/en"
}
The Brightcove Import Listener to create or update a video content fragment based on video assets from Brightcove AEM (a simplified code with all non-null checks skipped):
.....
@Activate
public void activate() {
try {
session = repository.loginService(SERVICE_NAME, null);
JackrabbitEventFilter ef = new JackrabbitEventFilter()
.setAbsPath("/content/dam/brightcove/en")
.setNodeTypes(new String[]{JcrConstants.NT_UNSTRUCTURED})
.setEventTypes(Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED)
.setIsDeep(true)
.setNoExternal(true);
JackrabbitObservationManager om = (JackrabbitObservationManager) session.getWorkspace().getObservationManager();
om.addEventListener (this, ef);
} catch (RepositoryException e) {
LOG.error("Unable to register session", e);
}
}
.......
@Override
public void onEvent(EventIterator events) {
try (ResourceResolver resourceResolver = getResourceResolverFromSession(session, resourceResolverFactory)) {
while (events.hasNext()) {
Event event = events.nextEvent();
String path = event.getPath();
if (!path.endsWith(ConnectorField.LAST_SYNC.getFieldName())) {
continue;
}
String metadataRelPath = joinAsPath(JCR_CONTENT, METADATA_FOLDER);
String targetPath = StringUtils.substringBeforeLast(path, "/" + metadataRelPath);
WorkflowSession workflowSession = resourceResolver.adaptTo(WorkflowSession.class);
WorkflowModel workflowModel = workflowSession.getModel(“{brightcove sync metadata workflow path}”);
Map<string, object=""> properties = new HashMap<>();
properties.put(JOB_PARAMETER_PATH, targetPath);
properties.put(JOB_PARAMETER_WF_ID, WF_MODEL_AUTHOR_ID);
jobManager.addJob(TOPIC, properties);
}
} catch (WorkflowException | RepositoryException | RuntimeException ex) {
LOG.error("Exception", ex);
}
}
</string,>
And a simple enum for Brightcove AEM field mapping:
package com.wcm.site.model.brightcove;
import com.adobe.cq.dam.cfm.ContentFragment;
import com.wcm.site.util.CFMUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ValueMap;
import java.util.Optional;
public enum ConnectorField {
ID("brc_id"),
TITLE("brc_title"),
TEXT_TRACKS("brc_text_tracks"),
CUSTOM_FIELDS("brc_custom_fields"),
BRC_TAGS("brc_tags"),
TAGS("tags"),
PAGE_NAME_HINT("pageNameHint"),
DETAILS("details"),
IS_AEM_EXPOSED("isaemexposed"),
TYPE("type"),
DESCRIPTION("brc_description"),
LONG_DESCRIPTION("brc_long_description"),
CREATED_AT("brc_created_at"),
UPDATED_AT("brc_updated_at"),
DURATION("brc_duration"),
TEXT("text"),
POSTER("brc_poster"),
STAGE("stage"),
LAST_SYNC("brc_lastsync"),
THUMBNAIL("brc_thumbnail");
private String fieldName;
ConnectorField(String fieldName) {
this.fieldName = fieldName;
}
public String getContent(Optional<contentfragment> contentFragment) {
return CFMUtils.getContent(contentFragment, this.fieldName);
}
public <t> T getValue(Optional<contentfragment> contentFragment, Class<t> type) {
return CFMUtils.getValue(contentFragment, this.fieldName, type);
}
public boolean getBooleanValue(Optional<contentfragment> contentFragment) {
return BooleanUtils.toBoolean(CFMUtils.getValue(contentFragment, this.fieldName));
}
public <t> T getValue(ValueMap valueMap, Class<t> type) {
return valueMap.get(this.getFieldName(), type);
}
public String getValue(Optional<contentfragment> contentFragment) {
return Optional.ofNullable(getValue(contentFragment, String.class))
.orElse(StringUtils.EMPTY);
}
public String getValue(ValueMap valueMap) {
return Optional.ofNullable(getValue(valueMap, String.class))
.orElse(StringUtils.EMPTY);
}
public boolean getBooleanValue(ValueMap valueMap) {
return BooleanUtils.toBoolean(this.getValue(valueMap));
}
public String getFieldName() {
return fieldName;
}
}
</contentfragment></t></t></contentfragment></t></contentfragment></t></contentfragment>
Stay tuned for the next articles on this subject. We’ll be showing how to manage Event content fragments and corresponding pages next. Should you have any questions or need AEM development services, reach out to us. We work wonders with AEM!






.png)

