diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java b/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java index 94bedb6b66..7102d48c45 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectIterationFilesAction.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.util.List; +import org.hibernate.validator.InvalidStateException; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; @@ -51,6 +52,8 @@ import org.zanata.service.TranslationFileService; import org.zanata.service.TranslationService; +import javax.faces.context.FacesContext; + @Name("projectIterationFilesAction") @Scope(ScopeType.PAGE) public class ProjectIterationFilesAction @@ -125,7 +128,7 @@ public TransUnitWords getTransUnitWordsForDocument(HDocument doc) } @Restrict("#{projectIterationFilesAction.fileUploadAllowed}") - public void uploadTranslationFile() + public String uploadTranslationFile() { TranslationsResource transRes = null; try @@ -156,10 +159,14 @@ public void uploadTranslationFile() { FacesMessages.instance().add(Severity.ERROR, zex.getMessage(), this.translationFileUpload.getFileName()); } + + // NB This needs to be done as for some reason seam is losing the parameters when redirecting + // This is efectively the same as returning void + return FacesContext.getCurrentInstance().getViewRoot().getViewId(); } @Restrict("#{projectIterationFilesAction.documentUploadAllowed}") - public void uploadDocumentFile() + public String uploadDocumentFile() { try { @@ -169,7 +176,7 @@ public void uploadDocumentFile() // TODO Copy Trans values // Extensions are hard-coded to GetText, since it is the only supported format at the time this.documentServiceImpl.saveDocument(this.projectSlug, this.iterationSlug, - this.documentFileUpload.getDocumentPath() + doc.getName(), doc, new StringSet(ExtensionType.GetText.toString()), + doc, new StringSet(ExtensionType.GetText.toString()), false); FacesMessages.instance().add(Severity.INFO, "Document file {0} uploaded.", this.documentFileUpload.getFileName()); @@ -178,6 +185,14 @@ public void uploadDocumentFile() { FacesMessages.instance().add(Severity.ERROR, zex.getMessage(), this.documentFileUpload.getFileName()); } + catch (InvalidStateException isex) + { + FacesMessages.instance().add(Severity.ERROR, "Invalid arguments"); + } + + // NB This needs to be done as for some reason seam is losing the parameters when redirecting + // This is efectively the same as returning void + return FacesContext.getCurrentInstance().getViewRoot().getViewId(); } public boolean isFileUploadAllowed() diff --git a/zanata-war/src/main/java/org/zanata/rest/service/SourceDocResourceService.java b/zanata-war/src/main/java/org/zanata/rest/service/SourceDocResourceService.java index c0dfea0c34..375318e5b7 100644 --- a/zanata-war/src/main/java/org/zanata/rest/service/SourceDocResourceService.java +++ b/zanata-war/src/main/java/org/zanata/rest/service/SourceDocResourceService.java @@ -239,7 +239,7 @@ public Response post(Resource resource, @QueryParam("ext") Set extension // TODO No need for docId param since it's resource.getName() document = this.documentServiceImpl.saveDocument( - this.projectSlug, this.iterationSlug, resource.getName(), resource, extensions, copytrans); + this.projectSlug, this.iterationSlug, resource, extensions, copytrans); EntityTag etag = eTagUtils.generateETagForDocument(hProjectIteration, document.getDocId(), extensions); @@ -350,7 +350,8 @@ public Response putResource(@PathParam("id") String idNoSlash, Resource resource response = Response.ok(); } - document = this.documentServiceImpl.saveDocument(projectSlug, iterationSlug, id, resource, extensions, copytrans); + resource.setName( id ); + document = this.documentServiceImpl.saveDocument(projectSlug, iterationSlug, resource, extensions, copytrans); EntityTag etag = eTagUtils.generateETagForDocument(hProjectIteration, document.getDocId(), extensions); diff --git a/zanata-war/src/main/java/org/zanata/service/DocumentService.java b/zanata-war/src/main/java/org/zanata/service/DocumentService.java index 85c74253d6..dbf12fa141 100644 --- a/zanata-war/src/main/java/org/zanata/service/DocumentService.java +++ b/zanata-war/src/main/java/org/zanata/service/DocumentService.java @@ -35,13 +35,12 @@ public interface DocumentService * * @param projectSlug The document's project id. * @param iterationSlug The document's project iteration id. - * @param docId The document id. - * @param sourceDoc The document contents. + * @param sourceDoc The document to save. (If the document's name matches a docId already stored, it will be overwritten) * @param extensions Document extensions to save. * @param copyTrans Whether to copy translations from other projects or not. A true value does not guarantee that * this will happen, it is only a suggestion. * @return The created / updated document */ - public HDocument saveDocument( String projectSlug, String iterationSlug, String docId, Resource sourceDoc, + public HDocument saveDocument( String projectSlug, String iterationSlug, Resource sourceDoc, Set extensions, boolean copyTrans ); } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java index ee014743b0..f1f953f18b 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/DocumentServiceImpl.java @@ -68,31 +68,14 @@ public class DocumentServiceImpl implements DocumentService private ApplicationConfiguration applicationConfiguration; - public DocumentServiceImpl() - { - } - - public DocumentServiceImpl(ProjectIterationDAO projectIterationDAO, - DocumentDAO documentDAO, - LocaleService localeService, - CopyTransService copyTransService, - ResourceUtils resourceUtils, - ApplicationConfiguration applicationConfiguration) - { - this.projectIterationDAO = projectIterationDAO; - this.documentDAO = documentDAO; - this.localeServiceImpl = localeService; - this.copyTransServiceImpl = copyTransService; - this.resourceUtils = resourceUtils; - this.applicationConfiguration = applicationConfiguration; - } - - public HDocument saveDocument( String projectSlug, String iterationSlug, String docId, Resource sourceDoc, + public HDocument saveDocument( String projectSlug, String iterationSlug, Resource sourceDoc, Set extensions, boolean copyTrans ) { // Only active iterations allow the addition of a document HProjectIteration hProjectIteration = projectIterationDAO.getBySlug(projectSlug, iterationSlug); + String docId = sourceDoc.getName(); + HDocument document = documentDAO.getByDocIdAndIteration(hProjectIteration, docId); HLocale hLocale = this.localeServiceImpl.validateSourceLocale( sourceDoc.getLang() ); 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 969dc00600..d7da8695d0 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 @@ -100,13 +100,15 @@ private Resource parsePotFile( InputStream fileContents, String docPath, String PoReader2 poReader = new PoReader2(); // assume english as source locale Resource res = poReader.extractTemplate(new InputSource(fileContents), new LocaleId("en"), fileName); + // trim extra spaces + docPath = docPath.trim(); // get rid of leading slashes ("/") - if( docPath.startsWith("/") ) + while( docPath.startsWith("/") ) { docPath = docPath.substring(1); } - // Add a trailing slash ("/") if there isn't one - if( !docPath.endsWith("/") ) + // Add a trailing slash ("/") if there isn't one, and there is a need for one + if( docPath.length() > 0 && !docPath.endsWith("/") ) { docPath = docPath.concat("/"); } 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 d3d81158c8..3bfa88a9b1 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 @@ -325,4 +325,14 @@ public interface WebTransMessages extends Messages @DefaultMessage("Warning! This is a public channel") String thisIsAPublicChannel(); + + @DefaultMessage("Only show documents that contain the search text with matching case") + String docListFilterCaseSensitiveDescription(); + + @DefaultMessage("Only show documents with full path and name in the search text") + String docListFilterExactMatchDescription(); + + @DefaultMessage("Enter text to filter the document list. Use commas (,) to separate multiple searches") + String docListFilterDescription(); + } 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 8c97851dd0..d65bc40d6f 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 @@ -79,14 +79,18 @@ interface DocumentListViewUiBinder extends UiBinder(); filterTextBox = new ClearableTextBox(resources, uiMessages); + initWidget(uiBinder.createAndBindUi(this)); + filterTextBox.setTitle(messages.docListFilterDescription()); // TODO set this from the presenter if possible - dataProvider = new ListDataProvider(); + caseSensitiveCheckBox.setTitle(messages.docListFilterCaseSensitiveDescription()); + exactSearchCheckBox.setTitle(messages.docListFilterExactMatchDescription()); - initWidget(uiBinder.createAndBindUi(this)); } @Override diff --git a/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/SearchResultsPresenterTest.java b/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/SearchResultsPresenterTest.java new file mode 100644 index 0000000000..37945e9f02 --- /dev/null +++ b/zanata-war/src/test/java/org/zanata/webtrans/client/presenter/SearchResultsPresenterTest.java @@ -0,0 +1,311 @@ +/* + * 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.webtrans.client.presenter; + +import static org.easymock.EasyMock.and; +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; + +import java.util.ArrayList; + +import net.customware.gwt.presenter.client.EventBus; + +import org.easymock.Capture; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.zanata.webtrans.client.events.TransUnitUpdatedEvent; +import org.zanata.webtrans.client.events.TransUnitUpdatedEventHandler; +import org.zanata.webtrans.client.events.WorkspaceContextUpdateEvent; +import org.zanata.webtrans.client.events.WorkspaceContextUpdateEventHandler; +import org.zanata.webtrans.client.history.History; +import org.zanata.webtrans.client.history.Window; +import org.zanata.webtrans.client.history.Window.Location; +import org.zanata.webtrans.client.keys.KeyShortcut; +import org.zanata.webtrans.client.presenter.SearchResultsPresenter.Display; +import org.zanata.webtrans.client.resources.WebTransMessages; +import org.zanata.webtrans.client.rpc.CachingDispatchAsync; +import org.zanata.webtrans.shared.model.WorkspaceContext; + +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.HasClickHandlers; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.EventHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.event.shared.GwtEvent.Type; +import com.google.gwt.user.client.ui.HasText; +import com.google.gwt.user.client.ui.HasValue; + +/** + * @author David Mason, damason@redhat.com + * + */ +@Test(groups = { "unit-tests" }) +public class SearchResultsPresenterTest +{ + + private static final int TOTAL_KEY_SHORTCUTS = 5; + + private static final String TEST_MESSAGE_NO_TEXT_FLOWS_SELECTED = "No text flows selected"; + private static final String TEST_MESSAGE_SELECT_ALL_TEXT_FLOWS_KEY_SHORTCUT = "Select all text flows"; + private static final String TEST_MESSAGE_FOCUS_SEARCH_PHRASE_KEY_SHORTCUT = "Focus search phrase"; + private static final String TEST_MESSAGE_FOCUS_REPLACEMENT_PHRASE_KEY_SHORTCUT = "Focus replacement phrase"; + private static final String TEST_MESSAGE_REPLACE_SELECTED_KEY_SHORTCUT = "Replace selected text flows"; + private static final String TEST_MESSAGE_TOGGLE_ROW_ACTION_BUTTONS = "Toggle row action buttons"; + + + //object under test + SearchResultsPresenter searchResultsPresenter; + + + ArrayList createdMocks; + Object[] allMocks; + + CachingDispatchAsync mockDispatcher; + Display mockDisplay; + EventBus mockEventBus; + History mockHistory; + KeyShortcutPresenter mockKeyShortcutPresenter; + WebTransMessages mockMessages; + Location mockWindowLocation; + WorkspaceContext mockWorkspaceContext; + + HasClickHandlers mockReplaceAllButton; + HasValue mockReplacementTextBox; + HasValue mockRequirePreviewChk; + HasClickHandlers mockSearchButton; + HasClickHandlers mockSelectAllButton; + HasValue mockSelectAllChk; + HasText mockSelectionInfoLabel; + + + Capture> capturedHistoryValueChangeHandler; + Capture capturedReplaceAllButtonClickHandler; + Capture> capturedReplacementTextBoxValueChangeHandler; + Capture> capturedRequirePreviewChkValueChangeHandler; + Capture capturedSearchButtonClickHandler; + Capture capturedSelectAllButtonClickHandler; + Capture> capturedSelectAllChkValueChangeHandler; + + Capture capturedTransUnitUpdatedEventHandler; + Capture capturedWorkspaceContextUpdatedEventHandler; + private Capture capturedKeyShortcuts; + + + + @BeforeClass + public void createMocks() + { + createAllMocks(); + createAllCaptures(); + } + + @SuppressWarnings("unchecked") + private void createAllMocks() + { + createdMocks = new ArrayList(); + + mockDispatcher = createAndAddMock(CachingDispatchAsync.class); + mockDisplay = createAndAddMock(Display.class); + mockEventBus = createAndAddMock(EventBus.class); + mockHistory = createAndAddMock(History.class); + mockKeyShortcutPresenter = createAndAddMock(KeyShortcutPresenter.class); + mockMessages = createAndAddMock(WebTransMessages.class); + mockWindowLocation = createAndAddMock(Window.Location.class); + mockWorkspaceContext = createAndAddMock(WorkspaceContext.class); + + mockReplaceAllButton = createAndAddMock(HasClickHandlers.class); + mockReplacementTextBox = createAndAddMock(HasValue.class); + mockRequirePreviewChk = createAndAddMock(HasValue.class); + mockSearchButton = createAndAddMock(HasClickHandlers.class); + mockSelectAllButton = createAndAddMock(HasClickHandlers.class); + mockSelectAllChk = createAndAddMock(HasValue.class); + mockSelectionInfoLabel = createAndAddMock(HasText.class); + + allMocks = createdMocks.toArray(); + } + + private void createAllCaptures() + { + capturedHistoryValueChangeHandler = new Capture>(); + capturedReplaceAllButtonClickHandler = new Capture(); + capturedReplacementTextBoxValueChangeHandler = new Capture>(); + capturedRequirePreviewChkValueChangeHandler = new Capture>(); + capturedSearchButtonClickHandler = new Capture(); + capturedSelectAllButtonClickHandler = new Capture(); + capturedSelectAllChkValueChangeHandler = new Capture>(); + + capturedTransUnitUpdatedEventHandler = new Capture(); + capturedWorkspaceContextUpdatedEventHandler = new Capture(); + + capturedKeyShortcuts = new Capture(); + } + + private T createAndAddMock(Class clazz) + { + T mock = createMock(clazz); + createdMocks.add(mock); + return mock; + } + + @BeforeMethod + public void beforeMethod() + { + resetAllMocks(); + resetAllCaptures(); + + setupDefaultMockExpectations(); + + searchResultsPresenter = new SearchResultsPresenter(mockDisplay, mockEventBus, + mockDispatcher, mockHistory, mockMessages, mockWorkspaceContext, + mockKeyShortcutPresenter, mockWindowLocation); + + } + + public void testExpectedActionsOnBind() + { + replayAllMocks(); + searchResultsPresenter.bind(); + verifyAllMocks(); + } + + private void setupDefaultMockExpectations() + { + boolean workspaceIsReadOnly = false; + + expectUiMessages(); + + expect(mockWorkspaceContext.isReadOnly()).andReturn(workspaceIsReadOnly).anyTimes(); + + expectDisplayComponentGetters(); + + mockSelectionInfoLabel.setText(TEST_MESSAGE_NO_TEXT_FLOWS_SELECTED); + expectLastCall().once(); + + mockDisplay.setReplaceAllButtonEnabled(false); + expectLastCall().once(); + + mockDisplay.setReplaceAllButtonVisible(!workspaceIsReadOnly); + expectLastCall().once(); + + expectHandlerRegistrations(); + + expect(mockKeyShortcutPresenter.registerKeyShortcut(capture(capturedKeyShortcuts))).andReturn(createMock(HandlerRegistration.class)).times(TOTAL_KEY_SHORTCUTS); + } + + private void expectUiMessages() + { + expect(mockMessages.noTextFlowsSelected()).andReturn(TEST_MESSAGE_NO_TEXT_FLOWS_SELECTED).anyTimes(); + expect(mockMessages.selectAllTextFlowsKeyShortcut()).andReturn(TEST_MESSAGE_SELECT_ALL_TEXT_FLOWS_KEY_SHORTCUT).anyTimes(); + expect(mockMessages.focusSearchPhraseKeyShortcut()).andReturn(TEST_MESSAGE_FOCUS_SEARCH_PHRASE_KEY_SHORTCUT).anyTimes(); + expect(mockMessages.focusReplacementPhraseKeyShortcut()).andReturn(TEST_MESSAGE_FOCUS_REPLACEMENT_PHRASE_KEY_SHORTCUT).anyTimes(); + expect(mockMessages.replaceSelectedKeyShortcut()).andReturn(TEST_MESSAGE_REPLACE_SELECTED_KEY_SHORTCUT).anyTimes(); + expect(mockMessages.toggleRowActionButtons()).andReturn(TEST_MESSAGE_TOGGLE_ROW_ACTION_BUTTONS).anyTimes(); + } + + private void expectDisplayComponentGetters() + { + expect(mockDisplay.getSelectionInfoLabel()).andReturn(mockSelectionInfoLabel).anyTimes(); + expect(mockDisplay.getSearchButton()).andReturn(mockSearchButton).anyTimes(); + expect(mockDisplay.getReplacementTextBox()).andReturn(mockReplacementTextBox).anyTimes(); + expect(mockDisplay.getSelectAllChk()).andReturn(mockSelectAllChk).anyTimes(); + expect(mockDisplay.getRequirePreviewChk()).andReturn(mockRequirePreviewChk).anyTimes(); + expect(mockDisplay.getReplaceAllButton()).andReturn(mockReplaceAllButton).anyTimes(); + expect(mockDisplay.getSelectAllButton()).andReturn(mockSelectAllButton).anyTimes(); + } + + private void expectHandlerRegistrations() + { + expectClickHandlerRegistration(mockSearchButton, capturedSearchButtonClickHandler); + expectValueChangeHandlerRegistration(mockReplacementTextBox, capturedReplacementTextBoxValueChangeHandler); + expectValueChangeHandlerRegistration(mockSelectAllChk, capturedSelectAllChkValueChangeHandler); + expectValueChangeHandlerRegistration(mockRequirePreviewChk, capturedRequirePreviewChkValueChangeHandler); + expectClickHandlerRegistration(mockReplaceAllButton, capturedReplaceAllButtonClickHandler); + expectClickHandlerRegistration(mockSelectAllButton, capturedSelectAllButtonClickHandler); + + expect(mockHistory.addValueChangeHandler(capture(capturedHistoryValueChangeHandler))).andReturn(createMock(HandlerRegistration.class)).once(); + + expectEventHandlerRegistration(TransUnitUpdatedEvent.getType(), TransUnitUpdatedEventHandler.class, capturedTransUnitUpdatedEventHandler); + expectEventHandlerRegistration(WorkspaceContextUpdateEvent.getType(), WorkspaceContextUpdateEventHandler.class, capturedWorkspaceContextUpdatedEventHandler); + } + + // copied from AppPresenterTest + /** + * Expect a single handler registration on a mock object, and capture the + * click handler in the given {@link Capture} + * + * @param mockObjectToClick + * @param captureForHandler + */ + private void expectClickHandlerRegistration(HasClickHandlers mockObjectToClick, Capture captureForHandler) + { + expect(mockObjectToClick.addClickHandler(and(capture(captureForHandler), isA(ClickHandler.class)))).andReturn(createMock(HandlerRegistration.class)).once(); + } + + private void expectValueChangeHandlerRegistration(HasValue mockObjectWithValue, Capture> captureForHandler) + { + expect(mockObjectWithValue.addValueChangeHandler(capture(captureForHandler))).andReturn(createMock(HandlerRegistration.class)).once(); + } + + //copied from AppPresenterTest + private void expectEventHandlerRegistration(Type expectedType, Class expectedClass, Capture handlerCapture) + { + expect(mockEventBus.addHandler(eq(expectedType), and(capture(handlerCapture), isA(expectedClass)))).andReturn(createMock(HandlerRegistration.class)).once(); + } + + private void resetAllCaptures() + { + capturedHistoryValueChangeHandler.reset(); + capturedReplaceAllButtonClickHandler.reset(); + capturedReplacementTextBoxValueChangeHandler.reset(); + capturedRequirePreviewChkValueChangeHandler.reset(); + capturedSearchButtonClickHandler.reset(); + capturedSelectAllButtonClickHandler.reset(); + capturedSelectAllChkValueChangeHandler.reset(); + + capturedTransUnitUpdatedEventHandler.reset(); + capturedWorkspaceContextUpdatedEventHandler.reset(); + + capturedKeyShortcuts.reset(); + } + + private void resetAllMocks() + { + reset(allMocks); + } + + private void replayAllMocks() + { + replay(allMocks); + } + + private void verifyAllMocks() + { + verify(allMocks); + } +}