Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: PHPUnit / PHPCS / Phan
on:
pull_request:
branches: [ "*" ]

push:
branches: [ main ]

jobs:
build:
strategy:
matrix:
php_version: ['8.2']
mw: ['REL1_43', 'REL1_44', 'master']

runs-on: ubuntu-latest
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_version }}
coverage: none
extensions: ast
- uses: actions/checkout@v3

- name: Checkout Mediawiki
uses: actions/checkout@v3
with:
repository: wikimedia/mediawiki
ref: ${{ matrix.mw }}

- name: Checkout TableProgressTracking extension
uses: actions/checkout@v3
with:
path: extensions/TableProgressTracking

- name: Cache TableProgressTracking composer dependencies
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-v3-${{ hashFiles('extensions/TableProgressTracking/composer.lock') }}

- name: Install TableProgresssTracking composer dependencies
working-directory: ./extensions/TableProgressTracking
run: composer install --prefer-dist --no-progress

- name: Run PHPCS and minus-x
working-directory: ./extensions/TableProgressTracking
run: composer test

- name: Run Phan static analysis
working-directory: ./extensions/TableProgressTracking
run: composer phan

- name: Start MySQL
run: sudo systemctl start mysql.service

- name: Install MediaWiki composer dependencies
run: composer update --prefer-dist --no-progress

- name: Install & configure MediaWiki
run: |
php maintenance/install.php --dbtype mysql --dbuser root --dbpass root --pass TestPassword testwiki TestAdmin

echo 'error_reporting( E_ALL | E_STRICT );' >> LocalSettings.php
echo 'ini_set( "display_errors", 1 );' >> LocalSettings.php
echo '$wgShowExceptionDetails = true;' >> LocalSettings.php
echo '$wgShowDBErrorBacktrace = true;' >> LocalSettings.php
echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php

echo 'wfLoadExtension( "TableProgressTracking" );' >> LocalSettings.php

- name: Run parser tests
run: |
for file in extensions/TableProgressTracking/tests/parser/*.txt; do
echo "Running parser tests from $file"
php tests/parser/parserTests.php --file="$file"
done
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
vendor
composer.lock
7 changes: 7 additions & 0 deletions .phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<ruleset>
<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki" />
<file>.</file>
<arg name="extensions" value="php"/>
<arg name="encoding" value="UTF-8"/>
</ruleset>
27 changes: 27 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"require-dev": {
"mediawiki/mediawiki-codesniffer": "46.0.0",
"mediawiki/mediawiki-phan-config": "0.15.1",
"mediawiki/minus-x": "1.1.3",
"php-parallel-lint/php-console-highlighter": "1.0.0",
"php-parallel-lint/php-parallel-lint": "1.4.0"
},
"scripts": {
"test": [
"parallel-lint . --exclude vendor --exclude node_modules",
"@phpcs",
"minus-x check ."
],
"fix": [
"minus-x fix .",
"phpcbf"
],
"phan": "phan -d . --long-progress-bar",
"phpcs": "phpcs -sp --cache"
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
2 changes: 1 addition & 1 deletion extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"descriptionmsg": "tableprogresstracking-desc",
"license-name": "Apache-2.0",
"type": "parserhook",
"version": "1.1.1",
"version": "1.2.0",
"requires": {
"MediaWiki": ">= 1.43.0"
},
Expand Down
8 changes: 7 additions & 1 deletion includes/Hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
use MediaWiki\Installer\Hook\LoadExtensionSchemaUpdatesHook;
use MediaWiki\Storage\Hook\MultiContentSaveHook;

class Hooks implements ParserFirstCallInitHook, LoadExtensionSchemaUpdatesHook, MultiContentSaveHook {
class Hooks implements
ParserFirstCallInitHook,
LoadExtensionSchemaUpdatesHook,
MultiContentSaveHook
{
/**
* @inheritDoc
*
Expand Down Expand Up @@ -40,5 +44,7 @@ public function onMultiContentSave( $renderedRevision, $user, $summary, $flags,
$status->fatal( 'tableprogresstracking-duplicate-tables' );
return false;
}

return true;
}
}
32 changes: 20 additions & 12 deletions includes/ProgressTableProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use MediaWiki\Html\Html;
use MediaWiki\Parser\Parser;
use MediaWiki\Parser\PPFrame;
use Wikimedia\AtEase\AtEase;

class ProgressTableProcessor {

Expand Down Expand Up @@ -70,13 +71,13 @@ public function __construct( string $wikitext, array $args, Parser $parser, PPFr

// Only set the unique column index if it is provided in the arguments
// if not, we validate later that each row passes its own data-row-id
// note we must - 1 from the value the user passsed as an argument as
// note we must - 1 from the value the user passsed as an argument as
// DOMNodeList::item() is zero-based and if the user passed 1 wanting the first column
// they would get the second
if ( isset( $this->args['unique-column-index'] ) ) {
$this->uniqueColumnIndex = intval( $this->args['unique-column-index'] ) - 1;
}

// check the table-id argument is set, if not, we can't do much herer
if ( empty( $this->args['table-id'] ) ) {
$this->errorMessage = 'The table-id argument is required.';
Expand All @@ -94,7 +95,7 @@ public function __construct( string $wikitext, array $args, Parser $parser, PPFr
private function loadAndValidateHtml(): void {
// first parse our wikitext so we can get the HTML representation if it;
// we use ->recursiveTagParseFully here as we need the final HTML version of the
// table so that we can ensure if unique-column-index is used, and the content of the
// table so that we can ensure if unique-column-index is used, and the content of the
// cell is a link, or any other HTML code, such as bold, then we get the right content
// in the data-row-id. If we use ->recursiveTagParse(), then we end up with parser strip tags
// such as <!--LINK'" 0:0--> and there is no easy way to get the link object from the
Expand All @@ -110,16 +111,19 @@ private function loadAndValidateHtml(): void {

// Suppress warnings for potentially malformed HTML from wikitext.
// there must be a better way to do this?!! Can't find it at present, though?!>!?!
@$this->dom->loadHTML(
AtEase::suppressWarnings();
$this->dom->loadHTML(
mb_convert_encoding( $tableHtml, 'HTML-ENTITIES', 'UTF-8' ),
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
);
AtEase::restoreWarnings();

$tableNode = $this->dom->getElementsByTagName( 'table' )->item( 0 );

if ( !$tableNode ) {
$this->parser->getOutput()->updateCacheExpiry( 0 );
$this->errorMessage = 'No table was provided for progress tracking. Please include a table between the <table-progress-tracking> tags.';
$this->errorMessage = 'No table was provided for progress tracking.' .
'Please include a table between the <table-progress-tracking> tags.';
return;
}

Expand All @@ -133,8 +137,9 @@ private function loadAndValidateHtml(): void {

/**
* Validates that the unique-column-index is within the valid range for the table
* @todo there is an error here, if someone passes wikitext to the column which has the unique-column-index, it falls
* back to the row index.
* @todo there is an error here, if someone passes wikitext to the column which has the unique-column-index,
* it causes the fallback to the row index.
* @return void
*/
private function validateUniqueColumnIndex(): void {
if ( $this->uniqueColumnIndex < 0 ) {
Expand All @@ -153,7 +158,8 @@ private function validateUniqueColumnIndex(): void {
}

if ( $this->uniqueColumnIndex >= $maxColumns ) {
$this->errorMessage = "unique-column-index ({$this->uniqueColumnIndex}) is out of range. Table has {$maxColumns} columns (0-" . ( $maxColumns - 1 ) . ").";
$this->errorMessage = "unique-column-index ({$this->uniqueColumnIndex}) is out of range." .
"Table has {$maxColumns} columns (0-" . ( $maxColumns - 1 ) . ").";
return;
}
}
Expand All @@ -168,7 +174,8 @@ private function validateDataRowIds(): bool {
foreach ( $dataRows as $row ) {
$rowId = $this->extractDataRowId( $row );
if ( empty( $rowId ) ) {
$this->errorMessage = 'When unique-column-index is not provided, all data rows must have a data-row-id attribute.';
$this->errorMessage = 'When unique-column-index is not provided,
all data rows must have a data-row-id attribute.';
return false;
}
}
Expand Down Expand Up @@ -272,7 +279,8 @@ private function addProgressHeader(): void {
*/
private function processDataRows(): void {
$xpath = new DOMXPath( $this->dom );
// this is fucked, but this should be better than just trying to get the tr element with ->getElementByTagName('tr') as that will return all tr elements, including the header ones
// this is fucked, but this should be better than just trying to get the tr element with
// ->getElementByTagName('tr') as that will return all tr elements, including the header ones
$dataRows = $xpath->query( './/tr[not(th)]', $this->table );
$rowIndex = 0;

Expand Down Expand Up @@ -306,8 +314,8 @@ private function addCheckboxCellToRow( DOMElement $row, int $rowIndex ): void {
$checkBoxInput->setAttribute( 'data-row-id', $rowId );
$checkBoxInput->setAttribute( 'id', $rowId );
// disable the checkbox by default, when the JS runs, it will remove the disabled attribute.
// this is to ensure that no checkbox is selected before the JS initialises (or in the case of an unregistered user,
// the checkbox will remain disabled)
// this is to ensure that no checkbox is selected before the JS initialises (or in the case of an unregistered
// user, the checkbox will remain disabled)
$checkBoxInput->setAttribute( 'disabled', 'disabled' );

// empty span for the icon as per:
Expand Down
2 changes: 1 addition & 1 deletion includes/Rest/TrackProgressHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function run( int $articleId, int $tableId ): Response {

$response = $this->getResponseFactory()->create();
$response->setStatus( 201 );

return $response;
}

Expand Down
4 changes: 2 additions & 2 deletions includes/TableGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public static function hasDuplicateTables( RenderedRevision $revision ): bool {
return false;
}

$text= $content->getText();
$text = $content->getText();

// match all <table-progress-tracking> tags with a table-id attribute, in any order
preg_match_all(
preg_match_all(
'/<table-progress-tracking[^>]*\btable-id\s*=\s*["\']?([^"\'>\s]+)["\']?[^>]*>/i',
$text,
$matches
Expand Down
48 changes: 48 additions & 0 deletions tests/parser/singleTable.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
!! version 2
!! hooks
table-progress-tracking
!! endhooks
!! test
A singular table on an article with the default check icon
!! wikitext
<table-progress-tracking table-id="1" unique-column-index="1">
{| class="wikitable"
! Column 1 !! Column 2 !! Column 3
|-
| Row1-1 || Row1-2 || Row1-3
|-
| Row2-1 || Row2-2 || Row2-3
|-
| Row3-1 || Row3-2 || Row3-3
|-
| Row4-1 || Row4-2 || Row4-3
|}
</table-progress-tracking>
!! html
<table class="wikitable progress-tracking-table" data-progress-table-id="1">
<tbody><tr><th><span class="ext-tableProgressTracking-icon-check"></span></th>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3
</th></tr>
<tr data-row-id="Row1-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row1-1" id="Row1-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row1-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row1-1</td>
<td>Row1-2</td>
<td>Row1-3
</td></tr>
<tr data-row-id="Row2-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row2-1" id="Row2-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row2-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row2-1</td>
<td>Row2-2</td>
<td>Row2-3
</td></tr>
<tr data-row-id="Row3-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row3-1" id="Row3-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row3-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row3-1</td>
<td>Row3-2</td>
<td>Row3-3
</td></tr>
<tr data-row-id="Row4-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row4-1" id="Row4-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row4-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row4-1</td>
<td>Row4-2</td>
<td>Row4-3
</td></tr></tbody></table>
!! end
48 changes: 48 additions & 0 deletions tests/parser/tableWithCustomHeader.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
!! version 2
!! hooks
table-progress-tracking
!! endhooks
!! test
A table with a customised header
!! wikitext
<table-progress-tracking table-id="1" unique-column-index="1" header-label="Title">
{| class="wikitable"
! Column 1 !! Column 2 !! Column 3
|-
| Row1-1 || Row1-2 || Row1-3
|-
| Row2-1 || Row2-2 || Row2-3
|-
| Row3-1 || Row3-2 || Row3-3
|-
| Row4-1 || Row4-2 || Row4-3
|}
</table-progress-tracking>
!! html
<table class="wikitable progress-tracking-table" data-progress-table-id="1">
<tbody><tr><th>Title</th>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3
</th></tr>
<tr data-row-id="Row1-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row1-1" id="Row1-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row1-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row1-1</td>
<td>Row1-2</td>
<td>Row1-3
</td></tr>
<tr data-row-id="Row2-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row2-1" id="Row2-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row2-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row2-1</td>
<td>Row2-2</td>
<td>Row2-3
</td></tr>
<tr data-row-id="Row3-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row3-1" id="Row3-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row3-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row3-1</td>
<td>Row3-2</td>
<td>Row3-3
</td></tr>
<tr data-row-id="Row4-1"><td class="progress-tracker-checkbox-cell"><div class="cdx-checkbox"><div class="cdx-checkbox__wrapper"><input type="checkbox" class="cdx-checkbox__input" data-row-id="Row4-1" id="Row4-1" disabled="" /><span class="cdx-checkbox__icon"></span><div class="cdx-checkbox__label cdx-label"><label for="Row4-1" class="cdx-label__label"><span class="cdx-label__label__text"> </span></label></div></div></div></td>
<td>Row4-1</td>
<td>Row4-2</td>
<td>Row4-3
</td></tr></tbody></table>
!! end