このチュートリアルでは、Adobe Experience Manager(AEM)を使用して完全な機能を持つ Web サイトを作成できます。Web サイトは汎用の Web サイトをベースとし、主に Web 開発者をターゲットとしています。開発はすべて、作成者環境内で行われます。
- AEM をインストールします。
- CRXDE Lite(開発環境)にアクセスします。
- CRXDE Lite でプロジェクトの構造を設定します。
- コンテンツページ作成の基礎として使用するテンプレート、コンポーネントおよびスクリプトを作成します。
- Web サイトのルートページを作成し、次にコンテンツページを作成します。
- ページで使用する以下のコンポーネントを作成します。
- 上部ナビゲーション
- リストの子
- ロゴ
- 画像
- テキスト画像
- 検索
- 上部ナビゲーション
- 様々な基盤コンポーネントを含めます。

最終結果のダウンロード
演習を行わずにチュートリアルに従う場合は、website-1.0.zip をダウンロードします。このファイルは、このチュートリアルの結果を含む AEM コンテンツパッケージです。パッケージマネージャーを使用して、作成者インスタンスにパッケージをインストールします。
注意:このパッケージをインストールすると、このチュートリアルを使用して作成した作成者インスタンス上のリソースがすべて上書きされます。
ダウンロード
Web サイトを開発するための AEM インスタンスをインストールするには、作成者インスタンスと発行インスタンスを含むデプロイメント環境の設定手順に従うか、または汎用インストールを実行します。汎用インストールでは、AEM クイックスタート JAR ファイルをダウンロードし、license.properties ファイルを JAR ファイルと同じディレクトリに配置して、JAR ファイルをダブルクリックします。
AEM をインストールしたら、ようこそページで CRXDE Lite のリンクをクリックして CRXDE Lite 開発環境にアクセスします。

注意:
デフォルトポートを使用してローカルにインストールされた AEM 作成者インスタンスの CRXDE Lite の URL は http://localhost:4502/crx/de/ です。
注意:
以下のリンクをクリックして mywebsite.zip をダウンロードします。アーカイブには、デザイン用の static.css および画像ファイルが含まれています。
ダウンロード
-
AEM のようこそページで、「ツール」をクリックします(http://localhost:4502/libs/cq/core/content/welcome.html)。
-
http://localhost:4502 への WebDAV アクセスを使用して、サンプルの static.css ファイルと images フォルダーを、ダウンロードした mywebsite.zip ファイルから /etc/designs/mywebsite フォルダーにコピーします。
- サンプル Web サイトのコンテンツページを作成する際に使用する contentpage テンプレート
- コンテンツのページをレンダリングする際に使用する contentpage コンポーネント
- contentpage スクリプト
サイトの Web ページの基礎として使用するテンプレートを作成します。
テンプレートは、新しいページのデフォルトのコンテンツを定義するものです。複雑な Web サイトでは、複数のテンプレートを使用して様々なタイプのページを作成する場合があります。この演習では、すべてのページが 1 つの単純なテンプレートをベースとしています。
コンテンツを定義し、contentpage テンプレートを使用するコンポーネントを作成します。コンポーネントの場所は、contentpage テンプレートの「リソースタイプ」プロパティの値と一致する必要があります。
-
CRXDE Lite で、/apps/mywebsite/components/contentpage 内の contentpage.jsp ファイルを開きます。このファイルには、デフォルトで以下のコードが含まれています。
<%-- My Website Content Page Component component. This is My Website Content Page Component. --%><% %><%@include file="/libs/foundation/global.jsp"%><% %><%@page session="false" %><% %><% /* TODO add you code here */ %>
-
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>My title</title> </head> <body> <div>My body</div> </body> </html>
-
AEM のようこそページ(http://localhost:4502/libs/cq/core/content/welcome.html)で、「Web サイト」をクリックします。
-
Web ブラウザーの新しいタブまたはウィンドウで、http://localhost:4502/content/mywebsite/en/products.html を開いて Products ページを確認します。

この演習では、スーパータイプが AEM のページコンポーネントとなるように pagecontent コンポーネントを設定します。コンポーネントはスーパータイプの機能を継承するので、pagecontent はページコンポーネントのスクリプトとプロパティを継承します。
例えば、自分のコンポーネントの JSP コード内で、スーパータイプコンポーネントによって提供されているスクリプトを、自分のコンポーネントに含まれているかのように参照できます。
-
<%@include file="/libs/foundation/global.jsp"%><% %><%@page session="false" contentType="text/html; charset=utf-8" %><% %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <cq:include script="head.jsp"/> <cq:include script="body.jsp"/> </html>
ここでは、それぞれページ本体の一部を生成する複数のスクリプトを作成します。次に、pagecontent コンポーネントで body.jsp ファイルを作成して、AEM のページコンポーネントの body.jsp を上書きします。作成する body.jsp ファイルには、ページ本体の様々な部分を生成するスクリプトを含めます。
ヒント:コンポーネントのスーパータイプ内のファイルと同じ名前で相対的な場所も同じファイルがコンポーネントに含まれている場合、これをオーバーレイと呼びます。
-
<%@include file="/libs/foundation/global.jsp"%><% %><body> <div id="CQ"> <div class="topnav">topnav</div> <div class="content"> <cq:include script="left.jsp" /> <cq:include script="center.jsp" /> <cq:include script="right.jsp" /> </div> <div class="footer"> <div class="toolbar">toolbar</div> </div> </div> </body>
ここでは、ナビゲーションを簡単にするために、Web サイトのすべての最上位ページへのリンクを表示するコンポーネントを作成します。このコンポーネントのコンテンツは、contentpage テンプレートを使用して作成されたすべてのページの上部に表示されます。
上部ナビゲーションコンポーネント(topnav)の最初のバージョンでは、ナビゲーション項目はテキストリンクのみです。2 番目のバージョンでは、画像ナビゲーションリンクと共に topnav を実装します。

-
<%@include file="/libs/foundation/global.jsp"%><% %><%@ page import="java.util.Iterator, com.day.text.Text, com.day.cq.wcm.api.PageFilter, com.day.cq.wcm.api.Page" %><% /* get starting point of navigation */ Page navRootPage = currentPage.getAbsoluteParent(2); if (navRootPage == null && currentPage != null) { navRootPage = currentPage; } if (navRootPage != null) { Iterator<Page> children = navRootPage.listChildren(new PageFilter(request)); while (children.hasNext()) { Page child = children.next(); %><a href="<%= child.getPath() %>.html"><%=child.getTitle() %></a><% } } %>
ハイパーテキストの代わりに画像リンクを使用してナビゲーションを制御するように、topnav コンポーネントのレンダリングスクリプトを強化します。画像には、リンクターゲットのタイトルとサブタイトルが含まれます。
この演習では、Sling の要求処理を実証します。ページナビゲーションリンクに使用する画像を動的に生成するスクリプトを呼び出すように topnav.jsp スクリプトを変更します。この演習では、Sling で画像ソースファイルの URL を解析し、画像のレンダリングに使用するスクリプトを特定します。
例えば、Products ページへの画像リンクのソースは、http://localhost:4502/content/mywebsite/en/products.navimage.png などになります。Sling では、この URL を解析して、リソースタイプおよびリソースのレンダリングに使用するスクリプトを特定します。
- Sling がリソースのパスを /content/mwebysite/en/products.png と特定します。
- Sling がこのパスを /content/mywebsite/en/products ノードと照合します。
- Sling がこのノードの sling:resourceType を mywebsite/components/contentpage と特定します。
- Sling が、このコンポーネント内で、URL セレクター(navimage)およびファイル名拡張子(png)に最も一致するスクリプトを見つけます。
この演習では、Sling はこれらの URL を、ユーザーが作成する /apps/mywebsite/components/contentpage/navimage.png.java スクリプトと照合します。
-
以下のコードを navimage.png.java にコピーします。このコードによって、AbstractImageServlet クラスが拡張されます。
- AbstractImageServlet は、現在のリソースのプロパティを格納する ImageContext オブジェクトを作成します。
- リソースの親ページは、ImageContext オブジェクトから抽出されます。その後、ページのタイトルとサブタイトルが取得されます。
- ImageHelper は、サイトデザイン、ページタイトルおよびページサブタイトルの navimage_bg.jpg ファイルから画像を生成するために使用します。
package apps.mywebsite.components.contentpage; import java.awt.Color; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import javax.jcr.RepositoryException; import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; import com.day.cq.wcm.api.components.Component; import com.day.cq.wcm.api.designer.Designer; import com.day.cq.commons.SlingRepositoryException; import com.day.cq.wcm.commons.WCMUtils; import com.day.cq.wcm.commons.AbstractImageServlet; import com.day.cq.commons.ImageHelper; import com.day.image.Font; import com.day.image.Layer; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; /** * Renders the navigation image */ public class navimage_png extends AbstractImageServlet { protected Layer createLayer(ImageContext ctx) throws RepositoryException, IOException { PageManager pageManager = ctx.resolver.adaptTo(PageManager.class); Page currentPage = pageManager.getContainingPage(ctx.resource); /* constants for image appearance */ int scale = 6; int paddingX = 24; int paddingY = 24; Color bgColor = new Color(0x004a565c, true); /* obtain the page title */ String title = currentPage.getTitle(); if (title == null) { title = currentPage.getName(); } /* format the title text */ title = title.toUpperCase(); Paint titleColor = Color.WHITE; Font titleFont = new Font("Myriad Pro", 10 * scale, Font.BOLD); int titleBase = 10 * scale; /* obtain and format the page subtitle */ String subtitle = currentPage.getProperties().get("subtitle", ""); Paint subtitleColor = new Color(0xffa9afb1, true); Font subTitleFont = new Font("Tahoma", 7); int subTitleBase = 20; /* create a layer that contains the background image from the mywebsite design */ Designer dg = ctx.resolver.adaptTo(Designer.class); String imgPath = new String(dg.getDesignPath(currentPage)+"/images/navimage_bg.jpg"); Layer bg = ImageHelper.createLayer(ctx.resolver.resolve(imgPath)); /* draw the title text (4 times bigger) */ Rectangle2D titleExtent = titleFont.getTextExtent(0, 0, 0, 0, title, Font.ALIGN_LEFT, 0, 0); Rectangle2D subtitleExtent = subTitleFont.getTextExtent(0, 0, 0, 0, subtitle, Font.ALIGN_LEFT, 0, 0); /* ensure subtitleExtent is wide enough */ if ( subtitle.length() > 0 ) { int titleWidth = (int)titleExtent.getWidth() / scale; if ( subtitleExtent.getWidth() > titleWidth && subtitleExtent.getWidth() + 2 * paddingX > bg.getWidth() ) { int charWidth = (int)subtitleExtent.getWidth() / subtitle.length(); int maxWidth = (bg.getWidth() > titleWidth + 2 * paddingX ? bg.getWidth() - 2 * paddingX : titleWidth); int len = (maxWidth - ( 2 * charWidth) ) / charWidth; subtitle = subtitle.substring(0, len) + "..."; subtitleExtent = subTitleFont.getTextExtent(0, 0, 0, 0, subtitle, Font.ALIGN_LEFT, 0, 0); } } int width = Math.max((int) titleExtent.getWidth(), (int) subtitleExtent.getWidth()); /* create the text layer */ Layer text = new Layer(width, (int) titleExtent.getHeight() + 40, new Color(0x01ffffff, true)); text.setPaint(titleColor); text.drawText(0, titleBase, 0, 0, title, titleFont, Font.ALIGN_LEFT | Font.ALIGN_BASE, 0, 0); text.resize(text.getWidth() / scale, text.getHeight() / scale); text.setX(0); text.setY(0); if (subtitle.length() > 0) { /* draw the subtitle normal sized */ text.setPaint(subtitleColor); text.drawText(0, subTitleBase, 0, 0, subtitle, subTitleFont, Font.ALIGN_LEFT | Font.ALIGN_BASE, 0, 0); } /* merge the image and text layers */ text.setY(paddingY); text.setX(paddingX); text.setBackgroundColor(bgColor); int bgWidth = bg.getWidth(); if ( text.getWidth() + 2 * paddingX > bgWidth ) { bgWidth = text.getWidth() + 2 * paddingX; bg.resize(bgWidth, bg.getHeight()); } bg.merge(text); return bg; } }
ページ(製品ページなど)のタイトル、説明、日付を含むページリンクのリストを生成するリストの子(listchildren)コンポーネントを作成します。このリンクのターゲットは、現在のページの、またはコンポーネントのダイアログで指定されているルートページの子ページです。

-
CRXDE Lite で、Product 1 ページの説明と日付を設定します。
- /content/mywebsite/en/products/product1/jcr:content ノードを選択します。
- 「プロパティ」タブで、以下の値を入力します。
- 名前:jcr:description
- タイプ:String
- 値:This is a description of the Product 1!
- 「追加」をクリックします。
- 「プロパティ」タブで、以下の値を使用してもう 1 つのプロパティを作成します。
- 名前:date
- タイプ:String
- 値:02/14/2008
- 「追加」をクリックします。
- 「すべて保存」をクリックします。
-
CRXDE Lite で、Product 2 ページの説明と日付を設定します。
- /content/mywebsite/en/products/product2/jcr:content ノードを選択します。
- 「プロパティ」タブで、以下の値を入力します。
- 名前:jcr:description
- タイプ:String
- 値:This is a description of the Product 2!
- 「追加」をクリックします。
- 同じテキストボックスで、前の値を以下の値に置き換えます。
- 名前:date
- タイプ:String
- 値:05/11/2012
- 「追加」をクリックします。
- 「すべて保存」をクリックします。
-
<%@include file="/libs/foundation/global.jsp"%><% %><%@ page import="java.util.Iterator, com.day.cq.wcm.api.PageFilter"%><% /* Create a new Page object using the path of the current page */ String listroot = properties.get("listroot", currentPage.getPath()); Page rootPage = pageManager.getPage(listroot); /* iterate through the child pages and gather properties */ if (rootPage != null) { Iterator<Page> children = rootPage.listChildren(new PageFilter(request)); while (children.hasNext()) { Page child = children.next(); String title = child.getTitle() == null ? child.getName() : child.getTitle(); String date = child.getProperties().get("date",""); %><div class="item"> <a href="<%= child.getPath() %>.html"><b><%= title %></b></a> <span><%= date %></span><br> <%= child.getProperties().get("jcr:description","") %><br> </div><% } } %>
このコンポーネントの完全な動作は、Products ページを表示して確認できます。
- 親ページ(「リストルートのパス」)が定義されていないとき。
- 親ページ(「リストルートのパス」)が定義されているとき。
会社のロゴを表示し、サイトのホームページへのリンクを提供するコンポーネントを作成します。このコンポーネントにはデザインモードのダイアログが含まれており、プロパティ値はサイトデザイン(/etc/designs/mywebsite)に格納されます。
- このデザインを使用するページに追加されるコンポーネントのすべてのインスタンスにプロパティ値が適用されます。
- プロパティの設定には、このデザインを使用するページ上にあるコンポーネントのどのインスタンスでも使用できます。
デザインモードのダイアログには、画像とリンクパスを設定するためのプロパティが含まれています。ロゴコンポーネントは、Web サイトのすべてのページの左上に配置されます。

注意:
Adobe Experience Manager は、より充実した機能を持つロゴコンポーネント(/libs/foundation/components/logo)を提供します。
-
<%@include file="/libs/foundation/global.jsp"%><% %><%@ page import="com.day.text.Text, com.day.cq.wcm.foundation.Image, com.day.cq.commons.Doctype" %><% /* obtain the path for home */ long absParent = currentStyle.get("absParent", 2L); String home = Text.getAbsoluteParent(currentPage.getPath(), (int) absParent); /* obtain the image */ Resource res = currentStyle.getDefiningResource("imageReference"); if (res == null) { res = currentStyle.getDefiningResource("image"); } /* if no image use text link, otherwise draw the image */ %> <a href="<%= home %>.html"><% if (res == null) { %>Home<% } else { Image img = new Image(res); img.setItemName(Image.NN_FILE, "image"); img.setItemName(Image.PN_REFERENCE, "imageReference"); img.setSelector("img"); img.setDoctype(Doctype.fromRequest(request)); img.setAlt("Home"); img.draw(out); } %></a>
ロゴイメージを取得してページに書き込むスクリプトを作成します。
- logo コンポーネントノードを右クリックし、作成/ファイルを作成をクリックして、img.GET.java という名前のスクリプトファイルを作成します。
- ファイルを開き、以下のコードをファイルにコピーして、「すべて保存」をクリックします。
package apps.mywebsite.components.logo; import java.io.IOException; import java.io.InputStream; import javax.jcr.RepositoryException; import javax.jcr.Property; import javax.servlet.http.HttpServletResponse; import com.day.cq.wcm.foundation.Image; import com.day.cq.wcm.commons.RequestHelper; import com.day.cq.wcm.commons.WCMUtils; import com.day.cq.wcm.commons.AbstractImageServlet; import com.day.cq.commons.SlingRepositoryException; import com.day.image.Layer; import org.apache.commons.io.IOUtils; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; /** * Renders an image */ public class img_GET extends AbstractImageServlet { protected Layer createLayer(ImageContext c) throws RepositoryException, IOException { /* don't create the layer yet. handle everything later */ return null; } protected void writeLayer(SlingHttpServletRequest req, SlingHttpServletResponse resp, ImageContext c, Layer layer) throws IOException, RepositoryException { Image image = new Image(c.resource); image.setItemName(Image.NN_FILE, "image"); image.setItemName(Image.PN_REFERENCE, "imageReference"); if (!image.hasContent()) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); return; } /* get pure layer */ layer = image.getLayer(false, false, false); /* do not re-encode layer, just spool */ Property data = image.getData(); InputStream in = data.getStream(); resp.setContentLength((int) data.getLength()); String contentType = image.getMimeType(); if (contentType.equals("application/octet-stream")) { contentType=c.requestImageType; } resp.setContentType(contentType); IOUtils.copy(in, resp.getOutputStream()); in.close(); resp.flushBuffer(); } }
段落システム(parsys)は、段落のリストを管理するので、Web サイトの重要部分です。これを使用すると、作成者は段落コンポーネントをページに追加し、構造を指定できます。
parsys コンポーネント(基盤コンポーネントの 1 つ)を、contentpage コンポーネントに追加します。
注意:
Adobe Experience Manager は、より充実した機能を持つ画像コンポーネント(/libs/foundation/components/image)を提供します。
- /apps/mywebsite/components/logo ノードを右クリックして、「コピー」をクリックします。
- /apps/mywebsite/components ノードを右クリックして、「貼り付け」をクリックします。
- Copy of logo ノードを右クリックし、「名前を変更」をクリックし、既存のテキストを削除して、"image" と入力します。
- image コンポーネントノードを選択して、以下のプロパティ値を変更します。
- jcr:title:My Image Component
- jcr:description:This is My Image Component
- 以下のプロパティ値を使用して、image ノードにプロパティを追加します。
- 名前:componentGroup
- タイプ:String
- 値:MyWebsite
- image ノードの下で、design_dialog ノードの名前を "dialog" に変更します。
- logo.jsp の名前を "image.jsp" に変更します。
- img.GET.java を開いて、パッケージを "apps.mywebsite.components.image" に変更します。

-
<%@include file="/libs/foundation/global.jsp"%><% %><%@ page import="com.day.cq.commons.Doctype, com.day.cq.wcm.foundation.Image, com.day.cq.wcm.api.components.DropTarget, com.day.cq.wcm.api.components.EditConfig, com.day.cq.wcm.commons.WCMUtils" %><% /* global.jsp provides access to the current resource through the resource object */ Image img = new Image(resource); img.setItemName(Image.NN_FILE, "image"); img.setItemName(Image.PN_REFERENCE, "imageReference"); img.setSelector("img"); img.setDoctype(Doctype.fromRequest(request)); img.setAlt("Home"); img.draw(out); %>
cq:editConfig ノードタイプを使用すると、プロパティを編集するときに、コンポーネントの一定の動作を設定できます。
ここでは、cq:editConfig ノードを使用して、コンテンツファインダーから画像コンポーネントへ、アセットをドラッグできるようにします。
CRXDE Lite で、/apps/mywebsite/components/image ノードの下に、以下のように新しいノードを作成します。
名前:cq:editConfig
タイプ:cq:EditConfig
cq:editConfig ノードの下に、以下のように新しいノードを作成します。
名前:cq:dropTargets
タイプ:cq:DropTargetConfig
cq:dropTargets ノードの下に、以下のように新しいノードを作成します。
名前:image
タイプ:nt:unstructured
- CRXDE で、プロパティを以下のように設定します。

ここでは、Web サイトでコンテンツを検索するコンポーネントを作成します。この検索コンポーネントは、任意のページ(特殊な検索結果ページなど)の段落システムに配置できます。
検索入力ボックスは、English ページに以下のように表示されます。

-
<%@ page import="com.day.cq.wcm.foundation.Search,com.day.cq.tagging.TagManager" %> <%@include file="/libs/foundation/global.jsp" %><% %><cq:setContentBundle/><% Search search = new Search(slingRequest); String searchIn = (String) properties.get("searchIn"); String requestSearchPath = request.getParameter("path"); if (searchIn != null) { /* only allow the "path" request parameter to be used if it is within the searchIn path configured */ if (requestSearchPath != null && requestSearchPath.startsWith(searchIn)) { search.setSearchIn(requestSearchPath); } else { search.setSearchIn(searchIn); } } else if (requestSearchPath != null) { search.setSearchIn(requestSearchPath); } pageContext.setAttribute("search", search); TagManager tm = resourceResolver.adaptTo(TagManager.class); %><c:set var="trends" value="${search.trends}"/><% %><center> <form action="${currentPage.path}.html"> <input size="41" maxlength="2048" name="q" value="${fn:escapeXml(search.query)}"/> <input value="<fmt:message key="searchButtonText"/>" type="submit" /> </form> </center> <br/> <c:set var="result" value="${search.result}"/> <c:choose> <c:when test="${empty result && empty search.query}"> </c:when> <c:when test="${empty result.hits}"> <c:if test="${result.spellcheck != null}"> <p><fmt:message key="spellcheckText"/> <a href="<c:url value="${currentPage.path}.html"><c:param name="q" value="${result.spellcheck}"/></c:url>"><b><c:out value="${result.spellcheck}"/></b></a></p> </c:if> <fmt:message key="noResultsText"> <fmt:param value="${fn:escapeXml(search.query)}"/> </fmt:message> </c:when> <c:otherwise> <p class="searchmeta">Results ${result.startIndex + 1} - ${result.startIndex + fn:length(result.hits)} of ${result.totalMatches} for <b>${fn:escapeXml(search.query)}</b>. (${result.executionTime} seconds)</p> <br/> <div class="searchresults"> <div class="results"> <c:forEach var="hit" items="${result.hits}" varStatus="status"> <div class="hit"> <a href="${hit.URL}">${hit.title}</a> <div class="excerpt">${hit.excerpt}</div> <div class="hiturl"> ${hit.URL}<c:if test="${!empty hit.properties['cq:lastModified']}"> - <c:catch><fmt:formatDate value="${hit.properties['cq:lastModified'].time}" dateStyle="medium"/></c:catch></c:if> - <a href="${hit.similarURL}"><fmt:message key="similarPagesText"/></a> </div></div> </c:forEach> </div> <br/> <div class="searchRight"> <c:if test="${fn:length(trends.queries) > 0}"> <p><fmt:message key="searchTrendsText"/></p> <div class="searchTrends"> <c:forEach var="query" items="${trends.queries}"> <a href="<c:url value="${currentPage.path}.html"><c:param name="q" value="${query.query}"/></c:url>"><span style="font-size:${query.size}px"><c:out value="${query.query}"/></span></a> </c:forEach> </div> </c:if> <c:if test="${result.facets.languages.containsHit}"> <p>Languages</p> <c:forEach var="bucket" items="${result.facets.languages.buckets}"> <c:set var="bucketValue" value="${bucket.value}"/> <c:set var="label" value='<%= new java.util.Locale((String) pageContext.getAttribute("bucketValue")).getDisplayLanguage(request.getLocale()) %>'/> <c:choose> <c:when test="${param.language != null}">${label} (${bucket.count}) - <a href="<cq:requestURL><cq:removeParam name="language"/></cq:requestURL>">remove filter</a></c:when> <c:otherwise><a title="filter results" href="<cq:requestURL><cq:addParam name="language" value="${bucket.value}"/></cq:requestURL>">${label} (${bucket.count})</a></c:otherwise> </c:choose><br/> </c:forEach> </c:if> <c:if test="${result.facets.tags.containsHit}"> <p>Tags</p> <c:forEach var="bucket" items="${result.facets.tags.buckets}"> <c:set var="bucketValue" value="${bucket.value}"/> <c:set var="tag" value="<%= tm.resolve((String) pageContext.getAttribute("bucketValue")) %>"/> <c:if test="${tag != null}"> <c:set var="label" value="${tag.title}"/> <c:choose> <c:when test="<%= request.getParameter("tag") != null && java.util.Arrays.asList(request.getParameterValues("tag")).contains(pageContext.getAttribute("bucketValue")) %>">${label} (${bucket.count}) - <a href="<cq:requestURL><cq:removeParam name="tag" value="${bucket.value}"/></cq:requestURL>">remove filter</a></c:when> <c:otherwise><a title="filter results" href="<cq:requestURL><cq:addParam name="tag" value="${bucket.value}"/></cq:requestURL>">${label} (${bucket.count})</a></c:otherwise> </c:choose><br/> </c:if> </c:forEach> </c:if> <c:if test="${result.facets.mimeTypes.containsHit}"> <jsp:useBean id="fileTypes" class="com.day.cq.wcm.foundation.FileTypes"/> <p>File types</p> <c:forEach var="bucket" items="${result.facets.mimeTypes.buckets}"> <c:set var="bucketValue" value="${bucket.value}"/> <c:set var="label" value="${fileTypes[bucket.value]}"/> <c:choose> <c:when test="<%= request.getParameter("mimeType") != null && java.util.Arrays.asList(request.getParameterValues("mimeType")).contains(pageContext.getAttribute("bucketValue")) %>">${label} (${bucket.count}) - <a href="<cq:requestURL><cq:removeParam name="mimeType" value="${bucket.value}"/></cq:requestURL>">remove filter</a></c:when> <c:otherwise><a title="filter results" href="<cq:requestURL><cq:addParam name="mimeType" value="${bucket.value}"/></cq:requestURL>">${label} (${bucket.count})</a></c:otherwise> </c:choose><br/> </c:forEach> </c:if> <c:if test="${result.facets.lastModified.containsHit}"> <p>Last Modified</p> <c:forEach var="bucket" items="${result.facets.lastModified.buckets}"> <c:choose> <c:when test="${param.from == bucket.from && param.to == bucket.to}">${bucket.value} (${bucket.count}) - <a href="<cq:requestURL><cq:removeParam name="from"/><cq:removeParam name="to"/></cq:requestURL>">remove filter</a></c:when> <c:otherwise><a title="filter results" href="<cq:requestURL><cq:removeParam name="from"/><cq:removeParam name="to"/><c:if test="${bucket.from != null}"><cq:addParam name="from" value="${bucket.from}"/></c:if><c:if test="${bucket.to != null}"><cq:addParam name="to" value="${bucket.to}"/></c:if></cq:requestURL>">${bucket.value} (${bucket.count})</a></c:otherwise> </c:choose><br/> </c:forEach> </c:if> <c:if test="${fn:length(search.relatedQueries) > 0}"> <br/><br/><div class="related"> <fmt:message key="relatedSearchesText"/> <c:forEach var="rq" items="${search.relatedQueries}"> <a href="${currentPage.path}.html?q=${rq}"><c:out value="${rq}"/></a> </c:forEach></div> </c:if> </div> <c:if test="${fn:length(result.resultPages) > 1}"> <div class="pagination"> <fmt:message key="resultPagesText"/> <c:if test="${result.previousPage != null}"> <a href="${result.previousPage.URL}"><fmt:message key="previousText"/></a> </c:if> <c:forEach var="page" items="${result.resultPages}"> <c:choose> <c:when test="${page.currentPage}">${page.index + 1}</c:when> <c:otherwise> <a href="${page.URL}">${page.index + 1}</a> </c:otherwise> </c:choose> </c:forEach> <c:if test="${result.nextPage != null}"> <a href="${result.nextPage.URL}"><fmt:message key="nextText"/></a> </c:if> </div> </c:if> </div> </c:otherwise> </c:choose>