The Component's Nature
.content.xml
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
cq:isContainer="{Boolean}false"
jcr:primaryType="cq:Component"
jcr:title="Title"
componentGroup="Client"/>
The Component's Authorability
dialog.xml
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:Dialog" title="Title" xtype="dialog">
<items jcr:primaryType="cq:TabPanel">
<items jcr:primaryType="cq:WidgetCollection">
<tab1 jcr:primaryType="cq:Widget" title="Title" xtype="panel">
<items jcr:primaryType="cq:WidgetCollection">
<linkText jcr:primaryType="cq:Widget" fieldLabel="Title"
fieldDescription="Title for this component" name="./title"
xtype="textfield" />
</items>
</tab1>
</items>
</items>
</jcr:root>
The Component's Editing Context
_cq_editConfig.xml
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
cq:actions="[text:Title ,-,EDITDELETE,-]"
cq:layout="editbar"
cq:dialogMode="floating"
jcr:primaryType="cq:EditConfig"/>
</jcr:root>
The Component's Business Logic
Java Backing Bean
public class Title {
private final String title;
public Title(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.title = properties.get("title", "DEFAULT TITLE");
}
public String getTitle() {
return title;
}
}
The Component's View
JSP
<%@include file="/libs/foundation/global.jsp"%>
<%@ page import="com.client.components.content.title.Title" %>
<c:set var="title" value="<%=new Title(slingRequest) %>"/>
${title.title}
Issues with the current process
The fracturing of Component Definition
Complicated/Schemaless XML
Development Process Encouraged By The Complexity
Introducing...
The CQ Component Plugin
What is the CQ Component Plugin?
The CQ Component Plugin is a Maven/Gradle plugin that uses annotations inside of the backing beans for components to create the required XML files for a component.
Overview
How To Use It (Maven)
<plugin>
<groupId>com.citytechinc.cq.cq-component-plugin</groupId>
<artifactId>cq-component-maven-plugin</artifactId>
<version>2.6.0</version>
<executions>
<execution>
<goals>
<goal>component</goal>
</goals>
</execution>
</executions>
<configuration>
<componentPathBase>jcr_root/apps/client/components</componentPathBase>
<defaultComponentGroup>Client Group</defaultComponentGroup>
</configuration>
</plugin>
How To Use It (Gradle)
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath group: 'com.citytechinc.cq.cq-component-plugin',
name: 'cq-component-maven-plugin', version: '2.6.0'
}
}
componentPlugin {
componentPathBase = "jcr_root/apps/project/components"
defaultComponentGroup="Client Group"
}
install.dependsOn generateComponents
How it works
- Runs after the package are created
- Scans classes/jars for annotations
- Adds dialog.xml, _cq_editConfig.xml, and .content.xml to package
Examples
Basic Example
@Component(value = "Title")
public class Title {
@DialogField(fieldLabel = "Title", fieldDescription = "Our title")
private final String title;
public Title(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.title = properties.get("title", "DEFAULT TITLE");
}
public String getTitle() {
return title;
}
}
Basic Example
@Component(value = "Title")
public class Title {
@DialogField(fieldLabel = "Title", fieldDescription = "Our title")
private final String title;
public Title(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.title = properties.get("title", "DEFAULT TITLE");
}
public String getTitle() {
return title;
}
}
Basic Example
@Component(value = "Title")
public class Title {
@DialogField(fieldLabel = "Title", fieldDescription = "Our title")
private final String title;
public Title(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.title = properties.get("title", "DEFAULT TITLE");
}
public String getTitle() {
return title;
}
}
Basic Example
@Component(value = "Title")
public class Title {
private final ValueMap properties;
public Title(SlingHttpServletRequest request) {
properties = request.getResource().adaptTo(ValueMap.class);
}
@DialogField(fieldLabel = "Title", fieldDescription = "Our title")
public String getTitle() {
return properties.get("title", "DEFAULT TITLE");;
}
}
Basic Example
Complex Widgets and Tabs Example
@Component(value = "Mixed Tabs", tabs = { @Tab(title = "First tab"),
@Tab(path = "/libs/foundation/components/page/tab_basic.infinity.json"),
@Tab(title = "Last tab") })
public class MixedTabs {
@DialogField(fieldLabel = "Title", fieldDescription = "Our title", tab = 1)
@MultiField
private String title;
@DialogField(fieldLabel = "Image", tab = 3)
@Html5SmartImage(disableZoom = true, name = "image", allowCrop=true,
allowUpload = false, tab = false, height = 150)
private String image;
}
Complex Widgets and Tabs Example
Extending a Component
Original Title
@Component(value = "Title")
public class Title {
@DialogField(fieldLabel = "Title", fieldDescription = "Our title", ranking = 1)
private final String title;
public Title(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.title = properties.get("title", "DEFAULT TITLE");
}
public String getTitle() {
return title;
}
}
Title with Link
@Component("Title With Link")
public class TitleWithLink extends Title {
@DialogField(fieldLabel = "Link", fieldDescription = "Our link", ranking = 2)
@PathField
private final String link;
public TitleWithLink(SlingHttpServletRequest request) {
super(request);
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
this.link = properties.get("link", String.class);
}
public String getLink() {
return link;
}
}
Title with Link
Reusability
Using objects provides the ability to use dialog elements and authoring experiences across multiple dialogs with no additional effort
A Basic Link
public class Link {
@DialogField(fieldLabel = "Link Path")
@PathField
private String linkPath;
@DialogField(fieldLabel = "Link Text")
private String linkText;
public String getLinkPath() {
return linkPath;
}
public void setLinkPath(String linkPath) {
this.linkPath = linkPath;
}
public String getLinkText() {
return linkText;
}
public void setLinkText(String linkText) {
this.linkText = linkText;
}
}
A Link With a Title
@Component(value = "Single Link")
public class SingleLink {
@DialogField(fieldLabel = "Title")
private final String title;
@DialogField
@DialogFieldSet
private final Link link;
public SingleLink(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
title = properties.get("title", "DEFAULT TITLE");
String path = properties.get("linkPath", "/content/defualtpath");
String linkText = properties.get("linkText", "DEFAULT LINK TITLE");
link = new Link();
link.setLinkPath(path);
link.setLinkText(linkText);
}
public String getTitle() {
return title;
}
public Link getLink() {
return link;
}
}
A Link With a Title
A title with 3 Links
@Component(value = "Three Links")
public class ThreeLinks {
@DialogField(fieldLabel = "Title")
private final String title;
@DialogField(fieldLabel = "Link 1")
@DialogFieldSet(namePrefix = "link1/")
private final Link link1;
@DialogField(fieldLabel = "Link 2")
@DialogFieldSet(namePrefix = "link2/")
private final Link link2;
@DialogField(fieldLabel = "Link 3")
@DialogFieldSet(namePrefix = "link3/")
private final Link link3;
public ThreeLinks(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
title = properties.get("title", "DEFAULT TITLE");
link1 = new Link();
link1.setLinkPath(properties.get("link1/linkPath", "/content/defualtpath"));
link1.setLinkText(properties.get("link1/linkText", "DEFAULT LINK TITLE"));
//More Logic
}
}
A title with 3 Links
A title with unlimited Links
@Component(value = "Multi Links")
public class MultiLinks {
@DialogField(fieldLabel = "Title")
private final String title;
@DialogField
@DialogFieldSet
@MultiCompositeField
private final List links;
public MultiLinks(SlingHttpServletRequest request) {
ValueMap properties = request.getResource().adaptTo(ValueMap.class);
title = properties.get("title", "DEFAULT TITLE");
links = new ArrayList();
Iterable resources = request.getResource().getChild("links").getChildren();
for (Resource r : resources) {
ValueMap linkProps = r.adaptTo(ValueMap.class);
String path = linkProps.get("linkPath", "/content/defualtpath");
String linkText = linkProps.get("linkText", "DEFAULT LINK TITLE");
Link link = new Link();
link.setLinkPath(path);
link.setLinkText(linkText);
links.add(link);
}
}
public String getTitle() {
return title;
}
public List getLinks() {
return links;
}
}
A title with unlimited Links
Extending the plugin
- Adding Widgets
- Adding Transformers
Adding a widget
The annotation
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface MultiCompositeField {
boolean matchBaseName() default false;
String prefix() default "./";
}
Adding a widget
The Maker
public final class MultiCompositeFieldWidgetMaker extends AbstractWidgetMaker {
private static final String FIELD_CONFIGS = "fieldConfigs";
public MultiCompositeFieldWidgetMaker(WidgetMakerParameters parameters) {
super(parameters);
}
@Override
public DialogElement make() throws ClassNotFoundException, SecurityException, InvalidComponentFieldException,
NotFoundException, CannotCompileException, NoSuchFieldException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
MultiCompositeField multiCompositeFieldAnnotation = getAnnotation(MultiCompositeField.class);
MultiCompositeFieldWidgetParameters widgetParameters = new MultiCompositeFieldWidgetParameters();
widgetParameters.setMatchBaseName(multiCompositeFieldAnnotation.matchBaseName());
widgetParameters.setPrefix(multiCompositeFieldAnnotation.prefix());
widgetParameters.setFieldName(getFieldNameForField());
widgetParameters.setFieldLabel(getFieldLabelForField());
widgetParameters.setFieldDescription(getFieldDescriptionForField());
widgetParameters.setAdditionalProperties(getAdditionalPropertiesForField());
widgetParameters.setHideLabel(getHideLabelForField());
widgetParameters.setName(getNameForField());
widgetParameters.setAllowBlank(!getIsRequiredForField());
widgetParameters.setDefaultValue(getDefaultValueForField());
widgetParameters.setListeners(getListeners());
widgetParameters.setContainedElements(buildWidgetCollection(multiCompositeFieldAnnotation));
return new MultiCompositeFieldWidget(widgetParameters);
}
.
.
.
}
Adding a widget
The widget
@Widget(annotationClass = MultiCompositeField.class,
makerClass = MultiCompositeFieldWidgetMaker.class,
xtype = MultiCompositeFieldWidget.XTYPE)
public final class MultiCompositeFieldWidget extends AbstractWidget {
public static final String XTYPE = "multicompositefield";
private final boolean matchBaseName;
private final String prefix;
public MultiCompositeFieldWidget(MultiCompositeFieldWidgetParameters parameters) {
super(parameters);
this.matchBaseName = parameters.isMatchBaseName();
this.prefix = parameters.getPrefix();
}
public String getPrefix() {
return prefix;
}
public boolean isMatchBaseName() {
return matchBaseName;
}
}
Helpful Links
https://github.com/Citytechinc/cq-component-maven-plugin
Source: https://slidedeck.io/michaelhodgdon/circuit-component-plugin
No comments:
Post a Comment
If you have any doubts or questions, please let us know.