diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..dc45f2c
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -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
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..88e99d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+vendor
+composer.lock
\ No newline at end of file
diff --git a/.phpcs.xml b/.phpcs.xml
new file mode 100644
index 0000000..9ed2260
--- /dev/null
+++ b/.phpcs.xml
@@ -0,0 +1,7 @@
+
+
+
+ .
+
+
+
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..9d6ec3f
--- /dev/null
+++ b/composer.json
@@ -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
+ }
+ }
+}
\ No newline at end of file
diff --git a/extension.json b/extension.json
index e78ca32..8487100 100644
--- a/extension.json
+++ b/extension.json
@@ -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"
},
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 42ce952..27c5691 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -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
*
@@ -40,5 +44,7 @@ public function onMultiContentSave( $renderedRevision, $user, $summary, $flags,
$status->fatal( 'tableprogresstracking-duplicate-tables' );
return false;
}
+
+ return true;
}
}
diff --git a/includes/ProgressTableProcessor.php b/includes/ProgressTableProcessor.php
index 6dd07fd..96ee2a0 100644
--- a/includes/ProgressTableProcessor.php
+++ b/includes/ProgressTableProcessor.php
@@ -9,6 +9,7 @@
use MediaWiki\Html\Html;
use MediaWiki\Parser\Parser;
use MediaWiki\Parser\PPFrame;
+use Wikimedia\AtEase\AtEase;
class ProgressTableProcessor {
@@ -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.';
@@ -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 and there is no easy way to get the link object from the
@@ -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 tags.';
+ $this->errorMessage = 'No table was provided for progress tracking.' .
+ 'Please include a table between the tags.';
return;
}
@@ -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 ) {
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
@@ -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:
diff --git a/includes/Rest/TrackProgressHandler.php b/includes/Rest/TrackProgressHandler.php
index e1e76dd..3ea299c 100644
--- a/includes/Rest/TrackProgressHandler.php
+++ b/includes/Rest/TrackProgressHandler.php
@@ -65,7 +65,7 @@ public function run( int $articleId, int $tableId ): Response {
$response = $this->getResponseFactory()->create();
$response->setStatus( 201 );
-
+
return $response;
}
diff --git a/includes/TableGenerator.php b/includes/TableGenerator.php
index d9dc7af..ab8d2c9 100644
--- a/includes/TableGenerator.php
+++ b/includes/TableGenerator.php
@@ -70,10 +70,10 @@ public static function hasDuplicateTables( RenderedRevision $revision ): bool {
return false;
}
- $text= $content->getText();
+ $text = $content->getText();
// match all tags with a table-id attribute, in any order
- preg_match_all(
+ preg_match_all(
'/]*\btable-id\s*=\s*["\']?([^"\'>\s]+)["\']?[^>]*>/i',
$text,
$matches
diff --git a/tests/parser/singleTable.txt b/tests/parser/singleTable.txt
new file mode 100644
index 0000000..ef2188d
--- /dev/null
+++ b/tests/parser/singleTable.txt
@@ -0,0 +1,48 @@
+!! version 2
+!! hooks
+table-progress-tracking
+!! endhooks
+!! test
+A singular table on an article with the default check icon
+!! wikitext
+
+{| 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
+|}
+
+!! html
+
+ |
+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
+ |
+!! end
\ No newline at end of file
diff --git a/tests/parser/tableWithCustomHeader.txt b/tests/parser/tableWithCustomHeader.txt
new file mode 100644
index 0000000..09c9ee3
--- /dev/null
+++ b/tests/parser/tableWithCustomHeader.txt
@@ -0,0 +1,48 @@
+!! version 2
+!! hooks
+table-progress-tracking
+!! endhooks
+!! test
+A table with a customised header
+!! wikitext
+
+{| 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
+|}
+
+!! html
+
+| Title |
+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
+ |
+!! end
\ No newline at end of file