/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/portal/trunk/portal-impl/impl/src/java/org/sakaiproject/portal/charon/site/PortalSiteHelperImpl.java $
* $Id: PortalSiteHelperImpl.java 132914 2013-12-26 16:30:58Z csev@umich.edu $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.portal.charon.site;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.alias.api.Alias;
import org.sakaiproject.alias.cover.AliasService;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.user.cover.PreferencesService;
import org.sakaiproject.user.api.Preferences;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.entity.api.EntityProducer;
import org.sakaiproject.entity.api.EntitySummary;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.Summary;
import org.sakaiproject.entity.cover.EntityManager;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.portal.api.PageFilter;
import org.sakaiproject.portal.api.Portal;
import org.sakaiproject.portal.api.PortalSiteHelper;
import org.sakaiproject.portal.api.SiteView;
import org.sakaiproject.portal.api.SiteView.View;
import org.sakaiproject.portal.charon.ToolHelperImpl;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SitePage;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.thread_local.cover.ThreadLocalManager;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.tool.api.Placement;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.Tool;
import org.sakaiproject.tool.cover.ToolManager;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.user.cover.PreferencesService;
import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.util.ArrayUtil;
import org.sakaiproject.util.MapUtil;
import org.sakaiproject.util.Web;
import org.sakaiproject.portal.util.ToolUtils;
/**
* @author ieb
* @since Sakai 2.4
* @version $Rev: 132914 $
*/
@SuppressWarnings("deprecation")
public class PortalSiteHelperImpl implements PortalSiteHelper
{
// Alias prefix for page aliases. Use Entity.SEPARATOR as IDs shouldn't contain it.
private static final String PAGE_ALIAS = Entity.SEPARATOR+ "pagealias"+ Entity.SEPARATOR;
private static final Log log = LogFactory.getLog(PortalSiteHelper.class);
private final String PROP_PARENT_ID = SiteService.PROP_PARENT_ID;
private static final String PROP_HTML_INCLUDE = "sakai:htmlInclude";
private static final String PROP_MENU_CLASS = "sakai:menuClass";
protected final static String CURRENT_PLACEMENT = "sakai:ToolComponent:current.placement";
private Portal portal;
private boolean lookForPageAliases;
// 2.3 back port
// private final String PROP_PARENT_ID = "sakai:parent-id";
private ToolHelperImpl toolHelper = new ToolHelperImpl();
/**
* @param portal
*/
public PortalSiteHelperImpl(Portal portal, boolean lookForPageAliases)
{
this.portal = portal;
this.lookForPageAliases = lookForPageAliases;
}
/* (non-Javadoc)
* @see org.sakaiproject.portal.api.PortalSiteHelper#doGatewaySiteList()
*/
public boolean doGatewaySiteList()
{
String gatewaySiteListPref = ServerConfigurationService
.getString("gatewaySiteList");
if (gatewaySiteListPref == null) return false;
return (gatewaySiteListPref.trim().length() > 0);
}
// Determine if we are to do multiple tabs for the anonymous view (Gateway)
/**
* @see org.sakaiproject.portal.api.PortalSiteHelper#doGatewaySiteList()
*/
public String getGatewaySiteId()
{
String gatewaySiteListPref = ServerConfigurationService
.getString("gatewaySiteList");
if (gatewaySiteListPref == null) return null;
String[] gatewaySiteIds = getGatewaySiteList();
if (gatewaySiteIds == null)
{
return null;
}
// Loop throught the sites making sure they exist and are visitable
for (int i = 0; i < gatewaySiteIds.length; i++)
{
String siteId = gatewaySiteIds[i];
Site site = null;
try
{
site = getSiteVisit(siteId);
}
catch (IdUnusedException e)
{
continue;
}
catch (PermissionException e)
{
continue;
}
if (site != null)
{
return siteId;
}
}
log.warn("No suitable gateway sites found, gatewaySiteList preference had "
+ gatewaySiteIds.length + " sites.");
return null;
}
// Return the list of tabs for the anonymous view (Gateway)
// If we have a list of sites, return that - if not simply pull in the
// single
// Gateway site
/**
* @return
*/
private String[] getGatewaySiteList()
{
String gatewaySiteListPref = ServerConfigurationService
.getString("gatewaySiteList");
if (gatewaySiteListPref == null || gatewaySiteListPref.trim().length() < 1)
{
gatewaySiteListPref = ServerConfigurationService.getGatewaySiteId();
}
if (gatewaySiteListPref == null || gatewaySiteListPref.trim().length() < 1)
return null;
String[] gatewaySites = gatewaySiteListPref.split(",");
if (gatewaySites.length < 1) return null;
return gatewaySites;
}
/*
* Get All Sites which indicate the current site as their parent
*/
// TODO: Move into SiteStructureProvider
/**
* @see org.sakaiproject.portal.api.PortalSiteHelper#getSubSites(org.sakaiproject.site.api.Site)
*/
public List<Site> getSubSites(Site site)
{
if (site == null) return null;
Map<String, String> propMap = new HashMap<String, String>();
propMap.put(PROP_PARENT_ID, site.getId());
// This should not call getUserSites(boolean) because the property is variable, while the call is cacheable otherwise
List<Site> mySites = SiteService.getSites(
org.sakaiproject.site.api.SiteService.SelectionType.ACCESS, null, null,
propMap, org.sakaiproject.site.api.SiteService.SortType.TITLE_ASC, null);
return mySites;
}
public List<Map> getSitesInContext(String context, String userId)
{
return null;
}
/**
* This method takes a list of sites and organizes it into a list of maps of
* properties. There is an additional complication that the depth contains
* informaiton arround.
*
* @see org.sakaiproject.portal.api.PortalSiteHelper#convertSitesToMaps(javax.servlet.http.HttpServletRequest,
* java.util.List, java.lang.String, java.lang.String,
* java.lang.String, boolean, boolean, boolean, boolean,
* java.lang.String, boolean)
*/
public List<Map> convertSitesToMaps(HttpServletRequest req, List mySites,
String prefix, String currentSiteId, String myWorkspaceSiteId,
boolean includeSummary, boolean expandSite, boolean resetTools,
boolean doPages, String toolContextPath, boolean loggedIn)
{
List<Map> l = new ArrayList<Map>();
Map<String, Integer> depthChart = new HashMap<String, Integer>();
boolean motdDone = false;
// We only compute the depths if there is no user chosen order
boolean computeDepth = true;
Session session = SessionManager.getCurrentSession();
if ( session != null )
{
Preferences prefs = PreferencesService.getPreferences(session.getUserId());
ResourceProperties props = prefs.getProperties("sakai:portal:sitenav");
List propList = props.getPropertyList("order");
if (propList != null)
{
computeDepth = false;
}
}
// Determine the depths of the child sites if needed
for (Iterator i = mySites.iterator(); i.hasNext();)
{
Site s = (Site) i.next();
// The first site is the current site
if (currentSiteId == null) currentSiteId = s.getId();
Integer cDepth = Integer.valueOf(0);
if ( computeDepth )
{
ResourceProperties rp = s.getProperties();
String ourParent = rp.getProperty(PROP_PARENT_ID);
// System.out.println("Depth Site:"+s.getTitle()+
// "parent="+ourParent);
if (ourParent != null)
{
Integer pDepth = depthChart.get(ourParent);
if (pDepth != null)
{
cDepth = pDepth + 1;
}
}
depthChart.put(s.getId(), cDepth);
// System.out.println("Depth = "+cDepth);
}
Map m = convertSiteToMap(req, s, prefix, currentSiteId, myWorkspaceSiteId,
includeSummary, expandSite, resetTools, doPages, toolContextPath,
loggedIn);
// Add the Depth of the site
m.put("depth", cDepth);
if (includeSummary && m.get("rssDescription") == null)
{
if (!motdDone)
{
summarizeTool(m, s, "sakai.motd");
motdDone = true;
}
else
{
summarizeTool(m, s, "sakai.announcements");
}
}
l.add(m);
}
return l;
}
/**
* SAK-23567 Gets an array with 2 int values {left,right}
* left: left percentage (before cut separator)
* right: right percentage (after separator)
*/
protected int [] getCutMethod(String siteTitleCutMethodString) {
String [] siteTitleCutMethod = siteTitleCutMethodString.split(":");
int [] cutMethod = new int[]{100,0};
try {
if (siteTitleCutMethod.length==2) {
cutMethod[0] = Integer.parseInt(siteTitleCutMethod[0]);
cutMethod[1] = Integer.parseInt(siteTitleCutMethod[1]);
if (cutMethod[0]+cutMethod[1]!=100) throw new Exception();
}
} catch (Throwable ex) {
cutMethod[0] = 100; cutMethod[1] = 0;
}
return cutMethod;
}
/**
* SAK-23567 Gets the shortened version of the title
*
* Controlled by "site.title.cut.method", "site.title.maxlength", and "site.title.cut.separator"
*
* @param fullTitle the full site title (or desc) to shorten
* @return the trimmed title
*/
protected String makeShortenedTitle(String fullTitle) {
// this method defines the defaults for the 3 configuration options
return getResumeTitle(fullTitle
,ServerConfigurationService.getString("site.title.cut.method", "100:0")
,ServerConfigurationService.getInt("site.title.maxlength", 25)
,ServerConfigurationService.getString("site.title.cut.separator", " ..."));
}
/**
* TESTING ONLY
* use {@link #makeShortenedTitle(String)} instead
*
* SAK-23567 Gets the resumed version of the title
*
* @param fullTitle
* @param cutMethod
* @param siteTitleMaxLength
* @param cutSeparator
* @return the trimmed title
*/
protected String getResumeTitle(String fullTitle,String cutMethod,int siteTitleMaxLength,String cutSeparator) {
String titleStr = fullTitle;
if ( titleStr != null ) {
titleStr = titleStr.trim();
if ( titleStr.length() > siteTitleMaxLength && siteTitleMaxLength >= 10 ) {
int [] siteTitleCutMethod = getCutMethod(cutMethod);
int begin = Math.round(((siteTitleMaxLength-cutSeparator.length())*siteTitleCutMethod[0])/100);
int end = Math.round(((siteTitleMaxLength-cutSeparator.length())*siteTitleCutMethod[1])/100);
// Adjust odd character to the begin
begin += (siteTitleMaxLength - (begin + cutSeparator.length() + end));
titleStr = ((begin>0)?titleStr.substring(0,begin):"") + cutSeparator +((end>0)?titleStr.substring(titleStr.length()-end):"");
} else if ( titleStr.length() > siteTitleMaxLength ) {
titleStr = titleStr.substring(0,siteTitleMaxLength);
}
}
return titleStr;
}
/**
* Explode a site into a map suitable for use in the map
*
* @see org.sakaiproject.portal.api.PortalSiteHelper#convertSiteToMap(javax.servlet.http.HttpServletRequest,
* org.sakaiproject.site.api.Site, java.lang.String, java.lang.String,
* java.lang.String, boolean, boolean, boolean, boolean,
* java.lang.String, boolean)
*/
public Map convertSiteToMap(HttpServletRequest req, Site s, String prefix,
String currentSiteId, String myWorkspaceSiteId, boolean includeSummary,
boolean expandSite, boolean resetTools, boolean doPages,
String toolContextPath, boolean loggedIn)
{
if (s == null) return null;
Map<String, Object> m = new HashMap<String, Object>();
// In case the effective is different than the actual site
String effectiveSite = getSiteEffectiveId(s);
boolean isCurrentSite = currentSiteId != null
&& (s.getId().equals(currentSiteId) || effectiveSite
.equals(currentSiteId));
m.put("isCurrentSite", Boolean.valueOf(isCurrentSite));
m.put("isMyWorkspace", Boolean.valueOf(myWorkspaceSiteId != null
&& (s.getId().equals(myWorkspaceSiteId) || effectiveSite
.equals(myWorkspaceSiteId))));
String fullTitle = s.getTitle();
String titleStr = makeShortenedTitle(fullTitle);
m.put("siteTitle", Web.escapeHtml(titleStr));
m.put("fullTitle", Web.escapeHtml(fullTitle));
m.put("siteDescription", s.getHtmlDescription());
if ( s.getShortDescription() !=null && s.getShortDescription().trim().length()>0 ){
// SAK-23895: Allow display of site description in the tab instead of site title
String shortDesc = s.getShortDescription();
String shortDesc_trimmed = makeShortenedTitle(shortDesc);
m.put("shortDescription", Web.escapeHtml(shortDesc_trimmed));
}
String siteUrl = Web.serverUrl(req)
+ ServerConfigurationService.getString("portalPath") + "/";
if (prefix != null) siteUrl = siteUrl + prefix + "/";
// siteUrl = siteUrl + Web.escapeUrl(siteHelper.getSiteEffectiveId(s));
m.put("siteUrl", siteUrl + Web.escapeUrl(getSiteEffectiveId(s)));
m.put("siteType", s.getType());
m.put("siteId", s.getId());
// TODO: This should come from the site neighbourhood.
ResourceProperties rp = s.getProperties();
String ourParent = rp.getProperty(PROP_PARENT_ID);
// We are not really a child unless the parent exists
// And we have a valid pwd
boolean isChild = false;
// Get the current site hierarchy
if (ourParent != null && isCurrentSite)
{
List<Site> pwd = getPwd(s, ourParent);
if (pwd != null)
{
List<Map> l = new ArrayList<Map>();
for (int i = 0; i < pwd.size(); i++)
{
Site site = pwd.get(i);
// System.out.println("PWD["+i+"]="+site.getId()+"
// "+site.getTitle());
Map<String, Object> pm = new HashMap<String, Object>();
pm.put("siteTitle", Web.escapeHtml(site.getTitle()));
pm.put("siteUrl", siteUrl + Web.escapeUrl(getSiteEffectiveId(site)));
l.add(pm);
isChild = true;
}
if ( l.size() > 0 ) m.put("pwd", l);
}
}
// If we are a child and have a non-zero length, pwd
// show breadcrumbs
if ( isChild ) {
m.put("isChild", Boolean.valueOf(isChild));
m.put("parentSite", ourParent);
}
if (includeSummary)
{
summarizeTool(m, s, "sakai.announce");
}
if (expandSite)
{
Map pageMap = pageListToMap(req, loggedIn, s, /* SitePage */null,
toolContextPath, prefix, doPages, resetTools, includeSummary);
m.put("sitePages", pageMap);
}
return m;
}
/**
* Gets the path of sites back to the root of the tree.
* @param s
* @param ourParent
* @return
*/
private List<Site> getPwd(Site s, String ourParent)
{
if (ourParent == null) return null;
// System.out.println("Getting Current Working Directory for
// "+s.getId()+" "+s.getTitle());
int depth = 0;
Vector<Site> pwd = new Vector<Site>();
Set<String> added = new HashSet<String>();
// Add us to the list at the top (will become the end)
pwd.add(s);
added.add(s.getId());
// Make sure we don't go on forever
while (ourParent != null && depth < 8)
{
depth++;
Site site = null;
try
{
site = SiteService.getSiteVisit(ourParent);
}
catch (Exception e)
{
break;
}
// We have no patience with loops
if (added.contains(site.getId())) break;
// System.out.println("Adding Parent "+site.getId()+"
// "+site.getTitle());
pwd.insertElementAt(site, 0); // Push down stack
added.add(site.getId());
ResourceProperties rp = site.getProperties();
ourParent = rp.getProperty(PROP_PARENT_ID);
}
// PWD is only defined for > 1 site
if (pwd.size() < 2) return null;
return pwd;
}
/**
* Produce a page and/or a tool list doPage = true is best for the
* tabs-based portal and for RSS - these think in terms of pages doPage =
* false is best for the portlet-style - it unrolls all of the tools unless
* a page is marked as a popup. If the page is a popup - it is left a page
* and marked as such. restTools = true - generate resetting tool URLs.
*
* @see org.sakaiproject.portal.api.PortalSiteHelper#pageListToMap(javax.servlet.http.HttpServletRequest,
* boolean, org.sakaiproject.site.api.Site,
* org.sakaiproject.site.api.SitePage, java.lang.String,
* java.lang.String, boolean, boolean, boolean)
*/
public Map pageListToMap(HttpServletRequest req, boolean loggedIn, Site site,
SitePage page, String toolContextPath, String portalPrefix, boolean doPages,
boolean resetTools, boolean includeSummary)
{
Map<String, Object> theMap = new HashMap<String, Object>();
String effectiveSiteId = getSiteEffectiveId(site);
// Should be pushed up to the API, similar to server configiuration service, but supporting an Enum(always, never, true, false).
boolean showHelp = true;
// Supports true, false, never, always
String showHelpGlobal = ServerConfigurationService.getString("display.help.menu", "true");
if ("never".equals(showHelp))
{
showHelp = false;
}
else if ("always".equals(showHelpGlobal))
{
showHelp = true;
}
else
{
showHelp = Boolean.valueOf(showHelpGlobal).booleanValue();
String showHelpSite = site.getProperties().getProperty("display-help-menu");
if (showHelpSite != null)
{
showHelp = Boolean.valueOf(showHelpSite).booleanValue();
}
}
String iconUrl = "";
try {
if (site.getIconUrlFull() != null)
iconUrl = new URI(site.getIconUrlFull()).toString();
} catch (URISyntaxException uex) {
log.debug("Icon URL is invalid: " + site.getIconUrlFull());
}
boolean published = site.isPublished();
String type = site.getType();
theMap.put("siteId", site.getId());
theMap.put("pageNavPublished", Boolean.valueOf(published));
theMap.put("pageNavType", type);
theMap.put("pageNavIconUrl", iconUrl);
String htmlInclude = site.getProperties().getProperty(PROP_HTML_INCLUDE);
if (htmlInclude != null) theMap.put("siteHTMLInclude", htmlInclude);
// theMap.put("pageNavSitToolsHead",
// Web.escapeHtml(rb.getString("sit_toolshead")));
// order the pages based on their tools and the tool order for the
// site type
// List pages = site.getOrderedPages();
List pages = getPermittedPagesInOrder(site);
List<Map> l = new ArrayList<Map>();
String addMoreToolsUrl = null;
for (Iterator i = pages.iterator(); i.hasNext();)
{
SitePage p = (SitePage) i.next();
// check if current user has permission to see page
// one tool on the page
List<ToolConfiguration> pTools = p.getTools();
ToolConfiguration firstTool = null;
String toolsOnPage = null;
// Check the tools that indicate the portal is to do the popup
Iterator<ToolConfiguration> toolz = pTools.iterator();
String source = null;
int count = 0;
ToolConfiguration pageTool = null;
while(toolz.hasNext()){
count++;
pageTool = toolz.next();
source = ToolUtils.getToolPopupUrl(pageTool);
if ( "sakai.siteinfo".equals(pageTool.getToolId()) ) {
addMoreToolsUrl = ToolUtils.getPageUrl(req, site, p, portalPrefix,
resetTools, effectiveSiteId, null);
addMoreToolsUrl += "?sakai_action=doMenu_edit_site_tools&panel=Shortcut";
}
}
if ( count != 1 ) {
source = null;
addMoreToolsUrl = null;
pageTool = null;
}
boolean current = (page != null && p.getId().equals(page.getId()) && !p
.isPopUp());
String pageAlias = lookupPageToAlias(site.getId(), p);
String pagerefUrl = ToolUtils.getPageUrl(req, site, p, portalPrefix,
resetTools, effectiveSiteId, pageAlias);
if (doPages || p.isPopUp())
{
Map<String, Object> m = new HashMap<String, Object>();
StringBuffer desc = new StringBuffer();
boolean hidden = false;
if (pTools != null && pTools.size() > 0) {
firstTool = pTools.get(0);
hidden = true; // Only set the page to hidden when we have tools that might un-hide it.
Iterator<ToolConfiguration> tools = pTools.iterator();
//get the tool descriptions for this page, typically only one per page, execpt for the Home page
int tCount = 0;
while(tools.hasNext()){
ToolConfiguration t = tools.next();
if (hidden && !isHidden(t))
{
hidden = false;
}
if (tCount > 0){
desc.append(" | ");
}
if ( t.getTool() == null ) continue;
desc.append(t.getTool().getDescription());
tCount++;
}
}
boolean siteUpdate = SecurityService.unlock("site.upd", site.getReference());
if ( ! siteUpdate ) addMoreToolsUrl = null;
if ( ! ServerConfigurationService.getBoolean("portal.experimental.addmoretools", false) ) addMoreToolsUrl = null;
String pagePopupUrl = Web.returnUrl(req, "/page/");
m.put("isPage", Boolean.valueOf(true));
m.put("current", Boolean.valueOf(current));
m.put("ispopup", Boolean.valueOf(p.isPopUp()));
m.put("pagePopupUrl", pagePopupUrl);
m.put("pageTitle", Web.escapeHtml(p.getTitle()));
m.put("jsPageTitle", Web.escapeJavascript(p.getTitle()));
m.put("pageId", Web.escapeUrl(p.getId()));
m.put("jsPageId", Web.escapeJavascript(p.getId()));
m.put("pageRefUrl", pagerefUrl);
m.put("toolpopup", Boolean.valueOf(source!=null));
m.put("toolpopupurl", source);
// TODO: Should have Web.escapeHtmlAttribute()
String description = desc.toString().replace("\"",""");
m.put("description", description);
m.put("hidden", Boolean.valueOf(hidden));
// toolsOnPage is always null
//if (toolsOnPage != null) m.put("toolsOnPage", toolsOnPage);
if (includeSummary) summarizePage(m, site, p);
if (firstTool != null)
{
String menuClass = firstTool.getToolId();
menuClass = "icon-" + menuClass.replace('.', '-');
m.put("menuClass", menuClass);
Properties tmp = firstTool.getConfig();
if ( tmp != null ) {
String mc = tmp.getProperty(PROP_MENU_CLASS);
if ( mc != null && mc.length() > 0 ) m.put("menuClassOverride", mc);
}
}
else
{
m.put("menuClass", "icon-default-tool");
}
m.put("pageProps", createPageProps(p));
// this is here to allow the tool reorder to work
m.put("_sitePage", p);
l.add(m);
continue;
}
String toolUrl = Web.returnUrl(req, "/" + portalPrefix + "/"
+ Web.escapeUrl(getSiteEffectiveId(site)));
if (resetTools) {
toolUrl = toolUrl + "/tool-reset/";
} else {
toolUrl = toolUrl + "/tool/";
}
// Loop through the tools again and Unroll the tools
Iterator iPt = pTools.iterator();
while (iPt.hasNext())
{
ToolConfiguration placement = (ToolConfiguration) iPt.next();
Tool tool = placement.getTool();
if (tool != null)
{
String toolrefUrl = toolUrl + Web.escapeUrl(placement.getId());
Map<String, Object> m = new HashMap<String, Object>();
m.put("isPage", Boolean.valueOf(false));
m.put("toolId", Web.escapeUrl(placement.getId()));
m.put("jsToolId", Web.escapeJavascript(placement.getId()));
m.put("toolRegistryId", placement.getToolId());
m.put("toolTitle", Web.escapeHtml(placement.getTitle()));
m.put("jsToolTitle", Web.escapeJavascript(placement.getTitle()));
m.put("toolrefUrl", toolrefUrl);
m.put("toolpopup", Boolean.valueOf(source!=null));
m.put("toolpopupurl", source);
String menuClass = placement.getToolId();
menuClass = "icon-" + menuClass.replace('.', '-');
m.put("menuClass", menuClass);
Properties tmp = placement.getConfig();
if ( tmp != null ) {
String mc = tmp.getProperty(PROP_MENU_CLASS);
if ( mc != null && mc.length() > 0 ) m.put("menuClassOverride", mc);
}
// this is here to allow the tool reorder to work if requried.
m.put("_placement", placement);
l.add(m);
}
}
}
PageFilter pageFilter = portal.getPageFilter();
if (pageFilter != null)
{
l = pageFilter.filterPlacements(l, site);
}
if ( addMoreToolsUrl != null ) {
theMap.put("pageNavAddMoreToolsUrl", addMoreToolsUrl);
theMap.put("pageNavCanAddMoreTools", true);
} else {
theMap.put("pageNavCanAddMoreTools", false);
}
theMap.put("pageNavTools", l);
theMap.put("pageMaxIfSingle", ServerConfigurationService.getBoolean(
"portal.experimental.maximizesinglepage", false));
theMap.put("pageNavToolsCount", Integer.valueOf(l.size()));
String helpUrl = ServerConfigurationService.getHelpUrl(null);
theMap.put("pageNavShowHelp", Boolean.valueOf(showHelp));
theMap.put("pageNavHelpUrl", helpUrl);
theMap.put("helpMenuClass", "icon-sakai-help");
theMap.put("subsiteClass", "icon-sakai-subsite");
// theMap.put("pageNavSitContentshead",
// Web.escapeHtml(rb.getString("sit_contentshead")));
// Display presence? Global property display.users.present may be always / never / true / false
// If true or false, the value may be overriden by the site property display-users-present
// which may be true or false.
boolean showPresence;
String globalShowPresence = ServerConfigurationService.getString("display.users.present","true");
if ("never".equals(globalShowPresence)) {
showPresence = false;
} else if ("always".equals(globalShowPresence)) {
showPresence = true;
} else {
showPresence = Boolean.valueOf(globalShowPresence).booleanValue();
String showPresenceSite = site.getProperties().getProperty("display-users-present");
if (showPresenceSite != null)
{
showPresence = Boolean.valueOf(showPresenceSite).booleanValue();
}
}
// Check to see if this is a my workspace site, and if so, whether presence is disabled
if (showPresence && SiteService.isUserSite(site.getId()) && !ServerConfigurationService.getBoolean("display.users.present.myworkspace", false))
showPresence = false;
String presenceUrl = Web.returnUrl(req, "/presence/"
+ Web.escapeUrl(site.getId()));
// theMap.put("pageNavSitPresenceTitle",
// Web.escapeHtml(rb.getString("sit_presencetitle")));
// theMap.put("pageNavSitPresenceFrameTitle",
// Web.escapeHtml(rb.getString("sit_presenceiframetit")));
theMap.put("pageNavShowPresenceLoggedIn", Boolean.valueOf(showPresence
&& loggedIn));
theMap.put("pageNavPresenceUrl", presenceUrl);
//add softly deleted status
theMap.put("softlyDeleted", site.isSoftlyDeleted());
// Retrieve whether or not we are to put presence in a frame
theMap.put("pageNavPresenceIframe", Boolean.valueOf(
ServerConfigurationService.getBoolean("display.users.present.iframe", false)) );
theMap.put("sakaiPresenceTimeDelay", Integer.valueOf(
ServerConfigurationService.getInt("display.users.present.time.delay", 3000)) );
return theMap;
}
/**
* @param p
* @return
*/
private Map createPageProps(SitePage p)
{
Map properties = new HashMap();
for (Iterator<String> i = p.getProperties().getPropertyNames(); i.hasNext();)
{
String propName = i.next();
properties.put(propName, p.getProperties().get(propName));
}
return properties;
}
/**
* @see org.sakaiproject.portal.api.PortalSiteHelper#getMyWorkspace(org.sakaiproject.tool.api.Session)
*/
public Site getMyWorkspace(Session session)
{
String siteId = SiteService.getUserSiteId(session.getUserId());
// Make sure we can visit
Site site = null;
try
{
site = getSiteVisit(siteId);
}
catch (IdUnusedException e)
{
site = null;
}
catch (PermissionException e)
{
site = null;
}
return site;
}
/*
* Temporarily set a placement with the site id as the context - we do not
* set a tool ID this will not be a rich enough placement to do *everything*
* but for those services which call
* ToolManager.getCurrentPlacement().getContext() to contextualize their
* information - it wil be sufficient.
*/
public boolean setTemporaryPlacement(Site site)
{
if (site == null) return false;
Placement ppp = ToolManager.getCurrentPlacement();
if (ppp != null && site.getId().equals(ppp.getContext()))
{
return true;
}
// Create a site-only placement
Placement placement = new org.sakaiproject.util.Placement("portal-temporary", /* toolId */
null, /* tool */null,
/* config */null, /* context */site.getId(), /* title */null);
ThreadLocalManager.set(CURRENT_PLACEMENT, placement);
// Debugging
ppp = ToolManager.getCurrentPlacement();
if (ppp == null)
{
System.out.println("WARNING portal-temporary placement not set - null");
}
else
{
String cont = ppp.getContext();
if (site.getId().equals(cont))
{
return true;
}
else
{
System.out.println("WARNING portal-temporary placement mismatch site="
+ site.getId() + " context=" + cont);
}
}
return false;
}
public boolean summarizePage(Map m, Site site, SitePage page)
{
List pTools = page.getTools();
Iterator iPt = pTools.iterator();
while (iPt.hasNext())
{
ToolConfiguration placement = (ToolConfiguration) iPt.next();
if (summarizeTool(m, site, placement.getToolId()))
{
return true;
}
}
return false;
}
/**
* There must be a better way of doing this as this hard codes the services
* in surely there should be some whay of looking up the serivce and making
* the getSummary part of an interface. TODO: Add an interface beside
* EntityProducer to generate summaries Make this discoverable
*
* @param m
* @param site
* @param toolIdentifier
* @return
*/
private boolean summarizeTool(Map m, Site site, String toolIdentifier)
{
if (site == null) return false;
setTemporaryPlacement(site);
Map newMap = null;
/*
* This is a new, cooler way to do this (I hope) chmaurer... (ieb) Yes:)
* All summaries now through this interface
*/
// offer to all EntityProducers
for (Iterator i = EntityManager.getEntityProducers().iterator(); i.hasNext();)
{
EntityProducer ep = (EntityProducer) i.next();
if (ep instanceof EntitySummary)
{
try
{
EntitySummary es = (EntitySummary) ep;
// if this producer claims this tool id
if (ArrayUtil.contains(es.summarizableToolIds(), toolIdentifier))
{
String summarizableReference = es.getSummarizableReference(site
.getId(), toolIdentifier);
newMap = es.getSummary(summarizableReference, 5, 30);
}
}
catch (Throwable t)
{
log.warn(
"Error encountered while asking EntitySummary to getSummary() for: "
+ toolIdentifier, t);
}
}
}
if (newMap != null)
{
return (MapUtil.copyHtml(m, "rssDescription", newMap,
Summary.PROP_DESCRIPTION) && MapUtil.copy(m, "rssPubdate", newMap,
Summary.PROP_PUBDATE));
}
else
{
Time modDate = site.getModifiedTime();
// Yes, some sites have never been modified
if (modDate != null)
{
m.put("rssPubDate", (modDate.toStringRFC822Local()));
}
return false;
}
}
/**
* If this is a user site, return an id based on the user EID, otherwise
* just return the site id.
*
* @param site
* The site.
* @return The effective site id.
*/
public String getSiteEffectiveId(Site site)
{
if (SiteService.isUserSite(site.getId()))
{
try
{
String userId = SiteService.getSiteUserId(site.getId());
String eid = UserDirectoryService.getUserEid(userId);
return SiteService.getUserSiteId(eid);
}
catch (UserNotDefinedException e)
{
log.warn("getSiteEffectiveId: user eid not found for user site: "
+ site.getId());
}
}
else
{
String displayId = portal.getSiteNeighbourhoodService().lookupSiteAlias(site.getReference(), null);
if (displayId != null)
{
return displayId;
}
}
return site.getId();
}
/**
* Do the getSiteVisit, but if not found and the id is a user site, try
* translating from user EID to ID.
*
* @param siteId
* The Site Id.
* @return The Site.
* @throws PermissionException
* If not allowed.
* @throws IdUnusedException
* If not found.
*/
public Site getSiteVisit(String siteId) throws PermissionException, IdUnusedException
{
try
{
return SiteService.getSiteVisit(siteId);
}
catch (IdUnusedException e)
{
if (SiteService.isUserSite(siteId))
{
try
{
String userEid = SiteService.getSiteUserId(siteId);
String userId = UserDirectoryService.getUserId(userEid);
String alternateSiteId = SiteService.getUserSiteId(userId);
return SiteService.getSiteVisit(alternateSiteId);
}
catch (UserNotDefinedException ee)
{
}
}
else
{
String reference = portal.getSiteNeighbourhoodService().parseSiteAlias(siteId);
Reference ref = EntityManager.getInstance().newReference(reference);
try
{
return SiteService.getSiteVisit(ref.getId());
}
catch (IdUnusedException iue)
{
}
}
// re-throw if that didn't work
throw e;
}
}
/**
* Retrieve the list of pages in this site, checking to see if the user has
* permission to see the page - by checking the permissions of tools on the
* page.
*
* @param site
* @return
*/
public List getPermittedPagesInOrder(Site site)
{
// Get all of the pages
List pages = site.getOrderedPages();
boolean siteUpdate = SecurityService.unlock("site.upd", site.getReference());
List newPages = new ArrayList();
for (Iterator i = pages.iterator(); i.hasNext();)
{
// check if current user has permission to see page
SitePage p = (SitePage) i.next();
List pTools = p.getTools();
Iterator iPt = pTools.iterator();
boolean allowPage = false;
while (iPt.hasNext())
{
ToolConfiguration placement = (ToolConfiguration) iPt.next();
boolean thisTool = allowTool(site, placement);
boolean unHidden = siteUpdate || ! isHidden(placement);
if (thisTool && unHidden) allowPage = true;
}
if (allowPage) newPages.add(p);
}
PageFilter pageFilter = portal.getPageFilter();
if (pageFilter != null)
{
newPages = pageFilter.filter(newPages, site);
}
return newPages;
}
/**
* Make sure that we have a proper page selected in the site pageid is
* generally the last page used in the site. pageId must be in the site and
* the user must have permission for the page as well.
*
* @see org.sakaiproject.portal.api.PortalSiteHelper#lookupSitePage(java.lang.String,
* org.sakaiproject.site.api.Site)
*/
public SitePage lookupSitePage(String pageId, Site site)
{
// Make sure we have some permitted pages
List pages = getPermittedPagesInOrder(site);
if (pages.isEmpty()) return null;
SitePage page = site.getPage(pageId);
if (page == null)
{
page = lookupAliasToPage(pageId, site);
if (page == null)
{
page = (SitePage) pages.get(0);
return page;
}
}
// Make sure that they user has permission for the page.
// If the page is not in the permitted list go to the first
// page.
boolean found = false;
for (Iterator i = pages.iterator(); i.hasNext();)
{
SitePage p = (SitePage) i.next();
if (p.getId().equals(page.getId())) return page;
}
return (SitePage) pages.get(0);
}
public SitePage lookupAliasToPage(String alias, Site site)
{
//Shortcut if we aren't using page aliases.
if (!lookForPageAliases)
{
return null;
}
SitePage page = null;
if (alias != null && alias.length() > 0)
{
try
{
// Use page#{siteId}:{pageAlias} So we can scan for fist colon and alias can contain any character
String refString = AliasService.getTarget(buildAlias(alias, site));
String aliasPageId = EntityManager.newReference(refString).getId();
page = (SitePage) site.getPage(aliasPageId);
}
catch (IdUnusedException e)
{
if (log.isDebugEnabled())
{
log.debug("Alias does not resolve " + e.getMessage());
}
}
}
return page;
}
public String lookupPageToAlias(String siteId, SitePage page)
{
// Shortcut if we aren't using page aliases.
if (!lookForPageAliases)
{
return null;
}
String alias = null;
List<Alias> aliases = AliasService.getAliases(page.getReference());
if (aliases.size() > 0)
{
if (aliases.size() > 1 && log.isWarnEnabled())
{
log.warn("More than one alias for: "+siteId+ ":"+ page.getId());
// Sort on ID so it is consistent in the alias it uses.
Collections.sort(aliases, getAliasComparator());
}
alias = aliases.get(0).getId();
alias = parseAlias(alias, siteId);
}
return alias;
}
/**
* Find the short alias.
* @param alias
* @return
*/
private String parseAlias(String aliasId, String siteId)
{
String prefix = PAGE_ALIAS+ siteId+ Entity.SEPARATOR;
String alias = null;
if (aliasId.startsWith(prefix))
{
alias = aliasId.substring(prefix.length());
}
return alias;
}
private String buildAlias(String alias, Site site)
{
return PAGE_ALIAS+site.getId()+Entity.SEPARATOR+alias;
}
private Comparator<Alias> getAliasComparator()
{
return new Comparator<Alias>() {
public int compare(Alias o1, Alias o2)
{
// Sort by date, then by ID to assure consistent order.
return o1.getCreatedTime().compareTo(o2.getCreatedTime()) * 10 +
o1.getId().compareTo(o2.getId());
}
};
}
/**
* @see org.sakaiproject.portal.api.PortalSiteHelper#allowTool(org.sakaiproject.site.api.Site,
* org.sakaiproject.tool.api.Placement)
*/
public boolean allowTool(Site site, Placement placement)
{
return toolHelper.allowTool(site, placement);
}
/**
* Check to see if a tool placement is hidden.
* Can be used to check is a page should be hidden.
*/
public boolean isHidden(Placement placement)
{
return toolHelper.isHidden(placement);
}
/*
* (non-Javadoc)
*
* @see org.sakaiproject.portal.api.PortalSiteHelper#getSitesView(org.sakaiproject.portal.api.SiteView.View,
* javax.servlet.http.HttpServletRequest,
* org.sakaiproject.tool.api.Session, java.lang.String)
*/
public SiteView getSitesView(View view, HttpServletRequest request, Session session,
String siteId)
{
switch (view)
{
case CURRENT_SITE_VIEW:
return new CurrentSiteViewImpl(this, portal.getSiteNeighbourhoodService(), request, session, siteId, SiteService
.getInstance(), ServerConfigurationService.getInstance(),
PreferencesService.getInstance());
case ALL_SITES_VIEW:
return new AllSitesViewImpl(this, portal.getSiteNeighbourhoodService(), request, session, siteId, SiteService
.getInstance(), ServerConfigurationService.getInstance(),
PreferencesService.getInstance());
case DEFAULT_SITE_VIEW:
return new DefaultSiteViewImpl(this, portal.getSiteNeighbourhoodService(), request, session, siteId, SiteService
.getInstance(), ServerConfigurationService.getInstance(),
PreferencesService.getInstance());
case DHTML_MORE_VIEW:
return new MoreSiteViewImpl(this,portal.getSiteNeighbourhoodService(), request, session, siteId, SiteService
.getInstance(), ServerConfigurationService.getInstance(),
PreferencesService.getInstance());
case SUB_SITES_VIEW:
return new SubSiteViewImpl(this, portal.getSiteNeighbourhoodService(), request, session, siteId, SiteService
.getInstance(), ServerConfigurationService.getInstance(),
PreferencesService.getInstance());
}
return null;
}
public boolean isJoinable(String siteId, String userId) {
Site site = null;
try {
site = getSite(siteId);
} catch (IdUnusedException e) {
}
return site != null && site.isJoinable() && site.getUserRole(userId) == null;
}
public Site getSite(String siteId) throws IdUnusedException
{
Site site = null;
try {
site = SiteService.getInstance().getSite(siteId);
return site;
} catch (IdUnusedException e) {
// Attempt to lookup by alias.
String reference = portal.getSiteNeighbourhoodService().parseSiteAlias(siteId);
if (reference != null)
{
Reference ref = EntityManager.getInstance().newReference(reference);
try
{
site = SiteService.getInstance().getSite(ref.getId());
return site;
}
catch (IdUnusedException e2)
{
}
}
throw e;
}
}
}