Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
9.80% |
5 / 51 |
|
14.29% |
1 / 7 |
CRAP | |
0.00% |
0 / 1 |
| Hooks | |
9.80% |
5 / 51 |
|
14.29% |
1 / 7 |
255.74 | |
0.00% |
0 / 1 |
| getHelpGuiderUrl | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
| getTourNames | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| addTour | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
30 | |||
| onBeforePageDisplay | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
| onResourceLoaderRegisterModules | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
6 | |||
| onRedirectSpecialArticleRedirectParams | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| onMakeGlobalVariablesScript | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace MediaWiki\Extension\GuidedTour; |
| 4 | |
| 5 | use MediaWiki\Json\FormatJson; |
| 6 | use MediaWiki\Output\Hook\BeforePageDisplayHook; |
| 7 | use MediaWiki\Output\Hook\MakeGlobalVariablesScriptHook; |
| 8 | use MediaWiki\Output\OutputPage; |
| 9 | use MediaWiki\Registration\ExtensionRegistry; |
| 10 | use MediaWiki\ResourceLoader as RL; |
| 11 | use MediaWiki\ResourceLoader\Hook\ResourceLoaderRegisterModulesHook; |
| 12 | use MediaWiki\ResourceLoader\ResourceLoader; |
| 13 | use MediaWiki\Skin\Skin; |
| 14 | use MediaWiki\SpecialPage\Hook\RedirectSpecialArticleRedirectParamsHook; |
| 15 | use MediaWiki\Title\Title; |
| 16 | |
| 17 | /** |
| 18 | * Use a hook to include this extension's functionality in pages |
| 19 | * (if the page is called with a tour) |
| 20 | * |
| 21 | * @author Terry Chay tchay@wikimedia.org |
| 22 | * @author Matthew Flaschen mflaschen@wikimedia.org |
| 23 | * @author Luke Welling lwelling@wikimedia.org |
| 24 | */ |
| 25 | class Hooks implements |
| 26 | BeforePageDisplayHook, |
| 27 | ResourceLoaderRegisterModulesHook, |
| 28 | RedirectSpecialArticleRedirectParamsHook, |
| 29 | MakeGlobalVariablesScriptHook |
| 30 | { |
| 31 | // Tour cookie name. It will be prefixed automatically. |
| 32 | public const COOKIE_NAME = '-mw-tour'; |
| 33 | |
| 34 | private const TOUR_PARAM = 'tour'; |
| 35 | |
| 36 | /** |
| 37 | * ResourceLoader callback that adds the page name of a GuidedTour local |
| 38 | * documentation page, to demonstrate showing tour content from pages. This is |
| 39 | * a hack pending forcontent messages: https://phabricator.wikimedia.org/T27349 |
| 40 | * |
| 41 | * If the page does not exist, it will not be set. |
| 42 | * |
| 43 | * @param RL\Context $context |
| 44 | * @return array |
| 45 | */ |
| 46 | public static function getHelpGuiderUrl( RL\Context $context ) { |
| 47 | $data = []; |
| 48 | |
| 49 | $pageName = $context->msg( 'guidedtour-help-guider-url' ) |
| 50 | ->inContentLanguage()->plain(); |
| 51 | $pageTitle = Title::newFromText( $pageName ); |
| 52 | if ( $pageTitle !== null && $pageTitle->exists() ) { |
| 53 | $data['pageName'] = $pageName; |
| 54 | } |
| 55 | |
| 56 | return $data; |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * Parses tour cookie, returning an array of tour names (empty if there is no |
| 61 | * cookie or the format is invalid) |
| 62 | * |
| 63 | * Example cookie. This happens to have multiple tours, but cookies with any |
| 64 | * number of tours are accepted: |
| 65 | * |
| 66 | * { |
| 67 | * version: 1, |
| 68 | * tours: { |
| 69 | * firsttour: { |
| 70 | * step: 4 |
| 71 | * }, |
| 72 | * secondtour: { |
| 73 | * step: 2 |
| 74 | * }, |
| 75 | * thirdtour: { |
| 76 | * step: 3, |
| 77 | * firstArticleId: 38333 |
| 78 | * } |
| 79 | * } |
| 80 | * } |
| 81 | * |
| 82 | * This only supports new-style cookies. Old cookies will be converted on the |
| 83 | * client-side, then the tour module will be loaded. |
| 84 | * |
| 85 | * @param string $cookieValue cookie value |
| 86 | * |
| 87 | * @return array array of tour names, empty if no cookie or cookie is invalid |
| 88 | */ |
| 89 | public static function getTourNames( $cookieValue ) { |
| 90 | if ( $cookieValue !== null ) { |
| 91 | $parsed = FormatJson::decode( $cookieValue, true ); |
| 92 | if ( isset( $parsed['tours'] ) ) { |
| 93 | return array_keys( $parsed['tours'] ); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | return []; |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Adds a built-in or wiki tour. |
| 102 | * |
| 103 | * If user JS is disallowed on this page, it does nothing. |
| 104 | * |
| 105 | * If the built-in one exists as a module, it will add that. |
| 106 | * |
| 107 | * Otherwise, it will add the general guided tour module, which will take care of |
| 108 | * loading the on-wiki tour |
| 109 | * |
| 110 | * It will not attempt to add tours that violate the naming rules. |
| 111 | * |
| 112 | * @param OutputPage $out output page |
| 113 | * @param string $tourName the tour name, such as 'gettingstarted' |
| 114 | * |
| 115 | * @return bool true if a module was added, false otherwise |
| 116 | */ |
| 117 | public static function addTour( $out, $tourName ) { |
| 118 | $isUserJsAllowed = $out->getAllowedModules( RL\Module::TYPE_SCRIPTS ) |
| 119 | >= RL\Module::ORIGIN_USER_INDIVIDUAL; |
| 120 | |
| 121 | // Exclude '-' because MediaWiki message keys use it as a separator after the tourname. |
| 122 | // Exclude '.' because module names use it as a separator. |
| 123 | // "User JS" refers to on-wiki JavaScript. In theory we could still add |
| 124 | // extension-defined tours, but it's more conservative not to. |
| 125 | if ( $isUserJsAllowed && $tourName !== null && strpbrk( $tourName, '-.' ) === false ) { |
| 126 | $tourModuleName = "ext.guidedTour.tour.$tourName"; |
| 127 | if ( $out->getResourceLoader()->getModule( $tourModuleName ) ) { |
| 128 | // Add the tour itself for extension-defined tours. |
| 129 | $out->addModules( $tourModuleName ); |
| 130 | } else { |
| 131 | /* |
| 132 | Otherwise, add the main module, which attempts to import an |
| 133 | on-wiki tour. |
| 134 | */ |
| 135 | $out->addModules( 'ext.guidedTour' ); |
| 136 | } |
| 137 | return true; |
| 138 | } |
| 139 | |
| 140 | return false; |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Handler for BeforePageDisplay hook. |
| 145 | * |
| 146 | * Adds a tour-related module if possible. |
| 147 | * |
| 148 | * If the query parameter is set, it will use that. |
| 149 | * Otherwise, it will use the cookie if that exists. |
| 150 | * |
| 151 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforePageDisplay |
| 152 | * |
| 153 | * @param OutputPage $out OutputPage object |
| 154 | * @param Skin $skin Skin being used. |
| 155 | */ |
| 156 | public function onBeforePageDisplay( $out, $skin ): void { |
| 157 | // test for tour enabled in url first |
| 158 | $request = $out->getRequest(); |
| 159 | $queryTourName = $request->getVal( self::TOUR_PARAM ); |
| 160 | if ( $queryTourName !== null ) { |
| 161 | self::addTour( $out, $queryTourName ); |
| 162 | } else { |
| 163 | $cookieValue = $request->getCookie( self::COOKIE_NAME ); |
| 164 | $tours = self::getTourNames( $cookieValue ); |
| 165 | foreach ( $tours as $tourName ) { |
| 166 | self::addTour( $out, $tourName ); |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * Registers VisualEditor tour if VE is installed |
| 173 | * |
| 174 | * @param ResourceLoader $resourceLoader |
| 175 | */ |
| 176 | public function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ): void { |
| 177 | if ( ExtensionRegistry::getInstance()->isLoaded( 'VisualEditor' ) ) { |
| 178 | $resourceLoader->register( |
| 179 | 'ext.guidedTour.tour.firsteditve', |
| 180 | [ |
| 181 | 'scripts' => 'tours/firsteditve.js', |
| 182 | 'localBasePath' => __DIR__ . '/../modules', |
| 183 | 'remoteExtPath' => 'GuidedTour/modules', |
| 184 | 'dependencies' => 'ext.guidedTour', |
| 185 | 'messages' => [ |
| 186 | 'editsection', |
| 187 | 'publishchanges', |
| 188 | 'guidedtour-tour-firstedit-edit-page-title', |
| 189 | 'guidedtour-tour-firsteditve-edit-page-description', |
| 190 | 'guidedtour-tour-firstedit-edit-section-title', |
| 191 | 'guidedtour-tour-firsteditve-edit-section-description', |
| 192 | 'guidedtour-tour-firstedit-save-title', |
| 193 | 'guidedtour-tour-firsteditve-save-description', |
| 194 | ], |
| 195 | ] |
| 196 | ); |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * @param array &$redirectParams |
| 202 | */ |
| 203 | public function onRedirectSpecialArticleRedirectParams( &$redirectParams ) { |
| 204 | array_push( $redirectParams, self::TOUR_PARAM, 'step' ); |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * @param array &$vars |
| 209 | * @param OutputPage $out |
| 210 | */ |
| 211 | public function onMakeGlobalVariablesScript( &$vars, $out ): void { |
| 212 | GuidedTourLauncher::onMakeGlobalVariablesScript( $vars, $out ); |
| 213 | } |
| 214 | } |