AEM Tagging Framework aem-tagging-framework
To tag content and leverage the AEM tagging infrastructure :
- The tag must exist as a node of type
cq:Tag
under the taxonomy root node. - The tagged content node’s
NodeType
must include thecq:Taggable
mixin. - The TagID is added to the content node’s
cq:tags
property and resolves to a node of typecq:Tag
.
Tags : cq:Tag Node Type tags-cq-tag-node-type
The declaration of a tag is captured in the repository in a node of type cq:Tag.
A tag can be a simple word (e.g. fruit
) or represent a hierarchical taxonomy (e.g. fruit/apple
, meaning both fruits in general and the more specific apple).
Tags are identified by a unique TagID.
A tag has optional meta information such as a title, localized titles, and a description. The title should be displayed in user interfaces instead of the TagID
, when present.
The tagging framework also provides the ability to restrict authors and site visitors to use only specific, predefined tags.
Tag Characteristics tag-characteristics
- The node type is
cq:Tag
. - The node name is a component of the
TagID
. - The
TagID
always includes a namespace. - Optional
jcr:title
property (the title to display in the UI) - Optional
jcr:description
property - When containing child nodes, it is referred to as a container tag.
- It is stored in the repository below a base path called the taxonomy root node.
TagID tagid
A TagID
identifies a path which resolves to a tag node in the repository.
Typically, the TagID
is a shorthand starting with the namespace or it can be absolute starting from the taxonomy root node.
When content is tagged, if it does not yet exist, the cq:tags
property is added to the content node and the TagID
is added to the property’s string array value.
The TagID
consists of a namespace followed by the local TagID
. Container tags have sub-tags that represent a hierarchical order in the taxonomy. Sub-tags can be used to reference tags same as any local TagID
. For example tagging content with fruit
is allowed, even if it is a container tag with sub-tags, such as fruit/apple
and fruit/banana
.
Taxonomy Root Node taxonomy-root-node
The taxonomy root node is the base path for all tags in the repository. The taxonomy root node must not be a node of type cq:Tag
.
In AEM, the base path is /content/cq:tags
and the root node is of type cq:Folder
.
Tag Namespace tag-namespace
Namespaces allow group things. The most typical use case is to have a namespace per site (e.g. public, internal, and portal) or per larger application (e.g. Sites, Assets, Forms) but namespaces can be used for various other needs. Namespaces are used in the user interface to only show the subset of tags (i.e. tags of a certain namespace) that is applicable to the current content.
The tag’s namespace is the first level in the taxonomy subtree, which is the node immediately below the taxonomy root node. A namespace is a node of type cq:Tag
whose parent is not a cq:Tag
node type.
All tags have a namespace. If no namespace is specified, the tag is assigned to the default namespace, which is TagID
default
(title is Standard Tags
) that is /content/cq:tags/default
.
Container Tags container-tags
A container tag is a node of type cq:Tag
containing any number and type of child nodes, which makes it possible to enhance the tag model with custom metadata.
Furthermore, container tags (or super-tags) in a taxonomy serve as the sub-summation of all sub-tags. For example, content tagged with fruit/apple
is considered to be tagged with fruit
as well. This means that searching for content just tagged with fruit
would also find the content tagged with fruit/apple
.
Resolving TagIDs resolving-tagids
If the tag ID contains a colon :
, the colon separates the namespace from the tag or sub-taxonomy, which are then separated with normal slashes /
. If the tag ID does not have a colon, the default namespace is implied.
The standard and only location of tags is below /content/cq:tags
.
Tag referencing non-existing paths or paths that do not point to a cq:Tag
node are considered invalid and are ignored.
The following table shows some sample TagIDs
, their elements, and how the TagID
resolves to an absolute path in the repository.
TagID
dam:fruit/apple/braeburn
dam
fruit/apple/braeburn
fruit
, apple
braeburn
/content/cq:tags/dam/fruit/apple/braeburn
color/red
default
color/red
color
red
/content/cq:tags/default/color/red
sky
default
sky
sky
/content/cq:tags/default/sky
dam:
dam
/content/cq:tags/dam
/content/cq:tags/category/car
category
car
car
car
/content/cq:tags/category/car
Localization of Tag Title localization-of-tag-title
When the tag includes the optional title string (jcr:title
) it is possible to localize the title for display by adding the property jcr:title.<locale>
.
For more details see:
- Tags in Different Languages, which describes use of the APIs.
- Managing Tags in Different Languages, which describes use of the Tagging console.
Access Control access-control
Tags exist as nodes in the repository under the taxonomy root node. Allowing or denying authors and site visitors the ability to create tags in a given namespace can be achieved by setting appropriate ACLs in the repository.
Also, denying read permissions for certain tags or namespaces will control the ability to apply tags to specific content.
A typical configuration includes:
- Allowing the
tag-administrators
group/role write access to all namespaces (add/modify under/content/cq:tags
).- This group comes with AEM out-of-the-box.
- Allowing users/authors read access to all the namespaces that should be readable to them (mostly all).
- Allowing users/authors write access to those namespaces where tags should be freely definable by users/authors (
add_node
under/content/cq:tags/some_namespace
)
Taggable Content : cq:Taggable Mixin taggable-content-cq-taggable-mixin
In order for application developers to attach tagging to a content type, the node’s registration (CND) must include the cq:Taggable
mixin or the cq:OwnerTaggable
mixin.
The cq:OwnerTaggable
mixin, which inherits from cq:Taggable
, is intended to indicate that the content can be classified by the owner/author. In AEM, it is only an attribute of the cq:PageContent
node. The cq:OwnerTaggable
mixin is not required by the tagging framework.
jcr:content
node). Examples include:- Pages (
cq:Page
) where thejcr:content
node is of typecq:PageContent
which includes thecq:Taggable
mixin. - Assets (
cq:Asset
) where thejcr:content/metadata
node always has thecq:Taggable
mixin.
Node Type Notation (CND) node-type-notation-cnd
Node Type definitions exist in the repository as CND files. The CND notation is defined as part of the JCR documentation here.
The essential definitions for the Node Types included in AEM are as follows:
[cq:Tag] > mix:title, nt:base
orderable
- * (undefined) multiple
- * (undefined)
+ * (nt:base) = cq:Tag version
[cq:Taggable]
mixin
- cq:tags (string) multiple
[cq:OwnerTaggable] > cq:Taggable
mixin
Tagged Content: cq:tags Property tagged-content-cq-tags-property
The cq:tags
property is a string array used to store one or more TagID
s when they are applied to content by authors or site visitors. The property only has meaning when added to a node which is defined with the cq:Taggable
mixin.
cq:tags
.Moving and Merging Tags moving-and-merging-tags
The following is a description of the effects in the repository when moving or merging tags using the Tagging console:
-
When a tag A is moved or merged into tag B under
/content/cq:tags
:- Tag A is not deleted and gets a
cq:movedTo
property. - Tag B is created (in case of a move) and gets a
cq:backlinks
property.
- Tag A is not deleted and gets a
-
cq:movedTo
points to tag B.-
This property means that tag A has been moved or merged into tag B.
-
Moving tag B will update this property accordingly. Tag A is thus hidden and is only kept in the repository to resolve tag IDs in content nodes pointing to tag A.
-
The tag garbage collector removes tags like tag A once no more content nodes point to them.
-
A special value for the
cq:movedTo
property isnirvana
: it is applied when the tag is deleted but cannot be removed from the repository because there are subtags with acq:movedTo
that must be kept.note note NOTE The cq:movedTo
property is only added to the moved or merged tag if either of these conditions are met:- The tag is used in content (meaning it has a reference).
- The tag has children that have already been moved.
-
-
cq:backlinks
keeps the references in the other direction, i.e. it keeps a list of all the tags that have been moved to or merged with tag B.- This is mostly required to keep
cq:movedTo
properties up to date when tag B is moved/merged/deleted as well or when tag B is activated, in which case all its backlinks tags must be activated as well.
- This is mostly required to keep
cq:backlinks
property is only added to the moved or merged tag if either of these conditions are met:- The tag is used in content (meaning it has a reference).
- The tag has children that have already been moved.
-
Reading a
cq:tags
property of a content node involves the following resolving:-
If there is no match under
/content/cq:tags
, no tag is returned. -
If the tag has a
cq:movedTo
property set, the referenced tag ID is followed.- This step is repeated as long as the followed tag has a
cq:movedTo
property.
- This step is repeated as long as the followed tag has a
-
If the followed tag does not have a
cq:movedTo
property, the tag is read.
-
-
To publish the change when a tag has been moved or merged, the
cq:Tag
node and all its backlinks must be replicated.- This is automatically done when the tag is activated in the tag administration console.
-
Later updates to the page’s
cq:tags
property automatically clean up the “old” references.- This is triggered because resolving a moved tag through the API returns the destination tag, thus providing the destination tag ID.
Tags Migration tags-migration
In Adobe Experience Manager 6.4 onwards, tags are stored under /content/cq:tags
. However, in scenarios where Adobe Experience Manager has been upgraded from previous version the tags are still present under the old location /etc/tags
. In upgraded systems tags need to be migrated to /content/cq:tags
.
geometrixx-outdoors:activity/biking
) instead of hard coding the tag base path (for example, /etc/tags/geometrixx-outdoors/activity/biking
).com.day.cq.tagging.servlets.TagListServlet
can be used.If Upgraded AEM Instance Supports TagManager API**
-
At the start of component, the TagManager API detects whether it is an upgraded AEM instance. In upgraded system, tags are stored under
/etc/tags
. -
The TagManager API then runs in backward compatibility mode, which means the API uses
/etc/tags
as the base path. If not, it uses new location/content/cq:tags
. -
Update the tags location.
-
After migrating tags to the new location, run the following script.
import org.apache.sling.api.resource.*
import javax.jcr.*
ResourceResolverFactory resourceResolverFactory = osgi.getService(ResourceResolverFactory.class);
ResourceResolver resolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
Session session = resolver.adaptTo(Session.class);
def queryManager = session.workspace.queryManager;
def statement = "/jcr:root/content/cq:tags//element(*, cq:Tag)[jcr:contains(@cq:movedTo,\'/etc/tags\') or jcr:contains(@cq:backlinks,\'/etc/tags\')]";
def query = queryManager.createQuery(statement, "xpath");
println "query = ${query.statement}\n";
def tags = query.execute().getNodes();
tags.each { node ->
def tagPath = node.path;
println "tag = ${tagPath}";
if(node.hasProperty("cq:movedTo") && node.getProperty("cq:movedTo").getValue().toString().startsWith("/etc/tags")){
def movedTo = node.getProperty("cq:movedTo").getValue().toString();
println "cq:movedTo = ${movedTo} \n";
movedTo = movedTo.replace("/etc/tags","/content/cq:tags");
node.setProperty("cq:movedTo",movedTo);
} else if(node.hasProperty("cq:backlinks")){
String[] backLinks = node.getProperty("cq:backlinks").getValues();
int count = 0;
backLinks.each { value ->
if(value.startsWith("/etc/tags")){
println "cq:backlinks = ${value}\n";
backLinks[count] = value.replace("/etc/tags","/content/cq:tags");
}
count++;
}
node.setProperty("cq:backlinks",backLinks);
}
}
session.save();
println "---------------------------------Success-------------------------------------"
The script fetches all those tags that have /etc/tags
in the value of cq:movedTo/cq:backLinks
property. It then iterates through the fetched result set and resolves the cq:movedTo
and cq:backlinks
property values to /content/cq:tags
paths (in the case where /etc/tags
is detected in the value).
If Upgraded AEM Instance Runs on Classic UI**
/etc/tags
needs to be created followed by cq-tagging
component restart.In case the upgraded AEM instances is supported by the TagManager API and runs in Classic UI:
-
Once references to old tag base path
/etc/tags
are replaced by using tagId or new tag location/content/cq:tags
, you can migrate tags to the new location/content/cq:tags
in CRX DE followed by component restart. -
After migrating tags to the new location, run the above mentioned script.