Article feedback/Version 5/Technical Design

This page describes technical designs for the Article Feedback Tool Version 5 (AFT V5).

See also: feature requirements page, project overview page, useful links, as well as data and metrics plan.

Overview
The front end of the AFTv5 extension builds upon the ArticleFeedback extension: on article pages, a set of javascript modules attach a new div to the bottom of the page and fill it with a feedback form. On submission, the form is replaced by a CTA (call to action). Most of this code has been rewritten to allow for the selection of one of a set of possible forms, and one of a set of possible CTAs. Right now, form options 1, 4, and 6 are available, and CTA 4, 5, and 6 being active (in part depending of whether the user is logged in or out).

The back end is entirely new: rather than displaying aggregate data, the Special page shows a feed of the responses to the article passed in to the url: /Special:ArticleFeedbackv5 for all feedback, /Special:ArticleFeedbackv5/Page for feedback for a certain page, or /Special:ArticleFeedbackv5/Page/FeedbackId for a specific feedback entry.

Database schema


"More detailed information is available on the schema documentation page"

There is only 1 table for AFTv5 data:. It can live on the same database the rest of the MediaWiki setup's code is, or it can be in a totally different database.

As a result of  being   by default, the standard scenario for AFTv5 will have the table live on the same database. To have AFTv5 live on a separate database, change the value for  to the external server's value. Assuming you have an external database at, you'd set

When a feedback form is submitted, a record is created in.

All moderation actions performed on a feedback entry will result in an entry being written to the core MediaWiki  table. Because it would be expensive to join  and   (and even impossible if the AFTv5 table lives on a different database) to query for the status of feedback, those statuses are de-normalized and are also represented in   (like ,  , ...)

Cache
Pretty much all data is meant to be read from cache. As soon as feedback is added or updated, the cached values will be updated immediately. Look at inline  comments if you're interested in the internals.

Upgrading and Maintenance
AFTv5 can be upgraded using.

If you are running are running AFTv5 from a different database, however, instead of running maintenance/update.php, you will need to manually apply updates though. SQL update files can be applied using. Maintenance scripts will read the correct cluster information from your config, so you won't have to explicitly specify the cluster there.

The upgrade hook is located in, in the   method. If you are bringing in new schema changes, you need to:


 * 1) Create a SQL upgrade file in the   directory
 * 2) Add a   call to   indicating what to check for and what file to run if it's missing.

There are two maintenance scripts available:


 * will purge all the cached data, forcing all data to be re-fetched from the database.
 * can be temporarily called (e.g. as a cronjob) to archive feedback that has not been moderated for a certain amount of time (as defined by  and  )

Other scripts in AFTv5's maintenance folder are update scripts, and are not meant to be run other otherwise.

Startup
AFTv5 mainly consists of three parts: a frontend form (displayed on pages in the namespaces defined in ), a link to the feedback page (displayed on corresponding talk pages), and the feedback page itself.

Actually, there's a fourth part: there's also a link to the feedback page from a user's watchlist (if there is feedback for his watchlisted pages). That feedback page will only show feedback from pages on that person's watchlist. For scaling reasons, it is not encouraged to enable this on wikis with a large number of pages.

Each of these parts can only appear when the main article falls within the configurable parameters. Below is a list of them, in order of precedence (the higher options will override the lower):


 * Not disabled via user preferences (AFT appears if other checks pass)
 * Category blacklist (AFTv5 never appears).
 * Page protection level: feedback can be enabled or disabled for in /Page?action=protect (fined-grained control for administrators), or from the special page directly via an API call (for editors).
 * Category whitelist (AFTv5 appears)
 * Lottery (AFTv5 appears if the page ID falls within range allowed)

We also have to make sure that the user's browser is supported.

We're performing all of these checks in JavaScript, which has a reasonably short cache timeout compared to the back end, which only clears cache when the page is edited -- lower-traffic pages could take up to 30 days to clear. Through PHP, we'll expose all of the required information to JavaScript in, in the   method (e.g. for talk pages, which can't just read the relevant article's id from JavaScript).

The relevant information can be found in JavaScript through. (where location can be "article", "talk" or "special") is called to verify if AFTv5 can be displayed.


 * Articles:
 * Loads module
 * Loads module
 * Loads module
 * Talk pages:
 * Loads module
 * Special page:
 * Loads module
 * Loads module
 * Watchlist page:
 * Loads module

On the article and talk pages, AFT being not enabled will just stop the relevant JavaScript from loading and the page will appear as normal. On the special page, the entire contents are removed and replaced with an error message saying that AFT is not enabled for the requested page -- or, if it was just the user agent check that failed, the error message will say that.

Frontend
If startup is successful, the primary module (ext.articleFeedbackv5.js) is loaded. The primary module attaches a new div to the bottom of the page and invokes the jQuery plugin (jquery.articleFeedbackv5.js) on it -- if the user scrolls to the bottom of the page, they'll see the form there; if they click the toolbox link or one of the optional prominent links, they'll be scrolled down to the feedback form. The particular form the user sees, and the presence/location of the optional link, are chosen via bucketing.

If the user hits the submit button, it runs an error check and, if there are no issues, invokes an AJAX request. Upon successful return, the form is replaced with a CTA (call to action), which is also chosen via bucketing.

Flow

 * 1) Startup phase
 * Determines whether to display the tool :
 * If all is well, it loads the main module.
 * 1) Main phase
 * Creates a new div, inserts it at the bottom of the article, and invokes the jQuery plugin on it
 * Selects a trigger link option and adds it to the page (if any)
 * * NB: The click event calls the plugin's open-as-modal event
 * 1) Init phase (, method  )
 * Sets some state variables, chooses a display option, and binds the "appear" event to the holder div
 * 1) Load phase (, method  )
 * Sets up the bottom of the page and overlay containers, builds the selected form, and puts it in the appropriate container
 * 1) Submit phase (, method  )
 * Runs validation, locks the form, and sends off an ajax request
 * 1) Submit response phase (, method  )
 * On success, selects a CTA and loads it into the appropriate container, and removes the feedback link(s)
 * On error, sets an error state and unlocks the form

Query string options
You can choose a form and/or a trigger link option and avoid bucketing by passing the following in the url:

Form Bucketing Process


Bucketing basically works like this:


 * 1) If the plugin is in the debug state, and a form has been requested in the url, and that value is known, select it.
 * 2) Call , which will:
 * 3) Retrieve a previously selected value (unless already expired) from a cookie, or
 * 4) Select a value based on the given (percentage) odds, and save that value to a cookie
 * 5) Verify the selected value is valid (e.g. CTA4 should not be displayed to logged-in users), and if not, fallback to a valid value (based on what's enabled via bucketing)

When bucketing config (e.g. percentages) are updated, be sure to update the 'version' number. Otherwise, values previously saved to the cookie will continue to be read.

Currently, bucketing is used for:


 * : CTA buckets expire immediately. Instead of reading a previously selected value from cookie, one will be re-generated every time. As a result, people may get to see different CTA's each and every time.
 * : This is meant to enable clicktracking only for a select amount of visitors. Although the infrastructure is still there, clicktracking is no longer implemented.
 * : CTA buckets expire immediately. Instead of reading a previously selected value from cookie, one will be re-generated every time. As a result, people may get to see different CTA's each and every time.
 * : This is meant to enable clicktracking only for a select amount of visitors. Although the infrastructure is still there, clicktracking is no longer implemented.

Submit AJAX call
This call is made when the user submits any of the feedback forms.

Call can be found at:

, method

Response can be found at:

Parameters:

Success response:

JSON object with one key,, containing this object:

Error response:

JSON object with one key,, containing the   and   for the appropriate error.

Abuse Filtering


AFT makes use of, SpamBlacklist, and AbuseFilter. You can turn abuse filtering on by setting  to true. If any of these three are available they will be used to filter out abusive feedback. See flowchart for details.

,, and   can be set to AFTv5-specific values for AbuseFilter's emergency shutdown. The emergency shutdown mechanism was intended to disable filters that are hit too often (to make sure that a broken filter does not keep blocking edits). Since AFTv5 feedback is very accessible, it will likely solicit more unproductive feedback and as a result, it could make perfect sense for filters to be triggered much more compared to regular page edits.

Actions
AFT makes use of AbuseFilter's "disallow" and "warn" actions, and it provides custom actions to automatically flag incoming feedback:

Creating Filters
AFT makes use of AbuseFilter's new "group" feature -- any filters set to "feedback" will be processed; other ones will be ignored.

When you set up filters for AFT, you should:


 * 1) Be cautious about the filter name; if feedback offends a filter, the filter name will be displayed in the error message.
 * 2) Set "feedback" as the group
 * 3) Write conditions appropriate for feedback
 * 4) Select one of the action options above

Writing filters is just as risky for feedback as it is for edits. You should read through the AbuseFilter conditions documentation carefully before proceeding.

You will have access to the following variables when writing conditions:

If you're adapting an edit filter to work with feedback, you should change conditions checking  to use. You can remove any conditions related to the previous state of the article (e.g., ).

You can test these filters just as you would the edit filters, by watching the AbuseFilter log.

Feedback list
The feedback list is loaded via an AJAX call, using the  function. This provides for paginated (incremental) display of feedback without the need to reload the entire page.

The  function pulls the highlighted post(s) separately.

Feedback post actions
Implementation of actions for the special page revolves around the  array of objects. Each array element represents an action, keyed by the action name, and contains an object with the following structure:
 * hasTipsy - true if the action needs a flyover panel*tipsyHtml - html for the corresponding flyover panel
 * click - click action
 * apiFlagType - flag type for api call
 * apiFlagDir - flag direction for api call (+/-1)
 * onSuccess - callback to execute after action success. Callback parameters:
 * id - respective post id
 * data - any data returned by the AJAX call

The following actions are implemented:


 * hidden filters are:  (increase),   (decrease),   (increase), and   (decrease)
 * visible filters are:  (increase),   (increase if applicable),   (increase if applicable),   (increase if applicable),   (increase if applicable),   (increase if applicable),   (increase if applicable),   (increase if applicable),   (increase if applicable), and   (increase if applicable)

The actual AJAX call for flagging actions is performed by the  function. The function locks the flagging actions for its duration to avoid multiple simultaneous requests.

User activity tracking
The user activity on the special page is stored client-side, via cookies. This functionality provides some limitation on actions repetition by the user.

TODO


 * 1) Moderation tools - hide, promote (to talk page), or flag various pieces of feedback.
 * 2) Sorting and filtering options - good comments first (how that's defined is unclear), newest first, etc. Basically, tools are needed to manage or at least hide the inevitable flood of spam "feedback".
 * 3) A special page, on the level of the talk pages - add a link to this in the not-quite-top-level navigation on said talk page.
 * 4) Need to work out which bits of this page are must-have-now vs must-have-eventually.
 * 5) Phase 1 will have a limited version with:
 * 6) Filters: All, Visible only
 * 7) Moderation Tools: Hide this comment
 * 8) Sorting: By date only
 * 9) Pagination: Fixed at 50

Filters
The feedback page allows the user to see feedback grouped according to a set of filters. These are available according to permissions (e.g., to see hidden or deleted feedback), and each action performed may adjust the counts in :


 * Filters in the visible column also have  appended the the where clause.
 * Filters in the not deleted column also have  appended the the where clause.
 * Filters in the all column have nothing appended the the where clause.

Relevance
Relevance scores are adjusted according to actions taken on the feedback. The weight of each action is configurable. The default values are:

Logging
Central activity log (https://en.wikipedia.org/w/index.php?title=Special%3ALog&type=articlefeedbackv5) calls upon class ArticleFeedbackv5LogFormatter to format the AFTv5 entries. This class extends from the default LogFormatter class and adds in some additional details to the entry (feedback text, page title & link, feedback n° & link)

The texts for all actions being logged are defined in ArticleFeedbackv5.php (as is the hook to the class that'll be formatting the log: ArticleFeedbackv5LogFormatter)

Note: We don't want some of the activity to show up, which is why certain actions are prefixed with suppress/ (see *core*/includes/logging/LogFormatter.php; "case 'suppress'") rather than articlefeedbackv5/. This way, we still have logs of all these actions, though not immediately visible.

The logging of all activity happens through a function call to ApiArticleFeedbackv5Utils::logActivity (ApiArticleFeedbackv5Utils.php) which will both insert the log entry, and increment the activity count on an entry (joining with the logging table is complex and not desired on such huge dataset).

The "view activity" link in the feedback page toolbox (both on central feedback page & permalink) also fetches its data from this logging table.

Database
ApiArticleFeedbackv5Utils::logActivity is the wrapper method to call when inserting log entries. This method performs some additional checks and will build the final data to be inserted.

 table

 table

Metrics / Clicktracking
AFTv5 uses the clicktracking extension to track how the extension is being used. All event names are prefixed with, where   is the value of.

Platforms
We aim to support the following web browsers for phases 1.0 and 1.5:
 * Internet Explorer 7+
 * Firefox 3+
 * Safari 5+
 * Opera 10+
 * Chrome 5+

NB: Support for IE7 was pulled from phase 1.0.

We will focus our testing on these top browser versions for Wikipedia:
 * IE 8			(17%)
 * Chrome 14	       (16%)
 * Firefox 7		(11%)
 * IE 7			(7%)
 * IE 9			(6%)
 * Firefox 3		(5%)
 * Firefox 6		(2%)

These will be tested on these desktop platforms:
 * Windows	(78%)
 * Mac		 (8%)
 * Linux		 (3%)

We are not currently planning to support IE6 or mobile platforms for phases 1.0 and 1.5, and will not show the forms at all on these unsupported platforms. In future versions, we will aim for 'graceful degradation' in unsupported platforms, and are working on a good definition for testing that objective.