@Override
  public int doGet(String[] parts, HttpServletRequest req, HttpServletResponse res, Session session)
      throws PortalHandlerException {
    // recognize and dispatch the 'tool' option: [1] = "tool", [2] =
    // placement id (of a site's tool placement), rest for the tool
    if ((parts.length > 2) && (parts[1].equals(getUrlFragment()))) {
      try {
        // Resolve the placements of the form
        // /portal/tool/sakai.resources?sakai.site=~csev
        String toolPlacement = portal.getPlacement(req, res, session, parts[2], false);
        if (toolPlacement == null) {
          return ABORT;
        }
        parts[2] = toolPlacement;

        doTool(
            req,
            res,
            session,
            parts[2],
            req.getContextPath() + req.getServletPath() + Web.makePath(parts, 1, 3),
            Web.makePath(parts, 3, parts.length));
        return END;
      } catch (Exception ex) {
        throw new PortalHandlerException(ex);
      }
    } else {
      return NEXT;
    }
  }
  @Override
  public int doGet(String[] parts, HttpServletRequest req, HttpServletResponse res, Session session)
      throws PortalHandlerException {

    if ((parts.length >= 3) && (parts[1].equals(PresenceHandler.URL_FRAGMENT))) {
      try {
        doPresence(
            req,
            res,
            session,
            parts[2],
            req.getContextPath() + req.getServletPath() + Web.makePath(parts, 1, 3),
            Web.makePath(parts, 3, parts.length));
        return END;
      } catch (Exception ex) {
        throw new PortalHandlerException(ex);
      }
    } else {
      return NEXT;
    }
  }
 @Override
 public int doGet(String[] parts, HttpServletRequest req, HttpServletResponse res, Session session)
     throws PortalHandlerException {
   if ((parts.length > 2) && (parts[1].equals(SiteResetHandler.URL_FRAGMENT))) {
     try {
       String siteUrl = req.getContextPath() + "/site" + Web.makePath(parts, 2, parts.length);
       // Make sure to add the parameters such as panel=Main
       String queryString = Validator.generateQueryString(req);
       if (queryString != null) {
         siteUrl = siteUrl + "?" + queryString;
       }
       portalService.setResetState("true");
       res.sendRedirect(siteUrl);
       return RESET_DONE;
     } catch (Exception ex) {
       throw new PortalHandlerException(ex);
     }
   } else {
     return NEXT;
   }
 }
  public String getActionURL(FacesContext context, String viewId) {
    HttpServletRequest req = (HttpServletRequest) context.getExternalContext().getRequest();

    if (req.getAttribute(URL_EXT) == null) {
      // If the request didn't go through JsfTool (the JSF is accessed directly from its webapp,
      // not as a Sakai tool), then don't do Sakai's special action URL handling.
      return getWrapped().getActionURL(context, viewId);
    }

    // get the path that got us here (from the tool's point of view)
    String path = viewId;

    // modify the path to remove things that were added by Sakai navigation to get here (prefix
    // path, suffix extension)
    String prefix = (String) req.getAttribute(URL_PATH);
    if ((prefix != null) && path.startsWith(prefix)) path = path.substring(prefix.length());

    Object extensions = req.getAttribute(URL_EXT);
    String[] exts =
        extensions instanceof String ? new String[] {(String) extensions} : (String[]) extensions;
    for (String ext : exts) {
      if ((ext != null) && path.endsWith(ext))
        path = path.substring(0, path.length() - ext.length());
    }

    // make sure the URL processing uses the Sakai, not Native the request object so we can get at
    // the URL information setup by the invoker
    req.removeAttribute(Tool.NATIVE_URL);

    // form our return URL
    String rv = Web.returnUrl(req, path);

    // restore (if needed)
    req.setAttribute(Tool.NATIVE_URL, Tool.NATIVE_URL);

    M_log.debug("action url for view: " + viewId + " = " + rv);

    return rv;
  }
  // NOTE: This code is duplicated in SkinnableCharonPortal.java
  // make sure to change code both places
  protected void setupForward(
      HttpServletRequest req, HttpServletResponse res, Placement p, String skin)
      throws ToolException {
    // setup html information that the tool might need (skin, body on load,
    // js includes, etc).
    if (skin == null || skin.length() == 0)
      skin = ServerConfigurationService.getString("skin.default");
    String skinRepo = ServerConfigurationService.getString("skin.repo");
    String headCssToolBase =
        "<link href=\""
            + skinRepo
            + "/tool_base.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n";
    String headCssToolSkin =
        "<link href=\""
            + skinRepo
            + "/"
            + skin
            + "/tool.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n";
    String headCss = headCssToolBase + headCssToolSkin;
    String headJs =
        "<script type=\"text/javascript\" src=\"/library/js/headscripts.js\"></script>\n";
    String head = headCss + headJs;
    StringBuilder bodyonload = new StringBuilder();
    if (p != null) {
      String element = Web.escapeJavascript("Main" + p.getId());
      bodyonload.append("setMainFrameHeight('" + element + "');");
    }
    bodyonload.append("setFocus(focus_path);");

    req.setAttribute("sakai.html.head", head);
    req.setAttribute("sakai.html.head.css", headCss);
    req.setAttribute("sakai.html.head.css.base", headCssToolBase);
    req.setAttribute("sakai.html.head.css.skin", headCssToolSkin);
    req.setAttribute("sakai.html.head.js", headJs);
    req.setAttribute("sakai.html.body.onload", bodyonload.toString());
  }
  /**
   * 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 pageUrl =
        Web.returnUrl(
            req, "/" + portalPrefix + "/" + Web.escapeUrl(getSiteEffectiveId(site)) + "/page/");
    String toolUrl =
        Web.returnUrl(req, "/" + portalPrefix + "/" + Web.escapeUrl(getSiteEffectiveId(site)));
    if (resetTools) {
      toolUrl = toolUrl + "/tool-reset/";
    } else {
      toolUrl = toolUrl + "/tool/";
    }

    String pagePopupUrl = Web.returnUrl(req, "/page/");
    boolean showHelp = ServerConfigurationService.getBoolean("display.help.menu", true);
    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
      // we will draw page button if it have permission to see at least
      // one tool on the page
      List<ToolConfiguration> pTools = p.getTools();
      ToolConfiguration firstTool = null;
      String toolsOnPage = null;

      boolean current = (page != null && p.getId().equals(page.getId()) && !p.isPopUp());
      String alias = lookupPageToAlias(site.getId(), p);
      String pagerefUrl = pageUrl + Web.escapeUrl((alias != null) ? alias : p.getId());

      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++;
            if ("sakai.siteinfo".equals(t.getToolId())) {
              addMoreToolsUrl =
                  Web.returnUrl(
                      req,
                      "/site/"
                          + Web.escapeUrl(site.getId())
                          + "/page/"
                          + Web.escapeUrl(p.getId())
                          + "?sakai_action=doMenu_edit_site_tools&panel=Shortcut");
            }
          }
          // Won't work with mutliple tools per page
          if (tCount > 1) addMoreToolsUrl = null;
        }

        boolean siteUpdate = SecurityService.unlock("site.upd", site.getReference());
        if (!siteUpdate) addMoreToolsUrl = null;

        if (!ServerConfigurationService.getBoolean("portal.experimental.addmoretools", false))
          addMoreToolsUrl = null;

        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);

        // TODO: Should have Web.escapeHtmlAttribute()
        String description = desc.toString().replace("\"", "&quot;");
        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;
      }

      // 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);
          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 {
      String showPresenceSite = site.getProperties().getProperty("display-users-present");

      if (showPresenceSite == null) {
        showPresence = Boolean.valueOf(globalShowPresence).booleanValue();
      } else {
        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);

    // 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;
  }
  /**
   * 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))));
    int siteTitleMaxLength = ServerConfigurationService.getInt("site.title.maxlength", 25);
    String titleStr = s.getTitle();
    String fullTitle = titleStr;
    if (titleStr != null) {
      titleStr = titleStr.trim();
      if (titleStr.length() > siteTitleMaxLength && siteTitleMaxLength >= 10) {
        titleStr = titleStr.substring(0, siteTitleMaxLength - 4) + " ...";
      } else if (titleStr.length() > siteTitleMaxLength) {
        titleStr = titleStr.substring(0, siteTitleMaxLength);
      }
      titleStr = titleStr.trim();
    }
    m.put("siteTitle", Web.escapeHtml(titleStr));
    m.put("fullTitle", Web.escapeHtml(fullTitle));
    m.put("siteDescription", Web.escapeHtml(s.getDescription()));
    m.put("shortDescription", Web.escapeHtml(s.getShortDescription()));
    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;
  }
  protected void dispatch(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {
    // NOTE: this is a simple path dispatching, taking the path as the view id = jsp file name for
    // the view,
    //       with default used if no path and a path prefix as configured.
    // TODO: need to allow other sorts of dispatching, such as pulling out drill-down ids and making
    // them
    //       available to the JSF

    // build up the target that will be dispatched to
    String target = req.getPathInfo();

    // see if we have a helper request
    if (sendToHelper(req, res, target)) {
      return;
    }

    // see if we have a resource request - i.e. a path with an extension, and one that is not the
    // JSF_EXT
    if (isResourceRequest(target)) {
      // get a dispatcher to the path
      RequestDispatcher resourceDispatcher = getServletContext().getRequestDispatcher(target);
      if (resourceDispatcher != null) {
        resourceDispatcher.forward(req, res);
        return;
      }
    }

    if ("Title".equals(req.getParameter("panel"))) {
      // This allows only one Title JSF for each tool
      target = "/title.jsf";
    } else {
      ToolSession session = SessionManager.getCurrentToolSession();

      if (target == null || "/".equals(target) || target.length() == 0) {
        if (!m_defaultToLastView) {
          // make sure tool session is clean
          session.clearAttributes();
        }

        target = computeDefaultTarget();

        // make sure it's a valid path
        if (!target.startsWith("/")) {
          target = "/" + target;
        }

        // now that we've messed with the URL, send a redirect to make it official
        res.sendRedirect(Web.returnUrl(req, target));
        return;
      }

      // see if we want to change the specifically requested view
      String newTarget = redirectRequestedTarget(target);

      // make sure it's a valid path
      if (!newTarget.startsWith("/")) {
        newTarget = "/" + newTarget;
      }

      if (!newTarget.equals(target)) {
        // now that we've messed with the URL, send a redirect to make it official
        res.sendRedirect(Web.returnUrl(req, newTarget));
        return;
      }
      target = newTarget;

      // store this
      session.setAttribute(LAST_VIEW_VISITED, target);
    }

    // add the configured folder root and extension (if missing)
    target = m_path + target;

    // add the default JSF extension (if we have no extension)
    int lastSlash = target.lastIndexOf("/");
    int lastDot = target.lastIndexOf(".");
    if (lastDot < 0 || lastDot < lastSlash) {
      target += JSF_EXT;
    }

    // set the information that can be removed from return URLs
    req.setAttribute(URL_PATH, m_path);
    req.setAttribute(URL_EXT, ".jsp");

    // set the sakai request object wrappers to provide the native, not Sakai set up, URL
    // information
    // - this assures that the FacesServlet can dispatch to the proper view based on the path info
    req.setAttribute(Tool.NATIVE_URL, Tool.NATIVE_URL);

    // TODO: Should setting the HTTP headers be moved up to the portal level as well?
    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");

    // dispatch to the target
    /*M_log.debug("dispatching path: " + req.getPathInfo() + " to: " + target + " context: "
    + getServletContext().getServletContextName());*/
    RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(target);
    dispatcher.forward(req, res);

    // restore the request object
    req.removeAttribute(Tool.NATIVE_URL);
    req.removeAttribute(URL_PATH);
    req.removeAttribute(URL_EXT);
  }
  protected boolean sendToHelper(HttpServletRequest req, HttpServletResponse res, String target) {
    String path = req.getPathInfo();
    if (path == null) path = "/";

    // 0 parts means the path was just "/", otherwise parts[0] = "", parts[1] = item id, parts[2] if
    // present is "edit"...
    String[] parts = path.split("/");

    if (parts.length < 2) {
      return false;
    }

    if (!parts[1].endsWith(HELPER_EXT)) {
      return false;
    }

    ToolSession toolSession = SessionManager.getCurrentToolSession();

    Enumeration params = req.getParameterNames();
    while (params.hasMoreElements()) {
      String paramName = (String) params.nextElement();
      if (paramName.startsWith(HELPER_SESSION_PREFIX)) {
        String attributeName = paramName.substring(HELPER_SESSION_PREFIX.length());
        toolSession.setAttribute(attributeName, req.getParameter(paramName));
      }
    }

    // calc helper id
    int posEnd = parts[1].lastIndexOf(".");

    String helperId = target.substring(1, posEnd + 1);
    ActiveTool helperTool = ActiveToolManager.getActiveTool(helperId);

    // get the current location (if one doesn't exist) and save it for when we return from the
    // helper
    if (toolSession.getAttribute(helperTool.getId() + Tool.HELPER_DONE_URL) == null) {
      toolSession.setAttribute(
          helperTool.getId() + Tool.HELPER_DONE_URL,
          req.getContextPath() + req.getServletPath() + computeDefaultTarget(true));
    }
    toolSession.setAttribute(
        helperTool.getId() + "thetoolPath", req.getContextPath() + req.getServletPath());

    // saves the alternate done url map into a tool specific attribute
    if (toolSession.getAttribute(helperTool.getId() + ToolFinishedView.ALTERNATE_DONE_URL)
        == null) {
      toolSession.setAttribute(
          helperTool.getId() + ToolFinishedView.ALTERNATE_DONE_URL,
          toolSession.getAttribute(ToolFinishedView.ALTERNATE_DONE_URL));
      toolSession.setAttribute(
          helperTool.getId() + ToolFinishedView.ALTERNATE_DONE_URL_MAP,
          toolSession.getAttribute(ToolFinishedView.ALTERNATE_DONE_URL_MAP));
      toolSession.removeAttribute(ToolFinishedView.ALTERNATE_DONE_URL);
      toolSession.removeAttribute(ToolFinishedView.ALTERNATE_DONE_URL_MAP);
    }

    /*comment out for using the global parameter rather than tool-by-tool setting
        SessionState state = UsageSessionService.getSessionState(toolSession.getPlacementId());
    		boolean show_other_sites = ServerConfigurationService.getBoolean("syllabus.resources.show_all_collections.helper", true);
    		state.setAttribute("resources.allow_user_to_see_all_sites", (new Boolean(show_other_sites)).toString());
    		state.setAttribute("resources.user_chooses_to_see_other_sites", (new Boolean(show_other_sites)).toString());
    */
    String context = req.getContextPath() + req.getServletPath() + Web.makePath(parts, 1, 2);
    String toolPath = Web.makePath(parts, 2, parts.length);
    try {
      helperTool.help(req, res, context, toolPath);
    } catch (ToolException e) {
      throw new RuntimeException(e);
    }

    return true; // was handled as helper call
  }