From edf4ea62596cfc2f4035dc52de93e32b4e5952ea Mon Sep 17 00:00:00 2001 From: Yuji Iwai <59548717+iwai-wovn@users.noreply.github.com> Date: Thu, 14 May 2020 12:11:07 +0900 Subject: [PATCH] Add `ignorePaths` option feature (#153) * Add `ignorePaths` option feature * Bump up version --- README.en.md | 42 ++++++++++++-- pom.xml | 2 +- .../com/github/wovnio/wovnjava/Headers.java | 19 ++++++- .../com/github/wovnio/wovnjava/Settings.java | 27 +++++++++ .../github/wovnio/wovnjava/HeadersTest.java | 56 ++++++++++++++++--- .../github/wovnio/wovnjava/SettingsTest.java | 39 +++++++++++++ 6 files changed, 170 insertions(+), 15 deletions(-) diff --git a/README.en.md b/README.en.md index 2250c510..d9d70061 100644 --- a/README.en.md +++ b/README.en.md @@ -202,7 +202,37 @@ Configure WovnServletFilter to determine request path and query from the same HT _The sample request header shown above was referenced from the following site:_ https://coderwall.com/p/jhkw7w/passing-request-uri-into-request-header -### 2.7. ignoreClasses +### 2.7. ignorePaths + +A comma-separated list of URL path for which you would like WOVN to not translation content withing given directories. + +Ignored paths and their contents will not be processed by WovnServletFilter, and will not be sent to Wovn.io for translation. + +For instance, if you want to not translation the admin directory of your website, you should configure as below. + +```XML + + ignorePaths + /admin,/wp-admin + +``` +With this configuration, WOVN.java will ignore the following URLs +```Text +https://my-wesite.com/admin +https://my-wesite.com/admin/ +https://my-website.com/admin/plugin.html +https://my-wesite.com/wp-admin +https://my-wesite.com/wp-admin/ +https://my-website.com/wp-admin/anypages +``` +but allow the following +```Text +https://my-website.com/index.html +https://my-website.com/user/admin +https://my-website.com/adminpage +``` + +### 2.8. ignoreClasses A comma-separated list of HTML classes for which you would like WOVN to skip the elements of. (This setting is used to prevent confidential data contained in the page to be translated from being sent to WOVN) @@ -229,7 +259,7 @@ Including three classes, `email-address-element`, `my-secret-class`, and `noshow ``` -### 2.8. enableFlushBuffer +### 2.9. enableFlushBuffer A flag to adjust the behavior of `ServletResponse.flushBuffer()`. This parameter is set to `false` by default (recommended). @@ -238,7 +268,7 @@ When `enableFlushBuffer` is set to `false`, WovnServletFilter will capture calls immediately writing content to the client. Only when the complete HTML response is ready will the filter translate the content and send it to the client. This is necessary in order to translate the content properly. -### 2.9. sitePrefixPath +### 2.10. sitePrefixPath This parameter lets you set a prefix path to use as an anchor for which WOVN will translate pages. With this setting, WOVN will only translate pages that match the prefix path, and the path language code will be added _after_ the prefix path. @@ -268,7 +298,7 @@ Furthermore, it is highly recommended to also configure your `web.xml` with a co ``` -### 2.10. langCodeAliases +### 2.11. langCodeAliases This setting lets you specify the language identifier for your supported languages. @@ -303,7 +333,7 @@ To illustrate, here is an example: Achieve this result by configuring `jp` as an alias for Japanese. -### 2.11. customDomainLangs +### 2.12. customDomainLangs This setting lets you define the domain and path that corresponds to each of your supported languages. @@ -344,7 +374,7 @@ If this setting is used, each language declared in `supportedLangs` must be give Lastly, the path declared for your original language must match the structure of the underlying web server. In other words, you cannot use this setting to change the request path of your content in original language. -### 2.12. debugMode +### 2.13. debugMode A flag to enable extra debugging features. diff --git a/pom.xml b/pom.xml index 736568b5..dd3ac5a5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.github.wovnio wovnjava wovnjava - 1.1.3 + 1.2.0 https://github.com/WOVNio/wovnjava diff --git a/src/main/java/com/github/wovnio/wovnjava/Headers.java b/src/main/java/com/github/wovnio/wovnjava/Headers.java index 03d3ba4b..1d2646ae 100644 --- a/src/main/java/com/github/wovnio/wovnjava/Headers.java +++ b/src/main/java/com/github/wovnio/wovnjava/Headers.java @@ -1,5 +1,6 @@ package com.github.wovnio.wovnjava; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.net.URL; import java.net.MalformedURLException; @@ -52,7 +53,7 @@ class Headers { this.shouldRedirectExplicitDefaultLangUrl = this.urlLanguagePatternHandler.shouldRedirectExplicitDefaultLangUrl(clientRequestUrl); - this.isValidRequest = this.requestLang != null && this.urlContext != null; + this.isValidRequest = this.requestLang != null && this.urlContext != null && !this.isIgnoredPath(this.urlContext.getURL().getPath()); } /* @@ -127,4 +128,20 @@ public LinkedHashMap getHreflangUrlMap() { hreflangs.put(hreflangCodeDefaultLang, urlDefaultLang); return hreflangs; } + + private boolean isIgnoredPath(String contextPath) { + ArrayList ignorePaths = this.settings.ignorePaths; + + contextPath = contextPath.toLowerCase(); + + for (String ignorePath : ignorePaths) { + + String prefixPath = ignorePath; + String prefixPathTrailingSlash = ignorePath + "/"; + + if (contextPath.equals(prefixPath) || contextPath.startsWith(prefixPathTrailingSlash)) + return true; + } + return false; + } } diff --git a/src/main/java/com/github/wovnio/wovnjava/Settings.java b/src/main/java/com/github/wovnio/wovnjava/Settings.java index 630d341f..3b5023b4 100644 --- a/src/main/java/com/github/wovnio/wovnjava/Settings.java +++ b/src/main/java/com/github/wovnio/wovnjava/Settings.java @@ -39,6 +39,7 @@ class Settings { public final String originalUrlHeader; public final String originalQueryStringHeader; + public final ArrayList ignorePaths; public final ArrayList ignoreClasses; public final int connectTimeout; @@ -68,6 +69,7 @@ class Settings { this.snippetUrl = this.devMode ? DefaultSnippetUrlDevelopment : DefaultSnippetUrlProduction; this.ignoreClasses = reader.getArrayParameter("ignoreClasses"); + this.ignorePaths = normalizeIgnorePaths(reader.getArrayParameter("ignorePaths")); this.originalUrlHeader = stringOrDefault(reader.getStringParameter("originalUrlHeader"), ""); this.originalQueryStringHeader = stringOrDefault(reader.getStringParameter("originalQueryStringHeader"), ""); @@ -146,6 +148,28 @@ private String normalizeSitePrefixPath(String value) throws ConfigurationError { } } + private ArrayList normalizeIgnorePaths(ArrayList values) { + + ArrayList ignorePaths = new ArrayList(); + + for (String path : values) { + if (path.isEmpty()) { + continue; + } + + path = path.toLowerCase(); + + if (!path.startsWith("/")) path = "/" + path; + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + ignorePaths.add(path); + } + + return ignorePaths; + } + private Map parseLangCodeAliases(String rawLangCodeAliases) throws ConfigurationError { return LanguageAliasSerializer.deserializeFilterConfig(rawLangCodeAliases); } @@ -179,6 +203,9 @@ String hash() throws NoSuchAlgorithmException { for (Lang lang : supportedLangs) { md.update(lang.code.getBytes()); } + for (String path : ignorePaths) { + md.update(path.getBytes()); + } md.update(useProxy ? new byte[]{ 0 } : new byte[] { 1 }); md.update(originalUrlHeader.getBytes()); md.update(originalQueryStringHeader.getBytes()); diff --git a/src/test/java/com/github/wovnio/wovnjava/HeadersTest.java b/src/test/java/com/github/wovnio/wovnjava/HeadersTest.java index da927817..ff485137 100644 --- a/src/test/java/com/github/wovnio/wovnjava/HeadersTest.java +++ b/src/test/java/com/github/wovnio/wovnjava/HeadersTest.java @@ -124,7 +124,7 @@ public void testConvertToDefaultLanguage__QueryPattern() throws ConfigurationErr } public void testConvertToDefaultLanguage__PathPatternWithSitePrefixPath() throws ConfigurationError, MalformedURLException { - Headers h = makeHeaderWithSitePrefixPath("/global/en/foo", "/global/"); + Headers h = createHeaders("/global/en/foo", "/global/", ""); URL url; url = new URL("http://site.com/global/en/"); @@ -221,7 +221,7 @@ public void testLocationWithSubdomain() throws ConfigurationError { } public void testLocationWithSitePrefixPath() throws ConfigurationError { - Headers h = makeHeaderWithSitePrefixPath("/global/ja/foo", "/global/"); + Headers h = createHeaders("/global/ja/foo", "/global/", ""); assertEquals("http://example.com/", h.locationWithLangCode("http://example.com/")); assertEquals("http://example.com/global/ja/", h.locationWithLangCode("http://example.com/global/")); assertEquals("https://example.com/global/ja/", h.locationWithLangCode("https://example.com/global/")); @@ -240,24 +240,66 @@ public void testLocationWithSitePrefixPath() throws ConfigurationError { public void testGetIsValidRequest() throws ConfigurationError { Headers h; - h = makeHeaderWithSitePrefixPath("/", "global"); + h = createHeaders("/", "global", ""); assertEquals(false, h.getIsValidRequest()); - h = makeHeaderWithSitePrefixPath("/global", "global"); + h = createHeaders("/global", "global", ""); assertEquals(true, h.getIsValidRequest()); - h = makeHeaderWithSitePrefixPath("/global/ja/foo", "global"); + h = createHeaders("/global/ja/foo", "global", ""); assertEquals(true, h.getIsValidRequest()); - h = makeHeaderWithSitePrefixPath("/ja/global/foo", "global"); + h = createHeaders("/ja/global/foo", "global", ""); assertEquals(false, h.getIsValidRequest()); } - private Headers makeHeaderWithSitePrefixPath(String requestPath, String sitePrefixPath) throws ConfigurationError { + public void testGetIsValidRequest__withIgnoredPaths() throws ConfigurationError { + Headers h; + + h = createHeaders("/", "", "/admin,/wp-admin"); + assertEquals(true, h.getIsValidRequest()); + + h = createHeaders("/user/admin", "", "/admin,/wp-admin"); + assertEquals(true, h.getIsValidRequest()); + + h = createHeaders("/adminpage", "", "/admin,/wp-admin"); + assertEquals(true, h.getIsValidRequest()); + + h = createHeaders("/admin", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + h = createHeaders("/wp-admin/", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + h = createHeaders("/wp-admin/page", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + h = createHeaders("/ja/admin", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + h = createHeaders("/ja/wp-admin/", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + h = createHeaders("/en/admin", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + h = createHeaders("/en/wp-admin/", "", "/admin,/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + + + h = createHeaders("/city/wp-admin", "city", "/admin,/wp-admin"); + assertEquals(true, h.getIsValidRequest()); + + h = createHeaders("/city/wp-admin", "city", "/city/admin,/city/wp-admin"); + assertEquals(false, h.getIsValidRequest()); + } + + private Headers createHeaders(String requestPath, String sitePrefixPath, String ignorePaths) throws ConfigurationError { HttpServletRequest mockRequest = MockHttpServletRequest.create("https://example.com" + requestPath); HashMap option = new HashMap() {{ put("urlPattern", "path"); put("sitePrefixPath", sitePrefixPath); + put("ignorePaths", ignorePaths); }}; Settings s = TestUtil.makeSettings(option); UrlLanguagePatternHandler ulph = UrlLanguagePatternHandlerFactory.create(s); diff --git a/src/test/java/com/github/wovnio/wovnjava/SettingsTest.java b/src/test/java/com/github/wovnio/wovnjava/SettingsTest.java index 231f3c36..71a5a43e 100644 --- a/src/test/java/com/github/wovnio/wovnjava/SettingsTest.java +++ b/src/test/java/com/github/wovnio/wovnjava/SettingsTest.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.ArrayList; +import java.util.Arrays; import javax.servlet.FilterConfig; @@ -45,6 +46,7 @@ public void testDefaultSettings__MinimalConfiguration__DefaultValuesOK() throws ArrayList emptyArrayList = new ArrayList(); assertEquals(emptyArrayList, s.ignoreClasses); + assertEquals(emptyArrayList, s.ignorePaths); assertEquals(Settings.DefaultSnippetUrlProduction, s.snippetUrl); assertEquals(Settings.DefaultApiUrlProduction, s.apiUrl); @@ -328,6 +330,39 @@ public void testSnippetUrl__DevelopmentMode__DefaultValue() throws Configuration assertEquals(Settings.DefaultSnippetUrlDevelopment, s.snippetUrl); } + public void testIgnorePaths__DeclareEmptyString__UseDefaultEmptyArray() throws ConfigurationError { + FilterConfig config = TestUtil.makeConfigWithValidDefaults(new HashMap() {{ + put("ignorePaths", ""); + }}); + Settings s = new Settings(config); + ArrayList emptyArrayList = new ArrayList(); + assertEquals(emptyArrayList, s.ignorePaths); + } + + public void testIgnorePaths__emptyValues__areExcluded() throws ConfigurationError { + Settings s = createSettings(new HashMap() {{ + put("ignorePaths", ",,,"); + }}); + ArrayList emptyArrayList = new ArrayList(); + assertEquals(emptyArrayList, s.ignorePaths); + } + + public void testIgnorePaths__pathsWithoutLeadingSlash__slashAdded() throws ConfigurationError { + Settings s = createSettings(new HashMap() {{ + put("ignorePaths", ",path1/,path2/,pa/th3/"); + }}); + ArrayList expectedArrayList = new ArrayList(Arrays.asList("/path1", "/path2", "/pa/th3")); + assertEquals(expectedArrayList, s.ignorePaths); + } + + public void testIgnorePaths__pathsWithTrailingSlash__slashRemoved() throws ConfigurationError { + Settings s = createSettings(new HashMap() {{ + put("ignorePaths", ",/path1/,/path2/,/pa/th3/"); + }}); + ArrayList expectedArrayList = new ArrayList(Arrays.asList("/path1", "/path2", "/pa/th3")); + assertEquals(expectedArrayList, s.ignorePaths); + } + public void testIgnoreClasses__DeclareEmptyString__UseDefaultEmptyArray() throws ConfigurationError { FilterConfig config = TestUtil.makeConfigWithValidDefaults(new HashMap() {{ put("ignoreClasses", ""); @@ -438,4 +473,8 @@ public void testReadTimeout__InvalidInteger__ThrowError() throws ConfigurationEr } assertEquals(true, errorThrown); } + + private Settings createSettings(HashMap values) throws ConfigurationError { + return new Settings(TestUtil.makeConfigWithValidDefaults(values)); + } }