User:Catrope/Extension review/ArticleFeedbackv5

Base revision:

ArticleFeedbackv5.php

 * Line 82, 103: please be aware that tracking bucketing generates a lot of noise in the tracking data, to the point where it brought the site down once. This can only be enabled on the cluster if we use UDP logging (which we want to do anyway, I just need to get around to enabling it), and I will probably want to have it turned off initially when deploying.
 * Line 119-129: the keys in the bucket configuration (-, A, B, C) don't match the keys in the documentation comment (0, 1, 2, 3)
 * Line 161: are you guys even using the survey stuff anymore? If not, it should be stripped out of v5

ArticleFeedbackv5.hooks.php

 * Line 293-297: debugging code, please comment out or remove. This seems to be unfinished edit tagging functionality?

ArticleFeedbackv5.i18n.php

 * Line 35, 50: inconsistency between  (with spaces) and   (without spaces)
 * Line 39: message key is misspelled (invalud). The correct spelling is used in api/ApiViewFeedbackArticleFeedbackv5.php, which means that code will hit a missing message
 * Line 42: don't capitalize 'Feedback'. Our i18n people don't like Title Case, and 'feedback' isn't capitalized in any of the other messages
 * Our i18n people also prefer 'page' over 'article' but I think Fabrice raised a similar point already
 * Line 85: Don't Use Title Case In 'Very Good'

SpecialArticleFeedback5.php
Overall it looks like this special page is very much unfinished, and it is undeployable in its current state. This either needs serious work before deployment, or needs to be disabled by commenting it out from $wgSpecialPages.
 * Line 17, 48-50, 53, 55, 56, 61: hardcoded English text
 * Line 22: The  thing will not work on Wikipedias, because on those wikis "Wikipedia:" is a namespace prefix rather than an interwiki prefix. There are also general issues with building wikitext this way. You should build links the proper way:
 * Here  is a Title object (see also my Title::newFromText example below) and the second parameter is HTML (which is why you have to escape the i18n message)
 * Line 43-44: " This is a terrible, terrible hack. I'm taking it out as soon as I stop being an idiot and sort this ResourceLoader thing out". Would this week be a good time to stop being an idiot? ;) If you have questions regarding ResourceLoader, I can answer them, I wrote half of it. I'll be on vacation Tue-Fri but should be reasonably responsive to e-mail.
 * Line 75: MediaWiki has built-in functionality for obtaining a page ID from a title, and unlike your pageIdFromTitle function it also works for namespaced titles like this page :)
 * Line 75: MediaWiki has built-in functionality for obtaining a page ID from a title, and unlike your pageIdFromTitle function it also works for namespaced titles like this page :)

sql/ArticleFeedbackv5.sql
It saddens me to see that my feedback on the schema was largely ignored.
 * Line 22: I've told you before that you should really have a user_ip field (IP address or null) field here rather than a user_text field (IP address or user name). The latter is the old style used in most of MW core, the former is the new style used in newer extensions. If you insist on having a user_text field, at least make sure the data type is exactly the same as that of user.user_name
 * Line 34, 35: I've told you before that you should not use MySQL's TIMESTAMP type. You also acknowledged that the modified field wasn't needed because the rows in this table aren't changed, but it's still there
 * With the exception of the first table, comments are few and far between. Please add a comment above every table to explain what it contains (this is missing even for the first table -- from the field names I can deduce that it contains individual ratings) and above every field name where the contents of the field aren't immediately obvious from their name (things like auto increment IDs don't need comments, but things like foreign keys, enums and most text fields do)

api/ApiArticleFeedbackv5Utils.php

 * Line 34, 36, 38: please resolve this TODO
 * Line 54:  is global'ed but   is used in the code (and defined in the setup file). You should use   when debugging, that should have caused an E_NOTICE about an undefined variable to appear
 * Line 98-111: you don't need to write your own getRevisionId function, there is a built-in function for obtaining the latest revision ID of a page: obtain a Title object and call $title->getLatestRevID
 * Line 116, 138: per the TODO comments, use memcached here
 * Line 122-125, 144-147: always pass the __METHOD__ parameter to select
 * Line 118: this function passes the return value of select through unmodified, which means it does not return an array, but a ResultWrapper. These objects are iterable, so in practice you don't notice the difference (you can use foreach on them and such) until you try passing them into array_* functions and everything explodes. If you document your return values correctly, this is less likely to happen
 * Line 150-152: creating an array before you append to it is something that you need to do in JavaScript (which is annoying as hell), but it's not needed in PHP. In fact, you can even do stuff like  and that'll just work.

api/ApiArticleFeedbackv5.php

 * Line 38-40: why is this check commented out?
 * Line 76: TODO ERROR
 * Line 90-104: I'm impressed that you guys put in the Squid purge thing. This is probably the only extension that does this, and it's not something I thought would have been easy to figure out on your own. However, you need to get these things exactly right, if there's the slightest discrepancy it won't work at all:
 * Line 94: this should be list=articlefeedbackv5-view-ratings to match the JS file
 * Line 96: the empty string here betrays that the anontoken parameter is unused. It should be removed here, in the JS file, and in the API module
 * Line 97: afuserrating is even more unused (not defined in the API module, not passed by JS) and should also be removed
 * Line 142: you should never do escaping in an input validation function. Escaping should happen as close to the output as possible. I know this isn't done right now but I recommend removing the comment suggesting this lest anyone read it and implement it
 * Line 201: the containing function is called three times, so getRevisionLimit is also called three times. You should cache its return value somewhere, either in getRevisionLimit itself or somewhere in this class
 * Line 211-240: these two queries are poorly indexed. You will need the following:
 * an index on (af_page_id, af_revision_id). There is currently an index on af_revision_id alone, but that is insufficient for this query
 * an index on (afi_data_type)
 * an index on (aa_feedback_id, aa_field_id, aa_response_option_id)
 * with all those, the query should be fairly reasonable (although I'll believe that when I EXPLAIN it), but I'd really prefer updating the rollup tables with increments/decrements (UPDATE foo SET bar=bar+1 WHERE baz) over recomputing the values every time. The GROUP BY query is a bit scary, and will probably be slow for heavily rated pages
 * Line 242: I have no idea what that comment is supposed to tell me
 * Line 275: am I misinterpreting the meaning of the per-revision rollup tables here? It seems these tables track the numbers for that revision and the 29 revs before it combined, rather than just tracking the numbers for that revision. I can't tell which interpretation is correct because there is no documentation in the code and no documentation in the SQL file either
 * Line 290-300: instead of that DELETE-INSERT-DELETE-INSERT sequence, you may want to use two REPLACE queries. See DatabaseBase::replace in includes/db/Database.php
 * Line 310: this function is named and documented as a getter, but in reality it inserts a row. Please name and document the function accordingly
 * Line 389, 394, 400, 406, 411, 412, 420, 421: you don't need to specify ISMULTI => false or REQUIRED => false, these are false by default

api/ApiFlagFeedbackArticleFeedbackv5.php

 * Line 18: the 'af' prefix conflicts with the v4 modules and with the other two modules. Every query module should have a unique parameter prefix. Also, action= modules conventionally do not have a parameter prefix
 * Line 31, 43, 49: ideally you would return an error code or message key from the API and have the JS client do the i18n work. This prevents annoying bugs when ?uselang= is used

api/ApiViewFeedbackArticleFeedbackv5.php

 * A submodule of action=query has to be able to coexist peacefully with its fellow submodules, because any number of submodules can be invoked in the same request. This means you have to
 * choose a unique parameter prefix (line 22, see also comment on ApiFlagFeedback)
 * namespace your ApiResult additions by using $this->getModuleName as the key rather than 'data' (line 49-51). This is done correctly in the ViewRatings module, on line 36
 * Line 89: a COUNT(*) query on an unbounded number of rows is unacceptable in production. Doesn't the rollup table have this information?
 * Line 103-110: we have a choice between DESC sorting and DESC sorting? That looks broken
 * Line 119-126: this query has WHERE af_page_id=N ORDER BY af_id, so there should be an index of (af_page_id, af_id)
 * Line 123: don't use OFFSET-based paging. Instead, page with WHERE af_page_id >= $params['continue'] and set a query-continue for the next page ID. Grep the core code for query-continue for examples, or ask me
 * Line 188, 200, 203, 206 : as it is, these messages are being treated as raw HTML. This is occasionally OK, but please avoid it wherever possible. The output of these wfMsg calls should be escaped
 * Line 201, 204: use the 'pipe-separator'
 * Line 225: $found is the result of a wfMsg call, should also be escaped
 * Line 226: THIS IS A GLARINGLY OBVIOUS STORED XSS VULNERABILITY. You have to escape aa_response_text before putting it in your HTML output
 * Line 234, 242, 256: More instances of unescaped database values in the HTML output
 * Line 233, 241, 247, 252, 270, 274: More instances of unescaped messages

api/ApiViewRatingsArticleFeedbackv5.php

 * Line 55: you don't need the setIndexedTagname_internal call here because there are no numerically indexed arrays around
 * Line 75-77: the fetchRevisionRollup function is unused
 * Line 113: why are you grouping by afi_name? It's unnecessary because you're already grouping by rating_id (and afi_name is derived from that). Also, are afi_id and aa_rating_id joinable against each other? If so, shouldn't the latter be called aa_field_id? I wouldn't have to ask if there were any documentation whatsoever in the SQL file
 * Also, because the revisions branch is dead/unreachable, you can remove the SUM and GROUP BY parts, right?
 * Line 127, 130: the userrating and subaction parameters are undocumented and unused
 * Line 128, 135-139: the anontoken and revid parameters are unused

modules/ext.articleFeedbackv5/ext.articleFeedbackv5.startup.js

 * Line 23: this is checking the v4 preference name, should check the v5 preference name instead
 * Line 30-32: why does the bucketing happen here rather than in ext.articleFeedbackv5.js ? There's another bucket call there, so removing the bucket call here wouldn't break anything, it would just change when the bucketing occurs (and it would bucket way way fewer people in the beginning, that's important)

modules/jquery.articleFeedbackv5/jquery.articleFeedbackv5.special.js

 * Line 13-14: you can use  to get the URL for api.php
 * Line 54: "TODO user ID?" yeah strangely the user ID isn't available in JS. But you shouldn't need to pass it to the API module anyway
 * Line 95-97: can't you use  rather than   ?
 * Line 100, 101: please use .text rather than .html for text, it escapes HTML. Using HTML functions for things that aren't really HTML scares me

modules/ext.articleFeedbackv5/ext.articleFeedbackv5.js

 * Line 78: you can't use a self-closing span tag here. IE will choke if you use self-closing span tags in a jQuery constructor
 * Line 93: ditto, closing a tag

modules/jquery.articleFeedbackv5/jquery.articleFeedbackv5.js

 * Line 112: you can use  to get the URL for api.php
 * Line 243: all your HTML classes and IDs are nicely namespaced, but  isn't
 * Line 297: this URL is passed in in wgArticleFeedbackv5TermsPage. Bucket 2 does grab this variable
 * Line 363-364, 621-623, 656, 941, 942: why is this commented out?
 * Line 577, 847, 1312: this grabs the proper variable for the terms page URL, but feeding it through wikiGetLink is wrong, because it's already an absolute URL, not a page name
 * Line 682, 712:  leaks to the global scope
 * Line 776: why is there hardcoded English here? ("Remove this rating")
 * Line 828: so what is this for?
 * Line 1055: you cannot have an unclosed html:msg tag here (self-closing is OK, that's what's used elsewhere) because it's invalid HTML and IE will barf
 * Line 1156: as written this will bucket all users in a with-expertise and a without-expertise group, even if they're never shown form number 5. It's probably better to move this in a bit deeper so the expertise vs. no expertise bucketing only happens if needed, and we end up bucketing fewer people (=fewer cookies sent around, fewer bucketing events if bucket tracking is enabled)
 * Line 1276-1277: is something missing here?
 * Line 1616: the anontoken parameter is not used by this API module and should be removed
 * Line 1618:  is called but the setting is called
 * Line 1632, 2309: you cannot call console.log if you're not sure you're in debug mode, it causes a JS error if there is no active debugger
 * Line 1782-1796: why is  not used here?
 * Line 2028, 2029, 2314: use .text for message contents, not .html