* @param string $mine
* @param string $yours
* @param string &$result
+ * @param string &$mergeAttemptResult
* @return bool
*/
-function wfMerge( $old, $mine, $yours, &$result ) {
+function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult = null ) {
global $wgDiff3;
# This check may also protect against code injection in
$oldtextName, $yourtextName );
$handle = popen( $cmd, 'r' );
- if ( fgets( $handle, 1024 ) ) {
- $conflict = true;
- } else {
- $conflict = false;
- }
+ $mergeAttemptResult = '';
+ do {
+ $data = fread( $handle, 8192 );
+ if ( strlen( $data ) == 0 ) {
+ break;
+ }
+ $mergeAttemptResult .= $data;
+ } while ( true );
pclose( $handle );
+ $conflict = $mergeAttemptResult !== '';
+
# Merge differences
$cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName,
$oldtextName, $yourtextName );
/**
* Primary entry point
+ * @throws Exception
* @throws MWException
* @return bool
*/
/**
* @param array $pageInfo
* @param array $revisionInfo
+ * @throws MWException
* @return bool|mixed
*/
private function processRevision( $pageInfo, $revisionInfo ) {
$links[] = Html::rawElement(
'span',
$attribs,
- $this->msg( $msg )->rawParams( $link )->escaped()
+ $this->msg( $msg )->rawParams( $link )->parse()
);
}
return Html::rawElement(
'span',
$attribs,
- Xml::checkLabel(
- $this->msg( $message, '' )->text(),
- $name,
- $name,
- (int)$value
+ // not using Html::checkLabel because that would escape the contents
+ Html::check( $name, (int)$value, [ 'id' => $name ] ) . Html::rawElement(
+ 'label',
+ $attribs + [ 'for' => $name ],
+ // <nowiki/> at beginning to avoid messages with "$1 ..." being parsed as pre tags
+ $this->msg( $message, '<nowiki/>' )->parse()
)
);
}
/**
* @param Title|Revision $obj
+ * @throws MWException
* @return bool
*/
private function skippedNamespace( $obj ) {
@import 'mediawiki.ui/variables';
@import 'mw.rcfilters.mixins';
-@rcfilters-spinner-width: 70px;
-@rcfilters-head-min-height: 250px;
-@rcfilters-wl-head-min-height: 320px;
+@rcfilters-spinner-size: 12px;
+@rcfilters-head-min-height: 210px;
+@rcfilters-head-margin-bottom: 20px;
+@rcfilters-wl-head-min-height: 300px;
// Corrections for the standard special page
.client-js {
// Reserve space for the UI while it loads
.rcfilters-head {
min-height: @rcfilters-head-min-height;
+ margin-bottom: @rcfilters-head-margin-bottom;
}
// On the watchlist, reserve a bit more
}
}
- .rcfilters-container {
- min-height: 100px;
- margin: 0;
- }
-
.mw-changeslist {
// Reserve space for the highlight circles
ul,
display: none;
position: absolute;
left: 50%;
- width: @rcfilters-spinner-width;
// Make sure the middle of the spinner is centered, rather than its left edge
- margin-left: -@rcfilters-spinner-width/2;
+ margin-left: -3 * @rcfilters-spinner-size / 2;
opacity: 0.8;
white-space: nowrap;
&:after {
content: '';
display: inline-block;
- width: 12px;
- height: 12px;
+ width: @rcfilters-spinner-size;
+ height: @rcfilters-spinner-size;
background-color: @colorGray12;
border-radius: 100%;
.animation( rcfiltersBouncedelay 1.5s ease-in-out -0.16s infinite both );
body:not( .mw-rcfilters-ui-initialized ) .rcfilters-spinner {
display: block;
// When initializing, display the spinner on top of the area where the UI will appear
- margin-top: -@rcfilters-head-min-height/2;
+ margin-top: -( @rcfilters-head-min-height + @rcfilters-head-margin-bottom ) / 2;
}
body.mw-rcfilters-ui-loading .rcfilters-spinner {
display: block;
display: none;
}
+.mw-changeslist-legend.mw-collapsed {
+ margin: 0;
+}
+
/* Prevent pushing down of content if legend is initially collapsed */
-.mw-changeslist-legend.mw-collapsed ~ ul.special > li:first-child {
+.mw-changeslist-legend.mw-collapsed ~ ul:first-of-type > li:first-child {
clear: right;
}
background-color: #fff;
position: absolute;
right: 0;
- margin-top: -0.5em;
}
];
}
+ /**
+ * @covers ::wfMerge
+ */
+ public function testMerge_worksWithLessParameters() {
+ $this->markTestSkippedIfNoDiff3();
+
+ $mergedText = null;
+ $successfulMerge = wfMerge( "old1\n\nold2", "old1\n\nnew2", "new1\n\nold2", $mergedText );
+
+ $mergedText = null;
+ $conflictingMerge = wfMerge( 'old', 'old and mine', 'old and yours', $mergedText );
+
+ $this->assertEquals( true, $successfulMerge );
+ $this->assertEquals( false, $conflictingMerge );
+ }
+
/**
* @param string $old Text as it was in the database
* @param string $mine Text submitted while user was editing
* @param string $yours Text submitted by the user
* @param bool $expectedMergeResult Whether the merge should be a success
* @param string $expectedText Text after merge has been completed
+ * @param string $expectedMergeAttemptResult Diff3 output if conflicts occur
*
* @dataProvider provideMerge()
* @group medium
* @covers ::wfMerge
*/
- public function testMerge( $old, $mine, $yours, $expectedMergeResult, $expectedText ) {
+ public function testMerge( $old, $mine, $yours, $expectedMergeResult, $expectedText,
+ $expectedMergeAttemptResult ) {
$this->markTestSkippedIfNoDiff3();
$mergedText = null;
- $isMerged = wfMerge( $old, $mine, $yours, $mergedText );
+ $attemptMergeResult = null;
+ $isMerged = wfMerge( $old, $mine, $yours, $mergedText, $mergeAttemptResult );
$msg = 'Merge should be a ';
$msg .= $expectedMergeResult ? 'success' : 'failure';
$this->assertEquals( $expectedMergeResult, $isMerged, $msg );
+ $this->assertEquals( $expectedMergeAttemptResult, $mergeAttemptResult );
if ( $isMerged ) {
// Verify the merged text
"one one one ONE ONE\n" .
"\n" .
"two two TWO TWO\n", // note: will always end in a newline
+
+ // mergeAttemptResult:
+ "",
],
// #1: conflict, fail
// result:
null,
+
+ // mergeAttemptResult:
+ "1,3c\n" .
+ "one one one\n" .
+ "\n" .
+ "two two\n" .
+ ".\n",
],
];
}