/**
* 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.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.fenixedu.bennu.core.security.Authenticate;
import org.fenixedu.bennu.signals.DomainObjectEvent;
import org.fenixedu.bennu.signals.Signal;
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 com.google.common.collect.Sets;
import pt.ist.fenixframework.Atomic;
import pt.ist.fenixframework.FenixFramework;
import pt.ist.fenixframework.consistencyPredicates.ConsistencyPredicate;
/**
* Model of a Menu for a given {@link Page}
*/
public class Menu extends Menu_Base implements Wrappable, Sluggable, Cloneable, Comparable<Menu>{
public static final String SIGNAL_CREATED = "fenixedu.cms.menu.created";
public static final String SIGNAL_DELETED = "fenixedu.cms.menu.deleted";
public static final String SIGNAL_EDITED = "fenixedu.cms.menu.edited";
public Menu(Site site, LocalizedString name) {
if (Authenticate.getUser() == null) {
throw CmsDomainException.forbiden();
}
this.setCreatedBy(Authenticate.getUser());
this.setCreationDate(new DateTime());
setSite(site);
setTopMenu(false);
this.setName(name);
this.setPrivileged(false);
this.setOrder(site.getMenusSet().size());
Signal.emit(Menu.SIGNAL_CREATED, new DomainObjectEvent<>(this));
}
@Override
public Site getSite() {
return super.getSite();
}
@Atomic
public void delete() {
Signal.emit(Menu.SIGNAL_DELETED, new DomainObjectEvent<>(this));
Sets.newHashSet(getItemsSet()).stream().distinct().forEach(MenuItem::delete);
this.setCreatedBy(null);
this.setSite(null);
this.deleteDomainObject();
}
@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) {
Menu m = getSite().menuForSlug(slug);
return m == null || m == this;
}
/**
* saves the name of the post and creates a new slug for the post.
*/
@Override
public void setName(LocalizedString name) {
LocalizedString prevName = getName();
super.setName(name);
if (prevName == null) {
String slug = StringNormalizer.slugify(name.getContent());
setSlug(slug);
}
}
/**
* Puts a {@link MenuItem} at a given position, shifting the existing ones to the right.
*
* @param item
* The {@link MenuItem} to be added.
* @param position
* the position to save the item.
*/
public void putAt(MenuItem item, int position) {
if (position < 0) {
position = 0;
}
if (position >= this.getToplevelItemsSet().size()) {
item.removeFromParent();
position = getToplevelItemsSet().size();
}
if (item.getPosition() != null) {
item.removeFromParent();
}
List<MenuItem> list = getToplevelItemsSorted().collect(Collectors.toList());
list.add(position, item);
MenuItem.fixOrder(list);
getToplevelItemsSet().add(item);
getItemsSet().add(item);
}
/**
* Removes a given {@link MenuItem} from the Menu.
*
* @param mi
* the {@link MenuItem} to be removed.
*/
public void remove(MenuItem mi) {
getToplevelItemsSet().remove(mi);
MenuItem.fixOrder(getToplevelItemsSorted().collect(Collectors.toList()));
getItemsSet().remove(mi);
MenuItem.fixOrder(getItemsSorted().collect(Collectors.toList()));
}
/**
* Adds a given {@link MenuItem} as the last item.
*
* @param mi the {@link MenuItem} to be added.
*/
public void add(MenuItem mi) {
this.putAt(mi, getToplevelItemsSet().size());
}
public Stream<MenuItem> getToplevelItemsSorted() {
return getToplevelItemsSet().stream().sorted();
}
public Stream<MenuItem> getItemsSorted() {
return getItemsSet().stream().sorted();
}
@Override
public Menu clone(CloneCache cloneCache) {
return cloneCache.getOrClone(this, obj -> {
Collection<MenuItem> menuItems = new HashSet<>(getItemsSet());
LocalizedString name = getName() != null ? fromJson(getName().json()) : null;
Menu clone = new Menu(getSite(), name);
cloneCache.setClone(Menu.this, clone);
clone.setName(name);
clone.setOrder(getOrder());
for (MenuItem menuItem : menuItems) {
menuItem.clone(cloneCache).setMenu(clone);
}
return clone;
});
}
@Override
public int compareTo(Menu o) {
return getOrder().compareTo(o.getOrder());
}
@SuppressWarnings("unused")
private class MenuWrap extends Wrap {
private final Page page;
private final Stream<Wrap> children;
public MenuWrap() {
this.page = null;
this.children = getToplevelItemsSorted().filter(MenuItem::isVisible).map(MenuItem::makeWrap);
}
public MenuWrap(Page page) {
this.page = page;
this.children = getToplevelItemsSorted().filter(MenuItem::isVisible).map(item -> item.makeWrap(page));
}
public Stream<Wrap> getChildren() {
return children;
}
public int getOrder(){
return Menu.this.getOrder();
}
public LocalizedString getName() {
return Menu.this.getName();
}
public Wrap getSite() {
return Menu.this.getSite().makeWrap();
}
public Boolean getTopMenu() {
return Menu.this.getTopMenu();
}
}
@Override
public Wrap makeWrap() {
return new MenuWrap();
}
public Wrap makeWrap(Page page) {
return new MenuWrap(page);
}
public MenuItem menuItemForOid(String menuItemOid) {
MenuItem menuItem = FenixFramework.getDomainObject(menuItemOid);
if(menuItem != null && FenixFramework.isDomainObjectValid(menuItem) && menuItem.getMenu() == this) {
return menuItem;
}
return null;
}
@ConsistencyPredicate
public boolean checkMenuOrder(){
return getOrder() != null && !(getOrder()<0);
}
}