Commit 56a16b4f authored by daryl herzmann's avatar daryl herzmann Committed by GitHub

Merge pull request #828 from guusdk/OF-1353_maxServerVersion

Various plugin-related improvements.
parents 414074f2 2c82359e
......@@ -2644,6 +2644,8 @@ plugin.admin.version.available = Version {0} Available
plugin.admin.changelog = Change Log
plugin.admin.update = Update
plugin.admin.updating = Updating
plugin.admin.failed.minserverversion=The version of the plugin that is installed requires Openfire {0} or later versions!
plugin.admin.failed.priortoserverversion=The version of the plugin that is installed is no longer compatible with Openfire {0} and later versions!
# System Email
......@@ -2918,6 +2920,8 @@ plugin.available.installation.success = plugin installed successfully.
plugin.available.commercial_plugins = Commercial Plugins
plugin.available.outdated = The list of plugins below requires a newer version of the server.
plugin.available.outdated.update = Update the server now.
plugin.available.deprecated=These plugins that are installed are compatible with older versions of the server only.
plugin.available.autoupdate = Available plugins list auto-updated on
plugin.available.autoupdate.on = Auto update is turned on.
plugin.available.autoupdate.off = Auto update is off.
......
package org.jivesoftware.admin;
import org.jivesoftware.util.ByteFormat;
import org.jivesoftware.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
* Utility functions that are exposed through a taglib.
*
......@@ -28,4 +35,65 @@ public class JSTLFunctions
{
return string.split(regex);
}
/**
* A formatter for formatting byte sizes. For example, formatting 12345 byes results in
* "12.1 K" and 1234567 results in "1.18 MB".
*
* @see ByteFormat
*/
public static String byteFormat( long bytes )
{
return new ByteFormat().format( bytes );
}
/**
* Translates a string into {@code application/x-www-form-urlencoded} format using a specific encoding scheme. This
* method uses the UTF-8 encoding scheme to obtain the bytes for unsafe characters.
*
* @see URLEncoder
*/
public static String urlEncode( String string )
{
try
{
return URLEncoder.encode( string, "UTF-8" );
}
catch ( UnsupportedEncodingException e )
{
// Should never occur, as UTF-8 encoding is mantdatory to implement for any JRE.
throw new IllegalStateException( "Unable to URL-encode string: " + string, e );
}
}
/**
* Decodes a {@code application/x-www-form-urlencoded} string using the UTF-8 encoding scheme. The encoding is used
* to determine what characters are represented by any consecutive sequences of the form "<i>{@code %xy}</i>".
*
* @see URLDecoder
*/
public static String urlDecode( String string )
{
try
{
return URLDecoder.decode( string, "UTF-8" );
}
catch ( UnsupportedEncodingException e )
{
// Should never occur, as UTF-8 encoding is mantdatory to implement for any JRE.
throw new IllegalStateException( "Unable to URL-decode string: " + string, e );
}
}
/**
* This method takes a string which may contain HTML tags (ie, &lt7;b&gt;,
* &lt;table&gt;, etc) and converts the '&lt;' and '&gt;' characters to
* their HTML escape sequences. It will also replace LF with &lt;br&gt;.
*
* @see StringUtils#escapeHTMLTags(String);
*/
public static String escapeHTMLTags( String string )
{
return StringUtils.escapeHTMLTags( string );
}
}
/*
* Copyright 2016 IgniteRealtime.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.container;
/**
* An enumeration for license agreement types.
*/
public enum License
{
/**
* Distributed using a commercial license.
*/
commercial,
/**
* Distributed using the GNU Public License (GPL).
*/
gpl,
/**
* Distributed using the Apache license.
*/
apache,
/**
* For internal use at an organization only and is not re-distributed.
*/
internal,
/**
* Distributed under another license agreement not covered by one of the other choices. The license agreement
* should be detailed in a Readme or License file that accompanies the code.
*/
other
}
......@@ -29,6 +29,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
/**
* Servlet is used for retrieval of plugin icons.
......@@ -44,28 +45,22 @@ public class PluginIconServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
String pluginName = ParamUtils.getParameter(request, "plugin");
String canonicalName = ParamUtils.getParameter(request, "plugin");
PluginManager pluginManager = XMPPServer.getInstance().getPluginManager();
Plugin plugin = pluginManager.getPlugin(pluginName);
if (plugin != null) {
// Try looking for PNG file first then default to GIF.
File icon = new File(pluginManager.getPluginDirectory(plugin), "logo_small.png");
boolean isPng = true;
if (!icon.exists()) {
icon = new File(pluginManager.getPluginDirectory(plugin), "logo_small.gif");
isPng = false;
}
if (icon.exists()) {
PluginMetadata metadata = pluginManager.getMetadata( canonicalName );
if (metadata != null) {
final URL icon = metadata.getIcon();
if ( icon != null ) {
// Clear any empty lines added by the JSP declaration. This is required to show
// the image in resin!
response.reset();
if (isPng) {
if ( icon.toExternalForm().toLowerCase().endsWith( ".png" )) {
response.setContentType("image/png");
}
else {
else if (icon.toExternalForm().toLowerCase().endsWith( ".png" )) {
response.setContentType("image/gif");
}
try (InputStream in = new FileInputStream(icon)) {
try (InputStream in = icon.openStream()) {
try (OutputStream ost = response.getOutputStream()) {
byte[] buf = new byte[1024];
int len;
......
/*
* Copyright (C) 2017 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.openfire.container;
import org.jivesoftware.util.Version;
import java.net.URL;
import java.nio.file.Path;
/**
* A bean-like representation of the metadata of a plugin.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class PluginMetadata
{
/**
* Human readable name of the plugin.
*/
private final String name;
/**
* Canonical name of the plugin.
*/
private final String canonicalName;
/**
* Description of the plugin as specified in plugin.xml.
*/
private final String description;
/**
* The version of the plugin.
*/
private final Version version;
/**
* Author of the plugin as specified in plugin.xml.
*/
private final String author;
/**
* Icon's location of the plugin.
*/
private final URL icon;
/**
* Changelog location of the latest version of the plugin.
*/
private final URL changelog;
/**
* ReadMe location of the latest version of the plugin.
*/
private final URL readme;
/**
* Type of license of the plugin.
*/
private final String license;
/**
* Minimum server version (inclusive) required by this plugin as specified in plugin.xml.
*/
private final Version minServerVersion;
/**
* Maximum server version (exclusive) required by this plugin as specified in plugin.xml.
*/
private final Version priorToServerVersion;
/**
* Constructs a metadata object based on a plugin.
*
* The plugin must be installed in Openfire.
*
* @param pluginDir the path of the plugin directory (cannot be null)
* @return Metadata for the plugin (never null).
*/
public static PluginMetadata getInstance( Path pluginDir )
{
return new PluginMetadata(
PluginMetadataHelper.getName( pluginDir ),
PluginMetadataHelper.getCanonicalName( pluginDir ),
PluginMetadataHelper.getDescription( pluginDir ),
PluginMetadataHelper.getVersion( pluginDir ),
PluginMetadataHelper.getAuthor( pluginDir ),
PluginMetadataHelper.getIcon( pluginDir ),
PluginMetadataHelper.getChangelog( pluginDir ),
PluginMetadataHelper.getReadme( pluginDir ),
PluginMetadataHelper.getLicense( pluginDir ),
PluginMetadataHelper.getMinServerVersion( pluginDir ),
PluginMetadataHelper.getPriorToServerVersion( pluginDir )
);
}
/**
* Constructs a metadata object based on a plugin.
*
* The plugin must be installed in Openfire.
*
* @param plugin The plugin (cannot be null)
* @return Metadata for the plugin (never null).
*/
public static PluginMetadata getInstance( Plugin plugin )
{
return new PluginMetadata(
PluginMetadataHelper.getName( plugin ),
PluginMetadataHelper.getCanonicalName( plugin ),
PluginMetadataHelper.getDescription( plugin ),
PluginMetadataHelper.getVersion( plugin ),
PluginMetadataHelper.getAuthor( plugin ),
PluginMetadataHelper.getIcon( plugin ),
PluginMetadataHelper.getChangelog( plugin ),
PluginMetadataHelper.getReadme( plugin ),
PluginMetadataHelper.getLicense( plugin ),
PluginMetadataHelper.getMinServerVersion( plugin ),
PluginMetadataHelper.getPriorToServerVersion( plugin )
);
}
public PluginMetadata( String name, String canonicalName, String description, Version version, String author,
URL icon, URL changelog, URL readme, String license,
Version minServerVersion, Version priorToServerVersion )
{
this.name = name;
this.canonicalName = canonicalName;
this.description = description;
this.version = version;
this.author = author;
this.icon = icon;
this.changelog = changelog;
this.readme = readme;
this.license = license;
this.minServerVersion = minServerVersion;
this.priorToServerVersion = priorToServerVersion;
}
public String getName()
{
return name;
}
public String getCanonicalName()
{
return canonicalName;
}
public String getDescription()
{
return description;
}
public Version getVersion()
{
return version;
}
public String getAuthor()
{
return author;
}
public URL getIcon()
{
return icon;
}
public URL getChangelog()
{
return changelog;
}
public URL getReadme()
{
return readme;
}
public String getLicense()
{
return license;
}
public Version getMinServerVersion()
{
return minServerVersion;
}
public Version getPriorToServerVersion()
{
return priorToServerVersion;
}
public String getHashCode() {
return String.valueOf( hashCode() );
}
}
......@@ -151,12 +151,12 @@ public class PluginMonitor
for ( final Path jarFile : ds )
{
final String fileName = jarFile.getFileName().toString();
final String pluginName = fileName.substring( 0, fileName.length() - 4 ).toLowerCase(); // strip extension.
final String canonicalPluginName = fileName.substring( 0, fileName.length() - 4 ).toLowerCase(); // strip extension.
jarSet.add( pluginName );
jarSet.add( canonicalPluginName );
// See if the JAR has already been exploded.
final Path dir = pluginsDirectory.resolve( pluginName );
final Path dir = pluginsDirectory.resolve( canonicalPluginName );
// See if the JAR is newer than the directory. If so, the plugin needs to be unloaded and then reloaded.
if ( Files.exists( dir ) && Files.getLastModifiedTime( jarFile ).toMillis() > Files.getLastModifiedTime( dir ).toMillis() )
......@@ -174,14 +174,14 @@ public class PluginMonitor
else
{
// Not the first time? Properly unload the plugin.
pluginManager.unloadPlugin( pluginName );
pluginManager.unloadPlugin( canonicalPluginName );
}
}
// If the JAR needs to be exploded, do so.
if ( Files.notExists( dir ) )
{
unzipPlugin( pluginName, jarFile, dir );
unzipPlugin( canonicalPluginName, jarFile, dir );
}
}
}
......@@ -270,10 +270,10 @@ public class PluginMonitor
for ( final Path path : hierarchy )
{
// If the plugin hasn't already been started, start it.
final String pluginName = PluginMetadataHelper.getCanonicalName( path );
if ( pluginManager.getPlugin( pluginName ) == null )
final String canonicalName = PluginMetadataHelper.getCanonicalName( path );
if ( pluginManager.getPlugin( canonicalName ) == null )
{
if ( pluginManager.loadPlugin( path ) )
if ( pluginManager.loadPlugin( canonicalName, path ) )
{
loaded++;
}
......@@ -289,7 +289,7 @@ public class PluginMonitor
// of all plugins that attempt to modify the admin panel.
if ( pluginManager.getPlugin( "admin" ) == null )
{
pluginManager.loadPlugin( dirs.getFirst().get( 0 ) );
pluginManager.loadPlugin( "admin", dirs.getFirst().get( 0 ) );
}
// Hierarchies could be processed in parallel. This is likely to be beneficial during the first
......
......@@ -560,11 +560,11 @@ public class AgentSession {
}
/**
* This agent is not longer related to this offer. The agent may have been selected to answer
* This agent is no longer related to this offer. The agent may have been selected to answer
* the user's request or the offer has been assigned to another agent or the request was
* cancelled.
*
* @param offer the offer that is not longer related to this agent.
* @param offer the offer that is no longer related to this agent.
*/
public void removeOffer(Offer offer) {
if (offer.equals(this.offer)) {
......
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.2</tlib-version>
<short-name>Tag Library for Openfire</short-name>
<description>Tab Library for Openfire Admin Console</description>
<short-name>admin</short-name>
<uri>admin</uri>
<tag>
<name>tabs</name>
<tag-class>org.jivesoftware.admin.TabsTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>css</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentcss</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>role</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>minEdition</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>justlinks</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>subnavbar</name>
<tag-class>org.jivesoftware.admin.SubnavTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>css</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentcss</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>role</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>minEdition</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>sidebar</name>
<tag-class>org.jivesoftware.admin.SidebarTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>css</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentcss</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>headercss</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>role</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>minEdition</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>subsidebar</name>
<tag-class>org.jivesoftware.admin.SubSidebarTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>css</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>currentcss</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>role</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>minEdition</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>ASN1DER</name>
<tag-class>org.jivesoftware.admin.ASN1DERTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag-file>
<name>contentBox</name>
<path>/WEB-INF/tags/admin/contentBox.tagx</path>
</tag-file>
<tag-file>
<name>infobox</name>
<path>/WEB-INF/tags/admin/infoBox.tagx</path>
</tag-file>
<tag-file>
<name>infoBox</name>
<path>/WEB-INF/tags/admin/infoBox.tagx</path>
</tag-file>
<function>
<name>replaceAll</name>
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String replaceAll(java.lang.String, java.lang.String, java.lang.String)</function-signature>
</function>
<function>
<name>split</name>
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String[] split(java.lang.String, java.lang.String)</function-signature>
</function>
<function>
<name>serverIdentities</name>
<function-class>org.jivesoftware.util.CertificateManager</function-class>
<function-signature>java.util.List getServerIdentities(java.security.cert.X509Certificate)</function-signature>
</function>
<function>
<name>clientIdentities</name>
<function-class>org.jivesoftware.util.CertificateManager</function-class>
<function-signature>java.util.List getClientIdentities(java.security.cert.X509Certificate)</function-signature>
</function>
<function>
<name>getProperty</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>java.lang.String getProperty(java.lang.String,java.lang.String)</function-signature>
</function>
<function>
<name>getIntProperty</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>int getIntProperty(java.lang.String,int)</function-signature>
</function>
<function>
<name>getBooleanProperty</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>boolean getBooleanProperty(java.lang.String,boolean)</function-signature>
</function>
<function>
<name>getLongProperty</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>long getLongProperty(java.lang.String,long)</function-signature>
</function>
<function>
<name>getListProperty</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>java.util.List getListProperty(java.lang.String,java.util.List)</function-signature>
</function>
</taglib>
<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:jsp="http://java.sun.com/JSP/Page"
version="2.0">
<jsp:directive.attribute name="title" required="true" />
<div class="jive-contentBoxHeader">
<c:out value="${title}" />
</div>
<div class="jive-contentBox">
<jsp:doBody />
</div>
</jsp:root>
\ No newline at end of file
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
version="2.0">
<jsp:directive.attribute name="type" required="true" />
<div class="jive-${type}">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td class="jive-icon"><img src="images/${type}-16x16.gif" width="16" height="16" border="0" alt=""/></td>
<td class="jive-icon-label">
<jsp:doBody />
</td>
</tr>
</tbody>
</table>
</div>
<br />
</jsp:root>
This diff is collapsed.
This diff is collapsed.
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.openfire.container.Plugin" %>
<%@ page import="org.jivesoftware.openfire.container.PluginManager" %>
<%@ page import="org.jivesoftware.openfire.container.PluginMetadataHelper" %>
<%@ page import="java.nio.file.Path" %>
<%@ page import="java.io.*" %><%--
- Copyright (C) 2017 Ignite Realtime Foundation. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--%>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<%
webManager.init(request, response, session, application, out );
final boolean showReadme = ParamUtils.getBooleanParameter( request, "showReadme", false);
final boolean showChangelog = ParamUtils.getBooleanParameter(request, "showChangelog", false);
if (showReadme || showChangelog )
{
final PluginManager pluginManager = webManager.getXMPPServer().getPluginManager();
final String pluginName = ParamUtils.getParameter(request, "plugin" );
final Plugin plugin = pluginManager.getPlugin( pluginName );
if ( plugin != null )
{
final Path filePath;
final Path pluginPath = pluginManager.getPluginPath( plugin );
if ( showChangelog )
{
filePath = pluginPath.resolve( "changelog.html" );
}
else
{
filePath = pluginPath.resolve( "readme.html" );
}
if ( filePath.toFile().exists() )
{
try ( final BufferedReader in = new BufferedReader( new InputStreamReader( new FileInputStream( filePath.toFile() ), "UTF8") ) )
{
String line;
while ((line = in.readLine()) != null)
{
response.getWriter().println( line );
}
}
catch ( IOException ioe )
{
ioe.printStackTrace();
}
}
}
}
%>
\ No newline at end of file
......@@ -74,6 +74,11 @@
<artifactId>xmppserver</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.igniterealtime.openfire</groupId>
<artifactId>webadmintld</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
......
......@@ -4,7 +4,7 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
version="2.1">
<tlib-version>1.2</tlib-version>
<tlib-version>1.3</tlib-version>
<short-name>Tag Library for Openfire</short-name>
<description>Tab Library for Openfire Admin Console</description>
<short-name>admin</short-name>
......@@ -151,6 +151,26 @@
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String[] split(java.lang.String, java.lang.String)</function-signature>
</function>
<function>
<name>byteFormat</name>
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String byteFormat(long)</function-signature>
</function>
<function>
<name>urlEncode</name>
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String urlEncode(java.lang.String)</function-signature>
</function>
<function>
<name>urlDecode</name>
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String urlDecode(java.lang.String)</function-signature>
</function>
<function>
<name>escapeHTMLTags</name>
<function-class>org.jivesoftware.admin.JSTLFunctions</function-class>
<function-signature>java.lang.String escapeHTMLTags(java.lang.String)</function-signature>
</function>
<function>
<name>serverIdentities</name>
<function-class>org.jivesoftware.util.CertificateManager</function-class>
......@@ -186,4 +206,20 @@
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>java.util.List getListProperty(java.lang.String,java.util.List)</function-signature>
</function>
<function>
<name>formatDate</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>java.lang.String formatDate(java.util.Date)</function-signature>
</function>
<function>
<name>formatDateTime</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>java.lang.String formatDateTime(java.util.Date)</function-signature>
</function>
<function>
<name>formatTime</name>
<function-class>org.jivesoftware.util.JiveGlobals</function-class>
<function-signature>java.lang.String formatTime(java.util.Date)</function-signature>
</function>
</taglib>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment