/** * 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 java.util.stream.Collectors.toList; import static org.fenixedu.commons.i18n.LocalizedString.fromJson; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; import org.fenixedu.bennu.core.domain.User; 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.wraps.Wrap; import org.fenixedu.cms.domain.wraps.Wrappable; 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; /** * Categories give a semantic group for {@link Site} and {@link Post}. */ public class Category extends Category_Base implements Wrappable, Sluggable, Cloneable { private static final long NUM_RECENT = 10; public static final String SIGNAL_CREATED = "fenixedu.cms.category.created"; public static final String SIGNAL_DELETED = "fenixedu.cms.category.deleted"; public static final String SIGNAL_EDITED = "fenixedu.cms.category.edited"; public static final Comparator<? super Category> CATEGORY_NAME_COMPARATOR = Comparator.comparing(Category::getName); /** * The logged {@link User} creates a new instance of a {@link Category} * @param site site * @param name name */ public Category(Site site, LocalizedString name) { super(); if (Authenticate.getUser() == null) { throw CmsDomainException.forbiden(); } this.setCreatedBy(Authenticate.getUser()); this.setCreationDate(new DateTime()); this.setSite(site); this.setName(name); this.setPrivileged(false); Signal.emit(Category.SIGNAL_CREATED, new DomainObjectEvent<Category>(this)); } @Override public Site getSite() { return super.getSite(); } @Override public void setName(LocalizedString name) { LocalizedString prevName = getName(); super.setName(name); 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 category 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 { Category c = getSite().categoryForSlug(slug); return c == this; } catch (CmsDomainException cmsDomainException){ return true; } } public String getAddress() { Page viewCategoryPage = this.getSite().getViewCategoryPage(); return viewCategoryPage == null ? null : viewCategoryPage.getAddress() + "/" + this.getSlug(); } public String getRssUrl() { return getSite().getRssUrl() + "/" + getSlug(); } @Atomic public void delete() { Signal.emit(SIGNAL_DELETED, this.getOid()); this.setCreatedBy(null); this.setSite(null); this.getComponentsSet().stream().forEach(Component::delete); this.getPostsSet().stream().forEach(post->post.removeCategories(this)); this.deleteDomainObject(); } @Override public Category clone(CloneCache cloneCache) { return cloneCache.getOrClone(this, obj -> { Collection<Post> posts = new HashSet<>(getPostsSet()); HashSet<Component> components = new HashSet<>(getComponentsSet()); LocalizedString name = getName() != null ? fromJson(getName().json()) : null; Category clone = new Category(getSite(), name); cloneCache.setClone(Category.this, clone); posts.stream().map(post -> post.clone(cloneCache)).forEach(clone::addPosts); components.stream().filter(ListCategoryPosts.class::isInstance) .map(ListCategoryPosts.class::cast) .forEach(clone::addComponents); return clone; }); } public class CategoryWrap extends Wrap { public LocalizedString getName() { return Category.this.getName(); } public String getAddress() { return Category.this.getAddress(); } public String getSlug() { return Category.this.getSlug(); } public DateTime getCreationDate() { return Category.this.getCreationDate(); } public String getRssUrl() { return Category.this.getRssUrl(); } } @Override public Wrap makeWrap() { return new CategoryWrap(); } public String getEditUrl() { return CoreConfiguration.getConfiguration().applicationUrl() + "/cms/categories/" + getSite().getSlug() + "/" + getSlug(); } public List<Post> getLatestPosts() { return getPostsSet().stream().sorted(Post.CREATION_DATE_COMPARATOR).collect(toList()); } }