I was recently published in Advertising Age with an article I wrote called “Responsible Personalization: How Brands Can Build Trust With Consumers.” Please take a look and share!
I was recently published in Advertising Age with an article I wrote called “Responsible Personalization: How Brands Can Build Trust With Consumers.” Please take a look and share!
I recently wrote an article called, “Reusing Tabs or Fields From an Existing Dialog” showing the benefits and steps to include previously defined dialog tabs and fields in another component for reuse. The article gained some traction when it was shared on one of Adobe’s thought leadership sites. It turns out, however, I may have taken a Joe Gunchy (Read more about Joe here. He’s an idiot) approach to this solution myself and did not consider what would happen if the parent tab or widget changed and how it would affect its children.
Tomek Niedźwiedź, an experienced AEM developer from the WPP Adobe Alliance partner Cognifide, commented on the post with a very smart and elegant solution to avoid the pitfall of reusing parts of a dialog when the parent itself is changed. I thought it would be a great guest post, and asked Tomek if he would like to contribute his solution. Below is a wonderfully written article which not only fits nicely into the type of technical content I want to share, but it’s also written in a voice that reads like a great story, which makes learning the material fun.
Please enjoy his article and be sure to thank him in the comments for sharing his expertise and his time!
Hi, my name is Tomek and I’m a Java/AEM developer at Cognifide. I was invited by Brad to write a follow-up on his article about tab and field reuse in AEM dialogs. If you haven’t read the original post and for some reason do not feel like reading the whole thing now, here’s a short recap.
We’re shown a working example of a component reusing a field already defined in another component.
There is a Carousel component that has a play speed configuration option which authors can use to set the amount of time, in milliseconds, between slide transitions. A playSpeed widget defined in the component’s dialog is then reused by a new component that happens to require the exact same configuration. Let’s say, for the sake of argument, that the new component shows an animation based on a set of images.
It all works fine when the new component is developed. The widget definition is read properly and the right interface is displayed to the user. Both components work like a charm. Everybody’s happy. But there’s something we’ve done that should be avoided. What is it and what are the consequences?
Right, there’s no way the user-friendly hint about the play speed in seconds that he added to the Carousel’s dialog would make sense in this context. This needs to be fixed.
I recently happened to be the new guy on one of my projects so I decided to leave little cautionary comment under the original post. Brad kindly invited me to write a whole guest post on the subject.
There’s a saying often quoted among software developers and I’m not sure who used it originally (according this answer on Stack Overflow, it may have been used for the first time as far back as 1991) but it goes something like this:
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. Code for readability.
Fortunately, I’m not the guy from this quote, nor have I ever worked with anyone fitting this description. In the worst-case scenario, I just write a blog about what I’ve run into. Given my writing skill, this is bad enough for the world so I think there’s no need for violence.
Anyway, the thing wrong with cross-referencing dialogs is that it makes code terse as opposed to concise. We avoided duplication but we fell into the trap of making the code so short as to hide important information.
Let’s see how we can structure a dialog for maximum readability. Think in terms of dependencies.
Here’s what we did. The relationship between the two components is simple but it’s only explicit when you look at Animation. There’s nothing in the Carousel component to tell us that parts of it are reused elsewhere. The only way to find out about this is to perform an explicit search across the entire codebase.
In Java, most developers would instinctively opt to implement a common interface and extract the common functionality to a separate class. Then use it by means of composition.
Can a similar thing be done with AEM dialogs? Yes it can. The cqinclude widget does not require us to link to a dialog specifically. In general, a path to literally anything that’s capable of returning a properly formatted JSON document should be sufficient. It’s just a piece of content that the dialog will retrieve by means of an AJAX call and use to render the right inputs.
We can easily do something like this:
When extracted, documented and placed in the right spot in the content tree, the play speed widget’s definition becomes a lot easier to reason about.
The same pattern can be easily followed in case of tabs, widgets or even arbitrary configuration nodes.
Let’s take a look at another simple, real life example to demonstrate just how flexible we can be with cqinclude.
One of the most commonly used widgets in the Classic UI is the Rich Text Editor. It’s quite powerful and highly configurable. It can appear in various components, in different contexts. Sometimes it will be customized to cater for the specific needs of a component, sometimes the configurations used across components will be very similar but minute details will differ (such as the label or size of the widget or the like). However, regardless of how many distinct components use it, it’s generally a good idea to provide a consistent authoring experience. Reusing configuration nodes can help us maintain a familiar feel across dialogs.
Let’s say that we want every Rich Text to have the same default paste behaviour and available formats and styles. We will define a common configuration in a single place in the components folder. In order to do that, create a node at:
A couple of things to note:
Here’s an example definition. As opposed to screenshots from CRXDE, I’m going to use an XML format that the Maven Vault Plugin uses when content is exported to a local file system (usually, to be made part of a codebase and put under version control together with the rest of the code).
In your IDE, you could see a file called richTextSettings.xml sitting under /apps/example/components/commons. Here’s what it might look like:
<?xml version="1.0" encoding="UTF-8"?> <jcr:root 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="nt:unstructured"> <edit jcr:primaryType="nt:unstructured" features="[cut,copy,delete]" defaultPasteMode="plaintext"/> <paraformat jcr:primaryType="nt:unstructured" features="*"> <formats jcr:primaryType="cq:WidgetCollection"> <p jcr:primaryType="nt:unstructured" tag="p" description="Paragraph"/> <h1 jcr:primaryType="nt:unstructured" tag="h1" description="Heading 1"/> <h2 jcr:primaryType="nt:unstructured" tag="h2" description="Heading 2"/> <h3 jcr:primaryType="nt:unstructured" tag="h3" description="Heading 3"/> <h4 jcr:primaryType="nt:unstructured" tag="h4" description="Heading 4"/> </formats> </paraformat> <styles jcr:primaryType="nt:unstructured" features="*"> <styles jcr:primaryType="cq:WidgetCollection"> <successButton jcr:primaryType="nt:unstructured" cssName="btn btn-success" text="Success Button"/> <defaultButton jcr:primaryType="nt:unstructured" cssName="btn btn-default" text="Default Button"/> <infoButton jcr:primaryType="nt:unstructured" cssName="btn btn-info" text="Info Button"/> <primaryButton jcr:primaryType="nt:unstructured" cssName="btn btn-primary" text="Primary Button"/> </styles> </styles> </jcr:root>
It’s all very simple. Just a number of RTE plugin settings to define available text formats, the paste behaviour and some custom styles, in case if the authors wished to inline some nice looking buttons.
Note that the jcr:nodeType of the node defined here is just nt:unstructured. This can be different if you want to define a tab (cq:Panel) or a widget (cq:Widget).
Here’s how the configuration can be referenced by a Rich Text Edit defined in a component dialog:
<?xml version="1.0"?> <productDescription jcr:primaryType="cq:Widget" allowBlank="true" fieldLabel="Product Description" name="./productDescription" xtype="richtext"> <rtePlugins jcr:primaryType="nt:unstructured" path="/apps/example/components/commons/richTextSettings.infinity.json" xtype="cqinclude"/> </productDescription>
All that the productDescription Rich Text Editor widget requires is a node called rtePlugins containing the right configurations. We can use the cqinclude xtype to reference our central configuration.
It doesn’t matter to the cqinclude if the node being included is of the type nt:unstructured, cq:Panel or cq:Widget. This allows us to flexibly mix pieces of functionality while maintaining the right level of granularity.
At the same time, by keeping this configuration separate, we avoid cross-referencing components. Wherever the common part is used, it is very clear to the developer that it is a shared piece of content and that multiple components may be affected. It’s also easy to find these components by searching for the occurrences of the shared bit’s path across dialogs.
Tomek is a Java/AEM developer with experience in CQ and AEM projects utilizing both the Cognifide technology stack (Slice, Zen Garden) and out of the box solutions provided by AEM (such as Sightly or Sling Models). A strong believer in the importance of communities and knowledge sharing in the world of software development, he’s an active user of Stack Exchange sites and is part of Cognifide’s internal training team introducing new developers into the basics of AEM development. Eager to go on-site, he works closely with Cognifide’s clients, providing them with technical knowledge and helping overcome their uncertainties.
The WPP Alliance is a subset of the network’s digital agencies and an Adobe partner in implementing Adobe Marketing Cloud solutions. With a proven track record of collaboration and delivering implementation and marketing services, the WPP Alliance allows clients to leverage the network’s strengths when client needs go beyond the expertise or footprint of a single agency. Included in the WPP Alliance are VML, Inc, Cognifide, Acceleration, Mirum, KBM Group, DT, and Wunderman.
A snowstorm was in the forecast so my youngest daughter Sophia and I went to our local Wal-mart to replenish the essentials: bread, milk, and self-esteem. Since we’d probably be cooped-up in the house for a few days, Sophia suggested we do a jigsaw puzzle together. It sounded like a good way to pass the time and do some father/daughter bonding together, so we headed to the toy department and selected a 1000-piece puzzle of a painting of classic Paris.
If you’ve ever done a jigsaw puzzle, you know a 1000-piece jigsaw puzzle is around 999 pieces too many. So, like any lazy man would do, the first thing I did when we got home was I opened Google and typed, “The Easiest Way to Solve a Jigsaw Puzzle.” To my surprise, pages of results filled my screen; however, I was too lazy to view more than a few so I visited the first three or four sites that explained the same basic process for solving the puzzle the quickest way:
After reading the instructions, I dumped the box onto the kitchen table, saw the overwhelming mound of pieces, then jumped directly to step #8, which ultimately resulted in being without a kitchen table for over a month. However, Thanksgiving Day was rapidly approaching and we needed to kick our jigsaw production into high gear so we could actually eat at our family table.
We studied the picture on the box, then flipped the pieces over one-by-one while simultaneously sorting them into their respective piles. We also picked through each pile to find any pieces with lettering or words on them, such as street signs, addresses on buildings, the artist’s signature, etc. Aside from the edge pieces, these are the easiest to identify and assemble.
We assembled our boundary with the edge pieces, then quickly assembled all of the signs and things with lettering. Easy money. Then, we did the “divide and conquer method” where I worked on the Eiffel Tower and Sophia worked on the Moulin Rouge sub-assemblies. My son Connor even jumped in to work on the hot air balloon. These were all different colors, sorted into different piles and we could work independently of each other while still working to accomplish the same goal.
Finally, we moved the large sub-assemblies into place and filled-in the remainder of the pieces to connect them together. The sky in the picture actually took the longest – probably because all of the pieces were the same, plain color. Boring. If you get stuck here, go directly to step #8 and call it good.
If you approach your AEM project like a jigsaw puzzle, you can gain efficiencies by following the same basic steps as above.
“We were recently engaged to consult on the first phase of a multi-site migration. Phase one consisted of only one of the client’s many web properties. The client considered this their ‘pilot’ site that would be a proof-of-concept for future roll-outs of other brands under their umbrella. There was a larger corporate-wide decision to use AEM as the enterprise-wide WCXM platform; however, there was a great disconnect between the individual brand managers who were approached the migration of their brand sites as separate projects entirely. So, separate RFP’s went out to agencies to bid on the work individually and the intercommunication between the brands and IT to standardize on the approach was non-existent at best. So, each RFP response was estimated ‘from scratch’ without any consideration of creating a library of common, reusable templates, etc. I evaluated the other properties to find commonalities and opportunities for standardization resulting in a potential (and realistic) savings of almost 20-30% on subsequent roll-outs by reusing the components, templates, and services. By evaluating and designing for the other web properties first and understanding the ‘big picture’, the pilot site set the groundwork for establishing a true, enterprise-wide system and standard for the subsequent brands to use and follow.”
“Sometimes when you start a jigsaw puzzle, you find pieces that are already attached. You may even get lucky and have four or five pieces already connected together. You could break these apart and reconnect them, but why? It’s already done for you! The same thing happens in a project. There are legacy features and functionality that already exist that someone will want to rewrite for the sake of rewriting, just to have it all in the WCXM system. An example of this is user generated content such as comments, ratings, and reviews of product data. Why reinvent the wheel and recreate that functionality when you can simply find a way to leverage it in its existing state? Rather, spend time fortifying the ability to retrieve and store that data in its existing state.” – Me
“First get the car running, then you can wax it.” – Me
Here is our finished product which is now framed an hanging on Sophia’s wall. In total, the puzzle took us over a month to complete. But the conversations and laughs we shared at this table will be remembered forever. Au revoir!
I am a single dad with three kids: Sophia (10), Connor (13), and Ariana (15). Together our initials spell, “S.C.A.B.s”. As Team S.C.A.B.s, we love to embark on regular adventures, such as weekend getaways to Chicago, hiking through trails where Jesse James and his gang hid from the law, or just exploring the great city where we live.
This fall we decided to do something I enjoyed as a kid: visiting a haunted house. But we didn’t just select any haunted house, we chose The Edge of Hell, one of the top-rated haunted houses in the country located in one of the many vacant buildings in the forgotten part of the city. As we waited in line to buy our tickets, we were greeted by rat-eating monsters, a galloping headless horseman, and fire-breathing, tattoo-covered hipsters in desperate need of a tetanus shot.
I paid for four tickets into The Edge of Hell which cost me $25 a piece plus tax for a grand total of $117. NO REFUNDS. Honestly, the receipt alone scared the hell out of me, but I’m cheap. The kids seemed to be in brave spirits, so we assembled in a standard conga line in order of age: I led first, Ariana was behind me holding my shoulders, Connor held onto Ariana, and little Sophia held onto Connor with her eyes closed tightly. We ascended the stairs near the entrance and our adventure began as the door and our last glimpse of light disappeared behind us.
The first thing we encountered in The Edge of Hell was a pack of large mechanical dogs (or “Hounds of Hell”) that popped out of a wall and barked rabidly at Team S.C.A.B.s. Instantly, I heard Connor yell from behind me, “Daaaaaaaaaad! I want to leave!”
“Connor, we just got started. Where are you going to go?” I asked.
Right on cue, an acne-covered teenage monster emerged from the darkness. “Chicken Exit,” he said, pointing to a steel door to our left. Before he even finished his sentence, Connor’s hands released from his sister’s shoulders and he walked towards the Chicken Exit. Sophia, not realizing where Connor was leading her, followed him with her eyes still shut tightly. Before I could object, the door slammed shut behind them. “NO REFUNDS” echoed in my head. The S.C.A.B.s were now reduced to just “A” and “B”. Thanks a lot, Teen Wolf.
Ariana and I continued on. We finished the tour and we found Connor and Sophia waiting near the exit with the other chickens. Connor was staring off into space, still grappled with fear. As we approached, Ariana folded her arms and said with her Big Sister attitude, “I think Connor should have to pay you back the $25.”
Connor paused, then stoically said, “Make it an even $30.”
“What’s the extra $5 for?” I asked.
“New underwear,” he muttered.
Why am I telling you this story on an AEM blog? Because it’s my job as a loving father to take every opportunity to embarrass my kids. But there is a point…
Fear is a powerful emotion and is amplified by surprise and the unknown. When you don’t know what’s hiding ahead, your senses are heightened, your pulse races, and the slightest deviation from ‘normal’ is enough to ruin your day (and likely your pants).
My kids have never been through a haunted house, but I have. Imagine if I could tell them what was around every corner before we reached it? Or even better – what if I could guide them through the haunted house with all of the lights on? I could take the fear of the unknown and every element of surprise out of the equation.
That’s your job as an architect or technical lead; that is, to help guide your clients through their digital transformation or AEM implementation with the lights on. This is likely their first foray through this process, but not yours. They have questions, they have concerns, and you have answers. Guide them.
I’ve recently been selected to take on a new role called the “Global Adobe Alliance Manager.” It’s my job to act on our clients behalf to help guide them through the entire life-cycle of their digital transformation using Adobe’s offerings.
Your company might not have a dedicated person for this role, but there are some things you should be doing to help guide your clients through this process from start to finish:
If you find this information useful, please share a link to my blog. If there is a topic you’d like to discuss, please use the comments below.
Issue 3 of Adobe’s “Sidekick” magazine is out and my “The Best Way to Test Targeting and Personalization” article was included. Please take a look at Sidekick for more AEM tips and best practices.
In my last article Usability Tip: Use Categories to Prefix Component Names (LINK), I showed how pre-pending a category name to the beginning of the component name aids in sorting and is a quick way to help find the component you want to use. After receiving a few questions regarding the post, a follow-up article is due in the form of a Q&A:
Q: “Why not use the componentGroup property?”
A: This is an excellent way to sort components when the content author manages only one site. You can use the componentGroup sort and categorize and restrict access to the components by use of permissions. However, if the content author manages more than one site within the same instance, this approach breaks down. It is possible to have two or more components with the same name but have different functionality. Joe Gunchy would totally do something like this. My approach uses the site name for the componentGroup property, then further segregates them by adding the category to the name itself.
Q: “Would you be willing to share your macro list of categories for reference?”
A: Yep. Here is the list of categories I used on my most current implementation:
What other categories can you suggest? Please let me know in the comments and we can add them to our list!
I normally don’t mix my business and personal life, but I wanted to share this with you.
This month, the Kansas City Business Magazine wrote a nice article about me, my work, my comedy career, and my writing. Please take a look! Read the magazine ARTICLE here.