Brion VIBBER has submitted this change and it was merged. Change subject: Display appropriate warning when AF returns a message ......................................................................
Display appropriate warning when AF returns a message - Links in AF messages still don't work, need to be sorted out. - Also makes sure that cookies are persisted across requests Change-Id: Ia95255309eee5fdab2040f4dc9aba76e0f9715e2 --- A wikipedia/assets/abusefilter.css A wikipedia/assets/abusefilter.html A wikipedia/assets/abusefilter.js M wikipedia/res/layout/activity_edit_section.xml M wikipedia/res/values/strings.xml M wikipedia/src/main/java/org/wikipedia/OkHttpConnectionFactory.java M wikipedia/src/main/java/org/wikipedia/editing/EditSectionActivity.java M www/Gruntfile.js A www/abusefilter.css A www/abusefilter.html A www/abusefilter.js A www/js/abusefilter.js 12 files changed, 464 insertions(+), 11 deletions(-) Approvals: Brion VIBBER: Verified; Looks good to me, approved diff --git a/wikipedia/assets/abusefilter.css b/wikipedia/assets/abusefilter.css new file mode 100644 index 0000000..3124f5c --- /dev/null +++ b/wikipedia/assets/abusefilter.css @@ -0,0 +1,114 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url(fonts/OpenSans.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url(fonts/OpenSans-Bold.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), url(fonts/OpenSans-Italic.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(fonts/OpenSans-BoldItalic.ttf) format('truetype'); +} + +/* Should be moved out to the MobileApp extension at some point */ +body { + background-color: #F2F2F2; + color: #333; + font-family: "Open Sans", sans-serif; + font-size: 16px; + line-height: 160%; + margin: 0; + padding: 0; + margin-top: 48px; +} +#content { + padding: 8px 24px; +} +a { + color: #347BFF; + text-decoration: none; +} +/* Headers */ +h1 { + font-family: serif; + color: #064AAD; + font-size: 26px; + line-height: 36px; + margin-bottom: 24px; + font-weight: bold; +} +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", sans-serif; +} +h2 { + font-size: 22px; +} +h3 { + font-size: 20px; +} +h4 { + font-size: 18px; +} +h5, +h6 { + font-size: 16px; +} +/* Basic Thumbnails */ +.thumb { + /* Don't stick thumbnails to the text above and below it */ + margin: 8px 0px; + /* Align everything inside a thumbnail to be centered */ + text-align: center; +} +.thumb .thumbinner { + /* Make sure that thumb takes up full width */ + width: 100% !important; +} +.thumb .magnify { + display: none; + /* Our parser is STUPID */ +} +.thumb .noresize { + overflow-x: auto; + /* Scrollbars for images that shouldn't be squished */ +} +/* Makes sure that we don't have horizontal scrollbars for the entire page because + an image is too wide to fit in to the current viewport */ +img { + max-width: 100% !important; + height: auto !important; +} +/* Basic tables */ +table { + width: 100% !important; + overflow-x: auto; + display: block; + border: 1px solid #ccc; + margin-top: 4px; + margin-bottom: 4px; +} +/* Last updated info */ +#attribution { + border-top: 1px solid #ccc; + padding: 8px; + text-align: center; + color: #888; + font-size: 80%; +} diff --git a/wikipedia/assets/abusefilter.html b/wikipedia/assets/abusefilter.html new file mode 100644 index 0000000..1c62ecd --- /dev/null +++ b/wikipedia/assets/abusefilter.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> + <base href="https://wikipedia.org" /> <!-- Force links to resolve with https as protocol, rather than file:// --> + <script src="file:///android_asset/abusefilter.js"></script> + + <link rel="stylesheet" type="text/css" href="file:///android_asset/abusefilter.css"> </link> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" /> +</head> +<body> +<div id="content"></div> +</body> +</html> \ No newline at end of file diff --git a/wikipedia/assets/abusefilter.js b/wikipedia/assets/abusefilter.js new file mode 100644 index 0000000..0a9f2d4 --- /dev/null +++ b/wikipedia/assets/abusefilter.js @@ -0,0 +1,46 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +var bridge = require('./bridge'); + +bridge.registerListener( 'displayWarning', function( payload ) { + var content = document.getElementById( 'content' ); + content.innerHTML = payload.html; +} ); +},{"./bridge":2}],2:[function(require,module,exports){ +function Bridge() { +} + +var eventHandlers = {}; + +// This is called directly from Java +window.handleMessage = function( type, msgPointer ) { + var that = this; + var payload = JSON.parse( marshaller.getPayload( msgPointer ) ); + if ( eventHandlers.hasOwnProperty( type ) ) { + eventHandlers[type].forEach( function( callback ) { + callback.call( that, payload ); + } ); + } +}; + +Bridge.prototype.registerListener = function( messageType, callback ) { + if ( eventHandlers.hasOwnProperty( messageType ) ) { + eventHandlers[messageType].push( callback ); + } else { + eventHandlers[messageType] = [ callback ]; + } +}; + +Bridge.prototype.sendMessage = function( messageType, payload ) { + var messagePack = { type: messageType, payload: payload }; + var ret = window.prompt( JSON.stringify( messagePack) ); + if ( ret ) { + return JSON.parse( ret ); + } +}; + +module.exports = new Bridge(); +// FIXME: Move this to somwehere else, eh? +window.onload = function() { + module.exports.sendMessage( "DOMLoaded", {} ); +}; +},{}]},{},[2,1]) \ No newline at end of file diff --git a/wikipedia/res/layout/activity_edit_section.xml b/wikipedia/res/layout/activity_edit_section.xml index 7dc7e2c..9c96bc2 100644 --- a/wikipedia/res/layout/activity_edit_section.xml +++ b/wikipedia/res/layout/activity_edit_section.xml @@ -77,4 +77,34 @@ /> </LinearLayout> + <ScrollView android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/edit_section_abusefilter_container" + android:visibility="gone" + android:background="@android:color/background_light" + > + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <WebView + android:id="@+id/edit_section_abusefilter_webview" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/border"/> + <TextView + android:id="@+id/edit_section_abusefilter_back" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:text="@string/edit_section_abusefilter_back_text" + android:padding="8dp" + android:background="?android:attr/activatedBackgroundIndicator" + style="?android:textAppearanceMedium" + /> + <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/border"/> + + </LinearLayout> + </ScrollView> </FrameLayout> \ No newline at end of file diff --git a/wikipedia/res/values/strings.xml b/wikipedia/res/values/strings.xml index 4b5a304..f6144d8 100644 --- a/wikipedia/res/values/strings.xml +++ b/wikipedia/res/values/strings.xml @@ -58,4 +58,5 @@ </string> <string name="edit_section_captcha_hint">Repeat words from above</string> <string name="edit_section_title_captcha">Enter captcha</string> + <string name="edit_section_abusefilter_back_text">Make changes to my edit</string> </resources> \ No newline at end of file diff --git a/wikipedia/src/main/java/org/wikipedia/OkHttpConnectionFactory.java b/wikipedia/src/main/java/org/wikipedia/OkHttpConnectionFactory.java index 68f348b..46ae791 100644 --- a/wikipedia/src/main/java/org/wikipedia/OkHttpConnectionFactory.java +++ b/wikipedia/src/main/java/org/wikipedia/OkHttpConnectionFactory.java @@ -1,12 +1,16 @@ package org.wikipedia; import android.content.Context; +import android.util.Log; import com.github.kevinsawicki.http.HttpRequest; import com.squareup.okhttp.HttpResponseCache; import com.squareup.okhttp.OkHttpClient; +import org.apache.http.impl.client.BasicCookieStore; import java.io.IOException; import java.net.*; +import java.util.List; +import java.util.Map; public class OkHttpConnectionFactory implements HttpRequest.ConnectionFactory { private static final long HTTP_CACHE_SIZE = 16 * 1024 * 1024; @@ -15,6 +19,10 @@ public OkHttpConnectionFactory(Context context) { client = new OkHttpClient(); + CookieManager cm = new CookieManager(); + cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL); + client.setCookieHandler(cm); + try { client.setResponseCache(new HttpResponseCache(context.getCacheDir(), HTTP_CACHE_SIZE)); } catch (IOException e) { diff --git a/wikipedia/src/main/java/org/wikipedia/editing/EditSectionActivity.java b/wikipedia/src/main/java/org/wikipedia/editing/EditSectionActivity.java index b3afc6e..555a384 100644 --- a/wikipedia/src/main/java/org/wikipedia/editing/EditSectionActivity.java +++ b/wikipedia/src/main/java/org/wikipedia/editing/EditSectionActivity.java @@ -3,25 +3,25 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; +import android.content.Context; import android.content.DialogInterface; -import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.webkit.WebView; import android.widget.*; import com.github.kevinsawicki.http.HttpRequest; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; -import com.squareup.picasso.Transformation; +import org.json.JSONException; +import org.json.JSONObject; import org.mediawiki.api.json.Api; import org.mediawiki.api.json.RequestBuilder; -import org.wikipedia.PageTitle; -import org.wikipedia.R; -import org.wikipedia.Utils; -import org.wikipedia.WikipediaApp; +import org.wikipedia.*; import org.wikipedia.page.Section; public class EditSectionActivity extends Activity { @@ -42,9 +42,14 @@ private View captchaContainer; private ImageView captchaImage; private EditText captchaText; - private Button captchaConfirm; + + private View abusefilterContainer; + private WebView abusefilterWebView; + private CommunicationBridge abusefilterBridge; + private View abuseFilterBackAction; private CaptchaEditResult captchaEditResult; + private AbuseFilterEditResult abusefilterEditResult; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,6 +78,11 @@ captchaImage = (ImageView) findViewById(R.id.edit_section_captcha_image); captchaText = (EditText) findViewById(R.id.edit_section_captcha_text); + abusefilterContainer = findViewById(R.id.edit_section_abusefilter_container); + abusefilterWebView = (WebView) findViewById(R.id.edit_section_abusefilter_webview); + abusefilterBridge = new CommunicationBridge(abusefilterWebView, "file:///android_asset/abusefilter.html"); + abuseFilterBackAction = findViewById(R.id.edit_section_abusefilter_back); + if (savedInstanceState != null && savedInstanceState.containsKey("sectionWikitext")) { sectionWikitext = savedInstanceState.getString("sectionWikitext"); } @@ -82,11 +92,23 @@ handleCaptcha(); } + if (savedInstanceState != null && savedInstanceState.containsKey("abusefilter")) { + abusefilterEditResult = savedInstanceState.getParcelable("abusefilter"); + handleAbuseFilter(); + } + sectionErrorRetry.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Utils.crossFade(sectionError, sectionProgress); fetchSectionText(); + } + }); + + abuseFilterBackAction.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + cancelAbuseFilter(); } }); @@ -154,6 +176,9 @@ } else if (result instanceof CaptchaEditResult) { captchaEditResult = (CaptchaEditResult) result; handleCaptcha(); + } else if (result instanceof AbuseFilterEditResult) { + abusefilterEditResult = (AbuseFilterEditResult) result; + handleAbuseFilter(); } else { // Expand to do everything. onCatch(null); @@ -163,11 +188,34 @@ }.execute(); } + private void handleAbuseFilter() { + if (abusefilterEditResult == null) { + return; + } + JSONObject payload = new JSONObject(); + try { + payload.putOpt("html", abusefilterEditResult.getWarning()); + } catch (JSONException e) { + // Goddamn Java + throw new RuntimeException(e); + } + abusefilterBridge.sendMessage("displayWarning", payload); + if (getCurrentFocus() != null) { + InputMethodManager keyboard = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); + keyboard.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); + } + Utils.fadeIn(abusefilterContainer); + progressDialog.dismiss(); + } + private void handleCaptcha() { + if (captchaEditResult == null) { + return; + } Picasso.with(EditSectionActivity.this) .load(Uri.parse(captchaEditResult.getCaptchaUrl(title.getSite()))) - // Don't use .fit() here - seems to cause the loading to fail - // See https://github.com/square/picasso/issues/249 + // Don't use .fit() here - seems to cause the loading to fail + // See https://github.com/square/picasso/issues/249 .into(captchaImage, new Callback() { @Override public void onSuccess() { @@ -190,6 +238,12 @@ captchaText.setText(""); getActionBar().setTitle(R.string.editsection_activity_title); Utils.crossFade(captchaContainer, sectionContainer); + } + + private void cancelAbuseFilter() { + abusefilterEditResult = null; + getActionBar().setTitle(R.string.editsection_activity_title); + Utils.crossFade(abusefilterContainer, sectionContainer); } public boolean onMenuItemSelected(int featureId, MenuItem item) { @@ -216,6 +270,7 @@ super.onSaveInstanceState(outState); outState.putString("sectionWikitext", sectionWikitext); outState.putParcelable("captcha", captchaEditResult); + outState.putParcelable("abusefilter", abusefilterEditResult); } private void fetchSectionText() { @@ -251,6 +306,8 @@ public void onBackPressed() { if (captchaEditResult != null) { cancelCaptcha(); + } else if (abusefilterEditResult != null) { + cancelAbuseFilter(); } else { finish(); } diff --git a/www/Gruntfile.js b/www/Gruntfile.js index 65b554c..9e4ec31 100644 --- a/www/Gruntfile.js +++ b/www/Gruntfile.js @@ -20,14 +20,16 @@ dist: { files: { "bundle.js": [ "js/main.js", "js/transforms.js", "js/bridge.js", "js/linkactions.js", "js/sections.js"], - "bundle-test.js": [ "js/main.js", "js/bridge.js", "tests/*.js" ] + "bundle-test.js": [ "js/main.js", "js/bridge.js", "tests/*.js" ], + "abusefilter.js": [ "js/bridge.js", "js/abusefilter.js" ] } } }, less: { all: { files: [ - { src: ["less/*.less"], dest: "styles.css"} + { src: ["less/fonts.less", "less/pagestyles.less", "less/ui.less"], dest: "styles.css"}, + { src: ["less/fonts.less", "less/pagestyles.less"], dest: "abusefilter.css"} ] } }, @@ -46,6 +48,9 @@ // Test files {src: ["bundle-test.js", "tests/index.html"], dest: "../wikipedia/assets/"}, + // Abusefilter files + { src: ["abusefilter.js", "abusefilter.css", "abusefilter.html"], dest: "../wikipedia/assets/" }, + // Images {src: ["images/*"], dest:"../wikipedia/assets/"}, diff --git a/www/abusefilter.css b/www/abusefilter.css new file mode 100644 index 0000000..3124f5c --- /dev/null +++ b/www/abusefilter.css @@ -0,0 +1,114 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url(fonts/OpenSans.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url(fonts/OpenSans-Bold.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), url(fonts/OpenSans-Italic.ttf) format('truetype'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(fonts/OpenSans-BoldItalic.ttf) format('truetype'); +} + +/* Should be moved out to the MobileApp extension at some point */ +body { + background-color: #F2F2F2; + color: #333; + font-family: "Open Sans", sans-serif; + font-size: 16px; + line-height: 160%; + margin: 0; + padding: 0; + margin-top: 48px; +} +#content { + padding: 8px 24px; +} +a { + color: #347BFF; + text-decoration: none; +} +/* Headers */ +h1 { + font-family: serif; + color: #064AAD; + font-size: 26px; + line-height: 36px; + margin-bottom: 24px; + font-weight: bold; +} +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", sans-serif; +} +h2 { + font-size: 22px; +} +h3 { + font-size: 20px; +} +h4 { + font-size: 18px; +} +h5, +h6 { + font-size: 16px; +} +/* Basic Thumbnails */ +.thumb { + /* Don't stick thumbnails to the text above and below it */ + margin: 8px 0px; + /* Align everything inside a thumbnail to be centered */ + text-align: center; +} +.thumb .thumbinner { + /* Make sure that thumb takes up full width */ + width: 100% !important; +} +.thumb .magnify { + display: none; + /* Our parser is STUPID */ +} +.thumb .noresize { + overflow-x: auto; + /* Scrollbars for images that shouldn't be squished */ +} +/* Makes sure that we don't have horizontal scrollbars for the entire page because + an image is too wide to fit in to the current viewport */ +img { + max-width: 100% !important; + height: auto !important; +} +/* Basic tables */ +table { + width: 100% !important; + overflow-x: auto; + display: block; + border: 1px solid #ccc; + margin-top: 4px; + margin-bottom: 4px; +} +/* Last updated info */ +#attribution { + border-top: 1px solid #ccc; + padding: 8px; + text-align: center; + color: #888; + font-size: 80%; +} diff --git a/www/abusefilter.html b/www/abusefilter.html new file mode 100644 index 0000000..1c62ecd --- /dev/null +++ b/www/abusefilter.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<head> + <base href="https://wikipedia.org" /> <!-- Force links to resolve with https as protocol, rather than file:// --> + <script src="file:///android_asset/abusefilter.js"></script> + + <link rel="stylesheet" type="text/css" href="file:///android_asset/abusefilter.css"> </link> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" /> +</head> +<body> +<div id="content"></div> +</body> +</html> \ No newline at end of file diff --git a/www/abusefilter.js b/www/abusefilter.js new file mode 100644 index 0000000..0a9f2d4 --- /dev/null +++ b/www/abusefilter.js @@ -0,0 +1,46 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +var bridge = require('./bridge'); + +bridge.registerListener( 'displayWarning', function( payload ) { + var content = document.getElementById( 'content' ); + content.innerHTML = payload.html; +} ); +},{"./bridge":2}],2:[function(require,module,exports){ +function Bridge() { +} + +var eventHandlers = {}; + +// This is called directly from Java +window.handleMessage = function( type, msgPointer ) { + var that = this; + var payload = JSON.parse( marshaller.getPayload( msgPointer ) ); + if ( eventHandlers.hasOwnProperty( type ) ) { + eventHandlers[type].forEach( function( callback ) { + callback.call( that, payload ); + } ); + } +}; + +Bridge.prototype.registerListener = function( messageType, callback ) { + if ( eventHandlers.hasOwnProperty( messageType ) ) { + eventHandlers[messageType].push( callback ); + } else { + eventHandlers[messageType] = [ callback ]; + } +}; + +Bridge.prototype.sendMessage = function( messageType, payload ) { + var messagePack = { type: messageType, payload: payload }; + var ret = window.prompt( JSON.stringify( messagePack) ); + if ( ret ) { + return JSON.parse( ret ); + } +}; + +module.exports = new Bridge(); +// FIXME: Move this to somwehere else, eh? +window.onload = function() { + module.exports.sendMessage( "DOMLoaded", {} ); +}; +},{}]},{},[2,1]) \ No newline at end of file diff --git a/www/js/abusefilter.js b/www/js/abusefilter.js new file mode 100644 index 0000000..b4a26dc --- /dev/null +++ b/www/js/abusefilter.js @@ -0,0 +1,6 @@ +var bridge = require('./bridge'); + +bridge.registerListener( 'displayWarning', function( payload ) { + var content = document.getElementById( 'content' ); + content.innerHTML = payload.html; +} ); \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/110228 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ia95255309eee5fdab2040f4dc9aba76e0f9715e2 Gerrit-PatchSet: 4 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: Yuvipanda <yuvipa...@gmail.com> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits