contentLanguage = $contentLanguage; $this->gadgetRepo = $gadgetRepo; $this->skinFactory = $skinFactory; } /** * Return title for Special:Gadgets heading and Special:Specialpages link. * * @return Message */ public function getDescription() { return $this->msg( 'special-gadgets' ); } /** * @param string|null $par Parameters passed to the page */ public function execute( $par ) { $parts = $par !== null ? explode( '/', $par ) : []; if ( count( $parts ) === 2 && $parts[0] === 'export' ) { $this->showExportForm( $parts[1] ); } else { $this->showMainForm(); } } /** * @param string $gadgetName * @return string */ private function makeAnchor( $gadgetName ) { return 'gadget-' . Sanitizer::escapeIdForAttribute( $gadgetName ); } /** * Displays form showing the list of installed gadgets */ public function showMainForm() { $output = $this->getOutput(); $this->setHeaders(); $this->addHelpLink( 'Extension:Gadgets' ); $output->setPageTitleMsg( $this->msg( 'gadgets-title' ) ); $output->addWikiMsg( 'gadgets-pagetext' ); $gadgets = $this->gadgetRepo->getStructuredList(); if ( !$gadgets ) { return; } $output->disallowUserJs(); $lang = $this->getLanguage(); $langSuffix = ""; if ( !$lang->equals( $this->contentLanguage ) ) { $langSuffix = "/" . $lang->getCode(); } $listOpen = false; $editDefinitionMessage = $this->getUser()->isAllowed( 'editsitejs' ) ? 'edit' : 'viewsource'; $editInterfaceMessage = $this->getUser()->isAllowed( 'editinterface' ) ? 'gadgets-editdescription' : 'gadgets-viewdescription'; $linkRenderer = $this->getLinkRenderer(); foreach ( $gadgets as $section => $entries ) { if ( $section !== false && $section !== '' ) { if ( $listOpen ) { $output->addHTML( Html::closeElement( 'ul' ) . "\n" ); $listOpen = false; } // H2 section heading $headingText = Html::rawElement( 'span', [ 'class' => 'mw-headline' ], $this->msg( "gadget-section-$section" )->parse() ); $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-section-$section$langSuffix" ); $leftBracket = Html::rawElement( 'span', [ 'class' => 'mw-editsection-bracket' ], '[' ); $linkTarget = $title ? $linkRenderer->makeLink( $title, $this->msg( $editInterfaceMessage )->text(), [], [ 'action' => 'edit' ] ) : htmlspecialchars( $section ); $rightBracket = Html::rawElement( 'span', [ 'class' => 'mw-editsection-bracket' ], ']' ); $editDescriptionLink = Html::rawElement( 'span', [ 'class' => 'mw-editsection' ], $leftBracket . $linkTarget . $rightBracket ); $output->addHTML( Html::rawElement( 'h2', [], $headingText . $editDescriptionLink ) . "\n" ); } /** * @var Gadget $gadget */ foreach ( $entries as $gadget ) { $name = $gadget->getName(); $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-{$name}$langSuffix" ); if ( !$title ) { continue; } $links = []; $definitionTitle = $this->gadgetRepo->getGadgetDefinitionTitle( $name ); if ( $definitionTitle ) { $links[] = $linkRenderer->makeLink( $definitionTitle, $this->msg( $editDefinitionMessage )->text(), [], [ 'action' => 'edit' ] ); } $links[] = $linkRenderer->makeLink( $title, $this->msg( $editInterfaceMessage )->text(), [], [ 'action' => 'edit' ] ); $links[] = $linkRenderer->makeLink( $this->getPageTitle( "export/{$name}" ), $this->msg( 'gadgets-export' )->text() ); $nameHtml = $this->msg( "gadget-{$name}" )->parse(); if ( !$listOpen ) { $listOpen = true; $output->addHTML( Html::openElement( 'ul' ) ); } $actionsHtml = '  ' . $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $links ) )->escaped(); $output->addHTML( Html::openElement( 'li', [ 'id' => $this->makeAnchor( $name ) ] ) . $nameHtml . $actionsHtml ); // Whether the next portion of the list item contents needs // a line break between it and the next portion. // This is set to false after lists, but true after lines of text. $needLineBreakAfter = true; // Portion: Show files, dependencies, speers if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-uses' )->escaped() . $this->msg( 'colon-separator' )->escaped() ); $links = []; foreach ( $gadget->getPeers() as $peer ) { $links[] = Html::element( 'a', [ 'href' => '#' . $this->makeAnchor( $peer ) ], $peer ); } foreach ( $gadget->getScriptsAndStyles() as $codePage ) { $title = Title::newFromText( $codePage ); if ( !$title ) { continue; } $links[] = $linkRenderer->makeLink( $title, $title->getText() ); } $output->addHTML( $lang->commaList( $links ) ); if ( $gadget->isPackaged() ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-packaged', $this->gadgetRepo->titleWithoutPrefix( $gadget->getScripts()[0], $gadget->getName() ) ) ); $needLineBreakAfter = true; } // Portion: Legacy scripts if ( $gadget->getLegacyScripts() ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( Html::errorBox( $this->msg( 'gadgets-legacy' )->parse(), '', 'mw-gadget-legacy' ) ); $needLineBreakAfter = false; } if ( $gadget->requiresES6() ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-requires-es6' )->parse() ); $needLineBreakAfter = true; } // Portion: Show required rights (optional) $rights = []; foreach ( $gadget->getRequiredRights() as $right ) { $rights[] = Html::element( 'code', [ 'title' => $this->msg( "right-$right" )->plain() ], $right ); } if ( $rights ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-required-rights', $lang->commaList( $rights ), count( $rights ) )->parse() ); $needLineBreakAfter = true; } // Portion: Show required skins (optional) $requiredSkins = $gadget->getRequiredSkins(); $skins = []; $validskins = $this->skinFactory->getSkinNames(); foreach ( $requiredSkins as $skinid ) { if ( isset( $validskins[$skinid] ) ) { $skins[] = $this->msg( "skinname-$skinid" )->plain(); } else { $skins[] = $skinid; } } if ( $skins ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-required-skins', $lang->commaList( $skins ) ) ->numParams( count( $skins ) )->parse() ); $needLineBreakAfter = true; } // Portion: Show required actions (optional) $actions = []; foreach ( $gadget->getRequiredActions() as $action ) { $actions[] = Html::element( 'code', [], $action ); } if ( $actions ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-required-actions', $lang->commaList( $actions ) ) ->numParams( count( $actions ) )->parse() ); $needLineBreakAfter = true; } // Portion: Show required namespaces (optional) $namespaces = $gadget->getRequiredNamespaces(); if ( $namespaces ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-required-namespaces', $lang->commaList( array_map( function ( int $ns ) use ( $lang ) { return $ns == NS_MAIN ? $this->msg( 'blanknamespace' )->text() : $lang->getFormattedNsText( $ns ); }, $namespaces ) ) )->numParams( count( $namespaces ) )->parse() ); $needLineBreakAfter = true; } // Portion: Show required content models (optional) $contentModels = []; foreach ( $gadget->getRequiredContentModels() as $model ) { $contentModels[] = Html::element( 'code', [ 'title' => ContentHandler::getLocalizedName( $model, $lang ) ], $model ); } if ( $contentModels ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-required-contentmodels', $lang->commaList( $contentModels ), count( $contentModels ) )->parse() ); $needLineBreakAfter = true; } // Portion: Show required categories (optional) $categories = []; foreach ( $gadget->getRequiredCategories() as $category ) { $title = Title::makeTitleSafe( NS_CATEGORY, $category ); $categories[] = $title ? $linkRenderer->makeLink( $title, $category ) : htmlspecialchars( $category ); } if ( $categories ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-required-categories' ) ->rawParams( $lang->commaList( $categories ) ) ->numParams( count( $categories ) )->parse() ); $needLineBreakAfter = true; } if ( $gadget->supportsUrlLoad() ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-supports-urlload' )->parse() ); $needLineBreakAfter = true; } // Portion: Show on by default (optional) if ( $gadget->isOnByDefault() ) { if ( $needLineBreakAfter ) { $output->addHTML( '
' ); } $output->addHTML( $this->msg( 'gadgets-default' )->parse() ); $needLineBreakAfter = true; } // Show warnings $warnings = $this->gadgetRepo->validationWarnings( $gadget ); if ( count( $warnings ) > 0 ) { $output->addHTML( Html::warningBox( implode( '
', array_map( static function ( $msg ) { return $msg->parse(); }, $warnings ) ) ) ); $needLineBreakAfter = false; } $output->addHTML( Html::closeElement( 'li' ) . "\n" ); } } if ( $listOpen ) { $output->addHTML( Html::closeElement( 'ul' ) . "\n" ); } } /** * Exports a gadget with its dependencies in a serialized form * @param string $gadget Name of gadget to export */ public function showExportForm( $gadget ) { $this->addHelpLink( 'Extension:Gadgets' ); $output = $this->getOutput(); try { $g = $this->gadgetRepo->getGadget( $gadget ); } catch ( InvalidArgumentException $e ) { $output->showErrorPage( 'error', 'gadgets-not-found', [ $gadget ] ); return; } $this->setHeaders(); $output->setPageTitleMsg( $this->msg( 'gadgets-export-title' ) ); $output->addWikiMsg( 'gadgets-export-text', $gadget, $g->getDefinition() ); $exportList = "MediaWiki:gadget-$gadget\n"; foreach ( $g->getScriptsAndStyles() as $page ) { $exportList .= "$page\n"; } $htmlForm = HTMLForm::factory( 'ooui', [], $this->getContext() ); $htmlForm ->setTitle( SpecialPage::getTitleFor( 'Export' ) ) ->addHiddenField( 'pages', $exportList ) ->addHiddenField( 'wpDownload', '1' ) ->addHiddenField( 'templates', '1' ) ->setAction( $this->getConfig()->get( MainConfigNames::Script ) ) ->setMethod( 'get' ) ->setSubmitText( $this->msg( 'gadgets-export-download' )->text() ) ->prepareForm() ->displayForm( false ); } /** * @inheritDoc */ protected function getGroupName() { return 'wiki'; } }