/*
* Copyright 2010 FatWire Corporation. All Rights Reserved.
*
* 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 com.fatwire.gst.foundation.wra.navigation;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import COM.FutureTense.Interfaces.ICS;
import COM.FutureTense.Interfaces.Utilities;
import com.fatwire.assetapi.data.AssetData;
import com.fatwire.assetapi.data.AssetId;
import com.fatwire.assetapi.site.SiteInfo;
import com.fatwire.gst.foundation.controller.AssetIdWithSite;
import com.fatwire.gst.foundation.facade.assetapi.AssetAccessTemplate;
import com.fatwire.gst.foundation.facade.assetapi.AssetClosure;
import com.fatwire.gst.foundation.facade.assetapi.AssetDataUtils;
import com.fatwire.gst.foundation.facade.assetapi.AttributeDataUtils;
import com.fatwire.gst.foundation.facade.assetapi.asset.DateFilterClosure;
import com.fatwire.gst.foundation.facade.assetapi.asset.PreviewContext;
import com.fatwire.gst.foundation.facade.runtag.render.LogDep;
import com.fatwire.gst.foundation.facade.runtag.siteplan.ListPages;
import com.fatwire.gst.foundation.wra.Alias;
import com.fatwire.gst.foundation.wra.AliasCoreFieldDao;
import com.fatwire.gst.foundation.wra.AssetApiAliasCoreFieldDao;
import com.fatwire.gst.foundation.wra.AssetApiWraCoreFieldDao;
import com.fatwire.gst.foundation.wra.VanityAsset;
import com.fatwire.gst.foundation.wra.WebReferenceableAsset;
import com.fatwire.gst.foundation.wra.WraCoreFieldDao;
import com.fatwire.gst.foundation.wra.WraUriBuilder;
import com.fatwire.mda.DimensionFilterInstance;
import com.openmarket.xcelerate.asset.AssetIdImpl;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.fatwire.gst.foundation.facade.runtag.asset.FilterAssetsByDate.isValidOnDate;
/**
* Used to retrieve the Navigation Bar data. See the description of
* {@link #getSitePlan(String)} for more details.
*
* @author David Chesebro
* @author Dolf Dijkstra
* @since Jun 17, 2010
*
* @deprecated as of release 12.x, will be replaced with a brand new, significantly improved NavigationService implementation (coming soon)
*
*/
@Deprecated
public class NavigationHelper {
/**
* ICS context
*/
protected final ICS ics;
final AssetAccessTemplate assetTemplate;
/**
* Local instance of the WraCoreFieldDao, pre-instantiated and ready to go
*/
protected final WraCoreFieldDao wraDao;
/**
* Local instance of the AliasCoreFieldDao.
*/
protected final AliasCoreFieldDao aliasDao;
/**
* Log file
*/
protected final Logger LOG = LoggerFactory.getLogger("tools.gsf.legacy.wra.navigation.NavigationHelper");
/**
* Effective date for the purposes of startdate/enddate comparisons for an
* asset.
*/
protected final Date assetEffectiveDate;
/**
* Constructor. Initializes assetEffectiveDate to null.
*
* @param ics object
*/
public NavigationHelper(final ICS ics) {
this.ics = ics;
this.wraDao = new AssetApiWraCoreFieldDao(ics);
this.aliasDao = new AssetApiAliasCoreFieldDao(ics, wraDao);
this.assetEffectiveDate = null;
assetTemplate = new AssetAccessTemplate(ics);
}
/**
* Constructor with all the dependencies listed. Initializes
* assetEffectiveDate to null.
*
* @param ics Content Server context object
* @param assetTemplate Asset Access Template
* @param wraDao WRA DAO
* @param aliasDao Alias Core Field DAO
*/
public NavigationHelper(final ICS ics, AssetAccessTemplate assetTemplate, WraCoreFieldDao wraDao,
AliasCoreFieldDao aliasDao) {
this.ics = ics;
this.wraDao = wraDao;
this.aliasDao = aliasDao;
this.assetTemplate = assetTemplate;
this.assetEffectiveDate = null;
}
/**
* Name of the page subtype indicating that this page is NOT rendered on the
* site but is instead merely used to group navigation components on the
* site.
*/
public static final String NAVBAR_NAME = "GSTNavName";
/**
* Name of the page subtype indicating that this page is a Link, meaning
* that the content is in the unnamed association
*/
public static final String NAVBAR_LINK = "GSTNavLink";
/**
* @param name the name of the Page asset
* @return NavNode for the Page with the name
*/
public NavNode getSitePlanByPage(final String name) {
return getSitePlanByPage(1, name);
}
/**
* Retrieves the NavNode for the given Page with the provided name.
*
* @param depth the maximum depth to retrieve, -1 for no limit.
* @param name the name of the Page asset
* @return NavNode for the Page with the name
*/
public NavNode getSitePlanByPage(final int depth, final String name) {
final String sitename = ics.GetVar("site");
if (StringUtils.isBlank(sitename)) {
throw new IllegalStateException(
"site is not a ics variable. This function needs this variable to be aviable and contain the name of the site.");
}
return getSitePlanByPage(depth, name, sitename);
}
/**
* Retrieves the NavNode for the given Page with the provided name.
*
* @param depth the maximum depth to retrieve, -1 for no limit.
* @param name the name of the Page asset
* @param dimensionFilter in order to translate the output.
* @return NavNode for the Page with the name
*/
public NavNode getSitePlanByPage(final int depth, final String name, final DimensionFilterInstance dimensionFilter) {
final String sitename = ics.GetVar("site");
if (StringUtils.isBlank(sitename)) {
throw new IllegalStateException(
"site is not a ics variable. This function needs this variable to be aviable and contain the name of the site.");
}
return getSitePlanByPage(depth, name, sitename, dimensionFilter);
}
/**
* Retrieves the NavNode for the given Page with the provided name.
*
* @param depth the maximum depth to retrieve, -1 for no limit.
* @param name the name of the Page asset
* @param sitename the name of the site you want the navigation for.
* @return NavNode for the Page with the name
*/
public NavNode getSitePlanByPage(final int depth, final String name, final String sitename) {
return getSitePlanByPage(depth, name, sitename, null);
}
/**
* Retrieves the NavNode for the given Page with the provided name.
*
* @param depth the maximum depth to retrieve, -1 for no limit.
* @param name the name of the Page asset
* @param sitename the name of the site you want the navigation for.
* @param dimensionFilter in order to translate the output.
* @return NavNode for the Page with the name
*/
public NavNode getSitePlanByPage(final int depth, final String name, final String sitename,
final DimensionFilterInstance dimensionFilter) {
final SiteInfo site = assetTemplate.readSiteInfo(sitename);
if (site == null) {
throw new RuntimeException("Site with name '" + sitename + "' not found.");
}
final AssetId pageid = assetTemplate.findByName(ics, "Page", name, site.getId());
if (pageid == null) {
return null;
}
return getSitePlan(depth, pageid, dimensionFilter);
}
/**
* Retrieves the NavNode for the given Page with the provided id.
*
* The NavNode contains all the attributes necessary to create a nav bar.
* <p>
* Links are not populated for Navigation Placeholders, but it is often very
* convenient to pass a navigation placeholder into this function in order
* to return all children under a specific placeholder.
* <p>
* StartDate and EndDate are checked and invalid pages aren't added. If a
* Page asset is not valid, its children are not even examined.
*
*
* @param pageid the assetid of the Page asset.
* @return the NavNode for this page
*/
public NavNode getSitePlan(final String pageid) {
return getSitePlan(new AssetIdImpl("Page", Long.parseLong(pageid)));
}
/**
* Get the NavNode for the current page with unlimited depth.
*
* @param pageid AssetId object for the given page id
* @return the NavNode associated with this pageid.
*/
public NavNode getSitePlan(final AssetId pageid) {
return getSitePlan(-1, pageid, 0, null);
}
/**
* Retrieves the NavNode for the given Page with the provided id.
*
* @param depth the maximum depth to retrieve, -1 for no limit.
* @param pageid the AssetId for the page
* @return the NavNode for this page
*/
public NavNode getSitePlan(final int depth, final AssetId pageid) {
return getSitePlan(depth, pageid, 0, null);
}
/**
* Retrieves the NavNode for the given Page with the provided id.
*
* @param depth the maximum depth to retrieve, -1 for no limit.
* @param pageid the AssetId for the page
* @param dimensionFilter in order to translate the output.
* @return the NavNode for this page
*/
public NavNode getSitePlan(final int depth, final AssetId pageid, final DimensionFilterInstance dimensionFilter) {
LOG.debug("Dimension filter " + dimensionFilter + " provided for site plan lookup");
return getSitePlan(depth, pageid, 0, dimensionFilter);
}
/**
* Called from public {@link #getSitePlan(int, AssetId)}. See that
* function's description for details
*
* @param depth the depth of the tree to retrieve, -1 for unlimited depth.
* @param pageId id of the page asset
* @param level starting level number when traversing the site plan tree
* @return NavNode of the site plan tree
*/
private NavNode getSitePlan(final int depth, final AssetId pageId, final int level,
final DimensionFilterInstance dimensionFilter) {
// check the start/end date of the page asset
LogDep.logDep(ics, pageId);
if (!isValidOnDate(ics, pageId, assetEffectiveDate)) {
// the input object is not valid. Abort
if (LOG.isDebugEnabled()) {
LOG.debug("Input asset " + pageId + " is not effective on " + assetEffectiveDate);
}
return null;
}
// determine if it's a wra, a placeholder or an alias
final AssetData pageData = AssetDataUtils.getAssetData(ics, pageId, "subtype", "name");
final String subtype = AttributeDataUtils.asString(pageData.getAttributeData("subtype"));
final String name = AttributeDataUtils.asString(pageData.getAttributeData("name"));
final boolean isNavigationPlaceholder = NAVBAR_NAME.equals(subtype);
final NavNode node = new NavNode();
node.setPage(pageId);
node.setLevel(level);
node.setPagesubtype(subtype);
node.setPagename(name);
if (isNavigationPlaceholder) {
// no link if it's just a placeholder
} else {
// not a GSTNavLink, probably 11g page
final AssetClosure closure = new NodeWraAssetClosure(node);
final DateFilterClosure dateFilterClosure = new DateFilterClosure(PreviewContext.getPreviewDate(ics,
assetEffectiveDate), closure);
// retrieve the unnamed association(s) based on date filter
if (dimensionFilter == null) {
assetTemplate.readAssociatedAssets(pageId, "-", dateFilterClosure);
} else {
final Collection<AssetId> associatedWraList = assetTemplate.readAssociatedAssetIds(pageId, "-");
for (final AssetId child : dimensionFilter.filterAssets(associatedWraList)) {
assetTemplate.readAsset(child, dateFilterClosure);
}
}
}
if (depth < 0 || depth > level) {
// get the children in the Site Plan
final List<AssetId> childrenIDs = ListPages.getChildPages(ics, pageId.getId());
for (final AssetId aid : childrenIDs) {
// note recursing here
final NavNode kidInfo = getSitePlan(depth, aid, level + 1, dimensionFilter);
if (kidInfo != null && kidInfo.getPage() != null) {
node.addChild(kidInfo);
}
}
}
return node;
}
/**
* Constant containing the asset type of the GST Alias asset.
*/
public final String GST_ALIAS_TYPE = Alias.ALIAS_ASSET_TYPE_NAME;
/**
* Return true if the asset type is a GSTAlias asset type. May be overridden
* if customers are attempting to retrofit this class for alias-like
* functionality that is not implemented by the GSTAlias asset type.
*
* @param id asset for which a link is required
* @return true if the asset is an alias, false if it is a web-referenceable
* asset
*/
protected boolean isGstAlias(final AssetId id) {
return GST_ALIAS_TYPE.equals(id.getType());
}
/**
* Get the URL for the alias.
*
* For external links, the targeturl attribute is rendered.
*
* For Aliases that refer to another WRA, the alias is allowed to override
* any WRA fields. For instance, the path, and the template can be
* overridden by an alias for a WRA (though the template in the Alias had
* better be typeless, or a template of the same name must exist in the
* WRA's asset type or there will be a problem).
*
* @param alias Alias bean, which of course is also a WRA.
* @return url
*/
protected String getUrlForAlias(final Alias alias) {
if (alias.getTargetUrl() != null) {
return alias.getTargetUrl();
} else {
return getUrlForWra(alias);
}
}
/**
* Get the URL to use for the web-referenceable asset.
*
* @param wra WebReferenceableAsset bean
* @return url
*/
protected String getUrlForWra(final VanityAsset wra) {
if (wra.getTemplate() == null || wra.getTemplate().length() == 0) {
LOG.warn("Asset " + wra + " does not have a valid template set.");
return null;
}
String wrapper = ics.GetProperty("com.fatwire.gst.foundation.url.wrapathassembler.dispatcher",
"ServletRequest.properties", true);
if (!Utilities.goodString(wrapper)) {
wrapper = "GST/Dispatcher";
}
return new WraUriBuilder(wra, wrapper).toURI(ics);
}
protected void decorateAsWra(final AssetId id, final NavNode node) {
final WebReferenceableAsset wra = wraDao.getWra(id);
final String url = getUrlForWra(wra);
final String linktext = wra.getLinkText();
node.setWra(wra);
if (url != null) {
node.setUrl(url);
}
if (linktext != null) {
node.setLinktext(linktext);
}
}
protected void decorateAsAlias(final AssetId id, final NavNode node) {
final Alias alias = aliasDao.getAlias(id);
final String url = getUrlForAlias(alias);
final String linktext = alias.getLinkText();
node.setWra(alias);
if (url != null) {
node.setUrl(url);
}
if (linktext != null) {
node.setLinktext(linktext);
}
}
/**
* Locate the page that contains the specified Web-Referenceable Asset.
* <p>
* A WRA is supposed to just be placed on one page (in the unnamed
* association block), and this method locates it. If it is not found, 0L is
* returned.
* <p>
* If multiple matches are found, a warning is logged and the first one is
* returned.
*
* @param site_name name of the site to search within
* @param wraAssetId the asset id of the web-referenceable asset
* @return page asset ID or 0L.
*/
public long findP(final String site_name, final AssetId wraAssetId) {
return wraDao.findP(new AssetIdWithSite(wraAssetId.getType(), wraAssetId.getId(), site_name));
}
class NodeWraAssetClosure implements AssetClosure {
private final NavNode node;
public NodeWraAssetClosure(final NavNode node) {
this.node = node;
}
@Override
public boolean work(final AssetData asset) {
final AssetId id = asset.getAssetId();
node.setId(id);
if (isGstAlias(id)) {
decorateAsAlias(id, node);
} else {
decorateAsWra(id, node);
}
return false; // needs to return only one node
}
}
}