diff --git a/src/androidTest/java/com/owncloud/android/AbstractIT.java b/src/androidTest/java/com/owncloud/android/AbstractIT.java index c5138d9cb7..910c2f708f 100644 --- a/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -18,14 +18,22 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import static junit.framework.TestCase.assertTrue; + /** * Common base for all integration tests */ @RunWith(AndroidJUnit4.class) public abstract class AbstractIT { + private static final int BUFFER_SIZE = 1024; + protected static OwnCloudClient client; protected static Context context; @@ -43,6 +51,43 @@ public static void beforeAll() { client.setUserId(loginName); // for test same as userId } + public String createFile(String name) throws IOException { + File tempDir = context.getFilesDir(); + + File file = new File(tempDir + File.separator + name); + file.createNewFile(); + + assertTrue(file.exists()); + + return file.getAbsolutePath(); + } + + /** + * Extracts file from AssetManager to cache folder. + * + * @param fileName Name of the asset file to extract. + * @param context Android context to access assets and file system. + * @return File instance of the extracted file. + */ + public static File extractAsset(String fileName, Context context) throws IOException { + File extractedFile = new File(context.getCacheDir() + File.separator + fileName); + if (!extractedFile.exists()) { + InputStream in = null; + FileOutputStream out = null; + in = context.getAssets().open(fileName); + out = new FileOutputStream(extractedFile); + byte[] buffer = new byte[BUFFER_SIZE]; + int readCount; + while ((readCount = in.read(buffer)) != -1) { + out.write(buffer, 0, readCount); + } + out.flush(); + out.close(); + in.close(); + } + return extractedFile; + } + @After public void after() { ArrayList list = new ReadFolderRemoteOperation("/").execute(client).getData(); diff --git a/src/androidTest/java/com/owncloud/android/ExceptionParserIT.java b/src/androidTest/java/com/owncloud/android/ExceptionParserIT.java index 977a16c4a8..7a3c4b29a8 100644 --- a/src/androidTest/java/com/owncloud/android/ExceptionParserIT.java +++ b/src/androidTest/java/com/owncloud/android/ExceptionParserIT.java @@ -4,20 +4,16 @@ import org.junit.Assert; import org.junit.Test; -import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import androidx.test.ext.junit.runners.AndroidJUnit4; - /** * Created by tobi on 3/21/18. */ -@RunWith(AndroidJUnit4.class) public class ExceptionParserIT { @Test diff --git a/src/androidTest/java/com/owncloud/android/FileIT.java b/src/androidTest/java/com/owncloud/android/FileIT.java index 2e68da419c..22937b886c 100644 --- a/src/androidTest/java/com/owncloud/android/FileIT.java +++ b/src/androidTest/java/com/owncloud/android/FileIT.java @@ -7,9 +7,6 @@ import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation; import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.ext.junit.runners.AndroidJUnit4; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -17,7 +14,6 @@ /** * Tests related to file operations */ -@RunWith(AndroidJUnit4.class) public class FileIT extends AbstractIT { @Test public void testCreateFolderSuccess() { diff --git a/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationTest.java b/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationTest.java index 95c62f26ec..6a0cb09827 100644 --- a/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationTest.java +++ b/src/androidTest/java/com/owncloud/android/lib/resources/files/SearchRemoteOperationTest.java @@ -32,6 +32,8 @@ import org.junit.Test; +import java.io.IOException; + import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; @@ -66,4 +68,324 @@ public void testSearchByFileIdSuccess() { assertEquals(1, result.getData().size()); assertEquals("/test/", ((RemoteFile) result.getData().get(0)).getRemotePath()); } + + @Test + public void testFileSearchEmpty() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", "123") + .execute(client).isSuccess()); + } + + SearchRemoteOperation sut = new SearchRemoteOperation("123123", + SearchRemoteOperation.SearchType.FILE_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(0, result.getData().size()); + } + + @Test + public void testFileSearchEverything() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", "123") + .execute(client).isSuccess()); + } + + SearchRemoteOperation sut = new SearchRemoteOperation("", SearchRemoteOperation.SearchType.FILE_SEARCH, false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(10, result.getData().size()); + } + + @Test + public void testFileSearchSuccess() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", "123") + .execute(client).isSuccess()); + } + + SearchRemoteOperation sut = new SearchRemoteOperation("image5", + SearchRemoteOperation.SearchType.FILE_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertEquals(1, result.getData().size()); + RemoteFile remoteFile = (RemoteFile) result.getData().get(0); + assertEquals("/image5.jpg", remoteFile.getRemotePath()); + } + + @Test + public void noFavorites() { + SearchRemoteOperation sut = new SearchRemoteOperation("", + SearchRemoteOperation.SearchType.FAVORITE_SEARCH, + false); + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertTrue(result.getData().isEmpty()); + } + + @Test + public void oneFavorite() { + String path = "/testFolder/"; + + // create folder, make it favorite + new CreateFolderRemoteOperation(path, true).execute(client); + assertTrue(new ToggleFavoriteRemoteOperation(true, path).execute(client).isSuccess()); + + SearchRemoteOperation sut = new SearchRemoteOperation("", + SearchRemoteOperation.SearchType.FAVORITE_SEARCH, + false); + RemoteOperationResult result = sut.execute(client); + + // test + assertTrue(result.isSuccess()); + assertEquals(1, result.getData().size()); + RemoteFile remoteFile = (RemoteFile) result.getData().get(0); + assertEquals(path, remoteFile.getRemotePath()); + } + + @Test + public void testContentTypeSearch() throws IOException { + String imagePath = createFile("image"); + assertTrue(new UploadFileRemoteOperation(imagePath, "/image.jpg", "image/jpg", "123") + .execute(client).isSuccess()); + + String filePath = createFile("pdf"); + assertTrue(new UploadFileRemoteOperation(filePath, "/pdf.pdf", "application/pdf", "123") + .execute(client).isSuccess()); + + SearchRemoteOperation sut = new SearchRemoteOperation("application/pdf", + SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(1, result.getData().size()); + } + + /** + * shows just all files, but sorted by date + */ + @Test + public void testRecentlyModifiedSearch() throws IOException { + long now = System.currentTimeMillis() / 1000; + String filePath = createFile("image"); + assertTrue(new UploadFileRemoteOperation(filePath, "/image.jpg", "image/jpg", String.valueOf(now - 50)) + .execute(client).isSuccess()); + + String videoPath = createFile("video"); + assertTrue(new UploadFileRemoteOperation(videoPath, "/video.mp4", "video/mpeg", String.valueOf(now - 10)) + .execute(client).isSuccess()); + + String pdfPath = createFile("pdf"); + assertTrue(new UploadFileRemoteOperation(pdfPath, "/pdf.pdf", "application/pdf", String.valueOf(now - 30)) + .execute(client).isSuccess()); + + String oldPath = createFile("pdf"); + assertTrue(new UploadFileRemoteOperation(oldPath, "/old.pdf", "application/pdf", "1") + .execute(client).isSuccess()); + + SearchRemoteOperation sut = new SearchRemoteOperation("", + SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(3, result.getData().size()); + + assertEquals("/video.mp4", ((RemoteFile) result.getData().get(0)).getRemotePath()); + assertEquals("/pdf.pdf", ((RemoteFile) result.getData().get(1)).getRemotePath()); + assertEquals("/image.jpg", ((RemoteFile) result.getData().get(2)).getRemotePath()); + } + + @Test + public void testVideoSearch() throws IOException { + String filePath = createFile("image"); + String remotePath = "/image.jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", "123") + .execute(client).isSuccess()); + + String videoPath = createFile("video"); + assertTrue(new UploadFileRemoteOperation(videoPath, "/video.mp4", "video/mpeg", "123") + .execute(client).isSuccess()); + + String pdfPath = createFile("pdf"); + assertTrue(new UploadFileRemoteOperation(pdfPath, "/pdf.pdf", "application/pdf", "123") + .execute(client).isSuccess()); + + SearchRemoteOperation sut = new SearchRemoteOperation("video/%", + SearchRemoteOperation.SearchType.CONTENT_TYPE_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(1, result.getData().size()); + } + + @Test + public void testPhotoSearchNoFiles() { + SearchRemoteOperation sut = new SearchRemoteOperation("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertTrue(result.getData().isEmpty()); + } + + @Test + public void testPhotoSearch() throws IOException { + String imagePath = createFile("image"); + assertTrue(new UploadFileRemoteOperation(imagePath, "/image.jpg", "image/jpg", "123") + .execute(client).isSuccess()); + + String filePath = createFile("pdf"); + assertTrue(new UploadFileRemoteOperation(filePath, "/pdf.pdf", "application/pdf", "123") + .execute(client).isSuccess()); + + SearchRemoteOperation sut = new SearchRemoteOperation("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(1, result.getData().size()); + } + + @Test + public void testPhotoSearchLimit() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, + remotePath, + "image/jpg", + String.valueOf(100000 + i * 10000)) + .execute(client).isSuccess()); + } + + // get all + SearchRemoteOperation sut = new SearchRemoteOperation("image/%", + SearchRemoteOperation.SearchType.PHOTO_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(10, result.getData().size()); + + // limit to 5 + sut.setLimit(5); + + result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(5, result.getData().size()); + } + + @Test + public void testPhotoSearchTimestamps() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", String.valueOf(i)) + .execute(client).isSuccess()); + } + + SearchRemoteOperation sut = new SearchRemoteOperation("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH, + false); + + // get all + RemoteOperationResult result = sut.execute(client); + assertEquals(10, result.getData().size()); + + // limit to timestamp 5 + sut.setTimestamp(5); + + result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(5, result.getData().size()); + } + + @Test + public void testPhotoSearchLimitAndTimestamp() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, + remotePath, + "image/jpg", + String.valueOf(100000 + i * 10000)) + .execute(client).isSuccess()); + } + + // get all + SearchRemoteOperation sut = new SearchRemoteOperation("image/%", + SearchRemoteOperation.SearchType.PHOTO_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(10, result.getData().size()); + + // limit to 5 + sut.setLimit(5); + sut.setTimestamp(120000); + + result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(2, result.getData().size()); + } + + @Test + public void testGallerySearch() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", "123") + .execute(client).isSuccess()); + } + String videoPath = createFile("video"); + assertTrue(new UploadFileRemoteOperation(videoPath, "/video.mp4", "video/mpeg", "123") + .execute(client).isSuccess()); + + String filePath = createFile("pdf"); + assertTrue(new UploadFileRemoteOperation(filePath, "/pdf.pdf", "application/pdf", "123") + .execute(client).isSuccess()); + + SearchRemoteOperation sut = new SearchRemoteOperation("image/%", SearchRemoteOperation.SearchType.GALLERY_SEARCH, + false); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(11, result.getData().size()); + } + + @Test + public void showOnlyFolders() throws IOException { + for (int i = 0; i < 10; i++) { + String filePath = createFile("image" + i); + String remotePath = "/image" + i + ".jpg"; + assertTrue(new UploadFileRemoteOperation(filePath, remotePath, "image/jpg", "123") + .execute(client).isSuccess()); + } + + SearchRemoteOperation sut = new SearchRemoteOperation("", SearchRemoteOperation.SearchType.FILE_SEARCH, + true); + + RemoteOperationResult result = sut.execute(client); + assertTrue(result.isSuccess()); + assertTrue(result.getData().isEmpty()); + + assertTrue(new CreateFolderRemoteOperation("/folder/", false).execute(client).isSuccess()); + + result = sut.execute(client); + assertTrue(result.isSuccess()); + assertEquals(1, result.getData().size()); + assertEquals("/folder/", ((RemoteFile) result.getData().get(0)).getRemotePath()); + } } diff --git a/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java b/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java new file mode 100644 index 0000000000..d4c159eba7 --- /dev/null +++ b/src/main/java/com/owncloud/android/lib/resources/files/NcSearchMethod.java @@ -0,0 +1,306 @@ +/* Nextcloud Android Library is available under MIT license + * + * @author Tobias Kaminsky + * Copyright (C) 2019 Tobias Kaminsky + * Copyright (C) 2019 Nextcloud GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.resources.files; + +import com.owncloud.android.lib.common.network.WebdavEntry; + +import org.apache.jackrabbit.webdav.search.SearchInfo; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Text; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +public class NcSearchMethod extends org.apache.jackrabbit.webdav.client.methods.SearchMethod { + private static final String HEADER_CONTENT_TYPE_VALUE = "text/xml"; + private static final String DAV_NAMESPACE = "DAV:"; + + private SearchRemoteOperation.SearchType searchType; + private long timestamp; + private int limit; + private boolean filterOutFiles; + private String userId; + + public NcSearchMethod(String uri, SearchInfo searchInfo, + SearchRemoteOperation.SearchType searchType, String userId, long timestamp, + int limit, boolean filterOutFiles) throws IOException { + super(uri, searchInfo); + this.searchType = searchType; + this.userId = userId; + this.limit = limit; + this.filterOutFiles = filterOutFiles; + this.timestamp = timestamp; + + setRequestHeader(HEADER_CONTENT_TYPE, HEADER_CONTENT_TYPE_VALUE); + setRequestBody(createQuery(searchInfo.getQuery())); + } + + private Document createQuery(String searchQuery) { + String internalSearchString = searchQuery; + + if (searchType == SearchRemoteOperation.SearchType.FAVORITE_SEARCH) { + internalSearchString = "yes"; + } + + Document query; + try { + query = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + } catch (ParserConfigurationException parserError) { + System.err.println("ParserConfigurationException: " + parserError.getLocalizedMessage()); + return null; + } + + // Create Nodes & Elements + Element searchRequestElement = query.createElementNS(DAV_NAMESPACE, "d:searchrequest"); + Element basicSearchElement = query.createElementNS(DAV_NAMESPACE, "d:basicsearch"); + Element selectElement = query.createElementNS(DAV_NAMESPACE, "d:select"); + Element selectPropsElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); + // get all + Element displayNameElement = query.createElementNS(DAV_NAMESPACE, "d:displayname"); + Element contentTypeElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); + Element resourceTypeElement = query.createElementNS(DAV_NAMESPACE, "d:resourcetype"); + Element contentLengthElement = query.createElementNS(DAV_NAMESPACE, "d:getcontentlength"); + Element lastModifiedElement = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); + Element creationDate = query.createElementNS(DAV_NAMESPACE, "d:creationdate"); + Element etagElement = query.createElementNS(DAV_NAMESPACE, "d:getetag"); + Element quotaUsedElement = query.createElementNS(DAV_NAMESPACE, "d:quota-used-bytes"); + Element quotaAvailableElement = query.createElementNS(DAV_NAMESPACE, "d:quota-available-bytes"); + Element permissionsElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:permissions"); + Element remoteIdElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:id"); + Element sizeElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:size"); + Element favoriteElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:favorite"); + + selectPropsElement.appendChild(displayNameElement); + selectPropsElement.appendChild(contentTypeElement); + selectPropsElement.appendChild(resourceTypeElement); + selectPropsElement.appendChild(contentLengthElement); + selectPropsElement.appendChild(lastModifiedElement); + selectPropsElement.appendChild(creationDate); + selectPropsElement.appendChild(etagElement); + selectPropsElement.appendChild(quotaUsedElement); + selectPropsElement.appendChild(quotaAvailableElement); + selectPropsElement.appendChild(permissionsElement); + selectPropsElement.appendChild(remoteIdElement); + selectPropsElement.appendChild(sizeElement); + selectPropsElement.appendChild(favoriteElement); + + Element fromElement = query.createElementNS(DAV_NAMESPACE, "d:from"); + Element scopeElement = query.createElementNS(DAV_NAMESPACE, "d:scope"); + Element hrefElement = query.createElementNS(DAV_NAMESPACE, "d:href"); + Element depthElement = query.createElementNS(DAV_NAMESPACE, "d:depth"); + Text hrefTextElement = query.createTextNode("/files/" + userId); + Text depthTextElement = query.createTextNode("infinity"); + Element whereElement = query.createElementNS(DAV_NAMESPACE, "d:where"); + Element folderElement; + Element equalsElement; + + switch (searchType) { + case FAVORITE_SEARCH: + case FILE_ID_SEARCH: + equalsElement = query.createElementNS(DAV_NAMESPACE, "d:eq"); + break; + + case RECENTLY_MODIFIED_SEARCH: + equalsElement = query.createElementNS(DAV_NAMESPACE, "d:gt"); + break; + + case GALLERY_SEARCH: + equalsElement = query.createElementNS(DAV_NAMESPACE, "d:or"); + break; + + default: + equalsElement = query.createElementNS(DAV_NAMESPACE, "d:like"); + break; + } + + Element propElement = null; + Element queryElement = null; + Element literalElement = null; + Text literalTextElement = null; + Element imageLikeElement = null; + Element videoLikeElement = null; + + if (searchType != SearchRemoteOperation.SearchType.GALLERY_SEARCH) { + propElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); + + switch (searchType) { + case CONTENT_TYPE_SEARCH: + case PHOTO_SEARCH: + queryElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); + break; + + case FILE_SEARCH: + queryElement = query.createElementNS(DAV_NAMESPACE, "d:displayname"); + break; + + case FAVORITE_SEARCH: + queryElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:favorite"); + break; + + case RECENTLY_MODIFIED_SEARCH: + queryElement = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); + break; + + case FILE_ID_SEARCH: + queryElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:fileid"); + break; + + default: + // no default + break; + } + + literalElement = query.createElementNS(DAV_NAMESPACE, "d:literal"); + if (searchType != SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH) { + if (searchType == SearchRemoteOperation.SearchType.FILE_SEARCH) { + internalSearchString = "%" + internalSearchString + "%"; + } + literalTextElement = query.createTextNode(internalSearchString); + } else { + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()); + dateFormat.setTimeZone(TimeZone.getDefault()); + Date date = new Date(); + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.DAY_OF_YEAR, -7); + date = calendar.getTime(); + + String formattedDateString = dateFormat.format(date); + literalTextElement = query.createTextNode(formattedDateString); + } + } else { + imageLikeElement = query.createElementNS(DAV_NAMESPACE, "d:like"); + Element imagePropElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); + Element imageQueryElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); + Element imageLiteralElement = query.createElementNS(DAV_NAMESPACE, "d:literal"); + Text imageLiteralTextElement = query.createTextNode("image/%"); + videoLikeElement = query.createElementNS(DAV_NAMESPACE, "d:like"); + Element videoPropElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); + Element videoQueryElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); + Element videoLiteralElement = query.createElementNS(DAV_NAMESPACE, "d:literal"); + Text videoLiteralTextElement = query.createTextNode("video/%"); + + videoLiteralElement.appendChild(videoLiteralTextElement); + imageLiteralElement.appendChild(imageLiteralTextElement); + + videoPropElement.appendChild(videoQueryElement); + videoLikeElement.appendChild(videoPropElement); + videoLikeElement.appendChild(videoLiteralElement); + + + imagePropElement.appendChild(imageQueryElement); + imageLikeElement.appendChild(imagePropElement); + imageLikeElement.appendChild(imageLiteralElement); + + } + + Element orderByElement = query.createElementNS(DAV_NAMESPACE, "d:orderby"); + + if (searchType == SearchRemoteOperation.SearchType.PHOTO_SEARCH || searchType == SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH) { + Element orderElement = query.createElementNS(DAV_NAMESPACE, "d:order"); + orderByElement.appendChild(orderElement); + Element orderPropElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); + orderElement.appendChild(orderPropElement); + Element orderPropElementValue = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); + orderPropElement.appendChild(orderPropElementValue); + Element orderAscDescElement = query.createElementNS(DAV_NAMESPACE, "d:descending"); + orderElement.appendChild(orderAscDescElement); + } + + // Build XML tree + searchRequestElement.setAttribute("xmlns:oc", "http://nextcloud.com/ns"); + query.appendChild(searchRequestElement); + searchRequestElement.appendChild(basicSearchElement); + basicSearchElement.appendChild(selectElement); + basicSearchElement.appendChild(fromElement); + basicSearchElement.appendChild(whereElement); + selectElement.appendChild(selectPropsElement); + fromElement.appendChild(scopeElement); + scopeElement.appendChild(hrefElement); + scopeElement.appendChild(depthElement); + hrefElement.appendChild(hrefTextElement); + depthElement.appendChild(depthTextElement); + + if (filterOutFiles) { + Element andElement = query.createElementNS(DAV_NAMESPACE, "d:and"); + folderElement = query.createElementNS(DAV_NAMESPACE, "d:is-collection"); + andElement.appendChild(folderElement); + andElement.appendChild(equalsElement); + whereElement.appendChild(andElement); + } else if (timestamp != -1) { + Element and = query.createElementNS(DAV_NAMESPACE, "d:and"); + Element lessThan = query.createElementNS(DAV_NAMESPACE, "d:lt"); + Element lastModified = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); + Element literal = query.createElementNS(DAV_NAMESPACE, "d:literal"); + Element prop = query.createElementNS(DAV_NAMESPACE, "d:prop"); + prop.appendChild(lastModified); + literal.setTextContent(String.valueOf(timestamp)); + + lessThan.appendChild(prop); + lessThan.appendChild(literal); + + and.appendChild(lessThan); + and.appendChild(equalsElement); + whereElement.appendChild(and); + } else { + whereElement.appendChild(equalsElement); + } + + if (searchType == SearchRemoteOperation.SearchType.GALLERY_SEARCH) { + equalsElement.appendChild(imageLikeElement); + equalsElement.appendChild(videoLikeElement); + } else { + equalsElement.appendChild(propElement); + equalsElement.appendChild(literalElement); + if (queryElement != null) { + propElement.appendChild(queryElement); + } + literalElement.appendChild(literalTextElement); + } + basicSearchElement.appendChild(orderByElement); + + if (limit > 0) { + Element limitElement = query.createElementNS(DAV_NAMESPACE, "d:limit"); + Element nResultElement = query.createElementNS(DAV_NAMESPACE, "d:nresults"); + nResultElement.appendChild(query.createTextNode(String.valueOf(limit))); + limitElement.appendChild(nResultElement); + basicSearchElement.appendChild(limitElement); + } + + return query; + } +} diff --git a/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java b/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java index 8354189d26..3de9c18740 100644 --- a/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java +++ b/src/main/java/com/owncloud/android/lib/resources/files/SearchRemoteOperation.java @@ -28,7 +28,6 @@ package com.owncloud.android.lib.resources.files; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.network.WebdavEntry; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.WebDavFileUtils; @@ -38,56 +37,49 @@ import org.apache.jackrabbit.webdav.client.methods.OptionsMethod; import org.apache.jackrabbit.webdav.search.SearchInfo; import org.apache.jackrabbit.webdav.xml.Namespace; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Text; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; /** * Remote operation performing the search in the Nextcloud server. */ public class SearchRemoteOperation extends RemoteOperation { - private static final String HEADER_CONTENT_TYPE_VALUE = "text/xml"; - - private static final String DAV_NAMESPACE = "DAV:"; public enum SearchType { - FILE_SEARCH, - FAVORITE_SEARCH, - CONTENT_TYPE_SEARCH, - RECENTLY_MODIFIED_SEARCH, - RECENTLY_ADDED_SEARCH, - SHARED_SEARCH, - GALLERY_SEARCH, - FILE_ID_SEARCH + FILE_SEARCH, // search by name + FAVORITE_SEARCH, // get all favorited files/folder + CONTENT_TYPE_SEARCH, // search by mimetype + RECENTLY_MODIFIED_SEARCH, // get files/folders that were modified within last 7 days, ordered descending by time + PHOTO_SEARCH, // gets all files with mimetype "image/%" + GALLERY_SEARCH, // combined photo and video + FILE_ID_SEARCH // search one file specified by file id } private String searchQuery; private SearchType searchType; private boolean filterOutFiles; + private int limit; + private long timestamp = -1; public SearchRemoteOperation(String query, SearchType searchType, boolean filterOutFiles) { this.searchQuery = query; this.searchType = searchType; this.filterOutFiles = filterOutFiles; } + + public void setLimit(int limit) { + this.limit = limit; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result; - SearchMethod searchMethod = null; + NcSearchMethod searchMethod = null; OptionsMethod optionsMethod; String webDavUrl = client.getNewWebdavUri().toString(); @@ -96,9 +88,17 @@ protected RemoteOperationResult run(OwnCloudClient client) { try { int optionsStatus = client.executeMethod(optionsMethod); boolean isSearchSupported = optionsMethod.isAllowed("SEARCH"); - + if (isSearchSupported) { - searchMethod = new SearchMethod(webDavUrl, new SearchInfo("NC", Namespace.XMLNS_NAMESPACE, "NC")); + searchMethod = new NcSearchMethod(webDavUrl, + new SearchInfo("NC", + Namespace.XMLNS_NAMESPACE, + searchQuery), + searchType, + getClient().getUserId(), + timestamp, + limit, + filterOutFiles); int status = client.executeMethod(searchMethod); @@ -135,222 +135,9 @@ protected RemoteOperationResult run(OwnCloudClient client) { searchMethod.releaseConnection(); // let the connection available for other methods } - if (optionsMethod != null) { - optionsMethod.releaseConnection(); - } + optionsMethod.releaseConnection(); } return result; } - - private class SearchMethod extends org.apache.jackrabbit.webdav.client.methods.SearchMethod { - - public SearchMethod(String uri, String statement, String language) throws IOException { - super(uri, statement, language); - } - - public SearchMethod(String uri, String statement, String language, Namespace languageNamespace) throws IOException { - super(uri, statement, language, languageNamespace); - } - - public SearchMethod(String uri, SearchInfo searchInfo) throws IOException { - super(uri, searchInfo); - setRequestHeader(HEADER_CONTENT_TYPE, HEADER_CONTENT_TYPE_VALUE); - setRequestBody(createQuery()); - } - - } - - private Document createQuery() { - - String internalSearchString = searchQuery; - - if (searchType == SearchType.FAVORITE_SEARCH) { - internalSearchString = "yes"; - } - - Document query; - try { - query = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - } catch (ParserConfigurationException parserError) { - System.err.println("ParserConfigurationException: " + parserError.getLocalizedMessage()); - return null; - } - - // Create Nodes & Elements - Element searchRequestElement = query.createElementNS(DAV_NAMESPACE, "d:searchrequest"); - Element basicSearchElement = query.createElementNS(DAV_NAMESPACE, "d:basicsearch"); - Element selectElement = query.createElementNS(DAV_NAMESPACE, "d:select"); - Element selectPropsElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); - // get all - Element displayNameElement = query.createElementNS(DAV_NAMESPACE, "d:displayname"); - Element contentTypeElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); - Element resourceTypeElement = query.createElementNS(DAV_NAMESPACE, "d:resourcetype"); - Element contentLengthElement = query.createElementNS(DAV_NAMESPACE, "d:getcontentlength"); - Element lastModifiedElement = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); - Element creationDate = query.createElementNS(DAV_NAMESPACE, "d:creationdate"); - Element etagElement = query.createElementNS(DAV_NAMESPACE, "d:getetag"); - Element quotaUsedElement = query.createElementNS(DAV_NAMESPACE, "d:quota-used-bytes"); - Element quotaAvailableElement = query.createElementNS(DAV_NAMESPACE, "d:quota-available-bytes"); - Element permissionsElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:permissions"); - Element remoteIdElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:id"); - Element sizeElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:size"); - Element favoriteElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:favorite"); - - selectPropsElement.appendChild(displayNameElement); - selectPropsElement.appendChild(contentTypeElement); - selectPropsElement.appendChild(resourceTypeElement); - selectPropsElement.appendChild(contentLengthElement); - selectPropsElement.appendChild(lastModifiedElement); - selectPropsElement.appendChild(creationDate); - selectPropsElement.appendChild(etagElement); - selectPropsElement.appendChild(quotaUsedElement); - selectPropsElement.appendChild(quotaAvailableElement); - selectPropsElement.appendChild(permissionsElement); - selectPropsElement.appendChild(remoteIdElement); - selectPropsElement.appendChild(sizeElement); - selectPropsElement.appendChild(favoriteElement); - - Element fromElement = query.createElementNS(DAV_NAMESPACE, "d:from"); - Element scopeElement = query.createElementNS(DAV_NAMESPACE, "d:scope"); - Element hrefElement = query.createElementNS(DAV_NAMESPACE, "d:href"); - Element depthElement = query.createElementNS(DAV_NAMESPACE, "d:depth"); - Text hrefTextElement = query.createTextNode("/files/" + getClient().getUserId()); - Text depthTextElement = query.createTextNode("infinity"); - Element whereElement = query.createElementNS(DAV_NAMESPACE, "d:where"); - Element folderElement = null; - if (filterOutFiles) { - folderElement = query.createElementNS(DAV_NAMESPACE, "d:is-collection"); - } - Element equalsElement; - if (searchType == SearchType.FAVORITE_SEARCH || searchType == SearchType.FILE_ID_SEARCH) { - equalsElement = query.createElementNS(DAV_NAMESPACE, "d:eq"); - } else if (searchType == SearchType.RECENTLY_MODIFIED_SEARCH || - searchType == SearchType.RECENTLY_ADDED_SEARCH) { - equalsElement = query.createElementNS(DAV_NAMESPACE, "d:gt"); - } else if (searchType == SearchType.GALLERY_SEARCH) { - equalsElement = query.createElementNS(DAV_NAMESPACE, "d:or"); - } else { - equalsElement = query.createElementNS(DAV_NAMESPACE, "d:like"); - } - - Element propElement = null; - Element queryElement = null; - Element literalElement = null; - Text literalTextElement = null; - Element imageLikeElement = null; - Element videoLikeElement = null; - - if (searchType != SearchType.GALLERY_SEARCH) { - propElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); - queryElement = null; - if (searchType == SearchType.CONTENT_TYPE_SEARCH) { - queryElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); - } else if (searchType == SearchType.FILE_SEARCH) { - queryElement = query.createElementNS(DAV_NAMESPACE, "d:displayname"); - } else if (searchType == SearchType.FAVORITE_SEARCH) { - queryElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:favorite"); - } else if (searchType == SearchType.RECENTLY_MODIFIED_SEARCH) { - queryElement = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); - } else if (searchType == SearchType.RECENTLY_ADDED_SEARCH) { - queryElement = query.createElementNS(DAV_NAMESPACE, "d:creationdate"); - } else if (searchType == SearchType.FILE_ID_SEARCH) { - queryElement = query.createElementNS(WebdavEntry.NAMESPACE_OC, "oc:fileid"); - } - literalElement = query.createElementNS(DAV_NAMESPACE, "d:literal"); - if (searchType != SearchType.RECENTLY_MODIFIED_SEARCH && searchType != SearchType.RECENTLY_ADDED_SEARCH) { - if (searchType == SearchType.FILE_SEARCH) { - internalSearchString = "%" + internalSearchString + "%"; - } - literalTextElement = query.createTextNode(internalSearchString); - } else { - DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()); - dateFormat.setTimeZone(TimeZone.getDefault()); - Date date = new Date(); - - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - calendar.add(Calendar.DAY_OF_YEAR, -7); - date = calendar.getTime(); - - String formattedDateString = dateFormat.format(date); - literalTextElement = query.createTextNode(formattedDateString); - } - } else { - imageLikeElement = query.createElementNS(DAV_NAMESPACE, "d:like"); - Element imagePropElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); - Element imageQueryElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); - Element imageLiteralElement = query.createElementNS(DAV_NAMESPACE, "d:literal"); - Text imageLiteralTextElement = query.createTextNode("image/%"); - videoLikeElement = query.createElementNS(DAV_NAMESPACE, "d:like"); - Element videoPropElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); - Element videoQueryElement = query.createElementNS(DAV_NAMESPACE, "d:getcontenttype"); - Element videoLiteralElement = query.createElementNS(DAV_NAMESPACE, "d:literal"); - Text videoLiteralTextElement = query.createTextNode("video/%"); - - videoLiteralElement.appendChild(videoLiteralTextElement); - imageLiteralElement.appendChild(imageLiteralTextElement); - - videoPropElement.appendChild(videoQueryElement); - videoLikeElement.appendChild(videoPropElement); - videoLikeElement.appendChild(videoLiteralElement); - - - imagePropElement.appendChild(imageQueryElement); - imageLikeElement.appendChild(imagePropElement); - imageLikeElement.appendChild(imageLiteralElement); - - } - - Element orderByElement = query.createElementNS(DAV_NAMESPACE, "d:orderby"); - - // Disabling order for now, but will leave the code in place for the future - - /*if (searchType == SearchType.RECENTLY_MODIFIED_SEARCH || searchType == SearchType.FAVORITE_SEARCH) { - Element orderElement = query.createElementNS(DAV_NAMESPACE, "d:order"); - orderByElement.appendChild(orderElement); - Element orderPropElement = query.createElementNS(DAV_NAMESPACE, "d:prop"); - orderElement.appendChild(orderPropElement); - Element orderPropElementValue = query.createElementNS(DAV_NAMESPACE, "d:getlastmodified"); - orderPropElement.appendChild(orderPropElementValue); - Element orderAscDescElement = query.createElementNS(DAV_NAMESPACE, "d:descending"); - orderElement.appendChild(orderAscDescElement); - }*/ - - // Build XML tree - searchRequestElement.setAttribute("xmlns:oc", "http://nextcloud.com/ns"); - query.appendChild(searchRequestElement); - searchRequestElement.appendChild(basicSearchElement); - basicSearchElement.appendChild(selectElement); - basicSearchElement.appendChild(fromElement); - basicSearchElement.appendChild(whereElement); - selectElement.appendChild(selectPropsElement); - fromElement.appendChild(scopeElement); - scopeElement.appendChild(hrefElement); - scopeElement.appendChild(depthElement); - hrefElement.appendChild(hrefTextElement); - depthElement.appendChild(depthTextElement); - if (folderElement != null) { - Element andElement = query.createElementNS(DAV_NAMESPACE, "d:and"); - andElement.appendChild(folderElement); - andElement.appendChild(equalsElement); - whereElement.appendChild(andElement); - } else { - whereElement.appendChild(equalsElement); - } - - if (searchType != SearchType.GALLERY_SEARCH) { - equalsElement.appendChild(propElement); - equalsElement.appendChild(literalElement); - propElement.appendChild(queryElement); - literalElement.appendChild(literalTextElement); - } else { - equalsElement.appendChild(imageLikeElement); - equalsElement.appendChild(videoLikeElement); - } - basicSearchElement.appendChild(orderByElement); - - return query; - } - }