config = $configFactory->makeConfig( 'discussiontools' ); $this->subscriptionStore = $subscriptionStore; $this->userNameUtils = $userNameUtils; $this->userOptionsLookup = $userOptionsLookup; } private function isMobile(): bool { if ( ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) ) { $mobFrontContext = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' ); return $mobFrontContext->shouldDisplayMobileView(); } return false; } /** * Adds DiscussionTools JS to the output. * * This is attached to the MediaWiki 'BeforePageDisplay' hook. * * @param OutputPage $output * @param Skin $skin * @return void This hook must not abort, it must return no value */ public function onBeforePageDisplay( $output, $skin ): void { $user = $output->getUser(); $req = $output->getRequest(); foreach ( HookUtils::FEATURES as $feature ) { // Add a CSS class for each enabled feature if ( HookUtils::isFeatureEnabledForOutput( $output, $feature ) ) { $output->addBodyClasses( "ext-discussiontools-$feature-enabled" ); } } if ( $this->isMobile() && HookUtils::isFeatureEnabledForOutput( $output, HookUtils::VISUALENHANCEMENTS ) ) { $output->addBodyClasses( 'collapsible-headings-collapsed' ); } // Load style modules if the tools can be available for the title // to selectively hide DT features, depending on the body classes added above. $availableForTitle = HookUtils::isAvailableForTitle( $output->getTitle() ); if ( $availableForTitle ) { $output->addModuleStyles( 'ext.discussionTools.init.styles' ); } // Load modules if any DT feature is enabled for this user if ( HookUtils::isFeatureEnabledForOutput( $output ) || // If there's an a/b test we need to include the JS for unregistered users just so // we can make sure we store the bucket ( $this->config->get( 'DiscussionToolsABTest' ) && !$user->isRegistered() ) ) { $output->addModules( [ 'ext.discussionTools.init' ] ); $enabledVars = []; foreach ( HookUtils::FEATURES as $feature ) { $enabledVars[$feature] = HookUtils::isFeatureEnabledForOutput( $output, $feature ); } $output->addJsConfigVars( 'wgDiscussionToolsFeaturesEnabled', $enabledVars ); $editor = $this->userOptionsLookup->getOption( $user, 'discussiontools-editmode' ); // User has no preferred editor yet // If the user has a preferred editor, this will be evaluated in the client if ( !$editor ) { // Check which editor we would use for articles // VE pref is 'visualeditor'/'wikitext'. Here we describe the mode, // not the editor, so 'visual'/'source' $editor = VisualEditorHooks::getPreferredEditor( $user, $req ) === 'visualeditor' ? 'visual' : 'source'; $output->addJsConfigVars( 'wgDiscussionToolsFallbackEditMode', $editor ); } } // This doesn't involve any DB checks, and so we can put it on every // page to make it easy to pick for logging in WikiEditor. If this // becomes not-cheap, move it elsewhere. $abstate = HookUtils::determineUserABTestBucket( $user ); if ( $abstate ) { $output->addJsConfigVars( 'wgDiscussionToolsABTestBucket', $abstate ); } // Replace the action=edit§ion=new form with the new topic tool. if ( HookUtils::shouldOpenNewTopicTool( $output->getContext() ) ) { $output->addJsConfigVars( 'wgDiscussionToolsStartNewTopicTool', true ); // For no-JS compatibility, redirect to the old new section editor if JS is unavailable. // This isn't great, because the user has to load the page twice. But making a page that is // both a view mode and an edit mode seems difficult, so I'm cutting some corners here. // (Code below adapted from VisualEditor.) $params = $output->getRequest()->getValues(); $params['dtenable'] = '0'; $url = wfScript() . '?' . wfArrayToCgi( $params ); $escapedUrl = htmlspecialchars( $url ); // Redirect if the user has no JS (