Commit dbece192 authored by Guus der Kinderen's avatar Guus der Kinderen Committed by daryl herzmann

Avatar resizer plugin inlining (#914)

* AvatarPlugin: Add prior-to-openfire 4.2.0. restriction

* Merged avatarResizer plugin with Openfire core (prevents OF-1145 & OF-1193)
parent 5f89a1ad
...@@ -32,6 +32,7 @@ import org.dom4j.DocumentHelper; ...@@ -32,6 +32,7 @@ import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
import org.dom4j.Node; import org.dom4j.Node;
import org.jivesoftware.openfire.vcard.DefaultVCardProvider; import org.jivesoftware.openfire.vcard.DefaultVCardProvider;
import org.jivesoftware.openfire.vcard.PhotoResizer;
import org.jivesoftware.openfire.vcard.VCardManager; import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.openfire.vcard.VCardProvider; import org.jivesoftware.openfire.vcard.VCardProvider;
import org.jivesoftware.util.AlreadyExistsException; import org.jivesoftware.util.AlreadyExistsException;
...@@ -243,6 +244,12 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener { ...@@ -243,6 +244,12 @@ public class LdapVCardProvider implements VCardProvider, PropertyEventListener {
vcard.add(avatarElement); vcard.add(avatarElement);
} }
} }
if ( JiveGlobals.getBooleanProperty( PhotoResizer.PROPERTY_RESIZE_ON_LOAD, PhotoResizer.PROPERTY_RESIZE_ON_LOAD_DEFAULT ) )
{
PhotoResizer.resizeAvatar( vcard );
}
Log.debug("LdapVCardProvider: Returning vcard"); Log.debug("LdapVCardProvider: Returning vcard");
return vcard; return vcard;
} }
......
...@@ -28,6 +28,7 @@ import org.dom4j.Element; ...@@ -28,6 +28,7 @@ import org.dom4j.Element;
import org.dom4j.io.SAXReader; import org.dom4j.io.SAXReader;
import org.jivesoftware.database.DbConnectionManager; import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.AlreadyExistsException; import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.NotFoundException; import org.jivesoftware.util.NotFoundException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -98,6 +99,12 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -98,6 +99,12 @@ public class DefaultVCardProvider implements VCardProvider {
} }
DbConnectionManager.closeConnection(rs, pstmt, con); DbConnectionManager.closeConnection(rs, pstmt, con);
} }
if ( JiveGlobals.getBooleanProperty( PhotoResizer.PROPERTY_RESIZE_ON_LOAD, PhotoResizer.PROPERTY_RESIZE_ON_LOAD_DEFAULT ) )
{
PhotoResizer.resizeAvatar( vCardElement );
}
return vCardElement; return vCardElement;
} }
} }
...@@ -109,6 +116,11 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -109,6 +116,11 @@ public class DefaultVCardProvider implements VCardProvider {
throw new AlreadyExistsException("Username " + username + " already has a vCard"); throw new AlreadyExistsException("Username " + username + " already has a vCard");
} }
if ( JiveGlobals.getBooleanProperty( PhotoResizer.PROPERTY_RESIZE_ON_CREATE, PhotoResizer.PROPERTY_RESIZE_ON_CREATE_DEFAULT ) )
{
PhotoResizer.resizeAvatar( vCardElement );
}
Connection con = null; Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
try { try {
...@@ -133,6 +145,12 @@ public class DefaultVCardProvider implements VCardProvider { ...@@ -133,6 +145,12 @@ public class DefaultVCardProvider implements VCardProvider {
// The user already has a vCard // The user already has a vCard
throw new NotFoundException("Username " + username + " does not have a vCard"); throw new NotFoundException("Username " + username + " does not have a vCard");
} }
if ( JiveGlobals.getBooleanProperty( PhotoResizer.PROPERTY_RESIZE_ON_CREATE, PhotoResizer.PROPERTY_RESIZE_ON_CREATE_DEFAULT ) )
{
PhotoResizer.resizeAvatar( vCardElement );
}
Connection con = null; Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
try { try {
......
package org.igniterealtime.openfire.plugin.avatarresizer; package org.jivesoftware.openfire.vcard;
import org.dom4j.Element; import org.dom4j.Element;
import org.jivesoftware.util.Base64; import org.jivesoftware.util.Base64;
...@@ -21,9 +21,22 @@ import java.util.Iterator; ...@@ -21,9 +21,22 @@ import java.util.Iterator;
/** /**
* Image resizing utility methods. * Image resizing utility methods.
*/ */
public class Resizer public class PhotoResizer
{ {
private static final Logger Log = LoggerFactory.getLogger( Resizer.class ); private static final Logger Log = LoggerFactory.getLogger( PhotoResizer.class );
// Property that, when 'true' causes avatars that are being loaded from backend storage to be resized, prior to be
// processed and send to entities.
public static final String PROPERTY_RESIZE_ON_LOAD = "avatar.resize.enable-on-load";
public static final boolean PROPERTY_RESIZE_ON_LOAD_DEFAULT = true;
// Property that, when 'true' causes avatars that are being stored in backend storage to be resized.
public static final String PROPERTY_RESIZE_ON_CREATE = "avatar.resize.enable-on-create";
public static final boolean PROPERTY_RESIZE_ON_CREATE_DEFAULT = false;
// Property that controls the target dimension, in pixels.
public static final String PROPERTY_TARGETDIMENSION = "avatar.resize.targetdimension";
public static final int PROPERTY_TARGETDIMENSION_DEFAULT = 96;
public static void resizeAvatar( final Element vCardElement ) public static void resizeAvatar( final Element vCardElement )
{ {
...@@ -63,7 +76,7 @@ public class Resizer ...@@ -63,7 +76,7 @@ public class Resizer
final byte[] original = Base64.decode( element.getTextTrim() ); final byte[] original = Base64.decode( element.getTextTrim() );
// Crop and shrink, if needed. // Crop and shrink, if needed.
final int targetDimension = JiveGlobals.getIntProperty( "avatar.resize.targetdimension", 96 ); final int targetDimension = JiveGlobals.getIntProperty( PROPERTY_TARGETDIMENSION, PROPERTY_TARGETDIMENSION_DEFAULT );
final byte[] resized = cropAndShrink( original, targetDimension, iw ); final byte[] resized = cropAndShrink( original, targetDimension, iw );
// If a resized image was created, replace to original avatar in the VCard. // If a resized image was created, replace to original avatar in the VCard.
......
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Avatar Resizer Plugin Changelog</title>
<style type="text/css">
BODY {
font-size: 100%;
}
BODY, TD, TH {
font-family: tahoma, verdana, arial, helvetica, sans-serif;
font-size: 0.8em;
}
H2 {
font-size: 10pt;
font-weight: bold;
padding-left: 1em;
}
A:hover {
text-decoration: none;
}
H1 {
font-family: tahoma, arial, helvetica, sans-serif;
font-size: 1.4em;
font-weight: bold;
border-bottom: 1px #ccc solid;
padding-bottom: 2px;
}
TT {
font-family: courier new;
font-weight: bold;
color: #060;
}
PRE {
font-family: courier new;
font-size: 100%;
}
</style>
</head>
<body>
<h1>
Avatar Resizer Plugin Changelog
</h1>
<p><b>1.0.1</b> -- April 14, 2016</p>
<ul>
<li>Added proper documentation.</li>
</ul>
<p><b>1.0.0</b> -- April 12, 2016</p>
<ul>
<li>First stab at porting the original code of Aaron Sierra. Distributed as a proof-of-concept in the <a
href="https://community.igniterealtime.org/message/256783">IgniteRealtime community</a>.
</li>
</ul>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<class>org.igniterealtime.openfire.plugin.avatarresizer.AvatarResizerPlugin</class>
<name>Avatar Resizer</name>
<description>Ensures vCard-based avatars are not to large for comfort.</description>
<author>Guus der Kinderen</author>
<version>1.0.1</version>
<date>4/14/2016</date>
</plugin>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>plugins</artifactId>
<groupId>org.igniterealtime.openfire</groupId>
<version>4.2.0-SNAPSHOT</version>
</parent>
<groupId>org.igniterealtime.openfire.plugins</groupId>
<artifactId>avatarResizer</artifactId>
<version>1.0.1</version>
<name>Avatar Resizer Plugin</name>
<description>Ensures vCard-based avatars are not to large for comfort.</description>
<developers>
<developer>
<id>guusdk</id>
<name>Guus der Kinderen</name>
<email>guus.der.kinderen@gmail.com</email>
<organization>Ignite Realtime</organization>
<organizationUrl>https://www.igniterealtime.org</organizationUrl>
</developer>
</developers>
<build>
<sourceDirectory>src/java</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Avatar Resizer Plugin Readme</title>
<style type="text/css">
BODY {
font-size : 100%;
}
BODY, TD, TH {
font-family : tahoma, verdana, arial, helvetica, sans-serif;
font-size : 0.8em;
text-align: left;
}
CAPTION {
font-size: 0.7em;
margin-top: 1.2em;
}
H2 {
font-size : 10pt;
font-weight : bold;
}
A:hover {
text-decoration : none;
}
H1 {
font-family : tahoma, arial, helvetica, sans-serif;
font-size : 1.4em;
font-weight: bold;
border-bottom : 1px #ccc solid;
padding-bottom : 2px;
}
TT {
font-family : courier new;
font-weight : bold;
color : #060;
}
PRE {
font-family : courier new;
font-size : 100%;
}
</style>
</head>
<body>
<h1>
Avatar Resizer Plugin Readme
</h1>
<h2>Overview</h2>
<p>
The avatar resizer plugin will scale down VCard-based avatars, when the corresponding vCard is stored in Openfire
(it will not affect avatars that are being transferred between end-users).
</p>
<p>
Using avatars that are large can introduce problems for clients - not only will all of the data need to be
transferred (which is done with inefficient base64-encoding), some clients have trouble displaying large sets. RAM
and CPU spikes can occur as a result.
</p>
<p>
To prevent these problems, this plugin scales Avatars from vCards, but only if the vCards are provided by Openfire
itself (through one of its <a href="https://www.igniterealtime.org/builds/openfire/docs/latest/documentation/javadoc/org/jivesoftware/openfire/vcard/VCardProvider.html">VCardProvider</a>
implementations, which include the default, database-oriented provider, an LDAP provider and a Atlassian Crowd
provider).
</p>
<p>
Avatar scaling of this plugin is in accordance with the guidelines listed in
<a href="https://www.xmpp.org/extensions/xep-0153.html#bizrules-image">XEP&#8209;0153: vCard-Based Avatars</a>.
Specifically, this plugin shrinks and crops the avatar to a 96 by 96 pixel square image (although the size is
configurable, see below).
</p>
<h2>Installation</h2>
<p>
Copy avatarResizer.jar into the plugins directory of your Openfire installation. The plugin will then be
automatically deployed. To upgrade to a new version, copy the new avatarResizer.jar file over the existing file.
</p>
<h2>Configuration</h2>
<p>
The plugin does not need configuration. Simply installing the plugin will enable it. The properties defined here can
be set to override default behavior of the plugin.
</p>
<table>
<caption>Openfire properties for the Avatar Resizer plugin</caption>
<thead>
<tr><th>Property name</th><th>Default value</th><th>Description</th></tr>
</thead>
<tbody>
<tr>
<td><tt>avatar.resize.targetdimension</tt></td>
<td><pre>96</pre></td>
<td>Size (in pixels) of the resized avatar.</td>
</tr>
</tbody>
</table>
<h2>Credits</h2>
<p>
This plugin is based on an implementation by Aaron Sierra, which was contributed to IgniteRealtime through Mike Ray.
</p>
<p>
Icons made by <a href="http://www.flaticon.com/authors/stephen-hutchings" title="Stephen Hutchings">Stephen Hutchings</a>
from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a>, licensed by
<a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a>.
</p>
</body>
</html>
package org.igniterealtime.openfire.plugin.avatarresizer;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.jivesoftware.openfire.vcard.VCardManager;
import org.jivesoftware.openfire.vcard.VCardProvider;
import org.jivesoftware.util.JiveGlobals;
import java.io.File;
/**
* A plugin that intercepts avatars in vCards retrieved from the vCardManager, and re-sizes them when appropriate.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class AvatarResizerPlugin implements Plugin
{
@Override
public void initializePlugin( PluginManager manager, File pluginDirectory )
{
final VCardProvider provider = VCardManager.getProvider();
if ( provider != null && !( provider instanceof DelegateVCardProvider ) )
{
// Setting the property will cause the VCardProvider to re-initialize.
JiveGlobals.setProperty( "provider.vcard.className", DelegateVCardProvider.class.getCanonicalName() );
final DelegateVCardProvider delegateVCardProvider = (DelegateVCardProvider) VCardManager.getProvider();
delegateVCardProvider.setDelegate( provider );
}
}
@Override
public void destroyPlugin()
{
final VCardProvider provider = VCardManager.getProvider();
if ( provider != null && provider instanceof DelegateVCardProvider )
{
final DelegateVCardProvider delegateVCardProvider = (DelegateVCardProvider) provider;
final VCardProvider originalProvider = delegateVCardProvider.getDelegate();
JiveGlobals.setProperty( "provider.vcard.className", originalProvider.getClass().getCanonicalName() );
}
}
}
package org.igniterealtime.openfire.plugin.avatarresizer;
import org.dom4j.Element;
import org.jivesoftware.openfire.vcard.VCardProvider;
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.NotFoundException;
/**
* A vCard Provider that delegates to another provider, applying image resizing on the results from the delegate.
*
* @author Guus der Kinderen, guus.der.kinderen@gmail.com
*/
public class DelegateVCardProvider implements VCardProvider
{
private VCardProvider delegate;
public DelegateVCardProvider()
{
}
public VCardProvider getDelegate()
{
return delegate;
}
public void setDelegate( VCardProvider delegate )
{
if ( delegate == null )
{
throw new IllegalArgumentException( "Argument 'delegate' cannot be null." );
}
if ( delegate instanceof DelegateVCardProvider )
{
throw new IllegalArgumentException( "Argument 'delegate' cannot be an instance of DelegateVCardProvider." );
}
this.delegate = delegate;
}
@Override
public Element loadVCard( String username )
{
final Element element = delegate.loadVCard( username );
Resizer.resizeAvatar( element );
return element;
}
@Override
public Element createVCard( String username, Element vCardElement ) throws AlreadyExistsException
{
final Element element = delegate.createVCard( username, vCardElement );
Resizer.resizeAvatar( element );
return element;
}
@Override
public Element updateVCard( String username, Element vCardElement ) throws NotFoundException
{
final Element element = delegate.updateVCard( username, vCardElement );
Resizer.resizeAvatar( element );
return element;
}
@Override
public void deleteVCard( String username )
{
delegate.deleteVCard( username );
}
@Override
public boolean isReadOnly()
{
return delegate.isReadOnly();
}
}
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
<modules> <modules>
<module>openfire-plugin-assembly-descriptor</module> <module>openfire-plugin-assembly-descriptor</module>
<module>avatarResizer</module>
<module>bookmarks</module> <module>bookmarks</module>
<module>broadcast</module> <module>broadcast</module>
<module>callbackOnOffline</module> <module>callbackOnOffline</module>
......
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