CQ Component Plugin – orHow I lost my XML – The Components of Yesteryear



CQ Component Plugin – orHow I lost my XML – The Components of Yesteryear

0 0


circuit-component-plugin


On Github michaelhodgdon / circuit-component-plugin

CQ Component Plugin

orHow I lost my XML

By Michael Hodgdon

#circuit14 #componentplugin #hashtag

The Components of Yesteryear

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

  • 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 is 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

http://code.citytechinc.com/cq-component-maven-plugin https://github.com/Citytechinc/cq-component-maven-plugin