/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.riotfamily.pages.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.hibernate.Session;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.IndexColumn;
import org.riotfamily.common.hibernate.Lifecycle;
import org.riotfamily.common.util.FormatUtils;
import org.riotfamily.common.util.Generics;
import org.riotfamily.common.web.cache.TagCacheItems;
import org.riotfamily.components.model.ContentEntity;
import org.riotfamily.core.security.AccessController;
import org.riotfamily.pages.config.PageType;
import org.springframework.util.StringUtils;
/**
* @author Felix Gnass [fgnass at neteye dot de]
* @author Jan-Frederic Linde [jfl at neteye dot de]
* @since 6.5
*/
@Entity
@Table(name="riot_pages", uniqueConstraints = {@UniqueConstraint(columnNames={"site_id", "path"})})
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region="pages")
@TagCacheItems
public class ContentPage extends ContentEntity implements Page, Lifecycle {
public static final String TITLE_PROPERTY = "title";
private Long id;
private String pageTypeName;
private ContentPage parent;
private List<ContentPage> children;
private Site site;
private String pathComponent;
private String path;
private Date creationDate;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public ContentPage() {
}
public ContentPage(String pathComponent, Site site) {
this.pathComponent = pathComponent;
this.site = site;
}
public String getPathComponent() {
return pathComponent;
}
public void setPathComponent(String pathComponent) {
this.pathComponent = pathComponent;
}
@Transient
public PageType getPageType() {
if (site != null && pageTypeName != null) {
return site.getSchema().getPageType(pageTypeName);
}
return null;
}
public void setPageType(PageType pageType) {
this.pageTypeName = pageType.getName();
}
@Column(name="pageType")
protected String getPageTypeName() {
return pageTypeName;
}
protected void setPageTypeName(String pageTypeName) {
this.pageTypeName = pageTypeName;
}
@ManyToOne(cascade=CascadeType.MERGE)
public Site getSite() {
return this.site;
}
public void setSite(Site site) {
this.site = site;
}
@ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE)
@JoinColumn(name="parent_id", updatable=false, insertable=false)
public ContentPage getParent() {
return parent;
}
public void setParent(ContentPage parent) {
this.parent = parent;
}
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="parent_id")
@IndexColumn(name="pos")
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region="pages")
public List<ContentPage> getChildren() {
return children;
}
public void setChildren(List<ContentPage> children) {
this.children = children;
}
// ----------------------------------------------------------------------
@Transient
public Locale getLocale() {
return site.getLocale();
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
@Transient
public String getTitle() {
Object title = getContentContainer().getContent(true).get(TITLE_PROPERTY);
if (title != null) {
return title.toString();
}
if (!StringUtils.hasText(pathComponent)) {
return "/";
}
return FormatUtils.xmlToTitleCase(pathComponent);
}
@Transient
public String getUrl() {
return getPath() + getSite().getDefaultSuffix(this);
}
@Transient
public boolean isRequestable() {
return (isPublished() && site.isEnabled())
|| AccessController.isAuthenticatedUser();
}
@Transient
public List<ContentPage> getSiblings() {
if (parent == null) {
return Collections.singletonList(this);
}
return parent.getChildren();
}
@Transient
public Date getLastPublished() {
return getContentContainer().getLastPublished();
}
@Override
public String toString() {
return String.format("ContentPage[path=%s,id=%s,site=%s]",
getPath(), getId(), site.getName());
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
public void addPage(ContentPage child) {
/*
if (!PageValidationUtils.isValidChild(this, child)) {
throw new DuplicatePathComponentException(
"Page '{0}' did not validate", child.toString());
}
*/
child.setParent(this);
child.setSite(getSite());
if (children == null) {
children = Generics.newArrayList();
}
children.add(child);
}
public void removePage(ContentPage child) {
children.remove(child);
if (this.equals(child.getParent())) {
child.setParent(null);
}
}
// ----------------------------------------------------------------------
// Schema methods
// ----------------------------------------------------------------------
@Transient
public boolean isSystemPage() {
return site.getSchema().isSystemPage(this);
}
@Transient
public boolean canHaveChildren() {
return site.getSchema().canHaveChildren(this);
}
@Transient
public boolean isValidChild(ContentPage child) {
return site.getSchema().isValidChild(this, child);
}
// ----------------------------------------------------------------------
// Materialized path methods
// ----------------------------------------------------------------------
public String getPath() {
if (path == null) {
materializePath();
}
return path;
}
public void setPath(String path) {
this.path = path;
}
// TODO: This method really should be private again.
public void updatePath() {
String oldPath = this.path;
if (materializePath()) {
PageAlias.create(this, oldPath);
updateChildPaths();
}
}
private void updateChildPaths() {
if (children != null) {
for (ContentPage child : children) {
child.updatePath();
}
}
}
private boolean materializePath() {
String path = buildPath();
if (!path.equals(this.path)) {
this.path = path;
return true;
}
return false;
}
private String buildPath() {
if (parent != null) {
String s = parent.getPath();
if (!s.endsWith("/")) {
s += "/";
}
return s + pathComponent;
}
return "/";
}
// ----------------------------------------------------------------------
// Implementation of the Lifecycle interface
// ----------------------------------------------------------------------
public void onSave() {
setCreationDate(new Date());
PageAlias.create(this, null);
updateChildPaths();
}
public void onUpdate(Object oldState) {
updatePath();
}
public void onDelete() {
PageAlias.resetByPage(this);
}
// ----------------------------------------------------------------------
// Persistence methods
// ----------------------------------------------------------------------
public static ContentPage load(Long id) {
return load(ContentPage.class, id);
}
public void refreshIfDetached() {
Session session = getSession();
if (!session.contains(this)) {
session.refresh(this);
}
}
public static ContentPage loadBySiteAndPath(Site site, String path) {
return query(ContentPage.class,
"from {} where site = ? and path = ?", site, path)
.cache().load();
}
public static ContentPage loadByTypeAndSite(String pageType, Site site) {
return query(ContentPage.class,
"from {} where pageType = ? and site = ?", pageType, site)
.cache().load();
}
public static List<ContentPage> findByTypesAndSite(Collection<String> types, Site site) {
if (types == null || types.isEmpty()) {
return Collections.emptyList();
}
return query(ContentPage.class,
"from {} where pageType in (:types) and site = :site")
.setParameterList("types", types)
.setParameter("site", site)
.cache().find();
}
}