001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/portal/tags/sakai_2-4-1/portal-util/util/src/java/org/sakaiproject/portal/util/PortalSiteHelper.java $
003: * $Id: PortalSiteHelper.java 29143 2007-04-19 01:10:38Z ajpoland@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006, 2007 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.portal.util;
021:
022: import java.io.IOException;
023: import java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Properties;
028:
029: import javax.servlet.http.HttpServletRequest;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.sakaiproject.announcement.cover.AnnouncementService;
034: import org.sakaiproject.authz.cover.SecurityService;
035: import org.sakaiproject.component.cover.ServerConfigurationService;
036: import org.sakaiproject.discussion.cover.DiscussionService;
037: import org.sakaiproject.entity.api.EntityProducer;
038: import org.sakaiproject.entity.api.EntitySummary;
039: import org.sakaiproject.entity.api.ResourceProperties;
040: import org.sakaiproject.entity.api.Summary;
041: import org.sakaiproject.entity.cover.EntityManager;
042: import org.sakaiproject.exception.IdUnusedException;
043: import org.sakaiproject.exception.PermissionException;
044: import org.sakaiproject.site.api.Site;
045: import org.sakaiproject.site.api.SitePage;
046: import org.sakaiproject.site.api.ToolConfiguration;
047: import org.sakaiproject.site.cover.SiteService;
048: import org.sakaiproject.thread_local.cover.ThreadLocalManager;
049: import org.sakaiproject.tool.api.Placement;
050: import org.sakaiproject.tool.api.Session;
051: import org.sakaiproject.tool.cover.ToolManager;
052: import org.sakaiproject.user.api.Preferences;
053: import org.sakaiproject.user.api.UserNotDefinedException;
054: import org.sakaiproject.user.cover.PreferencesService;
055: import org.sakaiproject.user.cover.UserDirectoryService;
056: import org.sakaiproject.util.ArrayUtil;
057: import org.sakaiproject.util.MapUtil;
058:
059: /**
060: * @author ieb
061: * @since Sakai 2.4
062: * @version $Rev: 29143 $
063: */
064:
065: public class PortalSiteHelper {
066:
067: private static final Log log = LogFactory
068: .getLog(PortalSiteHelper.class);
069:
070: // Determine if we are to do multiple tabs for the anonymous view (Gateway)
071: public boolean doGatewaySiteList() {
072: String gatewaySiteListPref = ServerConfigurationService
073: .getString("gatewaySiteList");
074: if (gatewaySiteListPref == null)
075: return false;
076: return (gatewaySiteListPref.trim().length() > 0);
077: }
078:
079: // Return the list of tabs for the anonymous view (Gateway)
080: // If we have a list of sites, return that - if not simply pull in the
081: // single
082: // Gateway site
083: private String[] getGatewaySiteList() {
084: String gatewaySiteListPref = ServerConfigurationService
085: .getString("gatewaySiteList");
086:
087: if (gatewaySiteListPref == null
088: || gatewaySiteListPref.trim().length() < 1) {
089: gatewaySiteListPref = ServerConfigurationService
090: .getGatewaySiteId();
091: }
092: if (gatewaySiteListPref == null
093: || gatewaySiteListPref.trim().length() < 1)
094: return null;
095:
096: String[] gatewaySites = gatewaySiteListPref.split(",");
097: if (gatewaySites.length < 1)
098: return null;
099:
100: return gatewaySites;
101: }
102:
103: // Get the sites which are to be displayed for the gateway
104: private List<Site> getGatewaySites() {
105: List<Site> mySites = new ArrayList<Site>();
106: String[] gatewaySiteIds = getGatewaySiteList();
107: if (gatewaySiteIds == null) {
108: return mySites; // An empty list - deal with this higher up in the
109: // food chain
110: }
111:
112: // Loop throught the sites making sure they exist and are visitable
113: for (int i = 0; i < gatewaySiteIds.length; i++) {
114: String siteId = gatewaySiteIds[i];
115:
116: Site site = null;
117: try {
118: site = getSiteVisit(siteId);
119: } catch (IdUnusedException e) {
120: continue;
121: } catch (PermissionException e) {
122: continue;
123: }
124:
125: if (site != null) {
126: mySites.add(site);
127: }
128: }
129:
130: if (mySites.size() < 1) {
131: log
132: .warn("No suitable gateway sites found, gatewaySiteList preference had "
133: + gatewaySiteIds.length + " sites.");
134: }
135: return mySites;
136: }
137:
138: /*
139: * Get All Sites for the current user. If the user is not logged in we
140: * return the list of publically viewable gateway sites. @param
141: * includeMyWorkspace When this is true - include the user's My Workspace as
142: * the first parameter. If false, do not include the MyWorkspace anywhere in
143: * the list. Some uses - such as the portlet styled portal or the rss styled
144: * portal simply want all of the sites with the MyWorkspace first. Other
145: * portals like the basic tabbed portal treats My Workspace separately from
146: * all of the rest of the workspaces.
147: */
148:
149: public List<Site> getAllSites(HttpServletRequest req,
150: Session session, boolean includeMyWorkspace)
151: throws IOException {
152:
153: boolean loggedIn = session.getUserId() != null;
154:
155: // Get the list of sites in the right order
156: List<Site> mySites;
157: if (!loggedIn) {
158: // collect the Publically Viewable Sites
159: mySites = getGatewaySites();
160: } else {
161: // collect the user's sites
162: mySites = SiteService
163: .getSites(
164: org.sakaiproject.site.api.SiteService.SelectionType.ACCESS,
165: null,
166: null,
167: null,
168: org.sakaiproject.site.api.SiteService.SortType.TITLE_ASC,
169: null);
170:
171: // collect the user's preferences
172: List prefExclude = new ArrayList();
173: List prefOrder = new ArrayList();
174: if (session.getUserId() != null) {
175: Preferences prefs = PreferencesService
176: .getPreferences(session.getUserId());
177: ResourceProperties props = prefs
178: .getProperties("sakai:portal:sitenav");
179:
180: List l = props.getPropertyList("exclude");
181: if (l != null) {
182: prefExclude = l;
183: }
184:
185: l = props.getPropertyList("order");
186: if (l != null) {
187: prefOrder = l;
188: }
189: }
190:
191: // remove all in exclude from mySites
192: mySites.removeAll(prefExclude);
193:
194: // Prepare to put sites in the right order
195: List<Site> ordered = new ArrayList<Site>();
196:
197: // First, place or remove MyWorkspace as requested
198: Site myWorkspace = getMyWorkspace(session);
199: if (myWorkspace != null) {
200: if (includeMyWorkspace) {
201: ordered.add(myWorkspace);
202: } else {
203: int pos = listIndexOf(myWorkspace.getId(), mySites);
204: if (pos != -1)
205: mySites.remove(pos);
206: }
207: }
208:
209: // re-order mySites to have order first, the rest later
210: for (Iterator i = prefOrder.iterator(); i.hasNext();) {
211: String id = (String) i.next();
212:
213: // find this site in the mySites list
214: int pos = listIndexOf(id, mySites);
215: if (pos != -1) {
216: // move it from mySites to order
217: Site s = mySites.get(pos);
218: ordered.add(s);
219: mySites.remove(pos);
220: }
221: }
222:
223: // pick up the rest of the sites
224: ordered.addAll(mySites);
225: mySites = ordered;
226: } // End if ( loggedIn )
227:
228: /*
229: * for (Iterator i = mySites.iterator(); i.hasNext();) { Site s = (Site)
230: * i.next(); System.out.println("Site:"+Web.escapeHtml(s.getTitle())+"
231: * id="+s.getId()); }
232: */
233: return mySites;
234: }
235:
236: public Site getMyWorkspace(Session session) {
237: String siteId = SiteService.getUserSiteId(session.getUserId());
238:
239: // Make sure we can visit
240: Site site = null;
241: try {
242: site = getSiteVisit(siteId);
243: } catch (IdUnusedException e) {
244: site = null;
245: } catch (PermissionException e) {
246: site = null;
247: }
248:
249: return site;
250: }
251:
252: protected final static String CURRENT_PLACEMENT = "sakai:ToolComponent:current.placement";
253:
254: /*
255: * Temporarily set a placement with the site id as the context - we do not
256: * set a tool ID this will not be a rich enough placement to do *everything*
257: * but for those services which call
258: * ToolManager.getCurrentPlacement().getContext() to contextualize their
259: * information - it wil be sufficient.
260: */
261:
262: public boolean setTemporaryPlacement(Site site) {
263: if (site == null)
264: return false;
265:
266: Placement ppp = ToolManager.getCurrentPlacement();
267: if (ppp != null && site.getId().equals(ppp.getContext())) {
268: return true;
269: }
270:
271: // Create a site-only placement
272: Placement placement = new org.sakaiproject.util.Placement(
273: "portal-temporary", /* toolId */
274: null, /* tool */null,
275: /* config */null, /* context */site.getId(), /* title */
276: null);
277:
278: ThreadLocalManager.set(CURRENT_PLACEMENT, placement);
279:
280: // Debugging
281: ppp = ToolManager.getCurrentPlacement();
282: if (ppp == null) {
283: System.out
284: .println("WARNING portal-temporary placement not set - null");
285: } else {
286: String cont = ppp.getContext();
287: if (site.getId().equals(cont)) {
288: return true;
289: } else {
290: System.out
291: .println("WARNING portal-temporary placement mismatch site="
292: + site.getId() + " context=" + cont);
293: }
294: }
295: return false;
296: }
297:
298: public boolean summarizePage(Map m, Site site, SitePage page) {
299: List pTools = page.getTools();
300: Iterator iPt = pTools.iterator();
301: while (iPt.hasNext()) {
302: ToolConfiguration placement = (ToolConfiguration) iPt
303: .next();
304:
305: if (summarizeTool(m, site, placement.getToolId())) {
306: return true;
307: }
308: }
309: return false;
310: }
311:
312: /*
313: * There must be a better way of doing this as this hard codes the services
314: * in surely there should be some whay of looking up the serivce and making
315: * the getSummary part of an interface. TODO: Add an interface beside
316: * EntityProducer to generate summaries Make this discoverable
317: */
318: public boolean summarizeTool(Map m, Site site, String toolIdentifier) {
319: if (site == null)
320: return false;
321:
322: setTemporaryPlacement(site);
323: Map newMap = null;
324: if ("sakai.motd".equals(toolIdentifier)) {
325: try {
326: String channel = "/announcement/channel/!site/motd";
327: newMap = AnnouncementService.getSummary(channel, 5, 30);
328: } catch (Exception e) {
329: newMap = null;
330: }
331: }
332: /*
333: * else if ("sakai.chat".equals(toolIdentifier)) { //This is now being
334: * done by the EntitySummary stuff - chmaurer 3/2007 try { String
335: * channel = ChatService.getInstance().channelReference(site.getId(),
336: * SiteService.MAIN_CONTAINER); newMap = ChatService.getSummary(channel,
337: * 5, 30); } catch (Exception e) { newMap = null; } }
338: */
339: else if ("sakai.discussion".equals(toolIdentifier)) {
340: try {
341: String channel = DiscussionService.getInstance()
342: .channelReference(site.getId(),
343: SiteService.MAIN_CONTAINER);
344: newMap = DiscussionService.getSummary(channel, 5, 30);
345: } catch (Exception e) {
346: newMap = null;
347: }
348: } else if ("sakai.announcements".equals(toolIdentifier)) {
349: try {
350: String channel = AnnouncementService.getInstance()
351: .channelReference(site.getId(),
352: SiteService.MAIN_CONTAINER);
353: newMap = AnnouncementService.getSummary(channel, 5, 30);
354: } catch (Exception e) {
355: newMap = null;
356: }
357: }
358:
359: /*
360: * This is a new, cooler way to do this (I hope) chmaurer...
361: */
362:
363: // offer to all EntityProducers
364: for (Iterator i = EntityManager.getEntityProducers().iterator(); i
365: .hasNext();) {
366: EntityProducer ep = (EntityProducer) i.next();
367: if (ep instanceof EntitySummary) {
368: try {
369: EntitySummary es = (EntitySummary) ep;
370:
371: // if this producer claims this tool id
372: if (ArrayUtil.contains(es.summarizableToolIds(),
373: toolIdentifier)) {
374: String summarizableReference = es
375: .getSummarizableReference(site.getId());
376: es.getSummary(summarizableReference, 5, 30);
377: }
378: } catch (Throwable t) {
379: log.warn(
380: "Error encountered while asking EntitySummary to getSummary() for: "
381: + toolIdentifier, t);
382: }
383: }
384: }
385:
386: if (newMap != null) {
387: return (MapUtil.copyHtml(m, "rssDescription", newMap,
388: Summary.PROP_DESCRIPTION) && MapUtil.copy(m,
389: "rssPubdate", newMap, Summary.PROP_PUBDATE));
390: } else {
391: m.put("rssPubDate", (site.getModifiedTime()
392: .toStringRFC822Local()));
393: return false;
394: }
395:
396: }
397:
398: /**
399: * If this is a user site, return an id based on the user EID, otherwise
400: * just return the site id.
401: *
402: * @param site
403: * The site.
404: * @return The effective site id.
405: */
406: public String getSiteEffectiveId(Site site) {
407: if (SiteService.isUserSite(site.getId())) {
408: try {
409: String userId = SiteService.getSiteUserId(site.getId());
410: String eid = UserDirectoryService.getUserEid(userId);
411: return SiteService.getUserSiteId(eid);
412: } catch (UserNotDefinedException e) {
413: log
414: .warn("getSiteEffectiveId: user eid not found for user site: "
415: + site.getId());
416: }
417: }
418:
419: return site.getId();
420: }
421:
422: /**
423: * Do the getSiteVisit, but if not found and the id is a user site, try
424: * translating from user EID to ID.
425: *
426: * @param siteId
427: * The Site Id.
428: * @return The Site.
429: * @throws PermissionException
430: * If not allowed.
431: * @throws IdUnusedException
432: * If not found.
433: */
434: public Site getSiteVisit(String siteId) throws PermissionException,
435: IdUnusedException {
436: try {
437: return SiteService.getSiteVisit(siteId);
438: } catch (IdUnusedException e) {
439: if (SiteService.isUserSite(siteId)) {
440: try {
441: String userEid = SiteService.getSiteUserId(siteId);
442: String userId = UserDirectoryService
443: .getUserId(userEid);
444: String alternateSiteId = SiteService
445: .getUserSiteId(userId);
446: return SiteService.getSiteVisit(alternateSiteId);
447: } catch (UserNotDefinedException ee) {
448: }
449: }
450:
451: // re-throw if that didn't work
452: throw e;
453: }
454: }
455:
456: /**
457: * Find the site in the list that has this id - return the position.
458: *
459: * @param value
460: * The site id to find.
461: * @param siteList
462: * The list of Site objects.
463: * @return The index position in siteList of the site with site id = value,
464: * or -1 if not found.
465: */
466: private int listIndexOf(String value, List siteList) {
467: for (int i = 0; i < siteList.size(); i++) {
468: Site site = (Site) siteList.get(i);
469: if (site.equals(value)) {
470: return i;
471: }
472: }
473:
474: return -1;
475: }
476:
477: public boolean allowTool(Site site, Placement placement) {
478: // No way to render an opinion
479: if (placement == null || site == null)
480: return true;
481:
482: // The site owner sees all pages !
483: if (SecurityService.unlock(SiteService.SECURE_UPDATE_SITE, site
484: .getReference())) {
485: return true;
486: }
487:
488: boolean retval = true;
489:
490: String TOOL_CFG_FUNCTIONS = "functions.require";
491: Properties roleConfig = placement.getConfig();
492: String roleList = roleConfig.getProperty(TOOL_CFG_FUNCTIONS);
493:
494: // allow by default, when no config keys are present
495: if (roleList != null && roleList.trim().length() > 0) {
496: String[] result = roleConfig
497: .getProperty(TOOL_CFG_FUNCTIONS).split("\\,");
498: for (int x = 0; x < result.length; x++) {
499: if (!SecurityService.unlock(result[x].trim(), site
500: .getReference()))
501: retval = false;
502: }
503: }
504: return retval;
505: }
506:
507: /*
508: * Retrieve the list of pages in this site, checking to see if the user has
509: * permission to see the page - by checking the permissions of tools on the
510: * page.
511: */
512:
513: // TODO: Move this into Site
514: public List getPermittedPagesInOrder(Site site) {
515: // Get all of the pages
516: List pages = site.getOrderedPages();
517:
518: // The site owner sees all pages !
519: if (SecurityService.unlock(SiteService.SECURE_UPDATE_SITE, site
520: .getReference())) {
521: return pages;
522: }
523:
524: List newPages = new ArrayList();
525:
526: for (Iterator i = pages.iterator(); i.hasNext();) {
527: // check if current user has permission to see page
528: SitePage p = (SitePage) i.next();
529: List pTools = p.getTools();
530: Iterator iPt = pTools.iterator();
531:
532: boolean allowPage = false;
533: while (iPt.hasNext()) {
534: ToolConfiguration placement = (ToolConfiguration) iPt
535: .next();
536:
537: boolean this Tool = allowTool(site, placement);
538: if (this Tool)
539: allowPage = true;
540: }
541: if (allowPage)
542: newPages.add(p);
543: }
544: return newPages;
545: }
546: }
|