001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal;
007:
008: import java.io.IOException;
009: import java.io.InputStream;
010: import java.net.URL;
011:
012: import javax.servlet.http.HttpServletRequest;
013: import javax.servlet.http.HttpSession;
014: import javax.servlet.http.HttpSessionBindingEvent;
015:
016: import org.apache.commons.logging.Log;
017: import org.apache.commons.logging.LogFactory;
018: import org.jasig.portal.i18n.LocaleManager;
019: import org.jasig.portal.jndi.JNDIManager;
020: import org.jasig.portal.layout.IUserLayout;
021: import org.jasig.portal.layout.IUserLayoutManager;
022: import org.jasig.portal.layout.IUserLayoutStore;
023: import org.jasig.portal.layout.TransientUserLayoutManagerWrapper;
024: import org.jasig.portal.layout.UserLayoutManagerFactory;
025: import org.jasig.portal.layout.UserLayoutStoreFactory;
026: import org.jasig.portal.layout.node.IUserLayoutChannelDescription;
027: import org.jasig.portal.properties.PropertiesManager;
028: import org.jasig.portal.security.IPerson;
029: import org.jasig.portal.utils.PropsMatcher;
030:
031: /**
032: * UserPreferencesManager is responsible for keeping: user id, user layout, user preferences
033: * and stylesheet descriptions.
034: * For method descriptions please see {@link IUserPreferencesManager}.
035: * @author Peter Kharchenko {@link <a href="mailto:pkharchenko@interactivebusiness.com">pkharchenko@interactivebusiness.com</a>}
036: * @version $Revision: 36713 $
037: */
038: public class UserPreferencesManager implements IUserPreferencesManager {
039:
040: private static final Log log = LogFactory
041: .getLog(UserPreferencesManager.class);
042: private static final String USER_PREFERENCES_KEY = UserPreferencesManager.class
043: .getName();
044:
045: /**
046: * Default value for saveUserPreferencesAtLogout.
047: * This value will be used when the corresponding property cannot be loaded.
048: */
049: private static final boolean DEFAULT_SAVE_USER_PREFERENCES_AT_LOGOUT = false;
050:
051: // user agent mapper for guessing the profile
052: static PropsMatcher uaMatcher;
053:
054: private IUserLayoutManager ulm;
055:
056: private UserPreferences complete_up;
057: // caching of stylesheet descriptions is recommended
058: // if they'll take up too much space, we can take them
059: // out, but cache stylesheet URIs, mime type and serializer name.
060: // Those are used in every rendering cycle.
061: private ThemeStylesheetDescription tsd;
062: private StructureStylesheetDescription ssd;
063: private boolean unmapped_user_agent = false;
064: IPerson m_person;
065: IUserLayoutStore ulsdb = null;
066:
067: private static final boolean saveUserPreferencesAtLogout = PropertiesManager
068: .getPropertyAsBoolean(UserPreferencesManager.class
069: .getName()
070: + ".save_UserPreferences_at_logout",
071: DEFAULT_SAVE_USER_PREFERENCES_AT_LOGOUT);
072:
073: /**
074: * Constructor does the following
075: * 1. Read layout.properties
076: * 2. read userLayout from the database
077: * @param req the servlet request object
078: * @param person the person object
079: */
080: public UserPreferencesManager(HttpServletRequest req, IPerson person)
081: throws PortalException {
082: this (req, person, null);
083: }
084:
085: /**
086: * Constructor does the following
087: * 1. Read layout.properties
088: * 2. read userLayout from the database
089: * @param req the servlet request object
090: * @param person the person object
091: * @param localeManager the locale manager
092: */
093: public UserPreferencesManager(HttpServletRequest req,
094: IPerson person, LocaleManager localeManager)
095: throws PortalException {
096: ulm = null;
097: try {
098: m_person = person;
099: // load user preferences
100: // Should obtain implementation in a different way!!
101: ulsdb = UserLayoutStoreFactory.getUserLayoutStoreImpl();
102: // determine user profile
103: String userAgent = req.getHeader("User-Agent");
104: if (userAgent == null || userAgent.equals("")) {
105: userAgent = MediaManager.NULL_USER_AGENT;
106: }
107: UserProfile upl = ulsdb.getUserProfile(m_person, userAgent);
108: if (upl == null) {
109: upl = ulsdb.getSystemProfile(userAgent);
110: }
111: if (upl == null) {
112: // try guessing the profile through pattern matching
113:
114: if (uaMatcher == null) {
115: // init user agent matcher
116: URL url = null;
117: try {
118: url = this .getClass().getResource(
119: "/properties/browser.mappings");
120: if (url != null) {
121: InputStream in = url.openStream();
122: try {
123: uaMatcher = new PropsMatcher(in);
124: } finally {
125: in.close();
126: }
127: }
128: } catch (IOException ioe) {
129: log
130: .error(
131: "UserPreferencesManager::UserPreferencesManager() : Exception occurred while loading browser mapping file: "
132: + url + ". ", ioe);
133: }
134: }
135:
136: if (uaMatcher != null) {
137: // try matching
138: String profileId = uaMatcher.match(userAgent);
139: if (profileId != null) {
140: // user agent has been matched
141:
142: upl = ulsdb.getSystemProfileById(Integer
143: .parseInt(profileId));
144: }
145: }
146:
147: }
148:
149: if (upl != null) {
150: if (localeManager != null
151: && LocaleManager.isLocaleAware()) {
152: upl.setLocaleManager(localeManager);
153: }
154: ulm = UserLayoutManagerFactory.getUserLayoutManager(
155: m_person, upl);
156:
157: final HttpSession session = req.getSession(true);
158: try {
159: if (session != null) {
160: complete_up = (UserPreferences) session
161: .getAttribute(USER_PREFERENCES_KEY);
162: }
163:
164: if (complete_up == null) {
165: complete_up = ulsdb.getUserPreferences(
166: m_person, upl);
167: } else {
168: log
169: .debug("Found UserPreferences in session, using it instead of creating new UserPreferences");
170: }
171: } catch (Exception e) {
172: log
173: .error(
174: "UserPreferencesManager(): caught an exception trying to retreive user preferences for user=\""
175: + m_person.getID()
176: + "\", profile=\""
177: + upl.getProfileName()
178: + "\".", e);
179: complete_up = new UserPreferences(upl);
180: }
181:
182: if (complete_up != null) {
183: session.setAttribute(USER_PREFERENCES_KEY,
184: complete_up);
185: }
186:
187: try {
188: // Initialize the JNDI context for this user
189: JNDIManager.initializeSessionContext(session,
190: Integer.toString(m_person.getID()), Integer
191: .toString(upl.getLayoutId()), ulm
192: .getUserLayoutDOM());
193: } catch (PortalException ipe) {
194: log
195: .error(
196: "UserPreferencesManager(): Could not properly initialize user context",
197: ipe);
198: }
199: } else {
200: // there is no user-defined mapping for this particular browser.
201: // user should be redirected to a browser-registration page.
202: unmapped_user_agent = true;
203: if (log.isDebugEnabled())
204: log
205: .debug("UserPreferencesManager::UserPreferencesManager() : unable to find a profile for user \""
206: + m_person.getID()
207: + "\" and userAgent=\""
208: + userAgent
209: + "\".");
210: }
211: } catch (PortalException pe) {
212: throw pe;
213: } catch (Exception e) {
214: log.error(
215: "Exception constructing UserPreferencesManager on request "
216: + req + " for user " + person, e);
217: }
218: }
219:
220: /**
221: * A simpler constructor, that only initialises the person object.
222: * Needed for ancestors.
223: * @param person an <code>IPerson</code> object.
224: */
225: public UserPreferencesManager(IPerson person) {
226: m_person = person;
227: }
228:
229: /* This function processes request parameters related to
230: * setting Structure/Theme stylesheet parameters and attributes.
231: * (uP_sparam, uP_tparam, uP_sfattr, uP_scattr
232: * uP_tcattr)
233: * It also processes layout root requests (uP_root) and
234: * (up_fname)
235: * @param req current <code>HttpServletRequest</code>
236: */
237: public void processUserPreferencesParameters(HttpServletRequest req) {
238: // layout root setting
239: String root;
240: if ((root = req.getParameter("uP_root")) != null) {
241: // If a channel specifies "me" as its root, set the root
242: // to the channel's subscribe Id
243: if (root.equals("me")) {
244: // get uPFile spec and search for "channel" clause
245: UPFileSpec upfs = new UPFileSpec(req);
246: root = upfs.getTargetNodeId();
247: }
248: if (root != null) {
249: complete_up.getStructureStylesheetUserPreferences()
250: .putParameterValue("userLayoutRoot", root);
251:
252: //If going to focused make sure we aren't minimzed
253: if (!root.equals(IUserLayout.ROOT_NODE_NAME)) {
254: complete_up.getThemeStylesheetUserPreferences()
255: .setChannelAttributeValue(root,
256: "minimized", "false");
257: }
258: } else {
259: log
260: .error("UserPreferencesManager::processUserPreferencesParameters() : unable to extract channel ID. servletPath=\""
261: + req.getServletPath() + "\".");
262: }
263: }
264:
265: // fname and root are mutually exclusive and
266: // should not be used in the same request,
267: // as an fname is treated as the root target.
268: String fname = req.getParameter(Constants.FNAME_PARAM);
269: if (fname != null) {
270: // get a subscribe id for the fname
271: String subId = null;
272: try {
273: subId = ulm.getSubscribeId(fname);
274: } catch (PortalException pe) {
275: log
276: .error(
277: "UserPreferencesManager::processUserPreferencesParameters(): Unable to get subscribe ID for fname="
278: + fname, pe);
279: }
280: if (ulm instanceof TransientUserLayoutManagerWrapper) {
281: // get wrapper implementation for focusing
282: TransientUserLayoutManagerWrapper iulm = (TransientUserLayoutManagerWrapper) ulm;
283: // .. and now set it as the focused id
284: iulm.setFocusedId(subId);
285: }
286:
287: complete_up.getStructureStylesheetUserPreferences()
288: .putParameterValue("userLayoutRoot", subId);
289: if (log.isDebugEnabled())
290: log
291: .debug("UserPreferencesManager::processUserPreferencesParameters() : "
292: + "setting sfname \" userLayoutRoot"
293: + "\"=\"" + subId + "\".");
294: }
295:
296: // Request to change the locale
297: String localesString = req
298: .getParameter(Constants.LOCALES_PARAM);
299: if (localesString != null) {
300: LocaleManager localeManager = complete_up.getProfile()
301: .getLocaleManager();
302: localeManager.setSessionLocales(LocaleManager
303: .parseLocales(localesString));
304: }
305:
306: // other params
307: String[] sparams = req.getParameterValues("uP_sparam");
308: if (sparams != null) {
309: for (int i = 0; i < sparams.length; i++) {
310: String pValue = req.getParameter(sparams[i]);
311: complete_up.getStructureStylesheetUserPreferences()
312: .putParameterValue(sparams[i], pValue);
313: if (log.isDebugEnabled())
314: log
315: .debug("UserPreferencesManager::processUserPreferencesParameters() : setting sparam \""
316: + sparams[i]
317: + "\"=\""
318: + pValue
319: + "\".");
320: }
321: }
322: String[] tparams = req.getParameterValues("uP_tparam");
323: if (tparams != null) {
324: for (int i = 0; i < tparams.length; i++) {
325: String pValue = req.getParameter(tparams[i]);
326: complete_up.getThemeStylesheetUserPreferences()
327: .putParameterValue(tparams[i], pValue);
328: if (log.isDebugEnabled())
329: log
330: .debug("UserPreferencesManager::processUserPreferencesParameters() : setting tparam \""
331: + tparams[i]
332: + "\"=\""
333: + pValue
334: + "\".");
335: }
336: }
337: // attribute processing
338: // structure transformation
339: String[] sfattrs = req.getParameterValues("uP_sfattr");
340: if (sfattrs != null) {
341: for (int i = 0; i < sfattrs.length; i++) {
342: String aName = sfattrs[i];
343: String[] aNode = req.getParameterValues(aName
344: + "_folderId");
345: if (aNode != null && aNode.length > 0) {
346: for (int j = 0; j < aNode.length; j++) {
347: String aValue = req.getParameter(aName + "_"
348: + aNode[j] + "_value");
349: complete_up
350: .getStructureStylesheetUserPreferences()
351: .setFolderAttributeValue(aNode[j],
352: aName, aValue);
353: if (log.isDebugEnabled())
354: log
355: .debug("UserPreferencesManager::processUserPreferencesParameters() : setting sfattr \""
356: + aName
357: + "\" of \""
358: + aNode[j]
359: + "\" to \""
360: + aValue + "\".");
361: }
362: }
363: }
364: }
365: String[] scattrs = req.getParameterValues("uP_scattr");
366: if (scattrs != null) {
367: for (int i = 0; i < scattrs.length; i++) {
368: String aName = scattrs[i];
369: String[] aNode = req.getParameterValues(aName
370: + "_channelId");
371: if (aNode != null && aNode.length > 0) {
372: for (int j = 0; j < aNode.length; j++) {
373: String aValue = req.getParameter(aName + "_"
374: + aNode[j] + "_value");
375: complete_up
376: .getStructureStylesheetUserPreferences()
377: .setChannelAttributeValue(aNode[j],
378: aName, aValue);
379: if (log.isDebugEnabled())
380: log
381: .debug("UserPreferencesManager::processUserPreferencesParameters() : setting scattr \""
382: + aName
383: + "\" of \""
384: + aNode[j]
385: + "\" to \""
386: + aValue + "\".");
387: }
388: }
389: }
390: }
391: // theme stylesheet attributes
392: String[] tcattrs = req.getParameterValues("uP_tcattr");
393: if (tcattrs != null) {
394: for (int i = 0; i < tcattrs.length; i++) {
395: String aName = tcattrs[i];
396: String[] aNode = req.getParameterValues(aName
397: + "_channelId");
398: if (aNode != null && aNode.length > 0) {
399: for (int j = 0; j < aNode.length; j++) {
400: String aValue = req.getParameter(aName + "_"
401: + aNode[j] + "_value");
402: complete_up.getThemeStylesheetUserPreferences()
403: .setChannelAttributeValue(aNode[j],
404: aName, aValue);
405: if (log.isDebugEnabled())
406: log
407: .debug("UserPreferencesManager::processUserPreferencesParameters() : setting tcattr \""
408: + aName
409: + "\" of \""
410: + aNode[j]
411: + "\" to \""
412: + aValue + "\".");
413: }
414: }
415: }
416: }
417:
418: // save processing handled at end to provide persisting of changes
419: // if desired.
420: String saveWhat = req.getParameter("uP_save");
421: if (saveWhat != null) {
422: try {
423: if (saveWhat.equals("preferences")) {
424: ulsdb.putUserPreferences(m_person, complete_up);
425: } else if (saveWhat.equals("layout")) {
426: ulm.saveUserLayout();
427: } else if (saveWhat.equals("all")) {
428: ulsdb.putUserPreferences(m_person, complete_up);
429: ulm.saveUserLayout();
430: }
431: if (log.isDebugEnabled())
432: log
433: .debug("UserPreferencesManager::processUserPreferencesParameters() : persisted "
434: + saveWhat + " changes.");
435:
436: } catch (Exception e) {
437: log
438: .error(
439: "UserPreferencesManager::processUserPreferencesParameters() : unable to persist "
440: + saveWhat + " changes. ", e);
441: }
442: }
443: }
444:
445: /**
446: * Returns current person object
447: * @return current <code>IPerson</code>
448: */
449: public IPerson getPerson() {
450: return (m_person);
451: }
452:
453: /**
454: * Returns a global channel Id given a channel instance Id
455: * @param channelSubscribeId subscribe id of a channel
456: * @return channel global id
457: */
458: protected String getChannelPublishId(String channelSubscribeId)
459: throws PortalException {
460: // Get the channel node from the user's layout
461: IUserLayoutChannelDescription channel = (IUserLayoutChannelDescription) getUserLayoutManager()
462: .getNode(channelSubscribeId);
463: if (channel != null) {
464: return channel.getChannelPublishId();
465: } else {
466: return null;
467: }
468: }
469:
470: public boolean isUserAgentUnmapped() {
471: return unmapped_user_agent;
472: }
473:
474: /*
475: * Resets both user layout and user preferences.
476: * Note that if any of the two are "null", old values will be used.
477: */
478: public void setNewUserLayoutAndUserPreferences(
479: IUserLayoutManager newUlm, UserPreferences newPreferences)
480: throws PortalException {
481: try {
482: if (newPreferences != null) {
483: // see if the profile has changed
484: if (complete_up.getProfile().getProfileId() != newPreferences
485: .getProfile().getProfileId()
486: || complete_up.getProfile().isSystemProfile() != newPreferences
487: .getProfile().isSystemProfile()) {
488: // see if a layout was passed
489: if (newUlm != null
490: && newUlm.getLayoutId() == newPreferences
491: .getProfile().getLayoutId()) {
492: // just use a new layout
493: this .ulm = newUlm;
494: } else {
495: // construct a new user layout manager, for a new profile
496: ulm = UserLayoutManagerFactory
497: .getUserLayoutManager(m_person,
498: newPreferences.getProfile());
499: }
500: }
501: ulsdb.putUserPreferences(m_person, newPreferences);
502: complete_up = newPreferences;
503:
504: }
505: } catch (Exception e) {
506: log.error("Exception setting new user layout manager "
507: + newUlm + " and/or new prefererences "
508: + newPreferences, e);
509: throw new GeneralRenderingException(e);
510: }
511: }
512:
513: public IUserLayoutManager getUserLayoutManager() {
514: return ulm;
515: }
516:
517: public void finishedSession(HttpSessionBindingEvent bindingEvent) {
518: // persist the layout and user preferences
519: try {
520: if (saveUserPreferencesAtLogout) {
521: ulsdb.putUserPreferences(m_person, complete_up);
522: ulm.saveUserLayout();
523: }
524: } catch (Exception e) {
525: log
526: .error(
527: "UserPreferencesManager::finishedSession() : unable to persist layout upon session termination !",
528: e);
529: }
530: }
531:
532: public UserPreferences getUserPreferencesCopy() {
533: return new UserPreferences(this .getUserPreferences());
534: }
535:
536: public UserProfile getCurrentProfile() {
537: return this .getUserPreferences().getProfile();
538: }
539:
540: public ThemeStylesheetDescription getThemeStylesheetDescription()
541: throws Exception {
542: if (this .tsd == null) {
543: tsd = ulsdb.getThemeStylesheetDescription(this
544: .getCurrentProfile().getThemeStylesheetId());
545: }
546: return tsd;
547: }
548:
549: public StructureStylesheetDescription getStructureStylesheetDescription()
550: throws Exception {
551: if (this .ssd == null) {
552: ssd = ulsdb.getStructureStylesheetDescription(this
553: .getCurrentProfile().getStructureStylesheetId());
554: }
555: return ssd;
556: }
557:
558: public UserPreferences getUserPreferences() {
559: return complete_up;
560: }
561: }
|