Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 106 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
ExtMobileFrontend | |
0.00% |
0 / 106 |
|
0.00% |
0 / 6 |
1056 | |
0.00% |
0 / 1 |
blankUserPageHTML | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
42 | |||
domParseWithContentProvider | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
domParseMobile | |
0.00% |
0 / 47 |
|
0.00% |
0 / 1 |
110 | |||
buildPageUserObject | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
getUserPageContent | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
30 | |||
getWikibaseDescription | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | use MediaWiki\Context\IContextSource; |
4 | use MediaWiki\Html\TemplateParser; |
5 | use MediaWiki\MediaWikiServices; |
6 | use MediaWiki\Output\OutputPage; |
7 | use MediaWiki\Title\Title; |
8 | use MediaWiki\User\User; |
9 | use MobileFrontend\Api\ApiParseExtender; |
10 | use MobileFrontend\ContentProviders\IContentProvider; |
11 | use MobileFrontend\Features\FeaturesManager; |
12 | use MobileFrontend\Hooks\HookRunner; |
13 | use MobileFrontend\Transforms\LazyImageTransform; |
14 | use MobileFrontend\Transforms\MakeSectionsTransform; |
15 | use MobileFrontend\Transforms\MoveLeadParagraphTransform; |
16 | use Wikibase\Client\WikibaseClient; |
17 | use Wikibase\DataModel\Entity\ItemId; |
18 | use Wikibase\DataModel\Services\Lookup\TermLookupException; |
19 | use Wikimedia\IPUtils; |
20 | |
21 | /** |
22 | * Implements additional functions to use in MobileFrontend |
23 | */ |
24 | class ExtMobileFrontend { |
25 | /** |
26 | * Provide alternative HTML for a user page which has not been created. |
27 | * Let the user know about it with pretty graphics and different texts depending |
28 | * on whether the user is the owner of the page or not. |
29 | * @internal Only for use inside MobileFrontend. |
30 | * @param OutputPage $out |
31 | * @param Title $title |
32 | * @return string that is empty if the transform does not apply. |
33 | */ |
34 | public static function blankUserPageHTML( OutputPage $out, Title $title ) { |
35 | $pageUser = self::buildPageUserObject( $title ); |
36 | $isHidden = $pageUser && $pageUser->isHidden(); |
37 | $canViewHidden = !$isHidden || $out->getAuthority()->isAllowed( 'hideuser' ); |
38 | |
39 | $out->addModuleStyles( [ |
40 | 'mobile.userpage.styles', 'mobile.userpage.images' |
41 | ] ); |
42 | |
43 | if ( $pageUser && !$title->exists() && $canViewHidden ) { |
44 | return self::getUserPageContent( |
45 | $out, $pageUser, $title ); |
46 | } else { |
47 | return ''; |
48 | } |
49 | } |
50 | |
51 | /** |
52 | * Obtains content using the given content provider and routes it to the mobile formatter |
53 | * if required. |
54 | * |
55 | * @param IContentProvider $provider |
56 | * @param OutputPage $out |
57 | * @param bool $mobileFormatHtml whether content should be run through the MobileFormatter |
58 | * |
59 | * @return string |
60 | */ |
61 | public static function domParseWithContentProvider( |
62 | IContentProvider $provider, |
63 | OutputPage $out, |
64 | $mobileFormatHtml = true |
65 | ) { |
66 | $html = $provider->getHTML(); |
67 | |
68 | // If we're not running the formatter we can exit earlier |
69 | if ( !$mobileFormatHtml ) { |
70 | return $html; |
71 | } else { |
72 | return self::domParseMobile( $out, $html ); |
73 | } |
74 | } |
75 | |
76 | /** |
77 | * Transforms content to be mobile friendly version. |
78 | * Filters out various elements and runs the MobileFormatter. |
79 | * |
80 | * @param OutputPage $out |
81 | * @param string $html to render. |
82 | * |
83 | * @return string |
84 | */ |
85 | public static function domParseMobile( OutputPage $out, $html = '' ) { |
86 | $services = MediaWikiServices::getInstance(); |
87 | /** @var FeaturesManager $featuresManager */ |
88 | $featuresManager = $services->getService( 'MobileFrontend.FeaturesManager' ); |
89 | /** @var MobileContext $context */ |
90 | $context = $services->getService( 'MobileFrontend.Context' ); |
91 | $config = $services->getService( 'MobileFrontend.Config' ); |
92 | |
93 | $title = $out->getTitle(); |
94 | $ns = $title->getNamespace(); |
95 | $action = $context->getRequest()->getText( 'action', 'view' ); |
96 | $isView = $action === 'view' || ApiParseExtender::isParseAction( $action ); |
97 | |
98 | $enableSections = ( |
99 | // Don't collapse sections e.g. on JS pages |
100 | $title->canExist() |
101 | && $title->getContentModel() == CONTENT_MODEL_WIKITEXT |
102 | // And not in certain namespaces |
103 | && !in_array( $ns, $config->get( 'MFNamespacesWithoutCollapsibleSections' ) ) |
104 | // And not when what's shown is not actually article text |
105 | && $isView |
106 | ); |
107 | |
108 | // https://phabricator.wikimedia.org/T232690 |
109 | if ( !MobileFormatter::canApply( $html, $config->get( 'MFMobileFormatterOptions' ) ) ) { |
110 | // In future we might want to prepend a message feeding |
111 | // back to the user that the page is not mobile friendly. |
112 | return $html; |
113 | } |
114 | |
115 | $formatter = new MobileFormatter( |
116 | MobileFormatter::wrapHtml( $html ), |
117 | $title, |
118 | $config, |
119 | $context |
120 | ); |
121 | |
122 | $hookRunner = new HookRunner( $services->getHookContainer() ); |
123 | $hookRunner->onMobileFrontendBeforeDOM( $context, $formatter ); |
124 | |
125 | $shouldLazyTransformImages = $featuresManager->isFeatureAvailableForCurrentUser( 'MFLazyLoadImages' ); |
126 | $leadParagraphEnabled = in_array( $ns, $config->get( 'MFNamespacesWithLeadParagraphs' ) ); |
127 | $showFirstParagraphBeforeInfobox = $leadParagraphEnabled && |
128 | $featuresManager->isFeatureAvailableForCurrentUser( 'MFShowFirstParagraphBeforeInfobox' ); |
129 | |
130 | $transforms = []; |
131 | if ( $enableSections ) { |
132 | $options = $config->get( 'MFMobileFormatterOptions' ); |
133 | $topHeadingTags = $options['headings']; |
134 | |
135 | $transforms[] = new MakeSectionsTransform( |
136 | $topHeadingTags, |
137 | true |
138 | ); |
139 | } |
140 | |
141 | if ( $shouldLazyTransformImages ) { |
142 | $transforms[] = new LazyImageTransform( $config->get( 'MFLazyLoadSkipSmallImages' ) ); |
143 | } |
144 | |
145 | if ( $showFirstParagraphBeforeInfobox ) { |
146 | $transforms[] = new MoveLeadParagraphTransform( $title, $title->getLatestRevID() ); |
147 | } |
148 | |
149 | $start = microtime( true ); |
150 | $formatter->applyTransforms( $transforms ); |
151 | $end = microtime( true ); |
152 | $report = sprintf( "MobileFormatter took %.3f seconds", $end - $start ); |
153 | |
154 | return $formatter->getText() . "\n<!-- $report -->"; |
155 | } |
156 | |
157 | /** |
158 | * Return new User object based on username or IP address. |
159 | * @param Title $title |
160 | * @return User|null |
161 | */ |
162 | private static function buildPageUserObject( Title $title ) { |
163 | $titleText = $title->getText(); |
164 | |
165 | $usernameUtils = MediaWikiServices::getInstance()->getUserNameUtils(); |
166 | if ( $usernameUtils->isIP( $titleText ) || IPUtils::isIPv6( $titleText ) ) { |
167 | return User::newFromAnyId( null, $titleText, null ); |
168 | } |
169 | |
170 | $user = User::newFromName( $titleText ); |
171 | if ( $user && $user->isRegistered() ) { |
172 | return $user; |
173 | } |
174 | |
175 | return null; |
176 | } |
177 | |
178 | /** |
179 | * Generate user page content for non-existent user pages |
180 | * |
181 | * @param IContextSource $output |
182 | * @param User $pageUser owner of the user page |
183 | * @param Title $title |
184 | * @return string |
185 | */ |
186 | protected static function getUserPageContent( IContextSource $output, |
187 | User $pageUser, Title $title |
188 | ) { |
189 | /** @var MobileContext $context */ |
190 | $context = MediaWikiServices::getInstance()->getService( 'MobileFrontend.Context' ); |
191 | $pageUsername = $pageUser->getName(); |
192 | // Is the current user viewing their own page? |
193 | $isCurrentUser = $output->getUser()->getName() === $pageUsername; |
194 | |
195 | $data = [ |
196 | 'userImageClass' => 'userpage-image-placeholder', |
197 | ]; |
198 | $data['ctaHeading'] = $isCurrentUser ? |
199 | $context->msg( 'mobile-frontend-user-page-no-owner-page-yet' )->text() : |
200 | $context->msg( 'mobile-frontend-user-page-no-page-yet', $pageUsername )->parse(); |
201 | $data['ctaDescription'] = $isCurrentUser ? |
202 | $context->msg( |
203 | 'mobile-frontend-user-page-describe-yourself', |
204 | $context->msg( 'mobile-frontend-user-page-describe-yourself-editors' )->text() |
205 | )->text() : |
206 | $context->msg( 'mobile-frontend-user-page-desired-action', $pageUsername )->parse(); |
207 | $data['createPageLinkLabel'] = $isCurrentUser ? |
208 | $context->msg( 'mobile-frontend-user-page-create-owner-page-link-label' )->text() : |
209 | $context->msg( |
210 | 'mobile-frontend-user-page-create-user-page-link-label', |
211 | $pageUser->getUserPage()->getBaseTitle() |
212 | )->parse(); |
213 | // Mobile editor has trouble when section is not specified. |
214 | // It doesn't matter here since the page doesn't exist. |
215 | $data['editUrl'] = $title->getLinkURL( [ 'action' => 'edit', 'section' => 0 ] ); |
216 | $data['editSection'] = 0; |
217 | $data['createPageLinkAdditionalClasses'] = $isCurrentUser ? |
218 | 'cdx-button cdx-button--action-progressive cdx-button--weight-primary' : ''; |
219 | |
220 | $templateParser = new TemplateParser( __DIR__ . '/templates' ); |
221 | return $templateParser->processTemplate( 'UserPageCta', $data ); |
222 | } |
223 | |
224 | /** |
225 | * Returns a short description of a page from Wikidata |
226 | * |
227 | * @param string $item Wikibase id of the page |
228 | * @return string|null |
229 | */ |
230 | public static function getWikibaseDescription( $item ) { |
231 | if ( !ExtensionRegistry::getInstance()->isLoaded( 'WikibaseClient' ) ) { |
232 | return null; |
233 | } |
234 | |
235 | $contLang = MediaWikiServices::getInstance()->getContentLanguage(); |
236 | $termLookup = WikibaseClient::getTermLookup(); |
237 | try { |
238 | $itemId = new ItemId( $item ); |
239 | } catch ( InvalidArgumentException $exception ) { |
240 | return null; |
241 | } |
242 | |
243 | try { |
244 | return $termLookup->getDescription( $itemId, $contLang->getCode() ); |
245 | } catch ( TermLookupException $exception ) { |
246 | return null; |
247 | } |
248 | } |
249 | } |