jenkins-bot has submitted this change and it was merged. Change subject: Change way to get title from links ......................................................................
Change way to get title from links For the mobileview/MW API case we can simply use the title attribute instead of decoding the href attribute. For the Parsoid/MCS case newer revisions of pages and once we get all pages re-rendered after Icae4b83278791ed09284bb73c3ba80363cac9202 gets deployed will also have a title attribute. For older pages the title attribute doesn't exists. But we can derive it in JavaScript code from the href attribute by removing a starting "/wiki/" or "./" and running decodeURIComponent on it. That way the JS code always returns a title property, which the Java side can just take as is. The onUrlClick() method gets a new parameter titleString. Instead of using getSite().titleForInternalLink(href) we now create the PageTitle directly using titleString. In the case of the gallery we don't get the link data from native code instead of JS. The parsing of the HTML there is done using Html.fromHtml() which doesn't preserve the title attribute of the link. In that one case the Java code will derive the title from the href attribute like it did before. This case is OK since the gallery data is not coming from Parsoid. Introducing one factory method to create a PageTitle with a potential fragment passed in separately. There was already a constructor with three String parameters. Related: Icae4b83278791ed09284bb73c3ba80363cac9202 Bug: T136223 Change-Id: Iedbe1ae611f4d433d4c6bcac3cce3935333753e0 --- M app/src/main/assets/bundle.js M app/src/main/assets/preview.js M app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java M app/src/main/java/org/wikipedia/editing/EditSectionActivity.java M app/src/main/java/org/wikipedia/page/LinkHandler.java M app/src/main/java/org/wikipedia/page/LinkMovementMethodExt.java M app/src/main/java/org/wikipedia/page/PageFragment.java M app/src/main/java/org/wikipedia/page/PageTitle.java M app/src/main/java/org/wikipedia/page/gallery/GalleryActivity.java M app/src/main/java/org/wikipedia/util/UriUtil.java M app/src/main/java/org/wikipedia/wiktionary/WiktionaryDialog.java A app/src/test/java/org/wikipedia/util/UriUtilTest.java M www/js/actions.js 13 files changed, 155 insertions(+), 18 deletions(-) Approvals: jenkins-bot: Verified Niedzielski: Looks good to me, approved diff --git a/app/src/main/assets/bundle.js b/app/src/main/assets/bundle.js index 2a21e83..f757c6d 100644 --- a/app/src/main/assets/bundle.js +++ b/app/src/main/assets/bundle.js @@ -39,6 +39,19 @@ } } +/** + * Either gets the title from the title attribute (for mobileview case and newer MCS pages) or, + * if that doesn't not exists try to derive it from the href attribute value. + * In the latter case it also unescapes HTML entities to get the correct title string. + */ +function getTitle( sourceNode, href ) { + if (sourceNode.hasAttribute( "title" )) { + return sourceNode.getAttribute( "title" ); + } else { + return href.replace(/^\/wiki\//, '').replace(/^\.\//, '').replace(/#.*$/, ''); + } +} + document.onclick = function() { var sourceNode = null; var curNode = event.target; @@ -69,7 +82,7 @@ } else if (sourceNode.classList.contains( 'image' )) { bridge.sendMessage( 'imageClicked', { "href": href } ); } else { - bridge.sendMessage( 'linkClicked', { "href": href } ); + bridge.sendMessage( 'linkClicked', { "href": href, "title": getTitle(sourceNode, href) } ); } event.preventDefault(); } diff --git a/app/src/main/assets/preview.js b/app/src/main/assets/preview.js index a8c2235..49c7fb1 100644 --- a/app/src/main/assets/preview.js +++ b/app/src/main/assets/preview.js @@ -39,6 +39,19 @@ } } +/** + * Either gets the title from the title attribute (for mobileview case and newer MCS pages) or, + * if that doesn't not exists try to derive it from the href attribute value. + * In the latter case it also unescapes HTML entities to get the correct title string. + */ +function getTitle( sourceNode, href ) { + if (sourceNode.hasAttribute( "title" )) { + return sourceNode.getAttribute( "title" ); + } else { + return href.replace(/^\/wiki\//, '').replace(/^\.\//, '').replace(/#.*$/, ''); + } +} + document.onclick = function() { var sourceNode = null; var curNode = event.target; @@ -69,7 +82,7 @@ } else if (sourceNode.classList.contains( 'image' )) { bridge.sendMessage( 'imageClicked', { "href": href } ); } else { - bridge.sendMessage( 'linkClicked', { "href": href } ); + bridge.sendMessage( 'linkClicked', { "href": href, "title": getTitle(sourceNode, href) } ); } event.preventDefault(); } diff --git a/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java b/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java index 617959c..d01cfe0 100644 --- a/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java +++ b/app/src/main/java/org/wikipedia/editing/EditPreviewFragment.java @@ -6,6 +6,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; import android.util.DisplayMetrics; @@ -27,6 +29,7 @@ import org.wikipedia.page.PageActivity; import org.wikipedia.page.PageTitle; import org.wikipedia.util.L10nUtil; +import org.wikipedia.util.UriUtil; import org.wikipedia.views.ObservableWebView; import org.wikipedia.bridge.CommunicationBridge; import org.wikipedia.editing.summaries.EditSummaryTag; @@ -190,17 +193,17 @@ } @Override - public void onUrlClick(final String href) { + public void onUrlClick(@NonNull final String href, @Nullable final String titleString) { // Check if this is an internal link, and if it is then open it internally if (href.startsWith("/wiki/")) { - PageTitle title = getSite().titleForInternalLink(href); + PageTitle title = PageTitle.withSeparateFragment(titleString, UriUtil.getFragment(href), getSite()); onInternalLinkClicked(title); } else { //Show dialogue asking user to confirm they want to leave showLeavingEditDialogue(new Runnable() { @Override public void run() { - openExternalLink(href); + openExternalLink(href, titleString); } }); } @@ -244,9 +247,10 @@ * of LinkHandler to do the heavy lifting. You can't call this method from inside a * Runnable or an AlertDialog, so we put it in here instead. * @param href The href of the external link to be opened. + * @param titleString the title of the page to be openend as a string */ - private void openExternalLink(String href) { - super.onUrlClick(href); + private void openExternalLink(String href, String titleString) { + super.onUrlClick(href, titleString); } @Override diff --git a/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java b/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java index 1cf3b8d..44b7e3e 100644 --- a/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java +++ b/app/src/main/java/org/wikipedia/editing/EditSectionActivity.java @@ -228,7 +228,7 @@ editLicenseText.setMovementMethod(new LinkMovementMethodExt(new LinkMovementMethodExt.UrlHandler() { @Override - public void onUrlClick(String url) { + public void onUrlClick(@NonNull String url, @Nullable String notUsed) { if (url.equals("https://#login")) { funnel.logLoginAttempt(); Intent loginIntent = LoginActivity.newIntent(EditSectionActivity.this, diff --git a/app/src/main/java/org/wikipedia/page/LinkHandler.java b/app/src/main/java/org/wikipedia/page/LinkHandler.java index 553ed39..44c806f 100644 --- a/app/src/main/java/org/wikipedia/page/LinkHandler.java +++ b/app/src/main/java/org/wikipedia/page/LinkHandler.java @@ -2,12 +2,15 @@ import android.content.Context; import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import org.wikipedia.Site; import org.wikipedia.bridge.CommunicationBridge; +import org.wikipedia.util.UriUtil; import static org.wikipedia.util.UriUtil.decodeURL; import static org.wikipedia.util.UriUtil.handleExternalLink; @@ -37,7 +40,7 @@ public void onMessage(String messageType, JSONObject messagePayload) { try { String href = decodeURL(messagePayload.getString("href")); - onUrlClick(href); + onUrlClick(href, messagePayload.getString("title")); } catch (IllegalArgumentException e) { // The URL is malformed and URL decoder can't understand it. Just do nothing. Log.d("Wikipedia", "A malformed URL was tapped."); @@ -47,14 +50,14 @@ } @Override - public void onUrlClick(String href) { + public void onUrlClick(@NonNull String href, @Nullable String titleString) { if (href.startsWith("//")) { // That's a protocol specific link! Make it https! href = "https:" + href; } Log.d("Wikipedia", "Link clicked was " + href); if (href.startsWith("/wiki/")) { - PageTitle title = getSite().titleForInternalLink(href); + PageTitle title = PageTitle.withSeparateFragment(titleString, UriUtil.getFragment(href), getSite()); onInternalLinkClicked(title); } else if (href.startsWith("#")) { onPageLinkClicked(href.substring(1)); diff --git a/app/src/main/java/org/wikipedia/page/LinkMovementMethodExt.java b/app/src/main/java/org/wikipedia/page/LinkMovementMethodExt.java index af59dc5..8b451c1 100644 --- a/app/src/main/java/org/wikipedia/page/LinkMovementMethodExt.java +++ b/app/src/main/java/org/wikipedia/page/LinkMovementMethodExt.java @@ -1,6 +1,9 @@ package org.wikipedia.page; +import org.wikipedia.util.UriUtil; + import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.text.Layout; import android.text.Spannable; import android.text.method.LinkMovementMethod; @@ -32,7 +35,7 @@ final URLSpan[] links = buffer.getSpans(off, off, URLSpan.class); if (links.length != 0) { String url = decodeURL(links[0].getURL()); - handler.onUrlClick(url); + handler.onUrlClick(url, UriUtil.getTitleFromUrl(url)); return true; } } @@ -41,6 +44,6 @@ public interface UrlHandler { - void onUrlClick(String url); + void onUrlClick(@NonNull String url, @Nullable String titleString); } } \ No newline at end of file diff --git a/app/src/main/java/org/wikipedia/page/PageFragment.java b/app/src/main/java/org/wikipedia/page/PageFragment.java index c56c2e8..83ea1b7 100755 --- a/app/src/main/java/org/wikipedia/page/PageFragment.java +++ b/app/src/main/java/org/wikipedia/page/PageFragment.java @@ -1051,7 +1051,7 @@ GalleryActivity.showGallery(getActivity(), model.getTitleOriginal(), filename, site, GalleryFunnel.SOURCE_NON_LEAD_IMAGE); } else { - linkHandler.onUrlClick(href); + linkHandler.onUrlClick(href, messagePayload.getString("title")); } } catch (JSONException e) { L.logRemoteErrorIfProd(e); diff --git a/app/src/main/java/org/wikipedia/page/PageTitle.java b/app/src/main/java/org/wikipedia/page/PageTitle.java index 029a830..b93d5a0 100644 --- a/app/src/main/java/org/wikipedia/page/PageTitle.java +++ b/app/src/main/java/org/wikipedia/page/PageTitle.java @@ -60,6 +60,25 @@ @Nullable private String description; @Nullable private final PageProperties properties; + /** + * Creates a new PageTitle object. + * Use this if you want to pass in a fragment portion separately from the title. + * + * @param prefixedText title of the page with optional namespace prefix + * @param fragment optional fragment portion + * @param site the wiki site the page belongs to + * @return a new PageTitle object matching the given input parameters + */ + public static PageTitle withSeparateFragment(@Nullable String prefixedText, @Nullable String fragment, @NonNull Site site) { + if (TextUtils.isEmpty(fragment)) { + return new PageTitle(prefixedText, site, null, (PageProperties) null); + } else { + // TODO: this class needs some refactoring to allow passing in a fragment + // without having to do string manipulations. + return new PageTitle(prefixedText + "#" + fragment, site, null, (PageProperties) null); + } + } + public PageTitle(@Nullable final String namespace, @NonNull String text, @Nullable String fragment, @Nullable String thumbUrl, @NonNull Site site) { this.namespace = namespace; this.text = text; diff --git a/app/src/main/java/org/wikipedia/page/gallery/GalleryActivity.java b/app/src/main/java/org/wikipedia/page/gallery/GalleryActivity.java index 4ad131b..9b52e54 100644 --- a/app/src/main/java/org/wikipedia/page/gallery/GalleryActivity.java +++ b/app/src/main/java/org/wikipedia/page/gallery/GalleryActivity.java @@ -368,7 +368,7 @@ private LinkMovementMethodExt linkMovementMethod = new LinkMovementMethodExt(new LinkMovementMethodExt.UrlHandler() { @Override - public void onUrlClick(String url) { + public void onUrlClick(@NonNull String url, @Nullable String notUsed) { L.v("Link clicked was " + url); url = resolveProtocolRelativeUrl(url); Site appSite = app.getSite(); diff --git a/app/src/main/java/org/wikipedia/util/UriUtil.java b/app/src/main/java/org/wikipedia/util/UriUtil.java index 4157026..1467957 100644 --- a/app/src/main/java/org/wikipedia/util/UriUtil.java +++ b/app/src/main/java/org/wikipedia/util/UriUtil.java @@ -8,6 +8,7 @@ import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.StringRes; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.Log; @@ -120,10 +121,37 @@ return title.getCanonicalUri() + "?wprov=" + context.getString(provId); } - public static String removeInternalLinkPrefix(String link) { + /** + * Note that while this method also replaces '_' with spaces it doesn't fully decode the string. + */ + @NonNull + public static String getTitleFromUrl(@NonNull String url) { + return removeFragment(removeLinkPrefix(url)).replace("_", " "); + } + + /** For internal links only */ + @NonNull + public static String removeInternalLinkPrefix(@NonNull String link) { return link.replaceFirst("/wiki/", ""); } + /** For links that could be internal or external links */ + @NonNull + private static String removeLinkPrefix(@NonNull String link) { + return link.replaceFirst("^.*?/wiki/", ""); + } + + /** Removes an optional fragment portion of a URL */ + @VisibleForTesting + @NonNull + static String removeFragment(@NonNull String link) { + return link.replaceFirst("#.*$", ""); + } + + public static String getFragment(String link) { + return Uri.parse(link).getFragment(); + } + private UriUtil() { } diff --git a/app/src/main/java/org/wikipedia/wiktionary/WiktionaryDialog.java b/app/src/main/java/org/wikipedia/wiktionary/WiktionaryDialog.java index 855f22e..4616c7c 100644 --- a/app/src/main/java/org/wikipedia/wiktionary/WiktionaryDialog.java +++ b/app/src/main/java/org/wikipedia/wiktionary/WiktionaryDialog.java @@ -204,7 +204,7 @@ private LinkMovementMethodExt linkMovementMethod = new LinkMovementMethodExt(new LinkMovementMethodExt.UrlHandler() { @Override - public void onUrlClick(String url) { + public void onUrlClick(@NonNull String url, @Nullable String notUsed) { if (url.startsWith(PATH_WIKI) || url.startsWith(PATH_CURRENT)) { dismiss(); showNewDialogForLink(url); diff --git a/app/src/test/java/org/wikipedia/util/UriUtilTest.java b/app/src/test/java/org/wikipedia/util/UriUtilTest.java new file mode 100644 index 0000000..54da52f --- /dev/null +++ b/app/src/test/java/org/wikipedia/util/UriUtilTest.java @@ -0,0 +1,41 @@ +package org.wikipedia.util; + +import org.wikipedia.test.TestRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@RunWith(TestRunner.class) +public class UriUtilTest { + /** + * Inspired by + * curl -s https://en.wikipedia.org/w/api.php?action=query&meta=siteinfo&format=json&siprop=general | jq .query.general.legaltitlechars + */ + private static final String TITLE + = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+"; + + /** + * Inspired by + *from http://stackoverflow.com/questions/2849756/list-of-valid-characters-for-the-fragment-identifier-in-an-url + */ + private static final String LEGAL_FRAGMENT_CHARS + = "!$&'()*+,;=-._~:@/?abc0123456789%D8%f6"; + + @Test + public void testRemoveFragment() { + assertThat(UriUtil.removeFragment(TITLE + "#" + LEGAL_FRAGMENT_CHARS), is(TITLE)); + } + + @Test + public void testRemoveEmptyFragment() { + assertThat(UriUtil.removeFragment(TITLE + "#"), is(TITLE)); + } + + @Test + public void testRemoveFragmentWithHash() { + assertThat(UriUtil.removeFragment(TITLE + "##"), is(TITLE)); + } +} diff --git a/www/js/actions.js b/www/js/actions.js index cb372e1..d6556c3 100644 --- a/www/js/actions.js +++ b/www/js/actions.js @@ -38,6 +38,19 @@ } } +/** + * Either gets the title from the title attribute (for mobileview case and newer MCS pages) or, + * if that doesn't not exists try to derive it from the href attribute value. + * In the latter case it also unescapes HTML entities to get the correct title string. + */ +function getTitle( sourceNode, href ) { + if (sourceNode.hasAttribute( "title" )) { + return sourceNode.getAttribute( "title" ); + } else { + return href.replace(/^\/wiki\//, '').replace(/^\.\//, '').replace(/#.*$/, ''); + } +} + document.onclick = function() { var sourceNode = null; var curNode = event.target; @@ -68,7 +81,7 @@ } else if (sourceNode.classList.contains( 'image' )) { bridge.sendMessage( 'imageClicked', { "href": href } ); } else { - bridge.sendMessage( 'linkClicked', { "href": href } ); + bridge.sendMessage( 'linkClicked', { "href": href, "title": getTitle(sourceNode, href) } ); } event.preventDefault(); } -- To view, visit https://gerrit.wikimedia.org/r/303942 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Iedbe1ae611f4d433d4c6bcac3cce3935333753e0 Gerrit-PatchSet: 16 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: BearND <bsitzm...@wikimedia.org> Gerrit-Reviewer: BearND <bsitzm...@wikimedia.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: Dbrant <dbr...@wikimedia.org> Gerrit-Reviewer: GWicke <gwi...@wikimedia.org> Gerrit-Reviewer: Mholloway <mhollo...@wikimedia.org> Gerrit-Reviewer: Niedzielski <sniedziel...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits