From 70e6e71579014f0876815214c9e025c36b4a9451 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Thu, 11 Nov 2021 23:36:25 +0000 Subject: [PATCH 1/2] delete.php: Use FormLayout --- delete.php | 115 ++++++++++++++++++++++++++++++--------------------- includes.php | 6 +-- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/delete.php b/delete.php index 906ac1ac..c70cb6c4 100644 --- a/delete.php +++ b/delete.php @@ -11,57 +11,80 @@ die( '

You are not allowed to delete this wiki.

' ); } -if ( !isset( $_POST['confirm' ] ) ) { - $patches = format_patch_list( $wikiData['patchList'], $wikiData['branch'] ); - $linkedTasks = format_linked_tasks( $wikiData['linkedTaskList'] ); - $creator = $wikiData[ 'creator' ] ?? ''; +if ( !$wikiData['deleted'] ) { + if ( !isset( $_POST['confirm' ] ) ) { + $patches = format_patch_list( $wikiData['patchList'], $wikiData['branch'] ); + $linkedTasks = format_linked_tasks( $wikiData['linkedTaskList'] ); + $creator = $wikiData[ 'creator' ] ?? ''; - echo '' . - '' . - '' . - '' . - '' . - '' . - ( $useOAuth ? '' : '' ) . - '' . - '' . - '' . - '' . - '' . - '' . - ( $useOAuth ? '' : '' ) . - '' . - '
WikiPatches
✓=Merged ✗=Abandoned
Linked tasks
✓=Resolved ✗=Declined/Invalid
TimeCreator
' . substr( $wiki, 0, 10 ) . '' . $patches . '' . $linkedTasks . '' . date( 'Y-m-d H:i:s', $wikiData[ 'created' ] ) . '' . ( $creator ? user_link( $creator ) : '?' ) . '
'; + echo '' . + '' . + '' . + '' . + '' . + '' . + ( $useOAuth ? '' : '' ) . + '' . + '' . + '' . + '' . + '' . + '' . + ( $useOAuth ? '' : '' ) . + '' . + '
WikiPatches
✓=Merged ✗=Abandoned
Linked tasks
✓=Resolved ✗=Declined/Invalid
TimeCreator
' . substr( $wiki, 0, 10 ) . '' . $patches . '' . $linkedTasks . '' . date( 'Y-m-d H:i:s', $wikiData[ 'created' ] ) . '' . ( $creator ? user_link( $creator ) : '?' ) . '
'; - echo '
' . - '

Are you sure you want to delete this wiki?

' . - '

This cannot be undone.

' . - new OOUI\ButtonInputWidget( [ - 'type' => 'submit', - 'name' => 'confirm', - 'label' => 'Delete', - 'flags' => [ 'primary', 'destructive' ] - ] ) . - new OOUI\HiddenInputWidget( [ - 'name' => 'csrf_token', - 'value' => get_csrf_token(), - ] ) . - '
'; - die(); -} + echo new OOUI\FormLayout( [ + 'method' => 'POST', + 'items' => [ + new OOUI\FieldsetLayout( [ + 'label' => new OOUI\HtmlSnippet( + '
Are you sure you want to delete this wiki? This cannot be undone.' + ), + 'items' => array_filter( [ + new OOUI\FieldLayout( + new OOUI\ButtonInputWidget( [ + 'type' => 'submit', + 'name' => 'confirm', + 'label' => 'Delete', + 'flags' => [ 'primary', 'destructive' ] + ] ), + [ + 'label' => ' ', + 'align' => 'inline', + ] + ), + new OOUI\FieldLayout( + new OOUI\HiddenInputWidget( [ + 'name' => 'csrf_token', + 'value' => get_csrf_token(), + ] ) + ), + ] ) + ] ) + ] + ] ); -if ( !isset( $_POST['csrf_token'] ) || !check_csrf_token( $_POST['csrf_token'] ) ) { - die( "Invalid session." ); -} + } else { + if ( !isset( $_POST['csrf_token'] ) || !check_csrf_token( $_POST['csrf_token'] ) ) { + die( "Invalid session." ); + } + + ob_implicit_flush( true ); -ob_implicit_flush( true ); + echo '
'; + $error = delete_wiki( $wiki ); + echo '
'; -echo '
'; -$error = delete_wiki( $wiki ); -echo '
'; + if ( $error ) { + die( '

Error deleting wiki:
' . htmlentities( $error ) . '

' ); + } + + // Refresh wiki data + $wikiData = get_wiki_data( $wiki ); + } +} -if ( $error ) { - die( '

Error deleting wiki:
' . htmlentities( $error ) . '

' ); -} else { +if ( $wikiData['deleted'] ) { echo '

Wiki deleted.

'; } diff --git a/includes.php b/includes.php index 9ef3eba3..5f08dfbc 100644 --- a/includes.php +++ b/includes.php @@ -316,7 +316,7 @@ function shell( $cmd, array $env = [] ): ?string { return $error ? null : $process->getOutput(); } -function delete_wiki( string $wiki ): int { +function delete_wiki( string $wiki ): ?string { global $mysqli; $wikiData = get_wiki_data( $wiki ); @@ -325,7 +325,7 @@ function delete_wiki( string $wiki ): int { return 'Wiki already deleted.'; } - $error = shell_echo( __DIR__ . '/deletewiki.sh', + $errorCode = shell_echo( __DIR__ . '/deletewiki.sh', [ 'PATCHDEMO' => __DIR__, 'WIKI' => $wiki @@ -355,7 +355,7 @@ function delete_wiki( string $wiki ): int { $stmt->execute(); $stmt->close(); - return $error; + return $errorCode ? 'Delete script failed.' : null; } $requestCache = []; From d5c1995b8ca720518fb54a439b99c44016b926e0 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Thu, 11 Nov 2021 23:38:48 +0000 Subject: [PATCH 2/2] Allow wikis to be redirected when deleting Fixes #54 --- 404.php | 40 ++++++++++++++++++++++++++--- delete.php | 64 +++++++++++++++++++++++++++++++++++++++++++++-- includes.php | 16 +++++++++--- sql/patchdemo.sql | 3 +++ 4 files changed, 113 insertions(+), 10 deletions(-) diff --git a/404.php b/404.php index 04366812..855cd0ad 100644 --- a/404.php +++ b/404.php @@ -4,9 +4,41 @@ include "header.php"; -echo new \OOUI\MessageWidget( [ - 'type' => 'error', - 'label' => 'Page not found. The wiki you are looking for may have been deleted.' -] ); +$redirect = false; + +// Check for redirect +$uri = $_SERVER['REQUEST_URI']; +if ( preg_match( '`/wikis/([0-9a-f]{10,32})/`', $uri, $matches, PREG_OFFSET_CAPTURE ) !== false ) { + $wiki = $matches[1][0]; + $offset = $matches[1][1]; + $wikiData = get_wiki_data( $wiki ); + // Follow up to 10 redirect steps + $i = 0; + while ( $wikiData['redirect'] && $i < 10 ) { + $redirect = $wikiData['redirect']; + $wikiData = get_wiki_data( $redirect ); + $i++; + } + $redirectUri = + substr( $uri, 0, $offset ) . + $redirect . + substr( $uri, $offset + strlen( $wiki ) ); +} + +if ( $redirect ) { + echo new \OOUI\MessageWidget( [ + 'type' => 'info', + 'icon' => 'articleRedirect', + 'label' => new \OOUI\HtmlSnippet( + 'This wiki has been deleted and the following wiki was selected as a direct replacement: ' . + '' . substr( $redirect, 0, 10 ) . '' + ) + ] ); +} else { + echo new \OOUI\MessageWidget( [ + 'type' => 'error', + 'label' => 'Page not found. The wiki you are looking for may have been deleted.' + ] ); +} include "footer.html"; diff --git a/delete.php b/delete.php index c70cb6c4..50a25681 100644 --- a/delete.php +++ b/delete.php @@ -34,6 +34,48 @@ '' . ''; + $username = $user ? $user->username : null; + $wikilist = [ + [ + 'data' => '', + 'label' => 'None', + ] + ]; + $stmt = $mysqli->prepare( ' + SELECT wiki, creator, UNIX_TIMESTAMP( created ) created + FROM wikis + WHERE !deleted + ORDER BY IF( creator = ?, 1, 0 ) DESC, created DESC + ' ); + if ( !$stmt ) { + die( $mysqli->error ); + } + $stmt->bind_param( 's', $username ); + $stmt->execute(); + $results = $stmt->get_result(); + if ( !$results ) { + die( $mysqli->error ); + } + $shownMyWikis = false; + $shownOtherWikis = false; + while ( $data = $results->fetch_assoc() ) { + if ( $data[ 'wiki' ] === $wiki ) { + continue; + } + $creator = $data[ 'creator' ] ?? ''; + if ( !$shownMyWikis && $creator === $username ) { + $wikilist[] = [ 'optgroup' => 'My wikis' ]; + $shownMyWikis = true; + } + if ( $shownMyWikis && !$shownOtherWikis && $creator !== $username ) { + $wikilist[] = [ 'optgroup' => 'Other wikis' ]; + $shownOtherWikis = true; + } + $wikilist[] = [ + 'data' => $data[ 'wiki' ], + 'label' => substr( $data[ 'wiki' ], 0, 10 ) . ' - ' . $data[ 'creator' ] . ' (' . date( 'Y-m-d H:i:s', $data[ 'created' ] ) . ')', + ]; + } echo new OOUI\FormLayout( [ 'method' => 'POST', 'items' => [ @@ -42,6 +84,18 @@ '
Are you sure you want to delete this wiki? This cannot be undone.' ), 'items' => array_filter( [ + count( $wikilist ) > 1 ? + new OOUI\FieldLayout( + new OOUI\DropdownInputWidget( [ + 'name' => 'redirect', + 'options' => $wikilist, + ] ), + [ + 'label' => 'Leave a redirect to another wiki (optional):', + 'align' => 'left', + ] + ) : + null, new OOUI\FieldLayout( new OOUI\ButtonInputWidget( [ 'type' => 'submit', @@ -51,7 +105,7 @@ ] ), [ 'label' => ' ', - 'align' => 'inline', + 'align' => 'left', ] ), new OOUI\FieldLayout( @@ -70,10 +124,12 @@ die( "Invalid session." ); } + $redirect = $_POST['redirect'] ?: null; + ob_implicit_flush( true ); echo '
'; - $error = delete_wiki( $wiki ); + $error = delete_wiki( $wiki, $redirect ); echo '
'; if ( $error ) { @@ -88,3 +144,7 @@ if ( $wikiData['deleted'] ) { echo '

Wiki deleted.

'; } + +if ( $wikiData['redirect'] ) { + echo '

Redirected to ' . $wikiData['redirect'] . '.

'; +} diff --git a/includes.php b/includes.php index 5f08dfbc..682b4ffe 100644 --- a/includes.php +++ b/includes.php @@ -77,7 +77,7 @@ function get_wiki_data( string $wiki ): array { global $mysqli; $stmt = $mysqli->prepare( ' - SELECT wiki, creator, UNIX_TIMESTAMP( created ) created, patches, branch, announcedTasks, timeToCreate, deleted + SELECT wiki, creator, UNIX_TIMESTAMP( created ) created, patches, branch, announcedTasks, timeToCreate, deleted, redirect FROM wikis WHERE wiki = ? ' ); if ( !$stmt ) { @@ -316,7 +316,11 @@ function shell( $cmd, array $env = [] ): ?string { return $error ? null : $process->getOutput(); } -function delete_wiki( string $wiki ): ?string { +function is_valid_hash( string $hash ): bool { + return preg_match( '/^[0-9a-f]{10,32}$/', $hash ) !== false; +} + +function delete_wiki( string $wiki, ?string $redirect = null ): ?string { global $mysqli; $wikiData = get_wiki_data( $wiki ); @@ -346,12 +350,16 @@ function delete_wiki( string $wiki ): ?string { ); } + if ( $redirect && !is_valid_hash( $redirect ) ) { + $redirect = null; + } + $stmt = $mysqli->prepare( ' UPDATE wikis - SET deleted = 1 + SET deleted = 1, redirect = ? WHERE wiki = ? ' ); - $stmt->bind_param( 's', $wiki ); + $stmt->bind_param( 'ss', $redirect, $wiki ); $stmt->execute(); $stmt->close(); diff --git a/sql/patchdemo.sql b/sql/patchdemo.sql index c78762b9..1d213cc8 100644 --- a/sql/patchdemo.sql +++ b/sql/patchdemo.sql @@ -42,3 +42,6 @@ ALTER TABLE `tasks` ALTER TABLE `wikis` ADD COLUMN IF NOT EXISTS `branch` VARCHAR(64) NOT NULL AFTER `patches`; + +ALTER TABLE `wikis` + ADD COLUMN IF NOT EXISTS `redirect` VARCHAR(32) NULL AFTER `deleted`;