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 +