diff --git a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortlet.java b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortlet.java index a0b952c3b..6e19c9d55 100644 --- a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortlet.java +++ b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortlet.java @@ -18,24 +18,35 @@ */ package com.arsdigita.cms.portlet; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageFactory; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.cms.CMS; import com.arsdigita.cms.ContentBundle; import com.arsdigita.cms.ContentItem; import com.arsdigita.cms.ContentPage; import com.arsdigita.cms.ContentSection; import com.arsdigita.cms.ContentSectionCollection; import com.arsdigita.cms.ItemCollection; +import com.arsdigita.cms.dispatcher.SimpleXMLGenerator; +import com.arsdigita.cms.portlet.utils.HttpServletRequestAdapter; +import com.arsdigita.cms.portlet.utils.HttpServletResponseAdapter; import com.arsdigita.domain.DataObjectNotFoundException; import com.arsdigita.domain.DomainObjectFactory; -import com.arsdigita.globalization.GlobalizationHelper; import com.arsdigita.persistence.OID; import com.arsdigita.portal.JSRPortlet; +import com.arsdigita.templating.PresentationManager; +import com.arsdigita.templating.Templating; +import com.arsdigita.xml.Document; +import com.arsdigita.xml.Element; import java.io.IOException; import java.io.PrintWriter; +import java.math.BigDecimal; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; -import java.util.ResourceBundle; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletException; @@ -43,6 +54,9 @@ import javax.portlet.PortletPreferences; import javax.portlet.PortletRequestDispatcher; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; @@ -83,13 +97,13 @@ public class ContentItemJSRPortlet extends JSRPortlet { */ @Override protected void doEdit(final RenderRequest request, final RenderResponse response) - throws PortletException, IOException { + throws PortletException, IOException { //response.setContentType("text/html"); //PrintWriter writer = new PrintWriter(response.getWriter()); //writer.println("You're now in Edit mode."); final ContentSectionCollection contentSections = ContentSection.getAllSections(); final List sections = new ArrayList((int) contentSections - .size()); + .size()); while (contentSections.next()) { sections.add(contentSections.getContentSection()); @@ -101,7 +115,7 @@ public class ContentItemJSRPortlet extends JSRPortlet { if ((selectedContentSection != null)) { final ContentSection selectedSection = new ContentSection(OID.valueOf( - selectedContentSection)); + selectedContentSection)); final ItemCollection items = selectedSection.getItems(); items.addFilter(String.format("(lower(name) LIKE lower('%%%s%%'))", search)); items.addVersionFilter(true); @@ -119,7 +133,7 @@ public class ContentItemJSRPortlet extends JSRPortlet { request.setAttribute("errors", errors); final String itemOID = request.getPreferences().getValue(response.getNamespace().concat( - PREFS_SELECTED_ITEM), ""); + PREFS_SELECTED_ITEM), ""); if (!itemOID.isEmpty()) { try { final OID oid = OID.valueOf(itemOID); @@ -148,15 +162,15 @@ public class ContentItemJSRPortlet extends JSRPortlet { //errors.add(String.format("The OID '%s' set in the preferences is invalid.", // itemOID)); errors.add(MessageFormat.format(getResourceBundle(request.getLocale()).getString( - "contentItemJSRPortlet.errors.perferences.illegal_oid"), + "contentItemJSRPortlet.errors.perferences.illegal_oid"), itemOID)); } catch (DataObjectNotFoundException ex) { //errors.add(String.format("The item identified by the OID '%s' does not exist.", // itemOID)); errors.add(MessageFormat.format( - getResourceBundle(request.getLocale()).getString( - "contentItemJSRPortlet.errors.perferences.item_does_not_exist"), - itemOID)); + getResourceBundle(request.getLocale()).getString( + "contentItemJSRPortlet.errors.perferences.item_does_not_exist"), + itemOID)); } } @@ -164,7 +178,7 @@ public class ContentItemJSRPortlet extends JSRPortlet { //request.setAttribute("helloworld", "Hello World Attribute"); final PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher( - "/templates/portlets/ContentItemJSRPortletAdmin.jsp"); + "/templates/portlets/ContentItemJSRPortletAdmin.jsp"); dispatcher.include(request, response); } @@ -178,7 +192,7 @@ public class ContentItemJSRPortlet extends JSRPortlet { */ @Override protected void doHelp(final RenderRequest request, final RenderResponse response) - throws PortletException, IOException { + throws PortletException, IOException { response.setContentType("text/html"); final PrintWriter writer = new PrintWriter(response.getWriter()); writer.println("You're now in Help mode."); @@ -194,10 +208,38 @@ public class ContentItemJSRPortlet extends JSRPortlet { */ @Override protected void doView(final RenderRequest request, final RenderResponse response) - throws PortletException, IOException { - response.setContentType("text/html"); - final PrintWriter writer = new PrintWriter(response.getWriter()); - writer.println("Hello world! You're in View mode."); + throws PortletException, IOException { +// response.setContentType("text/html"); +// final PrintWriter writer = new PrintWriter(response.getWriter()); +// writer.println("Hello world! You're in View mode."); + + final String itemOID = request.getPreferences().getValue(response.getNamespace().concat( + PREFS_SELECTED_ITEM), ""); + + if (itemOID.isEmpty()) { + final PrintWriter writer = response.getWriter(); + writer.append("
"); + writer.append(getResourceBundle(request.getLocale()).getString( + "contentItemJSRPortlet.no_item_configured")); + writer.append("
"); + } else { + try { + final OID oid = OID.valueOf(itemOID); + + final BigDecimal itemId = (BigDecimal) oid.get("id"); + final PortletRequestDispatcher dispatcher = getPortletContext(). + getRequestDispatcher(String.format( + "/portlets/content-item-portlet-servlet/items/%s", + itemId.toString())); + dispatcher.include(request, response); + + } catch (IllegalArgumentException ex) { + writeMessage(request, + response, + "contentItemJSRPortlet.errors.parameters.illegal_oid", + itemOID); + } + } } @Override @@ -233,16 +275,66 @@ public class ContentItemJSRPortlet extends JSRPortlet { } catch (IllegalArgumentException ex) { errors.add(MessageFormat.format(getResourceBundle(actionRequest.getLocale()). - getString("contentItemJSRPortlet.errors.parameters.illegal_oid"), + getString("contentItemJSRPortlet.errors.parameters.illegal_oid"), itemOID)); } catch (DataObjectNotFoundException ex) { errors.add(MessageFormat.format(getResourceBundle(actionRequest.getLocale()) - .getString( - "contentItemJSRPortlet.errors.parameters.item_does_not_exist"), + .getString( + "contentItemJSRPortlet.errors.parameters.item_does_not_exist"), itemOID)); } } } } + private void writeMessage(final RenderRequest request, + final RenderResponse response, + final String msgKey, + final Object... args) throws IOException { + final PrintWriter writer = response.getWriter(); + writer.append("
"); + writer.append(MessageFormat.format(getResourceBundle(request.getLocale()). + getString(msgKey), args)); + writer.append("
"); + } + + /** + * Special component to make it possible to use special XSL for the data served by the + * PortletDataProvider + * + */ + private class PortletDataItemPanel extends SimpleComponent { + + private final XMLGenerator xmlGenerator; + + public PortletDataItemPanel(final ContentItem item) { + super(); + this.xmlGenerator = new XMLGenerator(item); + } + + @Override + public void generateXML(final PageState state, final Element parent) { + final Element content = parent.newChildElement("cms:contentPanel", + CMS.CMS_XML_NS); + xmlGenerator.generateXML(state, content, ""); + } + + } + + private class XMLGenerator extends SimpleXMLGenerator { + + private final ContentItem item; + + public XMLGenerator(final ContentItem item) { + super(); + this.item = item; + } + + @Override + protected ContentItem getContentItem(final PageState state) { + return item; + } + + } + } diff --git a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources.properties b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources.properties index 1554882bb..774587ee7 100644 --- a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources.properties +++ b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources.properties @@ -10,3 +10,5 @@ contentItemJSRPortlet.edit.contentSectionSelect.label=Content section contentitemJSRPortlet.edit.search.label=Title of the content item contentItemJSRPortlet.edit.fieldset.label=Please choose the content item to display +contentItemJSRPortlet.no_item_configured=No item to show configured +contentItemJSRPortlet.errors.item_is_not_live=The configured is not published yet diff --git a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources_de.properties b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources_de.properties index 03e3e7412..5090a3667 100644 --- a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources_de.properties +++ b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemJSRPortletResources_de.properties @@ -7,3 +7,5 @@ contentItemJSRPortlet.errors.parameters.item_does_not_exist=Es gibt kein Item mi contentItemJSRPortlet.edit.contentSectionSelect.label=Content Section contentitemJSRPortlet.edit.search.label=Titel des Content Items contentItemJSRPortlet.edit.fieldset.label=Bitte w\u00e4hlen Sie das anzuzeigende Content Item +contentItemJSRPortlet.no_item_configured=Es wurde noch Content Item zum Anzeigen festgelegt. +contentItemJSRPortlet.errors.item_is_not_live=Das konfigurierte Content Item ist (noch) nicht publiziert. diff --git a/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemPortletItemProviderServlet.java b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemPortletItemProviderServlet.java new file mode 100644 index 000000000..c5180afd4 --- /dev/null +++ b/ccm-cms/src/com/arsdigita/cms/portlet/ContentItemPortletItemProviderServlet.java @@ -0,0 +1,195 @@ +package com.arsdigita.cms.portlet; + +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageFactory; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.page.PageTransformer; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ContentItem; +import com.arsdigita.cms.dispatcher.SimpleXMLGenerator; +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.persistence.OID; +import com.arsdigita.templating.PresentationManager; +import com.arsdigita.templating.Templating; +import com.arsdigita.web.Application; +import com.arsdigita.web.BaseServlet; +import com.arsdigita.xml.Document; +import com.arsdigita.xml.Element; +import java.io.IOException; +import java.math.BigDecimal; +import javax.portlet.PortletRequest; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * This servlet is the backend for the the {@link ContentItemJSRPortlet} and provides the view of a + * content item. Using a servlet is necessary to include the transformed HTML of the content item. + * The {@link PageTransformer} requires a HTTP request but in the Portlet only a + * {@link PortletRequest} is available. And a {@link PortletRequest} can't be converted into a + * {@link ServletHttpRequest}. + * + * @author Jens Pelzetter + */ +public class ContentItemPortletItemProviderServlet extends BaseServlet{ //BaseApplicationServlet { + + private static final String ITEMS = "items"; + private static final String CATEGORIES = "categories"; + + /** + * This method is the entry point to the logic of this class. The method analyses the path (from + * {@link HttpServletRequest#getPathInfo()}) and delegates to the responsible method of this + * class. + * + * @param request + * @param response + * @param app + * @throws ServletException + * @throws IOException + */ + @Override +// protected void doService(final HttpServletRequest request, +// final HttpServletResponse response, +// final Application app) + protected void doService(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + String path = request.getPathInfo(); + if (path.charAt(0) == '/') { + path = path.substring(1); + } + final String[] pathTokens = path.split("/"); + + if (pathTokens.length == 0) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + + //Check the first token of the path and delegate the clal + if (ITEMS.equals(pathTokens[0])) { + serveContentItem(pathTokens, request, response); + } else if (CATEGORIES.equals(pathTokens[0])) { + throw new UnsupportedOperationException("Not implemtend yet"); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * Serves an HTML fragment showing the detail view of a specific content item. Will create a + * BAD_REQUEST HTTP error if the provided ID/OID is invalid and a NOT_FOUND HTTP error if no + * content item with the provided ID/OID is found. + * + * @param pathTokens The tokens of the path + * @param request + * @param response + * @throws IOException + * @throws ServletException + */ + protected void serveContentItem(final String[] pathTokens, + final HttpServletRequest request, + final HttpServletResponse response) throws IOException, + ServletException { + //Retrieve the content item and ensure that we work with the live version of the item. + ContentItem item = null; + try { + final ContentItem tmpItem = retrieveContentItem(pathTokens[1]); + if (tmpItem.isLiveVersion()) { + item = tmpItem; + } else { + if (tmpItem.isLive()) { + item = tmpItem.getLiveVersion(); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND, + String.format("Item with ID %d is not published.", + pathTokens[1])); + } + } + } catch (DataObjectNotFoundException ex) { + //No item with the provided ID/OID found, respond with NOT_FOUND (404). + response.sendError(HttpServletResponse.SC_NOT_FOUND, + String.format("Item with ID/PID %d not found.", pathTokens[1])); + return; + } catch (NumberFormatException ex) { + //The provided ID was not a number, respond with bad request error. + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + String.format("'%s' is not a valid item id", pathTokens[1])); + return; + } catch (IllegalArgumentException ex) { + //The provided OID is was invalid, respond with bad request error. + response.sendError(HttpServletResponse.SC_BAD_REQUEST, + String.format("'%s' is not a valid item OID", pathTokens[1])); + return; + } + + //Create the XML output + final Page page = PageFactory.buildPage( + "PortletDataProvider", + String.format("ContentItem %s", item.getOID().toString())); + final PortletDataItemPanel panel = new PortletDataItemPanel(item); + page.add(panel); + page.lock(); + + //Delegate to theming enging + final Document document = page.buildDocument(request, response); + final PresentationManager presenter = Templating.getPresentationManager(); + presenter.servePage(document, request, response); + } + + /** + * Helper method encapsulating the logic for retrieving a content item. If the first character + * of the provided item id is a digit the method assumes that the numeric ID of the item was + * provided. Otherwise the method assumes that the OID of the item to serve was provided. + * + * @param itemId + * @return + */ + private ContentItem retrieveContentItem(final String itemId) { + if (Character.isDigit(itemId.charAt(0))) { + return new ContentItem(new BigDecimal(itemId)); + } else { + return new ContentItem(OID.valueOf(itemId)); + } + } + + /** + * Special component to make it possible to use special XSL for the data served by the + * PortletDataProvider + * + */ + private class PortletDataItemPanel extends SimpleComponent { + + private final XMLGenerator xmlGenerator; + + public PortletDataItemPanel(final ContentItem item) { + super(); + this.xmlGenerator = new XMLGenerator(item); + } + + @Override + public void generateXML(final PageState state, final Element parent) { + final Element content = parent.newChildElement("cms:contentPanel", + CMS.CMS_XML_NS); + xmlGenerator.generateXML(state, content, ""); + } + + } + + private class XMLGenerator extends SimpleXMLGenerator { + + private final ContentItem item; + + public XMLGenerator(final ContentItem item) { + super(); + this.item = item; + } + + @Override + protected ContentItem getContentItem(final PageState state) { + return item; + } + + } + +} diff --git a/ccm-cms/web/WEB-INF/web.d/web.ccm-cms.xml b/ccm-cms/web/WEB-INF/web.d/web.ccm-cms.xml index f738805da..9cb589918 100644 --- a/ccm-cms/web/WEB-INF/web.d/web.ccm-cms.xml +++ b/ccm-cms/web/WEB-INF/web.d/web.ccm-cms.xml @@ -31,6 +31,11 @@ template-xsl com.arsdigita.cms.dispatcher.TemplateXSLServlet + + + content-item-portlet-servlet + com.arsdigita.cms.portlet.ContentItemPortletItemProviderServlet + + + content-item-portlet-servlet + /portlets/content-item-portlet-servlet/* +