diff --git a/ccm-core/src/main/java/org/libreccm/sites/Site.java b/ccm-core/src/main/java/org/libreccm/sites/Site.java index e0559bdd1..7799af838 100644 --- a/ccm-core/src/main/java/org/libreccm/sites/Site.java +++ b/ccm-core/src/main/java/org/libreccm/sites/Site.java @@ -44,6 +44,10 @@ import javax.persistence.Table; @Entity @Table(name = "SITES", schema = DB_SCHEMA) @NamedQueries({ + @NamedQuery( + name = "Site.findByUuid", + query = "SELECT s FROM Site s WHERE s.uuid = :uuid" + ), @NamedQuery( name = "Site.findByDomain", query = "SELECT s FROM Site s " diff --git a/ccm-core/src/main/java/org/libreccm/sites/SiteAwareApplication.java b/ccm-core/src/main/java/org/libreccm/sites/SiteAwareApplication.java index 0cb9e6f6c..d91544d4d 100644 --- a/ccm-core/src/main/java/org/libreccm/sites/SiteAwareApplication.java +++ b/ccm-core/src/main/java/org/libreccm/sites/SiteAwareApplication.java @@ -67,7 +67,7 @@ public class SiteAwareApplication extends CcmApplication { @Override public int hashCode() { - int hash = 3; + int hash = super.hashCode(); if (site != null) { hash = 59 * hash + Objects.hashCode(site.getDomainOfSite()); hash = 59 * hash + Objects.hashCode(site.isDefaultSite()); @@ -84,6 +84,9 @@ public class SiteAwareApplication extends CcmApplication { if (obj == null) { return false; } + if (!super.equals(obj)) { + return false; + } if (!(obj instanceof SiteAwareApplication)) { return false; } diff --git a/ccm-core/src/main/java/org/libreccm/sites/SiteRepository.java b/ccm-core/src/main/java/org/libreccm/sites/SiteRepository.java index 81389faa3..fb646c3e5 100644 --- a/ccm-core/src/main/java/org/libreccm/sites/SiteRepository.java +++ b/ccm-core/src/main/java/org/libreccm/sites/SiteRepository.java @@ -41,6 +41,28 @@ public class SiteRepository extends AbstractEntityRepository { private static final long serialVersionUID = 3120528987720524155L; + /** + * Retrieve a {@link Site} by its UUID. + * + * @param uuid The UUID of the site. + * + * @return An {@link Optional} containing the {@link Site} if a site for the + * provided UUID exists. + */ + @Transactional(Transactional.TxType.REQUIRED) + public Optional findByUuid(final String uuid) { + try { + return Optional.of( + getEntityManager() + .createNamedQuery("Site.findByUuid", Site.class) + .setParameter("uuid", uuid) + .getSingleResult() + ); + } catch (NoResultException ex) { + return Optional.empty(); + } + } + /** * Retrieve the {@link Site} for a specific domain. * @@ -127,7 +149,7 @@ public class SiteRepository extends AbstractEntityRepository { public String getIdAttributeName() { return "objectId"; } - + @Override public Long getIdOfEntity(final Site entity) { return entity.getObjectId(); diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteDetailsModel.java b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteDetailsModel.java new file mode 100644 index 000000000..60fd9b9d1 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteDetailsModel.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 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.sites; + +import org.libreccm.sites.Site; +import org.libreccm.theming.ThemeInfo; +import org.libreccm.ui.Message; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Named("SiteDetailsModel") +public class SiteDetailsModel { + + private long siteId; + + private String uuid; + + private String identifier; + + private String domain; + + private boolean defaultSite; + + private String defaultTheme; + + private Map availableThemes; + + private List messages; + + public long getSiteId() { + return siteId; + } + + public String getUuid() { + return uuid; + } + + public String getIdentifier() { + return identifier; + } + + public boolean isNew() { + return siteId == 0; + } + + public String getDomain() { + return domain; + } + + public boolean isDefaultSite() { + return defaultSite; + } + + public String getDefaultTheme() { + return defaultTheme; + } + + public Map getAvailableThemes() { + return Collections.unmodifiableMap(availableThemes); + } + + public List getMessages() { + return Collections.unmodifiableList(messages); + } + + protected void addMessage(final Message message) { + messages.add(message); + } + + protected void setSite(final Site site) { + Objects.requireNonNull(site); + + siteId = site.getObjectId(); + uuid = site.getUuid(); + identifier = String.format("ID-%d", siteId); + domain = site.getDomainOfSite(); + defaultSite = site.isDefaultSite(); + defaultTheme = site.getDefaultTheme(); + } + + protected void setAvailableThemes(final List availableThemes) { + this.availableThemes = availableThemes + .stream() + .collect( + Collectors.toMap( + themeInfo -> themeInfo.getName(), + themeInfo -> themeInfo.getName() + ) + ); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteFormController.java b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteFormController.java new file mode 100644 index 000000000..a99e8fd03 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteFormController.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2020 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.sites; + +import org.libreccm.api.Identifier; +import org.libreccm.api.IdentifierParser; +import org.libreccm.core.CoreConstants; +import org.libreccm.security.AuthorizationRequired; +import org.libreccm.security.RequiresPrivilege; +import org.libreccm.sites.Site; +import org.libreccm.sites.SiteRepository; +import org.libreccm.ui.Message; +import org.libreccm.ui.MessageType; +import org.libreccm.ui.admin.AdminMessages; + +import java.util.Arrays; +import java.util.Optional; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.mvc.Controller; +import javax.transaction.Transactional; +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Controller +@Path("/sites") +public class SiteFormController { + + @Inject + private AdminMessages adminMessages; + + @Inject + private SiteDetailsModel siteDetailsModel; + + @Inject + private SiteRepository siteRepository; + + @Inject + private IdentifierParser identifierParser; + + @FormParam("domain") + private String domainOfSite; + + @FormParam("defaultSite") + private String defaultSite; + + @FormParam("defaultTheme") + private String defaultTheme; + + @POST + @Path("/new") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String createSite() { + final Site site = new Site(); + site.setDomainOfSite(domainOfSite); + if (defaultSite != null) { + resetDefaultSite(); + site.setDefaultSite(true); + } + site.setDefaultTheme(defaultTheme); + + siteRepository.save(site); + + return "redirect:sites"; + } + + @POST + @Path("/{siteIdentifier}/edit") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String updateSite( + @PathParam("siteIdentifier") final String siteIdentifierParam + ) { + final Identifier siteIdentifier = identifierParser.parseIdentifier( + siteIdentifierParam + ); + + final Optional result; + switch (siteIdentifier.getType()) { + case ID: + result = siteRepository.findById( + Long.parseLong(siteIdentifier.getIdentifier()) + ); + break; + default: + result = siteRepository.findByUuid( + siteIdentifier.getIdentifier() + ); + break; + } + + if (result.isPresent()) { + final Site site = result.get(); + + site.setDomainOfSite(domainOfSite); + site.setDefaultTheme(defaultTheme); + + final boolean isDefaultSite = defaultSite != null; + if (isDefaultSite != site.isDefaultSite()) { + resetDefaultSite(); + site.setDefaultSite(isDefaultSite); + } + siteRepository.save(site); + + return String.format( + "redirect:sites/ID-%d/details", site.getObjectId() + ); + } else { + siteDetailsModel.addMessage( + new Message( + adminMessages.getMessage( + "sites.not_found_message", + Arrays.asList(siteIdentifierParam) + ), + MessageType.WARNING + ) + ); + + return "org/libreccm/ui/admin/sites/site-not-found.xhtml"; + } + } + + private void resetDefaultSite() { + final Optional result = siteRepository + .findDefaultSite(); + if (result.isPresent()) { + final Site site = result.get(); + site.setDefaultSite(false); + siteRepository.save(site); + } + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteTableRow.java b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteTableRow.java new file mode 100644 index 000000000..1c4df5c8a --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SiteTableRow.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 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.sites; + +/** + * + * @author Jens Pelzetter + */ +public class SiteTableRow { + + private long siteId; + + private String uuid; + + private String domain; + + private boolean defaultSite; + + private String defaultTheme; + + public long getSiteId() { + return siteId; + } + + protected void setSiteId(final long siteId) { + this.siteId = siteId; + } + + public String getUuid() { + return uuid; + } + + protected void setUuid(final String uuid) { + this.uuid = uuid; + } + + public String getIdentifier() { + return String.format("ID-%d", siteId); + } + + public String getDomain() { + return domain; + } + + protected void setDomain(final String domain) { + this.domain = domain; + } + + public boolean isDefaultSite() { + return defaultSite; + } + + protected void setDefaultSite(final boolean defaultSite) { + this.defaultSite = defaultSite; + } + + public String getDefaultTheme() { + return defaultTheme; + } + + protected void setDefaultTheme(final String defaultTheme) { + this.defaultTheme = defaultTheme; + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesController.java b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesController.java index e7bd0a708..1d84fc313 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesController.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesController.java @@ -18,14 +18,33 @@ */ package org.libreccm.ui.admin.sites; +import org.libreccm.api.Identifier; +import org.libreccm.api.IdentifierParser; import org.libreccm.core.CoreConstants; import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.RequiresPrivilege; +import org.libreccm.sites.Site; +import org.libreccm.sites.SiteRepository; +import org.libreccm.theming.Themes; +import org.libreccm.ui.Message; +import org.libreccm.ui.MessageType; +import org.libreccm.ui.admin.AdminMessages; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; import javax.mvc.Controller; +import javax.mvc.Models; +import javax.transaction.Transactional; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; /** * @@ -35,12 +54,190 @@ import javax.ws.rs.Path; @Controller @Path("/sites") public class SitesController { - + + @Inject + private AdminMessages adminMessages; + + @Inject + private IdentifierParser identifierParser; + + @Inject + private Models models; + + @Inject + private SiteDetailsModel siteDetailsModel; + + @Inject + private SiteRepository siteRepository; + + @Inject + private Themes themes; + @GET @Path("/") @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) public String getSites() { - return "org/libreccm/ui/admin/sites.xhtml"; + final List sites = siteRepository.findAll(); + models.put( + "sites", + sites + .stream() + .map(this::buildSiteTableRow) + .collect(Collectors.toList()) + ); + + return "org/libreccm/ui/admin/sites/sites.xhtml"; } + + @GET + @Path("/{siteIdentifier}/details") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String getSite( + @PathParam("siteIdentifier") final String siteIdentifierParam + ) { + final Identifier siteIdentifier = identifierParser.parseIdentifier( + siteIdentifierParam + ); + + final Optional result; + switch (siteIdentifier.getType()) { + case ID: + result = siteRepository.findById( + Long.parseLong(siteIdentifier.getIdentifier()) + ); + break; + default: + result = siteRepository.findByUuid( + siteIdentifier.getIdentifier() + ); + break; + } + + if (result.isPresent()) { + siteDetailsModel.setSite(result.get()); + siteDetailsModel.setAvailableThemes(themes.getAvailableThemes()); + + return "org/libreccm/ui/admin/sites/site-details.xhtml"; + } else { + siteDetailsModel.addMessage( + new Message( + adminMessages.getMessage( + "sites.not_found_message", + Arrays.asList(siteIdentifierParam) + ), + MessageType.WARNING + ) + ); + + return "org/libreccm/ui/admin/sites/site-not-found.xhtml"; + } + } + + @GET + @Path("/new") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String createNewSite() { + siteDetailsModel.setAvailableThemes(themes.getAvailableThemes()); + return "org/libreccm/ui/admin/sites/site-form.xhtml"; + } + + @GET + @Path("/{siteIdentifier}/edit") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String editSite( + @PathParam("siteIdentifier") final String siteIdentifierParam + ) { + final Identifier siteIdentifier = identifierParser.parseIdentifier( + siteIdentifierParam + ); + + final Optional result; + switch (siteIdentifier.getType()) { + case ID: + result = siteRepository.findById( + Long.parseLong(siteIdentifier.getIdentifier()) + ); + break; + default: + result = siteRepository.findByUuid( + siteIdentifier.getIdentifier() + ); + break; + } + + if (result.isPresent()) { + siteDetailsModel.setSite(result.get()); + siteDetailsModel.setAvailableThemes(themes.getAvailableThemes()); + + return "org/libreccm/ui/admin/sites/site-form.xhtml"; + } else { + siteDetailsModel.addMessage( + new Message( + adminMessages.getMessage( + "sites.not_found_message", + Arrays.asList(siteIdentifierParam) + ), + MessageType.WARNING + ) + ); + + return "org/libreccm/ui/admin/sites/site-not-found.xhtml"; + } + } + + @POST + @Path("/{identifier}/delete") + @AuthorizationRequired + @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) + @Transactional(Transactional.TxType.REQUIRED) + public String deleteSite( + @PathParam("identifier") final String siteIdentifierParam, + @FormParam("confirmed") final String confirmed + ) { + if ("true".equals(confirmed)) { + final Identifier siteIdentifier = identifierParser.parseIdentifier( + siteIdentifierParam + ); + + final Optional result; + switch (siteIdentifier.getType()) { + case ID: + result = siteRepository.findById( + Long.parseLong(siteIdentifier.getIdentifier()) + ); + break; + default: + result = siteRepository.findByUuid( + siteIdentifier.getIdentifier() + ); + break; + } + + if (result.isPresent()) { + siteRepository.delete(result.get()); + } + } + + return "redirect:sites"; + } + + private SiteTableRow buildSiteTableRow(final Site site) { + final SiteTableRow row = new SiteTableRow(); + row.setSiteId(site.getObjectId()); + row.setUuid(site.getUuid()); + row.setDomain(site.getDomainOfSite()); + row.setDefaultSite(site.isDefaultSite()); + row.setDefaultTheme(site.getDefaultTheme()); + + return row; + } + } diff --git a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesPage.java b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesPage.java index a07f98a6f..6ae053284 100644 --- a/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesPage.java +++ b/ccm-core/src/main/java/org/libreccm/ui/admin/sites/SitesPage.java @@ -36,6 +36,7 @@ public class SitesPage implements AdminPage { public Set> getControllerClasses() { final Set> classes = new HashSet<>(); classes.add(SitesController.class); + classes.add(SiteFormController.class); return classes; } diff --git a/ccm-core/src/main/resources/META-INF/resources/components/libreccm/deleteDialog.xhtml b/ccm-core/src/main/resources/META-INF/resources/components/libreccm/deleteDialog.xhtml index c661863db..b70493b87 100644 --- a/ccm-core/src/main/resources/META-INF/resources/components/libreccm/deleteDialog.xhtml +++ b/ccm-core/src/main/resources/META-INF/resources/components/libreccm/deleteDialog.xhtml @@ -101,6 +101,7 @@