0 ) { continue; } $rule = preg_replace( '/^[\s]*/', '', $rule ); $cmd = preg_split( "/ +/", $rule, 2 ); if ( count( $cmd ) > 1 ) { $arg = $cmd[1]; } else { $arg = ''; } $cmd[0] = trim( $cmd[0] ); // after ... insert ..., before ... insert ... if ( $cmd[0] == 'before' ) { $before = $arg; $lastCmd = 'B'; } if ( $cmd[0] == 'after' ) { $after = $arg; $lastCmd = 'A'; } if ( $cmd[0] == 'insert' && $lastCmd != '' ) { if ( $lastCmd == 'A' ) { $insertionAfter = $arg; } if ( $lastCmd == 'B' ) { $insertionBefore = $arg; } } if ( $cmd[0] == 'template' ) { $template = $arg; } if ( $cmd[0] == 'parameter' ) { $nr++; $parameter[$nr] = $arg; if ( $nr > 0 ) { $afterparm[$nr] = [ $parameter[$nr - 1] ]; $n = $nr - 1; while ( $n > 0 && array_key_exists( $n, $optional ) ) { $n--; $afterparm[$nr][] = $parameter[$n]; } } } if ( $cmd[0] == 'value' ) { $value[$nr] = $arg; } if ( $cmd[0] == 'format' ) { $format[$nr] = $arg; } if ( $cmd[0] == 'tooltip' ) { $tooltip[$nr] = $arg; } if ( $cmd[0] == 'optional' ) { $optional[$nr] = true; } if ( $cmd[0] == 'afterparm' ) { $afterparm[$nr] = [ $arg ]; } if ( $cmd[0] == 'legend' ) { $legendPage = $arg; } if ( $cmd[0] == 'instruction' ) { $instructionPage = $arg; } if ( $cmd[0] == 'table' ) { $table = $arg; } if ( $cmd[0] == 'field' ) { $fieldFormat = $arg; } if ( $cmd[0] == 'replace' ) { $replaceThis = $arg; } if ( $cmd[0] == 'by' ) { $replacement = $arg; } if ( $cmd[0] == 'editform' ) { $editForm = $arg; } if ( $cmd[0] == 'action' ) { $action = $arg; } if ( $cmd[0] == 'hidden' ) { $hidden[] = $arg; } if ( $cmd[0] == 'preview' ) { $preview[] = $arg; } if ( $cmd[0] == 'save' ) { $save[] = $arg; } if ( $cmd[0] == 'summary' ) { $summary = $arg; } if ( $cmd[0] == 'exec' ) { // desired action (set or edit or preview) $exec = $arg; } } if ( $summary == '' ) { $summary .= "\nbulk update:"; if ( $replaceThis != '' ) { $summary .= "\n replace $replaceThis\n by $replacement"; } if ( $before != '' ) { $summary .= "\n before $before\n insertionBefore"; } if ( $after != '' ) { $summary .= "\n after $after\n insertionAfter"; } } // perform changes to the wiki source text ======================================= if ( $replaceThis != '' ) { $text = preg_replace( "$replaceThis", $replacement, $text ); } if ( $insertionBefore != '' && $before != '' ) { $text = preg_replace( "/($before)/", $insertionBefore . '\1', $text ); } if ( $insertionAfter != '' && $after != '' ) { $text = preg_replace( "/($after)/", '\1' . $insertionAfter, $text ); } // deal with template parameters ================================================= $user = RequestContext::getMain()->getUser(); if ( $template != '' ) { if ( $exec == 'edit' ) { $tpv = self::getTemplateParmValues( $text, $template ); $legendText = ''; if ( $legendPage != '' ) { $legendTitle = ''; $parser = clone MediaWikiServices::getInstance()->getParser(); LST::text( $parser, $legendPage, $legendTitle, $legendText ); $legendText = preg_replace( '/^.*?\/s', '', $legendText ); $legendText = preg_replace( '/\.*/s', '', $legendText ); } $instructionText = ''; $instructions = []; if ( $instructionPage != '' ) { $instructionTitle = ''; $parser = clone MediaWikiServices::getInstance()->getParser(); LST::text( $parser, $instructionPage, $instructionTitle, $instructionText ); $instructions = self::getTemplateParmValues( $instructionText, 'Template field' ); } // construct an edit form containing all template invocations $form = "
\n"; foreach ( $tpv as $call => $tplValues ) { $form .= "\n"; foreach ( $parameter as $nr => $parm ) { // try to extract legend from the docs of the template $myToolTip = ''; if ( array_key_exists( $nr, $tooltip ) ) { $myToolTip = $tooltip[$nr]; } $myInstruction = ''; $myType = ''; foreach ( $instructions as $instruct ) { if ( array_key_exists( 'field', $instruct ) && $instruct['field'] == $parm ) { if ( array_key_exists( 'doc', $instruct ) ) { $myInstruction = $instruct['doc']; } if ( array_key_exists( 'type', $instruct ) ) { $myType = $instruct['type']; } break; } } $myFormat = ''; if ( array_key_exists( $nr, $format ) ) { $myFormat = $format[$nr]; } if ( $legendText != '' && $myToolTip == '' ) { $myToolTip = preg_replace( '/^.*\/s', '', $legendText ); if ( strlen( $myToolTip ) == strlen( $legendText ) ) { $myToolTip = ''; } else { $myToolTip = preg_replace( '/\.*/s', '', $myToolTip ); } } $myValue = ''; if ( array_key_exists( $parm, $tpv[$call] ) ) { $myValue = $tpv[$call][$parm]; } $form .= self::editTemplateCall( $call, $parm, $myType, $myValue, $myFormat, $myToolTip, $myInstruction, $fieldFormat ); } $form .= "
\n

"; } foreach ( $hidden as $hide ) { $form .= ""; } $form .= ""; foreach ( $preview as $prev ) { $form .= " "; } $form .= "
\n"; return $form; } elseif ( $exec == 'set' || $exec == 'preview' ) { // loop over all invocations and parameters, this could be improved to enhance performance $matchCount = 10; for ( $call = 0; $call < 10; $call++ ) { foreach ( $parameter as $nr => $parm ) { // set parameters to values specified in the dpl source or get them from the http request if ( $exec == 'set' ) { $myValue = $value[$nr]; } else { if ( $call >= $matchCount ) { break; } $request = RequestContext::getMain()->getRequest(); $myValue = $request->getVal( urlencode( $call . '_' . $parm ), '' ); } $myOptional = array_key_exists( $nr, $optional ); $myAfterParm = []; if ( array_key_exists( $nr, $afterparm ) ) { $myAfterParm = $afterparm[$nr]; } $text = self::updateTemplateCall( $matchCount, $text, $template, $call, $parm, $myValue ?? '', $myAfterParm, $myOptional ); } if ( $exec == 'set' ) { break; } } } } if ( $exec == 'set' ) { return self::doUpdateArticle( $title, $text, $summary ); } elseif ( $exec == 'preview' ) { global $wgScriptPath; $request = RequestContext::getMain()->getRequest(); $titleX = Title::newFromText( $title ); $articleX = new Article( $titleX ); $userOptionsLookup = MediaWikiServices::getInstance()->getUserOptionsLookup(); $form = '
'; return $form; } return "exec must be one of the following: edit, preview, set"; } /** * @param string $title * @param string $text * @param string $summary * @return string */ private static function doUpdateArticle( $title, $text, $summary ) { $context = RequestContext::getMain(); $request = $context->getRequest(); $out = $context->getOutput(); $user = $context->getUser(); if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) { $out->addWikiMsg( 'sessionfailure' ); return 'session failure'; } $titleX = Title::newFromText( $title ); $permission_errors = MediaWikiServices::getInstance()->getPermissionManager()->getPermissionErrors( 'edit', $user, $titleX ); if ( count( $permission_errors ) == 0 ) { $services = MediaWikiServices::getInstance(); // MW 1.36+ if ( method_exists( $services, 'getWikiPageFactory' ) ) { $wikiPageFactory = $services->getWikiPageFactory(); $page = $wikiPageFactory->newFromTitle( $titleX ); } else { // @phan-suppress-next-line PhanDeprecatedFunction $page = WikiPage::factory( $titleX ); } $updater = $page->newPageUpdater( $user ); $content = $page->getContentHandler()->makeContent( $text, $titleX ); $updater->setContent( SlotRecord::MAIN, $content ); $comment = CommentStoreComment::newUnsavedComment( $summary ); $updater->saveRevision( $comment, EDIT_UPDATE | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY ); $out->redirect( $titleX->getFullUrl( $page->isRedirect() ? 'redirect=no' : '' ) ); return ''; } else { $out->showPermissionsErrorPage( $permission_errors ); return 'permission error'; } } /** * @param int $call * @param string $parameter * @param string $type * @param string $value * @param string $format * @param string $legend * @param string $instruction * @param string $fieldFormat * @return string */ private static function editTemplateCall( $call, $parameter, $type, $value, $format, $legend, $instruction, $fieldFormat ) { $matches = []; $nlCount = preg_match_all( '/\n/', $value, $matches ); if ( $nlCount > 0 ) { $rows = $nlCount + 1; } else { $rows = floor( strlen( $value ) / 50 ) + 1; } if ( preg_match( '/rows\s*=/', $format ) <= 0 ) { $format .= " rows=$rows"; } $cols = 50; if ( preg_match( '/cols\s*=/', $format ) <= 0 ) { $format .= " cols=$cols"; } $textArea = ""; return str_replace( '%NAME%', htmlspecialchars( str_replace( '_', ' ', $parameter ) ), str_replace( '%TYPE%', $type, str_replace( '%INPUT%', $textArea, str_replace( '%LEGEND%', "" . htmlspecialchars( $legend ) . "", str_replace( '%INSTRUCTION%', "" . htmlspecialchars( $instruction ) . "", $fieldFormat ) ) ) ) ); } /** * @param string $text * @param string $template * @return array|string */ private static function getTemplateParmValues( $text, $template ) { $matches = []; $noMatches = preg_match_all( '/\{\{\s*' . preg_quote( $template, '/' ) . '\s*[|}]/i', $text, $matches, PREG_OFFSET_CAPTURE ); if ( $noMatches <= 0 ) { return ''; } $textLen = strlen( $text ); $tval = []; $call = -1; foreach ( $matches as $matchA ) { foreach ( $matchA as $matchB ) { $match = $matchB[0]; $start = $matchB[1]; $tval[++$call] = []; $nr = 0; $parmValue = ''; $parmName = ''; $parm = ''; if ( $match[strlen( $match ) - 1] == '}' ) { break; } // search to the end of the template call $cbrackets = 2; for ( $i = $start + strlen( $match ); $i < $textLen; $i++ ) { $c = $text[$i]; if ( $c == '{' || $c == '[' ) { $cbrackets++; } if ( $c == '}' || $c == ']' ) { $cbrackets--; } if ( ( $cbrackets == 2 && $c == '|' ) || ( $cbrackets == 1 && $c == '}' ) ) { // parameter (name or value) found if ( $parmName == '' ) { $tval[$call][++$nr] = trim( $parm ); } else { $tval[$call][$parmName] = trim( $parmValue ); } $parmName = ''; $parmValue = ''; $parm = ''; continue; } else { if ( $parmName == '' ) { if ( $c == '=' ) { $parmName = trim( $parm ); } } else { $parmValue .= $c; } } $parm .= $c; if ( $cbrackets == 0 ) { break; } } } } return $tval; } /** * Changes a single parameter value within a certain call of a template * * @param int &$matchCount * @param string $text * @param string $template * @param int $call * @param string $parameter * @param string $value * @param array $afterParm * @param bool $optional * @return string */ private static function updateTemplateCall( &$matchCount, $text, $template, $call, $parameter, $value, $afterParm, $optional ) { // if parameter is optional and value is empty we leave everything as it is (i.e. we do not remove the parm) if ( $optional && $value == '' ) { return $text; } $matches = []; $noMatches = preg_match_all( '/\{\{\s*' . preg_quote( $template, '/' ) . '\s*[|}]/i', $text, $matches, PREG_OFFSET_CAPTURE ); if ( $noMatches <= 0 ) { return $text; } $beginSubst = -1; $endSubst = -1; $posInsertAt = 0; $apNrLast = 1000; foreach ( $matches as $matchA ) { $matchCount = count( $matchA ); foreach ( $matchA as $occurence => $matchB ) { if ( $occurence < $call ) { continue; } $match = $matchB[0]; $start = $matchB[1]; if ( $match[strlen( $match ) - 1] == '}' ) { // template was called without parameters, add new parameter and value // append parameter and value $beginSubst = 0; $endSubst = 0; $substitution = "|$parameter = $value"; break; } else { // there is already a list of parameters; we search to the end of the template call $cbrackets = 2; $parm = ''; $pos = $start + strlen( $match ) - 1; $textLen = strlen( $text ); for ( $i = $pos + 1; $i < $textLen; $i++ ) { $c = $text[$i]; if ( $c == '{' || $c == '[' ) { // we count both types of brackets $cbrackets++; } if ( $c == '}' || $c == ']' ) { $cbrackets--; } if ( ( $cbrackets == 2 && $c == '|' ) || ( $cbrackets == 1 && $c == '}' ) ) { // parameter (name / value) found $token = explode( '=', $parm, 2 ); if ( count( $token ) == 2 ) { // we need a pair of name / value $parmName = trim( $token[0] ); if ( $parmName == $parameter ) { // we found the parameter, now replace the current value $parmValue = trim( $token[1] ); if ( $parmValue == $value ) { // no need to change when values are identical break; } // keep spaces; if ( $parmValue == '' ) { if ( strlen( $token[1] ) > 0 && $token[1][strlen( $token[1] ) - 1] == "\n" ) { $substitution = str_replace( "\n", $value . "\n", $token[1] ); } else { $substitution = $value . $token[1]; } } else { $substitution = str_replace( $parmValue, $value, $token[1] ); } $beginSubst = $pos + strlen( $token[0] ) + 2; $endSubst = $i; break; } else { foreach ( $afterParm as $apNr => $ap ) { // store position for insertion if ( $parmName == $ap && $apNr < $apNrLast ) { $posInsertAt = $i; $apNrLast = $apNr; break; } } } } if ( $c == '}' ) { // end of template call reached, insert at stored position or here if ( $posInsertAt != 0 ) { $beginSubst = $posInsertAt; } else { $beginSubst = $i; } $substitution = "|$parameter = $value"; if ( $text[$beginSubst - 1] == "\n" ) { --$beginSubst; $substitution = "\n" . $substitution; } $endSubst = $beginSubst; break; } $pos = $i; $parm = ''; } else { $parm .= $c; } if ( $cbrackets == 0 ) { break; } } } break; } break; } if ( $beginSubst < 0 ) { return $text; } return substr( $text, 0, $beginSubst ) . ( $substitution ?? '' ) . substr( $text, $endSubst ); } /** * @param string $title * @param string $text * @param string $rulesText * @return string */ public static function deleteArticleByRule( $title, $text, $rulesText ) { // return "deletion of articles by DPL is disabled."; // we use ; as command delimiter; \; stands for a semicolon // \n is translated to a real linefeed $rulesText = str_replace( ";", '°', $rulesText ); $rulesText = str_replace( '\°', ';', $rulesText ); $rulesText = str_replace( "\\n", "\n", $rulesText ); $rules = explode( '°', $rulesText ); $exec = false; $message = ''; $reason = ''; foreach ( $rules as $rule ) { if ( preg_match( '/^\s*#/', $rule ) > 0 ) { // # is comment symbol continue; } $rule = preg_replace( '/^[\s]*/', '', $rule ); $cmd = preg_split( "/ +/", $rule, 2 ); if ( count( $cmd ) > 1 ) { $arg = $cmd[1]; } else { $arg = ''; } $cmd[0] = trim( $cmd[0] ); if ( $cmd[0] == 'reason' ) { $reason = $arg; } // we execute only if "exec" is given, otherwise we merely show what would be done if ( $cmd[0] == 'exec' ) { $exec = true; } } $reason .= "\nbulk delete by DPL query"; $titleX = Title::newFromText( $title ); if ( $exec ) { $context = RequestContext::getMain(); $out = $context->getOutput(); $user = $context->getUser(); # Check permissions $permission_errors = MediaWikiServices::getInstance()->getPermissionManager()->getPermissionErrors( 'delete', $user, $titleX ); $isReadOnly = MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly(); if ( count( $permission_errors ) > 0 ) { $out->showPermissionsErrorPage( $permission_errors ); return 'permission error'; } elseif ( $isReadOnly ) { throw new ReadOnlyError; } else { $articleX = new Article( $titleX ); $articleX->doDelete( $reason ); } } else { $message .= "set 'exec yes' to delete     '''$title'''\n"; } $message .= "
\n{$text}
"; return $message; } }