ItemSearch component
- * Default to limit the search to current content section
+ * Construct a new ItemSearch component Default to limit the
+ * search to current content section
*
* @param context the context for the retrieved items. Should be
- * {@link ContentItem#DRAFT} or {@link ContentItem#LIVE}
+ * {@link ContentItem#DRAFT} or {@link ContentItem#LIVE}
*/
public ItemSearch(String context) {
this(context, true);
@@ -55,32 +54,34 @@ public class ItemSearch extends Form implements Resettable, QueryGenerator {
* Construct a new ItemSearch component
*
* @param context the context for the retrieved items. Should be
- * {@link ContentItem#DRAFT} or {@link ContentItem#LIVE}
- * @param limitToContentSection limit the search to the current content section
+ * {@link ContentItem#DRAFT} or {@link ContentItem#LIVE}
+ * @param limitToContentSection limit the search to the current content
+ * section
*/
public ItemSearch(String context, boolean limitToContentSection) {
super("itemSearch", new SimpleContainer());
//setMethod("GET");
- m_section = createSearchSection(context, limitToContentSection);
- add(m_section);
+ itemSearchSection = createSearchSection(context, limitToContentSection);
+ add(itemSearchSection);
}
- protected ItemSearchSection createSearchSection(String context, boolean limitToContentSection) {
+ protected ItemSearchSection createSearchSection(String context,
+ boolean limitToContentSection) {
return new ItemSearchSection(context, limitToContentSection);
}
@Override
public boolean hasQuery(PageState state) {
- return m_section.hasQuery(state);
+ return itemSearchSection.hasQuery(state);
}
@Override
public QuerySpecification getQuerySpecification(PageState state) {
- return m_section.getQuerySpecification(state);
+ return itemSearchSection.getQuerySpecification(state);
}
@Override
public void reset(PageState state) {
- m_section.reset(state);
+ itemSearchSection.reset(state);
}
}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchBrowsePane.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchBrowsePane.java.todo
new file mode 100755
index 000000000..bfff7fdfb
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchBrowsePane.java.todo
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.ui;
+
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.ParameterSingleSelectionModel;
+import com.arsdigita.bebop.Resettable;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.bebop.SingleSelectionModel;
+import com.arsdigita.bebop.Tree;
+import com.arsdigita.bebop.event.ActionEvent;
+import com.arsdigita.bebop.event.ActionListener;
+import com.arsdigita.bebop.event.ChangeEvent;
+import com.arsdigita.bebop.event.ChangeListener;
+import com.arsdigita.bebop.event.FormInitListener;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.event.FormSubmissionListener;
+import com.arsdigita.bebop.event.TreeExpansionEvent;
+import com.arsdigita.bebop.event.TreeExpansionListener;
+import com.arsdigita.bebop.form.Option;
+import com.arsdigita.bebop.form.SingleSelect;
+import com.arsdigita.bebop.form.Submit;
+import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import com.arsdigita.cms.dispatcher.Utilities;
+
+import com.arsdigita.cms.CMS;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.contentsection.Folder;
+import com.arsdigita.cms.ui.authoring.NewItemForm;
+import com.arsdigita.cms.ui.folder.FolderRequestLocal;
+import com.arsdigita.cms.ui.folder.FolderSelectionModel;
+import com.arsdigita.cms.ui.folder.FolderTreeModelBuilder;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.toolbox.ui.LayoutPanel;
+import com.arsdigita.util.Assert;
+
+import java.math.BigDecimal;
+import org.apache.logging.log4j.LogManager;
+
+import org.apache.logging.log4j.Logger;
+import org.arsdigita.cms.CMSConfig;
+import org.librecms.CmsConstants;
+import org.librecms.contentsection.ContentSectionConfig;
+
+/**
+ * A pane that contains a folder tree on the left and a folder manipulator on
+ * the right.
+ *
+ * @author David LutterKort <dlutter@redhat.com>
+ * @author Jens Pelzetter
+ */
+public class ItemSearchBrowsePane extends SimpleContainer implements Resettable,
+ TreeExpansionListener,
+ ChangeListener,
+ FormProcessListener,
+ FormSubmissionListener {
+
+ private static final String CONTENT_TYPE_ID = "ct";
+ private static final Logger LOGGER = LogManager.getLogger(
+ ItemSearchBrowsePane.class);
+ private final FolderSelectionModel folderSelectionModel;
+ private final FolderRequestLocal folderRequestLocal;
+ private final Tree tree;
+ private ItemSearchFolderBrowser folderBrowser;
+ private SingleSelect sectionSelect;
+ private SingleSelectionModel typeSelectionModel;
+
+ public ItemSearchBrowsePane() {
+
+ final LayoutPanel mainPanel = new LayoutPanel();
+
+ setClassAttr("sidebarNavPanel");
+ setAttribute("navbar-title",
+ new GlobalizedMessage("cms.ui.folder_browser",
+ CmsConstants.CMS_BUNDLE)
+ .localize().toString());
+
+ final BoxPanel left = new BoxPanel(BoxPanel.VERTICAL);
+
+ final Label label = new Label(new GlobalizedMessage(
+ "cms.ui.folder_browser", CmsConstants.CMS_BUNDLE));
+ label.setClassAttr("heading");
+ left.add(label);
+
+ // As described in ticket 20540, some clients do not want the option to pick items from other
+ // subsites through the ItemSearchBrowsePane. A new parameter has been added to allow the
+ // administrator to pick between the old and new versions.
+ boolean linksOnlyInSameSubsite = CMSConfig.getConfig()
+ .isLinksOnlyInSameSubsite();
+ LOGGER.debug("linksOnlyInSameSubsite value is {}",
+ linksOnlyInSameSubsite);
+
+ tree = new Tree(new FolderTreeModelBuilder() {
+ @Override
+ protected Folder getRoot(PageState ps) {
+ Folder root = getRootFolder(ps);
+
+ if (null == root) {
+ return super.getRoot(ps);
+ }
+ return root;
+ }
+
+ });
+ folderSelectionModel = createFolderSelectionModel();
+ folderSelectionModel.addChangeListener(this);
+ folderRequestLocal = new FolderRequestLocal(folderSelectionModel);
+
+ if (!linksOnlyInSameSubsite) {
+ // The client should be able to pick between the subsites
+ Form sectionForm = getSectionForm();
+ add(sectionForm);
+ }
+
+ tree.setSelectionModel(folderSelectionModel);
+
+ tree.setClassAttr("navbar");
+ tree.addTreeExpansionListener(this);
+ left.add(tree);
+
+// CMSContainer container = new CMSContainer();
+ left.setClassAttr("main");
+
+ final BoxPanel body = new BoxPanel(BoxPanel.VERTICAL);
+ folderBrowser = new ItemSearchFolderBrowser(folderSelectionModel);
+ body.add(folderBrowser);
+ body.add(folderBrowser.getPaginator());
+
+// m_newItem = new SectionNewItemForm("newItem");
+// m_typeSel = new ParameterSingleSelectionModel(new BigDecimalParameter(CONTENT_TYPE_ID));
+// m_newItem.addProcessListener(this);
+//
+// container.add(m_newItem);
+ //add(container);
+ mainPanel.setLeft(left);
+ mainPanel.setBody(body);
+ add(mainPanel);
+ }
+
+ @Override
+ public boolean isVisible(PageState s) {
+ // Always expand root node
+ if (tree.isCollapsed(getRootFolder(s).getID().toString(), s)) {
+ tree.expand(getRootFolder(s).getID().toString(), s);
+ }
+
+ return super.isVisible(s);
+ }
+
+ private Form getSectionForm() {
+ Form sectionForm = new Form("isfbSectionForm",
+ new BoxPanel(BoxPanel.HORIZONTAL));
+ sectionForm.setClassAttr("navbar");
+
+ sectionSelect = new SingleSelect(new OIDParameter("isfbSection"));
+ ContentSectionCollection sections = ContentSection.getAllSections();
+ while (sections.next()) {
+ ContentSection section = sections.getContentSection();
+ sectionSelect.addOption(new Option(section.getOID().toString(),
+ section.getDisplayName()));
+ }
+
+ sectionForm.addInitListener(new FormInitListener() {
+ @Override
+ public void init(FormSectionEvent ev) {
+ PageState ps = ev.getPageState();
+
+ if (null == sectionSelect.getValue(ps)) {
+ ContentSection section = CMS.getContext().
+ getContentSection();
+ sectionSelect.setValue(ps, section.getOID());
+ }
+ }
+
+ });
+
+ sectionForm.add(sectionSelect);
+ sectionForm.add(new Submit("Change Section"));
+
+ return sectionForm;
+ }
+
+ private Folder getRootFolder(PageState ps) {
+ LOGGER.debug("Getting the root folder.");
+ if (sectionSelect != null) {
+ // We have more than one subsite to choose between
+ OID sectionOID = (OID) sectionSelect.getValue(ps);
+ if (LOGGER.isDebugEnabled()) {
+ if (null != sectionOID) {
+ LOGGER.debug("Using section " + sectionOID.toString());
+ } else {
+ LOGGER.debug("Using default section");
+ }
+ }
+
+ if (null == sectionOID) {
+ return null;
+ }
+
+ ContentSection section = (ContentSection) DomainObjectFactory.
+ newInstance(sectionOID);
+
+ return section.getRootFolder();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void register(Page p) {
+ super.register(p);
+ p.addComponentStateParam(this, folderSelectionModel.getStateParameter());
+
+ // Only add the SingleSelect item if it exists
+ if (sectionSelect != null) {
+ p.addComponentStateParam(this, sectionSelect.getParameterModel());
+ }
+
+ // Save the state of the new item component
+// p.addComponentStateParam(this, m_typeSel.getStateParameter());
+ p.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final PageState state = e.getPageState();
+
+ if (state.isVisibleOnPage(ItemSearchBrowsePane.this)) {
+ showHideSegments(state);
+ }
+ }
+
+ });
+ }
+
+ /**
+ * Show/hide segments based on access checks.
+ *
+ * @param state The page state
+ * @pre ( state != null )
+ */
+ private void showHideSegments(PageState state) {
+ SecurityManager sm = Utilities.getSecurityManager(state);
+ Folder folder = folderRequestLocal.getFolder(state);
+ Assert.exists(folder);
+
+ // MP: This should be checked on the current folder instead of just
+ // the content section.
+// boolean newItem =
+// sm.canAccess(state.getRequest(), SecurityManager.NEW_ITEM, folder);
+//
+// if (!newItem) {
+// browseMode(state);
+// }
+// m_newItem.setVisible(state, newItem);
+ }
+
+ @Override
+ public void reset(PageState s) {
+ //m_browser.reset(s);
+ }
+
+ public ItemSearchFolderBrowser getFolderBrowser() {
+ return folderBrowser;
+ }
+
+ public final FolderSelectionModel getFolderSelectionModel() {
+ return folderSelectionModel;
+ }
+
+ /**
+ * sets the current level of expansion of the folder tree and in the folder
+ * browser table
+ */
+ protected void setSelectedFolder(PageState s, String key) {
+
+ //set the selected folder of the folder browser
+ folderBrowser.getFolderSelectionModel().setSelectedKey(s, key);
+
+ //set the selected folder of the folder tree
+ folderSelectionModel.setSelectedKey(s, key);
+ Folder current = (Folder) folderSelectionModel.getSelectedObject(s);
+ Folder parent = (Folder) current.getParent();
+ if (parent != null) {
+ BigDecimal id = parent.getID();
+ tree.expand(id.toString(), s);
+ }
+ }
+
+ // Implement TreeExpansionListener
+ @Override
+ public void treeCollapsed(TreeExpansionEvent e) {
+ PageState s = e.getPageState();
+ folderSelectionModel.setSelectedKey(s, e.getNodeKey());
+ }
+
+ @Override
+ public void treeExpanded(TreeExpansionEvent e) {
+ return;
+ }
+
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ PageState s = e.getPageState();
+ Folder current = (Folder) folderSelectionModel.getSelectedObject(s);
+ Folder parent = (Folder) current.getParent();
+ folderBrowser.getPaginator().reset(s);
+ if (parent != null) {
+ BigDecimal id = parent.getID();
+ tree.expand(id.toString(), s);
+ }
+ //m_browser.getPermissionsPane().reset(s);
+ //m_browser.setPermissionLinkVis(s);
+ }
+
+ @Override
+ public void process(FormSectionEvent e) {
+ PageState s = e.getPageState();
+ final Object source = e.getSource();
+// if (source == m_newItem) {
+// BigDecimal typeID = m_newItem.getTypeID(s);
+// m_typeSel.setSelectedKey(s, typeID);
+// newItemMode(s);
+// } else {
+ browseMode(s);
+// }
+ }
+
+ @Override
+ public void submitted(FormSectionEvent e) {
+ PageState s = e.getPageState();
+ final Object source = e.getSource();
+// if (source == m_newItem) {
+// BigDecimal typeID = m_newItem.getTypeID(s);
+// m_typeSel.setSelectedKey(s, typeID);
+// //newItemMode(s);
+// }
+ }
+
+ private void browseMode(PageState s) {
+// m_browseSeg.setVisible(s, true);
+ typeSelectionModel.clearSelection(s);
+ }
+
+ private void newItemMode(PageState s) {
+// m_newItemSeg.setVisible(s, true);
+ }
+
+ private FolderSelectionModel createFolderSelectionModel() {
+ return new FolderSelectionModel("folder") {
+ @Override
+ protected BigDecimal getRootFolderID(PageState ps) {
+ Folder root = getRootFolder(ps);
+
+ if (null == root) {
+ return super.getRootFolderID(ps);
+ }
+ return root.getID();
+ }
+
+ };
+ }
+
+ private static class SectionNewItemForm extends NewItemForm {
+
+ public SectionNewItemForm(String name) {
+ super(name);
+ }
+
+ @Override
+ public ContentSection getContentSection(PageState s) {
+ return CMS.getContext().getContentSection();
+ }
+
+ }
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchFolderBrowser.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchFolderBrowser.java.todo
new file mode 100755
index 000000000..948483f55
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchFolderBrowser.java.todo
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.ui;
+
+import com.arsdigita.bebop.Bebop;
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Link;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.RequestLocal;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.PaginationModelBuilder;
+import com.arsdigita.bebop.Paginator;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.event.ActionEvent;
+import com.arsdigita.bebop.event.ActionListener;
+import com.arsdigita.bebop.event.TableActionAdapter;
+import com.arsdigita.bebop.event.TableActionEvent;
+import com.arsdigita.bebop.event.TableActionListener;
+import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.bebop.table.AbstractTableModelBuilder;
+import com.arsdigita.bebop.table.DefaultTableCellRenderer;
+import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.util.BebopConstants;
+import com.arsdigita.cms.CMS;
+import com.arsdigita.cms.ContentItem;
+import com.arsdigita.cms.ContentPage;
+import com.arsdigita.cms.ContentSection;
+import com.arsdigita.cms.ContentType;
+import com.arsdigita.cms.Folder;
+import com.arsdigita.cms.SecurityManager;
+import com.arsdigita.cms.dispatcher.Utilities;
+import com.arsdigita.cms.ui.folder.FolderSelectionModel;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.persistence.CompoundFilter;
+import com.arsdigita.persistence.FilterFactory;
+import com.arsdigita.toolbox.GlobalisationUtil;
+import com.arsdigita.util.Assert;
+
+import java.math.BigDecimal;
+
+/**
+ * Browse folders and items. If the user clicks on a folder, the folder selection model is updated.
+ * If the user clicks on any other item, an separate item selection model is updated.
+ *
+ * @author David Lutterkort
+ * @version $Revision: #9 $ $DateTime: 2004/08/17 23:15:09 $
+ */
+public class ItemSearchFolderBrowser extends Table {
+
+ private static final org.apache.log4j.Logger s_log = org.apache.log4j.Logger.getLogger(
+ ItemSearchFolderBrowser.class);
+ private static GlobalizedMessage[] s_headers = {
+ globalize("cms.ui.folder.name"),
+ globalize("cms.ui.folder.title"),
+ globalize("cms.ui.folder.type")};
+ private FolderSelectionModel m_currentFolder;
+ private TableActionListener m_folderChanger;
+ private TableActionListener m_deleter;
+ private TableActionListener m_indexChanger;
+ private TableColumn m_nameColumn;
+ private Paginator m_paginator;
+
+ public ItemSearchFolderBrowser(FolderSelectionModel currentFolder) {
+ super((FolderTableModelBuilder) null, s_headers);
+
+ FolderTableModelBuilder builder = new FolderTableModelBuilder();
+ setModelBuilder(builder);
+
+ m_paginator = new Paginator(builder, ContentSection.getConfig().
+ getFolderBrowseListSize());
+
+ m_currentFolder = currentFolder;
+
+ setClassAttr("dataTable");
+
+ getHeader().setDefaultRenderer(
+ new com.arsdigita.cms.ui.util.DefaultTableCellRenderer());
+ m_nameColumn = getColumn(0);
+ m_nameColumn.setCellRenderer(new NameCellRenderer());
+
+ m_folderChanger = new FolderChanger();
+ addTableActionListener(m_folderChanger);
+
+ setEmptyView(new Label(globalize("cms.ui.folder.no_items")));
+
+ Assert.exists(m_currentFolder.getStateParameter());
+ }
+
+ public Paginator getPaginator() {
+ return m_paginator;
+ }
+
+ @Override
+ public void register(Page p) {
+ super.register(p);
+ p.addComponentStateParam(this, m_currentFolder.getStateParameter());
+
+ p.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent event) {
+ // MP: This action listener should only be called when the
+ // folder browser is visible.
+ showHideFolderActions(event.getPageState());
+ }
+
+ });
+ }
+
+ private Folder getCurrentFolder(PageState state) {
+ return (Folder) m_currentFolder.getSelectedObject(state);
+ }
+
+ private void showHideFolderActions(PageState state) {
+ SecurityManager sm = Utilities.getSecurityManager(state);
+ Folder folder = getCurrentFolder(state);
+ Assert.exists(folder);
+ }
+
+ public FolderSelectionModel getFolderSelectionModel() {
+ return m_currentFolder;
+ }
+
+ private class FolderTableModelBuilder
+ extends AbstractTableModelBuilder implements PaginationModelBuilder {
+
+ private RequestLocal m_size = new RequestLocal() {
+
+ @Override
+ protected Object initialValue(PageState state) {
+ Folder.ItemCollection itemColl = getItemCollection(state);
+
+ if (null == itemColl) {
+ return new Integer(0);
+ }
+ return new Integer((int) itemColl.size());
+ }
+
+ };
+ private RequestLocal m_itemColl = new RequestLocal() {
+
+ @Override
+ protected Object initialValue(PageState state) {
+ Folder.ItemCollection itemColl = getItemCollection(state);
+
+ itemColl.addOrder("item.name");
+ itemColl.setRange(new Integer(m_paginator.getFirst(state)),
+ new Integer(m_paginator.getLast(state) + 1));
+
+ return itemColl;
+ }
+
+ };
+
+ public TableModel makeModel(Table t, PageState s) {
+ FolderSelectionModel sel = ((ItemSearchFolderBrowser) t).
+ getFolderSelectionModel();
+ Folder f = getCurrentFolder(s);
+
+ if (s_log.isDebugEnabled()) {
+ if (null == f) {
+ s_log.debug("Selected folder is null");
+ } else {
+ s_log.debug("Selected folder: " + f.getLabel() + " " + f.
+ getOID().toString());
+ }
+ }
+
+ if (f == null) {
+ return Table.EMPTY_MODEL;
+ } else {
+ t.getRowSelectionModel().clearSelection(s);
+ return new FolderTableModel((Folder.ItemCollection) m_itemColl.
+ get(s));
+ }
+ }
+
+ private Folder.ItemCollection getItemCollection(PageState state) {
+ Folder f = getCurrentFolder(state);
+ Folder.ItemCollection itemColl = f.getPrimaryInstances();
+
+ if (null == itemColl) {
+ return null;
+ }
+
+ BigDecimal singleTypeID = (BigDecimal) state.getValue(new BigDecimalParameter(
+ ItemSearch.SINGLE_TYPE_PARAM));
+
+ if (singleTypeID != null) {
+
+ // The Filter Factory
+ FilterFactory ff = itemColl.getFilterFactory();
+
+ // Create an or-filter
+ CompoundFilter or = ff.or();
+
+ // The content type must be either of the requested type
+ or.addFilter(ff.equals(ContentItem.CONTENT_TYPE + "."
+ + ContentType.ID, singleTypeID));
+
+ // Or must be a sibling of the requested type
+ /*
+ * jensp 2011-11-14: The orginal code here was only traversing
+ * one level, but ContentType hierarchies may have several
+ * levels. Therefore, this code was replaced by method which is
+ * called recursivly until the type with no descendents is
+ * reached.
+ */
+ createSiblingFilter(or, ff, singleTypeID);
+ /*try {
+ ContentType ct = new ContentType(singleTypeID);
+
+ StringTokenizer strTok = new StringTokenizer(ct.
+ getDescendants(), "/");
+ while (strTok.hasMoreElements()) {
+ or.addFilter(ff.equals(ContentItem.CONTENT_TYPE + "."
+ + ContentType.ID,
+ (String) strTok.nextElement()));
+ }
+ } catch (Exception ex) {
+ // WTF? The selected content type does not exist in the table???
+ s_log.error(String.format(
+ "Something is very wrong here, the ContentType '%s' "
+ + "seems not to exist. Ignoring for now, but please "
+ + "make your checks.",
+ singleTypeID.toString()),
+ ex);
+ }*/
+
+ itemColl.addFilter(or);
+
+ }
+
+ itemColl.addOrder("isFolder desc");
+ itemColl.addOrder("lower(item." + ContentItem.NAME + ") ");
+ return itemColl;
+ }
+
+ private void createSiblingFilter(final CompoundFilter filter,
+ final FilterFactory filterFactory,
+ final BigDecimal typeId) {
+ final ContentType type = new ContentType(typeId);
+ if ((type.getDescendants() == null)
+ || type.getDescendants().trim().isEmpty()) {
+ return;
+ } else {
+ final String[] descendantsIds = type.getDescendants().split("/");
+
+ for (String descendantId : descendantsIds) {
+ filter.addFilter(filterFactory.equals(String.format(
+ ContentItem.CONTENT_TYPE + "." + ContentType.ID),
+ descendantId));
+ createSiblingFilter(filter, filterFactory, descendantId);
+ }
+ }
+ }
+
+ private void createSiblingFilter(final CompoundFilter filter,
+ final FilterFactory filterFactory,
+ final String typeId) {
+ try {
+ final BigDecimal _typeId = new BigDecimal(typeId);
+ createSiblingFilter(filter, filterFactory, _typeId);
+ } catch (NumberFormatException ex) {
+ s_log.error(String.format("Failed to parse typeId '%s'.",
+ typeId),
+ ex);
+ }
+ }
+
+ public int getTotalSize(Paginator paginator, PageState state) {
+
+ Integer size = (Integer) m_size.get(state);
+ return size.intValue();
+ }
+
+ /**
+ * Indicates whether the paginator should be visible, based on the visibility of the folder
+ * browser itself and how many items are displayed
+ *
+ * @return true if folder browser is visible and there is more than 1 page of items, false
+ * otherwise
+ */
+ public boolean isVisible(PageState state) {
+ int size = ((Integer) m_size.get(state)).intValue();
+
+ return ItemSearchFolderBrowser.this.isVisible(state)
+ && (size
+ > ContentSection.getConfig().getFolderBrowseListSize());
+ }
+
+ }
+
+ /**
+ * Produce links to view an item or control links for folders to change into the folder.
+ */
+ private class NameCellRenderer extends DefaultTableCellRenderer {
+
+ public NameCellRenderer() {
+ super(true);
+ }
+
+ @Override
+ public Component getComponent(Table table, PageState state,
+ Object value, boolean isSelected,
+ Object key, int row, int column) {
+ Folder.ItemCollection coll = (Folder.ItemCollection) value;
+ String name = coll.getName();
+ if (coll.isFolder()) {
+ return super.getComponent(table, state, name, isSelected, key,
+ row, column);
+ } else {
+ ContentSection section = CMS.getContext().getContentSection();
+ BigDecimal id = (BigDecimal) key;
+
+ if (section == null) {
+ return new Label(name);
+ } else {
+ //ItemResolver resolver = section.getItemResolver();
+
+ //String url =
+ //resolver.generateItemURL
+ //(state, id, name, section, coll.getVersion()));
+ SimpleContainer container = new SimpleContainer();
+
+ String widget = (String) state.getValue(new StringParameter(
+ ItemSearchPopup.WIDGET_PARAM));
+ String searchWidget = (String) state.getValue(
+ new StringParameter("searchWidget"));
+ boolean useURL = "true".equals(state.getValue(new StringParameter(
+ ItemSearchPopup.URL_PARAM)));
+
+ String fillString;
+ if (useURL) {
+ fillString = ItemSearchPopup.getItemURL(state.getRequest(),
+ coll.getDomainObject().getOID());
+ } else {
+ fillString = id.toString();// + " (" + name + ")";
+ }
+
+ String title = ((ContentPage) coll.getDomainObject()).getTitle();
+
+ Label js = new Label(
+ generateJSLabel(id, widget, searchWidget, fillString, title),
+ false);
+ container.add(js);
+
+ String url = "#";
+
+ Link link = new Link(name, url);
+ link.setClassAttr("title");
+ link.setOnClick("return fillItem" + id + "()");
+
+ container.add(link);
+
+ return container;
+ }
+ }
+ }
+
+ private String generateJSLabel(BigDecimal id, String widget, String searchWidget,
+ String fill, String title) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(" ");
+
+ return buffer.toString();
+ }
+
+ }
+
+ /**
+ * Table model around ItemCollection
+ */
+ private static class FolderTableModel implements TableModel {
+
+ private static final int NAME = 0;
+ private static final int TITLE = 1;
+ private static final int TYPE = 2;
+ private Folder.ItemCollection m_itemColl;
+
+ public FolderTableModel(Folder.ItemCollection itemColl) {
+ m_itemColl = itemColl;
+ }
+
+ public int getColumnCount() {
+ return 3;
+ }
+
+ public boolean nextRow() {
+ return m_itemColl != null ? m_itemColl.next() : false;
+ }
+
+ public Object getElementAt(int columnIndex) {
+ switch (columnIndex) {
+ case NAME:
+ return m_itemColl;
+ case TITLE:
+ return m_itemColl.getDisplayName();
+ case TYPE:
+ return m_itemColl.getTypeLabel();
+ default:
+ throw new IndexOutOfBoundsException("Column index "
+ + columnIndex
+ + " not in table model.");
+ }
+ }
+
+ public Object getKeyAt(int columnIndex) {
+ // Mark folders by using their negative ID (dirty, dirty)
+ return (m_itemColl.isFolder()) ? m_itemColl.getID().negate()
+ : m_itemColl.getID();
+ }
+
+ }
+
+ private class FolderChanger extends TableActionAdapter {
+
+ @Override
+ public void cellSelected(TableActionEvent e) {
+ PageState s = e.getPageState();
+ int col = e.getColumn().intValue();
+
+ if (m_nameColumn != getColumn(col)) {
+ return;
+ }
+ String key = (String) e.getRowKey();
+ if (key.startsWith("-")) {
+ clearSelection(s);
+ getFolderSelectionModel().setSelectedKey(s, key.substring(1));
+ m_paginator.reset(s);
+ }
+ }
+
+ }
+
+ /**
+ * Getting the GlobalizedMessage using a CMS Class targetBundle.
+ *
+ * @param key The resource key
+ *
+ * @pre ( key != null )
+ */
+ private static GlobalizedMessage globalize(String key) {
+ //return FolderManipulator.globalize(key);
+ final GlobalisationUtil util = new GlobalisationUtil(
+ "com.arsdigita.cms."
+ + "ui.folder.CMSFolderResources");
+ return util.globalize(key);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchPage.java.todo b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchPage.java.todo
index 255ac53cb..16469cebb 100755
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchPage.java.todo
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchPage.java.todo
@@ -28,15 +28,12 @@ import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.bebop.parameters.BooleanParameter;
import com.arsdigita.bebop.parameters.IntegerParameter;
import com.arsdigita.bebop.parameters.StringParameter;
-import com.arsdigita.cms.*;
import com.arsdigita.cms.dispatcher.CMSPage;
-import com.arsdigita.cms.util.GlobalizationUtil;
import com.arsdigita.dispatcher.RequestContext;
-import com.arsdigita.domain.DataObjectNotFoundException;
import com.arsdigita.templating.PresentationManager;
import com.arsdigita.templating.Templating;
import com.arsdigita.util.UncheckedWrapperException;
-import com.arsdigita.web.Application;
+import org.libreccm.web.CcmApplication;
import com.arsdigita.web.Web;
import com.arsdigita.xml.Document;
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchSection.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchSection.java
new file mode 100755
index 000000000..317cd85bb
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearchSection.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.ui;
+
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.bebop.Container;
+import com.arsdigita.bebop.Resettable;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.FormSection;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import org.librecms.contentsection.ContentItem;
+import org.librecms.contentsection.ContentType;
+import com.arsdigita.cms.ui.search.ItemQueryComponent;
+import com.arsdigita.globalization.GlobalizedMessage;
+
+import com.arsdigita.search.ui.ResultsPane;
+import com.arsdigita.search.ui.QueryGenerator;
+import com.arsdigita.toolbox.ui.LayoutPanel;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.lucene.search.Query;
+import org.librecms.CmsConstants;
+
+/**
+ * Contains a form for specifying search parameters, as well as a
+ * {@link com.arsdigita.search.ui.ResultsPane} which will perform the search and
+ * display the results
+ *
+ * @author Stanislav Freidin (sfreidin@arsdigita.com)
+ * @version $Id: ItemSearchSection.java 1940 2009-05-29 07:15:05Z terry $
+ */
+public class ItemSearchSection extends FormSection implements Resettable,
+ QueryGenerator {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ ItemSearchSection.class);
+ public static final String SINGLE_TYPE_PARAM = "single_type";
+
+ private ItemQueryComponent itemQueryComponent;
+ private Component resultsComponent;
+
+ /**
+ * Construct a new ItemSearchSection component
+ *
+ * @param context the context for the retrieved items. Should be
+ * {@link ContentItem#DRAFT} or {@link ContentItem#LIVE}
+ * @param limitToContentSection limit the search to the current content
+ * section
+ */
+ public ItemSearchSection(final String context,
+ final boolean limitToContentSection) {
+ this(null, context, limitToContentSection);
+ }
+
+ /**
+ * Construct a new ItemSearchSection component
+ *
+ * @param context the context for the retrieved items. Should be
+ * {@link ContentItem#DRAFT} or {@link ContentItem#LIVE}
+ * @param name The name of the search parameter for the particular
+ * FormSection
+ * @param limitToContentSection limit the search to the current content
+ * section
+ */
+ public ItemSearchSection(final String name,
+ final String context,
+ final boolean limitToContentSection) {
+ this(name, context, limitToContentSection, null);
+ }
+
+ public ItemSearchSection(final String name,
+ final String context,
+ final boolean limitToContentSection,
+ final ContentType type) {
+ super(new SimpleContainer());
+ final String thisName;
+ if (name == null) {
+ thisName = "itemSearch";
+ } else {
+ thisName = name;
+ }
+
+ if (type == null) {
+ itemQueryComponent = createQueryGenerator(context,
+ limitToContentSection);
+ } else {
+ itemQueryComponent = createQueryGenerator(context,
+ limitToContentSection,
+ type);
+ }
+ resultsComponent = createResultsPane(itemQueryComponent);
+
+ LayoutPanel searchPanel = new LayoutPanel();
+ searchPanel.setLeft(itemQueryComponent);
+ searchPanel.setBody(resultsComponent);
+ this.add(searchPanel);
+
+// addQueryGenerator(this);
+// addResultsPane(this);
+ addFormListener();
+
+ setClassAttr("itemSearch");
+ }
+
+ @Override
+ public boolean hasQuery(final PageState state) {
+ return itemQueryComponent.hasQuery(state);
+ }
+
+ @Override
+ public Query getQuerySpecification(final PageState state) {
+ return itemQueryComponent.getQuerySpecification(state);
+ }
+
+ @Override
+ public void reset(final PageState state) {
+ resultsComponent.setVisible(state, false);
+ }
+
+ protected ItemQueryComponent createQueryGenerator(
+ final String context, final boolean limitToContentSection) {
+ return new ItemQueryComponent(context, limitToContentSection);
+ }
+
+ protected ItemQueryComponent createQueryGenerator(
+ final String context,
+ final boolean limitToContentSection,
+ final ContentType type) {
+
+ return new ItemQueryComponent(context, limitToContentSection, type);
+ }
+
+ protected Component createResultsPane(QueryGenerator generator) {
+ ResultsPane pane = new ResultsPane(generator);
+ pane.setRelativeURLs(true);
+ pane.setSearchHelpMsg(new GlobalizedMessage("cms.ui.search.help",
+ CmsConstants.CMS_BUNDLE));
+ pane.setNoResultsMsg(new GlobalizedMessage("cms.ui.search.no_results",
+ CmsConstants.CMS_BUNDLE));
+ return pane;
+ }
+
+ protected void addResultsPane(final Container container) {
+ container.add(resultsComponent);
+ }
+
+ protected void addQueryGenerator(final Container container) {
+ container.add(itemQueryComponent);
+ }
+
+ protected void processQuery(final PageState state) {
+ resultsComponent.setVisible(state, itemQueryComponent.hasQuery(state));
+ }
+
+ protected void addFormListener() {
+ addProcessListener(new SearchFormProcessListener());
+ }
+
+ // Hide results by default
+ @Override
+ public void register(final Page page) {
+ super.register(page);
+ page.setVisibleDefault(resultsComponent, false);
+ page.addGlobalStateParam(new BigDecimalParameter(SINGLE_TYPE_PARAM));
+ }
+
+ /**
+ * Displays the "keywords" and "content types" widgets
+ */
+ private class SearchFormProcessListener implements FormProcessListener {
+
+ @Override
+ public void process(final FormSectionEvent event)
+ throws FormProcessException {
+
+ PageState s = event.getPageState();
+ processQuery(s);
+ }
+ }
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java
new file mode 100755
index 000000000..cb01af551
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/search/ItemQueryComponent.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.cms.ui.search;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.form.Submit;
+import org.libreccm.categorization.Category;
+import com.arsdigita.cms.CMS;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.contentsection.ContentType;
+import com.arsdigita.cms.ui.ContentSectionPage;
+import com.arsdigita.search.ui.BaseQueryComponent;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides a basic query form for CMS admin pages that automatically
+ * adds components for the maximal set of filters supported by the current
+ * search query engine.
+ *
+ * @author unknown
+ * @author Sören Bernstein + * Typical use would be as follows: + *
+ * Form f = new Form("search");
+ * BaseQueryComponent q = new BaseQueryComponent();
+ * q.add(new ObjectTypeFilterComponent("com.arsdigita.kernel.User");
+ * q.add(new PermissionGenerator(PrivilegeDescriptor.READ));
+ * q.add(new Submit("Go"));
+ * f.add(q);
+ *
+ */
+public class BaseQueryComponent extends QueryComponent {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ BaseQueryComponent.class);
+
+ private Set filters;
+ private Form form;
+ private StringParameter termsParameter = new StringParameter("terms");
+
+ /**
+ * Creates a new query component
+ */
+ public BaseQueryComponent() {
+ super("query");
+ filters = new HashSet();
+ }
+
+ @Override
+ public void register(final Page page) {
+ super.register(page);
+ }
+
+ @Override
+ public void register(final Form form, final FormModel formModel) {
+ LOGGER.debug("Adding {} to form model...", termsParameter.getName());
+
+ termsParameter.setPassIn(true);
+ formModel.addFormParam(termsParameter);
+ this.form = form;
+ }
+
+ /**
+ * Gets the current search terms
+ *
+ * @return
+ */
+ @Override
+ protected String getTerms(final PageState state) {
+ final FormData formData = form.getFormData(state);
+
+ if (formData != null) {
+ final ParameterData data = formData.getParameter(termsParameter.
+ getName());
+ LOGGER.debug("Search terms were: {}", (String) data.getValue());
+
+ return (String) data.getValue();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ *
+ * @param state
+ * @param parent
+ */
+ @Override
+ public void generateXML(final PageState state, final Element parent) {
+ final Element content = generateParent(parent);
+
+ final Element terms = new Element(SearchConstants.XML_PREFIX + "terms",
+ SearchConstants.XML_NS);
+ terms.addAttribute("param", termsParameter.getName());
+ terms.addAttribute("value",
+ Globalization.decodeParameter(
+ state.getRequest(),
+ termsParameter.getName()));
+ generateErrorXML(state, terms);
+ content.addContent(terms);
+
+ generateChildrenXML(state, content);
+ }
+
+ protected void generateErrorXML(final PageState state,
+ final Element parent) {
+ final FormData formData = form.getFormData(state);
+ if (formData == null) {
+ return;
+ }
+
+ final Iterator iterator = formData.getErrors(termsParameter.getName());
+ while (iterator.hasNext()) {
+ final Element error = new Element(
+ SearchConstants.XML_PREFIX + "error",
+ SearchConstants.XML_NS);
+ error.setText((String) ((GlobalizedMessage) iterator.next()).
+ localize(state.getRequest())
+ );
+ parent.addContent(error);
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/search/ui/QueryComponent.java b/ccm-core/src/main/java/com/arsdigita/search/ui/QueryComponent.java
new file mode 100755
index 000000000..18e2ad25a
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/search/ui/QueryComponent.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.search.ui;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.search.SearchConstants;
+
+import com.arsdigita.util.Assert;
+import org.apache.lucene.search.Query;
+import org.hibernate.search.query.dsl.QueryBuilder;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.CcmObject;
+import org.libreccm.search.SearchManager;
+
+/**
+ * A base class for generating a query specification from the state. Subclasses
+ * must implement two methods, one for getting the query terms, the other for
+ * getting a set of filter specs.
+ */
+public abstract class QueryComponent extends SimpleContainer
+ implements QueryGenerator {
+
+ public QueryComponent(final String name) {
+ setTag(SearchConstants.XML_PREFIX + name);
+ setNamespace(SearchConstants.XML_NS);
+ }
+
+ /**
+ * Determine if a query specification is available
+ *
+ * @return true if the user has entered some search terms
+ */
+ @Override
+ public boolean hasQuery(final PageState state) {
+ String terms = getTerms(state);
+
+ return (terms != null && !"".equals(terms));
+ }
+
+ /**
+ * Returns the current query specification
+ */
+ @Override
+ public Query getQuerySpecification(final PageState state) {
+ final String terms = getTerms(state);
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final SearchManager searchManager = cdiUtil.
+ findBean(SearchManager.class);
+
+ final QueryBuilder queryBuilder = searchManager.createQueryBuilder(CcmObject.class);
+
+ return queryBuilder
+ .keyword().onFields("displayName", "summary", "description", "title")
+ .matching(terms)
+ .createQuery();
+
+ }
+
+ /**
+ * Returns the current query terms
+ *
+ * @param state
+ * @return the query terms, or null
+ */
+ protected abstract String getTerms(PageState state);
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/search/ui/QueryGenerator.java b/ccm-core/src/main/java/com/arsdigita/search/ui/QueryGenerator.java
new file mode 100755
index 000000000..cf4be3acd
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/search/ui/QueryGenerator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.search.ui;
+
+import com.arsdigita.bebop.PageState;
+import org.apache.lucene.search.Query;
+
+/**
+ * This interface provides the API for retrieving a query specification based on
+ * the current state. The ResultsPane component uses an instance of this class
+ * to retrieve the query spec and display a list of results
+ *
+ * @see com.arsdigita.search.ui.QueryComponent
+ * @see com.arsdigita.search.ui.ResultsPane
+ */
+public interface QueryGenerator {
+
+ /**
+ * Determines whether a query spec currently exists.
+ *
+ * @param state The current page state.
+ * @return true if a query spec is available.
+ */
+ boolean hasQuery(PageState state);
+
+ /**
+ * Retrieves the current query spec. This method can only be called if
+ * hasQuery(state) returns true.
+ *
+ * @param state The current page.
+ * @return The query
+ */
+ Query getQuerySpecification(PageState state);
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/search/ui/ResultsPane.java b/ccm-core/src/main/java/com/arsdigita/search/ui/ResultsPane.java
new file mode 100755
index 000000000..797172ee7
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/search/ui/ResultsPane.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.search.ui;
+
+import com.arsdigita.bebop.SimpleComponent;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.parameters.IntegerParameter;
+import com.arsdigita.globalization.Globalization;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.search.SearchConstants;
+import com.arsdigita.util.UncheckedWrapperException;
+
+import org.libreccm.security.Party;
+import com.arsdigita.xml.Element;
+import com.arsdigita.xml.XML;
+
+import com.arsdigita.web.URL;
+import com.arsdigita.web.ParameterMap;
+import com.arsdigita.web.Web;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.Level;
+import org.apache.logging.log4j.LogManager;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.lucene.search.Query;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.CcmObject;
+import org.libreccm.search.SearchManager;
+
+public class ResultsPane extends SimpleComponent {
+
+ private static final Logger LOGGER = LogManager.getLogger(ResultsPane.class);
+ public static final int PAGE_SIZE = 10;
+
+ private final int pageSize = PAGE_SIZE;
+
+ private final QueryGenerator queryGenerator;
+ private IntegerParameter pageNumber;
+ private boolean relative;
+ //jensp 2014-03-04 Allow using classes to set a suitable info messages.
+ private GlobalizedMessage searchHelpMsg;
+ private GlobalizedMessage noResultsMsg;
+
+ public ResultsPane(final QueryGenerator query) {
+ pageNumber = new IntegerParameter("page");
+ relative = false;
+ this.queryGenerator = query;
+ }
+
+ /**
+ * Determines whether the links to the search results will be relative or
+ * absolute. The default is absolute.
+ *
+ * @param relative
+ */
+ public void setRelativeURLs(final boolean relative) {
+ this.relative = relative;
+ }
+
+ public void setSearchHelpMsg(final GlobalizedMessage msg) {
+ searchHelpMsg = msg;
+ }
+
+ public void setNoResultsMsg(final GlobalizedMessage msg) {
+ noResultsMsg = msg;
+ }
+
+ @Override
+ public void generateXML(final PageState state, final Element parent) {
+ if (!queryGenerator.hasQuery(state)) {
+
+ LOGGER.debug("No query available, skipping XML generation");
+
+ final Element content = new Element(
+ SearchConstants.XML_PREFIX + "results",
+ SearchConstants.XML_NS);
+ final Element info = content.newChildElement("info");
+ if (searchHelpMsg == null) {
+ info.setText(
+ "To search for content items, please enter at least 3 letters into the search field. You can narrow the result by using additional parameters.");
+ } else {
+ //info.setText(GlobalizationUtil.globalize("cms.ui.search_help").localize().toString());
+ info.setText(searchHelpMsg.localize().toString());
+ }
+
+ parent.addContent(content);
+ return;
+ }
+
+ final Query spec = queryGenerator.getQuerySpecification(state);
+ final SearchManager searchManager = CdiUtil.createCdiUtil().findBean(
+ SearchManager.class);
+ final List