/** * Copyright © 2014 Instituto Superior Técnico * * This file is part of FenixEdu CMS. * * FenixEdu CMS 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 3 of the License, or * (at your option) any later version. * * FenixEdu CMS 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 FenixEdu CMS. If not, see <http://www.gnu.org/licenses/>. */ package org.fenixedu.cms.domain; import static org.fenixedu.commons.i18n.LocalizedString.fromJson; import java.util.Comparator; import java.util.HashSet; import java.util.Optional; import java.util.Set; import org.fenixedu.bennu.core.domain.User; import org.fenixedu.bennu.core.groups.AnyoneGroup; import org.fenixedu.bennu.core.groups.Group; import org.fenixedu.bennu.core.security.Authenticate; import org.fenixedu.bennu.core.util.CoreConfiguration; import org.fenixedu.bennu.signals.DomainObjectEvent; import org.fenixedu.bennu.signals.Signal; import org.fenixedu.cms.domain.component.Component; import org.fenixedu.cms.domain.component.ListCategoryPosts; import org.fenixedu.cms.domain.component.StaticPost; import org.fenixedu.cms.exceptions.CmsDomainException; import org.fenixedu.commons.StringNormalizer; import org.fenixedu.commons.i18n.LocalizedString; import org.joda.time.DateTime; import pt.ist.fenixframework.Atomic; /** * Model for a page on a given Site. */ public class Page extends Page_Base implements Sluggable, Cloneable { public static final String SIGNAL_CREATED = "fenixedu.cms.page.created"; public static final String SIGNAL_DELETED = "fenixedu.cms.page.deleted"; public static final String SIGNAL_EDITED = "fenixedu.cms.page.edited"; public static final Comparator<Page> CREATION_DATE_COMPARATOR = Comparator.comparing(Page::getCreationDate).reversed(); public static Comparator<Page> PAGE_NAME_COMPARATOR = Comparator.comparing(Page::getName); /** * the logged {@link User} creates a new Page. * @param site site * @param name name */ public Page(Site site, LocalizedString name) { super(); DateTime now = new DateTime(); setCreationDate(now); setModificationDate(now); if (Authenticate.getUser() == null) { throw CmsDomainException.forbiden(); } setCreatedBy(Authenticate.getUser()); setCanViewGroup(AnyoneGroup.get()); setSite(site); setPublished(false); setName(name); Signal.emit(Page.SIGNAL_CREATED, new DomainObjectEvent<Page>(this)); } @Override public Site getSite() { return super.getSite(); } @Override public void setName(LocalizedString name) { LocalizedString prevName = getName(); super.setName(name); setModificationDate(new DateTime()); if (prevName == null) { setSlug(StringNormalizer.slugify(name.getContent())); } } @Override public void setSlug(String slug) { super.setSlug(SlugUtils.makeSlug(this, slug)); } /** * A slug is valid if there are no other page on that site that have the * same slug. * * @param slug slug * @return true if it is a valid slug. */ @Override public boolean isValidSlug(String slug) { try { Page p = getSite().pageForSlug(slug); return p== this; } catch (CmsDomainException cmsDomainException){ return true; } } /** * Searches a {@link Component} of this page by oid. * * @param oid * the oid of the {@link Component} to be searched. * @return the {@link Component} with the given oid if it is a component of * this page and null otherwise. */ public Component componentForOid(String oid) { for (Component c : getComponentsSet()) { if (c.getExternalId().equals(oid)) { return c; } } return null; } @Atomic public void delete() { Signal.emit(SIGNAL_DELETED, this.getOid()); for (Component component : getComponentsSet()) { removeComponents(component); component.delete(); } getMenuItemsSet().stream().forEach(MenuItem::delete); setTemplate(null); setSite(null); setCreatedBy(null); setViewGroup(null); deleteDomainObject(); } /** * @return the URL link for this page. */ public String getAddress() { return CoreConfiguration.getConfiguration().applicationUrl() + "/" + getSite().getBaseUrl() + "/" + getSlug(); } /** * returns the group of people who can view this site. * * @return group the access group for this site */ public Group getCanViewGroup() { return getViewGroup().toGroup(); } /** * sets the access group for this site * * @param group * the group of people who can view this site */ @Atomic public void setCanViewGroup(Group group) { setViewGroup(group.toPersistentGroup()); } public static Page create(Site site, Menu menu, MenuItem parent, LocalizedString name, boolean published, String template, User creator, Component... components) { Page page = new Page(site, name); if (components != null && components.length > 0) { for (Component component : components) { page.addComponents(component); } } page.setTemplate(site.getTheme().templateForType(template)); if (creator == null) { page.setCreatedBy(site.getCreatedBy()); } else { page.setCreatedBy(creator); } page.setPublished(published); if (menu != null) { MenuItem.create(menu, page, name, parent); } return page; } @Override public void setPublished(boolean published) { setModificationDate(new DateTime()); super.setPublished(published); } @Override public void setTemplate(CMSTemplate template) { setModificationDate(new DateTime()); super.setTemplate(template); if (template != null) { setTemplateType(template.getType()); } else { setTemplateType(null); } } public boolean isPublished() { return getPublished(); } public String getRssUrl() { for (Component component : getComponentsSet()) { if (component instanceof ListCategoryPosts) { ListCategoryPosts listPosts = (ListCategoryPosts) component; if (listPosts.getCategory() != null) { return listPosts.getCategory().getRssUrl(); } } } return null; } public String getEditUrl() { if (isStaticPage()) { return CoreConfiguration.getConfiguration().applicationUrl() + "/cms/pages/" + getSite().getSlug() + "/" + getSlug() + "/edit"; } else { return CoreConfiguration.getConfiguration().applicationUrl() + "/cms/pages/advanced/" + getSite().getSlug() + "/" + getSlug() + "/edit"; } } @Override public CMSTemplate getTemplate() { String templateType = getTemplateType(); if (templateType == null) { return null; } CMSTemplate template = super.getTemplate(); CMSTheme theme = getSite().getTheme(); if (templateType != null) { if (template != null && template.getTheme() == theme && template.getType().equals(templateType)) { return template; } if (theme != null) { template = theme.templateForType(templateType); if (template != null) { return template; } } if (theme.getDefaultTemplate() != null) { return theme.getDefaultTemplate(); } return CMSTheme.getDefaultTheme().getDefaultTemplate(); } else { return null; } } public boolean isStaticPage() { return getComponentsSet().stream().filter(component -> StaticPost.class.isInstance(component)).findAny().isPresent(); } public Optional<Post> getStaticPost() { return getComponentsSet().stream().filter(component -> StaticPost.class.isInstance(component)) .map(component -> ((StaticPost) component).getPost()).findFirst(); } @Override public Page clone(CloneCache cloneCache) { return cloneCache.getOrClone(this, obj -> { Set<Component> components = new HashSet<>(getComponentsSet()); LocalizedString name = getName() != null ? fromJson(getName().json()) : null; Page clone = new Page(getSite(), name); cloneCache.setClone(Page.this, clone); clone.setCanViewGroup(getCanViewGroup()); clone.setPublished(getPublished()); clone.setCreatedBy(getCreatedBy()); clone.setModificationDate(getModificationDate()); clone.setCreationDate(getCreationDate()); clone.setTemplateType(getTemplateType()); clone.setViewGroup(getViewGroup()); components.stream().map(c -> c.clone(cloneCache)).forEach(clone::addComponents); return clone; }); } }