Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialListDuplicatedFiles
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 8
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 isExpensive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isSyndicated
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getQueryInfo
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 preprocessResults
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatResult
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2013 Brian Wolff
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Specials;
10
11use MediaWiki\MainConfigNames;
12use MediaWiki\MediaWikiServices;
13use MediaWiki\Page\LinkBatchFactory;
14use MediaWiki\Skin\Skin;
15use MediaWiki\SpecialPage\QueryPage;
16use MediaWiki\SpecialPage\SpecialPage;
17use MediaWiki\Title\Title;
18use stdClass;
19use Wikimedia\Rdbms\IConnectionProvider;
20use Wikimedia\Rdbms\IReadableDatabase;
21use Wikimedia\Rdbms\IResultWrapper;
22
23/**
24 * List all files where the current version is a duplicate of the current
25 * version of another file.
26 *
27 * @ingroup SpecialPage
28 * @author Brian Wolff
29 */
30class SpecialListDuplicatedFiles extends QueryPage {
31    private int $migrationStage;
32
33    public function __construct(
34        IConnectionProvider $dbProvider,
35        LinkBatchFactory $linkBatchFactory
36    ) {
37        parent::__construct( 'ListDuplicatedFiles' );
38        $this->setDatabaseProvider( $dbProvider );
39        $this->setLinkBatchFactory( $linkBatchFactory );
40        $this->migrationStage = MediaWikiServices::getInstance()->getMainConfig()->get(
41            MainConfigNames::FileSchemaMigrationStage
42        );
43    }
44
45    /** @inheritDoc */
46    public function isExpensive() {
47        return true;
48    }
49
50    /** @inheritDoc */
51    public function isSyndicated() {
52        return false;
53    }
54
55    /**
56     * Get all the duplicates by grouping on sha1s.
57     *
58     * A cheaper (but less useful) version of this
59     * query would be to not care how many duplicates a
60     * particular file has, and do a self-join on image or file table.
61     * However this version should be no more expensive then
62     * Special:MostLinked, which seems to get handled fine
63     * with however we are doing cached special pages.
64     * @return array
65     */
66    public function getQueryInfo() {
67        if ( $this->migrationStage & SCHEMA_COMPAT_READ_OLD ) {
68            $tables = [ 'image' ];
69            $nameField = 'img_name';
70            $hashField = 'img_sha1';
71            $conds = [];
72            $joins = [];
73        } else {
74            $tables = [ 'file', 'filerevision' ];
75            $nameField = 'file_name';
76            $hashField = 'fr_sha1';
77            $conds = [ 'file_deleted' => 0 ];
78            $joins = [ 'filerevision' => [ 'JOIN', 'file_latest = fr_id' ] ];
79        }
80        return [
81            'tables' => $tables,
82            'fields' => [
83                'namespace' => NS_FILE,
84                'title' => "MIN($nameField)",
85                'value' => 'count(*)'
86            ],
87            'conds' => $conds,
88            'join_conds' => $joins,
89            'options' => [
90                'GROUP BY' => $hashField,
91                'HAVING' => 'count(*) > 1',
92            ],
93        ];
94    }
95
96    /**
97     * Pre-fill the link cache
98     *
99     * @param IReadableDatabase $db
100     * @param IResultWrapper $res
101     */
102    public function preprocessResults( $db, $res ) {
103        $this->executeLBFromResultWrapper( $res );
104    }
105
106    /**
107     * @param Skin $skin
108     * @param stdClass $result Result row
109     * @return string
110     */
111    public function formatResult( $skin, $result ) {
112        // Future version might include a list of the first 5 duplicates
113        // perhaps separated by an "↔".
114        $image1 = Title::makeTitle( $result->namespace, $result->title );
115        $dupeSearch = SpecialPage::getTitleFor( 'FileDuplicateSearch', $image1->getDBkey() );
116
117        $msg = $this->msg( 'listduplicatedfiles-entry' )
118            ->params( $image1->getText() )
119            ->numParams( $result->value - 1 )
120            ->params( $dupeSearch->getPrefixedDBkey() );
121
122        return $msg->parse();
123    }
124
125    /** @inheritDoc */
126    public function execute( $par ) {
127        $this->addHelpLink( 'Help:Managing_files' );
128        parent::execute( $par );
129    }
130
131    /** @inheritDoc */
132    protected function getGroupName() {
133        return 'media';
134    }
135}
136
137/** @deprecated class alias since 1.41 */
138class_alias( SpecialListDuplicatedFiles::class, 'SpecialListDuplicatedFiles' );