diff --git a/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml b/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml
index c7937b77e..37684c867 100644
--- a/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml
+++ b/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml
@@ -45,5 +45,8 @@
+
+
\ No newline at end of file
diff --git a/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/faces-config.xml b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/faces-config.xml
new file mode 100644
index 000000000..59d6547c7
--- /dev/null
+++ b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/faces-config.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml
index fcb05ca5c..524e50256 100644
--- a/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml
+++ b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/web.xml
@@ -26,4 +26,20 @@
org.apache.shiro.web.env.EnvironmentLoaderListener
+
+
+ Faces Servlet
+ javax.faces.webapp.FacesServlet
+ 1
+
+
+
+ Faces Servlet
+ *.xhtml
+
+
+
+ javax.faces.PROJECT_STAGE
+ Development
+
diff --git a/ccm-bundle-devel-wildfly-web/src/main/webapp/test-index.xhtml b/ccm-bundle-devel-wildfly-web/src/main/webapp/test-index.xhtml
new file mode 100644
index 000000000..32b0d058d
--- /dev/null
+++ b/ccm-bundle-devel-wildfly-web/src/main/webapp/test-index.xhtml
@@ -0,0 +1,17 @@
+
+
+
+
+ JSF Test INDEX
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterAppCreator.java b/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterAppCreator.java
new file mode 100644
index 000000000..557a4c0c4
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterAppCreator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package com.arsdigita.cms;
+
+import org.libreccm.web.ApplicationCreator;
+import org.libreccm.web.ApplicationRepository;
+import org.libreccm.web.ApplicationType;
+import org.libreccm.web.CcmApplication;
+import org.librecms.CmsConstants;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class ContentCenterAppCreator implements ApplicationCreator {
+
+ @Inject
+ private ApplicationRepository appRepository;
+
+ @Override
+ public CcmApplication createInstance(final String primaryUrl,
+ final ApplicationType type) {
+ if (!CmsConstants.CONTENT_CENTER_URL.equals(primaryUrl)) {
+ throw new IllegalArgumentException(
+ "ContentCenter is a singleton application which is mounted at "
+ + "/content-center/");
+ }
+
+ return appRepository.retrieveApplicationForPath(primaryUrl);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterServlet.java b/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterServlet.java
index b5eecd808..587e3819d 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterServlet.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterServlet.java
@@ -48,7 +48,6 @@ import org.librecms.contentsection.ContentSectionRepository;
import java.io.IOException;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -136,8 +135,8 @@ public class ContentCenterServlet extends BaseApplicationServlet {
/* Check user and privilegies */
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final Shiro shiro = cdiUtil.findBean(Shiro.class);
- if (shiro.getSubject().isAuthenticated()) {
- throw new LoginSignal(sreq); // send to login page
+ if (!shiro.getSubject().isAuthenticated()) {
+ throw new LoginSignal(sreq); // send to login page
}
final PermissionChecker permissionChecker = cdiUtil.findBean(
PermissionChecker.class);
@@ -178,7 +177,7 @@ public class ContentCenterServlet extends BaseApplicationServlet {
// DispatcherHelper.sendRedirect(sresp, originalUrl + "/");
// return;
// }
- final Page page = (Page) pages.get(pathInfo);
+ final Page page = pages.get(pathInfo);
if (page != null) {
// Check user access.
@@ -274,7 +273,7 @@ public class ContentCenterServlet extends BaseApplicationServlet {
)
throws ServletException {
- if (CdiUtil.createCdiUtil().findBean(Shiro.class).getSubject()
+ if (!CdiUtil.createCdiUtil().findBean(Shiro.class).getSubject()
.isAuthenticated()) {
throw new LoginSignal(request);
}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterSetup.java b/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterSetup.java
new file mode 100644
index 000000000..f03aad40e
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ContentCenterSetup.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package com.arsdigita.cms;
+
+import org.libreccm.modules.InstallEvent;
+import org.libreccm.web.AbstractCcmApplicationSetup;
+import org.libreccm.web.CcmApplication;
+import org.librecms.CmsConstants;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ContentCenterSetup extends AbstractCcmApplicationSetup {
+
+ public ContentCenterSetup(final InstallEvent event) {
+ super(event);
+ }
+
+ @Override
+ public void setup() {
+ final CcmApplication contentCenter = new CcmApplication();
+ contentCenter.setUuid(UUID.randomUUID().toString());
+ contentCenter.setApplicationType(CmsConstants.CONTENT_CENTER_APP_TYPE);
+ contentCenter.setPrimaryUrl(CmsConstants.CONTENT_CENTER_URL);
+
+ getEntityManager().persist(contentCenter);
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearch.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearch.java.off
new file mode 100755
index 000000000..7f563c90d
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/ItemSearch.java.off
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2001-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.Form;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.Resettable;
+import com.arsdigita.bebop.SimpleContainer;
+
+import com.arsdigita.search.QuerySpecification;
+import com.arsdigita.search.ui.QueryGenerator;
+
+/**
+ * A wrapper around the {@link ItemSearchSection} which embedds
+ * the form section in a form.
+ *
+ * @author Stanislav Freidin (sfreidin@arsdigita.com)
+ * @version $Id: ItemSearch.java 1940 2009-05-29 07:15:05Z terry $
+ */
+public class ItemSearch extends Form implements Resettable, QueryGenerator {
+
+ private static final org.apache.log4j.Logger s_log =
+ org.apache.log4j.Logger.getLogger(ItemSearch.class);
+ public static final String SINGLE_TYPE_PARAM = ItemSearchSection.SINGLE_TYPE_PARAM;
+ private ItemSearchSection m_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}
+ */
+ public ItemSearch(String context) {
+ this(context, true);
+ }
+
+ /**
+ * 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
+ */
+ public ItemSearch(String context, boolean limitToContentSection) {
+ super("itemSearch", new SimpleContainer());
+ //setMethod("GET");
+ m_section = createSearchSection(context, limitToContentSection);
+ add(m_section);
+ }
+
+ protected ItemSearchSection createSearchSection(String context, boolean limitToContentSection) {
+ return new ItemSearchSection(context, limitToContentSection);
+ }
+
+ @Override
+ public boolean hasQuery(PageState state) {
+ return m_section.hasQuery(state);
+ }
+
+ @Override
+ public QuerySpecification getQuerySpecification(PageState state) {
+ return m_section.getQuerySpecification(state);
+ }
+
+ @Override
+ public void reset(PageState state) {
+ m_section.reset(state);
+ }
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.off b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.off
new file mode 100755
index 000000000..17f626f2b
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/NewItemForm.java.off
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2001-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.authoring;
+
+import com.arsdigita.bebop.BoxPanel;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.PrintEvent;
+import com.arsdigita.bebop.event.PrintListener;
+import com.arsdigita.bebop.form.Option;
+import com.arsdigita.bebop.form.OptionGroup;
+import com.arsdigita.bebop.form.SingleSelect;
+import com.arsdigita.bebop.form.Submit;
+import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import com.arsdigita.cms.ui.ItemSearch;
+import com.arsdigita.globalization.GlobalizedMessage;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+import com.arsdigita.util.UncheckedWrapperException;
+import com.arsdigita.xml.Element;
+
+import java.math.BigDecimal;
+
+import org.apache.log4j.Logger;
+import org.libreccm.security.Party;
+import org.librecms.CmsConstants;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.contentsection.ContentType;
+
+import java.awt.image.Kernel;
+import java.util.List;
+
+/**
+ * A form element which displays a select box of all content types available
+ * under the given content section, and forwards to the item creation UI when
+ * the user selects a content type to instantiate.
+ *
+ * @author Stanislav Freidin (sfreidin@arsdigtia.com)
+ * @version $Revision: #12 $ $DateTime: 2004/08/17 23:15:09 $
+ * @version $Id: NewItemForm.java 2161 2011-02-02 00:16:13Z pboy $
+ */
+public abstract class NewItemForm extends Form {
+
+ /**
+ * Internal logger instance to faciliate debugging. Enable logging output by
+ * editing /WEB-INF/conf/log4j.properties int hte runtime environment and
+ * set com.arsdigita.cms.ui.authoring.NewItemForm=DEBUG by uncommenting or
+ * adding the line.
+ */
+ private static final Logger s_log = Logger.getLogger(NewItemForm.class);
+
+ private final SingleSelect m_typeWidget;
+ private final Submit m_submit;
+ private final Label m_emptyLabel;
+ private final Label m_createLabel;
+ public static final String TYPE_ID = "tid";
+
+ public NewItemForm(String name) {
+ this(name, BoxPanel.HORIZONTAL);
+ }
+
+ /**
+ * Construct a new NewItemForm. It sets a vertical BoxPanel as the component
+ * container.
+ *
+ * @param name the name attribute of the form.
+ */
+ public NewItemForm(String name, int orientation) {
+
+ super(name, new BoxPanel(BoxPanel.VERTICAL));
+ setIdAttr("new_item_form");
+
+ //BoxPanel panel = new BoxPanel(BoxPanel.HORIZONTAL);
+ BoxPanel panel = new BoxPanel(orientation);
+ panel.setWidth("2%");
+ panel.setBorder(0);
+
+ // create and add an "empty" component
+ m_emptyLabel = new Label(new GlobalizedMessage(
+ "cms.ui.authoring.no_types_registered", CmsConstants.CMS_BUNDLE),
+ false);
+ m_emptyLabel.setIdAttr("empty_label");
+ panel.add(m_emptyLabel);
+
+ m_createLabel = new Label(new GlobalizedMessage(
+ "cms.ui.authoring.create_new", CmsConstants.CMS_BUNDLE),
+ false);
+ m_createLabel.setIdAttr("create_label");
+ panel.add(m_createLabel);
+
+ m_typeWidget = new SingleSelect(new BigDecimalParameter(TYPE_ID),
+ OptionGroup.SortMode.ALPHABETICAL_ASCENDING);
+ try {
+ m_typeWidget.addPrintListener(new PrintListener() {
+
+ // Read the content section's content types and add them as options
+ @Override
+ public void prepare(PrintEvent e) {
+ OptionGroup o = (OptionGroup) e.getTarget();
+ o.clearOptions();
+ PageState state = e.getPageState();
+
+ // gather the content types of this section into a list
+ ContentSection section = getContentSection(state);
+ ContentType parentType = null;
+ List typesCollection = null;
+ BigDecimal singleTypeID = (BigDecimal) state.getValue(
+ new BigDecimalParameter(
+ ItemSearch.SINGLE_TYPE_PARAM));
+
+ if (singleTypeID != null) {
+ try {
+ parentType = new ContentType(singleTypeID);
+ } catch (DataObjectNotFoundException ex) {
+ parentType = null;
+ }
+ }
+
+ if (parentType == null) {
+ typesCollection = section.getCreatableContentTypes();
+ } else {
+ typesCollection = section.getDescendantsOfContentType(
+ parentType);
+ }
+
+ typesCollection.addOrder(ContentType.LABEL);
+
+ if (!typesCollection.isEmpty()) {
+ // Add content types
+ while (typesCollection.next()) {
+ boolean list = true;
+ ContentType type = typesCollection.getContentType();
+ if (PermissionService
+ .getDirectGrantedPermissions(type.getOID())
+ .size() > 0) {
+ // chris gilbert - allow restriction of some types
+ // to certain users/groups. No interface to do
+ // this, but group could be created and permission
+ // granted in a content type loader
+ //
+ // can't permission filter the collection because
+ // most types will have no permissions granted.
+ // This approach involves a small overhead getting
+ // the count of granted permissions for each type
+ // (mitigated by only checking DIRECT permissions)
+
+ Party party = Kernel.getContext().getParty();
+ if (party == null) {
+ party = Kernel.getPublicUser();
+ }
+ PermissionDescriptor create
+ = new PermissionDescriptor(
+ PrivilegeDescriptor
+ .get(SecurityManager.CMS_NEW_ITEM),
+ type,
+ party);
+ list = PermissionService.checkPermission(create);
+
+ }
+ if (list) {
+ // o.addOption(new Option(type.getID().toString(), type.getName()));
+ o.addOption(new Option(type.getID().toString(),
+ new Label(type.getLabel())));
+ }
+
+ }
+ typesCollection.reset();
+ }
+ }
+
+ });
+ } catch (java.util.TooManyListenersException e) {
+ throw new UncheckedWrapperException("Too many listeners: " + e
+ .getMessage(), e);
+ }
+
+ panel.add(m_typeWidget);
+
+ m_submit = new Submit("new", GlobalizationUtil.globalize(
+ "cms.ui.authoring.go"));
+ panel.add(m_submit);
+
+ add(panel);
+ }
+
+ public abstract ContentSection getContentSection(PageState state);
+
+ /**
+ *
+ * @param state
+ *
+ * @return
+ */
+ public BigDecimal getTypeID(PageState state) {
+ return (BigDecimal) m_typeWidget.getValue(state);
+ }
+
+ /**
+ *
+ * @return
+ */
+ public final SingleSelect getTypeSelect() {
+ return m_typeWidget;
+ }
+
+ /**
+ * Generate XML - show/hide labels/widgets
+ *
+ * @param state
+ * @param parent
+ */
+ @Override
+ public void generateXML(PageState state, Element parent) {
+
+ if (isVisible(state)) {
+ ContentSection section = getContentSection(state);
+
+ ContentTypeCollection c = section.getCreatableContentTypes();
+ boolean isEmpty = c.isEmpty();
+ c.close();
+
+ m_createLabel.setVisible(state, !isEmpty);
+ m_typeWidget.setVisible(state, !isEmpty);
+ m_submit.setVisible(state, !isEmpty);
+ m_emptyLabel.setVisible(state, isEmpty);
+
+ super.generateXML(state, parent);
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java
new file mode 100755
index 000000000..7fffe49c2
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/ContentSectionContainer.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2001-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.contentcenter;
+
+import com.arsdigita.bebop.BoxPanel;
+
+import java.math.BigDecimal;
+
+import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.Embedded;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.Link;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SingleSelectionModel;
+import com.arsdigita.bebop.Table;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.event.FormSubmissionListener;
+import com.arsdigita.bebop.form.Hidden;
+import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import com.arsdigita.bebop.table.TableCellRenderer;
+import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.table.TableColumnModel;
+import com.arsdigita.bebop.table.TableModel;
+import com.arsdigita.bebop.table.TableModelBuilder;
+import com.arsdigita.cms.ui.CMSContainer;
+import com.arsdigita.ui.admin.GlobalizationUtil;
+import com.arsdigita.util.Assert;
+import com.arsdigita.util.LockableImpl;
+import com.arsdigita.web.Web;
+
+import org.libreccm.categorization.Category;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.security.PermissionChecker;
+import org.libreccm.security.User;
+import org.librecms.CmsConstants;
+import org.librecms.contentsection.ContentSection;
+import org.librecms.contentsection.ContentSectionConfig;
+import org.librecms.contentsection.ContentSectionRepository;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.mail.Folder;
+
+/**
+ * Displays all the content sections in table, with links to the admin (and in
+ * legacy mode to legacy public pages as well). Also displays a form for each
+ * content section to create an object of a given type (configurable). The list
+ * of available types retrieved for each content section.
+ *
+ *
+ * This class is a container for two other components: a form and a table. The
+ * form represents the drop down list of the content types available in a
+ * particular content section. It is an extension of the
+ * {@link com.arsdigita.cms.ui.authoring.NewItemForm}. The table displays each
+ * content section in one row, along with the specified form. The same form is
+ * reused in every row of the table.
+ *
+ * @author Michael Bryzek
+ * @version $Id: ContentSectionContainer.java 287 2005-02-22 00:29:02Z sskracic$
+ */
+public class ContentSectionContainer extends CMSContainer {
+
+ private static final String CONTENT_SECTION_CLASS = "contentSections";
+
+ private final ContentSectionTable m_table;
+ private final FormContainer m_formContainer;
+ private final SingleSelectionModel m_typeSel;
+ private final SingleSelectionModel m_sectionSel;
+
+ /**
+ * Constructs a new ContentSectionContainer which containts:
+ *
+ *
+ * - SimpleContainer (to contain the form)
+ *
+ * - Form (for creating a new content item in each section)
+ *
+ * - Table (Displays all content sections)
+ *
+ *
+ * @param typeSel passthrough to {@link NewItemForm}
+ * @param sectionSel passthrough to {@link NewItemForm}
+ */
+ public ContentSectionContainer(SingleSelectionModel typeSel,
+ SingleSelectionModel sectionSel) {
+ super();
+ setClassAttr(CONTENT_SECTION_CLASS);
+
+ m_typeSel = typeSel;
+ m_sectionSel = sectionSel;
+
+ m_formContainer = new FormContainer();
+ add(m_formContainer);
+ m_table = new ContentSectionTable();
+ add(m_table);
+ }
+
+ /**
+ *
+ * @param p
+ */
+ @Override
+ public void register(Page p) {
+ super.register(p);
+ p.setVisibleDefault(m_formContainer, false);
+ }
+
+ /**
+ *
+ */
+ private class FormContainer extends CMSContainer {
+
+// private final StaticNewItemForm m_form;
+ private final BigDecimalParameter m_sectionIdParam;
+
+ /**
+ * Constructor
+ */
+ private FormContainer() {
+ super();
+ m_sectionIdParam = new BigDecimalParameter("sectionId");
+// m_form = new StaticNewItemForm(m_sectionIdParam);
+
+// m_form.addSubmissionListener(new FormSubmissionListener() {
+//
+// /**
+// * Cancels the form if the user lacks the "create new items"
+// * privilege.
+// */
+// @Override
+// public void submitted(FormSectionEvent event)
+// throws FormProcessException {
+// PageState state = event.getPageState();
+// StaticNewItemForm form = (StaticNewItemForm) event
+// .getSource();
+//
+// ContentSection section = form.getContentSection(state);
+// final PermissionChecker permissionChecker = CdiUtil
+// .createCdiUtil().findBean(PermissionChecker.class);
+// Category folder = null;
+// //ToDo
+//// User user = Web.getWebContext().getUser();
+//// if (user != null) {
+//// folder = Folder.getUserHomeFolder(user, section);
+//// }
+//// if (folder == null) {
+//// folder = section.getRootFolder();
+//// }
+////ToDo End
+// folder = section.getRootDocumentsFolder();
+//
+// if (!permissionChecker.isPermitted(
+// CmsConstants.PRIVILEGE_ITEMS_CREATE_NEW, folder)) {
+// throw new FormProcessException(
+// (GlobalizationUtil.globalize(
+// "cms.ui.insufficient_privileges")));
+// }
+// }
+//
+// });
+//
+// m_form.addProcessListener(new FormProcessListener() {
+//
+// /**
+// * Process listener: redirects to the authoring kit to create a
+// * new item.
+// */
+// @Override
+// public void process(FormSectionEvent e) throws
+// FormProcessException {
+// StaticNewItemForm form = (StaticNewItemForm) e.getSource();
+// PageState state = e.getPageState();
+//
+// BigDecimal typeId = form.getTypeID(state);
+// if (typeId != null) {
+// Long sectionId = form.getContentSectionID(state);
+// m_sectionSel.setSelectedKey(state, sectionId);
+// m_typeSel.setSelectedKey(state, typeId);
+// }
+// }
+//
+// });
+//
+// add(m_form);
+ }
+
+ @Override
+ public void register(Page p) {
+ super.register(p);
+ p.addComponentStateParam(this, m_sectionIdParam);
+ }
+
+// public StaticNewItemForm getNewItemForm() {
+//// return m_form;
+// }
+
+ }
+
+// private static class StaticNewItemForm extends NewItemForm {
+//
+// private final Hidden m_sectionIDParamWidget;
+//
+// public StaticNewItemForm(BigDecimalParameter sectionParam) {
+// super("StaticNewItemForm", BoxPanel.VERTICAL);
+// setClassAttr("static-new-item-form");
+// m_sectionIDParamWidget = new Hidden(sectionParam);
+// add(m_sectionIDParamWidget);
+// setProcessInvisible(true);
+// }
+//
+// /**
+// * Sets the id of the content section in this form. This ID is used to
+// * generate a list of available content types in the section.
+// *
+// * @param state The current page state.
+// * @param id The id of the ContentSection for which this form should
+// * display a list of content types
+// *
+// * @pre ( state != null && id != null )
+// */
+// public void setSectionId(PageState state, BigDecimal id) {
+// Assert.exists(id);
+// m_sectionIDParamWidget.setValue(state, id);
+// }
+//
+// /**
+// * Retrieves the content section for this form given the specified page
+// * state. This method will return null if there is no content section.
+// *
+// * @param state The current page state.
+// *
+// * @return The current content section or null if the section does not
+// * exist
+// *
+// * @pre ( state != null )
+// */
+// @Override
+// public ContentSection getContentSection(PageState state) {
+// Long id = getContentSectionID(state);
+// Assert.exists(id);
+// ContentSection section;
+// section = CdiUtil.createCdiUtil().findBean(
+// ContentSectionRepository.class).findById(id);
+// return section;
+// }
+//
+// /**
+// * Retrieves the ID of the content section for this form given the
+// * specified page state. This method will return null if no content
+// * section id has been set.
+// *
+// * @param state The current page state.
+// *
+// * @return The id of the content section or null if it has not been set.
+// *
+// * @pre ( state != null )
+// */
+// private Long getContentSectionID(PageState state) {
+// return (Long) Long.parseLong((String) m_sectionIDParamWidget
+// .getValue(state));
+// }
+//
+// }
+
+ /**
+ * A table that displays all content sections, with links to their locations
+ * and admin pages and a {@link NewItemForm} next to each section.
+ *
+ * @author Michael Bryzek
+ * @version $Revision$ $DateTime: 2004/08/17 23:15:09 $
+ *
+ */
+ private class ContentSectionTable extends Table {
+
+ // We will use a (symboloc) headerKey to match columns. Because the
+ // number of columns depends on configuration for the llocation column,
+ // the index varies and con not be used.
+ private static final String COLUMN_SECTION = "Section";
+ private static final String COLUMN_LOCATION = "Public Site";
+ private static final String COLUMN_ACTION = "Action";
+
+ /**
+ * Constructs a new ContentSectionTable, using a default table model
+ * builder.
+ */
+ private ContentSectionTable() {
+ super();
+
+ // we must use symbolic keys (instead of symbolic column index as
+ // usual) to identify a column because their number is dynamic
+ // depending on configuration of the location column!
+ Integer colNo = 0;
+
+ Label emptyView = new Label(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.section"));
+ emptyView.setFontWeight(Label.ITALIC);
+ setEmptyView(emptyView);
+
+ setClassAttr("dataTable");
+
+ // add columns to the table
+ TableColumnModel columnModel = getColumnModel();
+
+ // prepare column headers
+ Label sectionHead = new Label(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.section"));
+ sectionHead.setHint(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.section_hint"));
+ Label locationHead = new Label(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.location"));
+ locationHead.setHint(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.location_hint"));
+ Label actionHead = new Label(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.action"));
+ actionHead.setHint(GlobalizationUtil
+ .globalize("cms.ui.contentcenter.action_hint"));
+
+ //TableColumn contentSectionColumn = new TableColumn(colNo, COLUMN_SECTION);
+ TableColumn contentSectionColumn = new TableColumn(
+ colNo,
+ sectionHead,
+ COLUMN_SECTION);
+ contentSectionColumn
+ .setCellRenderer(new AdminURLTableCellRenderer());
+ columnModel.add(contentSectionColumn);
+
+ TableColumn actionColumn = new TableColumn(
+ colNo++,
+ actionHead,
+ COLUMN_ACTION);
+ actionColumn.setCellRenderer(new ActionTableCellRenderer());
+ columnModel.add(actionColumn);
+
+ setModelBuilder(new ContentSectionTableModelBuilder());
+ }
+
+ /**
+ * An ContentSections table model builder
+ *
+ * @author Michael Bryzek
+ *
+ */
+ private class ContentSectionTableModelBuilder extends LockableImpl
+ implements TableModelBuilder {
+
+ @Override
+ public TableModel makeModel(Table table, PageState state) {
+ table.getRowSelectionModel().clearSelection(state);
+ return new ContentSectionTableModel((ContentSectionTable) table,
+ state);
+ }
+
+ }
+
+ /**
+ * An ContentSections table model
+ *
+ * @author Michael Bryzek
+ *
+ */
+ private class ContentSectionTableModel implements TableModel {
+
+ private final ContentSectionTable m_table;
+ private final TableColumnModel m_columnModel;
+ private final PageState m_state;
+ private final List m_contentSections;
+ private ContentSection m_section;
+ private int index = -1;
+
+ private ContentSectionTableModel(ContentSectionTable table,
+ PageState state) {
+ m_table = table;
+ m_columnModel = table.getColumnModel();
+ m_state = state;
+
+ // retrieve all Content Sections
+ m_contentSections = getContentSectionCollection();
+ }
+
+ /**
+ * Returns a collection of ContentSections to display in this table.
+ * This implementation orders the content sections by
+ * lower(label). They are also already filtered for the
+ * sections to which the current user has no access.
+ *
+ */
+ private List getContentSectionCollection() {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final PermissionChecker permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+ final List allSections = cdiUtil.findBean(
+ ContentSectionRepository.class).findAll();
+ return allSections
+ .stream()
+ .filter(section -> permissionChecker
+ .isPermitted(CmsConstants.PRIVILEGE_ITEMS_VIEW_PUBLISHED,
+ section))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public int getColumnCount() {
+ return m_columnModel.size();
+ }
+
+ @Override
+ public boolean nextRow() {
+ index++;
+ if (index < m_contentSections.size()) {
+ m_section = m_contentSections.get(index);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * By default, we return null. For the section, location, and action
+ * columns, we return the current Content Section if there is one.
+ *
+ * @param columnIndex The index of the current column
+ */
+ @Override
+ public Object getElementAt(int columnIndex) {
+ if (m_columnModel == null || m_section == null) {
+ return null;
+ }
+
+ TableColumn tc = m_columnModel.get(columnIndex);
+ String columnKey = (String) tc.getHeaderKey();
+
+ Object result = m_section;
+ if (columnKey.equals(COLUMN_SECTION)
+ || columnKey.equals(COLUMN_LOCATION)
+ || columnKey.equals(
+ COLUMN_ACTION)) {
+ result = m_section;
+ }
+ return result;
+ }
+
+ @Override
+ public Object getKeyAt(int columnIndex) {
+ return m_section.getObjectId();
+ }
+
+ /**
+ * Returns the table associated with this table model.
+ *
+ */
+ protected Table getTable() {
+ return m_table;
+ }
+
+ /**
+ * Returns the current page state
+ *
+ */
+ protected PageState getPageState() {
+ return m_state;
+ }
+
+ }
+
+ /**
+ * Sets the hidden parameter in the form containers form to the id of
+ * the current section. Then returns the form for display, but only if
+ * the user has permission to create new items in the current section.
+ *
+ * @author Michael Bryzek
+ *
+ */
+ private class ActionTableCellRenderer implements TableCellRenderer {
+
+ @Override
+ public Component getComponent(Table table, PageState state,
+ Object value,
+ boolean isSelected, Object key,
+ int row, int column) {
+ ContentSection section = (ContentSection) value;
+ Category folder = null;
+ //ToDo
+// User user = Web.getWebContext().getUser();
+// if (user != null) {
+// folder = Folder.getUserHomeFolder(user, section);
+// }
+// if (folder == null) {
+// folder = section.getRootFolder();
+// }
+
+ folder = section.getRootDocumentsFolder();
+ // If the user has no access, return an empty Label
+
+//
+// SecurityManager sm = new SecurityManager(section);
+//
+// if (!sm.canAccess(state.getRequest(), SecurityManager.NEW_ITEM,
+// folder)
+// || !ContentSection.getConfig()
+// .getAllowContentCreateInSectionListing()) {
+// // return null; // produces NPE here but works somewhere else.
+// // It's a kind of a hack. Label is supposed not to accept
+// // not-gloabalized data. Usually aou will return null here
+// // and xmlgenerator takes care of it. Doesn't work here.
+// return new Embedded(
+// " - - ");
+// } else {
+// // set the value of the sectionIdParameter in the form
+// // to this section
+// m_formContainer.getNewItemForm().setSectionId(state, section
+// .getID());
+// return m_formContainer.getNewItemForm();
+// }
+ //ToDo End
+ return new Embedded(
+ " - - ");
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Generates the correct URL to the public pages for a content section.
+ *
+ * @author Michael Bryzek
+ *
+ */
+ public static class URLTableCellRenderer implements TableCellRenderer {
+
+ /**
+ * The object passed in is the current content section. This returns a
+ * Link whose name and target are the url to the public pages.
+ *
+ * @return Link whose name and target are the url to the public pages of
+ * the current (passed in) content section or a Label if current
+ * use does not habe acces priviledge for the content section
+ */
+ @Override
+ public Component getComponent(Table table,
+ PageState state,
+ Object value,
+ boolean isSelected,
+ Object key,
+ int row,
+ int column) {
+
+ /* cast to ContentSection for further processing */
+ ContentSection section = (ContentSection) value;
+ String name = section.getLabel();
+ String path = section.getPrimaryUrl(); // from Application
+
+ // If the user has no access, return a Label instead of a Link
+ // Kind of a hack because Label is supposed not to accept
+ // "un-globalized" display data. Label had been abused here to
+ // to display a DataValue
+ return new Embedded("/" + name + "/", false);
+ // return null; // produces NPE here
+
+ }
+
+ }
+
+ /**
+ * Generates the correct URL to the admin pages for a content section.
+ *
+ * @author Michael Bryzek
+ *
+ */
+ public static class AdminURLTableCellRenderer extends URLTableCellRenderer {
+
+ /**
+ * The object passed in is the current content section
+ *
+ * @param table
+ * @param state
+ * @param row
+ * @param value
+ * @param column
+ * @param isSelected
+ * @param key
+ *
+ * @return
+ *
+ */
+ @Override
+ public Component getComponent(Table table, PageState state, Object value,
+ boolean isSelected, Object key,
+ int row, int column) {
+ ContentSection section = (ContentSection) value;
+
+ final PermissionChecker permissionChecker = CdiUtil.createCdiUtil()
+ .findBean(PermissionChecker.class);
+
+ // If the user has no access, return a Label instead of a Link
+ if (permissionChecker.isPermitted(
+ CmsConstants.PRIVILEGE_ITEMS_EDIT,
+ section.getRootDocumentsFolder())) {
+
+ return new Link(section.getLabel(),
+ generateURL(section.getPrimaryUrl() + "/"));
+ } else {
+ //return new Label(section.getName(), false);
+ // return null; // Produces a NPE although it shouldn't and
+ // indeed doesn't elsewhere
+ // Kind of a hack because Label is supposed not to accept
+ // "un-globalized" display data. Label had been abused here to
+ // to display a DataValue
+ return new Embedded(section.getLabel(), false);
+ }
+ }
+
+ /**
+ * Generates the admin url for the specified prefix. Always returns
+ * something that does not start with a forward slash.
+ *
+ * @param prefix The prefix of the URL
+ *
+ * @return
+ */
+ protected String generateURL(String prefix) {
+ return prefix;// + PageLocations.SECTION_PAGE;
+ }
+
+ }
+
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/TasksPanel.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/TasksPanel.java
index 12d0a81d6..77d93c43e 100755
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/TasksPanel.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/contentcenter/TasksPanel.java
@@ -75,8 +75,12 @@ public class TasksPanel extends CMSContainer {
// private ActionLink m_viewAllLink;
// private ActionLink m_viewShortLink;
private Paginator m_paginator;
- private ActionLink m_viewLockLink, m_viewUnlockLink, m_viewAllLockLink;
- private Label m_viewLockLabel, m_viewUnlockLabel, m_viewAllLockLabel;
+ private ActionLink m_viewLockLink;
+ private ActionLink m_viewUnlockLink;
+ private ActionLink m_viewAllLockLink;
+ private Label m_viewLockLabel;
+ private Label m_viewUnlockLabel;
+ private Label m_viewAllLockLabel;
private StringParameter m_sortDirectionParam;
private StringParameter m_sortTypeParam;
private StringParameter m_lockFilterParam;
@@ -103,7 +107,7 @@ public class TasksPanel extends CMSContainer {
private Label m_selectorLabel;
//ToDo
// private CreationSelector m_selector;
-// private ContentSectionContainer m_sections;
+ private ContentSectionContainer m_sections;
// ToDo End
private CcmObjectSelectionModel m_sectionSel;
private CcmObjectSelectionModel m_typeSel;
@@ -134,7 +138,8 @@ public class TasksPanel extends CMSContainer {
* @pre maxRows != null
*
*/
- public TasksPanel(int maxRows, CcmObjectSelectionModel typeModel,
+ public TasksPanel(int maxRows,
+ CcmObjectSelectionModel typeModel,
CcmObjectSelectionModel sectionModel) {
super();
@@ -192,14 +197,13 @@ public class TasksPanel extends CMSContainer {
// m_selector = new CreationSelector(m_typeSel, m_folderSel);
// m_creationPane.add(m_selector);
//ToDo End
-
m_creationPane.setClassAttr("itemCreationPane");
add(m_creationPane);
// The section list UIx
//ToDo
-// m_sections = new ContentSectionContainer(m_typeSel, m_sectionSel);
-// add(m_sections);
+ m_sections = new ContentSectionContainer(m_typeSel, m_sectionSel);
+ add(m_sections);
//ToDo End
// When a new type is selected, show the creation UI.
// When the selection is cleared, return to section list
@@ -444,8 +448,6 @@ public class TasksPanel extends CMSContainer {
// query.addEqualsFilter("isLocked", "f");
// } // else show all
// }
-
-
// private static class RootFolderSelectionModel
// extends FolderSelectionModel {
//
@@ -474,7 +476,6 @@ public class TasksPanel extends CMSContainer {
//
// }
//ToDo End
-
/**
*
*/
@@ -512,9 +513,8 @@ public class TasksPanel extends CMSContainer {
//
// return query;
// }
-
public int size(PageState ps) {
- return ((Long) m_taskCount.get(ps)).intValue();
+ return ((Integer)m_taskCount.get(ps)).intValue();
}
private RequestLocal m_taskCount = new RequestLocal() {
@@ -523,7 +523,7 @@ public class TasksPanel extends CMSContainer {
public Object initialValue(PageState state) {
// DataQuery query = makeQuery(state);
// return new Long(query.size());
-return null;
+ return 0;
}
};
@@ -536,7 +536,6 @@ return null;
exportAttributes(content);
// DataQuery query = makeQuery(state);
-
String lockFilterType = getLockFilterType(state);
content.addAttribute("lockFilterType", lockFilterType);
@@ -664,18 +663,18 @@ return null;
// String.valueOf(
// ContentItemPage.AUTHORING_TAB));
// }
- }
+ }
- // m_actionLabel.generateXML(state, content);
- String[][] sortableHeaders = {{SORT_TITLE,
- "cms.ui.workflow.task.item_title"},
- {SORT_ACTION, "cms.ui.action"},
- {SORT_DATE, "cms.ui.tasks_due_date"},
- {SORT_STATUS,
- "cms.ui.tasks_status_no_colon"},
- {SORT_USER,
- "cms.ui.workflow.task.locking_user"},
- {SORT_WORKFLOW, "cms.ui.workflow"}};
+ // m_actionLabel.generateXML(state, content);
+ String[][] sortableHeaders = {{SORT_TITLE,
+ "cms.ui.workflow.task.item_title"},
+ {SORT_ACTION, "cms.ui.action"},
+ {SORT_DATE, "cms.ui.tasks_due_date"},
+ {SORT_STATUS,
+ "cms.ui.tasks_status_no_colon"},
+ {SORT_USER,
+ "cms.ui.workflow.task.locking_user"},
+ {SORT_WORKFLOW, "cms.ui.workflow"}};
// for (int i = 0; i < sortableHeaders.length; i++) {
// String header = sortableHeaders[i][0];
// String labelKey = sortableHeaders[i][1];
@@ -705,14 +704,15 @@ return null;
// link.generateXML(state, content);
// state.clearControlEvent();
// }
- }
- @Override
- public void respond(PageState state) throws ServletException {
- String key = state.getControlEventName();
- String value = state.getControlEventValue();
- if (TASK_ACTION.equals(key)) {
- BigDecimal itemID = new BigDecimal(value);
+ }
+
+ @Override
+ public void respond(PageState state) throws ServletException {
+ String key = state.getControlEventName();
+ String value = state.getControlEventValue();
+ if (TASK_ACTION.equals(key)) {
+ BigDecimal itemID = new BigDecimal(value);
//
// try {
// ContentItem item = new ContentItem(itemID);
@@ -751,7 +751,7 @@ return null;
// throw new ServletException("Unknown content ID" + itemID);
// }
// } else
- if (SORT_UP.equals(key) || SORT_DOWN.equals(key)) {
+ if (SORT_UP.equals(key) || SORT_DOWN.equals(key)) {
state.setValue(m_sortTypeParam, value);
if (SORT_DOWN.equals(key)) {
state.setValue(m_sortDirectionParam, SORT_DOWN);
@@ -776,5 +776,4 @@ return null;
// return PermissionService.getFilterQuery(factory, "itemID", privilege,
// partyOID);
// }
-
}
diff --git a/ccm-cms/src/main/java/org/librecms/Cms.java b/ccm-cms/src/main/java/org/librecms/Cms.java
index 9d5e0212c..c58cc31cf 100644
--- a/ccm-cms/src/main/java/org/librecms/Cms.java
+++ b/ccm-cms/src/main/java/org/librecms/Cms.java
@@ -3,6 +3,10 @@
*/
package org.librecms;
+import com.arsdigita.cms.ContentCenterAppCreator;
+import com.arsdigita.cms.ContentCenterServlet;
+import com.arsdigita.cms.ContentCenterSetup;
+
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.core.CoreConstants;
@@ -14,6 +18,7 @@ import org.libreccm.modules.RequiredModule;
import org.libreccm.modules.ShutdownEvent;
import org.libreccm.modules.UnInstallEvent;
import org.libreccm.web.ApplicationType;
+import org.libreccm.web.CcmApplication;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionCreator;
import org.librecms.contentsection.ContentSectionSetup;
@@ -29,6 +34,13 @@ import java.util.Properties;
@RequiredModule(module = org.libreccm.core.CcmCore.class)
},
applicationTypes = {
+ @ApplicationType(
+ name = CmsConstants.CONTENT_CENTER_APP_TYPE,
+ applicationClass = CcmApplication.class,
+ descBundle = CmsConstants.CONTENT_CENTER_DESC_BUNDLE,
+ creator = ContentCenterAppCreator.class,
+ servlet = ContentCenterServlet.class
+ ),
@ApplicationType(
name = CmsConstants.CONTENT_SECTION_APP_TYPE,
applicationClass = ContentSection.class,
@@ -66,6 +78,12 @@ public class Cms implements CcmModule {
ex);
}
+ LOGGER.info("Setting content center...");
+ final ContentCenterSetup contentCenterSetup = new ContentCenterSetup(
+ event);
+ contentCenterSetup.setup();
+
+ LOGGER.info("Setting up content sections...");
final ContentSectionSetup contentSectionSetup = new ContentSectionSetup(
event);
contentSectionSetup.setup();
diff --git a/ccm-cms/src/main/java/org/librecms/CmsConstants.java b/ccm-cms/src/main/java/org/librecms/CmsConstants.java
index e174cbd44..cfa4bd3de 100644
--- a/ccm-cms/src/main/java/org/librecms/CmsConstants.java
+++ b/ccm-cms/src/main/java/org/librecms/CmsConstants.java
@@ -32,6 +32,7 @@ public class CmsConstants {
public static final String CONTENT_CENTER_APP_TYPE = "com.arsdigita.cms.ContentCenter";
public static final String CONTENT_CENTER_URL = "/content-center/";
+ public static final String CONTENT_CENTER_DESC_BUNDLE = "org.librecms.contentcenter.ContentCenterResources";
public static final String CONTENT_SECTION_APP_TYPE
= "org.librecms.contentsection.ContentSection";
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java
index 9d9d6a26d..9e82d0f09 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemManager.java
@@ -19,11 +19,14 @@
package org.librecms.contentsection;
import org.libreccm.categorization.Category;
+import org.libreccm.workflow.WorkflowTemplate;
+import org.librecms.lifecycle.LifecycleDefinition;
import java.util.List;
import java.util.Optional;
import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
/**
*
@@ -32,10 +35,88 @@ import javax.enterprise.context.RequestScoped;
@RequestScoped
public class ContentItemManager {
+ @Inject
+ private ContentItemRepository contentItemRepo;
+
+ /**
+ * Creates a new content item in the provided content section and folder
+ * with the default lifecycle and workflow.
+ *
+ * The folder must be a subfolder of the
+ * {@link ContentSection#rootDocumentsFolder} of the provided content
+ * section. Otherwise an {@link IllegalArgumentException} is thrown.
+ *
+ * @param The type of the content item.
+ * @param name The name (URL stub) of the new content item.
+ * @param section The content section in which the item is generated.
+ * @param folder The folder in which in the item is stored.
+ * @param type The type of the new content item.
+ *
+ * @return The new content item.
+ */
+ public T createContentItem(
+ final String name,
+ final ContentSection section,
+ final Category folder,
+ final Class type) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Creates a new content item in the provided content section and folder
+ * with the provided lifecycle and workflow.
+ *
+ * The folder must be a subfolder of the
+ * {@link ContentSection#rootDocumentsFolder} of the provided content
+ * section. Otherwise an {@link IllegalArgumentException} is thrown.
+ *
+ * Likewise the provided {@link LifecycleDefinition} and
+ * {@link WorkflowTemplate} must be defined in the provided content section.
+ * Otherwise an {@link IllegalArgumentException} is thrown.
+ *
+ * @param The type of the content item.
+ * @param name The name (URL stub) of the new content item.
+ * @param section The content section in which the item is
+ * generated.
+ * @param folder The folder in which in the item is stored.
+ * @param workflowTemplate
+ * @param lifecycleDefinition
+ * @param type The type of the new content item.
+ *
+ * @return The new content item.
+ */
+ public T createContentItem(
+ final String name,
+ final ContentSection section,
+ final Category folder,
+ final WorkflowTemplate workflowTemplate,
+ final LifecycleDefinition lifecycleDefinition,
+ final Class type) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Moves a content item to another folder in the same content section. This
+ * only moves the draft version of the item. The live version is moved after
+ * a the item is republished.
+ *
+ * @param item The item to move.
+ * @param targetFolder The folder to which the item is moved.
+ */
public void move(final ContentItem item, final Category targetFolder) {
throw new UnsupportedOperationException();
}
+ /**
+ * Creates an copy of the draft version of the item in the provided
+ * {@code targetFolder}.
+ *
+ * @param item The item to copy.
+ * @param targetFolder The folder in which the copy is created. If the
+ * target folder is the same folder as the folder of the
+ * original item an index is appended to the name of the
+ * item.
+ */
public void copy(final ContentItem item, final Category targetFolder) {
throw new UnsupportedOperationException();
}
@@ -64,21 +145,26 @@ public class ContentItemManager {
/**
* Determines if a content item has a live version.
*
- * @param item The item
+ * @param item The item
+ *
* @return {@code true} if the content item has a live version,
* {@code false} if not.
*/
public boolean isLive(final ContentItem item) {
throw new UnsupportedOperationException();
}
-
+
/**
* Retrieves the live version of the provided content item if any.
- *
- * @param
- * @param item
- * @param type
- * @return
+ *
+ * @param Type of the content item.
+ * @param item The item of which the live version should be retrieved.
+ * @param type Type of the content item.
+ *
+ * @return The live version of an item. If the item provided is already the
+ * live version the provided item is returned, otherwise the live
+ * version is returned. If there is no live version an empty
+ * {@link Optional} is returned.
*/
public Optional getLiveVersion(
final ContentItem item,
@@ -86,11 +172,37 @@ public class ContentItemManager {
throw new UnsupportedOperationException();
}
- public List getPendingVersions() {
+ /**
+ * Retrieves the pending versions of an item if there are any.
+ *
+ * @param Type of the content item to retrieve.
+ * @param item The item of which the pending versions are retrieved.
+ * @param type Type of the content item to retrieve.
+ *
+ * @return A list of the pending versions of the item.
+ */
+ public List getPendingVersions(
+ final ContentItem item,
+ final Class type) {
throw new UnsupportedOperationException();
}
-
- public T getDraftVersion(final ContentItem item) {
+
+ /**
+ * Retrieves the draft version
+ *
+ * @param Type of the item.
+ * @param item The item of which the draft version is retrieved.
+ * @param type Type of the item.
+ *
+ * @return The draft version of the provided content item. If the provided
+ * item is the draft version the provided item is simply returned.
+ * Otherwise the draft version is retrieved from the database and is
+ * returned. Each content item has a draft version (otherwise
+ * something is seriously wrong with the database) this method will
+ * never return {@code null}.
+ */
+ public T getDraftVersion(final ContentItem item,
+ final Class type) {
throw new UnsupportedOperationException();
}
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java
index c6e357b52..1095afd96 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentItemRepository.java
@@ -24,21 +24,23 @@ import org.libreccm.core.CcmObject;
import org.libreccm.core.CcmObjectRepository;
import java.util.List;
+import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
/**
+ * Repository for content items.
*
* @author Jens Pelzetter
*/
@RequestScoped
-public class ContentItemRepository
- extends AbstractAuditedEntityRepository{
+public class ContentItemRepository
+ extends AbstractAuditedEntityRepository {
@Inject
- private CcmObjectRepository ccmObjectRepo;
-
+ private CcmObjectRepository ccmObjectRepo;
+
@Override
public Long getEntityId(final ContentItem item) {
return item.getObjectId();
@@ -53,21 +55,48 @@ public class ContentItemRepository
public boolean isNew(final ContentItem item) {
return ccmObjectRepo.isNew(item);
}
-
- public ContentItem findById(final long itemId) {
+
+ /**
+ * Finds a content item by is id.
+ *
+ * @param itemId The id of item to retrieve.
+ *
+ * @return The content item identified by the provided {@code itemId} or
+ * nothing if there is such content item.
+ */
+ public Optional findById(final long itemId) {
final CcmObject result = ccmObjectRepo.findObjectById(itemId);
if (result instanceof ContentItem) {
- return (ContentItem) result;
+ return Optional.of((ContentItem) result);
} else {
- return null;
+ return Optional.empty();
}
}
-
- public T findById(final long itemId,
- final Class type) {
+
+ /**
+ * Finds a content item by its ID and ensures that is a the requested type.
+ *
+ * @param The type of the content item.
+ * @param itemId The id of item to retrieve.
+ * @param type The type of the content item.
+ *
+ * @return The content item identified by the provided id or an empty
+ * {@link Optional} if there is no such item or if it is not of the
+ * requested type.
+ */
+ public Optional findById(final long itemId,
+ final Class type) {
throw new UnsupportedOperationException();
}
-
+
+ /**
+ * Finds a content item by is UUID.
+ *
+ * @param uuid The id of item to retrieve.
+ *
+ * @return The content item identified by the provided {@code uuid} or
+ * nothing if there is such content item.
+ */
public ContentItem findByUuid(final String uuid) {
final CcmObject result = ccmObjectRepo.findObjectByUuid(uuid);
if (result instanceof ContentItem) {
@@ -76,18 +105,42 @@ public class ContentItemRepository
return null;
}
}
-
+
+ /**
+ * Finds a content item by its UUID and ensures that is a the requested type.
+ *
+ * @param The type of the content item.
+ * @param uuid The UUID of item to retrieve.
+ * @param type The type of the content item.
+ *
+ * @return The content item identified by the provided UUID or an empty
+ * {@link Optional} if there is no such item or if it is not of the
+ * requested type.
+ */
public T findByUuid(final String uuid,
final Class type) {
throw new UnsupportedOperationException();
}
-
+
+ /**
+ * Finds all content items of a specific type.
+ *
+ * @param The type of the items.
+ * @param type The type of the items.
+ * @return A list of all content items of the requested type.
+ */
public List findByType(final Class type) {
throw new UnsupportedOperationException();
}
-
+
+ /**
+ * Retrieves all content items in the provided folder.
+ *
+ * @param folder The folder.
+ * @return A list of all items in the provided folder.
+ */
public List findByFolder(final Category folder) {
throw new UnsupportedOperationException();
}
-
+
}
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java
index f197f6d9e..863be218d 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentSectionSetup.java
@@ -26,7 +26,6 @@ import org.libreccm.security.Role;
import org.libreccm.web.AbstractCcmApplicationSetup;
import org.librecms.CmsConstants;
-import java.util.Locale;
import java.util.UUID;
import static org.librecms.CmsConstants.*;
@@ -54,8 +53,13 @@ public class ContentSectionSetup extends AbstractCcmApplicationSetup {
if (getIntegrationProps().containsKey(INITIAL_CONTENT_SECTIONS)) {
sectionNames = getIntegrationProps().getProperty(
INITIAL_CONTENT_SECTIONS);
+ LOGGER.info(
+ "Found names for initial content sections in integration "
+ + "properties: {}", sectionNames);
} else {
sectionNames = "info";
+ LOGGER.info("No initial content sections definied integration "
+ + "properties, using default: {}", sectionNames);
}
for (final String contentSectionName : sectionNames.split(",")) {
@@ -64,13 +68,27 @@ public class ContentSectionSetup extends AbstractCcmApplicationSetup {
}
private void createContentSection(final String sectionName) {
+ LOGGER.debug("Creating content section with section name \"{}\"...",
+ sectionName);
final ContentSection section = new ContentSection();
section.setUuid(UUID.randomUUID().toString());
section.setApplicationType(CmsConstants.CONTENT_SECTION_APP_TYPE);
- section.setPrimaryUrl(sectionName);
+ section.setPrimaryUrl(String.format("/%s/", sectionName));
section.setDisplayName(sectionName);
section.setLabel(sectionName);
+ LOGGER.debug("New content section properties: "
+ + "uuid = {}; "
+ + "applicationType = \"{}\"; "
+ + "primaryUrl = \"{}\"; "
+ + "displayName = \"{}\"; "
+ + "label = \"{}\"",
+ section.getUuid(),
+ section.getApplicationType(),
+ section.getPrimaryUrl(),
+ section.getDisplayName(),
+ section.getLabel());
+
final Category rootFolder = new Category();
rootFolder.setUuid(UUID.randomUUID().toString());
rootFolder.setUniqueId(rootFolder.getUuid());
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/ContentType.java b/ccm-cms/src/main/java/org/librecms/contentsection/ContentType.java
index 7681d0ff5..d472c2669 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/ContentType.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/ContentType.java
@@ -25,8 +25,8 @@ import static org.librecms.CmsConstants.*;
import org.libreccm.core.CcmObject;
import org.libreccm.l10n.LocalizedString;
-import org.libreccm.workflow.Workflow;
-import org.librecms.lifecycle.Lifecycle;
+import org.libreccm.workflow.WorkflowTemplate;
+import org.librecms.lifecycle.LifecycleDefinition;
import java.io.Serializable;
import java.util.Objects;
@@ -98,11 +98,11 @@ public class ContentType extends CcmObject implements Serializable {
@ManyToOne
@JoinColumn(name = "DEFAULT_LIFECYCLE_ID")
- private Lifecycle defaultLifecycle;
+ private LifecycleDefinition defaultLifecycle;
@ManyToOne
@JoinColumn(name = "DEFAULT_WORKFLOW")
- private Workflow defaultWorkflow;
+ private WorkflowTemplate defaultWorkflow;
public String getContentItemClass() {
return contentItemClass;
@@ -160,19 +160,19 @@ public class ContentType extends CcmObject implements Serializable {
this.mode = mode;
}
- public Lifecycle getDefaultLifecycle() {
+ public LifecycleDefinition getDefaultLifecycle() {
return defaultLifecycle;
}
- protected void setDefaultLifecycle(final Lifecycle defaultLifecycle) {
+ protected void setDefaultLifecycle(final LifecycleDefinition defaultLifecycle) {
this.defaultLifecycle = defaultLifecycle;
}
- public Workflow getDefaultWorkflow() {
+ public WorkflowTemplate getDefaultWorkflow() {
return defaultWorkflow;
}
- protected void setDefaultWorkflow(final Workflow defaultWorkflow) {
+ protected void setDefaultWorkflow(final WorkflowTemplate defaultWorkflow) {
this.defaultWorkflow = defaultWorkflow;
}
diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_1__fix_content_types_constraints.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_1__fix_content_types_constraints.sql
new file mode 100644
index 000000000..1f9798ba6
--- /dev/null
+++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/h2/V7_0_0_1__fix_content_types_constraints.sql
@@ -0,0 +1,15 @@
+alter table CCM_CMS.CONTENT_TYPES
+ drop constraint FKoqvcvktnvt4ncx5k6daqat4u8;
+
+alter table CCM_CMS.CONTENT_TYPES
+ drop constraint FKpgeccqsr50xwb268ypmfx0r66;
+
+alter table CCM_CMS.CONTENT_TYPES
+ add constraint FK8s83we1tuh9r3j57dyos69wfa
+ foreign key (DEFAULT_LIFECYCLE_ID)
+ references CCM_CMS.LIFECYLE_DEFINITIONS;
+
+alter table CCM_CMS.CONTENT_TYPES
+ add constraint FKhnu9oikw8rpf22lt5fmk41t7k
+ foreign key (DEFAULT_WORKFLOW)
+ references CCM_CORE.WORKFLOW_TEMPLATES;
diff --git a/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_1__fix_content_types_constraints.sql b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_1__fix_content_types_constraints.sql
new file mode 100644
index 000000000..de4e04d5e
--- /dev/null
+++ b/ccm-cms/src/main/resources/db/migrations/org/librecms/ccm_cms/pgsql/V7_0_0_1__fix_content_types_constraints.sql
@@ -0,0 +1,16 @@
+alter table CCM_CMS.CONTENT_TYPES
+ drop constraint FKoqvcvktnvt4ncx5k6daqat4u8;
+
+alter table CCM_CMS.CONTENT_TYPES
+ drop constraint FKpgeccqsr50xwb268ypmfx0r66;
+
+alter table CCM_CMS.CONTENT_TYPES
+ add constraint FK8s83we1tuh9r3j57dyos69wfa
+ foreign key (DEFAULT_LIFECYCLE_ID)
+ references CCM_CMS.LIFECYLE_DEFINITIONS;
+
+alter table CCM_CMS.CONTENT_TYPES
+ add constraint FKhnu9oikw8rpf22lt5fmk41t7k
+ foreign key (DEFAULT_WORKFLOW)
+ references CCM_CORE.WORKFLOW_TEMPLATES;
+
diff --git a/ccm-cms/src/main/resources/org/librecms/contentcenter/ContentCenterResources.properties b/ccm-cms/src/main/resources/org/librecms/contentcenter/ContentCenterResources.properties
new file mode 100644
index 000000000..940caea92
--- /dev/null
+++ b/ccm-cms/src/main/resources/org/librecms/contentcenter/ContentCenterResources.properties
@@ -0,0 +1,3 @@
+
+application_title=Content Center
+application_desc=Content Center application
diff --git a/ccm-cms/src/main/resources/org/librecms/contentcenter/ContentCenterResources_de.properties b/ccm-cms/src/main/resources/org/librecms/contentcenter/ContentCenterResources_de.properties
new file mode 100644
index 000000000..940caea92
--- /dev/null
+++ b/ccm-cms/src/main/resources/org/librecms/contentcenter/ContentCenterResources_de.properties
@@ -0,0 +1,3 @@
+
+application_title=Content Center
+application_desc=Content Center application
diff --git a/ccm-cms/src/test/java/org/librecms/contentsection/EqualsAndHashCodeTest.java b/ccm-cms/src/test/java/org/librecms/contentsection/EqualsAndHashCodeTest.java
index a03eb9a5e..e63457677 100644
--- a/ccm-cms/src/test/java/org/librecms/contentsection/EqualsAndHashCodeTest.java
+++ b/ccm-cms/src/test/java/org/librecms/contentsection/EqualsAndHashCodeTest.java
@@ -31,7 +31,9 @@ import org.libreccm.tests.categories.UnitTest;
import org.libreccm.testutils.EqualsVerifier;
import org.libreccm.web.CcmApplication;
import org.libreccm.workflow.Workflow;
+import org.libreccm.workflow.WorkflowTemplate;
import org.librecms.lifecycle.Lifecycle;
+import org.librecms.lifecycle.LifecycleDefinition;
import java.util.Arrays;
import java.util.Collection;
@@ -137,17 +139,17 @@ public class EqualsAndHashCodeTest extends EqualsVerifier {
final Resource resource2 = new Resource();
resource2.setDisplayName("Resource 2");
- final Lifecycle lifecycle1 = new Lifecycle();
- lifecycle1.setFinished(true);
+ final LifecycleDefinition lifecycleDef1 = new LifecycleDefinition();
+ lifecycleDef1.setDefinitionId(-100);
- final Lifecycle lifecycle2 = new Lifecycle();
- lifecycle2.setFinished(false);
+ final LifecycleDefinition lifecycleDef2 = new LifecycleDefinition();
+ lifecycleDef2.setDefinitionId(-110);
- final Workflow workflow1 = new Workflow();
- workflow1.setWorkflowId(-100);
+ final WorkflowTemplate workflowTemplate1 = new WorkflowTemplate();
+ workflowTemplate1.setWorkflowId(-200);
- final Workflow workflow2 = new Workflow();
- workflow2.setWorkflowId(-200);
+ final WorkflowTemplate workflowTemplate2 = new WorkflowTemplate();
+ workflowTemplate2.setWorkflowId(-210);
verifier
.withPrefabValues(ContentItem.class, item1, item2)
@@ -161,8 +163,12 @@ public class EqualsAndHashCodeTest extends EqualsVerifier {
.withPrefabValues(CcmApplication.class, application1, application2)
.withPrefabValues(Domain.class, domain1, domain2)
.withPrefabValues(Resource.class, resource1, resource2)
- .withPrefabValues(Lifecycle.class, lifecycle1, lifecycle2)
- .withPrefabValues(Workflow.class, workflow1, workflow2);
+ .withPrefabValues(LifecycleDefinition.class,
+ lifecycleDef1,
+ lifecycleDef2)
+ .withPrefabValues(WorkflowTemplate.class,
+ workflowTemplate1,
+ workflowTemplate2);
}
/**
diff --git a/ccm-core/pom.xml b/ccm-core/pom.xml
index 23c889459..1dde82744 100644
--- a/ccm-core/pom.xml
+++ b/ccm-core/pom.xml
@@ -186,6 +186,11 @@
h2
test
+
+
+ org.primefaces
+ primefaces
+
diff --git a/ccm-core/src/main/java/com/arsdigita/bebop/Embedded.java b/ccm-core/src/main/java/com/arsdigita/bebop/Embedded.java
new file mode 100644
index 000000000..83b6eaee6
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/bebop/Embedded.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 Peter Boy, University of Bremen. 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.bebop;
+
+import static com.arsdigita.bebop.Component.BEBOP_XML_NS;
+import com.arsdigita.bebop.event.PrintEvent;
+import com.arsdigita.bebop.event.PrintListener;
+import com.arsdigita.xml.Element;
+
+/**
+ * Injects arbitrary content as a String into the xml output. It is not for
+ * any semantic type of data und it is not localizable. Specifically it is
+ * meant for data as Javascript and alike.
+ *
+ * It generates some fixed string to be included in the XML output.
+ *
+ * It resembles the Label methods for String parameters and currently
+ * generates the same XML attributes in order to avoid any need to modify the
+ * themes.
+ *
+ * @author pb
+ */
+public class Embedded extends SimpleComponent {
+
+ private final String m_content;
+ /** The setting for output escaping affects how markup in the
+ * content is handled.
+ * - If output escaping is in effect (true), <b>example</b>
+ * will appear literally.
+ * - If output escaping is disabled, <b>example</b> appears as the
+ * String "example" in bold (i.e. retaining the markup.
+ * Default is false. */
+ private boolean m_escaping = false; // default for a primitive
+ private PrintListener m_printListener;
+
+ /**
+ * Default constructor creates a new Embedded with the empty
+ * content.
+ *
+ * @param content
+ */
+ public Embedded() {
+ m_content = "";
+ }
+
+ /**
+ * Constructor creates a new Embedded with the specified
+ * (fixed) content.
+ *
+ * @param content
+ */
+ public Embedded(String content) {
+ m_content = content;
+ }
+
+ /**
+ * Constructor creates a new Embedded with the specified
+ * content and output escaping turned on if escaping is
+ * true.
+ *
+ * The setting for output escaping affects how markup in the
+ * content is handled. For example: - If output escaping
+ * is in effect, <b>content</b> will appear literally.
- If
+ * output escaping is disabled, <b>content</b> appears as the String
+ * "context" in bold.
+ *
+ * @param content the content to inject into the output.
+ * @param escaping true if output escaping will be in effect;
+ * false if output escaping will be disabled
+ */
+ public Embedded(String content, boolean escaping) {
+ m_content = content;
+ m_escaping = escaping;
+ }
+
+ /**
+ * Generates the (J)DOM fragment for a embedded.
+ *
+ * <bebop:link href="..." type="..." %bebopAttr;/>
+ *
+ *
+ * @param state The current {@link PageState}.
+ * @param parent The XML element to attach the XML to.
+ */
+ @Override
+ public void generateXML(PageState state, Element parent) {
+
+ if (!isVisible(state)) {
+ return;
+ }
+
+ Embedded target = firePrintEvent(state);
+
+ Element content = parent.newChildElement("bebop:label", BEBOP_XML_NS);
+ target.exportAttributes(content);
+
+ if (!target.m_escaping) {
+ content.addAttribute("escape", "yes");
+ } else {
+ content.addAttribute("escape", "no");
+ }
+
+ content.setText(m_content);
+ }
+
+ /**
+ *
+ * @param state
+ * @return
+ */
+ protected Embedded firePrintEvent(PageState state) {
+ Embedded e = this;
+
+ if (m_printListener != null) {
+ try {
+ e = (Embedded) this.clone();
+ m_printListener.prepare(new PrintEvent(this, state, e));
+ } catch (CloneNotSupportedException nse) {
+ throw new RuntimeException(
+ "Couldn't clone Embedded for PrintListener. "
+ + "This probably indicates a serious programming error: "
+ + nse.getMessage());
+ }
+ }
+
+ return e;
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java b/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java
index 6a9f78610..0a30f6bcc 100644
--- a/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java
+++ b/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java
@@ -437,7 +437,7 @@ public class CCMDispatcherServlet extends BaseServlet {
m_typeURI = servletAnnotation.urlPatterns()[0];
}
} else {
- m_typeURI = "";
+ m_typeURI = appType.servletPath();
}
}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/AdminJsfApplicationCreator.java b/ccm-core/src/main/java/org/libreccm/admin/ui/AdminJsfApplicationCreator.java
new file mode 100644
index 000000000..398bcd20a
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/AdminJsfApplicationCreator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import org.libreccm.web.ApplicationCreator;
+import org.libreccm.web.ApplicationRepository;
+import org.libreccm.web.ApplicationType;
+import org.libreccm.web.CcmApplication;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class AdminJsfApplicationCreator implements ApplicationCreator{
+
+ @Inject
+ private ApplicationRepository appRepo;
+
+ @Override
+ public CcmApplication createInstance(final String primaryUrl,
+ final ApplicationType type) {
+ if ("org.libreccm.ui.admin.AdminFaces".equals(primaryUrl)) {
+ throw new IllegalArgumentException(
+ "CCM Admin Faces is a singleton application"
+ + "which is mounted at /admin-jsf");
+ }
+
+ return appRepo.retrieveApplicationForPath(primaryUrl);
+ }
+
+
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/admin/ui/AdminJsfApplicationSetup.java b/ccm-core/src/main/java/org/libreccm/admin/ui/AdminJsfApplicationSetup.java
new file mode 100644
index 000000000..1b47adb49
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/admin/ui/AdminJsfApplicationSetup.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.admin.ui;
+
+import org.libreccm.modules.InstallEvent;
+import org.libreccm.web.AbstractCcmApplicationSetup;
+import org.libreccm.web.CcmApplication;
+
+import java.util.UUID;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class AdminJsfApplicationSetup extends AbstractCcmApplicationSetup {
+
+ public static final String ADMIN_APP_NAME = "CcmAdminJsf";
+
+ public AdminJsfApplicationSetup(final InstallEvent event) {
+ super(event);
+ }
+
+ @Override
+ public void setup() {
+ final CcmApplication admin = new CcmApplication();
+ admin.setUuid(UUID.randomUUID().toString());
+ admin.setApplicationType("org.libreccm.ui.admin.AdminFaces");
+ admin.setPrimaryUrl("/admin-jsf/");
+
+ getEntityManager().persist(admin);
+ }
+
+
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java b/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java
index 0846703f0..80ffeb184 100644
--- a/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java
+++ b/ccm-core/src/main/java/org/libreccm/auditing/AbstractAuditedEntityRepository.java
@@ -30,7 +30,7 @@ import java.util.List;
/**
*
* @author Jens Pelzetter
- * @author Tobias Osmers
* @param Primary key of the entity.
* @param Type of the entity
*/
@@ -42,6 +42,7 @@ public abstract class AbstractAuditedEntityRepository
public abstract K getEntityId(final T entity);
+ @SuppressWarnings("unchecked")
public T retrieveRevisionOfEntity(final T entity, final Number revision) {
final AuditQuery query = auditReader.createQuery()
.forEntitiesAtRevision(getEntityClass(), revision);
diff --git a/ccm-core/src/main/java/org/libreccm/core/CcmCore.java b/ccm-core/src/main/java/org/libreccm/core/CcmCore.java
index 41c8d0006..071e183df 100644
--- a/ccm-core/src/main/java/org/libreccm/core/CcmCore.java
+++ b/ccm-core/src/main/java/org/libreccm/core/CcmCore.java
@@ -35,6 +35,8 @@ import javax.persistence.EntityManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.libreccm.admin.ui.AdminJsfApplicationCreator;
+import org.libreccm.admin.ui.AdminJsfApplicationSetup;
import org.libreccm.modules.CcmModule;
import org.libreccm.modules.InitEvent;
@@ -60,7 +62,12 @@ import org.libreccm.web.ApplicationType;
descBundle = "com.arsdigita.ui.admin.AdminResources",
singleton = true,
creator = AdminApplicationCreator.class,
- servlet = AdminServlet.class)},
+ servlet = AdminServlet.class),
+ @ApplicationType(name = "org.libreccm.ui.admin.AdminFaces",
+ descBundle = "com.arsdigita.ui.admin.AdminResources",
+ singleton = true,
+ creator = AdminJsfApplicationCreator.class,
+ servletPath = "/admin-jsf/admin.xhtml")},
configurations = {
com.arsdigita.bebop.BebopConfig.class,
com.arsdigita.dispatcher.DispatcherConfig.class,
@@ -94,6 +101,11 @@ public class CcmCore implements CcmModule {
= new AdminApplicationSetup(event);
adminSetup.setup();
+ LOGGER.info("Setting up admin-jsf application (/ccm/admin-jsf/)...");
+ final AdminJsfApplicationSetup adminJsfSetup
+ = new AdminJsfApplicationSetup(event);
+ adminJsfSetup.setup();
+
LOGGER.info("Setting up login application...");
final LoginApplicationSetup loginSetup
= new LoginApplicationSetup(event);
diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/AuthorizationListener.java b/ccm-core/src/main/java/org/libreccm/ui/admin/AuthorizationListener.java
new file mode 100644
index 000000000..b603db11b
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/ui/admin/AuthorizationListener.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.ui.admin;
+
+import com.arsdigita.web.BaseServlet;
+import com.arsdigita.web.CCMDispatcherServlet;
+import com.arsdigita.web.WebConfig;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.net.URLCodec;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.enterprise.context.RequestScoped;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.event.ComponentSystemEvent;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.shiro.subject.Subject;
+import org.libreccm.configuration.ConfigurationManager;
+import org.libreccm.security.PermissionChecker;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named
+public class AuthorizationListener {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ AuthorizationListener.class);
+
+ @Inject
+ private Subject subject;
+
+ @Inject
+ private HttpServletRequest request;
+
+// @Inject
+// private HttpServletResponse response;
+ @Inject
+ private PermissionChecker permissionChecker;
+
+ @Inject
+ private ConfigurationManager confManager;
+
+ public void isPermitted(final ComponentSystemEvent event) {
+ if (!subject.isAuthenticated()) {
+ redirectToLogin();
+ return;
+ }
+
+ final String requiredPrivilege = (String) event.getComponent().
+ getAttributes().get("requiredPrivilege");
+
+ if (!permissionChecker.isPermitted(requiredPrivilege)) {
+ try {
+ final FacesContext facesContext = FacesContext.
+ getCurrentInstance();
+ final ExternalContext externalContext = facesContext.
+ getExternalContext();
+ final HttpServletResponse response
+ = (HttpServletResponse) externalContext
+ .getResponse();
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ } catch (IOException ex) {
+ LOGGER.error("Failed to send FORBIDDEN error to client.", ex);
+ throw new RuntimeException(
+ "Failed to send FORBIDDEN error to client", ex);
+ }
+ }
+
+ }
+
+ private void redirectToLogin() {
+ try {
+ final FacesContext facesContext = FacesContext.getCurrentInstance();
+ final ExternalContext externalContext = facesContext.
+ getExternalContext();
+ final HttpServletResponse response
+ = (HttpServletResponse) externalContext
+ .getResponse();
+ final WebConfig webConfig = confManager.findConfiguration(
+ WebConfig.class);
+ final URLCodec urlCodec = new URLCodec("utf-8");
+ response.sendRedirect(new URI(String.format(
+ "%s://%s:%d%s%s/register/?return_url=%s",
+ request.getScheme(),
+ request.getServerName(),
+ request.getLocalPort(),
+ CCMDispatcherServlet.getContextPath(),
+ webConfig.getDispatcherServletPath(),
+ urlCodec.encode(request.getAttribute(
+ BaseServlet.REQUEST_URL_ATTRIBUTE).toString())))
+ .toString());
+ } catch (IOException |
+ URISyntaxException |
+ EncoderException ex) {
+ LOGGER.error("Failed to redirect to login.", ex);
+ throw new RuntimeException("Failed to redirect to login.", ex);
+ }
+ }
+
+}
diff --git a/ccm-cms/src/main/java/org/librecms/ContentType.java b/ccm-core/src/main/java/org/libreccm/ui/admin/ConfProperty.java
similarity index 70%
rename from ccm-cms/src/main/java/org/librecms/ContentType.java
rename to ccm-core/src/main/java/org/libreccm/ui/admin/ConfProperty.java
index 265b88f7d..745e77962 100644
--- a/ccm-cms/src/main/java/org/librecms/ContentType.java
+++ b/ccm-core/src/main/java/org/libreccm/ui/admin/ConfProperty.java
@@ -16,18 +16,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
-package org.librecms;
-
-import org.librecms.contentsection.ContentItem;
+package org.libreccm.ui.admin;
/**
- * Annotation providing several informations about a content type. A content
- * type is provided by a class extending the {@link ContentItem} class.
- *
+ *
* @author Jens Pelzetter
*/
-public @interface ContentType {
-
- //ToDo
-
+public class ConfProperty {
+
+ private final String name;
+ private final String value;
+
+ public ConfProperty(final String name, final String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
}
diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/ConfigurationController.java b/ccm-core/src/main/java/org/libreccm/ui/admin/ConfigurationController.java
new file mode 100644
index 000000000..0d88ac81b
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/ui/admin/ConfigurationController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.ui.admin;
+
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Named;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named
+public class ConfigurationController {
+
+ public List getSystemInformation() {
+ final Properties properties = new Properties();
+
+ try (final InputStream stream = getClass().getResourceAsStream(
+ "systeminformation.properties")) {
+ if (stream == null) {
+ properties.put("version", "");
+ properties.put("appname", "LibreCCM");
+ properties.put("apphomepage", "http://www.libreccm.org");
+ } else {
+ properties.load(stream);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ final List sysInfo = new ArrayList<>();
+ properties.stringPropertyNames().forEach(propName -> sysInfo.add(
+ new ConfProperty(propName,
+ properties.getProperty(propName))));
+ return sysInfo;
+ }
+
+ public List getJavaSystemProperties() {
+ final Properties systemProperties = System.getProperties();
+ final List javaSysProps = new ArrayList<>();
+ systemProperties.stringPropertyNames().forEach(propName -> javaSysProps
+ .add(new ConfProperty(propName, systemProperties.getProperty(
+ propName))));
+ return javaSysProps;
+ }
+
+ public List getXmlConfig() {
+ final List xmlProps = new ArrayList<>();
+
+ final ResourceBundle texts = ResourceBundle.getBundle(
+ "com.arsdigita.ui.admin.AdminResources");
+
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.xml_transformer_factory"),
+ TransformerFactory.newInstance().getClass().getName()));
+ try {
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.xml_transformer"),
+ TransformerFactory.newInstance().newTransformer().getClass()
+ .getName()));
+ } catch (TransformerConfigurationException ex) {
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.xml_transformer"), "???"));
+ }
+
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.xml_document_builder_factory"),
+ DocumentBuilderFactory.newInstance().getClass().getName()));
+
+ try {
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.xml_document_builder"),
+ DocumentBuilderFactory.newInstance().newDocumentBuilder()
+ .getClass().getName()));
+ } catch (ParserConfigurationException ex) {
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.xml_document_builder"),
+ "???"));
+ }
+
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.sax_parser_factory"),
+ SAXParserFactory.newInstance().getClass().getName()));
+
+ try {
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.sax_parser"),
+ SAXParserFactory.newInstance().newSAXParser().getClass()
+ .getName()));
+ } catch (ParserConfigurationException | SAXException ex) {
+ xmlProps.add(new ConfProperty(
+ texts.getString("ui.admin.sysinfo.sax_parser"), "???"));
+ }
+
+ return xmlProps;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/UserContextController.java b/ccm-core/src/main/java/org/libreccm/ui/admin/UserContextController.java
new file mode 100644
index 000000000..8627e84ee
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/ui/admin/UserContextController.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 LibreCCM Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.ui.admin;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+import org.apache.shiro.subject.Subject;
+import org.libreccm.security.Shiro;
+import org.libreccm.security.User;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+@Named
+public class UserContextController {
+
+ @Inject
+ private Shiro shiro;
+
+ @Inject
+ private Subject subject;
+
+ public boolean isLoggedIn() {
+ return subject.isAuthenticated();
+ }
+
+ public String getCurrentUserName() {
+ final User user = shiro.getUser();
+
+ if (user == null) {
+ return "";
+ } else {
+ return String.format("%s %s",
+ user.getGivenName(),
+ user.getFamilyName());
+ }
+ }
+
+ public void changePassword() {
+
+ }
+
+ public void logout() {
+ subject.logout();
+ }
+
+}
diff --git a/ccm-core/src/main/resources/META-INF/resources/admin-jsf/admin.xhtml b/ccm-core/src/main/resources/META-INF/resources/admin-jsf/admin.xhtml
new file mode 100644
index 000000000..7d3112e73
--- /dev/null
+++ b/ccm-core/src/main/resources/META-INF/resources/admin-jsf/admin.xhtml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+ LibreCCM Admin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ccm-core/src/main/resources/META-INF/resources/admin-jsf/header.css b/ccm-core/src/main/resources/META-INF/resources/admin-jsf/header.css
new file mode 100644
index 000000000..bf7075fe4
--- /dev/null
+++ b/ccm-core/src/main/resources/META-INF/resources/admin-jsf/header.css
@@ -0,0 +1,33 @@
+body {
+ margin: 0;
+}
+
+div#header {
+ background-color: #56a1bd;
+
+ background-image: -moz-linear-gradient(top, #56a1bd 5%, #024C68 95%);
+ background-image: -webkit-linear-gradient(top, #56a1bd 5%, #024C68 95%);
+ background-image: linear-gradient(top, #56a1bd 5%, #024C68 95%);
+
+ display: flex;
+
+ height: 70px;
+
+ padding: 0 10px;
+
+ position: relative;
+}
+
+#logo, #user-widget {
+ flex: 1;
+}
+
+#user-widget {
+ position: absolute;
+ top: 15px;
+ right: 10px;
+}
+
+.ui-widget, .ui-widget .ui-widget {
+ font-size: 90% !important;
+}
\ No newline at end of file
diff --git a/ccm-core/src/main/resources/META-INF/resources/admin-jsf/libreccm.png b/ccm-core/src/main/resources/META-INF/resources/admin-jsf/libreccm.png
new file mode 100644
index 000000000..93fd7a317
Binary files /dev/null and b/ccm-core/src/main/resources/META-INF/resources/admin-jsf/libreccm.png differ
diff --git a/ccm-core/src/main/resources/META-INF/resources/test.xhtml b/ccm-core/src/main/resources/META-INF/resources/test.xhtml
new file mode 100644
index 000000000..b64fcda97
--- /dev/null
+++ b/ccm-core/src/main/resources/META-INF/resources/test.xhtml
@@ -0,0 +1,16 @@
+
+
+
+
+ JSF Test
+
+
+
+
+
+
+
+
+
+
diff --git a/ccm-core/src/main/resources/META-INF/test.xhtml b/ccm-core/src/main/resources/META-INF/test.xhtml
new file mode 100644
index 000000000..cdfa0c7ba
--- /dev/null
+++ b/ccm-core/src/main/resources/META-INF/test.xhtml
@@ -0,0 +1,16 @@
+
+
+
+
+ JSF Test
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 3dacd7b2a..788bc74c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -291,6 +291,12 @@
1.2.5
+
+
+ org.primefaces
+ primefaces
+ 6.0
+