diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java b/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java index 428651ccaa..fbffd33bac 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java @@ -290,6 +290,16 @@ public void setLocaleId(String localeId) this.localeId = localeId; } + public boolean hasOriginal(String docPath, String docName) + { + return translationFileServiceImpl.hasPersistedDocument(projectSlug, iterationSlug, docPath, docName); + } + + public String extensionOf(String docName) + { + return "." + translationFileServiceImpl.extractExtension(docName); + } + public String getDocumentNameFilter() { return documentNameFilter; diff --git a/zanata-war/src/main/java/org/zanata/rest/service/FileService.java b/zanata-war/src/main/java/org/zanata/rest/service/FileService.java index 4b67fa5137..af3d495d79 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/FileService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/FileService.java @@ -65,7 +65,11 @@ @Consumes( { MediaType.APPLICATION_OCTET_STREAM }) public class FileService implements FileResource { - + public static final String RAW_DOWNLOAD_TEMPLATE = "/raw/{projectSlug}/{iterationSlug}"; + + private static final String SUBSTITUTE_APPROVED_AND_FUZZY = "half-baked"; + private static final String SUBSTITUTE_APPROVED = "baked"; + @In private DocumentDAO documentDAO; @@ -81,6 +85,30 @@ public class FileService implements FileResource @In private ResourceUtils resourceUtils; + @GET + @Path(RAW_DOWNLOAD_TEMPLATE) + // /file/raw/{projectSlug}/{iterationSlug}?docId={docId} + public Response downloadRawFile( @PathParam("projectSlug") String projectSlug, + @PathParam("iterationSlug") String iterationSlug, + @QueryParam("docId") String docId) + { + final Response response; + HDocument document = this.documentDAO.getByProjectIterationAndDocId(projectSlug, iterationSlug, docId); + + if( document != null && translationFileServiceImpl.hasPersistedDocument(projectSlug, iterationSlug, document.getPath(), document.getName())) + { + InputStream fileContents = translationFileServiceImpl.streamDocument(projectSlug, iterationSlug, document.getPath(), document.getName()); + StreamingOutput output = new InputStreamStreamingOutput(fileContents); + response = Response.ok() + .header("Content-Disposition", "attachment; filename=\"" + document.getName() + "\"") + .entity(output).build(); + } + else + { + response = Response.status(Status.NOT_FOUND).build(); + } + return response; + } /** * Downloads a single translation file. @@ -106,7 +134,7 @@ public Response downloadTranslationFile( @PathParam("projectSlug") String projec @PathParam("fileType") String fileExtension, @QueryParam("docId") String docId ) { - final Response response; + final Response response; HDocument document = this.documentDAO.getByProjectIterationAndDocId(projectSlug, iterationSlug, docId); if( document == null ) @@ -118,7 +146,7 @@ else if ("po".equals(fileExtension)) final Set extensions = new HashSet(); extensions.add("gettext"); extensions.add("comment"); - + // Perform translation of Hibernate DTOs to JAXB DTOs TranslationsResource transRes = (TranslationsResource) this.translatedDocResourceService.getTranslations(docId, new LocaleId(locale), extensions, true).getEntity(); @@ -129,38 +157,35 @@ else if ("po".equals(fileExtension)) .header("Content-Disposition", "attachment; filename=\"" + document.getName() + ".po\"") .entity(output).build(); } - - // TODO decide whether to have one URL for original format, or use fileExtension for all - else if (translationFileServiceImpl.hasAdapterFor(fileExtension) && - translationFileServiceImpl.hasPersistedDocument(projectSlug, iterationSlug, document.getPath(), document.getName())) + else if ( (SUBSTITUTE_APPROVED.equals(fileExtension) || SUBSTITUTE_APPROVED_AND_FUZZY.equals(fileExtension)) + && translationFileServiceImpl.hasPersistedDocument(projectSlug, iterationSlug, document.getPath(), document.getName())) { final Set extensions = Collections.emptySet(); TranslationsResource transRes = (TranslationsResource) this.translatedDocResourceService.getTranslations(docId, new LocaleId(locale), extensions, true).getEntity(); - // Filter translations to only provide approved translations. - // "Preview" downloads should include fuzzy as well. - // New list is used as transRes list appears not to be a modifiable implementation. + // Filter to only provide translated targets. "Preview" downloads include fuzzy. + // Using new list is used as transRes list appears not to be a modifiable implementation. List translations = new ArrayList(); + boolean useFuzzy = SUBSTITUTE_APPROVED_AND_FUZZY.equals(fileExtension); for (TextFlowTarget target : transRes.getTextFlowTargets()) { - if (target.getState() == ContentState.Approved) + if (target.getState() == ContentState.Approved || (useFuzzy && target.getState() == ContentState.NeedReview)) { translations.add(target); } } InputStream fileContents = translationFileServiceImpl.streamDocument(projectSlug, iterationSlug, document.getPath(), document.getName()); - StreamingOutput output = new FormatAdapterStreamingOutput(fileContents, translations, locale, translationFileServiceImpl.getAdapterFor(fileExtension)); + StreamingOutput output = new FormatAdapterStreamingOutput(fileContents, translations, locale, translationFileServiceImpl.getAdapterFor(docId)); response = Response.ok() .header("Content-Disposition", "attachment; filename=\"" + document.getName() + "\"") .entity(output).build(); } else { - // TODO check if this media type is appropriate response = Response.status(Status.UNSUPPORTED_MEDIA_TYPE).build(); } - + return response; } @@ -239,6 +264,28 @@ public void write(OutputStream output) throws IOException, WebApplicationExcepti } } + private class InputStreamStreamingOutput implements StreamingOutput + { + private InputStream input; + + public InputStreamStreamingOutput(InputStream input) + { + this.input = input; + } + + @Override + public void write(OutputStream output) throws IOException, WebApplicationException + { + byte[] buffer = new byte[4096]; // To hold file contents + int bytesRead; // How many bytes in buffer + + while ((bytesRead = input.read(buffer)) != -1) + { + output.write(buffer, 0, bytesRead); + } + } + } + private class FormatAdapterStreamingOutput implements StreamingOutput { private List translations; diff --git a/zanata-war/src/main/java/org/zanata/service/TranslationFileService.java b/zanata-war/src/main/java/org/zanata/service/TranslationFileService.java index ccb6ad8fa8..d89f4af973 100644 --- a/zanata-war/src/main/java/org/zanata/service/TranslationFileService.java +++ b/zanata-war/src/main/java/org/zanata/service/TranslationFileService.java @@ -74,5 +74,12 @@ public interface TranslationFileService */ InputStream streamDocument(String projectSlug, String iterationSlug, String docPath, String docName); + /** + * + * @param fileNameOrExtension + * @return the extension for a given filename, or the extension that was passed in + */ + String extractExtension(String fileNameOrExtension); + } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/TranslationFileServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/TranslationFileServiceImpl.java index 1a381a6885..58c96f28ac 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/TranslationFileServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/TranslationFileServiceImpl.java @@ -180,7 +180,8 @@ public FileFormatAdapter getAdapterFor(String fileNameOrExtension) } } - private String extractExtension(String fileNameOrExtension) + @Override + public String extractExtension(String fileNameOrExtension) { if (fileNameOrExtension == null || fileNameOrExtension.length() == 0 || fileNameOrExtension.endsWith(".")) { diff --git a/zanata-war/src/main/webapp/iteration/files.xhtml b/zanata-war/src/main/webapp/iteration/files.xhtml index 32115ab6fe..7cbed306d2 100644 --- a/zanata-war/src/main/webapp/iteration/files.xhtml +++ b/zanata-war/src/main/webapp/iteration/files.xhtml @@ -98,10 +98,9 @@ - + - - +