From e4af4003c87bf526c63d216c1bb9cf4cb092e744 Mon Sep 17 00:00:00 2001 From: Earle Nietzel Date: Mon, 22 Jan 2024 15:13:58 -0500 Subject: [PATCH] SAK-48700 Portal improved performance of managing pinned sites https://sakaiproject.atlassian.net/browse/SAK-48700 --- .../impl/UsageSessionServiceAdaptor.java | 2 +- portal/portal-impl/impl/pom.xml | 5 +- .../charon/handlers/FavoritesHandler.java | 402 ++++++++++-------- .../portal/charon/handlers/SiteHandler.java | 44 +- 4 files changed, 225 insertions(+), 228 deletions(-) diff --git a/kernel/kernel-impl/src/main/java/org/sakaiproject/event/impl/UsageSessionServiceAdaptor.java b/kernel/kernel-impl/src/main/java/org/sakaiproject/event/impl/UsageSessionServiceAdaptor.java index 1561ba08e597..7c0cbeec8699 100644 --- a/kernel/kernel-impl/src/main/java/org/sakaiproject/event/impl/UsageSessionServiceAdaptor.java +++ b/kernel/kernel-impl/src/main/java/org/sakaiproject/event/impl/UsageSessionServiceAdaptor.java @@ -493,8 +493,8 @@ public boolean login(String uid, String eid, String remoteaddr, String ua, Strin } // post the login event - eventTrackingService().post(eventTrackingService().newEvent(event != null ? event : EVENT_LOGIN, null, true)); sakaiSession.setAttribute(Session.JUST_LOGGED_IN, Boolean.TRUE); + eventTrackingService().post(eventTrackingService().newEvent(event != null ? event : EVENT_LOGIN, null, true)); return true; } diff --git a/portal/portal-impl/impl/pom.xml b/portal/portal-impl/impl/pom.xml index 4224d6d4896d..b055b8b07ee8 100644 --- a/portal/portal-impl/impl/pom.xml +++ b/portal/portal-impl/impl/pom.xml @@ -130,9 +130,8 @@ - com.googlecode.json-simple - json-simple - ${json.simple.version} + com.fasterxml.jackson.core + jackson-databind org.mockito diff --git a/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/FavoritesHandler.java b/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/FavoritesHandler.java index fee8299efebd..6bce2d738150 100644 --- a/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/FavoritesHandler.java +++ b/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/FavoritesHandler.java @@ -15,11 +15,16 @@ */ package org.sakaiproject.portal.charon.handlers; +import java.io.IOException; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; -import java.util.Objects; +import java.util.Observable; +import java.util.Observer; import java.util.Optional; +import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -27,15 +32,15 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.http.entity.ContentType; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; -import org.sakaiproject.component.api.ServerConfigurationService; -import org.sakaiproject.component.cover.ComponentManager; +import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; +import org.sakaiproject.event.api.Event; +import org.sakaiproject.event.api.EventTrackingService; +import org.sakaiproject.event.api.UsageSessionService; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.portal.api.PortalHandlerException; @@ -45,13 +50,19 @@ import org.sakaiproject.site.api.SiteService.SelectionType; import org.sakaiproject.site.api.SiteService.SortType; import org.sakaiproject.tool.api.Session; +import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.user.api.Preferences; import org.sakaiproject.user.api.PreferencesEdit; import org.sakaiproject.user.api.PreferencesService; -import org.sakaiproject.user.api.UserDirectoryService; - -import org.apache.commons.lang3.StringUtils; - +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.web.context.support.SpringBeanAutowiringSupport; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; /** @@ -60,30 +71,33 @@ * */ @Slf4j -public class FavoritesHandler extends BasePortalHandler +public class FavoritesHandler extends BasePortalHandler implements Observer { private static final String URL_FRAGMENT = "favorites"; - private static final String SEPARATOR = ";"; private static final String FAVORITES_PROPERTY = "order"; - private static final String AUTO_FAVORITE_ENABLED_PROPERTY = "autoFavoriteEnabled"; private static final String SEEN_SITES_PROPERTY = "autoFavoritesSeenSites"; private static final String FIRST_TIME_PROPERTY = "firstTime"; - private PortalService portalService; - private PreferencesService preferencesService; - private ServerConfigurationService serverConfigurationService; - private SiteService siteService; - private UserDirectoryService userDirectoryService; - public FavoritesHandler() - { - setUrlFragment(URL_FRAGMENT); - preferencesService = ComponentManager.get(PreferencesService.class); - portalService = ComponentManager.get(PortalService.class); - serverConfigurationService = ComponentManager.get(ServerConfigurationService.class); - siteService = ComponentManager.get(SiteService.class); - userDirectoryService = ComponentManager.get(UserDirectoryService.class); - } + @Autowired @Qualifier("org.sakaiproject.event.api.EventTrackingService") + private EventTrackingService eventTrackingService; + @Autowired @Qualifier("org.sakaiproject.portal.api.PortalService") + private PortalService portalService; + @Autowired @Qualifier("org.sakaiproject.user.api.PreferencesService") + private PreferencesService preferencesService; + @Autowired @Qualifier("org.sakaiproject.authz.api.SecurityService") + private SecurityService securityService; + @Autowired @Qualifier("org.sakaiproject.tool.api.SessionManager") + private SessionManager sessionManager; + @Autowired @Qualifier("org.sakaiproject.site.api.SiteService") + private SiteService siteService; + + public FavoritesHandler() { + SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); + + setUrlFragment(URL_FRAGMENT); + eventTrackingService.addLocalObserver(this); + } @Override @@ -94,10 +108,8 @@ public int doGet(String[] parts, HttpServletRequest req, if ((parts.length == 3) && (parts[1].equals(URL_FRAGMENT))) { try { - String userId = session.getUserId(); - updateUserFavorites(userId); res.setContentType(ContentType.APPLICATION_JSON.toString()); - res.getWriter().write(getUserFavorites(userId).toJSON()); + res.getWriter().write(getUserFavorites(session.getUserId()).toJSON()); return END; } catch (Exception e) { throw new PortalHandlerException(e); @@ -116,11 +128,7 @@ public int doPost(String[] parts, HttpServletRequest req, try { FavoriteSites favorites = FavoriteSites.fromJSON(req.getParameter("userFavorites")); boolean reorder = StringUtils.equals("true", req.getParameter("reorder")); - - synchronized (session) { - saveUserFavorites(session.getUserId(), favorites, reorder); - } - + saveUserFavorites(session.getUserId(), favorites, reorder); res.setContentType(ContentType.APPLICATION_JSON.toString()); return END; } catch (Exception e) { @@ -130,171 +138,191 @@ public int doPost(String[] parts, HttpServletRequest req, return NEXT; } - public FavoriteSites getUserFavorites(String userId) { - FavoriteSites favoriteSites = new FavoriteSites(); - favoriteSites.favoriteSiteIds = new ArrayList<>(portalService.getPinnedSites(userId)); - return favoriteSites; - } - - public void updateUserFavorites(final String userId) { - if (StringUtils.isBlank(userId)) return; - - Preferences prefs = preferencesService.getPreferences(userId); - ResourceProperties props = prefs.getProperties(PreferencesService.SITENAV_PREFS_KEY); - List excludedSites = Optional.ofNullable(props.getPropertyList(PreferencesService.SITENAV_PREFS_EXCLUDE_KEY)).orElseGet(Collections::emptyList); - - List pinnedSites = portalService.getPinnedSites(userId); - List unPinnedSites = portalService.getUnpinnedSites(userId); - List combinedSiteIds = Stream.concat(pinnedSites.stream(), unPinnedSites.stream()).collect(Collectors.toList()); - - // when the user has no sites it is most likely their first login since pinning was introduced - if (combinedSiteIds.isEmpty()) { - log.debug("User has no pinned site data performing favorites migration for user [{}]", userId); - // look to the users favorites stored in preferences - List favoriteSiteIds = props.getPropertyList(FAVORITES_PROPERTY); - if (CollectionUtils.isNotEmpty(favoriteSiteIds)) { - favoriteSiteIds.stream() - .map(id -> { - try { - return siteService.getSiteVisit(id); - } catch (IdUnusedException | PermissionException e) { - return null; - } - }) - .filter(Objects::nonNull) - .filter(site -> site.getMember(userId).isActive()) - .map(Site::getId) - .forEach(id -> { - log.debug("Adding site [{}] from favorites to pinned sites for user [{}]", id, userId); - portalService.addPinnedSite(userId, id, true); - combinedSiteIds.add(id); - }); - } - - List seenSiteIds = props.getPropertyList(SEEN_SITES_PROPERTY); - if (CollectionUtils.isNotEmpty(seenSiteIds)) { - seenSiteIds.stream() - .filter(Predicate.not(favoriteSiteIds::contains)) - .map(id -> { - try { - return siteService.getSiteVisit(id); - } catch (IdUnusedException | PermissionException e) { - return null; - } - }) - .filter(Objects::nonNull) - .filter(site -> site.getMember(userId).isActive()) - .map(Site::getId) - .forEach(id -> { - log.debug("Adding site [{}] from unseen to unpinned sites for user [{}]", id, userId); - portalService.addPinnedSite(userId, id, false); - combinedSiteIds.add(id); - }); - } - if (favoriteSiteIds != null || seenSiteIds != null) { - removeFavoritesData(userId); - } - } + public FavoriteSites getUserFavorites(String userId) { + FavoriteSites favoriteSites = new FavoriteSites(); + favoriteSites.setFavoriteSiteIds(portalService.getPinnedSites(userId)); + return favoriteSites; + } + + public void syncUserSitesWithFavoriteSites(final String userId) { + if (StringUtils.isBlank(userId) || securityService.isSuperUser(userId)) return; + + List excludedSites = Collections.emptyList(); + List favoriteSiteIds = Collections.emptyList(); + List seenSiteIds = Collections.emptyList(); + + Preferences prefs = preferencesService.getPreferences(userId); + if (prefs != null) { + ResourceProperties props = prefs.getProperties(PreferencesService.SITENAV_PREFS_KEY); + excludedSites = Optional.ofNullable(props.getPropertyList(PreferencesService.SITENAV_PREFS_EXCLUDE_KEY)).orElse(excludedSites); + favoriteSiteIds = Optional.ofNullable(props.getPropertyList(FAVORITES_PROPERTY)).orElse(favoriteSiteIds); + seenSiteIds = Optional.ofNullable(props.getPropertyList(SEEN_SITES_PROPERTY)).orElse(seenSiteIds); + } - // Remove newly hidden sites from pinned and unpinned sites - combinedSiteIds.stream().filter(excludedSites::contains).forEach(siteId -> portalService.removePinnedSite(userId, siteId)); - combinedSiteIds.addAll(excludedSites); - - // This should not call getUserSites(boolean, boolean) because the property is variable, while the call is cacheable otherwise - List userSiteIds = siteService.getSiteIds(SelectionType.MEMBER, null, null, null, SortType.CREATED_ON_DESC, null); - - userSiteIds.stream() - .filter(Predicate.not(combinedSiteIds::contains)) - .map(id -> { - try { - return siteService.getSiteVisit(id); - } catch (IdUnusedException | PermissionException e) { - log.warn("Could not access site with id [{}], {}", id, e.toString()); - return null; - } - }) - .filter(Objects::nonNull) - .filter(site -> site.getMember(userId).isActive()) - .map(Site::getId) - .peek(id -> log.debug("Adding pinned site [{}] for user [{}]", id, userId)) - .forEach(id -> portalService.addPinnedSite(userId, id, true)); - } + List pinnedSites = portalService.getPinnedSites(userId); + List unPinnedSites = portalService.getUnpinnedSites(userId); + Set combinedSiteIds = Stream.concat(pinnedSites.stream(), unPinnedSites.stream()).collect(Collectors.toSet()); + + // when the user has no sites it is most likely their first login since pinning was introduced + if (combinedSiteIds.isEmpty()) { + log.debug("User has no pinned site data performing favorites migration for user [{}]", userId); + // look to the users favorites stored in preferences + favoriteSiteIds.stream() + .filter(siteId -> canAccessSite(siteId, userId)) + .forEach(siteId -> { + log.debug("Adding site [{}] from favorites to pinned sites for user [{}]", siteId, userId); + portalService.addPinnedSite(userId, siteId, true); + combinedSiteIds.add(siteId); + }); + + seenSiteIds.stream() + .filter(Predicate.not(favoriteSiteIds::contains)) + .filter(siteId -> canAccessSite(siteId, userId)) + .forEach(siteId -> { + log.debug("Adding site [{}] from unseen to unpinned sites for user [{}]", siteId, userId); + portalService.addPinnedSite(userId, siteId, false); + combinedSiteIds.add(siteId); + }); + + if (!favoriteSiteIds.isEmpty() || !seenSiteIds.isEmpty()) { + removeFavoriteSiteData(userId); + } + } - private void removeFavoritesData(String userId) { - PreferencesEdit edit = null; - try { - edit = preferencesService.edit(userId); - } catch (Exception e) { - log.warn("Could not get the preferences for user [{}], {}", userId, e.toString()); - } + // Remove newly hidden sites from pinned and unpinned sites + combinedSiteIds.stream().filter(excludedSites::contains).forEach(siteId -> portalService.removePinnedSite(userId, siteId)); + combinedSiteIds.addAll(excludedSites); + + // This should not call getUserSites(boolean, boolean) because the property is variable, while the call is cacheable otherwise + List userSiteIds = siteService.getSiteIds(SelectionType.MEMBER, null, null, null, SortType.CREATED_ON_DESC, null); + userSiteIds.stream() + .filter(Predicate.not(combinedSiteIds::contains)) + .filter(siteId -> canAccessSite(siteId, userId)) + .peek(id -> log.debug("Adding pinned site [{}] for user [{}]", id, userId)) + .forEach(id -> portalService.addPinnedSite(userId, id, true)); + } + + /** + * Check that the user can access the site + * + * @param siteId the id of the site + * @param userId the id of the user + * @return true if access is allowed to the site, otherwise false + */ + private boolean canAccessSite(String siteId, String userId) { + boolean access = false; + try { + // use getSiteVisit as it performs proper access checks + Site site = siteService.getSiteVisit(siteId); + access = site.getMember(userId).isActive() || site.isAllowed(userId, SiteService.SECURE_UPDATE_SITE); + } catch (IdUnusedException | PermissionException e) { + log.debug("User [{}] doesn't have access to site [{}], {}", userId, siteId, e.toString()); + } + return access; + } + + private void removeFavoriteSiteData(String userId) { + PreferencesEdit edit = null; + try { + edit = preferencesService.edit(userId); + } catch (Exception e) { + log.warn("Could not get the preferences for user [{}], {}", userId, e.toString()); + } - if (edit != null) { - try { - ResourcePropertiesEdit props = edit.getPropertiesEdit(org.sakaiproject.user.api.PreferencesService.SITENAV_PREFS_KEY); - log.debug("Clearing favorites data from preferences for user [{}]", userId); - props.removeProperty(FIRST_TIME_PROPERTY); - props.removeProperty(SEEN_SITES_PROPERTY); - props.removeProperty(FAVORITES_PROPERTY); - } catch (Exception e) { - log.warn("Could not remove favorites data for user [{}], {}", userId, e.toString()); - preferencesService.cancel(edit); - edit = null; // set to null since it was cancelled, prevents commit in finally - } finally { - if (edit != null) preferencesService.commit(edit); - } - } - } + if (edit != null) { + try { + ResourcePropertiesEdit props = edit.getPropertiesEdit(org.sakaiproject.user.api.PreferencesService.SITENAV_PREFS_KEY); + log.debug("Clearing favorites data from preferences for user [{}]", userId); + props.removeProperty(FIRST_TIME_PROPERTY); + props.removeProperty(SEEN_SITES_PROPERTY); + props.removeProperty(FAVORITES_PROPERTY); + } catch (Exception e) { + log.warn("Could not remove favorites data for user [{}], {}", userId, e.toString()); + preferencesService.cancel(edit); + edit = null; // set to null since it was cancelled, prevents commit in finally + } finally { + if (edit != null) preferencesService.commit(edit); + } + } + } - private void saveUserFavorites(String userId, FavoriteSites favorites, boolean reorder) throws PortalHandlerException { + private void saveUserFavorites(String userId, FavoriteSites favorites, boolean reorder) { - if (userId == null) { - return; - } + if (userId == null) return; if (reorder) { - portalService.reorderPinnedSites(favorites.favoriteSiteIds); + portalService.reorderPinnedSites(favorites.getFavoriteSiteIds()); } else { - portalService.savePinnedSites(favorites.favoriteSiteIds); + portalService.savePinnedSites(favorites.getFavoriteSiteIds()); } - } - - public static class FavoriteSites { - - public List favoriteSiteIds; - public boolean autoFavoritesEnabled; - - public FavoriteSites() { - favoriteSiteIds = Collections.emptyList(); - autoFavoritesEnabled = false; - } - - public String toJSON() { - JSONObject obj = new JSONObject(); + } + + @Override + public void update(Observable o, Object eventObject) { + if (eventObject instanceof Event) { + Event event = (Event) eventObject; + String eventName = event.getEvent(); + if (eventName.equals(UsageSessionService.EVENT_LOGIN)) { + Session sakaiSession = sessionManager.getCurrentSession(); + boolean justLoggedIn = BooleanUtils.toBoolean((Boolean) sakaiSession.getAttribute(Session.JUST_LOGGED_IN)); + String userId = sakaiSession.getUserId(); + if (justLoggedIn && userId != null && userId.equals(event.getUserId())) { + syncUserSitesWithFavoriteSites(userId); + } + } + } + } - obj.put("autoFavoritesEnabled", autoFavoritesEnabled); - obj.put("favoriteSiteIds", favoriteSiteIds); + public static class FavoriteSites { - return obj.toString(); - } + private Set favoriteSiteIds; - public static FavoriteSites fromJSON(String json) throws ParseException { - JSONParser parser = new JSONParser(); + public FavoriteSites() { + this.favoriteSiteIds = Collections.emptySet(); + } - JSONObject obj = (JSONObject)parser.parse(json); + public List getFavoriteSiteIds() { + return new ArrayList<>(favoriteSiteIds); + } - FavoriteSites result = new FavoriteSites(); - result.favoriteSiteIds = new ArrayList(); + public void setFavoriteSiteIds(List favoriteSiteIds) { + this.favoriteSiteIds = new HashSet<>(favoriteSiteIds); + } - if (obj.get("favoriteSiteIds") != null) { - // Site IDs might be numeric, so coerce everything to strings. - for (Object siteId : (List)obj.get("favoriteSiteIds")) { - if (siteId != null) { - result.favoriteSiteIds.add(siteId.toString()); - } - } - } + public String toJSON() { + StringWriter writer = new StringWriter(); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode root = mapper.createObjectNode(); + ArrayNode array = mapper.createArrayNode(); + favoriteSiteIds.forEach(array::add); + root.set("favoriteSiteIds", array); + + try { + mapper.writeValue(writer, root); + } catch (IOException ioe) { + log.warn("Could not serialize favorites, {}", ioe.toString()); + } + return writer.toString(); + } - return result; - } - } + public static FavoriteSites fromJSON(final String json) { + FavoriteSites favoriteSites = new FavoriteSites(); + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode favoriteSiteIdsNode = objectMapper.readTree(json).get("favoriteSiteIds"); + if (favoriteSiteIdsNode.isArray()) { + List siteIds = new ArrayList<>(); + for (JsonNode node : favoriteSiteIdsNode) { + siteIds.add(node.asText()); + } + favoriteSites.setFavoriteSiteIds(siteIds); + } else { + log.warn("Unexpected element while parsing json: {}", json); + } + } catch (JsonProcessingException jpe) { + log.warn("Could not parse json string [{}], {}", json, jpe.toString()); + } + return favoriteSites; + } + } } diff --git a/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/SiteHandler.java b/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/SiteHandler.java index bf44115a8764..bc13ea12f6b5 100644 --- a/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/SiteHandler.java +++ b/portal/portal-impl/impl/src/java/org/sakaiproject/portal/charon/handlers/SiteHandler.java @@ -730,30 +730,18 @@ protected void doSendResponse(PortalRenderContext rcontext, HttpServletResponse portal.sendResponse(rcontext, res, "site", null); } - protected void includeSiteNav(PortalRenderContext rcontext, HttpServletRequest req, - Session session, String siteId) + protected void includeSiteNav(PortalRenderContext rcontext, HttpServletRequest req, Session session, String siteId) { - if (session.getUserId() != null) { - refreshAutoFavorites(session); - } - if (rcontext.uses(INCLUDE_SITE_NAV)) { boolean loggedIn = session.getUserId() != null; boolean topLogin = ServerConfigurationService.getBoolean("top.login", true); + String accessibilityURL = ServerConfigurationService.getString("accessibility.url"); - String accessibilityURL = ServerConfigurationService - .getString("accessibility.url"); - rcontext.put("siteNavHasAccessibilityURL", Boolean - .valueOf((accessibilityURL != null && !accessibilityURL.equals("")))); + rcontext.put("siteNavHasAccessibilityURL", Boolean.valueOf((accessibilityURL != null && !accessibilityURL.equals("")))); rcontext.put("siteNavAccessibilityURL", accessibilityURL); rcontext.put("siteNavTopLogin", Boolean.valueOf(topLogin)); rcontext.put("siteNavLoggedIn", Boolean.valueOf(loggedIn)); - - ResourceProperties resourceProperties = PreferencesService.getPreferences(session.getUserId()) - .getProperties(org.sakaiproject.user.api.PreferencesService.SITENAV_PREFS_KEY); - - rcontext.put("currentSiteId", siteId); rcontext.put("sidebarSites", portal.getSiteHelper().getContextSitesWithPages(req, siteId, null, loggedIn)); @@ -769,35 +757,17 @@ protected void includeSiteNav(PortalRenderContext rcontext, HttpServletRequest r { includeLogo(rcontext, req, session, siteId); if (portal.getSiteHelper().doGatewaySiteList()) - includeTabs(rcontext, req, session, siteId, getUrlFragment(), - false); + includeTabs(rcontext, req, session, siteId, getUrlFragment(), false); } } - catch (Exception any) + catch (Exception e) { + log.warn("constructing logo and tabs, {}", e.toString()); } } } - final static String AUTO_FAVORITES_LAST_REFRESHED_TIME = "autoFavoritesLastRefreshedTime"; - - private void refreshAutoFavorites(Session session) { - Long lastRefreshTime = (Long)session.getAttribute(AUTO_FAVORITES_LAST_REFRESHED_TIME); - - if (lastRefreshTime == null) { - lastRefreshTime = Long.valueOf(0); - } - - long now = System.currentTimeMillis(); - - if ((now - lastRefreshTime) > AUTO_FAVORITES_REFRESH_INTERVAL_MS) { - new FavoritesHandler().updateUserFavorites(session.getUserId()); - session.setAttribute(AUTO_FAVORITES_LAST_REFRESHED_TIME, now); - } - } - - public void includeLogo(PortalRenderContext rcontext, HttpServletRequest req, - Session session, String siteId) throws IOException + public void includeLogo(PortalRenderContext rcontext, HttpServletRequest req, Session session, String siteId) throws IOException { if (rcontext.uses(INCLUDE_LOGO)) {