diff --git a/ccm-cms/pdl/com/arsdigita/content-section/Report-queries.pdl b/ccm-cms/pdl/com/arsdigita/content-section/Report-queries.pdl
new file mode 100644
index 000000000..6a89369af
--- /dev/null
+++ b/ccm-cms/pdl/com/arsdigita/content-section/Report-queries.pdl
@@ -0,0 +1,68 @@
+//
+// Copyright (C) 2009 Permeance Technologies Pty Ltd. 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
+//
+
+model com.arsdigita.cms;
+
+query getContentSectionSummary {
+ BigDecimal folderId;
+ String folder;
+ BigDecimal subfolderCount;
+
+ do {
+ SELECT ci.item_id as folder_id,
+ ci.name AS folder,
+ (SELECT COUNT(*)-1
+ FROM cms_items ci2
+ WHERE ci2.type_id is null
+ START WITH ci2.item_id = ci.item_id
+ CONNECT BY PRIOR ci2.item_id = ci2.parent_id
+ ) AS subfolder_count
+ FROM content_sections cs,
+ cms_items ci
+ WHERE ci.section_id = cs.section_id
+ AND cs.section_id = :sectionId
+ AND ci.type_id is null
+ AND ci.master_id is null
+ AND ((ci.parent_id = cs.root_folder_id) or (ci.item_id = cs.root_folder_id))
+ order by ci.name
+ } map {
+ folderId = folder_id;
+ folder = folder;
+ subfolderCount = subfolder_count;
+ }
+}
+
+query getContentTypeCountPerFolder {
+ String contentType;
+ Long typeCount;
+
+ do {
+ SELECT ct.label as content_type,
+ COUNT(*) as type_count
+ FROM content_types ct,
+ (SELECT *
+ FROM cms_items ci
+ START WITH ci.parent_id = :folderId
+ CONNECT BY PRIOR ci.item_id = ci.parent_id and ci.language is null) ci
+ WHERE ci.type_id = ct.type_id
+ GROUP BY ct.label
+ } map {
+ contentType = content_type;
+ typeCount = type_count;
+ }
+}
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/CMSResources.properties b/ccm-cms/src/com/arsdigita/cms/ui/CMSResources.properties
index d13ae9475..56ad542fa 100755
--- a/ccm-cms/src/com/arsdigita/cms/ui/CMSResources.properties
+++ b/ccm-cms/src/com/arsdigita/cms/ui/CMSResources.properties
@@ -823,3 +823,18 @@ Thank you for using {3}.\n
cms.contentassets.ui.description=Description
cms.ui.content_check_alert.subject=Content Check Alert
cms.ui.content_check_alert.body=Dear Author\n\nYou are listed as the Content Owner for the following {0} web page(s). Please check if they need amending or updating in any way:\n\n{1}\nIf you have any amendments or questions please email the appropriate web editor:\n\nsally.editor@aplaws.org (Council & Democracy, Environment, Education)\njohn.editor@aplaws.org (Community & Living, Business, Social Care)\nfrank.editor@aplaws.org (Council & Democracy, Environment, Education)\n\nYou can contact webmanager@aplaws.org with any questions about this email\n
+#
+# Reports section
+#
+cms.ui.reports=Reports
+cms.ui.reports.header=Reports
+cms.ui.reports.intro=Please select a report.
+# Columns for ContentSectionSummary report
+cms.ui.reports.css.emptyResult=No matching content items found.
+cms.ui.reports.css.reportName=Content Section Summary
+cms.ui.reports.css.contentSection=Content Section
+cms.ui.reports.css.folder=Folder
+cms.ui.reports.css.subfolderCount=Subfolders
+cms.ui.reports.css.contentType=Content Type
+cms.ui.reports.css.draft=Draft
+cms.ui.reports.css.live=Live
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/ContentSectionPage.java b/ccm-cms/src/com/arsdigita/cms/ui/ContentSectionPage.java
index ebb8eada0..4a5ff5c80 100755
--- a/ccm-cms/src/com/arsdigita/cms/ui/ContentSectionPage.java
+++ b/ccm-cms/src/com/arsdigita/cms/ui/ContentSectionPage.java
@@ -142,6 +142,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener {
private ContentTypeAdminPane m_typePane;
private UserAdminPane m_userAdminPane;
private ContentSoonExpiredPane m_csePane;
+ private ReportPane m_reportPane;
private static class TitlePrinter implements PrintListener {
public void prepare(PrintEvent e) {
@@ -173,6 +174,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener {
m_typePane = getContentTypeAdminPane();
m_userAdminPane = getUserAdminPane();
m_csePane = getCSEPane();
+ m_reportPane = getReportPane();
// The panes
m_tabbedPane = createTabbedPane();
@@ -204,6 +206,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener {
m_tabbedPane.setTabVisible(state, m_rolePane, sm.canAccess(user,SecurityConstants.STAFF_ADMIN));
// csePane: should check permission
m_tabbedPane.setTabVisible(state, m_csePane, true);
+ // TODO Check for reportPane as well
}
}
});
@@ -313,6 +316,13 @@ public class ContentSectionPage extends CMSPage implements ActionListener {
return m_csePane;
}
+ protected ReportPane getReportPane() {
+ if (m_reportPane == null) {
+ m_reportPane = new ReportPane();
+ }
+ return m_reportPane;
+ }
+
/**
* Adds the specified component, with the specified tab name, to
* the tabbed pane only if it is not null.
@@ -359,6 +369,7 @@ public class ContentSectionPage extends CMSPage implements ActionListener {
tab(pane, "cms.ui.content_types", getContentTypeAdminPane());
tab(pane, "cms.ui.user_admin", getUserAdminPane());
tab(pane, "cms.ui.cse", getCSEPane());
+ tab(pane, "cms.ui.reports", getReportPane());
return pane;
}
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/ReportPane.java b/ccm-cms/src/com/arsdigita/cms/ui/ReportPane.java
new file mode 100644
index 000000000..b754384f1
--- /dev/null
+++ b/ccm-cms/src/com/arsdigita/cms/ui/ReportPane.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2009 Permeance Technologies Pty Ltd. 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.List;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.ParameterSingleSelectionModel;
+import com.arsdigita.bebop.SingleSelectionModel;
+import com.arsdigita.bebop.event.ChangeEvent;
+import com.arsdigita.bebop.event.ChangeListener;
+import com.arsdigita.bebop.list.ListModel;
+import com.arsdigita.bebop.list.ListModelBuilder;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.cms.ui.report.ContentSectionSummaryTable;
+import com.arsdigita.cms.ui.report.Report;
+import com.arsdigita.cms.ui.report.ReportListModel;
+import com.arsdigita.toolbox.ui.ActionGroup;
+import com.arsdigita.toolbox.ui.Section;
+import com.arsdigita.util.LockableImpl;
+
+/**
+ * A pane that shows selectable reports and their results.
+ * A selectable list of reports is shown on the left-hand side, a selected report is shown as
+ * body.
+ *
+ * @author thomas-buckel
+ * @author tim-permeance
+ */
+public class ReportPane extends BaseAdminPane {
+
+ private final SingleSelectionModel m_selectionModel;
+ private final java.util.List m_availableReports;
+
+ public ReportPane() {
+ m_availableReports = getReports();
+
+ m_selectionModel = new ParameterSingleSelectionModel(new StringParameter(List.SELECTED));
+ m_selectionModel.addChangeListener(new SelectionListener());
+ setSelectionModel(m_selectionModel);
+
+ List m_reports = new List(new ReportListModelBuilder(m_availableReports));
+ m_reports.setSelectionModel(m_selectionModel);
+
+ final ReportsListSection reportsListSection = new ReportsListSection(m_reports);
+ setLeft(reportsListSection);
+
+ // Register the actual components of the reports for later usage
+ for (Report report : m_availableReports) {
+ getBody().add(report.getComponent());
+ }
+
+ setIntroPane(new Label(gz("cms.ui.reports.intro")));
+ }
+
+ /**
+ * @return List of available reports.
+ */
+ private java.util.List getReports() {
+ java.util.List reports = new ArrayList();
+ reports.add(new Report("cms.ui.reports.css.reportName", new ContentSectionSummaryTable()));
+ // Add other reports as required
+
+ Collections.sort(reports, new Comparator() {
+ @Override
+ public int compare(Report r1, Report r2) {
+ return r1.getName().compareTo(r2.getName());
+ }
+ });
+ return reports;
+ }
+
+ /**
+ * Get the report model that matches the given key.
+ * @param key Key to match.
+ * @return Report model that matches that given key, null if no matching report was found.
+ */
+ private Report getReportByKey(String key) {
+ for (Report report : m_availableReports) {
+ if (report.getKey().equals(key)) {
+ return report;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * UI section for left-hand list of reports.
+ */
+ private class ReportsListSection extends Section {
+ ReportsListSection(List reports) {
+ setHeading(gz("cms.ui.reports.header"));
+ ActionGroup group = new ActionGroup();
+ setBody(group);
+ group.setSubject(reports);
+ }
+ }
+
+ /**
+ * SelectionListener for selected report. It shows the selected report in the body of this
+ * component.
+ */
+ private class SelectionListener implements ChangeListener {
+
+ public final void stateChanged(final ChangeEvent e) {
+
+ final PageState state = e.getPageState();
+
+ getBody().reset(state);
+
+ if (m_selectionModel.isSelected(state)) {
+ Report selectedReport = getReportByKey(m_selectionModel.getSelectedKey(state).toString());
+ if (selectedReport != null) {
+ getBody().push(state, selectedReport.getComponent());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * ListModelBuilder creating a ReportListModel for a list of reports.
+ */
+ private static class ReportListModelBuilder extends LockableImpl implements ListModelBuilder {
+
+ private java.util.List reports;
+
+ private ReportListModelBuilder(java.util.List reports) {
+ this.reports = reports;
+ }
+
+ public final ListModel makeModel(final List list,
+ final PageState state) {
+ return new ReportListModel(reports);
+ }
+ }
+
+}
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/report/ContentSectionSummaryReportTableModelBuilder.java b/ccm-cms/src/com/arsdigita/cms/ui/report/ContentSectionSummaryReportTableModelBuilder.java
new file mode 100644
index 000000000..5df8caead
--- /dev/null
+++ b/ccm-cms/src/com/arsdigita/cms/ui/report/ContentSectionSummaryReportTableModelBuilder.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2009 Permeance Technologies Pty Ltd. 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.report;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.table.AbstractTableModelBuilder;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.cms.CMS;
+import com.arsdigita.cms.Folder;
+import com.arsdigita.persistence.DataCollection;
+import com.arsdigita.persistence.DataQuery;
+import com.arsdigita.persistence.DataQueryDataCollectionAdapter;
+import com.arsdigita.persistence.Session;
+import com.arsdigita.persistence.SessionManager;
+
+/**
+ * TableModelBuilder that creates a model for the content section summary report.
+ *
+ * @author thomas-buckel
+ */
+public class ContentSectionSummaryReportTableModelBuilder extends AbstractTableModelBuilder
+{
+
+ @Override
+ public TableModel makeModel(Table t, PageState s)
+ {
+ Session session = SessionManager.getSession();
+ DataQuery query = session.retrieveQuery("com.arsdigita.cms.getContentSectionSummary");
+ query.setParameter("sectionId", CMS.getContext().getContentSection().getID());
+ return new CSSRModel(new DataQueryDataCollectionAdapter(query, "folder"));
+ }
+
+ /**
+ * Generates a table model by combining a list of top level folder (content items) with statistics about
+ * content items with in the folder in draft and live versions.
+ */
+ private class CSSRModel implements TableModel
+ {
+
+ private final DataCollection m_folders;
+
+ private String m_folderName;
+
+ private BigDecimal m_subfolderCount;
+
+ private ContentTypeStatistics m_currentStatsRow;
+
+ private Iterator m_contentTypeStatIter;
+
+ CSSRModel(DataCollection folders)
+ {
+ m_folders = folders;
+ }
+
+ @Override
+ public final int getColumnCount()
+ {
+ return 5;
+ }
+
+ /**
+ * Combines an 'outer' iterator over the given DataCollection with an 'inner' iterator
+ * that contains rows for each row of the outer collection.
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean nextRow()
+ {
+ if ((m_contentTypeStatIter == null) && m_folders.next())
+ {
+ m_folderName = (String) m_folders.get("folder");
+ m_subfolderCount = (BigDecimal) m_folders.get("subfolderCount");
+ Folder draftFolder = new Folder((BigDecimal) m_folders.get("folderId"));
+
+ m_contentTypeStatIter = retrieveContentTypeStats(draftFolder);
+ if (m_contentTypeStatIter.hasNext())
+ {
+ m_currentStatsRow = m_contentTypeStatIter.next();
+ }
+ else
+ {
+ // Rather than recursing into nextRow() again, a m_currentStatsRow == null
+ // is rendered to show one row for the folder but with no content type or values.
+ m_contentTypeStatIter = null;
+ m_currentStatsRow = null;
+ }
+ return true;
+ }
+ else if (m_contentTypeStatIter != null)
+ {
+ if (m_contentTypeStatIter.hasNext())
+ {
+ m_currentStatsRow = m_contentTypeStatIter.next();
+ }
+ else
+ {
+ m_contentTypeStatIter = null;
+ return nextRow();
+ }
+ return true;
+ }
+ else
+ {
+ m_folders.close();
+ return false;
+ }
+ }
+
+ @Override
+ public final Object getKeyAt(final int column)
+ {
+ return -1;
+ }
+
+ @Override
+ public final Object getElementAt(final int column)
+ {
+ switch (column)
+ {
+ case 0:
+ return m_folderName;
+ case 1:
+ return m_subfolderCount.toString();
+ case 2:
+ return (m_currentStatsRow != null) ? m_currentStatsRow.getContentType() : "N/A";
+ case 3:
+ return (m_currentStatsRow != null) ? m_currentStatsRow.getDraftCount() : "N/A";
+ case 4:
+ return (m_currentStatsRow != null) ? m_currentStatsRow.getLiveCount() : "N/A";
+ default:
+ throw new IllegalArgumentException("Illegal column index " + column);
+ }
+ }
+
+ /**
+ * Retrieve a list of content types used within a folder and for each content type
+ * the number of draft and live content items of this type.
+ *
+ * @param draftFolder Draft folder to retrieve stats for.
+ *
+ * @return Iterator over the retrieved statistics. Empty iterator with no results
+ * where found.
+ */
+ private Iterator retrieveContentTypeStats(Folder draftFolder)
+ {
+
+ Session session = SessionManager.getSession();
+
+ // Query the number of content items per content type for drafts
+ DataQuery query = session.retrieveQuery("com.arsdigita.cms.getContentTypeCountPerFolder");
+ query.setParameter("folderId", draftFolder.getID());
+ DataCollection types = new DataQueryDataCollectionAdapter(query, "types");
+ Map draftContentTypeCounts = new HashMap();
+ try
+ {
+ while (types.next())
+ {
+ draftContentTypeCounts.put((String) types.get("contentType"), (Long) types.get("typeCount"));
+ }
+ }
+ finally
+ {
+ types.close();
+ }
+
+ // If there's a live version of the folder, query the number of content items per content type for it
+ // and merge both draft and live numbers
+ List result = new ArrayList();
+ Folder liveFolder = (Folder) draftFolder.getLiveVersion();
+ if (liveFolder != null)
+ {
+ query = session.retrieveQuery("com.arsdigita.cms.getContentTypeCountPerFolder");
+ query.setParameter("folderId", liveFolder.getID());
+ types = new DataQueryDataCollectionAdapter(query, "types");
+ try
+ {
+ while (types.next())
+ {
+ String contentType = (String) types.get("contentType");
+ long draftCount = (draftContentTypeCounts.get(contentType) != null) ? draftContentTypeCounts
+ .get(contentType) : 0;
+ long liveCount = (Long) types.get("typeCount");
+ result.add(new ContentTypeStatistics(contentType, draftCount, liveCount));
+ draftContentTypeCounts.remove(contentType);
+ }
+ }
+ finally
+ {
+ types.close();
+ }
+ }
+
+ // Add all draft stats that haven't been merged
+ for (Map.Entry draftCount : draftContentTypeCounts.entrySet())
+ {
+ result.add(new ContentTypeStatistics(draftCount.getKey(), draftCount.getValue(), 0));
+ }
+
+ return result.iterator();
+ }
+ }
+
+ /**
+ * Value object that holds content type statistics for a folder.
+ */
+ private static class ContentTypeStatistics
+ {
+
+ private final String m_contentType;
+
+ private final long m_draftCount;
+
+ private final long m_liveCount;
+
+ public ContentTypeStatistics(String contentType, long draftCount, long liveCount)
+ {
+ m_contentType = contentType;
+ m_draftCount = draftCount;
+ m_liveCount = liveCount;
+ }
+
+ public String getContentType()
+ {
+ return m_contentType;
+ }
+
+ public long getDraftCount()
+ {
+ return m_draftCount;
+ }
+
+ public long getLiveCount()
+ {
+ return m_liveCount;
+ }
+
+ }
+
+}
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/report/ContentSectionSummaryTable.java b/ccm-cms/src/com/arsdigita/cms/ui/report/ContentSectionSummaryTable.java
new file mode 100644
index 000000000..fa63b0728
--- /dev/null
+++ b/ccm-cms/src/com/arsdigita/cms/ui/report/ContentSectionSummaryTable.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 Permeance Technologies Pty Ltd. 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.report;
+
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.cms.util.GlobalizationUtil;
+
+/**
+ * Table component for content section summary report.
+ *
+ * @author thomas-buckel
+ */
+public class ContentSectionSummaryTable extends Table
+{
+
+ private static final String[] s_fixedReportColumns = new String[] { lz("cms.ui.reports.css.folder"),
+ lz("cms.ui.reports.css.subfolderCount"), lz("cms.ui.reports.css.contentType"),
+ lz("cms.ui.reports.css.draft"), lz("cms.ui.reports.css.live"), };
+
+ public ContentSectionSummaryTable()
+ {
+ super(new ContentSectionSummaryReportTableModelBuilder(), s_fixedReportColumns);
+ setEmptyView(new Label(lz("cms.ui.reports.css.emptyResult")));
+ }
+
+ private static String lz(final String key)
+ {
+ return (String) GlobalizationUtil.globalize(key).localize();
+ }
+
+}
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/report/Report.java b/ccm-cms/src/com/arsdigita/cms/ui/report/Report.java
new file mode 100644
index 000000000..738443a4f
--- /dev/null
+++ b/ccm-cms/src/com/arsdigita/cms/ui/report/Report.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 Permeance Technologies Pty Ltd. 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.report;
+
+import com.arsdigita.bebop.Component;
+import com.arsdigita.cms.util.GlobalizationUtil;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.util.Assert;
+
+/**
+ * UI model for a report.
+ * A report has a name and a component that displays the report.
+ *
+ * @author thomas-buckel
+ * @author tim-permeance
+ */
+public class Report {
+
+ private final String m_key;
+ private final String m_name;
+ private final Component m_component;
+
+ public Report(String key, Component component) {
+ Assert.exists(key, "Key for report is required");
+ Assert.isTrue(key.length() > 0, "Key for report must not be empty");
+ Assert.exists(component, "Component for report is required");
+
+ m_key = key;
+ m_name = gz(m_key).localize().toString();
+ m_component = component;
+ }
+
+ public String getKey() {
+ return m_key;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public Component getComponent() {
+ return m_component;
+ }
+
+ protected final static GlobalizedMessage gz(final String key) {
+ return GlobalizationUtil.globalize(key);
+ }
+
+}
diff --git a/ccm-cms/src/com/arsdigita/cms/ui/report/ReportListModel.java b/ccm-cms/src/com/arsdigita/cms/ui/report/ReportListModel.java
new file mode 100644
index 000000000..6c2719f39
--- /dev/null
+++ b/ccm-cms/src/com/arsdigita/cms/ui/report/ReportListModel.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Permeance Technologies Pty Ltd. 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.report;
+
+import java.util.List;
+
+import com.arsdigita.bebop.list.ListModel;
+import com.arsdigita.util.Assert;
+
+/**
+ * ListModel for Reports.
+ *
+ * @author thomas-buckel
+ * @author tim-permeance
+ */
+public class ReportListModel implements ListModel {
+
+ private int m_index = -1;
+ private final List m_reports;
+
+ public ReportListModel(List reports) {
+ Assert.exists(reports);
+ m_reports = reports;
+ }
+
+ @Override
+ public Object getElement() {
+ return m_reports.get(m_index).getName();
+ }
+
+ @Override
+ public String getKey() {
+ return m_reports.get(m_index).getKey();
+ }
+
+ @Override
+ public boolean next() {
+ m_index++;
+ return (m_reports.size() > m_index);
+ }
+
+}