/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.web;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Provider;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.struts.upload.MultipartRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.xml.XMLUtils;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.plugin.fileupload.FileUploadPlugin;
import com.xpn.xwiki.util.Util;
public class Utils
{
protected static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
/** A key that is used for placing a map of replaced (for protection) strings in the context. */
private static final String PLACEHOLDERS_CONTEXT_KEY = Utils.class.getCanonicalName() + "_placeholders";
/** Whether placeholders are enabled or not. */
private static final String PLACEHOLDERS_ENABLED_CONTEXT_KEY = Utils.class.getCanonicalName()
+ "_placeholders_enabled";
/**
* The component manager used by {@link #getComponent(Class)} and {@link #getComponent(Class, String)}. It is useful
* for any non component code that need to initialize/access components.
*/
private static ComponentManager rootComponentManager;
/**
* Generate the response by parsing a velocity template and printing the result to the {@link XWikiResponse
* Response}. This is the main entry point to the View part of the XWiki MVC architecture.
*
* @param template The name of the template to parse, without the {@code .vm} prefix. The template will be searched
* in the usual places: current XWikiSkins object, attachment of the current skin document, current skin
* folder, baseskin folder, /templates/ folder.
* @param context the current context
* @throws XWikiException when the response cannot be written to the client (for example when the client canceled
* the request, thus closing the socket)
* @see XWiki#parseTemplate(String, XWikiContext)
*/
public static void parseTemplate(String template, XWikiContext context) throws XWikiException
{
parseTemplate(template, true, context);
}
/**
* Generate the response by parsing a velocity template and (optionally) printing the result to the
* {@link XWikiResponse Response}.
*
* @param template The name of the template to parse, without the {@code .vm} prefix. The template will be searched
* in the usual places: current XWikiSkins object, attachment of the current skin document, current skin
* folder, baseskin folder, /templates/ folder.
* @param write Whether the generated response should be written to the client or not. If {@code false}, only the
* needed headers are generated, suitable for implementing a HEAD response.
* @param context the current context
* @throws XWikiException when the response cannot be written to the client (for example when the client canceled
* the request, thus closing the socket)
* @see XWiki#parseTemplate(String, XWikiContext)
*/
public static void parseTemplate(String template, boolean write, XWikiContext context) throws XWikiException
{
XWikiResponse response = context.getResponse();
// If a Redirect has already been sent then don't process the template since it means and we shouldn't write
// anymore to the servlet output stream!
// See: http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#sendRedirect(String)
// "After using this method, the response should be considered to be committed and should not be written
// to."
if ((response instanceof XWikiServletResponse)
&& ((XWikiServletResponse) response).getStatus() == HttpServletResponse.SC_FOUND) {
return;
}
// Set content-type and encoding (this can be changed later by pages themselves)
response.setContentType("text/html; charset=" + context.getWiki().getEncoding());
String action = context.getAction();
long cacheSetting = context.getWiki().getXWikiPreferenceAsLong("headers_nocache", -1, context);
if (cacheSetting == -1) {
cacheSetting = context.getWiki().ParamAsLong("xwiki.httpheaders.cache", -1);
}
if (cacheSetting == -1) {
cacheSetting = 1;
}
if ((!"download".equals(action)) && (!"skin".equals(action))) {
if (context.getResponse() instanceof XWikiServletResponse) {
// Add a last modified to tell when the page was last updated
if (context.getWiki().getXWikiPreferenceAsLong("headers_lastmodified", 0, context) != 0) {
if (context.getDoc() != null) {
response.setDateHeader("Last-Modified", context.getDoc().getDate().getTime());
}
}
// Set a nocache to make sure the page is reloaded after an edit
if (cacheSetting == 1) {
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
} else if (cacheSetting == 2) {
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "max-age=0, no-cache, no-store");
} else if (cacheSetting == 3) {
response.setHeader("Cache-Control", "private");
} else if (cacheSetting == 4) {
response.setHeader("Cache-Control", "public");
}
// Set an expires in one month
long expires = context.getWiki().getXWikiPreferenceAsLong("headers_expires", -1, context);
if (expires == -1) {
response.setDateHeader("Expires", -1);
} else if (expires != 0) {
response.setDateHeader("Expires", (new Date()).getTime() + 30 * 24 * 3600 * 1000L);
}
}
}
if (("download".equals(action)) || ("skin".equals(action))) {
// Set a nocache to make sure these files are not cached by proxies
if (cacheSetting == 1 || cacheSetting == 2) {
response.setHeader("Cache-Control", "no-cache");
}
}
context.getWiki().getPluginManager().beginParsing(context);
// This class allows various components in the rendering chain to use placeholders for some fragile data. For
// example, the URL generated for the image macro should not be further rendered, as it might get broken by wiki
// filters. For this to work, keep a map of [used placeholders -> values] in the context, and replace them when
// the content is fully rendered. The rendering code can use Utils.createPlaceholder.
// Initialize the placeholder map
enablePlaceholders(context);
String content = "";
try {
// Note: This line below can change the state of the response. For example a vm file can have a call to
// sendRedirect. In this case we need to be careful to not write to the output stream since it's already
// been committed. This is why we do a check below before calling response.getOutputStream().write().
content = context.getWiki().evaluateTemplate(template + ".vm", context);
// Replace all placeholders with the protected values
content = replacePlaceholders(content, context);
disablePlaceholders(context);
content = context.getWiki().getPluginManager().endParsing(content.trim(), context);
} catch (IOException e) {
LOGGER.debug("IOException while evaluating template [{}] from /templates/", template, e);
// get Error template "This template does not exist
try {
content = context.getWiki().evaluateTemplate("templatedoesnotexist.vm", context);
content = content.trim();
} catch (IOException ex) {
// Cannot write output, can't do anything else
}
}
if (!context.isFinished()) {
if (context.getResponse() instanceof XWikiServletResponse) {
// Set the content length to the number of bytes, not the
// string length, so as to handle multi-byte encodings
try {
response.setContentLength(content.getBytes(context.getWiki().getEncoding()).length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
// We only write if the caller has asked.
// We also make sure to verify that there hasn't been a call to sendRedirect before since it would mean the
// response has already been written to and we shouldn't try to write in it.
if (write
&& ((response instanceof XWikiServletResponse) && ((XWikiServletResponse) response).getStatus() != HttpServletResponse.SC_FOUND)) {
try {
try {
response.getOutputStream().write(content.getBytes(context.getWiki().getEncoding()));
} catch (IllegalStateException ex) {
response.getWriter().write(content);
}
} catch (IOException e) {
throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
XWikiException.ERROR_XWIKI_APP_SEND_RESPONSE_EXCEPTION, "Exception while sending response", e);
}
}
}
try {
response.getOutputStream().flush();
} catch (Throwable ex) {
try {
response.getWriter().flush();
} catch (Throwable ex2) {
}
}
}
/**
* Retrieve the URL to which the client should be redirected after the successful completion of the requested
* action. This is taken from the {@code xredirect} parameter in the query string. If this parameter is not set, or
* is set to an empty value, return the default redirect specified as the second argument.
*
* @param request the current request
* @param defaultRedirect the default value to use if no {@code xredirect} parameter is present
* @return the destination URL, as specified in the {@code xredirect} parameter, or the specified default URL
*/
public static String getRedirect(XWikiRequest request, String defaultRedirect)
{
String redirect = request.getParameter("xredirect");
if (StringUtils.isBlank(redirect)) {
redirect = defaultRedirect;
}
return redirect;
}
/**
* Retrieve the URL to which the client should be redirected after the successful completion of the requested
* action. This is taken from the {@code xredirect} parameter in the query string. If this parameter is not set, or
* is set to an empty value, compose an URL back to the current document, using the specified action and query
* string, and return it.
*
* @param action the XWiki action to use for composing the default redirect URL ({@code view}, {@code edit}, etc)
* @param queryString the query parameters to append to the fallback URL
* @param context the current context
* @return the destination URL, as specified in the {@code xredirect} parameter, or computed using the current
* document and the specified action and query string
*/
public static String getRedirect(String action, String queryString, XWikiContext context)
{
return getRedirect(action, queryString, "xredirect");
}
/**
* Retrieve the URL to which the client should be redirected after the successful completion of the requested
* action. If any of the specified {@code redirectParameters} (in order) is present in the query string, it is
* returned as the redirect destination. If none of the parameters is set, compose an URL back to the current
* document using the specified action and query string, and return it.
*
* @param action the XWiki action to use for composing the default redirect URL ({@code view}, {@code edit}, etc)
* @param queryString the query parameters to append to the fallback URL
* @param redirectParameters list of request parameters to look for as the redirect destination; each of the
* parameters is tried in the order they are passed, and the first one set to a non-empty value is
* returned, if any
* @return the destination URL, as specified in one of the {@code redirectParameters}, or computed using the current
* document and the specified action and query string
*/
public static String getRedirect(String action, String queryString, String... redirectParameters)
{
XWikiContext context = getContext();
XWikiRequest request = context.getRequest();
String redirect = null;
for (String p : redirectParameters) {
redirect = request.getParameter(p);
if (StringUtils.isNotEmpty(redirect)) {
break;
}
}
if (StringUtils.isEmpty(redirect)) {
redirect = context.getDoc().getURL(action, queryString, true, context);
}
return redirect;
}
/**
* Retrieve the URL to which the client should be redirected after the successful completion of the requested
* action. This is taken from the {@code xredirect} parameter in the query string. If this parameter is not set, or
* is set to an empty value, compose an URL back to the current document, using the specified action, and return it.
*
* @param action the XWiki action to use for composing the default redirect URL ({@code view}, {@code edit}, etc)
* @param context the current context
* @return the destination URL, as specified in the {@code xredirect} parameter, or computed using the current
* document and the specified action
*/
public static String getRedirect(String action, XWikiContext context)
{
return getRedirect(action, null, context);
}
/**
* Retrieve the name of the velocity template which should be used to generate the response. This is taken from the
* {@code xpage} parameter in the query string. If this parameter is not set, or is set to an empty value, return
* the provided default name.
*
* @param request the current request
* @param defaultpage the default value to use if no {@code xpage} parameter is set
* @return the name of the requested template, as specified in the {@code xpage} parameter, or the specified default
* template
*/
public static String getPage(XWikiRequest request, String defaultpage)
{
String page = request.getParameter("xpage");
if (StringUtils.isEmpty(page)) {
page = defaultpage;
}
return page;
}
/**
* Get the name of an uploaded file, corresponding to the specified form field.
*
* @param filelist the list of uploaded files, computed by the FileUpload plugin
* @param name the name of the form field
* @return the original name of the file, if the specified field name does correspond to an uploaded file, or
* {@code null} otherwise
*/
public static String getFileName(List<FileItem> filelist, String name)
{
for (FileItem item : filelist) {
if (name.equals(item.getFieldName())) {
return item.getName();
}
}
return null;
}
/**
* Get the content of an uploaded file, corresponding to the specified form field.
*
* @param filelist the list of uploaded files, computed by the FileUpload plugin
* @param name the name of the form field
* @return the content of the file, if the specified field name does correspond to an uploaded file, or {@code null}
* otherwise
* @throws XWikiException if the file cannot be read due to an underlying I/O exception
*/
public static byte[] getContent(List<FileItem> filelist, String name) throws XWikiException
{
for (FileItem item : filelist) {
if (name.equals(item.getFieldName())) {
byte[] data = new byte[(int) item.getSize()];
InputStream fileis = null;
try {
fileis = item.getInputStream();
fileis.read(data);
} catch (IOException e) {
throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
XWikiException.ERROR_XWIKI_APP_UPLOAD_FILE_EXCEPTION,
"Exception while reading uploaded parsed file", e);
} finally {
IOUtils.closeQuietly(fileis);
}
return data;
}
}
return null;
}
public static XWikiContext prepareContext(String action, XWikiRequest request, XWikiResponse response,
XWikiEngineContext engine_context) throws XWikiException
{
XWikiContext context = new XWikiContext();
String dbname = "xwiki";
URL url = XWiki.getRequestURL(request);
context.setURL(url);
context.setEngineContext(engine_context);
context.setRequest(request);
context.setResponse(response);
context.setAction(action);
context.setWikiId(dbname);
int mode = 0;
if (request instanceof XWikiServletRequest) {
mode = XWikiContext.MODE_SERVLET;
}
context.setMode(mode);
return context;
}
/**
* Parse the request parameters from the specified String using the specified encoding. <strong>IMPLEMENTATION
* NOTE</strong>: URL decoding is performed individually on the parsed name and value elements, rather than on the
* entire query string ahead of time, to properly deal with the case where the name or value includes an encoded
* {@code =} or {@code &} character that would otherwise be interpreted as a delimiter.
* <p>
* Code borrowed from Apache Tomcat 5.0
*
* @param data input string containing request parameters
* @param encoding the encoding to use for transforming bytes into characters
* @throws IllegalArgumentException if the data is malformed
*/
public static Map<String, String[]> parseParameters(String data, String encoding)
throws UnsupportedEncodingException
{
if (!StringUtils.isEmpty(data)) {
// use the specified encoding to extract bytes out of the
// given string so that the encoding is not lost. If an
// encoding is not specified, let it use platform default
byte[] bytes = null;
try {
if (encoding == null) {
bytes = data.getBytes();
} else {
bytes = data.getBytes(encoding);
}
} catch (UnsupportedEncodingException uee) {
}
return parseParameters(bytes, encoding);
}
return Collections.emptyMap();
}
/**
* Parse the request parameters from the specified byte array using the specified encoding. <strong>IMPLEMENTATION
* NOTE</strong>: URL decoding is performed individually on the parsed name and value elements, rather than on the
* entire query string ahead of time, to properly deal with the case where the name or value includes an encoded
* {@code =} or {@code &} character that would otherwise be interpreted as a delimiter.
* <p>
* NOTE: byte array data is modified by this method. Caller beware.
* <p>
* Code borrowed from Apache Tomcat 5.0
*
* @param data input byte array containing request parameters
* @param encoding Encoding to use for converting hex
* @throws UnsupportedEncodingException if the data is malformed
*/
public static Map<String, String[]> parseParameters(byte[] data, String encoding)
throws UnsupportedEncodingException
{
Map<String, String[]> map = new HashMap<String, String[]>();
if (data != null && data.length > 0) {
int ix = 0;
int ox = 0;
String key = null;
String value = null;
while (ix < data.length) {
byte c = data[ix++];
switch ((char) c) {
case '&':
value = new String(data, 0, ox, encoding);
if (key != null) {
putMapEntry(map, key, value);
key = null;
}
ox = 0;
break;
case '=':
if (key == null) {
key = new String(data, 0, ox, encoding);
ox = 0;
} else {
data[ox++] = c;
}
break;
case '+':
data[ox++] = (byte) ' ';
break;
case '%':
data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++]));
break;
default:
data[ox++] = c;
}
}
// The last value does not end in '&'. So save it now.
if (key != null) {
value = new String(data, 0, ox, encoding);
putMapEntry(map, key, value);
}
}
return map;
}
/**
* Convert a byte character value to the corresponding hexidecimal digit value.
* <p>
* Code borrowed from Apache Tomcat 5.0
* </p>
*
* @param b the character value byte
*/
private static byte convertHexDigit(byte b)
{
if ((b >= '0') && (b <= '9')) {
return (byte) (b - '0');
}
if ((b >= 'a') && (b <= 'f')) {
return (byte) (b - 'a' + 10);
}
if ((b >= 'A') && (b <= 'F')) {
return (byte) (b - 'A' + 10);
}
return 0;
}
/**
* Put name-value pair in map. When an entry for {@code name} already exist, add the new value to the array of
* values.
* <p>
* Code borrowed from Apache Tomcat 5.0
* </p>
*
* @param map the map that is being constructed
* @param name the name of the parameter
* @param value the value of the parameter
*/
private static void putMapEntry(Map<String, String[]> map, String name, String value)
{
String[] newValues = null;
String[] oldValues = map.get(name);
if (oldValues == null) {
newValues = new String[] {value};
} else {
newValues = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = value;
}
map.put(name, newValues);
}
/**
* Escapes the XML special characters in a <code>String</code> using numerical XML entities.
*
* @param value the text to escape, may be null
* @return a new escaped <code>String</code>, <code>null</code> if null input
* @deprecated starting with 2.7 use {@link XMLUtils#escape(Object) $services.xml.escape(content)}
*/
@Deprecated
public static String formEncode(String value)
{
return XMLUtils.escape(value);
}
public static String SQLFilter(String text)
{
try {
return text.replaceAll("'", "''");
} catch (Exception e) {
return text;
}
}
/**
* @deprecated replaced by {@link com.xpn.xwiki.util.Util#encodeURI(String, XWikiContext)} since 1.3M2
*/
@Deprecated
public static String encode(String text, XWikiContext context)
{
return Util.encodeURI(text, context);
}
/**
* @deprecated replaced by {@link com.xpn.xwiki.util.Util#decodeURI(String, XWikiContext)} since 1.3M2
*/
@Deprecated
public static String decode(String text, XWikiContext context)
{
return Util.decodeURI(text, context);
}
/**
* Process a multi-part request, extracting all the uploaded files.
*
* @param request the current request to process
* @param context the current context
* @return the instance of the {@link FileUploadPlugin} used to parse the uploaded files
*/
public static FileUploadPlugin handleMultipart(HttpServletRequest request, XWikiContext context)
{
FileUploadPlugin fileupload = null;
try {
if (request instanceof MultipartRequestWrapper) {
fileupload = new FileUploadPlugin("fileupload", "fileupload", context);
context.put("fileuploadplugin", fileupload);
fileupload.loadFileList(context);
MultipartRequestWrapper mpreq = (MultipartRequestWrapper) request;
List<FileItem> fileItems = fileupload.getFileItems(context);
for (FileItem item : fileItems) {
if (item.isFormField()) {
String sName = item.getFieldName();
String sValue = item.getString(context.getWiki().getEncoding());
mpreq.setParameter(sName, sValue);
}
}
}
} catch (Exception e) {
if ((e instanceof XWikiException)
&& (((XWikiException) e).getCode() == XWikiException.ERROR_XWIKI_APP_FILE_EXCEPTION_MAXSIZE)) {
context.put("exception", e);
} else {
LOGGER.error("Failed to process MultiPart request", e);
}
}
return fileupload;
}
/**
* @param componentManager the root component manager used by {@link #getComponent(Class)} and
* {@link #getComponent(Class, String)}
*/
public static void setComponentManager(ComponentManager componentManager)
{
Utils.rootComponentManager = componentManager;
}
/**
* @return the root component manager
* @deprecated last resort way of accessing the {@link ComponentManager}, make sure you cannot do it any other way
* possible since it add a strong dependency to a static to your code
*/
@Deprecated
public static ComponentManager getRootComponentManager()
{
return rootComponentManager;
}
/**
* @return the contextual component manager used by {@link #getComponent(Class)} and
* {@link #getComponent(Class, String)}
* @deprecated since 6.1M1, use {@link #getContextComponentManager()} instead
*/
@Deprecated
public static ComponentManager getComponentManager()
{
ComponentManager contextComponentManager;
try {
contextComponentManager = rootComponentManager.getInstance(ComponentManager.class, "context/root");
} catch (ComponentLookupException e) {
// This means the Context Root CM doesn't exist, use the Root CM.
contextComponentManager = rootComponentManager;
LOGGER.debug("Failed to find the [context/root] Component Manager. Cause: [{}]. Using the Root Component "
+ "Manager", ExceptionUtils.getRootCauseMessage(e));
}
return contextComponentManager;
}
/**
* @return the contextual component manager used by {@link #getComponent(Class)} and
* {@link #getComponent(Class, String)}
* @since 6.0RC1
* @deprecated last resort way of accessing the {@link ComponentManager}, make sure you cannot do it any other way
* possible since it add a strong dependency to a static to your code
*/
@Deprecated
public static ComponentManager getContextComponentManager()
{
ComponentManager contextComponentManager;
try {
contextComponentManager = rootComponentManager.getInstance(ComponentManager.class, "context");
} catch (ComponentLookupException e) {
// This means the Context CM doesn't exist, use the Root CM.
contextComponentManager = rootComponentManager;
LOGGER.debug("Failed to find the [context] Component Manager. Cause: [{}]. Using the Root Component "
+ "Manager", ExceptionUtils.getRootCauseMessage(e));
}
return contextComponentManager;
}
/**
* Lookup a XWiki component by role and hint.
*
* @param role the class (aka role) that the component implements
* @param hint a value to differentiate different component implementations for the same role
* @return the component's instance
* @throws RuntimeException if the component cannot be found/initialized, or if the component manager is not
* initialized
* @deprecated since 4.0M1 use {@link #getComponent(Type, String)} instead
*/
@Deprecated
public static <T> T getComponent(Class<T> role, String hint)
{
return getComponent((Type) role, hint);
}
/**
* Lookup a XWiki component by role (uses the default hint).
*
* @param role the class (aka role) that the component implements
* @return the component's instance
* @throws RuntimeException if the component cannot be found/initialized, or if the component manager is not
* initialized
* @deprecated since 4.0M1 use {@link #getComponent(Type)} instead
*/
@Deprecated
public static <T> T getComponent(Class<T> role)
{
return getComponent((Type) role);
}
/**
* Lookup a XWiki component by role and hint.
*
* @param roleType the class (aka role) that the component implements
* @param roleHint a value to differentiate different component implementations for the same role
* @return the component's instance
* @throws RuntimeException if the component cannot be found/initialized, or if the component manager is not
* initialized
* @deprecated starting with 4.1M2 use the Component Script Service instead
*/
@Deprecated
public static <T> T getComponent(Type roleType, String roleHint)
{
T component;
ComponentManager componentManager = getContextComponentManager();
if (componentManager != null) {
try {
component = componentManager.getInstance(roleType, roleHint);
} catch (ComponentLookupException e) {
throw new RuntimeException("Failed to load component for type [" + roleType + "] for hint [" + roleHint
+ "]", e);
}
} else {
throw new RuntimeException("Component manager has not been initialized before lookup for [" + roleType
+ "] for hint [" + roleHint + "]");
}
return component;
}
/**
* Lookup a XWiki component by role (uses the default hint).
*
* @param roleType the class (aka role) that the component implements
* @return the component's instance
* @throws RuntimeException if the component cannot be found/initialized, or if the component manager is not
* initialized
* @deprecated starting with 4.1M2 use the Component Script Service instead
*/
@Deprecated
public static <T> T getComponent(Type roleType)
{
return getComponent(roleType, "default");
}
/**
* @param <T> the component type
* @param role the role for which to return implementing components
* @return all components implementing the passed role
* @throws RuntimeException if some of the components cannot be found/initialized, or if the component manager is
* not initialized
* @since 2.0M3
* @deprecated since 4.0M1 use {@link #getComponentManager()} instead
*/
@Deprecated
public static <T> List<T> getComponentList(Class<T> role)
{
List<T> components;
ComponentManager componentManager = getContextComponentManager();
if (componentManager != null) {
try {
components = componentManager.getInstanceList(role);
} catch (ComponentLookupException e) {
throw new RuntimeException("Failed to load components with role [" + role.getName() + "]", e);
}
} else {
throw new RuntimeException("Component manager has not been initialized before lookup for role ["
+ role.getName() + "]");
}
return components;
}
/**
* Helper method for obtaining a valid xcontext from the execution context.
* <p>
* NOTE: Don't use this method to access the XWiki context in a component because
* {@link #setComponentManager(ComponentManager)} is not called when running component unit tests. You have to take
* the XWiki context yourself from the injected Execution when inside a component. This method should be used only
* by non-component code.
*
* @return the current context or {@code null} if the execution context is not yet initialized
* @since 3.2M3
*/
public static XWikiContext getContext()
{
Provider<XWikiContext> xcontextProvider = getComponent(XWikiContext.TYPE_PROVIDER);
return xcontextProvider != null ? xcontextProvider.get() : null;
}
/**
* Check if placeholders are enabled in the current context.
*
* @param context The current context.
* @return <code>true</code> if placeholders can be used, <code>false</code> otherwise.
*/
public static boolean arePlaceholdersEnabled(XWikiContext context)
{
Boolean enabled = (Boolean) context.get(PLACEHOLDERS_ENABLED_CONTEXT_KEY);
return enabled != null && enabled;
}
/**
* Enable placeholder support in the current request context.
*
* @param context The current context.
*/
public static void enablePlaceholders(XWikiContext context)
{
context.put(PLACEHOLDERS_CONTEXT_KEY, new HashMap<String, String>());
context.put(PLACEHOLDERS_ENABLED_CONTEXT_KEY, new Boolean(true));
}
/**
* Disable placeholder support in the current request context.
*
* @param context The current context.
*/
public static void disablePlaceholders(XWikiContext context)
{
context.remove(PLACEHOLDERS_CONTEXT_KEY);
context.remove(PLACEHOLDERS_ENABLED_CONTEXT_KEY);
}
/**
* Create a placeholder key for a string that should be protected from further processing. The value is stored in
* the context, and the returned key can be used by the calling code as many times in the rendering result. At the
* end of the rendering process all placeholder keys are replaced with the values they replace.
*
* @param value The string to hide.
* @param context The current context.
* @return The key to be used instead of the value.
*/
public static String createPlaceholder(String value, XWikiContext context)
{
if (!arePlaceholdersEnabled(context)) {
return value;
}
@SuppressWarnings("unchecked")
Map<String, String> renderingKeys = (Map<String, String>) context.get(PLACEHOLDERS_CONTEXT_KEY);
String key;
do {
key = "KEY" + RandomStringUtils.randomAlphanumeric(10) + "KEY";
} while (renderingKeys.containsKey(key));
renderingKeys.put(key, value);
return key;
}
/**
* Insert back the replaced strings.
*
* @param content The rendered content, with placeholders.
* @param context The current context.
* @return The content with all placeholders replaced with the real values.
*/
public static String replacePlaceholders(String content, XWikiContext context)
{
if (!arePlaceholdersEnabled(context)) {
return content;
}
String result = content;
@SuppressWarnings("unchecked")
Map<String, String> renderingKeys = (Map<String, String>) context.get(PLACEHOLDERS_CONTEXT_KEY);
for (Entry<String, String> e : renderingKeys.entrySet()) {
result = result.replace(e.getKey(), e.getValue());
}
return result;
}
/**
* Verify if the current request is an AJAX request.
*
* @param context the current request context
* @return True if this is an AJAX request, false otherwise.
* @since 2.4M2
*/
public static Boolean isAjaxRequest(XWikiContext context)
{
return BooleanUtils.isTrue((Boolean) context.get("ajax"));
}
}