/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/presence/trunk/presence-tool/tool/src/java/org/sakaiproject/presence/tool/PresenceTool.java $ * $Id: PresenceTool.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 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.presence.tool; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import java.util.Vector; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.event.api.UsageSession; import org.sakaiproject.event.cover.UsageSessionService; import org.sakaiproject.presence.cover.PresenceService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.tool.api.Placement; import org.sakaiproject.tool.api.ToolSession; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.tool.cover.ToolManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.cover.UserDirectoryService; import org.sakaiproject.util.PresenceObservingCourier; import org.sakaiproject.util.ResourceLoader; import org.sakaiproject.util.Web; /** * <p> * Presence is an tool which presents an auto-updating user presence list. * </p> */ public class PresenceTool extends HttpServlet { private static final long serialVersionUID = 1L; /** Our log (commons). */ private static Log M_log = LogFactory.getLog(PresenceTool.class); /** Request parameter to generate a fragment only. */ protected static final String OUTPUT_FRAGMENT = "output_fragment"; /** Tool state attribute where the observer is stored. */ protected static final String ATTR_OBSERVER = "observer"; /** Common Tool ID for the Sakai Chat Tool */ protected static final String CHAT_COMMON_ID = "sakai.chat"; /** Tool state attribute where the chat observer is stored. */ protected static final String ATTR_CHAT_OBSERVER = "chat_observer"; /** Presence prefix for context id for chat presence in a site **/ protected static final String CHAT_CONTEXT_PRESENCE_PREFIX = "chat_site_"; /** Localized messages * */ ResourceLoader rb = new ResourceLoader("presence"); /** * Shutdown the servlet. */ public void destroy() { M_log.info("destroy()"); super.destroy(); } /** * Respond to access requests. * * @param req * The servlet request. * @param res * The servlet response. * @throws ServletException. * @throws IOException. */ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // get the current tool session, where we store our observer ToolSession toolSession = SessionManager.getCurrentToolSession(); // get the current tool placement Placement placement = ToolManager.getCurrentPlacement(); // location is just placement String location = placement.getId(); // refresh our presence at the location PresenceService.setPresence(location); // If we are a full frame, make sure we have an observing watching // for presence change at location PresenceObservingCourier observer = (PresenceObservingCourier) toolSession.getAttribute(ATTR_OBSERVER); if (observer == null) { // setup an observer to notify us when presence at this location changes observer = new PresenceObservingCourier(location); toolSession.setAttribute(ATTR_OBSERVER, observer); } // get the list of users at the location List<User> users = PresenceService.getPresentUsers(location, placement.getContext()); // get SiteId from the current placement and retrieve site String siteId = placement.getContext(); Site site = null; ToolConfiguration toolConfig = null; List<User> chatUsers = null; if (siteId != null) { try { site = SiteService.getSiteVisit(siteId); } catch (Exception e) { // No problem - leave site null } } if (site != null) { toolConfig = site.getToolForCommonId(CHAT_COMMON_ID); } if (toolConfig != null) { // Check the secondary chat presence that's specific to the site (rather than channel or placement) String chatLocation = CHAT_CONTEXT_PRESENCE_PREFIX + siteId; chatUsers = PresenceService.getPresentUsers(chatLocation, siteId); PresenceObservingCourier chatObserver = (PresenceObservingCourier) toolSession.getAttribute(ATTR_CHAT_OBSERVER); if (chatObserver == null) { // Monitor presence changes at chatLocation and deliver them to this window's location with // no sub window (null) chatObserver = new PresenceObservingCourier(location, null, chatLocation); toolSession.setAttribute(ATTR_CHAT_OBSERVER, chatObserver); } } // start the response PrintWriter out = startResponse(req, res, "presence"); sendAutoUpdate(out, req, placement.getId(), placement.getContext()); sendPresence(out, users, chatUsers); // end the response if ( ! "yes".equals(req.getParameter(OUTPUT_FRAGMENT) ) ) endResponse(out); } /** * End the response * * @param out * @throws IOException */ protected void endResponse(PrintWriter out) throws IOException { out.println("</body></html>"); } /** * Access the Servlet's information display. * * @return servlet information. */ public String getServletInfo() { return "Sakai Presence Tool"; } /** * Initialize the servlet. * * @param config * The servlet config. * @throws ServletException */ public void init(ServletConfig config) throws ServletException { super.init(config); M_log.info("init()"); } /** * Send the HTML / Javascript to invoke an automatic update * * @param out * @param req * @param placementId * @param context */ protected void sendAutoUpdate(PrintWriter out, HttpServletRequest req, String placementId, String context) { // set the refresh of the courier to 1/2 the presence timeout value int updateTime = PresenceService.getTimeout() / 2; String userId = SessionManager.getCurrentSessionUserId(); StringBuilder url = new StringBuilder(Web.serverUrl(req)); url.append("/courier/"); url.append(placementId); url.append("?userId="); url.append(userId); out.println("<script type=\"text/javascript\" language=\"JavaScript\">"); out.println("updateTime = " + updateTime + "000;"); out.println("updateUrl = \"" + url.toString() + "\";"); out.println("scheduleUpdate();"); out.println("</script>"); } /** * Format the list of users * * @param out * @param users */ protected void sendPresence(PrintWriter out, List<User> users, List<User> chatUsers) { // is the current user running under an assumed (SU) user id? String asName = null; String myUserId = null; try { UsageSession usageSession = UsageSessionService.getSession(); if (usageSession != null) { // this is the absolutely real end-user id, even if running as another user myUserId = usageSession.getUserId(); // this is the user id the current user is running as String sessionUserId = SessionManager.getCurrentSessionUserId(); // if different if (!myUserId.equals(sessionUserId)) { asName = UserDirectoryService.getUser(sessionUserId).getDisplayName(); } } } catch (Exception any) { } out.println("<ul class=\"presenceList\">"); if (users == null) { out.println("<!-- Presence empty -->"); out.println("</ul>"); return; } // first pass - list Chat users (if any) if (chatUsers != null) { String msg = rb.getString("inchat"); for (User u : chatUsers) { String displayName = u.getDisplayName(); // adjust if this is the current user running as someone else if ((asName != null) && (u.getId().equals(myUserId))) { displayName += " (" + asName + ")"; } out.print("<li class=\"inChat\">"); out.print("<span title=\"" + msg + "\">"); out.print(Web.escapeHtml(displayName)); out.println("</span></li>"); } } // second pass - list remaining non-chat users List<User> nonChatUsers = new Vector<User>(users); if (chatUsers != null) { nonChatUsers.removeAll(chatUsers); } String msg = rb.getString("insite"); for (User u : nonChatUsers) { String displayName = u.getDisplayName(); // adjust if this is the current user running as someone else if ((asName != null) && (u.getId().equals(myUserId))) { displayName += " (" + asName + ")"; } out.print("<li>"); out.print("<span title=\"" + msg + "\">"); out.print(Web.escapeHtml(displayName)); out.println("</span></li>"); } out.println("</ul>"); } /** * Start the response. * * @param req * @param res * @param title * @param skin * @return * @throws IOException */ protected PrintWriter startResponse(HttpServletRequest req, HttpServletResponse res, String title) throws IOException { // headers res.setContentType("text/html; charset=UTF-8"); res.addDateHeader("Expires", System.currentTimeMillis() - (1000L * 60L * 60L * 24L * 365L)); res.addDateHeader("Last-Modified", System.currentTimeMillis()); res.addHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0"); res.addHeader("Pragma", "no-cache"); // get the writer PrintWriter out = res.getWriter(); if ( "yes".equals(req.getParameter(OUTPUT_FRAGMENT) ) ) return out; // form the head out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">" + " <head>" + " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />"); out.println("<title>" + rb.getString("insite") + "</title>"); // send the portal set-up head String head = (String) req.getAttribute("sakai.html.head"); if (head != null) { out.println(head); } out.println("</head>"); // Note: we ignore the portal set-up body onload // start the body out.println("<body>"); return out; } }