001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 * 
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.file;
029
030import org.opencms.main.CmsIllegalArgumentException;
031import org.opencms.main.OpenCms;
032import org.opencms.security.CmsOrganizationalUnit;
033import org.opencms.util.CmsResourceTranslator;
034import org.opencms.util.CmsUUID;
035import org.opencms.workplace.CmsWorkplace;
036
037import java.util.Hashtable;
038import java.util.Locale;
039import java.util.Map;
040
041/**
042 * Stores the information about the current users OpenCms context,
043 * for example the requested URI, the current project, the selected site and more.<p>  
044 * 
045 * @since 6.0.0 
046 */
047public final class CmsRequestContext {
048
049    /** Request context attribute for indicating that an editor is currently open. */
050    public static final String ATTRIBUTE_EDITOR = CmsRequestContext.class.getName() + ".ATTRIBUTE_EDITOR";
051
052    /** Request context attribute for indicating we want full links generated for HTML fields. */
053    public static final String ATTRIBUTE_FULLLINKS = CmsRequestContext.class.getName() + ".ATTRIBUTE_FULLLINKS";
054
055    /** Request context attribute for indicating the model file for a create resource operation. */
056    public static final String ATTRIBUTE_MODEL = CmsRequestContext.class.getName() + ".ATTRIBUTE_MODEL";
057
058    /** Request context attribute for indicating content locale for a create resource operation. */
059    public static final String ATTRIBUTE_NEW_RESOURCE_LOCALE = CmsRequestContext.class.getName()
060        + ".NEW_RESOURCE_LOCALE";
061
062    /** A map for storing (optional) request context attributes. */
063    private Map<String, Object> m_attributeMap;
064
065    /** The current project. */
066    private CmsProject m_currentProject;
067
068    /** The detail content resource (possibly null). */
069    private CmsResource m_detailResource;
070
071    /** Directory name translator. */
072    private CmsResourceTranslator m_directoryTranslator;
073
074    /** Current encoding. */
075    private String m_encoding;
076
077    /** File name translator. */
078    private CmsResourceTranslator m_fileTranslator;
079
080    /** The locale used by this request context. */
081    private Locale m_locale;
082
083    /** The fully qualified name of the organizational unit for this request. */
084    private String m_ouFqn;
085
086    /** The remote ip address. */
087    private String m_remoteAddr;
088
089    /** The current request time. */
090    private long m_requestTime;
091
092    /** The name of the root, e.g. /site_a/vfs. */
093    private String m_siteRoot;
094
095    /** Flag to indicate that this context should not update the user session. */
096    private boolean m_updateSession;
097
098    /** The URI for getUri() in case it is "overwritten".  */
099    private String m_uri;
100
101    /** The current user. */
102    private CmsUser m_user;
103
104    /**
105     * Constructs a new request context.<p>
106     * 
107     * @param user the current user
108     * @param project the current project
109     * @param requestedUri the requested OpenCms VFS URI
110     * @param siteRoot the users current site root
111     * @param locale the users current locale 
112     * @param encoding the encoding to use for this request
113     * @param remoteAddr the remote IP address of the user
114     * @param requestTime the time of the request (used for resource publication / expiration date)
115     * @param directoryTranslator the directory translator
116     * @param fileTranslator the file translator
117     * @param ouFqn the fully qualified name of the organizational unit
118     */
119    public CmsRequestContext(
120        CmsUser user,
121        CmsProject project,
122        String requestedUri,
123        String siteRoot,
124        Locale locale,
125        String encoding,
126        String remoteAddr,
127        long requestTime,
128        CmsResourceTranslator directoryTranslator,
129        CmsResourceTranslator fileTranslator,
130        String ouFqn) {
131
132        m_updateSession = true;
133        m_user = user;
134        m_currentProject = project;
135        m_uri = requestedUri;
136        setSiteRoot(siteRoot);
137        m_locale = locale;
138        m_encoding = encoding;
139        m_remoteAddr = remoteAddr;
140        m_requestTime = requestTime;
141        m_directoryTranslator = directoryTranslator;
142        m_fileTranslator = fileTranslator;
143        setOuFqn(ouFqn);
144    }
145
146    /**
147     * Returns the adjusted site root for a resource using the provided site root as a base.<p>
148     * 
149     * Usually, this would be the site root for the current site.
150     * However, if a resource from the <code>/system/</code> folder is requested,
151     * this will be the empty String.<p>
152     * 
153     * @param siteRoot the site root of the current site
154     * @param resourcename the resource name to get the adjusted site root for
155     * 
156     * @return the adjusted site root for the resource
157     */
158    public static String getAdjustedSiteRoot(String siteRoot, String resourcename) {
159
160        if (resourcename.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)
161            || OpenCms.getSiteManager().startsWithShared(resourcename)) {
162            return "";
163        } else {
164            return siteRoot;
165        }
166    }
167
168    /**
169     * Adds the current site root of this context to the given resource name,
170     * and also translates the resource name with the configured the directory translator.<p>
171     * 
172     * @param resourcename the resource name
173     * @return the translated resource name including site root
174     * @see #addSiteRoot(String, String)
175     */
176    public String addSiteRoot(String resourcename) {
177
178        return addSiteRoot(m_siteRoot, resourcename);
179    }
180
181    /**
182     * Adds the given site root of this context to the given resource name,
183     * taking into account special folders like "/system" where no site root must be added,
184     * and also translates the resource name with the configured the directory translator.<p>
185     * 
186     * @param siteRoot the site root to add
187     * @param resourcename the resource name
188     * @return the translated resource name including site root
189     */
190    public String addSiteRoot(String siteRoot, String resourcename) {
191
192        if ((resourcename == null) || (siteRoot == null)) {
193            return null;
194        }
195        siteRoot = getAdjustedSiteRoot(siteRoot, resourcename);
196        StringBuffer result = new StringBuffer(128);
197        result.append(siteRoot);
198        if (((siteRoot.length() == 0) || (siteRoot.charAt(siteRoot.length() - 1) != '/'))
199            && ((resourcename.length() == 0) || (resourcename.charAt(0) != '/'))) {
200            // add slash between site root and resource if required
201            result.append('/');
202        }
203        result.append(resourcename);
204        return m_directoryTranslator.translateResource(result.toString());
205    }
206
207    /**
208     * Returns the current project of the current user.
209     *
210     * @return the current project of the current user
211     * 
212     * @deprecated use {@link #getCurrentProject()} instead
213     */
214    @Deprecated
215    public CmsProject currentProject() {
216
217        return getCurrentProject();
218    }
219
220    /**
221     * Returns the current user object.<p>
222     *
223     * @return the current user object
224     * 
225     * @deprecated use {@link #getCurrentUser()} instead
226     */
227    @Deprecated
228    public CmsUser currentUser() {
229
230        return getCurrentUser();
231    }
232
233    /**
234     * Returns the adjusted site root for a resoure this context current site root.<p>
235     * 
236     * @param resourcename the resource name to get the adjusted site root for
237     * @return the adjusted site root for the resoure
238     * @see #getAdjustedSiteRoot(String, String)
239     */
240    public String getAdjustedSiteRoot(String resourcename) {
241
242        return getAdjustedSiteRoot(m_siteRoot, resourcename);
243    }
244
245    /**
246     * Gets the value of an attribute from the OpenCms request context attribute list.<p>
247     * 
248     * @param attributeName the attribute name
249     * @return Object the attribute value, or <code>null</code> if the attribute was not found
250     */
251    public Object getAttribute(String attributeName) {
252
253        if (m_attributeMap == null) {
254            return null;
255        }
256        return m_attributeMap.get(attributeName);
257    }
258
259    /**
260     * Returns the current project of the current user.
261     *
262     * @return the current project of the current user
263     */
264    public CmsProject getCurrentProject() {
265
266        return m_currentProject;
267    }
268
269    /**
270     * Returns the current user object.<p>
271     *
272     * @return the current user object
273     */
274    public CmsUser getCurrentUser() {
275
276        return m_user;
277    }
278
279    /**
280     * Gets the detail content structure id (or null if no detail content has been loaded).<p>
281     * 
282     * @return the detail content id 
283     */
284    public CmsUUID getDetailContentId() {
285
286        if (m_detailResource == null) {
287            return null;
288        }
289        return m_detailResource.getStructureId();
290    }
291
292    /**
293     * Gets the detail content resource (or null if no detail content has been loaded).<p>
294     * 
295     * @return the detail content resource 
296     */
297    public CmsResource getDetailResource() {
298
299        return m_detailResource;
300    }
301
302    /**
303     * Returns the directory name translator this context was initialized with.<p>
304     * 
305     * The directory translator is used to translate old VFS path information 
306     * to a new location. Example: <code>/bodys/index.html --> /system/bodies/</code>.<p>
307     * 
308     * @return the directory name translator this context was initialized with
309     */
310    public CmsResourceTranslator getDirectoryTranslator() {
311
312        return m_directoryTranslator;
313    }
314
315    /**
316     * Returns the current content encoding to be used in HTTP response.<p>
317     * 
318     * @return the encoding
319     */
320    public String getEncoding() {
321
322        return m_encoding;
323    }
324
325    /**
326     * Returns the file name translator this context was initialized with.<p>
327     * 
328     * The file name translator is used to translate filenames from uploaded files  
329     * to valid OpenCms filenames. Example: <code>W&uuml;ste W&ouml;rter.doc --> Wueste_Woerter.doc</code>.<p>
330     * 
331     * @return the file name translator this context was initialized with
332     */
333    public CmsResourceTranslator getFileTranslator() {
334
335        return m_fileTranslator;
336    }
337
338    /**
339     * Gets the name of the parent folder of the requested file.<p>
340     *
341     * @return the name of the parent folder of the requested file
342     */
343    public String getFolderUri() {
344
345        return CmsResource.getFolderPath(m_uri);
346    }
347
348    /**
349     * Returns the locale used by this request context.<p>
350     * 
351     * In normal operation, the request context locale is initialized using
352     * {@link org.opencms.i18n.I_CmsLocaleHandler#getI18nInfo(javax.servlet.http.HttpServletRequest, CmsUser, CmsProject, String)} 
353     * depending on the requested resource URI.<p>
354     * 
355     * @return the locale used by this request context
356     * 
357     * @see org.opencms.i18n.I_CmsLocaleHandler#getI18nInfo(javax.servlet.http.HttpServletRequest, CmsUser, CmsProject, String)
358     * @see org.opencms.i18n.CmsLocaleManager#getDefaultLocale(CmsObject, String)
359     */
360    public Locale getLocale() {
361
362        return m_locale;
363    }
364
365    /**
366     * Returns the fully qualified name of the organizational unit.<p>
367     * 
368     * @return the fully qualified name of the organizational unit
369     */
370    public String getOuFqn() {
371
372        return m_ouFqn;
373    }
374
375    /**
376     * Returns the remote ip address.<p>
377     * 
378     * @return the remote ip address as string
379     */
380    public String getRemoteAddress() {
381
382        return m_remoteAddr;
383    }
384
385    /**
386     * Returns the current request time.<p>
387     * 
388     * @return the current request time
389     */
390    public long getRequestTime() {
391
392        return m_requestTime;
393    }
394
395    /**
396     * Returns this request contexts uri extended with the current site root path.<p>
397     * 
398     * @return this request contexts uri extended with the current site root path
399     * 
400     * @see #getUri()
401     * @see #addSiteRoot(String)
402     */
403    public String getRootUri() {
404
405        return addSiteRoot(m_siteRoot, m_uri);
406    }
407
408    /**
409     * Adjusts the absolute resource root path for the current site.<p> 
410     * 
411     * The full root path of a resource is always available using
412     * <code>{@link CmsResource#getRootPath()}</code>. From this name this method cuts 
413     * of the current site root using 
414     * <code>{@link CmsRequestContext#removeSiteRoot(String)}</code>.<p>
415     * 
416     * If the resource root path does not start with the current site root,
417     * it is left untouched.<p>
418     * 
419     * @param resource the resource to get the adjusted site root path for
420     * 
421     * @return the absolute resource path adjusted for the current site
422     * 
423     * @see #removeSiteRoot(String)
424     * @see CmsResource#getRootPath()
425     * @see CmsObject#getSitePath(CmsResource)
426     */
427    public String getSitePath(CmsResource resource) {
428
429        return removeSiteRoot(resource.getRootPath());
430    }
431
432    /**
433     * Returns the current root directory in the virtual file system.<p>
434     * 
435     * @return the current root directory in the virtual file system
436     */
437    public String getSiteRoot() {
438
439        return m_siteRoot;
440    }
441
442    /**
443     * Returns the OpenCms VFS URI of the requested resource.<p>
444     *
445     * @return the OpenCms VFS URI of the requested resource
446     */
447    public String getUri() {
448
449        return m_uri;
450    }
451
452    /**
453     * Check if this request context will update the session.<p>
454     * 
455     * This is used mainly for CmsReports that continue to use the 
456     * users context, even after the http request is already finished.<p>
457     *
458     * @return true if this request context will update the session, false otherwise
459     */
460    public boolean isUpdateSessionEnabled() {
461
462        return m_updateSession;
463    }
464
465    /**
466     * Removes an attribute from the request context.<p>
467     * 
468     * @param key the name of the attribute to remove
469     * 
470     * @return the removed attribute, or <code>null</code> if no attribute was set with this name
471     */
472    public Object removeAttribute(String key) {
473
474        if (m_attributeMap != null) {
475            return m_attributeMap.remove(key);
476        }
477        return null;
478    }
479
480    /**
481     * Removes the current site root prefix from the absolute path in the resource name,
482     * that is adjusts the resource name for the current site root.<p> 
483     * 
484     * If the resource name does not start with the current site root,
485     * it is left untouched.<p>
486     * 
487     * @param resourcename the resource name
488     * 
489     * @return the resource name adjusted for the current site root
490     * 
491     * @see #getSitePath(CmsResource)
492     */
493    public String removeSiteRoot(String resourcename) {
494
495        String siteRoot = getAdjustedSiteRoot(m_siteRoot, resourcename);
496        if ((siteRoot == m_siteRoot)
497            && resourcename.startsWith(siteRoot)
498            && ((resourcename.length() == siteRoot.length()) || (resourcename.charAt(siteRoot.length()) == '/'))) {
499            resourcename = resourcename.substring(siteRoot.length());
500        }
501        if (resourcename.length() == 0) {
502            // input was a site root folder without trailing slash
503            resourcename = "/";
504        }
505        return resourcename;
506    }
507
508    /**
509     * Sets an attribute in the request context.<p>
510     * 
511     * @param key the attribute name
512     * @param value the attribute value
513     */
514    public void setAttribute(String key, Object value) {
515
516        if (m_attributeMap == null) {
517            // hash table is still the most efficient form of a synchronized Map
518            m_attributeMap = new Hashtable<String, Object>();
519        }
520        m_attributeMap.put(key, value);
521    }
522
523    /**
524     * Sets the current project for the user.<p>
525     *
526     * @param project the project to be set as current project
527     * 
528     * @return the CmsProject instance
529     */
530    public CmsProject setCurrentProject(CmsProject project) {
531
532        if (project != null) {
533            m_currentProject = project;
534        }
535        return m_currentProject;
536    }
537
538    /**
539     * Sets the detail content resource.<p>
540     * 
541     * @param detailResource the detail content resource 
542     */
543    public void setDetailResource(CmsResource detailResource) {
544
545        m_detailResource = detailResource;
546    }
547
548    /**
549     * Sets the current content encoding to be used in HTTP response.<p>
550     * 
551     * @param encoding the encoding
552     */
553    public void setEncoding(String encoding) {
554
555        m_encoding = encoding;
556    }
557
558    /**
559     * Sets the locale used by this request context.<p>
560     * 
561     * @param locale the locale to set
562     * 
563     * @see #getLocale() for more information about how the locale is set in normal operation
564     */
565    public void setLocale(Locale locale) {
566
567        m_locale = locale;
568    }
569
570    /**
571     * Sets the organizational unit fully qualified name.<p>
572     * 
573     * @param ouFqn the organizational unit fully qualified name
574     */
575    public void setOuFqn(String ouFqn) {
576
577        String userOu = CmsOrganizationalUnit.getParentFqn(m_user.getName());
578        if (ouFqn != null) {
579            if (ouFqn.startsWith(userOu)
580                || (ouFqn.startsWith(CmsOrganizationalUnit.SEPARATOR) && ouFqn.substring(1).startsWith(userOu))) {
581                m_ouFqn = ouFqn;
582            } else {
583                throw new CmsIllegalArgumentException(Messages.get().container(
584                    Messages.ERR_BAD_ORGUNIT_2,
585                    ouFqn,
586                    userOu));
587            }
588        } else {
589            m_ouFqn = userOu;
590        }
591        m_ouFqn = CmsOrganizationalUnit.removeLeadingSeparator(m_ouFqn);
592    }
593
594    /**
595     * Sets the current request time.<p>
596     * 
597     * @param time the request time
598     */
599    public void setRequestTime(long time) {
600
601        m_requestTime = time;
602    }
603
604    /**
605     * Sets the current root directory in the virtual file system.<p>
606     * 
607     * @param root the name of the new root directory
608     */
609    public void setSiteRoot(String root) {
610
611        // site roots must never end with a "/"
612        if (root.endsWith("/")) {
613            m_siteRoot = root.substring(0, root.length() - 1);
614        } else {
615            m_siteRoot = root;
616        }
617    }
618
619    /**
620     * Mark this request context to update the session or not.<p>
621     *
622     * @param value true if this request context will update the session, false otherwise
623     */
624    public void setUpdateSessionEnabled(boolean value) {
625
626        m_updateSession = value;
627    }
628
629    /**
630     * Set the requested resource OpenCms VFS URI, that is the value returned by {@link #getUri()}.<p>
631     * 
632     * Use this with caution! Many things (caches etc.) depend on this value.
633     * If you change this value, better make sure that you change it only temporarily 
634     * and reset it in a <code>try { // do something // } finally { // reset URI // }</code> statement.<p>
635     * 
636     * @param value the value to set the Uri to, must be a complete OpenCms path name like /system/workplace/style.css
637     */
638    public void setUri(String value) {
639
640        m_uri = value;
641    }
642
643    /**
644     * Switches the user in the context, required after a login.<p>
645     * 
646     * @param user the new user to use
647     * @param project the new users current project
648     * @param ouFqn the organizational unit
649     */
650    protected void switchUser(CmsUser user, CmsProject project, String ouFqn) {
651
652        m_user = user;
653        m_currentProject = project;
654        setOuFqn(ouFqn);
655    }
656}