mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Linter
synced 2024-11-23 15:36:52 +00:00
Initial commit
This configures a MediaWiki extension to recieve Parsoid's lint errors and expose them to users. Change-Id: Ie0776aecf145eb1c87c2a539ddf3ea8d35a899f5
This commit is contained in:
parent
44a0126f3a
commit
bce5b31616
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
.svn
|
||||
*~
|
||||
*.kate-swp
|
||||
.*.swp
|
||||
composer.lock
|
||||
vendor
|
||||
node_modules/
|
1
.jshintignore
Normal file
1
.jshintignore
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
24
.jshintrc
Normal file
24
.jshintrc
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
// Enforcing
|
||||
"bitwise": true,
|
||||
"eqeqeq": true,
|
||||
"freeze": true,
|
||||
"latedef": true,
|
||||
"noarg": true,
|
||||
"nonew": true,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"strict": false,
|
||||
|
||||
// Relaxing
|
||||
"es5": false,
|
||||
|
||||
// Environment
|
||||
"browser": true,
|
||||
"jquery": true,
|
||||
|
||||
"globals": {
|
||||
"mediaWiki": false,
|
||||
"OO": false
|
||||
}
|
||||
}
|
339
COPYING
Normal file
339
COPYING
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
32
Gruntfile.js
Normal file
32
Gruntfile.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*jshint node:true */
|
||||
module.exports = function ( grunt ) {
|
||||
grunt.loadNpmTasks( 'grunt-contrib-jshint' );
|
||||
grunt.loadNpmTasks( 'grunt-jsonlint' );
|
||||
grunt.loadNpmTasks( 'grunt-banana-checker' );
|
||||
grunt.loadNpmTasks( 'grunt-jscs' );
|
||||
var conf = grunt.file.readJSON( 'extension.json' );
|
||||
|
||||
grunt.initConfig( {
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: true
|
||||
},
|
||||
all: [
|
||||
'modules/*.js'
|
||||
]
|
||||
},
|
||||
jscs: {
|
||||
src: '<%= jshint.all %>'
|
||||
},
|
||||
banana: conf.MessagesDirs,
|
||||
jsonlint: {
|
||||
all: [
|
||||
'**/*.json',
|
||||
'!node_modules/**'
|
||||
]
|
||||
}
|
||||
} );
|
||||
|
||||
grunt.registerTask( 'test', [ 'jshint', 'jscs', 'jsonlint', 'banana' ] );
|
||||
grunt.registerTask( 'default', 'test' );
|
||||
};
|
3
README
Normal file
3
README
Normal file
|
@ -0,0 +1,3 @@
|
|||
Extension to help lint wiki pages. Currently designed to take advantage
|
||||
of Parsoid's linter, could also be extended to linting JS or CSS in the
|
||||
future.
|
15
composer.json
Normal file
15
composer.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"jakub-onderka/php-parallel-lint": "0.9",
|
||||
"mediawiki/mediawiki-codesniffer": "0.8.0-alpha.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": [
|
||||
"parallel-lint . --exclude vendor",
|
||||
"phpcs -p -s"
|
||||
],
|
||||
"fix": [
|
||||
"phpcbf"
|
||||
]
|
||||
}
|
||||
}
|
60
extension.json
Normal file
60
extension.json
Normal file
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "Linter",
|
||||
"license-name": "GPL-2.0+",
|
||||
"url": "https://www.mediawiki.org/wiki/Extension:Linter",
|
||||
"author": [
|
||||
"Kunal Mehta"
|
||||
],
|
||||
"descriptionmsg": "linter-desc",
|
||||
"type": "specialpage",
|
||||
"AutoloadClasses": {
|
||||
"MediaWiki\\Linter\\Hooks": "includes/Hooks.php",
|
||||
"MediaWiki\\Linter\\Database": "includes/Database.php",
|
||||
"MediaWiki\\Linter\\LintError": "includes/LintError.php",
|
||||
"MediaWiki\\Linter\\ApiRecordLint": "includes/ApiRecordLint.php",
|
||||
"MediaWiki\\Linter\\ApiQueryLintErrors": "includes/ApiQueryLintErrors.php",
|
||||
"MediaWiki\\Linter\\SpecialLintErrors": "includes/SpecialLintErrors.php",
|
||||
"MediaWiki\\Linter\\LintErrorsPager": "includes/LintErrorsPager.php"
|
||||
},
|
||||
"MessagesDirs": {
|
||||
"Linter": "i18n"
|
||||
},
|
||||
"Hooks": {
|
||||
"LoadExtensionSchemaUpdates": "MediaWiki\\Linter\\Hooks::onLoadExtensionSchemaUpdates",
|
||||
"EditFormInitialText": "MediaWiki\\Linter\\Hooks::onEditFormInitialText"
|
||||
},
|
||||
"APIModules": {
|
||||
"record-lint": "MediaWiki\\Linter\\ApiRecordLint"
|
||||
},
|
||||
"APIListModules": {
|
||||
"linterrors": "MediaWiki\\Linter\\ApiQueryLintErrors"
|
||||
},
|
||||
"SpecialPages": {
|
||||
"LintErrors": "MediaWiki\\Linter\\SpecialLintErrors"
|
||||
},
|
||||
"ResourceModules": {
|
||||
"ext.linter.edit": {
|
||||
"scripts": "ext.linter.edit.js",
|
||||
"dependencies": [
|
||||
"jquery.textSelection"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ResourceFileModulePaths": {
|
||||
"localBasePath": "modules",
|
||||
"remoteExtPath": "Linter/modules"
|
||||
},
|
||||
"config": {
|
||||
"LinterCategories": {
|
||||
"fostered": true,
|
||||
"obsolete-tag": true,
|
||||
"bogus-image-options": true,
|
||||
"missing-end-tag": true,
|
||||
"stripped-tag": true
|
||||
},
|
||||
"LinterSubmitterWhitelist": {
|
||||
"127.0.0.1": true
|
||||
}
|
||||
},
|
||||
"manifest_version": 1
|
||||
}
|
38
i18n/en.json
Normal file
38
i18n/en.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Kunal Mehta"
|
||||
]
|
||||
},
|
||||
"linterrors": "Lint errors",
|
||||
"linterrors-subpage": "Lint errors: $1",
|
||||
"linter-desc": "Track lint errors from an external service and show them to users",
|
||||
"linter-pager-title": "Page title",
|
||||
"linter-pager-template": "Through a template?",
|
||||
"linter-pager-obsolete-tag-details": "Obsolete HTML tag",
|
||||
"linter-pager-bogus-image-options-details": "Bogus file option",
|
||||
"linter-pager-missing-end-tag-details": "Missing end tag",
|
||||
"linter-pager-stripped-tag-details": "Stripped tag",
|
||||
"linter-category-fostered": "Fostered content",
|
||||
"linter-category-fostered-desc": "These pages have fostered content.",
|
||||
"linter-category-obsolete-tag": "Obsolete HTML tags",
|
||||
"linter-category-obsolete-tag-desc": "These pages use obsolete HTML tags.",
|
||||
"linter-category-bogus-image-options": "Bogus file options",
|
||||
"linter-category-bogus-image-options-desc": "These pages have files with bogus options.",
|
||||
"linter-category-missing-end-tag": "Missing end tag",
|
||||
"linter-category-missing-end-tag-desc": "These pages have missing end tags.",
|
||||
"linter-category-stripped-tag": "Stripped tags",
|
||||
"linter-category-stripped-tag-desc": "These pages have stripped tags.",
|
||||
"linker-page-title-edit": "$1 ($2)",
|
||||
"linker-page-edit": "edit",
|
||||
"apihelp-query+linterrors-description": "Get a list of lint errors",
|
||||
"apihelp-query+linterrors-param-category": "Category of lint errors",
|
||||
"apihelp-query+linterrors-param-limit": "Number of results to query",
|
||||
"apihelp-query+linterrors-param-from": "Lint ID to start querying from",
|
||||
"apihelp-query+linterrors-param-namespace": "Only include lint errors from the specified namespaces",
|
||||
"apihelp-query+linterrors-example-1": "Get all lint errors of the obsolete-tag category",
|
||||
"apihelp-record-lint-description": "Record a lint error in the database",
|
||||
"apihelp-record-lint-param-data": "JSON encoded data about the error",
|
||||
"apihelp-record-lint-param-page": "Page title",
|
||||
"apihelp-record-lint-param-revision": "Revision ID that the error was found in"
|
||||
}
|
38
i18n/qqq.json
Normal file
38
i18n/qqq.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"Kunal Mehta"
|
||||
]
|
||||
},
|
||||
"linterrors": "{{doc-special|LintErrors}}",
|
||||
"linterrors-subpage": "Page title for Special:LintErrors, $1 is the localized category description",
|
||||
"linter-desc": "{{desc|name=Linter|url=https://www.mediawiki.org/wiki/Extension:Linter}}",
|
||||
"linter-pager-title": "Table column heading for the column with page titles in it",
|
||||
"linter-pager-template": "Table column heading for column with template title in it or an em dash if lint error is not inside a template",
|
||||
"linter-pager-obsolete-tag-details": "Table column heading",
|
||||
"linter-pager-bogus-image-options-details": "Table column heading",
|
||||
"linter-pager-missing-end-tag-details": "Table column heading",
|
||||
"linter-pager-stripped-tag-details": "Table column heading",
|
||||
"linter-category-fostered": "Name of lint error category",
|
||||
"linter-category-fostered-desc": "Description of category.",
|
||||
"linter-category-obsolete-tag": "Name of lint error category",
|
||||
"linter-category-obsolete-tag-desc": "Description of category.",
|
||||
"linter-category-bogus-image-options": "Name of lint error category",
|
||||
"linter-category-bogus-image-options-desc": "Description of category",
|
||||
"linter-category-missing-end-tag": "Name of lint error category",
|
||||
"linter-category-missing-end-tag-desc": "Description of category",
|
||||
"linter-category-stripped-tag": "Name of lint error category",
|
||||
"linter-category-stripped-tag-desc": "Description of category",
|
||||
"linker-page-title-edit": "Used in a table cell. $1 is a link to the page, $2 is a link to edit that page, the link text is {{msg-mw|linker-page-edit}}",
|
||||
"linker-page-edit": "Link text for edit link in {{msg-mw|linker-page-title-edit}} and {{msg-mw|linker-page-title-edit-template}}",
|
||||
"apihelp-query+linterrors-description": "{{doc-apihelp-description|query+linterrors}}",
|
||||
"apihelp-query+linterrors-param-category": "{{doc-apihelp-param|query+linterrors|category}}",
|
||||
"apihelp-query+linterrors-param-limit": "{{doc-apihelp-param|query+linterrors|limit}}",
|
||||
"apihelp-query+linterrors-param-from": "{{doc-apihelp-param|query+linterrors|from}}",
|
||||
"apihelp-query+linterrors-param-namespace": "{{doc-apihelp-param|query+linterrors|namespace}}",
|
||||
"apihelp-query+linterrors-example-1": "{{doc-apihelp-example|query+linterrors}}",
|
||||
"apihelp-record-lint-description": "{{doc-apihelp-description|record-lint}}",
|
||||
"apihelp-record-lint-param-data": "{{doc-apihelp-param|record-lint|data}}",
|
||||
"apihelp-record-lint-param-page": "{{doc-apihelp-param|record-lint|page}}",
|
||||
"apihelp-record-lint-param-revision": "{{doc-apihelp-param|record-lint|revision}}"
|
||||
}
|
127
includes/ApiQueryLintErrors.php
Normal file
127
includes/ApiQueryLintErrors.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use ApiBase;
|
||||
use ApiQuery;
|
||||
use ApiQueryBase;
|
||||
use ApiResult;
|
||||
use Title;
|
||||
|
||||
class ApiQueryLintErrors extends ApiQueryBase {
|
||||
public function __construct( ApiQuery $queryModule, $moduleName ) {
|
||||
parent::__construct( $queryModule, $moduleName, 'lnt' );
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$params = $this->extractRequestParams();
|
||||
|
||||
$this->addTables( 'linter' );
|
||||
if ( $params['category'] !== null ) {
|
||||
$this->addWhereFld( 'linter_cat', $params['category'] );
|
||||
} else {
|
||||
// Limit only to enabled categories (there might be others in the DB)
|
||||
$this->addWhereFld( 'linter_cat', $this->getCategories() );
|
||||
}
|
||||
$db = $this->getDB();
|
||||
if ( $params['from'] !== null ) {
|
||||
$this->addWhere( 'linter_id >= ' . $db->addQuotes( $params['from'] ) );
|
||||
}
|
||||
if ( $params['namespace'] !== null ) {
|
||||
$this->addWhereFld( 'page_namespace', $params['namespace'] );
|
||||
}
|
||||
$this->addTables( 'page' );
|
||||
$this->addJoinConds( [ 'page' => [ 'INNER JOIN', 'page_id=linter_page' ] ] );
|
||||
$this->addFields( [
|
||||
'linter_id', 'linter_cat', 'linter_params',
|
||||
'page_namespace', 'page_title',
|
||||
] );
|
||||
// Be explicit about ORDER BY
|
||||
$this->addOption( 'ORDER BY', 'linter_id' );
|
||||
// Add +1 to limit to know if there's another row for continuation
|
||||
$this->addOption( 'LIMIT', $params['limit'] + 1 );
|
||||
$rows = $this->select( __METHOD__ );
|
||||
$result = $this->getResult();
|
||||
$count = 0;
|
||||
foreach ( $rows as $row ) {
|
||||
$lintError = Database::makeLintError( $row );
|
||||
$count++;
|
||||
if ( $count > $params['limit'] ) {
|
||||
$this->setContinueEnumParameter( 'from', $lintError->lintId );
|
||||
break;
|
||||
}
|
||||
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
|
||||
|
||||
$data = [
|
||||
'title' => $title->getPrefixedText(),
|
||||
'lintId' => $lintError->lintId,
|
||||
'category' => $lintError->category,
|
||||
'location' => $lintError->location,
|
||||
'templateInfo' => $lintError->templateInfo,
|
||||
'params' => $lintError->getExtraParams(),
|
||||
];
|
||||
// template info and params are an object
|
||||
$data['params'][ApiResult::META_TYPE] = 'assoc';
|
||||
$data['templateInfo'][ApiResult::META_TYPE] = 'assoc';
|
||||
|
||||
$fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $data );
|
||||
if ( !$fit ) {
|
||||
$this->setContinueEnumParameter( 'from', $lintError->lintId );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getCategories() {
|
||||
global $wgLinterCategories;
|
||||
return array_keys( array_filter( $wgLinterCategories ) );
|
||||
}
|
||||
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
'category' => [
|
||||
ApiBase::PARAM_TYPE => $this->getCategories(),
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
],
|
||||
'limit' => [
|
||||
ApiBase::PARAM_DFLT => 10,
|
||||
ApiBase::PARAM_TYPE => 'limit',
|
||||
ApiBase::PARAM_MIN => 1,
|
||||
ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
|
||||
ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
|
||||
],
|
||||
'namespace' => [
|
||||
ApiBase::PARAM_TYPE => 'namespace',
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
],
|
||||
'from' => [
|
||||
ApiBase::PARAM_TYPE => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getExamplesMessages() {
|
||||
return [
|
||||
'action=query&list=linterrors&lecategory=obsolete-tag' =>
|
||||
'apihelp-query+linterrors-example-1',
|
||||
];
|
||||
}
|
||||
}
|
86
includes/ApiRecordLint.php
Normal file
86
includes/ApiRecordLint.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use ApiBase;
|
||||
use FormatJson;
|
||||
use IPSet\IPSet;
|
||||
use Title;
|
||||
|
||||
/**
|
||||
* API module for an external service to record
|
||||
* a lint error
|
||||
*/
|
||||
class ApiRecordLint extends ApiBase {
|
||||
|
||||
public function execute() {
|
||||
global $wgLinterSubmitterWhitelist;
|
||||
|
||||
$ipSet = new IPSet(
|
||||
array_keys( array_filter( $wgLinterSubmitterWhitelist ) )
|
||||
);
|
||||
if ( !$ipSet->match( $this->getRequest()->getIP() ) ) {
|
||||
$this->dieUsage(
|
||||
'Your IP address has not been whitelisted to report lint errors',
|
||||
'invalid-ip'
|
||||
);
|
||||
}
|
||||
$params = $this->extractRequestParams();
|
||||
$data = FormatJson::decode( $params['data'], true );
|
||||
if ( !is_array( $data ) ) {
|
||||
$this->dieUsage( 'Invalid data', 'invalid-data' );
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$title = Title::newFromText( $params['page'] );
|
||||
if ( !$title || !$title->getArticleID()
|
||||
|| $title->getLatestRevID() != $params['revision']
|
||||
) {
|
||||
$this->dieUsage( 'Invalid, non-existent, or outdated title', 'invalid-title' );
|
||||
}
|
||||
foreach ( $data as $info ) {
|
||||
$info['params']['location'] = array_slice( $info['dsr'], 0, 2 );
|
||||
if ( isset( $info['templateInfo'] ) && $info['templateInfo'] ) {
|
||||
$info['params']['templateInfo'] = $info['templateInfo'];
|
||||
}
|
||||
$errors[] = new LintError(
|
||||
$info['type'],
|
||||
$info['params']
|
||||
);
|
||||
}
|
||||
|
||||
$lintDb = new Database( $title->getArticleID() );
|
||||
$result = $lintDb->setForPage( $errors );
|
||||
$this->getResult()->addValue( $this->getModuleName(), 'success', $result );
|
||||
}
|
||||
|
||||
public function isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
'data' => 'string',
|
||||
'page' => 'string',
|
||||
'revision' => 'int',
|
||||
];
|
||||
}
|
||||
}
|
191
includes/Database.php
Normal file
191
includes/Database.php
Normal file
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use DBAccessObjectUtils;
|
||||
use IDBAccessObject;
|
||||
use FormatJson;
|
||||
|
||||
/**
|
||||
* Database logic
|
||||
*/
|
||||
class Database implements IDBAccessObject {
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $pageId;
|
||||
|
||||
/**
|
||||
* @param int $pageId
|
||||
*/
|
||||
public function __construct( $pageId ) {
|
||||
$this->pageId = $pageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific LintError by id
|
||||
*
|
||||
* @param int $id linter_id
|
||||
* @param int $flags
|
||||
* @return bool|LintError
|
||||
*/
|
||||
public function getFromId( $id, $flags = 0 ) {
|
||||
list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
|
||||
$row = wfGetDB( $index )->selectRow(
|
||||
'linter',
|
||||
[ 'linter_cat', 'linter_params' ],
|
||||
[ 'linter_id' => $id, 'linter_page' => $this->pageId ],
|
||||
__METHOD__,
|
||||
$options
|
||||
);
|
||||
|
||||
if ( $row ) {
|
||||
$row->linter_id = $id;
|
||||
return $this->makeLintError( $row );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a database row into a LintError object
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return LintError
|
||||
*/
|
||||
public static function makeLintError( $row ) {
|
||||
return new LintError(
|
||||
$row->linter_cat,
|
||||
$row->linter_params,
|
||||
(int)$row->linter_id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the lint errors for a page
|
||||
*
|
||||
* @param int $flags
|
||||
* @return LintError[]
|
||||
*/
|
||||
public function getForPage( $flags = 0 ) {
|
||||
list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
|
||||
$rows = wfGetDB( $index )->select(
|
||||
'linter',
|
||||
[ 'linter_id', 'linter_cat', 'linter_params' ],
|
||||
[ 'linter_page' => $this->pageId ],
|
||||
__METHOD__,
|
||||
$options
|
||||
);
|
||||
$result = [];
|
||||
foreach ( $rows as $row ) {
|
||||
$error = $this->makeLintError( $row );
|
||||
$result[$error->id()] = $error;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a LintError object into an array for
|
||||
* inserting/querying in the database
|
||||
*
|
||||
* @param LintError $error
|
||||
* @return array
|
||||
*/
|
||||
private function serializeError( LintError $error ) {
|
||||
if ( $error->lintId !== 0 ) {
|
||||
return [
|
||||
'linter_id' => $error->lintId,
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'linter_page' => $this->pageId,
|
||||
'linter_cat' => $error->category,
|
||||
'linter_params' => FormatJson::encode( $error->params, false, FormatJson::ALL_OK ),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the specified lint errors in the
|
||||
* database
|
||||
*
|
||||
* @param LintError[] $errors
|
||||
* @return array [ 'deleted' => int|bool, 'added' => int ]
|
||||
*/
|
||||
public function setForPage( $errors ) {
|
||||
$previous = $this->getForPage( self::READ_LATEST );
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
if ( !$previous && !$errors ) {
|
||||
return [ 'deleted' => 0, 'added' => 0 ];
|
||||
} elseif ( !$previous && $errors ) {
|
||||
$toInsert = $errors;
|
||||
$toDelete = [];
|
||||
} elseif ( $previous && !$errors ) {
|
||||
$dbw->delete(
|
||||
'linter',
|
||||
[ 'linter_page' => $this->pageId ],
|
||||
__METHOD__
|
||||
);
|
||||
return [ 'deleted' => true, 'added' => 0 ];
|
||||
} else {
|
||||
$toInsert = [];
|
||||
$toDelete = $previous;
|
||||
// Diff previous and errors
|
||||
foreach ( $errors as $error ) {
|
||||
$uniqueId = $error->id();
|
||||
if ( isset( $previous[$uniqueId] ) ) {
|
||||
unset( $toDelete[$uniqueId] );
|
||||
} else {
|
||||
$toInsert[] = $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $toDelete ) {
|
||||
$ids = [];
|
||||
foreach ( $toDelete as $lintError ) {
|
||||
if ( $lintError->lintId ) {
|
||||
$ids[] = $lintError->lintId;
|
||||
}
|
||||
}
|
||||
$dbw->delete(
|
||||
'linter',
|
||||
[ 'linter_id' => $ids ],
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
|
||||
if ( $toInsert ) {
|
||||
$dbw->insert(
|
||||
'linter',
|
||||
array_map( [ $this, 'serializeError' ], $toInsert ),
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'deleted' => count( $toDelete ),
|
||||
'added' => count( $toInsert ),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
66
includes/Hooks.php
Normal file
66
includes/Hooks.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use DatabaseUpdater;
|
||||
use EditPage;
|
||||
|
||||
class Hooks {
|
||||
/**
|
||||
* @param DatabaseUpdater $updater
|
||||
*/
|
||||
public static function onLoadExtensionSchemaUpdates( DatabaseUpdater $updater ) {
|
||||
$updater->addExtensionTable(
|
||||
'linter', dirname( __DIR__ ) . '/linter.sql'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook: EditFormInitialText
|
||||
*
|
||||
* If there is a lintid parameter, look up that error in the database
|
||||
* and setup and output our client-side helpers
|
||||
*
|
||||
* @param EditPage $editPage
|
||||
*/
|
||||
public static function onEditFormInitialText( EditPage $editPage ) {
|
||||
$context = $editPage->getContext();
|
||||
$request = $context->getRequest();
|
||||
$lintId = $request->getInt( 'lintid' );
|
||||
if ( !$lintId ) {
|
||||
return;
|
||||
}
|
||||
$title = $editPage->getTitle();
|
||||
|
||||
$lintError = ( new Database( $title->getArticleID() ) )->getFromId( $lintId );
|
||||
if ( !$lintError ) {
|
||||
// Already fixed or bogus URL parameter?
|
||||
return;
|
||||
}
|
||||
|
||||
$out = $context->getOutput();
|
||||
$out->addJsConfigVars( [
|
||||
'wgLinterErrorCategory' => $lintError->category,
|
||||
'wgLinterErrorLocation' => $lintError->location,
|
||||
] );
|
||||
$out->addModules( 'ext.linter.edit' );
|
||||
}
|
||||
}
|
105
includes/LintError.php
Normal file
105
includes/LintError.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use FormatJson;
|
||||
|
||||
/**
|
||||
* Model to represent a LintError
|
||||
*/
|
||||
class LintError {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $category;
|
||||
|
||||
/**
|
||||
* @var int[] [ start, end ]
|
||||
*/
|
||||
public $location;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $lintId;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $params;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $templateInfo;
|
||||
|
||||
/**
|
||||
* @param string $category
|
||||
* @param string|array $params JSON string or already decoded array
|
||||
* @param int $lintId linter_id
|
||||
*/
|
||||
public function __construct( $category, $params, $lintId = 0 ) {
|
||||
$this->category = $category;
|
||||
if ( is_string( $params ) ) {
|
||||
$params = FormatJson::decode( $params, true );
|
||||
}
|
||||
$this->params = $params;
|
||||
$this->lintId = $lintId;
|
||||
// Convenient accessors for all errors
|
||||
$this->location = $params['location'];
|
||||
$this->templateInfo = isset( $params['templateInfo'] )
|
||||
? $params['templateInfo'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LintError $other
|
||||
* @return bool
|
||||
*/
|
||||
public function equals( LintError $other ) {
|
||||
return $this->category === $other->category
|
||||
&& $this->params === $other->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique id to identify this error, for internal
|
||||
* purposes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id() {
|
||||
return $this->category . FormatJson::encode( $this->params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the params that are extra for this error,
|
||||
* not part of the default set
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtraParams() {
|
||||
$params = $this->params;
|
||||
unset( $params['templateInfo'] );
|
||||
unset( $params['location'] );
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
}
|
137
includes/LintErrorsPager.php
Normal file
137
includes/LintErrorsPager.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use IContextSource;
|
||||
use InvalidArgumentException;
|
||||
use LinkCache;
|
||||
use Html;
|
||||
use MediaWiki\Linker\LinkRenderer;
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use TablePager;
|
||||
use TitleValue;
|
||||
use Title;
|
||||
|
||||
class LintErrorsPager extends TablePager {
|
||||
|
||||
private $category;
|
||||
/**
|
||||
* @var LinkRenderer
|
||||
*/
|
||||
private $linkRenderer;
|
||||
|
||||
public function __construct( IContextSource $context, $category, LinkRenderer $linkRenderer ) {
|
||||
$this->category = $category;
|
||||
$this->linkRenderer = $linkRenderer;
|
||||
parent::__construct( $context );
|
||||
}
|
||||
|
||||
public function getQueryInfo() {
|
||||
return [
|
||||
'tables' => [ 'page', 'linter' ],
|
||||
'fields' => array_merge(
|
||||
LinkCache::getSelectFields(),
|
||||
[
|
||||
'page_namespace', 'page_title',
|
||||
'linter_id', 'linter_params',
|
||||
]
|
||||
),
|
||||
'conds' => [ 'linter_cat' => $this->category ],
|
||||
'join_conds' => [ 'page' => [ 'INNER JOIN', 'page_id=linter_page' ] ]
|
||||
];
|
||||
}
|
||||
|
||||
protected function doBatchLookups() {
|
||||
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
||||
foreach ( $this->mResult as $row ) {
|
||||
$titleValue = new TitleValue( (int)$row->page_namespace, $row->page_title );
|
||||
$linkCache->addGoodLinkObjFromRow( $titleValue, $row );
|
||||
}
|
||||
}
|
||||
|
||||
public function isFieldSortable( $field ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function formatValue( $name, $value ) {
|
||||
$row = $this->mCurrentRow;
|
||||
$row->linter_cat = $this->category;
|
||||
$lintError = Database::makeLintError( $row );
|
||||
switch ( $name ) {
|
||||
case 'title':
|
||||
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
|
||||
$viewLink = $this->linkRenderer->makeLink( $title );
|
||||
if ( !$title->quickUserCan( 'edit', $this->getUser() ) ) {
|
||||
return $viewLink;
|
||||
}
|
||||
$editLink = $this->linkRenderer->makeLink(
|
||||
$title,
|
||||
$this->msg( 'linker-page-edit' )->text(),
|
||||
[],
|
||||
[ 'action' => 'edit', 'lintid' => $lintError->lintId, ]
|
||||
);
|
||||
|
||||
return $this->msg( 'linker-page-title-edit' )->rawParams( $viewLink, $editLink )->escaped();
|
||||
case 'details':
|
||||
if ( $this->category === 'obsolete-tag' && isset( $lintError->params['name'] ) ) {
|
||||
return Html::element( 'code', [], $lintError->params['name'] );
|
||||
} elseif ( $this->category === 'bogus-image-options' && isset( $lintError->params['items'] ) ) {
|
||||
$list = array_map( function( $in ) {
|
||||
return Html::element( 'code', [], $in );
|
||||
}, $lintError->params['items'] );
|
||||
return $this->getLanguage()->commaList( $list );
|
||||
}
|
||||
return '';
|
||||
case 'template':
|
||||
if ( !$lintError->templateInfo ) {
|
||||
return '—';
|
||||
}
|
||||
$templateName = $lintError->templateInfo['name'];
|
||||
$templateTitle = Title::newFromText( $templateName, NS_TEMPLATE );
|
||||
if ( !$templateTitle ) {
|
||||
// Shouldn't be possible...???
|
||||
return '&mdash';
|
||||
}
|
||||
|
||||
return $this->linkRenderer->makeLink(
|
||||
$templateTitle
|
||||
);
|
||||
default:
|
||||
throw new InvalidArgumentException( "Unexpected name: $name" );
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefaultSort() {
|
||||
return 'linter_id';
|
||||
}
|
||||
|
||||
public function getFieldNames() {
|
||||
$names = [
|
||||
'title' => $this->msg( 'linter-pager-title' ),
|
||||
];
|
||||
if ( $this->category !== 'fostered' ) {
|
||||
// TODO: don't hardcode list of stuff with no parameters...?
|
||||
$names['details'] = $this->msg( "linter-pager-{$this->category}-details" );
|
||||
}
|
||||
$names['template'] = $this->msg( "linter-pager-template" );
|
||||
return $names;
|
||||
}
|
||||
}
|
88
includes/SpecialLintErrors.php
Normal file
88
includes/SpecialLintErrors.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
|
||||
namespace MediaWiki\Linter;
|
||||
|
||||
use Html;
|
||||
use SpecialPage;
|
||||
|
||||
class SpecialLintErrors extends SpecialPage {
|
||||
|
||||
private $category;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct( 'LintErrors' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function getCategories() {
|
||||
global $wgLinterCategories;
|
||||
return array_keys( array_filter( $wgLinterCategories ) );
|
||||
}
|
||||
|
||||
public function execute( $par ) {
|
||||
$this->setHeaders();
|
||||
$this->outputHeader();
|
||||
$cats = $this->getCategories();
|
||||
if ( in_array( $par, $cats ) ) {
|
||||
$this->category = $par;
|
||||
}
|
||||
|
||||
if ( !$this->category ) {
|
||||
$this->addHelpLink( 'Help:Extension:Linter' );
|
||||
$this->showCategoryList( $cats );
|
||||
} else {
|
||||
$this->addHelpLink( "Help:Extension:Linter/{$this->category}" );
|
||||
$out = $this->getOutput();
|
||||
$out->setPageTitle(
|
||||
$this->msg( 'linterrors-subpage',
|
||||
$this->msg( "linter-category-{$this->category}" )->text()
|
||||
)
|
||||
);
|
||||
$out->addBacklinkSubtitle( $this->getPageTitle() );
|
||||
$out->addWikiMsg( "linter-category-{$this->category}-desc" );
|
||||
$pager = new LintErrorsPager(
|
||||
$this->getContext(), $this->category, $this->getLinkRenderer()
|
||||
);
|
||||
$out->addParserOutput( $pager->getFullOutput() );
|
||||
}
|
||||
}
|
||||
|
||||
private function showCategoryList( array $cats ) {
|
||||
$linkRenderer = $this->getLinkRenderer();
|
||||
$html = Html::openElement( 'ul' ) . "\n";
|
||||
sort( $cats );
|
||||
foreach ( $cats as $cat ) {
|
||||
$html .= Html::rawElement( 'li', [], $linkRenderer->makeKnownLink(
|
||||
$this->getPageTitle( $cat ),
|
||||
$this->msg( "linter-category-$cat" )->text()
|
||||
) ) . "\n";
|
||||
}
|
||||
$html .= Html::closeElement( 'ul' );
|
||||
$this->getOutput()->addHTML( $html );
|
||||
}
|
||||
|
||||
public function getGroupName() {
|
||||
return 'maintenance';
|
||||
}
|
||||
|
||||
}
|
13
linter.sql
Normal file
13
linter.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
CREATE TABLE /*_*/linter (
|
||||
-- primary key
|
||||
linter_id int UNSIGNED AUTO_INCREMENT PRIMARY KEY not null,
|
||||
-- page id
|
||||
linter_page int UNSIGNED not null,
|
||||
-- error category
|
||||
linter_cat VARCHAR(30) not null,
|
||||
-- extra parameters about the error, JSON encoded
|
||||
linter_params blob NOT NULL
|
||||
) /*$wgDBTableOptions*/;
|
||||
|
||||
-- Query by page
|
||||
CREATE INDEX /*i*/linter_page ON /*_*/linter (linter_page);
|
8
modules/ext.linter.edit.js
Normal file
8
modules/ext.linter.edit.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
( function ( mw, $ ) {
|
||||
$( function () {
|
||||
var location = mw.config.get( 'wgLinterErrorLocation' );
|
||||
if ( location ) {
|
||||
$( '#wpTextbox1' ).focus().textSelection( 'setSelection', { start: location[ 0 ], end: location[ 1 ] } );
|
||||
}
|
||||
} );
|
||||
}( mediaWiki, jQuery ) );
|
13
package.json
Normal file
13
package.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "0.4.5",
|
||||
"grunt-cli": "0.1.13",
|
||||
"grunt-contrib-jshint": "0.11.3",
|
||||
"grunt-banana-checker": "0.4.0",
|
||||
"grunt-jscs": "2.2.0",
|
||||
"grunt-jsonlint": "1.0.7"
|
||||
}
|
||||
}
|
14
phpcs.xml
Normal file
14
phpcs.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset>
|
||||
<rule ref="vendor/mediawiki/mediawiki-codesniffer/MediaWiki"/>
|
||||
<file>.</file>
|
||||
<rule ref="MediaWiki.Commenting.FunctionComment.Missing">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<rule ref="MediaWiki.Commenting.FunctionComment.MissingParamComment">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
<arg name="extensions" value="php,php5,inc"/>
|
||||
<arg name="encoding" value="utf8"/>
|
||||
<exclude-pattern>vendor</exclude-pattern>
|
||||
</ruleset>
|
Loading…
Reference in a new issue