diff --git a/docs/release-notes.md b/docs/release-notes.md index e1cd2ed196..c09c78217a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -37,6 +37,9 @@ Example usage in html file: ` supportedTypes = Lists.transform(ProjectType + .getSupportedSourceFileTypes(getProjectType()), + new Function() { + @Override + public String apply(DocumentType docType) { + return Joiner.on(",").join( + docType.getSourceExtensions()); + } + }); + return Joiner.on(", ").join(supportedTypes); + } + + public String getAcceptedSourceFile() { + List supportedTypes = Lists.transform(ProjectType + .getSupportedSourceFileTypes(getProjectType()), + new Function() { + @Override + public String apply(DocumentType docType) { + return docType.name() + "[" + Joiner.on(",").join( + docType.getSourceExtensions()) + "]"; + } + }); + return Joiner.on(", ").join(supportedTypes); } private void updateProjectType() { diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java index a8488eeb15..446fa99859 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java @@ -36,6 +36,7 @@ import javax.faces.application.FacesMessage; import javax.validation.ConstraintViolationException; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; @@ -82,6 +83,7 @@ import org.zanata.ui.InMemoryListFilter; import org.zanata.ui.model.statistic.WordStatistic; import org.zanata.util.DateUtil; +import org.zanata.util.FileUtil; import org.zanata.util.ServiceLocator; import org.zanata.util.StatisticsUtil; import org.zanata.util.UrlUtil; @@ -695,17 +697,34 @@ public boolean isFileUploadAllowed(HLocale hLocale) { .getProject(), hLocale); } + private DocumentType getDocumentType(Optional docType) { + DocumentType type; + if (!docType.isPresent()) { + // if docType null, only 1 document type available for selected file extensions + type = translationFileServiceImpl + .getDocumentTypes(sourceFileUpload.getFileName()) + .iterator().next(); + } else { + // if docType not null, adapter is selected by user from drop down + type = DocumentType.getByName(docType.get()); + } + return type; + } + public void uploadSourceFile() { identity.checkPermission("import-template", getVersion()); if (sourceFileUpload.getFileName().endsWith(".pot")) { uploadPotFile(); } else { - DocumentType type = - translationFileServiceImpl.getDocumentType(sourceFileUpload - .getFileName()); - if (translationFileServiceImpl.hasAdapterFor(type)) { - uploadAdapterFile(); + + Optional docType = + Optional.fromNullable(sourceFileUpload.documentType); + + DocumentType documentType = getDocumentType(docType); + + if (translationFileServiceImpl.hasAdapterFor(documentType)) { + uploadAdapterFile(documentType); } else { conversationScopeMessages.setMessage( FacesMessage.SEVERITY_INFO, @@ -721,10 +740,17 @@ public boolean isPoDocument(String docId) { versionSlug, docId); } - public String extensionOf(String docPath, String docName) { + public String sourceExtensionOf(String docPath, String docName) { return "." - + translationFileServiceImpl.getFileExtension(projectSlug, - versionSlug, docPath, docName); + + translationFileServiceImpl.getSourceFileExtension(projectSlug, + versionSlug, docPath, docName); + } + + public String translationExtensionOf(String docPath, String docName) { + return "." + + translationFileServiceImpl.getTranslationFileExtension( + projectSlug, + versionSlug, docPath, docName); } public boolean hasOriginal(String docPath, String docName) { @@ -754,7 +780,7 @@ private void uploadPotFile() { String docId = sourceFileUpload.getDocId(); if (docId == null) { docId = - translationFileServiceImpl.generateDocId( + FileUtil.generateDocId( sourceFileUpload.getDocumentPath(), sourceFileUpload.getFileName()); } @@ -788,7 +814,7 @@ private void uploadPotFile() { private Optional getOptionalParams() { return Optional.fromNullable(Strings.emptyToNull(sourceFileUpload - .getAdapterParams())); + .getAdapterParams())); } public void setSelectedLocaleId(String localeId) { @@ -805,7 +831,7 @@ public void setSelectedDocumentId(String projectSlug, String versionSlug, // TODO add logging for disk writing errors // TODO damason: unify this with Source/TranslationDocumentUpload - private void uploadAdapterFile() { + private void uploadAdapterFile(DocumentType docType) { String fileName = sourceFileUpload.getFileName(); String docId = sourceFileUpload.getDocId(); String documentPath = ""; @@ -845,17 +871,16 @@ private void uploadAdapterFile() { HDocument document = null; try { Resource doc; + if (docId == null) { - doc = - translationFileServiceImpl.parseAdapterDocumentFile( + doc = translationFileServiceImpl.parseAdapterDocumentFile( tempFile.toURI(), documentPath, fileName, - getOptionalParams()); + getOptionalParams(), Optional.of(docType.name())); } else { - doc = - translationFileServiceImpl + doc = translationFileServiceImpl .parseUpdatedAdapterDocumentFile( tempFile.toURI(), docId, fileName, - getOptionalParams()); + getOptionalParams(), Optional.of(docType.name())); } doc.setLang(new LocaleId(sourceFileUpload.getSourceLang())); Set extensions = Collections. emptySet(); @@ -879,8 +904,7 @@ private void uploadAdapterFile() { HRawDocument rawDocument = new HRawDocument(); rawDocument.setDocument(document); rawDocument.setContentHash(new String(Hex.encodeHex(md5hash))); - rawDocument.setType(DocumentType.typeFor(translationFileServiceImpl - .extractExtension(fileName))); + rawDocument.setType(docType); rawDocument.setUploadedBy(identity.getCredentials().getUsername()); Optional params = getOptionalParams(); @@ -899,7 +923,7 @@ private void uploadAdapterFile() { "uploaded file did not pass virus scan"); } filePersistService.persistRawDocumentContentFromFile(rawDocument, - tempFile); + tempFile, FilenameUtils.getExtension(fileName)); documentDAO.addRawDocument(document, rawDocument); documentDAO.flush(); } @@ -925,17 +949,39 @@ public boolean isCopyActionsRunning() { copyTransManager.isCopyTransRunning(getVersion()); } + public boolean needDocumentTypeSelection(String fileName) { + return translationFileServiceImpl.hasMultipleDocumentTypes(fileName); + } + + public List getDocumentTypes(String fileName) { + return Lists.newArrayList( + translationFileServiceImpl.getDocumentTypes(fileName)); + } + + public void setDefaultTranslationDocType(String fileName) { + translationFileUpload.setDocumentType(null); + } + public void uploadTranslationFile(HLocale hLocale) { identity.checkPermission("modify-translation", hLocale, getVersion() .getProject()); try { // process the file + String fileName = translationFileUpload.fileName; + if (!needDocumentTypeSelection(fileName)) { + //Get documentType for this file name + DocumentType documentType = getDocumentTypes(fileName).get(0); + translationFileUpload.setDocumentType(documentType.name()); + } + + Optional docType = + Optional.fromNullable(translationFileUpload.documentType); TranslationsResource transRes = translationFileServiceImpl.parseTranslationFile( translationFileUpload.getFileContents(), translationFileUpload.getFileName(), hLocale .getLocaleId().getId(), projectSlug, - versionSlug, translationFileUpload.docId); + versionSlug, translationFileUpload.docId, docType); // translate it Set extensions; @@ -1145,6 +1191,8 @@ public static class SourceFileUploadHelper implements Serializable { private String sourceLang = "en-US"; // en-US by default private String adapterParams = ""; + + private String documentType; } /** @@ -1164,5 +1212,7 @@ public static class TranslationFileUploadHelper implements Serializable { private boolean mergeTranslations = true; // Merge by default private boolean assignCreditToUploader = false; + + private String documentType; } } diff --git a/zanata-war/src/main/java/org/zanata/adapter/FileFormatAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/FileFormatAdapter.java index d666f69512..04f065f478 100644 --- a/zanata-war/src/main/java/org/zanata/adapter/FileFormatAdapter.java +++ b/zanata-war/src/main/java/org/zanata/adapter/FileFormatAdapter.java @@ -24,8 +24,11 @@ import java.net.URI; import java.util.Map; +import javax.annotation.Nonnull; + import org.zanata.common.LocaleId; import org.zanata.exception.FileFormatAdapterException; +import org.zanata.file.GlobalDocumentId; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.TextFlowTarget; import org.zanata.rest.dto.resource.TranslationsResource; @@ -55,15 +58,20 @@ public interface FileFormatAdapter { * @throws FileFormatAdapterException * if the document cannot be parsed */ - Resource parseDocumentFile(URI documentUri, LocaleId sourceLocale, - Optional params) throws FileFormatAdapterException, + Resource parseDocumentFile(@Nonnull URI documentUri, + @Nonnull LocaleId sourceLocale, Optional params) + throws FileFormatAdapterException, IllegalArgumentException; /** * Extract translation strings from the given translation document. * - * @param translatedDocumentContent + * @param fileUri * translated document to parse + * @param sourceLocaleId + * source locale id + * @param localeId + * translation locale id * @param params * adapter-specific parameter string. See documentation for * individual adapters. @@ -73,9 +81,10 @@ Resource parseDocumentFile(URI documentUri, LocaleId sourceLocale, * @throws IllegalArgumentException * if translatedDocumentContent or localeId is null */ - TranslationsResource parseTranslationFile(URI fileUri, String localeId, - Optional params) throws FileFormatAdapterException, - IllegalArgumentException; + TranslationsResource parseTranslationFile(@Nonnull URI fileUri, + @Nonnull LocaleId sourceLocaleId, @Nonnull String localeId, + Optional params) + throws FileFormatAdapterException, IllegalArgumentException; /** * Write translated file to the given output, using the given list of @@ -83,9 +92,11 @@ TranslationsResource parseTranslationFile(URI fileUri, String localeId, * * @param output * stream to write translated document - * @param original + * @param originalFile * source document - * @param translations + * @param resource + * source file + * @param translationsResource * to use in generating translated file * @param locale * to use for translated document @@ -99,8 +110,8 @@ TranslationsResource parseTranslationFile(URI fileUri, String localeId, * if any parameters are null */ void writeTranslatedFile(OutputStream output, URI originalFile, - Map translations, String locale, - Optional params) throws FileFormatAdapterException, - IllegalArgumentException; + Resource resource, TranslationsResource translationsResource, + String locale, Optional params) + throws FileFormatAdapterException, IllegalArgumentException; } diff --git a/zanata-war/src/main/java/org/zanata/adapter/GenericPropertiesAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/GenericPropertiesAdapter.java new file mode 100644 index 0000000000..eb03ff9d27 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/adapter/GenericPropertiesAdapter.java @@ -0,0 +1,177 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * 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.zanata.adapter; + +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import lombok.extern.slf4j.Slf4j; +import org.zanata.adapter.properties.PropReader; +import org.zanata.adapter.properties.PropWriter; +import org.zanata.common.ContentState; +import org.zanata.common.LocaleId; +import org.zanata.exception.FileFormatAdapterException; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.util.FileUtil; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +/** + * Properties file adapter to read and write. + * + * See + * {@link org.zanata.adapter.PropertiesLatinOneAdapter} + * {@link org.zanata.adapter.PropertiesUTF8Adapter} + * + * @author Alex Eng aeng@redhat.com + */ +@Slf4j +public class GenericPropertiesAdapter implements FileFormatAdapter { + + public static final String ISO_8859_1 = Charsets.ISO_8859_1.name(); + + public static final String UTF_8 = Charsets.UTF_8.name(); + + private final PropWriter.CHARSET charset; + + public GenericPropertiesAdapter(String charset) { + this.charset = toPropWriterCharset(charset); + } + + private PropWriter.CHARSET toPropWriterCharset(String charset) { + if (charset.equals(ISO_8859_1)) { + return PropWriter.CHARSET.Latin1; + } else if (charset.equals(UTF_8)) { + return PropWriter.CHARSET.UTF8; + } else { + throw new FileFormatAdapterException( + "Not supported charset '" + charset + "' for properties file"); + } + } + + @Override + public Resource parseDocumentFile(URI fileUri, LocaleId sourceLocale, + Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + if (sourceLocale == null) { + throw new IllegalArgumentException("Source locale cannot be null"); + } + + if (fileUri == null) { + throw new IllegalArgumentException("Document URI cannot be null"); + } + + PropReader propReader = new PropReader(charset, sourceLocale, + ContentState.Approved); + + Resource doc = new Resource(); + + try (BufferedInputStream inputStream = readStream(fileUri)) { + propReader.extractTemplate(doc, inputStream); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Could not open the URL. The URL is OK but the input stream could not be opened.\n" + + e.getMessage(), e); + } + return doc; + } + + @Override + public TranslationsResource parseTranslationFile(URI fileUri, + LocaleId sourceLocaleId, String localeId, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + PropReader propReader = new PropReader(charset, sourceLocaleId, + ContentState.Approved); + + /** + * Resource is not needed for properties file translation parser as its + * only used for contentHash check + */ + Resource srcDoc = new Resource(); + + TranslationsResource targetDoc = new TranslationsResource(); + + try (BufferedInputStream inputStream = readStream(fileUri)) { + propReader.extractTarget(targetDoc, inputStream, srcDoc); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Could not open the URL. The URL is OK but the input stream could not be opened.\n" + + e.getMessage(), e); + } + return targetDoc; + } + + @Override + public void writeTranslatedFile(OutputStream output, URI originalFile, + Resource resource, TranslationsResource translationsResource, + String locale, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + // write source string with empty translation + boolean createSkeletons = true; + + File tempFile = null; + try { + tempFile = File.createTempFile("filename", "extension"); + PropWriter.writeTranslationsFile(resource, + translationsResource, tempFile, + charset, createSkeletons); + + FileUtil.writeFileToOutputStream(tempFile, output); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Unable to generate translated file", e); + } finally { + FileUtil.tryDeleteFile(tempFile); + } + } + + private BufferedInputStream readStream(URI fileUri) + throws FileFormatAdapterException, + IllegalArgumentException { + URL url = null; + + try { + url = fileUri.toURL(); + return new BufferedInputStream(url.openStream()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException( + "Could not open the URI. The URI must be absolute: " + + ((url == null) ? "URL is null" : url.toString()), + e); + } catch (MalformedURLException e) { + throw new FileFormatAdapterException( + "Could not open the URI. The URI may be malformed: " + + ((url == null) ? "URL is null" : url.toString()), + e); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Could not open the URL. The URL is OK but the input stream could not be opened.\n" + + e.getMessage(), e); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/adapter/GettextAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/GettextAdapter.java new file mode 100644 index 0000000000..650154001d --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/adapter/GettextAdapter.java @@ -0,0 +1,171 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * 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.zanata.adapter; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.CharSet; +import org.apache.commons.lang.StringUtils; +import org.xml.sax.InputSource; +import org.zanata.adapter.po.PoReader2; +import org.zanata.adapter.po.PoWriter2; +import org.zanata.common.LocaleId; +import org.zanata.common.io.FileDetails; +import org.zanata.dao.DocumentDAO; +import org.zanata.exception.FileFormatAdapterException; +import org.zanata.file.GlobalDocumentId; +import org.zanata.model.HDocument; +import org.zanata.rest.StringSet; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TextFlowTarget; +import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.rest.service.ResourceUtils; +import org.zanata.util.FileUtil; +import org.zanata.util.ServiceLocator; + +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; + +/** + * Adapter to read and write {@link org.zanata.common.DocumentType#GETTEXT} + * + * TODO: Convert to okapi gettext adapter once all client conversion is + * migrated to server + * + * @author Alex Eng aeng@redhat.com + */ +@Slf4j +public class GettextAdapter implements FileFormatAdapter { + + @Override + public Resource parseDocumentFile(URI fileUri, LocaleId sourceLocale, + Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + + if (sourceLocale == null) { + throw new IllegalArgumentException("Source locale cannot be null"); + } + + if (fileUri == null) { + throw new IllegalArgumentException("Document URI cannot be null"); + } + + PoReader2 reader = new PoReader2(); + + BufferedInputStream inputStream = readStream(fileUri); + Resource doc = reader.extractTemplate(new InputSource(inputStream), + sourceLocale, ""); + try { + inputStream.close(); + } catch (IOException e) { + } + return doc; + } + + @Override + public TranslationsResource parseTranslationFile(URI fileUri, + LocaleId sourceLocaleId, String localeId, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + + if (StringUtils.isEmpty(localeId)) { + throw new IllegalArgumentException( + "locale id string cannot be null or empty"); + } + + PoReader2 reader = new PoReader2(); + + BufferedInputStream inputStream = readStream(fileUri); + TranslationsResource resource = + reader.extractTarget(new InputSource(inputStream)); + + try { + inputStream.close(); + } catch (IOException e) { + } + + return resource; + } + + @Override + public void writeTranslatedFile(OutputStream output, URI originalFile, + Resource resource, TranslationsResource translationsResource, + String locale, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + + PoWriter2 writer = new PoWriter2(true, true); + try { + writer.writePo(output, Charsets.UTF_8.name(), resource, + translationsResource); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Unable to generate translated file", e); + } + } + + private BufferedInputStream readStream(URI fileUri) throws FileFormatAdapterException, + IllegalArgumentException { + URL url = null; + + try { + url = fileUri.toURL(); + return new BufferedInputStream(url.openStream()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException( + "Could not open the URI. The URI must be absolute: " + + ((url == null) ? "URL is null" : url.toString()), + e); + } catch (MalformedURLException e) { + throw new FileFormatAdapterException( + "Could not open the URI. The URI may be malformed: " + + ((url == null) ? "URL is null" : url.toString()), + e); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Could not open the URL. The URL is OK but the input stream could not be opened.\n" + + e.getMessage(), e); + } + } + + private HDocument getDocument(GlobalDocumentId documentId) { + return getDocumentDAO().getByGlobalId(documentId); + } + + private DocumentDAO getDocumentDAO() { + return ServiceLocator.instance().getInstance(DocumentDAO.class); + } + + private ResourceUtils getResourceUtils() { + return ServiceLocator.instance().getInstance(ResourceUtils.class); + } +} diff --git a/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java index 07b6a932b8..7dae36c76a 100644 --- a/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java +++ b/zanata-war/src/main/java/org/zanata/adapter/OkapiFilterAdapter.java @@ -51,9 +51,12 @@ import org.zanata.rest.dto.resource.TextFlow; import org.zanata.rest.dto.resource.TextFlowTarget; import org.zanata.rest.dto.resource.TranslationsResource; +import org.zanata.util.FileUtil; import org.zanata.util.HashUtil; +import com.google.common.base.Charsets; import com.google.common.base.Optional; +import com.google.common.collect.Maps; /** * An adapter that uses a provided {@link IFilter} implementation to parse @@ -219,7 +222,6 @@ private String getTranslatableText(TextUnit tu) { * Separates translatable text from surrounding non-translatable text. * * @param tu - * @return */ private Map getPartitionedText(TextUnit tu) { return TranslatableSeparator.separate(GenericContent @@ -230,8 +232,7 @@ private Map getPartitionedText(TextUnit tu) { /** * Separates translatable text from surrounding non-translatable text. * - * @param tu - * @return + * @param letterCodedText */ private Map getPartitionedText(String letterCodedText) { return TranslatableSeparator.separate(letterCodedText); @@ -277,7 +278,7 @@ private String stripPath(String name) { @Override public TranslationsResource parseTranslationFile(URI fileUri, - String localeId, Optional filterParams) + LocaleId sourceLocaleId, String localeId, Optional filterParams) throws FileFormatAdapterException, IllegalArgumentException { if (localeId == null || localeId.isEmpty()) { throw new IllegalArgumentException( @@ -336,26 +337,53 @@ private TranslationsResource parseTranslationFile(RawDocument rawDoc, @Override public void writeTranslatedFile(OutputStream output, URI originalFile, - Map translations, String locale, - Optional params) throws FileFormatAdapterException, - IllegalArgumentException { - net.sf.okapi.common.LocaleId localeId = + Resource resource, TranslationsResource translationsResource, + String locale, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + + Map translations = + transformToMapByResId( + translationsResource.getTextFlowTargets()); + + try { + net.sf.okapi.common.LocaleId localeId = net.sf.okapi.common.LocaleId.fromString(locale); - IFilterWriter writer = filter.createFilterWriter(); - writer.setOptions(localeId, getOutputEncoding()); - if (requireFileOutput) { - writeTranslatedFileWithFileOutput(output, originalFile, + IFilterWriter writer = filter.createFilterWriter(); + writer.setOptions(localeId, getOutputEncoding()); + + if (requireFileOutput) { + writeTranslatedFileWithFileOutput(output, originalFile, translations, localeId, writer, params); - } else { - writer.setOutput(output); - generateTranslatedFile(originalFile, translations, localeId, + } else { + writer.setOutput(output); + generateTranslatedFile(originalFile, translations, localeId, writer, params); + } + } catch (IllegalArgumentException e) { + throw new FileFormatAdapterException( + "Unable to generate translated file", e); } } + /** + * Transform list of TextFlowTarget to map with TextFlowTarget.resId as key + * + * @param targets + * @return + */ + private Map transformToMapByResId( + List targets) { + Map resIdTargetMap = Maps.newHashMap(); + + for(TextFlowTarget target: targets) { + resIdTargetMap.put(target.getResId(), target); + } + return resIdTargetMap; + } + protected String getOutputEncoding() { - return "UTF-8"; + return Charsets.UTF_8.name(); } private void writeTranslatedFileWithFileOutput(OutputStream output, @@ -370,12 +398,7 @@ private void writeTranslatedFileWithFileOutput(OutputStream output, generateTranslatedFile(originalFile, translations, localeId, writer, params); - byte[] buffer = new byte[4096]; // To hold file contents - int bytesRead; - FileInputStream input = new FileInputStream(tempFile); - while ((bytesRead = input.read(buffer)) != -1) { - output.write(buffer, 0, bytesRead); - } + FileUtil.writeFileToOutputStream(tempFile, output); } catch (IOException e) { // FIXME log throw new FileFormatAdapterException( @@ -385,14 +408,7 @@ private void writeTranslatedFileWithFileOutput(OutputStream output, throw new FileFormatAdapterException( "Unable to generate translated file", e); } finally { - if (tempFile != null) { - if (!tempFile.delete()) { - log.warn( - "unable to remove temporary file {}, marked for delete on exit", - tempFile.getAbsolutePath()); - tempFile.deleteOnExit(); - } - } + FileUtil.tryDeleteFile(tempFile); } } diff --git a/zanata-war/src/main/java/org/zanata/adapter/PropertiesLatinOneAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/PropertiesLatinOneAdapter.java new file mode 100644 index 0000000000..b5d107b801 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/adapter/PropertiesLatinOneAdapter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * 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.zanata.adapter; + +/** + * Adapter to read and write {@link org.zanata.common.DocumentType#PROPERTIES} file + * + * TODO: Convert to okapi properties adapter once all client conversion is + * migrated to server + * + * @author Alex Eng aeng@redhat.com + */ +public class PropertiesLatinOneAdapter extends GenericPropertiesAdapter { + public PropertiesLatinOneAdapter() { + super(GenericPropertiesAdapter.ISO_8859_1); + } +} diff --git a/zanata-war/src/main/java/org/zanata/adapter/PropertiesUTF8Adapter.java b/zanata-war/src/main/java/org/zanata/adapter/PropertiesUTF8Adapter.java new file mode 100644 index 0000000000..9d47b7845b --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/adapter/PropertiesUTF8Adapter.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * 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.zanata.adapter; + +/** + * Adapter to read and write + * {@link org.zanata.common.DocumentType#PROPERTIES_UTF8} file + * + * TODO: Convert to okapi properties adapter once all client conversion is + * migrated to server + * + * @author Alex Eng aeng@redhat.com + */ +public class PropertiesUTF8Adapter extends GenericPropertiesAdapter { + public PropertiesUTF8Adapter() { + super(GenericPropertiesAdapter.UTF_8); + } +} diff --git a/zanata-war/src/main/java/org/zanata/adapter/XliffAdapter.java b/zanata-war/src/main/java/org/zanata/adapter/XliffAdapter.java new file mode 100644 index 0000000000..a4b226fa0a --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/adapter/XliffAdapter.java @@ -0,0 +1,130 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * 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.zanata.adapter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; + +import lombok.extern.slf4j.Slf4j; + +import org.zanata.adapter.xliff.XliffCommon; +import org.zanata.adapter.xliff.XliffReader; +import org.zanata.adapter.xliff.XliffWriter; +import org.zanata.common.LocaleId; +import org.zanata.exception.FileFormatAdapterException; +import org.zanata.rest.dto.resource.Resource; +import org.zanata.rest.dto.resource.TranslationsResource; + +import com.google.common.base.Optional; +import org.zanata.util.FileUtil; + +/** + * Adapter to read and write {@link org.zanata.common.DocumentType#XLIFF} file + * + * TODO: Convert to okapi xliff adapter once all client conversion is + * migrated to server + * + * @author Alex Eng aeng@redhat.com + */ +@Slf4j +public class XliffAdapter implements FileFormatAdapter { + + @Override + public Resource parseDocumentFile(URI fileUri, LocaleId sourceLocale, + Optional filterParams) throws FileFormatAdapterException, + IllegalArgumentException { + + if (sourceLocale == null) { + throw new IllegalArgumentException("Source locale cannot be null"); + } + + if (fileUri == null) { + throw new IllegalArgumentException("Document URI cannot be null"); + } + + XliffReader xliffReader = new XliffReader(); + + File tempFile = null; + Resource doc = null; + try { + tempFile = new File(fileUri); + doc = xliffReader.extractTemplate(tempFile, sourceLocale, + tempFile.getName(), + XliffCommon.ValidationType.CONTENT.name()); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Could not open the URL. The URL is OK but the input stream could not be opened.\n" + + e.getMessage(), e); + } + return doc; + } + + @Override + public TranslationsResource parseTranslationFile(URI fileUri, + LocaleId sourceLocaleId, String localeId, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + + XliffReader xliffReader = new XliffReader(); + TranslationsResource targetDoc = null; + File transFile = null; + + try { + transFile = new File(fileUri); + targetDoc = xliffReader.extractTarget(transFile); + } catch (FileNotFoundException e) { + throw new FileFormatAdapterException( + "Could not open the URL. The URL is OK but the input stream could not be opened.\n" + + e.getMessage(), e); + } finally { + FileUtil.tryDeleteFile(transFile); + } + return targetDoc; + } + + @Override + public void writeTranslatedFile(OutputStream output, URI originalFile, + Resource resource, TranslationsResource translationsResource, + String locale, Optional params) + throws FileFormatAdapterException, IllegalArgumentException { + + //write source string with empty translation + boolean createSkeletons = true; + + File tempFile = null; + try { + tempFile = File.createTempFile("filename", "extension"); + XliffWriter.writeFile(tempFile, resource, locale, + translationsResource, createSkeletons); + + FileUtil.writeFileToOutputStream(tempFile, output); + } catch (IOException e) { + throw new FileFormatAdapterException( + "Unable to generate translated file", e); + } finally { + FileUtil.tryDeleteFile(tempFile); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/file/DocumentUploadUtil.java b/zanata-war/src/main/java/org/zanata/file/DocumentUploadUtil.java index 6f7f17de32..76d22a81b1 100644 --- a/zanata-war/src/main/java/org/zanata/file/DocumentUploadUtil.java +++ b/zanata-war/src/main/java/org/zanata/file/DocumentUploadUtil.java @@ -161,10 +161,17 @@ private void failIfUploadIdNotValidAndMatching(GlobalDocumentId id, private static void failIfDocumentTypeNotRecognized( DocumentFileUploadForm uploadForm) throws ChunkUploadException { - if (DocumentType.typeFor(uploadForm.getFileType()) == null) { - throw new ChunkUploadException(Status.PRECONDITION_FAILED, + try { + DocumentType type = DocumentType.valueOf(uploadForm.getFileType()); + if(type == null) { + throw new ChunkUploadException(Status.PRECONDITION_FAILED, "Value '" + uploadForm.getFileType() - + "' is not a recognized document type."); + + "' is not a recognized document type."); + } + } catch (IllegalArgumentException e) { + throw new ChunkUploadException(Status.PRECONDITION_FAILED, + "Value '" + uploadForm.getFileType() + + "' is not a recognized document type."); } } @@ -217,7 +224,7 @@ private HDocumentUpload createMultipartUpload(GlobalDocumentId id, HDocumentUpload newUpload = new HDocumentUpload(); newUpload.setProjectIteration(projectIteration); newUpload.setDocId(id.getDocId()); - newUpload.setType(DocumentType.typeFor(uploadForm.getFileType())); + newUpload.setType(DocumentType.getByName(uploadForm.getFileType())); // locale intentionally left null for source newUpload.setLocale(locale); newUpload.setContentHash(uploadForm.getHash()); diff --git a/zanata-war/src/main/java/org/zanata/file/FilePersistService.java b/zanata-war/src/main/java/org/zanata/file/FilePersistService.java index c3b2cff205..1770af048a 100644 --- a/zanata-war/src/main/java/org/zanata/file/FilePersistService.java +++ b/zanata-war/src/main/java/org/zanata/file/FilePersistService.java @@ -30,7 +30,7 @@ public interface FilePersistService { // InputStream) public void persistRawDocumentContentFromFile(HRawDocument rawDocument, - File rawFile); + File rawFile, String extension); void copyAndPersistRawDocument(HRawDocument fromDoc, HRawDocument toDoc); diff --git a/zanata-war/src/main/java/org/zanata/file/FileSystemPersistService.java b/zanata-war/src/main/java/org/zanata/file/FileSystemPersistService.java index 00ebba399b..b0e8bb0a8e 100644 --- a/zanata-war/src/main/java/org/zanata/file/FileSystemPersistService.java +++ b/zanata-war/src/main/java/org/zanata/file/FileSystemPersistService.java @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.AutoCreate; import org.jboss.seam.annotations.In; @@ -60,8 +61,8 @@ public class FileSystemPersistService implements FilePersistService { @Override public void persistRawDocumentContentFromFile(HRawDocument rawDocument, - File fromFile) { - String fileName = generateFileNameFor(rawDocument); + File fromFile, String extension) { + String fileName = generateFileNameFor(rawDocument, extension); rawDocument.setFileId(fileName); File newFile = getFileForName(fileName); @@ -82,7 +83,9 @@ public void persistRawDocumentContentFromFile(HRawDocument rawDocument, @Override public void copyAndPersistRawDocument(HRawDocument fromDoc, HRawDocument toDoc) { - persistRawDocumentContentFromFile(toDoc, getFileForRawDocument(fromDoc)); + File file = getFileForRawDocument(fromDoc); + persistRawDocumentContentFromFile(toDoc, file, + FilenameUtils.getExtension(file.getName())); } private File getFileForName(String fileName) { @@ -104,11 +107,10 @@ private File ensureDocsDirectory() { return docsDirectory; } - private static String generateFileNameFor(HRawDocument rawDocument) { + private static String generateFileNameFor(HRawDocument rawDocument, String extension) { // Could change to use id of rawDocument, and throw if rawDocument has // no id yet. String idAsString = rawDocument.getDocument().getId().toString(); - String extension = rawDocument.getType().getExtension(); return idAsString + "." + extension; } diff --git a/zanata-war/src/main/java/org/zanata/file/SourceDocumentUpload.java b/zanata-war/src/main/java/org/zanata/file/SourceDocumentUpload.java index 9c131fbccc..2fb04e4108 100644 --- a/zanata-war/src/main/java/org/zanata/file/SourceDocumentUpload.java +++ b/zanata-war/src/main/java/org/zanata/file/SourceDocumentUpload.java @@ -34,11 +34,13 @@ import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.zanata.common.DocumentType; import org.zanata.common.EntityStatus; import org.zanata.common.LocaleId; +import org.zanata.common.ProjectType; import org.zanata.dao.DocumentDAO; import org.zanata.dao.DocumentUploadDAO; import org.zanata.dao.ProjectIterationDAO; @@ -147,17 +149,28 @@ public Response tryValidatedUploadSourceFile(GlobalDocumentId id, uploadForm)); } - if (DocumentType.typeFor(uploadForm.getFileType()) == DocumentType.GETTEXT_PORTABLE_OBJECT_TEMPLATE) { - InputStream potStream = getInputStream(tempFile, uploadForm); - parsePotFile(potStream, id, uploadForm); - } else { + HProjectIteration version = + projectIterationDAO.getBySlug(id.getProjectSlug(), id.getVersionSlug()); + + if (version == null) { + throw new ZanataServiceException("Project version not found: " + + id.getProjectSlug() + " " + id.getVersionSlug()); + } + + if (version.getProjectType() == ProjectType.File) { if (!tempFile.isPresent()) { - tempFile = - Optional.of(util - .persistTempFileFromUpload(uploadForm)); + tempFile = Optional.of(util + .persistTempFileFromUpload(uploadForm)); } processAdapterFile(tempFile.get(), id, uploadForm); + } else if (DocumentType.getByName(uploadForm.getFileType()) == DocumentType.GETTEXT) { + InputStream potStream = getInputStream(tempFile, uploadForm); + parsePotFile(potStream, id, uploadForm); + } else { + throw new ZanataServiceException("Unsupported source file: " + + id.getDocId()); } + if (tempFile.isPresent()) { tempFile.get().delete(); } @@ -202,7 +215,7 @@ private boolean isDocumentUploadAllowed(GlobalDocumentId id) { private void failIfFileTypeNotValid(DocumentFileUploadForm uploadForm) throws ChunkUploadException { - DocumentType type = DocumentType.typeFor(uploadForm.getFileType()); + DocumentType type = DocumentType.getByName(uploadForm.getFileType()); if (!isSourceDocumentType(type)) { throw new ChunkUploadException(Status.BAD_REQUEST, "The type \"" + uploadForm.getFileType() @@ -216,7 +229,7 @@ private boolean isSourceDocumentType(DocumentType type) { } private boolean isPotType(DocumentType type) { - return type == DocumentType.GETTEXT_PORTABLE_OBJECT_TEMPLATE; + return type == DocumentType.GETTEXT; } private boolean isAdapterType(DocumentType type) { @@ -268,10 +281,13 @@ private void processAdapterFile(@Nonnull File tempFile, id.getVersionSlug(), id.getDocId()); } try { + Optional docType = + Optional.fromNullable(uploadForm.getFileType()); + Resource doc = translationFileServiceImpl.parseUpdatedAdapterDocumentFile( tempFile.toURI(), id.getDocId(), - uploadForm.getFileType(), params); + uploadForm.getFileType(), params, docType); doc.setLang(LocaleId.EN_US); // TODO Copy Trans values document = @@ -288,7 +304,7 @@ private void processAdapterFile(@Nonnull File tempFile, String contentHash = uploadForm.getHash(); DocumentType documentType = - DocumentType.typeFor(uploadForm.getFileType()); + DocumentType.getByName(uploadForm.getFileType()); persistRawDocument(document, tempFile, contentHash, documentType, params); @@ -305,7 +321,7 @@ private void persistRawDocument(HDocument document, File rawFile, rawDocument.setType(documentType); rawDocument.setUploadedBy(identity.getCredentials().getUsername()); filePersistService.persistRawDocumentContentFromFile(rawDocument, - rawFile); + rawFile, FilenameUtils.getExtension(rawFile.getName())); if (params.isPresent()) { rawDocument.setAdapterParameters(params.get()); } diff --git a/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java b/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java index e66b0eb31b..cdccec2a76 100644 --- a/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java +++ b/zanata-war/src/main/java/org/zanata/file/TranslationDocumentUpload.java @@ -135,11 +135,14 @@ public class TranslationDocumentUpload { // FIXME this is misusing the 'filename' field. the method // should probably take a // type anyway + Optional docType = + Optional.fromNullable(uploadForm.getFileType()); + transRes = translationFileServiceImpl.parseAdapterTranslationFile( tempFile.get(), id.getProjectSlug(), id.getVersionSlug(), id.getDocId(), localeId, - uploadForm.getFileType()); + uploadForm.getFileType(), docType); } if (tempFile.isPresent()) { tempFile.get().delete(); @@ -192,7 +195,7 @@ private void failIfFileTypeNotValid(DocumentFileUploadForm uploadForm) String fileType = uploadForm.getFileType(); if (!fileType.equals(".po") && !translationFileServiceImpl.hasAdapterFor(DocumentType - .typeFor(fileType))) { + .getByName(fileType))) { throw new ChunkUploadException(Status.BAD_REQUEST, "The type \"" + fileType + "\" specified in form parameter 'type' " + "is not valid for a translation file on this server."); diff --git a/zanata-war/src/main/java/org/zanata/liquibase/custom/MigrateRawDocumentsToFileSystem.java b/zanata-war/src/main/java/org/zanata/liquibase/custom/MigrateRawDocumentsToFileSystem.java index 232a18f8b1..3bb23d6d7b 100644 --- a/zanata-war/src/main/java/org/zanata/liquibase/custom/MigrateRawDocumentsToFileSystem.java +++ b/zanata-war/src/main/java/org/zanata/liquibase/custom/MigrateRawDocumentsToFileSystem.java @@ -244,7 +244,9 @@ private static String fileNameFromResults(ResultSet idAndTypeResult) } private static String fileNameFromIdAndType(Long docId, String type) { - String extension = DocumentType.valueOf(type).getExtension(); + String extension = + DocumentType.valueOf(type).getSourceExtensions().iterator() + .next(); return docId.toString() + "." + extension; } 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 69493a67b8..ddb5c126bd 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 @@ -25,28 +25,34 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Type; import java.net.URI; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; +import java.util.List; import java.util.Properties; import java.util.Set; import javax.annotation.Nonnull; import javax.ws.rs.Path; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; +import org.jboss.resteasy.util.GenericType; + import lombok.extern.slf4j.Slf4j; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.zanata.adapter.FileFormatAdapter; import org.zanata.adapter.po.PoWriter2; import org.zanata.common.ContentState; +import org.zanata.common.DocumentType; import org.zanata.common.LocaleId; import org.zanata.dao.DocumentDAO; import org.zanata.file.FilePersistService; @@ -55,6 +61,7 @@ import org.zanata.file.SourceDocumentUpload; import org.zanata.file.TranslationDocumentUpload; import org.zanata.model.HDocument; +import org.zanata.model.HRawDocument; import org.zanata.rest.DocumentFileUploadForm; import org.zanata.rest.StringSet; import org.zanata.rest.dto.resource.Resource; @@ -66,6 +73,7 @@ import com.google.common.base.Optional; import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.io.ByteStreams; @Name("fileService") @@ -102,7 +110,12 @@ public class FileService implements FileResource { @In("filePersistService") private FilePersistService filePersistService; + /** + * Deprecated. + * @see #acceptedFileTypeList + */ @Override + @Deprecated public Response acceptedFileTypes() { StringSet acceptedTypes = new StringSet(""); acceptedTypes.addAll(translationFileServiceImpl @@ -110,6 +123,17 @@ public Response acceptedFileTypes() { return Response.ok(acceptedTypes.toString()).build(); } + @Override + public Response acceptedFileTypeList() { + Type genericType = new GenericType>() { + }.getGenericType(); + + Object entity = + new GenericEntity>(Lists.newArrayList(translationFileServiceImpl + .getSupportedDocumentTypes()), genericType); + return Response.ok(entity).build(); + } + @Override public Response uploadSourceFile(String projectSlug, String iterationSlug, String docId, DocumentFileUploadForm uploadForm) { @@ -129,7 +153,7 @@ public Response uploadTranslationFile(String projectSlug, GlobalDocumentId id = new GlobalDocumentId(projectSlug, iterationSlug, docId); return translationUploader.tryUploadTranslationFile(id, localeId, - merge, assignCreditToUploader , uploadForm); + merge, assignCreditToUploader, uploadForm); } @Override @@ -228,6 +252,8 @@ public Response downloadTranslationFile(String projectSlug, if (!filePersistService.hasPersistedDocument(id)) { return Response.status(Status.NOT_FOUND).build(); } + Resource res = this.resourceUtils.buildResource(document); + final Set extensions = Collections. emptySet(); TranslationsResource transRes = (TranslationsResource) this.translatedDocResourceService @@ -237,8 +263,8 @@ public Response downloadTranslationFile(String projectSlug, // include fuzzy. // New list is used as transRes list appears not to be a modifiable // implementation. - Map translations = - new HashMap(); + List filteredTranslations = Lists.newArrayList(); + boolean useFuzzy = FILETYPE_TRANSLATED_APPROVED_AND_FUZZY.equals(fileType); for (TextFlowTarget target : transRes.getTextFlowTargets()) { @@ -248,18 +274,18 @@ public Response downloadTranslationFile(String projectSlug, // this if (target.getState() == ContentState.Approved || (useFuzzy && target.getState() == ContentState.NeedReview)) { - translations.put(target.getResId(), target); + filteredTranslations.add(target); } } - HDocument hDocument = - documentDAO.getByProjectIterationAndDocId(projectSlug, - iterationSlug, docId); + transRes.getTextFlowTargets().clear(); + transRes.getTextFlowTargets().addAll(filteredTranslations); + InputStream inputStream; try { inputStream = filePersistService - .getRawDocumentContentAsStream(hDocument + .getRawDocumentContentAsStream(document .getRawDocument()); } catch (RawDocumentContentAccessException e) { log.error(e.toString(), e); @@ -274,24 +300,24 @@ public Response downloadTranslationFile(String projectSlug, // the generated file should be scanned instead virusScanner.scan(tempFile, name); URI uri = tempFile.toURI(); + HRawDocument hRawDocument = document.getRawDocument(); FileFormatAdapter adapter = - translationFileServiceImpl.getAdapterFor(hDocument - .getRawDocument().getType()); - String rawParamString = - hDocument.getRawDocument().getAdapterParameters(); + translationFileServiceImpl.getAdapterFor(hRawDocument.getType()); + String rawParamString = hRawDocument.getAdapterParameters(); Optional params = Optional. fromNullable(Strings .emptyToNull(rawParamString)); StreamingOutput output = - new FormatAdapterStreamingOutput(uri, translations, locale, - adapter, params); + new FormatAdapterStreamingOutput(uri, res, transRes, + locale, adapter, params); + response = Response.ok() .header("Content-Disposition", "attachment; filename=\"" - + document.getName() + "\"") + + generateTranslationFileName(document) + "\"") .entity(output).build(); - // TODO damason: remove more immediately, but make sure response has + // TODO damason: remove more immediately, but make sure response has // finished with the file // Note: may not be necessary when file storage is on disk. tempFile.deleteOnExit(); @@ -301,6 +327,25 @@ Optional. fromNullable(Strings return response; } + /** + * Generate translation file with translation extension from DocumentType + * + * Source file name will be used if translation extension cannot be found + * in HRawDocument#DocumentType + * + * @param document + */ + private String generateTranslationFileName(HDocument document) { + String srcExt = FilenameUtils.getExtension(document.getName()); + DocumentType documentType = document.getRawDocument().getType(); + String transExt = documentType.getExtensions().get(srcExt); + if (StringUtils.isEmpty(transExt)) { + return document.getName(); + } + return FilenameUtils.removeExtension(document + .getName()) + "." + transExt; + } + @Override public Response download(String downloadId) { // TODO scan (again) for virus @@ -408,16 +453,19 @@ public void write(OutputStream output) throws IOException, } private class FormatAdapterStreamingOutput implements StreamingOutput { - private Map translations; + private Resource resource; + private TranslationsResource translationsResource; private String locale; private URI original; private FileFormatAdapter adapter; private Optional params; public FormatAdapterStreamingOutput(URI originalDoc, - Map translations, String locale, - FileFormatAdapter adapter, Optional params) { - this.translations = translations; + Resource resource, TranslationsResource translationsResource, + String locale, FileFormatAdapter adapter, + Optional params) { + this.resource = resource; + this.translationsResource = translationsResource; this.locale = locale; this.original = originalDoc; this.adapter = adapter; @@ -428,8 +476,8 @@ public FormatAdapterStreamingOutput(URI originalDoc, public void write(OutputStream output) throws IOException, WebApplicationException { // FIXME should the generated file be virus scanned? - adapter.writeTranslatedFile(output, original, translations, locale, - params); + adapter.writeTranslatedFile(output, original, resource, + translationsResource, locale, params); } } 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 8a120e7533..26475a6327 100644 --- a/zanata-war/src/main/java/org/zanata/service/TranslationFileService.java +++ b/zanata-war/src/main/java/org/zanata/service/TranslationFileService.java @@ -28,6 +28,7 @@ import com.google.common.base.Optional; +import javax.annotation.Nonnull; import java.io.File; import java.io.InputStream; import java.net.URI; @@ -56,7 +57,7 @@ public interface TranslationFileService { */ TranslationsResource parseTranslationFile(InputStream fileContents, String fileName, String localeId, String projectSlug, - String iterationSlug, String docId) throws ZanataServiceException; + String iterationSlug, String docId, Optional documentType) throws ZanataServiceException; /** * Extract the translated strings from a po file to usable form, using @@ -87,7 +88,7 @@ TranslationsResource parsePoFile(InputStream fileContents, */ TranslationsResource parseAdapterTranslationFile(File tempFile, String projectSlug, String iterationSlug, String docId, - String localeId, String fileName) throws ZanataServiceException; + String localeId, String fileName, Optional documentType) throws ZanataServiceException; /** * Extract the translatable strings from a new document file or from a new @@ -106,6 +107,10 @@ TranslationsResource parseAdapterTranslationFile(File tempFile, Resource parseUpdatedPotFile(InputStream fileContents, String docId, String uploadFileName, boolean offlinePo); + boolean hasMultipleDocumentTypes(String fileNameOrExtension); + + Set getDocumentTypes(String fileNameOrExtension); + /** * Extracts the translatable strings from a document file to a usable form. * @@ -124,7 +129,7 @@ Resource parseUpdatedPotFile(InputStream fileContents, String docId, * there is an error during parsing */ Resource parseAdapterDocumentFile(URI documentFile, String path, - String fileName, Optional params) + String fileName, Optional params, Optional documentType) throws ZanataServiceException; /** @@ -145,7 +150,7 @@ Resource parseAdapterDocumentFile(URI documentFile, String path, * @throws ZanataServiceException */ Resource parseUpdatedAdapterDocumentFile(URI documentFile, String docId, - String uploadFileName, Optional params) + String uploadFileName, Optional params, Optional documentType) throws ZanataServiceException; /** @@ -161,7 +166,7 @@ Resource parseUpdatedAdapterDocumentFile(URI documentFile, String docId, FileFormatAdapter getAdapterFor(DocumentType type); - DocumentType getDocumentType(String fileNameOrExtension); + Set getSupportedDocumentTypes(); /** * Persist an input stream to a temporary file. @@ -186,24 +191,15 @@ Resource parseUpdatedAdapterDocumentFile(URI documentFile, String docId, */ void removeTempFile(File tempFile); - String getFileExtension(String projectSlug, String iterationSlug, + String getSourceFileExtension(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); + String getTranslationFileExtension(String projectSlug, String iterationSlug, + String docPath, String docName); /** * @return true if the specified document is of type po, false if it is any * other type, including null. */ - boolean - isPoDocument(String projectSlug, String iterationSlug, String docId); - - String generateDocId(String path, String fileName); - + boolean isPoDocument(String projectSlug, String iterationSlug, String docId); } 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 e4e779e6dd..71da934890 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 @@ -23,16 +23,23 @@ import com.google.common.base.Optional; import com.google.common.collect.MapMaker; import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.xml.sax.InputSource; import org.zanata.adapter.DTDAdapter; import org.zanata.adapter.FileFormatAdapter; +import org.zanata.adapter.GettextAdapter; import org.zanata.adapter.HTMLAdapter; import org.zanata.adapter.IDMLAdapter; import org.zanata.adapter.OpenOfficeAdapter; import org.zanata.adapter.PlainTextAdapter; +import org.zanata.adapter.PropertiesLatinOneAdapter; +import org.zanata.adapter.PropertiesUTF8Adapter; +import org.zanata.adapter.XliffAdapter; import org.zanata.adapter.po.PoReader2; import org.zanata.adapter.SubtitleAdapter; import org.zanata.common.DocumentType; @@ -44,10 +51,13 @@ import org.zanata.exception.ZanataServiceException; import org.zanata.model.HDocument; import org.zanata.model.HProjectIteration; +import org.zanata.model.HRawDocument; import org.zanata.rest.dto.resource.Resource; import org.zanata.rest.dto.resource.TranslationsResource; import org.zanata.service.TranslationFileService; +import org.zanata.util.FileUtil; +import javax.annotation.Nonnull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -58,8 +68,7 @@ import java.util.Set; import static org.jboss.seam.ScopeType.STATELESS; -import static org.zanata.common.DocumentType.GETTEXT_PORTABLE_OBJECT; -import static org.zanata.common.DocumentType.GETTEXT_PORTABLE_OBJECT_TEMPLATE; +import static org.zanata.common.DocumentType.GETTEXT; import static org.zanata.common.DocumentType.HTML; import static org.zanata.common.DocumentType.IDML; import static org.zanata.common.DocumentType.OPEN_DOCUMENT_GRAPHICS; @@ -67,7 +76,10 @@ import static org.zanata.common.DocumentType.OPEN_DOCUMENT_SPREADSHEET; import static org.zanata.common.DocumentType.OPEN_DOCUMENT_TEXT; import static org.zanata.common.DocumentType.PLAIN_TEXT; +import static org.zanata.common.DocumentType.PROPERTIES; +import static org.zanata.common.DocumentType.PROPERTIES_UTF8; import static org.zanata.common.DocumentType.SUBTITLE; +import static org.zanata.common.DocumentType.XLIFF; import static org.zanata.common.DocumentType.XML_DOCUMENT_TYPE_DEFINITION; /** @@ -95,6 +107,10 @@ public class TranslationFileServiceImpl implements TranslationFileService { DOCTYPEMAP.put(IDML, IDMLAdapter.class); DOCTYPEMAP.put(HTML, HTMLAdapter.class); DOCTYPEMAP.put(SUBTITLE, SubtitleAdapter.class); + DOCTYPEMAP.put(PROPERTIES, PropertiesLatinOneAdapter.class); + DOCTYPEMAP.put(PROPERTIES_UTF8, PropertiesUTF8Adapter.class); + DOCTYPEMAP.put(XLIFF, XliffAdapter.class); + DOCTYPEMAP.put(GETTEXT, GettextAdapter.class); } private static Set SUPPORTED_EXTENSIONS = @@ -103,7 +119,7 @@ public class TranslationFileServiceImpl implements TranslationFileService { private static Set buildSupportedExtensionSet() { Set supported = new HashSet(); for (DocumentType type : DOCTYPEMAP.keySet()) { - supported.addAll(type.getExtensions()); + supported.addAll(type.getSourceExtensions()); } return supported; } @@ -117,19 +133,28 @@ private static Set buildSupportedExtensionSet() { @Override public TranslationsResource parseTranslationFile(InputStream fileContents, String fileName, String localeId, String projectSlug, - String iterationSlug, String docId) throws ZanataServiceException { - if (fileName.endsWith(".po")) { - return parsePoFile(fileContents, projectSlug, iterationSlug, docId); - } else if (hasAdapterFor(fileName)) { + String iterationSlug, String docId, Optional documentType) throws ZanataServiceException { + + HProjectIteration version = + projectIterationDAO.getBySlug(projectSlug, iterationSlug); + + if (version == null) { + throw new ZanataServiceException("Project version not found: " + + projectSlug + " " + iterationSlug); + } + + if (version.getProjectType() == ProjectType.File) { File tempFile = persistToTempFile(fileContents); TranslationsResource transRes = - parseAdapterTranslationFile(tempFile, projectSlug, - iterationSlug, docId, localeId, fileName); + parseAdapterTranslationFile(tempFile, projectSlug, + iterationSlug, docId, localeId, fileName, documentType); removeTempFile(tempFile); return transRes; + } else if (fileName.endsWith(".po")) { + return parsePoFile(fileContents, projectSlug, iterationSlug, docId); } else { throw new ZanataServiceException("Unsupported Translation file: " - + fileName); + + fileName); } } @@ -148,14 +173,17 @@ public TranslationsResource parsePoFile(InputStream fileContents, @Override public TranslationsResource parseAdapterTranslationFile(File tempFile, String projectSlug, String iterationSlug, String docId, - String localeId, String fileName) { - Optional params = - documentDAO.getAdapterParams(projectSlug, iterationSlug, docId); + String localeId, String fileName, Optional documentType) { + HDocument doc = + documentDAO.getByProjectIterationAndDocId(projectSlug, + iterationSlug, docId); + TranslationsResource transRes; + FileFormatAdapter adapter = getAdapterFor(documentType, fileName); try { - transRes = - getAdapterFor(fileName).parseTranslationFile( - tempFile.toURI(), localeId, params); + transRes = adapter.parseTranslationFile(tempFile.toURI(), doc + .getSourceLocaleId(), localeId, + getAdapterParams(doc)); } catch (FileFormatAdapterException e) { throw new ZanataServiceException("Error parsing translation file: " + fileName, e); @@ -165,15 +193,18 @@ public TranslationsResource parseAdapterTranslationFile(File tempFile, return transRes; } - @Override - public String generateDocId(String path, String fileName) { - String docName = fileName; - if (docName.endsWith(".pot")) { - docName = docName.substring(0, docName.lastIndexOf('.')); + public Optional getAdapterParams(HDocument doc) { + if (doc != null) { + HRawDocument rawDoc = doc.getRawDocument(); + if (rawDoc != null) { + return Optional.fromNullable(rawDoc.getAdapterParameters()); + } } - return convertToValidPath(path) + docName; + return Optional. absent(); } + + @Override public Resource parseUpdatedPotFile(InputStream fileContents, String docId, String fileName, boolean offlinePo) { @@ -190,53 +221,42 @@ public Resource parseUpdatedPotFile(InputStream fileContents, String docId, } } + @Override + public boolean hasMultipleDocumentTypes(String fileNameOrExtension) { + String extension = FilenameUtils.getExtension(fileNameOrExtension); + return DocumentType.fromSourceExtension(extension).size() > 1; + } + + @Override + public Set getDocumentTypes(String fileNameOrExtension) { + String extension = FilenameUtils.getExtension(fileNameOrExtension); + return DocumentType.fromSourceExtension(extension); + } + @Override public Resource parseAdapterDocumentFile(URI documentFile, - String documentPath, String fileName, Optional params) - throws ZanataServiceException { + String documentPath, String fileName, Optional params, + Optional documentType) throws ZanataServiceException { return parseUpdatedAdapterDocumentFile(documentFile, - convertToValidPath(documentPath) + fileName, fileName, params); + FileUtil.convertToValidPath(documentPath) + fileName, fileName, + params, documentType); } @Override public Resource parseUpdatedAdapterDocumentFile(URI documentFile, - String docId, String fileName, Optional params) - throws ZanataServiceException { - if (hasAdapterFor(fileName)) { - FileFormatAdapter adapter = getAdapterFor(fileName); - Resource doc; - try { - doc = - adapter.parseDocumentFile(documentFile, new LocaleId( - "en"), params); - } catch (FileFormatAdapterException e) { - throw new ZanataServiceException( - "Error parsing document file: " + fileName, e); - } - doc.setName(docId); - return doc; - } else { - throw new ZanataServiceException("Unsupported Document file: " - + fileName); - } - } - - /** - * A valid path is either empty, or has a trailing slash and no leading - * slash. - * - * @param path - * @return valid path - */ - private String convertToValidPath(String path) { - path = path.trim(); - while (path.startsWith("/")) { - path = path.substring(1); - } - if (path.length() > 0 && !path.endsWith("/")) { - path = path.concat("/"); + String docId, String fileName, Optional params, + Optional documentType) throws ZanataServiceException { + FileFormatAdapter adapter = getAdapterFor(documentType, fileName); + Resource doc; + try { + doc = adapter.parseDocumentFile(documentFile, new LocaleId( + "en"), params); + } catch (FileFormatAdapterException e) { + throw new ZanataServiceException( + "Error parsing document file: " + fileName, e); } - return path; + doc.setName(docId); + return doc; } private TranslationsResource parsePoFile(InputStream fileContents, @@ -269,12 +289,17 @@ public boolean hasAdapterFor(DocumentType type) { return DOCTYPEMAP.containsKey(type); } + @Override + public Set getSupportedDocumentTypes() { + return DOCTYPEMAP.keySet(); + } + private boolean hasAdapterFor(String fileNameOrExtension) { - String extension = extractExtension(fileNameOrExtension); + String extension = FilenameUtils.getExtension(fileNameOrExtension); if (extension == null) { return false; } - DocumentType documentType = DocumentType.typeFor(extension); + DocumentType documentType = DocumentType.getByName(extension); if (documentType == null) { return false; } @@ -282,24 +307,31 @@ private boolean hasAdapterFor(String fileNameOrExtension) { } private FileFormatAdapter getAdapterFor(String fileNameOrExtension) { - String extension = extractExtension(fileNameOrExtension); + String extension = FilenameUtils.getExtension(fileNameOrExtension); if (extension == null) { throw new RuntimeException( "Cannot find adapter for null filename or extension."); } - DocumentType documentType = DocumentType.typeFor(extension); + DocumentType documentType = DocumentType.getByName(extension); if (documentType == null) { throw new RuntimeException( - "Cannot choose an adapter because the provided string '" - + fileNameOrExtension - + "' does not match any known document type."); + "Cannot choose an adapter because the provided string '" + + fileNameOrExtension + + "' does not match any known document type."); } - return getAdapterFor(documentType); - } - - @Override - public DocumentType getDocumentType(String fileNameOrExtension) { - return DocumentType.typeFor(extractExtension(fileNameOrExtension)); + FileFormatAdapter adapter = getAdapterFor(documentType); + if (hasMultipleDocumentTypes(fileNameOrExtension)) { + /** + * TODO: throw runtime error. Need to wait for all upload file + * dialog implement multiple adapter check for file extension. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=1217671 + */ + log.warn( + "More than 1 adapter found for this file extension: '{}'. Adapter '{}' will be used.", + extension, adapter.getClass().getName()); + } + return adapter; } @Override @@ -316,23 +348,23 @@ public FileFormatAdapter getAdapterFor(DocumentType type) { } } - @Override - public String extractExtension(String fileNameOrExtension) { - if (fileNameOrExtension == null || fileNameOrExtension.length() == 0 - || fileNameOrExtension.endsWith(".")) { - // could throw exception here - return null; - } - - String extension; - if (fileNameOrExtension.contains(".")) { - extension = - fileNameOrExtension.substring(fileNameOrExtension - .lastIndexOf('.') + 1); - } else { - extension = fileNameOrExtension; + /** + * Get an appropriate adapter for a document type or file name. + * + * @param documentType + * @param fileName + * @return adapter for given documentType if present, otherwise return adapter + * with given fileName. + */ + private FileFormatAdapter getAdapterFor(Optional documentType, + @Nonnull String fileName) { + if (documentType.isPresent() && StringUtils.isNotEmpty( + documentType.get())) { + DocumentType docType = DocumentType.valueOf(documentType.get()); + return docType != null ? getAdapterFor(docType) + : getAdapterFor(fileName); } - return extension; + return getAdapterFor(fileName); } @Override @@ -368,12 +400,24 @@ public void removeTempFile(File tempFile) { } @Override - public String getFileExtension(String projectSlug, String iterationSlug, + public String getSourceFileExtension(String projectSlug, String iterationSlug, String docPath, String docName) { + return FilenameUtils.getExtension(docName); + } + + @Override + public String getTranslationFileExtension(String projectSlug, String iterationSlug, + String docPath, String docName) { + + String srcExt = + getSourceFileExtension(projectSlug, iterationSlug, docPath, + docName); + HDocument doc = - documentDAO.getByProjectIterationAndDocId(projectSlug, - iterationSlug, docPath + docName); - return doc.getRawDocument().getType().getExtension(); + documentDAO.getByProjectIterationAndDocId(projectSlug, + iterationSlug, docPath + docName); + + return doc.getRawDocument().getType().getExtensions().get(srcExt); } @Override @@ -401,8 +445,7 @@ public boolean isPoDocument(String projectSlug, String iterationSlug, // additional check in case we do start storing raw documents for po DocumentType docType = doc.getRawDocument().getType(); - return docType == GETTEXT_PORTABLE_OBJECT - || docType == GETTEXT_PORTABLE_OBJECT_TEMPLATE; + return docType == GETTEXT; } return false; } diff --git a/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java b/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java deleted file mode 100644 index cea0366071..0000000000 --- a/zanata-war/src/main/java/org/zanata/servlet/FileUploadServlet.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2012, Red Hat, Inc. and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * 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.zanata.servlet; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Set; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import lombok.extern.slf4j.Slf4j; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.FileItemFactory; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.jboss.seam.servlet.ContextualHttpServletRequest; -import org.zanata.common.LocaleId; -import org.zanata.common.MergeType; -import org.zanata.exception.ZanataServiceException; -import org.zanata.rest.StringSet; -import org.zanata.rest.dto.extensions.ExtensionType; -import org.zanata.rest.dto.resource.TranslationsResource; -import org.zanata.service.TranslationFileService; -import org.zanata.service.TranslationService; -import org.zanata.service.impl.TranslationFileServiceImpl; -import org.zanata.service.impl.TranslationServiceImpl; -import org.zanata.util.ServiceLocator; -import org.zanata.webtrans.client.ui.FileUploadDialog; - -/** - * Used for translation file upload from GWT editor. For endpoint, see servlet - * binding for this class in web.xml - * - * @see {@link FileUploadDialog} - * - * @author Alex Eng aeng@redhat.com - * - */ -@Slf4j -public class FileUploadServlet extends HttpServlet { - private static final long serialVersionUID = 1L; - - private final static String[] MANDATORY_FIELDS = { "projectSlug", - "versionSlug", "docId", "fileName", "targetLocale", - "uploadFileElement", "merge" }; - - private TranslationFileService translationFileServiceImpl; - - private TranslationService translationServiceImpl; - - @Override - protected void doPost(final HttpServletRequest request, - final HttpServletResponse response) throws ServletException, - IOException { - new ContextualHttpServletRequest(request) { - @Override - public void process() throws Exception { - doWork(request, response); - } - }.run(); - } - - private void validateParams(HashMap params) { - for (String mandatoryField : MANDATORY_FIELDS) { - if (!params.containsKey(mandatoryField)) { - throw new ZanataServiceException("Mandatory field '" - + mandatoryField + "' not found in form"); - } - } - - } - - private void doWork(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - - // process only multipart requests - if (ServletFileUpload.isMultipartContent(req)) { - translationFileServiceImpl = - ServiceLocator.instance().getInstance( - TranslationFileServiceImpl.class); - translationServiceImpl = - ServiceLocator.instance().getInstance( - TranslationServiceImpl.class); - - // Create a factory for disk-based file items - FileItemFactory factory = new DiskFileItemFactory(); - - // Create a new file upload handler - ServletFileUpload upload = new ServletFileUpload(factory); - - // Parse the request - try { - List items = upload.parseRequest(req); - HashMap params = - new HashMap(); - for (FileItem item : items) { - if (log.isDebugEnabled()) { - log.debug("param- " + item.getFieldName() + " value-" - + item.getString()); - } - params.put(item.getFieldName(), item); - } - - validateParams(params); - - String projectSlug = params.get("projectSlug").getString(); - String versionSlug = params.get("versionSlug").getString(); - String docId = params.get("docId").getString(); - // process the file - TranslationsResource transRes = - translationFileServiceImpl.parseTranslationFile(params - .get("uploadFileElement").getInputStream(), - params.get("fileName").getString(), - params.get("targetLocale").getString(), - projectSlug, versionSlug, docId); - - // translate it - Set extensions; - if (params.get("fileName").getString().endsWith(".po")) { - extensions = - new StringSet(ExtensionType.GetText.toString()); - } else { - extensions = Collections. emptySet(); - } - MergeType mergeType = - Boolean.parseBoolean(params.get("merge").getString()) ? MergeType.AUTO - : MergeType.IMPORT; - - boolean assignCreditToUploader = Boolean.parseBoolean( - params.get("assignCreditToUploader").getString()); - List warnings = - translationServiceImpl.translateAllInDoc(projectSlug, - versionSlug, docId, - new LocaleId(params.get("targetLocale") - .getString()), transRes, extensions, - mergeType, assignCreditToUploader); - - StringBuilder response = new StringBuilder(); - response.append("Status code: "); - response.append(HttpServletResponse.SC_OK); - response.append(" File '" + params.get("fileName").getString() - + "' uploaded. \n"); - if (!warnings.isEmpty()) { - response.append("Warnings:\n"); - for (String warning : warnings) { - response.append(warning + "\n"); - } - } - resp.setContentLength(response.toString().length()); - resp.setContentType("text/plain"); - resp.setStatus(HttpServletResponse.SC_OK); - resp.setCharacterEncoding("utf8"); - - resp.getWriter().print(response.toString()); - resp.getWriter().flush(); - - } catch (Exception e) { - resp.sendError( - HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - "An error occurred while uploading the file : " - + e.getMessage()); - } - - } else { - resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, - "Request contents type is not supported."); - } - } -} diff --git a/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java b/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java index f8a2db3f49..64be211c70 100644 --- a/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java +++ b/zanata-war/src/main/java/org/zanata/servlet/MultiFileUploadServlet.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.Collections; import java.util.List; import javax.persistence.OptimisticLockException; @@ -34,28 +35,29 @@ import javax.ws.rs.core.Response; import com.google.common.base.Optional; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; import org.hibernate.StaleStateException; import org.hibernate.exception.ConstraintViolationException; import org.jboss.seam.servlet.ContextualHttpServletRequest; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.zanata.action.AuthenticatedAccountHome; +import org.zanata.common.DocumentType; import org.zanata.file.GlobalDocumentId; import org.zanata.file.SourceDocumentUpload; import org.zanata.file.UserFileUploadTracker; import org.zanata.rest.DocumentFileUploadForm; import org.zanata.rest.dto.ChunkUploadResponse; -import org.zanata.service.TranslationFileService; -import org.zanata.service.impl.TranslationFileServiceImpl; +import org.zanata.util.FileUtil; import org.zanata.util.ServiceLocator; import static com.google.common.base.Strings.emptyToNull; @@ -242,29 +244,31 @@ private class FileUploadRequestHandler { private String projectSlug; private String versionSlug; + private final List fileTypes; private String path = ""; private String lang = "en-US"; private String fileParams = ""; private SourceDocumentUpload sourceUploader; - private TranslationFileService translationFileServiceImpl; public FileUploadRequestHandler(HttpServletRequest request) { this.request = request; projectSlug = request.getParameter("p"); versionSlug = request.getParameter("v"); - + /** + * TODO: add types parameter in all caller of /files/upload (multifile upload) + * https://bugzilla.redhat.com/show_bug.cgi?id=1217671 + */ + String fileTypesQuery = request.getParameter("types"); + if (StringUtils.isNotEmpty(fileTypesQuery)) { + fileTypes = Lists.newArrayList(fileTypesQuery.split(",")); + } else { + fileTypes = Collections.emptyList(); + } sourceUploader = ServiceLocator.instance().getInstance(SourceDocumentUpload.class); - translationFileServiceImpl = ServiceLocator.instance().getInstance( - TranslationFileServiceImpl.class); } public JSONArray process() throws FileUploadException { - // FIXME fail with error? - if (translationFileServiceImpl == null) { - log.error("translationFileServiceImpl is null"); - } - List items = getRequestItems(); JSONArray filesJson = processFilesFromItems(items); @@ -322,7 +326,7 @@ private void recordParametersFromItems(List items) { * @return JSON summary of outcome of the attempt. */ private JSONObject processFileItem(FileItem item) { - String docId = translationFileServiceImpl.generateDocId(path, item.getName()); + String docId = FileUtil.generateDocId(path, item.getName()); GlobalDocumentId id = new GlobalDocumentId(projectSlug, versionSlug, docId); Optional errorMessage; @@ -392,11 +396,36 @@ private DocumentFileUploadForm createUploadFormForItem(FileItem item) throws IOE form.setFirst(true); form.setLast(true); form.setSize(item.getSize()); - form.setFileType(translationFileServiceImpl.extractExtension(item.getName())); + + form.setFileType(getFileTypeForItem(item.getName())); form.setFileStream(item.getInputStream()); return form; } + private String getFileTypeForItem(String filename) { + String extension = FilenameUtils.getExtension(filename); + /** + * TODO: Implement docType selection for multifile upload. + * At the moment, get the first docType from returned list. + */ + DocumentType fileType = + DocumentType.fromSourceExtension(extension).iterator() + .next(); + if (!fileTypes.isEmpty()) { + for (String parsedFileType : fileTypes) { + DocumentType docType = DocumentType.getByName( + parsedFileType); + if (docType != null + && docType.getSourceExtensions() + .contains(extension)) { + fileType = docType; + break; + } + } + } + return fileType == null ? extension : fileType.name(); + } + /** * @return absent if the given String is null or empty, otherwise an diff --git a/zanata-war/src/main/java/org/zanata/util/FileUtil.java b/zanata-war/src/main/java/org/zanata/util/FileUtil.java new file mode 100644 index 0000000000..07e7d12b66 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/util/FileUtil.java @@ -0,0 +1,109 @@ +/* + * Copyright 2015, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * 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.zanata.util; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.zanata.exception.FileFormatAdapterException; + +import javax.annotation.Nullable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Utility class for file related operations. + * + * @author Alex Eng aeng@redhat.com + */ +@Slf4j +public class FileUtil { + + /** + * Generate documentId by concatenating path with fileName + * + * e.g "foo", "bar.txt" = "foo/bar.txt" + * + * @param path + * @param fileName + */ + public static String generateDocId(String path, String fileName) { + String docName = fileName; + if (docName.endsWith(".pot")) { + docName = docName.substring(0, docName.lastIndexOf('.')); + } + return convertToValidPath(path) + docName; + } + + /** + * A valid path is either empty, or has a trailing slash and no leading + * slash. + * + * @param path + * @return valid path + */ + public static String convertToValidPath(String path) { + path = path.trim(); + while (path.startsWith("/")) { + path = path.substring(1); + } + if (path.length() > 0 && !path.endsWith("/")) { + path = path.concat("/"); + } + return path; + } + + /** + * Try delete given file or use File#deleteOnExit + * + * @param file + */ + public static void tryDeleteFile(@Nullable File file) { + if (file != null) { + if (!file.delete()) { + log.warn( + "unable to remove file {}, marked for delete on exit", + file.getAbsolutePath()); + file.deleteOnExit(); + } + } + } + + /** + * Write given file to outputstream. + * + * @param file + * @param output + * @throws IOException + */ + public static void writeFileToOutputStream(File file, OutputStream output) + throws IOException { + byte[] buffer = new byte[4096]; // To hold file contents + int bytesRead; + FileInputStream input = new FileInputStream(file); + while ((bytesRead = input.read(buffer)) != -1) { + output.write(buffer, 0, bytesRead); + } + } +} diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java b/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java index eb68ee48bb..35b85d0626 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/Application.java @@ -283,10 +283,6 @@ public static String getAllFilesDownloadURL(String downloadId) { return getModuleParentBaseUrl() + "rest/file/download/" + downloadId; } - public static String getUploadFileUrl() { - return GWT.getModuleBaseURL() + "files/upload"; - } - @CoverageIgnore("JSNI") public static native void redirectToUrl(String url)/*-{ $wnd.location = url; diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/DocumentListPresenter.java b/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/DocumentListPresenter.java index 030a8b8ea6..64b731752b 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/DocumentListPresenter.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/presenter/DocumentListPresenter.java @@ -29,8 +29,6 @@ import java.util.Map; import java.util.Map.Entry; -import javax.servlet.http.HttpServletResponse; - import net.customware.gwt.presenter.client.EventBus; import net.customware.gwt.presenter.client.widget.WidgetPresenter; @@ -45,7 +43,6 @@ import org.zanata.webtrans.client.events.DocumentSelectionHandler; import org.zanata.webtrans.client.events.DocumentStatsUpdatedEvent; import org.zanata.webtrans.client.events.NotificationEvent; -import org.zanata.webtrans.client.events.NotificationEvent.Severity; import org.zanata.webtrans.client.events.RefreshProjectStatsEvent; import org.zanata.webtrans.client.events.RunDocValidationEvent; import org.zanata.webtrans.client.events.RunDocValidationEventHandler; @@ -84,12 +81,10 @@ import com.allen_sauer.gwt.log.client.Log; import com.google.common.base.Objects; -import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gwt.core.shared.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent; import com.google.inject.Inject; public class DocumentListPresenter extends WidgetPresenter @@ -594,44 +589,6 @@ public void onSuccess( }); } - @Override - public void showUploadDialog(DocumentInfo docInfo) { - display.showUploadDialog(docInfo, userWorkspaceContext - .getWorkspaceContext().getWorkspaceId()); - } - - @Override - public void cancelFileUpload() { - display.closeFileUpload(); - } - - @Override - public void onFileUploadComplete(SubmitCompleteEvent event) { - display.closeFileUpload(); - if (event.getResults().contains( - String.valueOf(HttpServletResponse.SC_OK))) { - if (event.getResults().contains("Warnings")) { - eventBus.fireEvent(new NotificationEvent(Severity.Warning, - "File uploaded with warnings", event.getResults(), - true, null)); - } else { - eventBus.fireEvent(new NotificationEvent(Severity.Info, - "File uploaded", event.getResults(), true, null)); - } - queryStats(); - } else { - eventBus.fireEvent(new NotificationEvent(Severity.Error, - "File upload failed.", event.getResults(), true, null)); - } - } - - @Override - public void onUploadFile() { - if (!Strings.isNullOrEmpty(display.getSelectedUploadFileName())) { - display.submitUploadForm(); - } - } - @Override public void onWorkspaceContextUpdated(WorkspaceContextUpdateEvent event) { userWorkspaceContext.setProjectActive(event.isProjectActive()); @@ -694,7 +651,7 @@ public void onRunDocValidation(RunDocValidationEvent event) { @Override public void onFailure(Throwable caught) { eventBus.fireEvent(new NotificationEvent( - Severity.Error, + NotificationEvent.Severity.Error, "Unable to run validation")); eventBus.fireEvent(new DocValidationResultEvent( new Date())); diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java b/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java index e114dd192b..0aa900612a 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/resources/WebTransMessages.java @@ -501,9 +501,6 @@ String undoUnsuccessful(@PluralCount int unsuccessfulCount, @DefaultMessage("Last run: {0}") String lastValidationRun(String completeTime); - @DefaultMessage("Upload document to merge/override translation") - String uploadButtonTitle(); - @DefaultMessage("Download document with extension {0}") String downloadFileTitle(String key); diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/DocumentListTable.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/DocumentListTable.java index ef899202c2..408341e853 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/DocumentListTable.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/DocumentListTable.java @@ -414,17 +414,6 @@ private Widget getActionWidget(final DocumentInfo docInfo) { anchor.setTarget("_blank"); panel.add(anchor); } - Anchor upload = new Anchor(); - upload.setTitle(messages.uploadButtonTitle()); - upload.setStyleName("i i--import l--push-left-half"); - upload.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - listener.showUploadDialog(docInfo); - } - }); - panel.add(upload); - return panel; } diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java b/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java deleted file mode 100644 index 1b5f97e39a..0000000000 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/ui/FileUploadDialog.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.zanata.webtrans.client.ui; - -import com.google.gwt.user.client.ui.Button; -import org.zanata.webtrans.client.resources.Resources; -import org.zanata.webtrans.client.view.DocumentListDisplay; -import org.zanata.webtrans.shared.model.DocumentInfo; -import org.zanata.webtrans.shared.model.WorkspaceId; - -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.DialogBox; -import com.google.gwt.user.client.ui.FileUpload; -import com.google.gwt.user.client.ui.FormPanel; -import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent; -import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler; -import com.google.gwt.user.client.ui.HasHorizontalAlignment; -import com.google.gwt.user.client.ui.Hidden; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Image; -import com.google.gwt.user.client.ui.PushButton; -import com.google.gwt.user.client.ui.VerticalPanel; - -public class FileUploadDialog extends DialogBox { - private final FileUpload upload; - private final CheckBox merge; - private final CheckBox myTranslations; - private final Button cancelButton; - private final Button uploadButton; - private final Image loadingIcon; - private final FormPanel form; - - private final Hidden projectSlug; - private final Hidden versionSlug; - private final Hidden docId; - private final Hidden fileName; - private final Hidden targetLocale; - private final Hidden mergeTranslation; - private final Hidden assignCreditToUploader; - - public FileUploadDialog(Resources resources) { - setText("File upload"); - setGlassEnabled(true); - setAutoHideEnabled(true); - setStyleName("gwt-DialogBox"); - - UnorderedListWidget panel = new UnorderedListWidget(); - panel.setStyleName("list--no-bullets"); - - loadingIcon = new Image(resources.spinner()); - loadingIcon.setVisible(false); - - upload = new FileUpload(); - upload.setName("uploadFileElement"); - - merge = new CheckBox("Merge?"); - merge.setValue(true); - - myTranslations = new CheckBox("My translations?"); - myTranslations.setValue(false); - - cancelButton = new Button("Cancel"); - uploadButton = new Button("Upload"); - - UnorderedListWidget buttonPanel = new UnorderedListWidget(); - buttonPanel.setStyleName("list--horizontal l--float-right"); - buttonPanel.add(new ListItemWidget(loadingIcon)); - buttonPanel.add(new ListItemWidget(cancelButton)); - buttonPanel.add(new ListItemWidget(uploadButton)); - - panel.add(new ListItemWidget(upload)); - panel.add(new ListItemWidget(merge)); - panel.add(new ListItemWidget(myTranslations)); - panel.add(new ListItemWidget(buttonPanel)); - - projectSlug = new Hidden("projectSlug"); - versionSlug = new Hidden("versionSlug"); - docId = new Hidden("docId"); - fileName = new Hidden("fileName"); - targetLocale = new Hidden("targetLocale"); - mergeTranslation = new Hidden("merge"); - assignCreditToUploader = new Hidden("assignCreditToUploader"); - - panel.add(projectSlug); - panel.add(versionSlug); - panel.add(docId); - panel.add(fileName); - panel.add(targetLocale); - panel.add(mergeTranslation); - panel.add(assignCreditToUploader); - - // Because we're going to add a FileUpload widget, we'll need to set - // the - // form to use the POST method, and multipart MIME encoding. - form = new FormPanel(); - form.setStyleName("new-zanata"); - form.setEncoding(FormPanel.ENCODING_MULTIPART); - form.setMethod(FormPanel.METHOD_POST); - form.setWidget(panel); - - add(form); - } - - public void registerHandler(final DocumentListDisplay.Listener listener, - String uploadFileURL) { - form.setAction(uploadFileURL); - cancelButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - listener.cancelFileUpload(); - } - }); - - uploadButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - listener.onUploadFile(); - } - }); - - form.addSubmitCompleteHandler(new SubmitCompleteHandler() { - @Override - public void onSubmitComplete(SubmitCompleteEvent event) { - listener.onFileUploadComplete(event); - } - }); - } - - public void submitForm() { - fileName.setValue(getUploadFileName()); - mergeTranslation.setValue(merge.getValue().toString()); - assignCreditToUploader.setValue(myTranslations.getValue().toString()); - loadingIcon.setVisible(true); - form.submit(); - } - - public String getUploadFileName() { - String fileName = upload.getFilename(); - - if (fileName.contains("/")) { - return fileName.substring(fileName.lastIndexOf("/") + 1); - } else if (fileName.contains("\\")) { - return fileName.substring(fileName.lastIndexOf("\\") + 1); - } - - return fileName; - } - - public void setDocumentInfo(DocumentInfo docInfo, WorkspaceId workspaceId) { - projectSlug.setValue(workspaceId.getProjectIterationId() - .getProjectSlug()); - versionSlug.setValue(workspaceId.getProjectIterationId() - .getIterationSlug()); - docId.setValue(docInfo.getId().getDocId()); - targetLocale.setValue(workspaceId.getLocaleId().toString()); - } - - @Override - public void hide() { - loadingIcon.setVisible(false); - DOM.setElementProperty(upload.getElement(), "value", ""); - super.hide(); - } - -} diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListDisplay.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListDisplay.java index c614b08c67..50fcd429cc 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListDisplay.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListDisplay.java @@ -34,9 +34,6 @@ import org.zanata.webtrans.shared.model.AuditInfo; import org.zanata.webtrans.shared.model.DocumentId; import org.zanata.webtrans.shared.model.DocumentInfo; -import org.zanata.webtrans.shared.model.WorkspaceId; - -import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent; /** * @@ -76,16 +73,8 @@ interface Listener { void downloadAllFiles(); - void showUploadDialog(DocumentInfo docInfo); - void cancelDownloadAllFiles(); - void cancelFileUpload(); - - void onFileUploadComplete(SubmitCompleteEvent event); - - void onUploadFile(); - void updateDownloadFileProgress(); void sortList(String header, boolean asc); @@ -105,14 +94,6 @@ interface Listener { InlineLink getDownloadAllFilesInlineLink(String url); - void showUploadDialog(DocumentInfo info, WorkspaceId workspaceId); - - void closeFileUpload(); - - String getSelectedUploadFileName(); - - void submitUploadForm(); - void startGetDownloadStatus(int periodMillis); void stopGetDownloadStatus(); diff --git a/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListView.java b/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListView.java index 5e6a486156..529dd8eb87 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListView.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/client/view/DocumentListView.java @@ -31,7 +31,6 @@ import org.zanata.webtrans.client.ui.DocumentListTable.DocValidationStatus; import org.zanata.webtrans.client.ui.DocumentNode; import org.zanata.webtrans.client.ui.DownloadFilesConfirmationBox; -import org.zanata.webtrans.client.ui.FileUploadDialog; import org.zanata.webtrans.client.ui.HasPager; import org.zanata.webtrans.client.ui.InlineLink; import org.zanata.webtrans.client.ui.LoadingPanel; @@ -92,7 +91,6 @@ interface DocumentListViewUiBinder extends HTMLPanel tableWrapper; private final DownloadFilesConfirmationBox confirmationBox; - private final FileUploadDialog fileUploadDialog; private final LoadingPanel loadingPanel; @@ -109,7 +107,6 @@ public DocumentListView(Resources resources, WebTransMessages messages, confirmationBox = new DownloadFilesConfirmationBox(false, messages, resources); - fileUploadDialog = new FileUploadDialog(resources); pager = new Pager(messages); searchField = new SearchField(this); searchField.setTextBoxTitle(messages.docListFilterDescription()); @@ -193,8 +190,6 @@ public void updateFilter(boolean docFilterCaseSensitive, public void setListener(Listener documentListPresenter) { this.listener = documentListPresenter; confirmationBox.registerHandler(listener); - fileUploadDialog.registerHandler(listener, - Application.getUploadFileUrl()); documentListTable.setListener(listener); } @@ -267,27 +262,6 @@ public void setDisabledStyle(String styleName) { }; } - @Override - public void showUploadDialog(DocumentInfo info, WorkspaceId workspaceId) { - fileUploadDialog.setDocumentInfo(info, workspaceId); - fileUploadDialog.center(); - } - - @Override - public void closeFileUpload() { - fileUploadDialog.hide(); - } - - @Override - public String getSelectedUploadFileName() { - return fileUploadDialog.getUploadFileName(); - } - - @Override - public void submitUploadForm() { - fileUploadDialog.submitForm(); - } - @Override public void startGetDownloadStatus(int periodMillis) { timer.scheduleRepeating(periodMillis); diff --git a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentListHandler.java b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentListHandler.java index b72a761c27..252db90588 100644 --- a/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentListHandler.java +++ b/zanata-war/src/main/java/org/zanata/webtrans/server/rpc/GetDocumentListHandler.java @@ -88,7 +88,7 @@ public GetDocumentListResult execute(GetDocumentList action, if (filePersistService.hasPersistedDocument(id)) { String extension = "." - + translationFileServiceImpl.getFileExtension( + + translationFileServiceImpl.getSourceFileExtension( iterationId.getProjectSlug(), iterationId.getIterationSlug(), hDoc.getPath(), hDoc.getName()); diff --git a/zanata-war/src/main/resources/messages.properties b/zanata-war/src/main/resources/messages.properties index 8595583113..21abe44984 100644 --- a/zanata-war/src/main/resources/messages.properties +++ b/zanata-war/src/main/resources/messages.properties @@ -450,6 +450,8 @@ jsf.iteration.CopyTransOpts.tooltip=Help: Set this version's "Copy Translations" jsf.iteration.tooltip.readonly=This version is currently read only jsf.iteration.tooltip.obsolete=This version is currently archived +jsf.iteration.documentType.select=Document type + #------ [home] > Projects > [project-id] > Settings > Languages ! Heading for the section with languages that are not enabled, but can be enabled @@ -692,6 +694,7 @@ jsf.upload.NotLoggedIn=You are not logged in. Open a separate tab or window to l jsf.upload.UploadInProgress=You already have an upload in progress. Wait for the other upload to finish, then try again. Uploads may take up to 5 minutes to finish processing. jsf.upload.ErrorWhileChecking=Got an error while checking if it is ok to upload: {error}. If the error persists, please report it using the "Report a problem" link at the bottom of the page. jsf.upload.UploadedBytesExceedFileSize=Uploaded bytes exceed file size +jsf.upload.selectDocType=Please select a document type #------ [home] > Groups ------ jsf.NoGroups=No groups diff --git a/zanata-war/src/main/webapp-jboss/WEB-INF/web.xml b/zanata-war/src/main/webapp-jboss/WEB-INF/web.xml index 1ec16eb385..58155f3fdb 100644 --- a/zanata-war/src/main/webapp-jboss/WEB-INF/web.xml +++ b/zanata-war/src/main/webapp-jboss/WEB-INF/web.xml @@ -268,17 +268,6 @@ /webtrans/gwteventservice - - - uploadServlet - org.zanata.servlet.FileUploadServlet - - - - uploadServlet - /webtrans/files/upload - - multiUploadServlet diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml index ccf730657d..3802759a40 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/version/documents-tab.xhtml @@ -33,7 +33,8 @@ #{msgs['jsf.iteration.files.UploadNewSourceDocument']} @@ -114,8 +115,9 @@