Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
PFFormEditAction
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 9
1806
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
 show
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 displayTab
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
342
 displayFormChooser
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
132
 getNumPagesPerForm
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
6
 printLinksToFormArray
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 displayForm
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3use MediaWiki\Html\Html;
4use MediaWiki\MediaWikiServices;
5
6/**
7 * Handles the formedit action.
8 *
9 * @author Yaron Koren
10 * @author Stephan Gambke
11 * @file
12 * @ingroup PF
13 */
14
15class PFFormEditAction extends Action {
16
17    /**
18     * Return the name of the action this object responds to
19     * @return string lowercase
20     */
21    public function getName() {
22        return 'formedit';
23    }
24
25    /**
26     * The main action entry point.  Do all output for display and send it to the context
27     * output. Do not use globals $wgOut, $wgRequest, etc, in implementations; use
28     * $this->getOutput(), etc.
29     * @throws ErrorPageError
30     * @return false
31     */
32    public function show() {
33        return self::displayForm( $this, $this->getArticle() );
34    }
35
36    /**
37     * Execute the action in a silent fashion: do not display anything or release any errors.
38     * @return bool whether execution was successful
39     */
40    public function execute() {
41        return true;
42    }
43
44    /**
45     * Adds an "action" (i.e., a tab) to edit the current article with
46     * a form
47     * @param IContextSource $obj
48     * @param array &$links
49     */
50    static function displayTab( $obj, &$links ) {
51        $title = $obj->getTitle();
52        $user = $obj->getUser();
53
54        // Make sure that this is not a special page, and
55        // that the user is allowed to edit it
56        // - this function is almost never called on special pages,
57        // but before SMW is fully initialized, it's called on
58        // Special:SMWAdmin for some reason, which is why the
59        // special-page check is there.
60        if ( !isset( $title ) ||
61            ( $title->getNamespace() == NS_SPECIAL ) ) {
62            return;
63        }
64
65        $form_names = PFFormLinker::getDefaultFormsForPage( $title );
66        if ( count( $form_names ) == 0 ) {
67            return;
68        }
69
70        global $wgPageFormsRenameEditTabs, $wgPageFormsRenameMainEditTab;
71
72        $content_actions = &$links['views'];
73
74        $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
75        $user_can_edit = $permissionManager->userCan( 'edit', $user, $title, $permissionManager::RIGOR_QUICK );
76
77        // Create the form edit tab, and apply whatever changes are
78        // specified by the edit-tab global variables.
79        if ( $wgPageFormsRenameEditTabs ) {
80            $form_edit_tab_msg = $user_can_edit ? 'edit' : 'pf_viewform';
81            if ( array_key_exists( 'edit', $content_actions ) ) {
82                $msg = $user_can_edit ? 'pf_editsource' : 'viewsource';
83                $content_actions['edit']['text'] = wfMessage( $msg )->text();
84            }
85        } else {
86            if ( $user_can_edit ) {
87                $form_edit_tab_msg = $title->exists() ? 'formedit' : 'pf_formcreate';
88            } else {
89                $form_edit_tab_msg = 'pf_viewform';
90            }
91            // Check for renaming of main edit tab only if
92            // $wgPageFormsRenameEditTabs is off.
93            if ( $wgPageFormsRenameMainEditTab ) {
94                if ( array_key_exists( 'edit', $content_actions ) ) {
95                    $msg = $user_can_edit ? 'pf_editsource' : 'viewsource';
96                    $content_actions['edit']['text'] = wfMessage( $msg )->text();
97                }
98            }
99        }
100
101        $class_name = ( $obj->getRequest()->getVal( 'action' ) == 'formedit' ) ? 'selected' : '';
102        $form_edit_tab = [
103            'class' => $class_name,
104            'text' => wfMessage( $form_edit_tab_msg )->text(),
105            'href' => $title->getLocalURL( 'action=formedit' )
106        ];
107
108        // Find the location of the 'edit' tab, and add 'edit
109        // with form' right before it.
110        // This is a "key-safe" splice - it preserves both the keys
111        // and the values of the array, by editing them separately
112        // and then rebuilding the array. Based on the example at
113        // http://us2.php.net/manual/en/function.array-splice.php#31234
114        $tab_keys = array_keys( $content_actions );
115        $tab_values = array_values( $content_actions );
116        $edit_tab_location = array_search( 'edit', $tab_keys );
117
118        // If there's no 'edit' tab, look for the 'view source' tab
119        // instead.
120        if ( $edit_tab_location == null ) {
121            $edit_tab_location = array_search( 'viewsource', $tab_keys );
122        }
123
124        // This should rarely happen, but if there was no edit *or*
125        // view source tab, set the location index to -1, so the
126        // tab shows up near the end.
127        if ( $edit_tab_location == null ) {
128            $edit_tab_location = -1;
129        }
130        array_splice( $tab_keys, $edit_tab_location, 0, 'formedit' );
131        array_splice( $tab_values, $edit_tab_location, 0, [ $form_edit_tab ] );
132        $content_actions = [];
133        foreach ( $tab_keys as $i => $key ) {
134            $content_actions[$key] = $tab_values[$i];
135        }
136
137        if ( !$obj->getUser()->isAllowed( 'viewedittab' ) ) {
138            // The tab can have either of these two actions.
139            unset( $content_actions['edit'] );
140            unset( $content_actions['viewsource'] );
141        }
142    }
143
144    static function displayFormChooser( $output, $title ) {
145        global $wgPageFormsMainFormsMinimum;
146
147        $output->addModules( 'ext.pageforms.main.styles' );
148
149        $targetName = $title->getPrefixedText();
150        $output->setPageTitle( wfMessage( "creating", $targetName )->text() );
151
152        try {
153            $formNames = PFUtils::getAllForms();
154        } catch ( MWException $e ) {
155            $output->addHTML( Html::element( 'div', [ 'class' => 'error' ], $e->getMessage() ) );
156            return;
157        }
158
159        $output->addHTML( Html::element( 'p', null, wfMessage( 'pf-formedit-selectform' )->text() ) );
160        $pagesPerForm = self::getNumPagesPerForm();
161        $totalPages = 0;
162        foreach ( $pagesPerForm as $formName => $numPages ) {
163            $totalPages += $numPages;
164        }
165        // We define "popular forms" as those that are used to
166        // edit more than the specified amount of the wiki's
167        // form-editable pages. (Set by $wgPageFormsMainFormsMinimum,
168        // which by default is 1%.)
169        $popularForms = [];
170        foreach ( $pagesPerForm as $formName => $numPages ) {
171            if ( $numPages > $totalPages * $wgPageFormsMainFormsMinimum ) {
172                $popularForms[] = $formName;
173            }
174        }
175        $otherForms = [];
176        foreach ( $formNames as $i => $formName ) {
177            if ( !in_array( $formName, $popularForms ) ) {
178                $otherForms[] = $formName;
179            }
180        }
181
182        $fe = PFUtils::getSpecialPage( 'FormEdit' );
183
184        if ( count( $popularForms ) > 0 ) {
185            if ( count( $otherForms ) > 0 ) {
186                $output->addHTML( Html::element(
187                    'p',
188                    [],
189                    wfMessage( 'pf-formedit-mainforms' )->text()
190                ) );
191            }
192            $text = self::printLinksToFormArray( $popularForms, $targetName, $fe );
193            $output->addHTML( Html::rawElement( 'div', [ 'class' => 'infoMessage mainForms' ], $text ) );
194        }
195
196        if ( count( $otherForms ) > 0 ) {
197            if ( count( $popularForms ) > 0 ) {
198                $output->addHTML( Html::element(
199                    'p',
200                    [],
201                    wfMessage( 'pf-formedit-otherforms' )->text()
202                ) );
203            }
204            $text = self::printLinksToFormArray( $otherForms, $targetName, $fe );
205            $output->addHTML( Html::rawElement( 'div', [ 'class' => 'infoMessage otherForms' ], $text ) );
206        }
207
208        $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
209        $linkParams = [ 'action' => 'edit', 'redlink' => true ];
210        $noFormLink = $linkRenderer->makeKnownLink( $title, wfMessage( 'pf-formedit-donotuseform' )->escaped(), [], $linkParams );
211        $output->addHTML( Html::rawElement( 'p', null, $noFormLink ) );
212    }
213
214    /**
215     * Find the number of pages on the wiki that use each form, by getting
216     * all the categories that have a #default_form call pointing to a
217     * particular form, and adding up the number of pages in each such
218     * category.
219     * This approach doesn't count #default_form calls for namespaces or
220     * individual pages, but that doesn't seem like a big deal, because,
221     * when creating a page in a namespace that has a form, this interface
222     * probably won't get called anyway; and #default_form calls for
223     * individual pages are (hopefully) pretty rare.
224     * @return int[]
225     */
226    static function getNumPagesPerForm() {
227        $dbr = PFUtils::getReadDB();
228        $res = $dbr->select(
229            [ 'category', 'page', 'page_props' ],
230            [ 'pp_value', 'SUM(cat_pages) AS total_pages' ],
231            [
232                // Keep backward compatibility with
233                // the page property name for
234                // Semantic Forms.
235                'pp_propname' => [ 'PFDefaultForm', 'SFDefaultForm' ]
236            ],
237            __METHOD__,
238            [
239                'GROUP BY' => 'pp_value',
240                'ORDER BY' => 'total_pages DESC',
241                'LIMIT' => 100
242            ],
243            [
244                'page' => [ 'JOIN', 'cat_title = page_title' ],
245                'page_props' => [ 'JOIN', 'page_id = pp_page' ]
246            ]
247        );
248
249        $pagesPerForm = [];
250        while ( $row = $res->fetchRow() ) {
251            $formName = $row['pp_value'];
252            $pagesPerForm[$formName] = $row['total_pages'];
253        }
254        return $pagesPerForm;
255    }
256
257    static function printLinksToFormArray( $formNames, $targetName, $fe ) {
258        $text = '';
259        foreach ( $formNames as $i => $formName ) {
260            if ( $i > 0 ) {
261                $text .= ' <span class="pageforms-separator">&middot;</span> ';
262            }
263
264            // Special handling for forms whose name contains a slash.
265            if ( strpos( $formName, '/' ) !== false ) {
266                $url = $fe->getPageTitle()->getLocalURL( [ 'form' => $formName, 'target' => $targetName ] );
267            } else {
268                $url = $fe->getPageTitle( "$formName/$targetName" )->getLocalURL();
269            }
270            $text .= Html::element( 'a', [ 'href' => $url ], $formName );
271        }
272        return $text;
273    }
274
275    /**
276     * The function called if we're in index.php (as opposed to one of the
277     * special pages)
278     * @param Action $action
279     * @param Article $article
280     * @return true
281     */
282    static function displayForm( $action, $article ) {
283        $output = $action->getOutput();
284        $title = $article->getTitle();
285        $form_names = PFFormLinker::getDefaultFormsForPage( $title );
286        if ( count( $form_names ) == 0 ) {
287            // If no form is set, display an interface to let the
288            // user choose out of all the forms defined on this wiki
289            // (or none at all).
290            self::displayFormChooser( $output, $title );
291            return true;
292        }
293
294        if ( count( $form_names ) > 1 ) {
295            $warning_text = Html::warningBox( wfMessage( 'pf_formedit_morethanoneform' )->text() );
296            $output->addWikiTextAsInterface( $warning_text );
297        }
298
299        $form_name = $form_names[0];
300        $page_name = $title->getPrefixedText();
301
302        $pfFormEdit = new PFFormEdit();
303        $pfFormEdit->printForm( $form_name, $page_name );
304
305        return false;
306    }
307
308    public function doesWrites() {
309        return true;
310    }
311}