/*
* 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.rendering.internal.macro.chart;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.commons.io.IOUtils;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.component.annotation.Component;
import org.xwiki.environment.Environment;
import org.xwiki.model.EntityType;
import org.xwiki.model.ModelContext;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.rendering.macro.MacroExecutionException;
/**
* Save generated Chart images to a temporary storage location.
*
* @version $Id: 4ff842aa7e6df69dcc5224ed9f5963bf94c32949 $
* @since 4.2M3
*/
@Component
@Named("tmp")
@Singleton
public class TemporaryChartImageWriter implements ChartImageWriter
{
/**
* Default encoding used for encoding wiki, space, page and image file names when generating the Image in the
* temporary folder.
*/
private static final String DEFAULT_ENCODING = "UTF-8";
/**
* The module name used when creating temporary files. This is the module used by the temporary resource action to
* retrieve the temporary image file.
*/
private static final String MODULE_NAME = "chart";
/**
* Space part of the temporary location where generated chart image are stored.
*/
private static final String SPACE = "space";
/**
* Page part of the temporary location where generated chart image are stored.
*/
private static final String PAGE = "page";
/**
* Used to get the temporary directory.
*/
@Inject
private Environment environment;
/**
* Used to get the current wiki.
*/
@Inject
private ModelContext modelContext;
/**
* Used to compute the URL to the temporary stored image generated by the chart.
*/
@Inject
private DocumentAccessBridge documentAccessBridge;
@Override
public void writeImage(ImageId imageId, byte[] imageData) throws MacroExecutionException
{
File imageFile = getStorageLocation(imageId);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(imageFile);
fos.write(imageData);
fos.close();
} catch (IOException e) {
throw new MacroExecutionException("Failed to write the generated chart image", e);
} finally {
IOUtils.closeQuietly(fos);
}
}
/**
* Compute the location where to store the generated chart image.
*
* @param imageId the image id that we use to generate a unique storage location
* @return the location where to store the generated chart image
* @throws MacroExecutionException if an error happened when computing the location
*/
protected File getStorageLocation(ImageId imageId) throws MacroExecutionException
{
File directory;
try {
String currentWiki = URLEncoder.encode(getCurrentWiki(), DEFAULT_ENCODING);
// TODO: We need to decide if it's ok to use the the hardcoded "space/page" or if we want to use the
// current document in which case we need to extract it from the XDOM. The reason I haven't done it
// by default is because it takes more time and the image id seems unique enough to not cause collisions.
directory = new File(this.environment.getTemporaryDirectory(),
String.format("temp/%s/%s/%s/%s", MODULE_NAME, currentWiki, SPACE, PAGE));
directory.mkdirs();
} catch (Exception e) {
// Should not happen since UTF8 encoding should always be present
throw new MacroExecutionException("Failed to compute chart image location", e);
}
File locationFile = new File(directory, String.format("%s.png", imageId.getId()));
return locationFile;
}
/**
* @return the current wiki
* @throws MacroExecutionException if the current wiki couldn't be found
*/
private String getCurrentWiki() throws MacroExecutionException
{
EntityReference reference = this.modelContext.getCurrentEntityReference();
if (reference == null) {
// This shouldn't happen since there's always supposed to be a current wiki
throw new MacroExecutionException("The current wiki couldn't be computed");
}
return reference.extractReference(EntityType.WIKI).getName();
}
@Override
public String getURL(ImageId imageId) throws MacroExecutionException
{
DocumentReference reference = new DocumentReference(getCurrentWiki(), SPACE, PAGE);
String prefix = this.documentAccessBridge.getDocumentURL(reference, "temp", null, null);
return String.format("%s/%s/%s.png", prefix, MODULE_NAME, imageId.getId());
}
}