/* * 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 org.xwiki.watchlist.internal; import java.awt.Image; import java.awt.image.RenderedImage; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.environment.Environment; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.EntityReferenceSerializer; import com.xpn.xwiki.XWiki; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.api.Attachment; import com.xpn.xwiki.api.Document; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiAttachmentContent; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.plugin.image.ImageProcessor; import com.xpn.xwiki.user.api.XWikiRightService; /** * Default implementation for {@link UserAvatarAttachmentExtractor}. * * @version $Id: c88093bd06d93d5e838c747d51777ef5a1225ca4 $ * @since 7.1RC1 */ @Component @Singleton public class DefaultUserAvatarAttachmentExtractor implements UserAvatarAttachmentExtractor { /** * Logging framework. */ @Inject private Logger logger; /** * Used to get file resources. */ @Inject private Environment environment; /** * Used to resize user avatars. */ @Inject private ImageProcessor imageProcessor; @Inject private Provider<XWikiContext> xwikiContextProvider; @Inject private EntityReferenceSerializer<String> serializer; @Override public Attachment getUserAvatar(DocumentReference userReference) { String fileName = getFileName(userReference); return getUserAvatar(userReference, 50, 50, fileName); } private String getFileName(DocumentReference userReference) { String fileName; if (userReference == null) { fileName = XWikiRightService.GUEST_USER_FULLNAME; } else { fileName = serializer.serialize(userReference); } fileName = String.format("%s.png", fileName); return fileName; } @Override public Attachment getUserAvatar(DocumentReference userReference, int width, int height, String fileName) { // FIXME: Unfortunately, the ImagePlugin is too much request-oriented and not generic enough to be reused // without rewriting. In the end, that might be the right way to go and rewriting it might be inevitable. Attachment result = null; InputStream sourceImageInputStream = null; try { XWikiContext context = xwikiContextProvider.get(); XWiki wiki = context.getWiki(); XWikiAttachment realAvatarAttachment; XWikiAttachment fakeAvatarAttachment = null; // Use a second variable to be able to reassign it on the else branch below. DocumentReference actualUserReference = userReference; if (actualUserReference != null) { // Registered user. XWikiDocument userProfileDocument = wiki.getDocument(userReference, context); DocumentReference usersClassReference = wiki.getUserClass(context).getDocumentReference(); String avatarFileName = userProfileDocument.getStringValue(usersClassReference, "avatar"); realAvatarAttachment = userProfileDocument.getAttachment(avatarFileName); if (realAvatarAttachment != null && realAvatarAttachment.isImage(context)) { // Valid avatar, use the real attachment (and image), but make sure to use a clone as to not impact // the // real document. fakeAvatarAttachment = (XWikiAttachment) realAvatarAttachment.clone(); sourceImageInputStream = realAvatarAttachment.getContentInputStream(context); result = new Attachment(new Document(userProfileDocument, context), fakeAvatarAttachment, context); } else { // No or invalid avatar, treat the user reference as it did not exist (guest user) so it can be // handled below for both cases. actualUserReference = null; } } if (actualUserReference == null) { // Guest user. // No avatar. Return a fake attachment with the "noavatar.png" standard image. fakeAvatarAttachment = new XWikiAttachment(); sourceImageInputStream = environment.getResourceAsStream("/resources/icons/xwiki/noavatar.png"); result = new Attachment(null, fakeAvatarAttachment, context); } // In both cases, set an empty attachment content that will be filled with the resized image. This way we // also avoid a request to the DB for the attachment content, since it will already be available. fakeAvatarAttachment.setAttachment_content(new XWikiAttachmentContent(fakeAvatarAttachment)); // Resize the image and write it to the fake attachment. int resizedWidth = 50; int resizedHeight = 50; resizeImageToAttachment(sourceImageInputStream, resizedWidth, resizedHeight, fakeAvatarAttachment); // Set a fixed name for the user avatar file so that it is easy to work with in a template, for example. fakeAvatarAttachment.setFilename(fileName); } catch (Exception e) { logger.error("Failed to retrieve the avatar for the user {}", userReference, e); return null; } finally { // Close the source image input stream since we are done reading from it. if (sourceImageInputStream != null) { IOUtils.closeQuietly(sourceImageInputStream); } } return result; } private void resizeImageToAttachment(InputStream imageFileInputStream, int width, int height, XWikiAttachment outputAttachment) throws IOException { OutputStream attachmentOutputStream = null; try { Image originalImage = imageProcessor.readImage(imageFileInputStream); RenderedImage resizedImage = imageProcessor.scaleImage(originalImage, width, height); attachmentOutputStream = outputAttachment.getAttachment_content().getContentOutputStream(); imageProcessor.writeImage(resizedImage, "image/png", 1.0f, attachmentOutputStream); } finally { // Close the attachment output stream since we are done writing to it. if (attachmentOutputStream != null) { IOUtils.closeQuietly(attachmentOutputStream); } } } }