From 66d5affe5fcf45509028c8f191fd98f68066dbf5 Mon Sep 17 00:00:00 2001 From: Charlot Date: Tue, 10 Mar 2026 11:59:46 +0100 Subject: [PATCH 1/5] Added rebuild-files-clean --- extension.json | 2 +- i18n/en.json | 1 + maintenance/WspsMaintenance.php | 24 +++- src/Core/PSClean.php | 238 ++++++++++++++++++++++++++++++++ src/Core/PSCore.php | 5 +- 5 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 src/Core/PSClean.php diff --git a/extension.json b/extension.json index 43aceb2..e8b0051 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "PageSync", - "version": "2.6.10", + "version": "2.7.0", "author": [ "Sen-Sai" ], diff --git a/i18n/en.json b/i18n/en.json index e6fec98..79789f5 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -211,6 +211,7 @@ "wsps-maintenance-analyze-server-wiki-error-slot-missing": "Slot $1 is missing on server.", "wsps-maintenance-analyze-server-wiki-error-slot-unsynced": "Slot $1 content in Wiki and on Server are not identical", "wsps-maintenance-analyze-server-wiki-slot": " Slot : ", + "wsps-maintenance-clean-header": "Delete files from server not in index", "apihelp-wsps-description": "PageSync API module", "apihelp-wsps-summary": "PageSync API module", "apihelp-wsps-param-what": "Action to take; valid values are 'add', 'remove', 'gettags' and 'updatetags'", diff --git a/maintenance/WspsMaintenance.php b/maintenance/WspsMaintenance.php index 90736a6..3b34977 100644 --- a/maintenance/WspsMaintenance.php +++ b/maintenance/WspsMaintenance.php @@ -52,6 +52,10 @@ public function __construct() { 'rebuild-files', 'Will take the index file and re-create all files from the database' ); + $this->addOption( + 'rebuild-files-clean', + 'Same as rebuild-files, but will also physically remove all files not in the PageSync from the server' + ); $this->addOption( 'rebuild-index', 'Will recreate the index file from existing file structure' @@ -67,7 +71,7 @@ public function __construct() { ); $this->addOption( 'force-rebuild-files', - 'Used with rebuild-files. This forces rebuild-files without prompting for user interaction' + 'Used with rebuild-files and rebuild-files-clean. This forces rebuild-files without prompting for user interaction' ); $this->addOption( @@ -259,6 +263,11 @@ public function execute() { $silent = true; } + if ( $this->hasOption( 'special' ) ) { + $special = true; + $silent = true; + } + $skipDifferentUser = false; if ( $this->hasOption( 'skip-if-page-is-changed-in-wiki' ) ) { $skipDifferentUser = true; @@ -315,15 +324,18 @@ public function execute() { } return; } - - if ( $this->hasOption( 'rebuild-files' ) ) { + if ( $this->hasOption( 'rebuild-files' ) || $this->hasOption( 'rebuild-files-clean' ) ) { // We need to rebuild the index file here. $continueOnError = $this->hasOption( 'continue-on-error' ); if ( $continueOnError ) { $errorList = []; } if ( $this->hasOption( 'force-rebuild-files' ) === false ) { - echo "\n[Rebuilding files from index]\n"; + if ( $this->hasOption( 'rebuild-files-clean' ) ) { + echo "\n[Rebuilding files from index and remove unused files from server. This cannot be undone!]\n"; + } else { + echo "\n[Rebuilding files from index]\n"; + } $answer = strtolower( readline( "Are you sure (y/n)" ) ); if ( $answer !== "y" ) { die( "no action\n\n" ); @@ -388,6 +400,10 @@ public function execute() { echo implode( "\n", $errorList ) . "\n"; } } + if ( $this->hasOption( 'rebuild-files-clean' ) ) { + $cleaner = new \PageSync\Core\PSClean(); + $cleaner->cleanServerFiles(); + } die(); } diff --git a/src/Core/PSClean.php b/src/Core/PSClean.php new file mode 100644 index 0000000..5122e19 --- /dev/null +++ b/src/Core/PSClean.php @@ -0,0 +1,238 @@ +serverList[] = $fileName; + continue; + } + $explodedFileName = explode( '_', $fileName ); + $cnt = count( $explodedFileName ) - 2; + $mergedFileName = ''; + if ( $cnt >= 1 ) { + for ( $i = 0; $i < $cnt; $i++ ) { + if ( $i === 0 ) { + $mergedFileName .= $explodedFileName[$i]; + } else { + $mergedFileName .= '_' . $explodedFileName[$i]; + } + } + $this->serverList[] = $mergedFileName; + } + } + } + $this->exportPath = PSConfig::$config['exportPath']; + $indexList = PSCore::getFileIndex(); + if ( $indexList !== false ) { + $this->indexList = $indexList; + } + } + + /** + * @return void + */ + public function cleanServerFiles(): void { + echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), + "blue+bold", + true, + "", + wfMessage( "wsps-maintenance-analyze-start" )->plain() ); + if ( empty( $this->serverList ) ) { + echo Colors::cEcho('No files on server', + "yellow+bold", + true + ); + echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), + "blue+bold", + true, + "", + wfMessage( "wsps-maintenance-analyze-end" )->plain() ); + return; + } + $this->serverList = array_unique( $this->serverList ); + $cntDeleted = 0; + $cntNotDeleted = 0; + $cntErrors = 0; + $notDeleted = []; + foreach ( $this->serverList as $infoFile ) { + if ( !array_key_exists( $infoFile, $this->indexList ) ) { + if ( $this->deleteInfoFile( $infoFile ) ) { + $cntDeleted++; + } else { + $cntErrors++; + $notDeleted[$infoFile] = false; + } + if ( $this->deleteSlotFiles( $infoFile ) ) { + $cntDeleted++; + } else { + $cntErrors++; + $notDeleted[$infoFile] = false; + } + if ( $this->deleteDatFiles( $infoFile ) ) { + $cntDeleted++; + } else { + $cntErrors++; + $notDeleted[$infoFile] = false; + } + } else { + $cntNotDeleted++; + } + } + + if ( !empty( $notDeleted ) ) { + $this->notDeletedInfo( $notDeleted ); + } + + echo Colors::cEcho( $cntErrors -1 . ' could not be deleted', + "red+bold", + true + ); + echo Colors::cEcho( $cntDeleted -1 . ' files deleted', + "yellow+bold", + true + ); + echo Colors::cEcho( $cntNotDeleted . ' info files not deleted', + "green+bold", + true + ); + + echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), + "blue+bold", + true, + "", + wfMessage( "wsps-maintenance-analyze-end" )->plain() ); + + } + + private function notDeletedInfo( array $notDeleted ): void { + foreach ( $notDeleted as $file=>$tmp ) { + echo Colors::cEcho( $this->exportPath . $file , + "red+bold", + true, + 'NOT DELETED', + 'ERROR' + ); + } + } + + /** + * @param $file + * + * @return void + */ + private function echoDeleted( $file ): void { + echo Colors::cEcho( $file, + "yellow", + false, + '', + "deleted", + true + ); + } + + /** + * @param $file + * + * @return void + */ + private function echoNotDeleted( $file ): void { + echo Colors::cEcho( $file, + "red", + false, + 'Could not be deleted', + "ERROR", + true + ); + } + + + /** + * @param $file + * + * @return bool + */ + private function deleteInfoFile( $file ): bool { + $infoFile = $file . '.info'; + if ( file_exists( $this->exportPath . $infoFile ) ) { + if ( unlink( $this->exportPath . $infoFile ) ) { + $this->echoDeleted( $infoFile ); + + return true; + } else { + $this->echoNotDeleted( $infoFile ); + + return false; + } + } else { + return false; + } + } + + /** + * @param $file + * + * @return bool + */ + private function deleteSlotFiles( $file ): bool { + $slotFiles = $file . '_slot*.wiki'; + $filesList = glob( $this->exportPath . $slotFiles ); + foreach ( $filesList as $singleFile ) { + if ( unlink( $singleFile) ) { + $this->echoDeleted( $singleFile ); + } else { + $this->echoNotDeleted( $singleFile ); + return false; + } + } + return true; + } + + /** + * @param $file + * + * @return bool + */ + private function deleteDatFiles( $file ): bool { + $dataFiles = $file . '.data'; + if ( file_exists( $this->exportPath . $dataFiles ) ) { + if ( unlink( $this->exportPath . $dataFiles ) ) { + $this->echoDeleted( $dataFiles ); + + return true; + } else { + $this->echoNotDeleted( $dataFiles ); + + return false; + } + } else { + return false; + } + } +} \ No newline at end of file diff --git a/src/Core/PSCore.php b/src/Core/PSCore.php index 0dc9dbc..529bd01 100644 --- a/src/Core/PSCore.php +++ b/src/Core/PSCore.php @@ -69,11 +69,14 @@ public static function cleanFileName( string $fname ) : string { /** * @return array */ - public static function getFilesFromServer(): array { + public static function getFilesFromServer( bool $allFiles = false ): array { if ( empty( PSConfig::$config ) ) { self::setConfig(); } $path = PSConfig::$config['exportPath']; + if ( $allFiles ) { + return glob( $path . '*.*' ); + } return glob( $path . "*.info" ); } From 1c05d8545f0b9a036b7034cb2c2a24ebf2ea53c2 Mon Sep 17 00:00:00 2001 From: designburo Date: Tue, 10 Mar 2026 14:02:43 +0100 Subject: [PATCH 2/5] code-cleanup --- src/Core/PSClean.php | 83 ++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/src/Core/PSClean.php b/src/Core/PSClean.php index 5122e19..55df734 100644 --- a/src/Core/PSClean.php +++ b/src/Core/PSClean.php @@ -21,8 +21,6 @@ class PSClean { */ private string $exportPath; - - public function __construct() { $serverFullList = PSCore::getFilesFromServer( true ); if ( !empty( $serverFullList ) ) { @@ -59,21 +57,27 @@ public function __construct() { * @return void */ public function cleanServerFiles(): void { - echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), + echo Colors::cEcho( + wfMessage( "wsps-maintenance-clean-header" )->plain(), "blue+bold", true, "", - wfMessage( "wsps-maintenance-analyze-start" )->plain() ); + wfMessage( "wsps-maintenance-analyze-start" )->plain() + ); if ( empty( $this->serverList ) ) { - echo Colors::cEcho('No files on server', + echo Colors::cEcho( + 'No files on server', "yellow+bold", true ); - echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), + echo Colors::cEcho( + wfMessage( "wsps-maintenance-clean-header" )->plain(), "blue+bold", true, "", - wfMessage( "wsps-maintenance-analyze-end" )->plain() ); + wfMessage( "wsps-maintenance-analyze-end" )->plain() + ); + return; } $this->serverList = array_unique( $this->serverList ); @@ -110,30 +114,40 @@ public function cleanServerFiles(): void { $this->notDeletedInfo( $notDeleted ); } - echo Colors::cEcho( $cntErrors -1 . ' could not be deleted', + echo Colors::cEcho( + $cntErrors - 1 . ' could not be deleted', "red+bold", true ); - echo Colors::cEcho( $cntDeleted -1 . ' files deleted', + echo Colors::cEcho( + $cntDeleted - 1 . ' files deleted', "yellow+bold", true ); - echo Colors::cEcho( $cntNotDeleted . ' info files not deleted', + echo Colors::cEcho( + $cntNotDeleted . ' info files not deleted', "green+bold", true ); - echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), + echo Colors::cEcho( + wfMessage( "wsps-maintenance-clean-header" )->plain(), "blue+bold", true, "", - wfMessage( "wsps-maintenance-analyze-end" )->plain() ); - + wfMessage( "wsps-maintenance-analyze-end" )->plain() + ); } + /** + * @param array $notDeleted + * + * @return void + */ private function notDeletedInfo( array $notDeleted ): void { - foreach ( $notDeleted as $file=>$tmp ) { - echo Colors::cEcho( $this->exportPath . $file , + foreach ( $notDeleted as $file => $tmp ) { + echo Colors::cEcho( + $this->exportPath . $file, "red+bold", true, 'NOT DELETED', @@ -143,42 +157,41 @@ private function notDeletedInfo( array $notDeleted ): void { } /** - * @param $file + * @param string $file * * @return void */ - private function echoDeleted( $file ): void { - echo Colors::cEcho( $file, + private function echoDeleted( string $file ): void { + echo Colors::cEcho( + $file, "yellow", false, '', - "deleted", - true + "deleted" ); } /** - * @param $file + * @param string $file * * @return void */ - private function echoNotDeleted( $file ): void { - echo Colors::cEcho( $file, + private function echoNotDeleted( string $file ): void { + echo Colors::cEcho( + $file, "red", false, 'Could not be deleted', - "ERROR", - true + "ERROR" ); } - /** - * @param $file + * @param string $file * * @return bool */ - private function deleteInfoFile( $file ): bool { + private function deleteInfoFile( string $file ): bool { $infoFile = $file . '.info'; if ( file_exists( $this->exportPath . $infoFile ) ) { if ( unlink( $this->exportPath . $infoFile ) ) { @@ -196,30 +209,32 @@ private function deleteInfoFile( $file ): bool { } /** - * @param $file + * @param string $file * * @return bool */ - private function deleteSlotFiles( $file ): bool { + private function deleteSlotFiles( string $file ): bool { $slotFiles = $file . '_slot*.wiki'; $filesList = glob( $this->exportPath . $slotFiles ); foreach ( $filesList as $singleFile ) { - if ( unlink( $singleFile) ) { + if ( unlink( $singleFile ) ) { $this->echoDeleted( $singleFile ); } else { $this->echoNotDeleted( $singleFile ); + return false; } } + return true; } /** - * @param $file + * @param string $file * * @return bool */ - private function deleteDatFiles( $file ): bool { + private function deleteDatFiles( string $file ): bool { $dataFiles = $file . '.data'; if ( file_exists( $this->exportPath . $dataFiles ) ) { if ( unlink( $this->exportPath . $dataFiles ) ) { @@ -235,4 +250,4 @@ private function deleteDatFiles( $file ): bool { return false; } } -} \ No newline at end of file +} From 6f684bbef64f3e8a7e38165f08ce1d9e68fbcf33 Mon Sep 17 00:00:00 2001 From: designburo Date: Tue, 10 Mar 2026 14:04:32 +0100 Subject: [PATCH 3/5] readme update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4da3c52..3e09a9c 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Detailed documentation can be found here : https://www.open-csp.org/DevOps:Doc/P #### Development +* 2.7.0 rebuild-files-clean. Same as rebuild-files, but will now also physically unused files from the server * 2.6.10 fix for maintenance script when installing a shared file. Changed pages were counted as unchanged * 2.6.9 fix for maintenance script rename and removed specific PHP 8.3 usage * 2.6.8 Added fail catcher for broken pages. Added continue-on-error argument for maintenance script From c1576d65bff6923268b2b77d8e592b9d497c75b0 Mon Sep 17 00:00:00 2001 From: designburo Date: Tue, 10 Mar 2026 19:33:13 +0100 Subject: [PATCH 4/5] simplified getting files liset for clean-up --- src/Core/PSClean.php | 35 ++++++++++++++--------------------- src/Core/PSCore.php | 2 ++ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Core/PSClean.php b/src/Core/PSClean.php index 55df734..d00fe3f 100644 --- a/src/Core/PSClean.php +++ b/src/Core/PSClean.php @@ -23,27 +23,20 @@ class PSClean { public function __construct() { $serverFullList = PSCore::getFilesFromServer( true ); - if ( !empty( $serverFullList ) ) { - foreach ( $serverFullList as $file ) { - $pathInfo = pathinfo( $file ); - $fileName = $pathInfo['filename']; - if ( $pathInfo['extension'] === 'info' || $pathInfo['extension'] === 'data' ) { - $this->serverList[] = $fileName; - continue; - } - $explodedFileName = explode( '_', $fileName ); - $cnt = count( $explodedFileName ) - 2; - $mergedFileName = ''; - if ( $cnt >= 1 ) { - for ( $i = 0; $i < $cnt; $i++ ) { - if ( $i === 0 ) { - $mergedFileName .= $explodedFileName[$i]; - } else { - $mergedFileName .= '_' . $explodedFileName[$i]; - } - } - $this->serverList[] = $mergedFileName; - } + + foreach ( $serverFullList as $file ) { + $pathInfo = pathinfo( $file ); + $fileName = $pathInfo['filename']; + if ( $pathInfo['extension'] === 'info' || $pathInfo['extension'] === 'data' ) { + $this->serverList[] = $fileName; + continue; + } + $explodedFileName = explode( '_', $fileName ); + $cnt = count( $explodedFileName ) - 2; + if ( $cnt > 2 ) { + unset( $explodedFileName[ $cnt - 1 ] ); + unset( $explodedFileName[ $cnt - 2 ] ); + $this->serverList[] = implode( '_', $explodedFileName ); } } $this->exportPath = PSConfig::$config['exportPath']; diff --git a/src/Core/PSCore.php b/src/Core/PSCore.php index 529bd01..beea243 100644 --- a/src/Core/PSCore.php +++ b/src/Core/PSCore.php @@ -67,6 +67,8 @@ public static function cleanFileName( string $fname ) : string { } /** + * @param bool $allFiles + * * @return array */ public static function getFilesFromServer( bool $allFiles = false ): array { From c07b2e6f3a9d9b55ff2cc1ce93d85c5a9ef1e1f4 Mon Sep 17 00:00:00 2001 From: designburo Date: Tue, 10 Mar 2026 19:57:52 +0100 Subject: [PATCH 5/5] simplified getting files liset for clean-up --- src/Core/PSClean.php | 128 ++++++++++--------------------------------- 1 file changed, 30 insertions(+), 98 deletions(-) diff --git a/src/Core/PSClean.php b/src/Core/PSClean.php index d00fe3f..6000892 100644 --- a/src/Core/PSClean.php +++ b/src/Core/PSClean.php @@ -32,7 +32,7 @@ public function __construct() { continue; } $explodedFileName = explode( '_', $fileName ); - $cnt = count( $explodedFileName ) - 2; + $cnt = count( $explodedFileName ); if ( $cnt > 2 ) { unset( $explodedFileName[ $cnt - 1 ] ); unset( $explodedFileName[ $cnt - 2 ] ); @@ -74,55 +74,17 @@ public function cleanServerFiles(): void { return; } $this->serverList = array_unique( $this->serverList ); - $cntDeleted = 0; $cntNotDeleted = 0; - $cntErrors = 0; - $notDeleted = []; foreach ( $this->serverList as $infoFile ) { if ( !array_key_exists( $infoFile, $this->indexList ) ) { - if ( $this->deleteInfoFile( $infoFile ) ) { - $cntDeleted++; - } else { - $cntErrors++; - $notDeleted[$infoFile] = false; - } - if ( $this->deleteSlotFiles( $infoFile ) ) { - $cntDeleted++; - } else { - $cntErrors++; - $notDeleted[$infoFile] = false; - } - if ( $this->deleteDatFiles( $infoFile ) ) { - $cntDeleted++; - } else { - $cntErrors++; - $notDeleted[$infoFile] = false; - } + $this->deleteInfoFile( $infoFile ); + $this->deleteSlotFiles( $infoFile ); + $this->deleteDatFiles( $infoFile ); } else { $cntNotDeleted++; } } - if ( !empty( $notDeleted ) ) { - $this->notDeletedInfo( $notDeleted ); - } - - echo Colors::cEcho( - $cntErrors - 1 . ' could not be deleted', - "red+bold", - true - ); - echo Colors::cEcho( - $cntDeleted - 1 . ' files deleted', - "yellow+bold", - true - ); - echo Colors::cEcho( - $cntNotDeleted . ' info files not deleted', - "green+bold", - true - ); - echo Colors::cEcho( wfMessage( "wsps-maintenance-clean-header" )->plain(), "blue+bold", @@ -132,115 +94,85 @@ public function cleanServerFiles(): void { ); } - /** - * @param array $notDeleted - * - * @return void - */ - private function notDeletedInfo( array $notDeleted ): void { - foreach ( $notDeleted as $file => $tmp ) { - echo Colors::cEcho( - $this->exportPath . $file, - "red+bold", - true, - 'NOT DELETED', - 'ERROR' - ); - } - } - /** * @param string $file + * @param string $function * * @return void */ - private function echoDeleted( string $file ): void { + private function echoDeleted( string $file, string $function ): void { echo Colors::cEcho( $file, "yellow", false, - '', + $function, "deleted" ); } /** * @param string $file + * @param string $function * * @return void */ - private function echoNotDeleted( string $file ): void { + private function echoNotDeleted( string $file, string $function ): void { echo Colors::cEcho( $file, "red", false, 'Could not be deleted', - "ERROR" + $function ); } /** * @param string $file * - * @return bool + * @return void */ - private function deleteInfoFile( string $file ): bool { - $infoFile = $file . '.info'; - if ( file_exists( $this->exportPath . $infoFile ) ) { - if ( unlink( $this->exportPath . $infoFile ) ) { - $this->echoDeleted( $infoFile ); - - return true; + private function deleteInfoFile( string $file ): void { + $infoFile = $this->exportPath . $file . '.info'; + if ( file_exists( $infoFile ) ) { + if ( unlink( $infoFile ) ) { + $this->echoDeleted( $infoFile, __function__ ); } else { - $this->echoNotDeleted( $infoFile ); - - return false; + $this->echoNotDeleted( $infoFile, __function__ ); } - } else { - return false; } } /** * @param string $file * - * @return bool + * @return void */ - private function deleteSlotFiles( string $file ): bool { - $slotFiles = $file . '_slot*.wiki'; - $filesList = glob( $this->exportPath . $slotFiles ); + private function deleteSlotFiles( string $file ): void { + $slotFiles = $this->exportPath . $file . '_slot*.wiki'; + $filesList = glob( $slotFiles ); foreach ( $filesList as $singleFile ) { if ( unlink( $singleFile ) ) { - $this->echoDeleted( $singleFile ); + $this->echoDeleted( $singleFile, __function__ ); } else { - $this->echoNotDeleted( $singleFile ); - - return false; + $this->echoNotDeleted( $singleFile, __function__ ); } } - - return true; } /** * @param string $file * - * @return bool + * @return void */ - private function deleteDatFiles( string $file ): bool { - $dataFiles = $file . '.data'; - if ( file_exists( $this->exportPath . $dataFiles ) ) { - if ( unlink( $this->exportPath . $dataFiles ) ) { - $this->echoDeleted( $dataFiles ); - - return true; + private function deleteDatFiles( string $file ): void { + $dataFiles = $this->exportPath . $file . '.data'; + if ( file_exists( $dataFiles ) ) { + if ( unlink( $dataFiles ) ) { + $this->echoDeleted( $dataFiles, __function__ ); } else { - $this->echoNotDeleted( $dataFiles ); + $this->echoNotDeleted( $dataFiles, __function__ ); - return false; } - } else { - return false; } } }