Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 113
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZObjectEditAction
0.00% covered (danger)
0.00%
0 / 113
0.00% covered (danger)
0.00%
0 / 10
306
0.00% covered (danger)
0.00%
0 / 1
 getName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTargetZObjectWithLabels
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTargetZObject
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getPageMetaTitle
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 getPageTitleMsg
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
6
 getLabelElement
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 getBCP47CodeElements
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
6
 show
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 getRestriction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * WikiLambda edit action for ZObjects
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda;
12
13use MediaWiki\Actions\Action;
14use MediaWiki\Html\Html;
15use MediaWiki\MainConfigNames;
16use MediaWiki\MediaWikiServices;
17use MediaWiki\Skin\Skin;
18use MediaWiki\SpecialPage\SpecialPage;
19
20class ZObjectEditAction extends Action {
21    use ZObjectEditingPageTrait;
22
23    /**
24     * @inheritDoc
25     */
26    public function getName() {
27        return 'edit';
28    }
29
30    /**
31     * Get the type and language labels of the target zObject
32     * @return array
33     */
34    public function getTargetZObjectWithLabels() {
35        return $this->getTargetZObject()->getTypeStringAndLanguage( $this->getLanguage() );
36    }
37
38    /**
39     * Get the target zObject
40     * @return ZObjectContent|bool Found ZObject
41     */
42    public function getTargetZObject() {
43        $zObjectStore = WikiLambdaServices::getZObjectStore();
44        return $zObjectStore->fetchZObjectByTitle( $this->getTitle() );
45    }
46
47    /**
48     * Get page title meta tag
49     * @return string
50     */
51    protected function getPageMetaTitle() {
52        // If the page doesn't exist (e.g. it's been deleted), return nothing.
53        $targetZObject = $this->getTargetZObject();
54        if ( !$targetZObject ) {
55            return '';
56        }
57
58        $sitename = $this->getContext()->getConfig()->get( MainConfigNames::Sitename );
59
60        $zid = $targetZObject->getZid();
61        $label = $targetZObject->getLabels()
62            ->buildStringForLanguage( $this->getLanguage() )
63            ->fallbackWithEnglish()
64            ->getString();
65
66        return $this->msg( 'wikilambda-edit' )->text() . ' ' .
67            ( $label ?: $zid ) . ' - ' .
68            $sitename;
69    }
70
71    /**
72     * Get page header message
73     * @return string
74     */
75    protected function getPageTitleMsg() {
76        // If the page doesn't exist (e.g. it's been deleted), return nothing.
77        $targetZObject = $this->getTargetZObject();
78        if ( !$targetZObject ) {
79            return '';
80        }
81
82        // the language object of the user's preferred language
83        $zObjectLabelsWithLang = $this->getTargetZObjectWithLabels();
84        $label = $this->getLabelElement( $targetZObject );
85        $zObjectId = $targetZObject->getZid();
86        $id = Html::element(
87            'span',
88            [
89                'class' => 'ext-wikilambda-editpage-header__zid'
90            ],
91            $zObjectId
92        );
93
94        /* BCP47 Codes can occur in two places:
95            (1) in front of the entire header - this happens if
96                (a) ONLY the TYPE is in a non-user language OR
97                (b) if both the TYPE and the NAME are in the SAME non-user language
98            (2) in front of the name - this happens if the NAME is in a non-user language
99        */
100        [ $BCP47CodeObjectName, $BCP47CodeObjectType ] = $this->getBCP47CodeElements(
101            $targetZObject,
102            $zObjectLabelsWithLang
103        );
104
105        $prefix = Html::element(
106            'span', [ 'class' => 'ext-wikilambda-editpage-header__title' ],
107            $this->msg( 'wikilambda-edit' )->text()
108        );
109
110        return Html::rawElement(
111            'span',
112            [ 'class' => 'ext-wikilambda-editpage-header' ],
113            " " . $prefix . " " . $BCP47CodeObjectType . " " . $zObjectLabelsWithLang[ 'title' ]
114            . $this->msg( 'colon-separator' )->text() . $BCP47CodeObjectName . $label . ' ' . $id );
115    }
116
117    /**
118     * Get the HTML element for the label.
119     *
120     * @param ZObjectContent $targetZObject The target ZObject.
121     * @return string The HTML string for the label element.
122     */
123    protected function getLabelElement( ZObjectContent $targetZObject ) {
124        [ 'title' => $labelText ] = $targetZObject->getLabels()
125            ->buildStringForLanguage( $this->getLanguage() )
126            ->fallbackWithEnglish()
127            ->placeholderForTitle()
128            ->getStringAndLanguageCode();
129        $untitledStyle = $labelText === wfMessage( 'wikilambda-editor-default-name' )->text() ?
130        'ext-wikilambda-editpage-header__title--untitled' : null;
131
132        return Html::element(
133            'span',
134            [
135                'class' => [
136                    'ext-wikilambda-editpage-header__title',
137                    'ext-wikilambda-editpage-header__title--function-name',
138                    $untitledStyle
139                ]
140            ],
141            $labelText
142        );
143    }
144
145    /**
146     * Get BCP47 code elements for name and type
147     *
148     * @param ZObjectContent $targetZObject The target ZObject.
149     * @param array $zObjectLabelsWithLang
150     * @return array
151     */
152    protected function getBCP47CodeElements( ZObjectContent $targetZObject, array $zObjectLabelsWithLang ) {
153        // used to go from LANG_CODE -> LANG_NAME
154        // TODO (T362246): Dependency-inject
155        $services = MediaWikiServices::getInstance();
156
157        // the BCP47 code of the language currently being rendered for the zObject Type
158        $typeLangCode = $zObjectLabelsWithLang['languageCode'] ?: '';
159        $typeLangLabel = $services->getLanguageNameUtils()->getLanguageName( $typeLangCode );
160
161        // the BCP47 code of the language currently being rendered for the zObject Type
162        $userLangCode = $this->getLanguage()->getCode();
163        $nameLangCode = $targetZObject
164            ->getLabels()
165            ->buildStringForLanguage( $this->getLanguage() )
166            ->fallbackWithEnglish()
167            ->getLanguageProvided() ?? $userLangCode;
168        $nameLangLabel = $services->getLanguageNameUtils()->getLanguageName( $nameLangCode );
169
170        $BCP47CodeObjectName = UIUtils::wrapBCP47CodeInFakeCodexChip(
171            $nameLangCode,
172            $nameLangLabel,
173            UIUtils::getBCP47ClassName( 'name', $nameLangCode, $userLangCode ),
174        );
175        $BCP47CodeObjectType = UIUtils::wrapBCP47CodeInFakeCodexChip(
176            $typeLangCode,
177            $typeLangLabel,
178            UIUtils::getBCP47ClassName( 'type', $typeLangCode, $userLangCode ),
179        );
180
181        return [ $BCP47CodeObjectName, $BCP47CodeObjectType ];
182    }
183
184    public function show() {
185        $output = $this->getOutput();
186        $output->addModules( [ 'ext.wikilambda.app' ] );
187
188        // (T347528) The action's Title object, not the Message getPageTitle() for the HTML page heading
189        $zId = $this->getTitle()->getBaseText();
190
191        $output->addModuleStyles( [ 'ext.wikilambda.editpage.styles' ] );
192
193        // (T328679) If the page doesn't exist yet, route the user to the ZObject creation system
194        // rather than running the code below that assumes the ZObject exists.
195        if ( !$this->getTitle()->exists() ) {
196            $specialTitle = SpecialPage::getTitleFor( 'CreateObject' );
197            $this->getOutput()->redirect( $specialTitle->getFullURL() );
198            return;
199        }
200
201        // (T290217) Set page header
202        $output->setPageTitle( $this->getPageTitleMsg() );
203
204        // (T360169) Set page title meta tag
205        $output->setHTMLTitle( $this->getPageMetaTitle() );
206
207        $zObjectLabelsWithLang = $this->getTargetZObjectWithLabels();
208
209        if ( $zObjectLabelsWithLang[ 'title' ] === 'Function' ) {
210            $url = Skin::makeInternalOrExternalUrl( $this->msg( 'wikilambda-users-help-link' )->text() );
211            $linkLabel = Html::element(
212                'a',
213                [
214                    'class' => 'ext-wikilambda-editpage-header__description--link',
215                    'href' => $url
216                ],
217                $this->msg( 'wikilambda-special-edit-function-definition-special-permission-link-label' )->text()
218            );
219
220            $output->addHtml( Html::rawElement(
221                'div', [ 'class' => 'ext-wikilambda-editpage-header__description' ],
222                $this->msg( 'wikilambda-special-edit-function-definition-description' )->rawParams( $linkLabel )
223                ->escaped()
224            ) );
225        }
226
227        $this->generateZObjectPayload( $this->getContext(), $output, [
228            'createNewPage' => false,
229            'zId' => $zId,
230        ] );
231    }
232
233    /**
234     * @inheritDoc
235     */
236    public function getRestriction() {
237        // This is a very basic check; proper type-specific checking depends on the attemped
238        // edit/creation content, which isn't available yet
239        return 'wikilambda-create';
240    }
241
242    /** @inheritDoc */
243    public function doesWrites() {
244        return true;
245    }
246}