1<?php
2/**
3 * Formats credits for articles
4 *
5 * Copyright 2004, Evan Prodromou <evan@wikitravel.org>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
20 *
21 * @file
22 * @ingroup Actions
23 * @author <evan@wikitravel.org>
24 */
25
26use MediaWiki\Linker\LinkRenderer;
27use MediaWiki\User\UserFactory;
28
29/**
30 * @ingroup Actions
31 */
32class CreditsAction extends FormlessAction {
33
34	/** @var LinkRenderer */
35	private $linkRenderer;
36
37	/** @var UserFactory */
38	private $userFactory;
39
40	/**
41	 * @param Page $page
42	 * @param IContextSource $context
43	 * @param LinkRenderer $linkRenderer
44	 * @param UserFactory $userFactory
45	 */
46	public function __construct(
47		Page $page,
48		IContextSource $context,
49		LinkRenderer $linkRenderer,
50		UserFactory $userFactory
51	) {
52		parent::__construct( $page, $context );
53		$this->linkRenderer = $linkRenderer;
54		$this->userFactory = $userFactory;
55	}
56
57	public function getName() {
58		return 'credits';
59	}
60
61	protected function getDescription() {
62		return $this->msg( 'creditspage' )->escaped();
63	}
64
65	/**
66	 * This is largely cadged from PageHistory::history
67	 *
68	 * @return string HTML
69	 */
70	public function onView() {
71		$this->getOutput()->addModuleStyles( [
72			'mediawiki.action.styles',
73		] );
74
75		if ( $this->getWikiPage()->getId() == 0 ) {
76			$s = $this->msg( 'nocredits' )->parse();
77		} else {
78			$s = $this->getCredits( -1 );
79		}
80
81		return Html::rawElement( 'div', [ 'id' => 'mw-credits' ], $s );
82	}
83
84	/**
85	 * Get a list of contributors
86	 *
87	 * @param int $cnt Maximum list of contributors to show
88	 * @param bool $showIfMax Whether to contributors if there more than $cnt
89	 * @return string Html
90	 */
91	public function getCredits( $cnt, $showIfMax = true ) {
92		$s = '';
93
94		if ( $cnt != 0 ) {
95			$s = $this->getAuthor();
96			if ( $cnt > 1 || $cnt < 0 ) {
97				$s .= ' ' . $this->getContributors( $cnt - 1, $showIfMax );
98			}
99		}
100
101		return $s;
102	}
103
104	/**
105	 * Get the last author with the last modification time
106	 *
107	 * @return string HTML
108	 */
109	private function getAuthor() {
110		$page = $this->getWikiPage();
111		$user = $this->userFactory->newFromName( $page->getUserText(), UserFactory::RIGOR_NONE );
112
113		$timestamp = $page->getTimestamp();
114		if ( $timestamp ) {
115			$lang = $this->getLanguage();
116			$d = $lang->date( $page->getTimestamp(), true );
117			$t = $lang->time( $page->getTimestamp(), true );
118		} else {
119			$d = '';
120			$t = '';
121		}
122
123		return $this->msg( 'lastmodifiedatby', $d, $t )->rawParams(
124			$this->userLink( $user ) )->params( $user->getName() )->escaped();
125	}
126
127	/**
128	 * Whether we can display the user's real name (not a hidden pref)
129	 *
130	 * @since 1.24
131	 * @return bool
132	 */
133	protected function canShowRealUserName() {
134		$hiddenPrefs = $this->context->getConfig()->get( 'HiddenPrefs' );
135		return !in_array( 'realname', $hiddenPrefs );
136	}
137
138	/**
139	 * Get a list of contributors of $article
140	 * @param int $cnt Maximum list of contributors to show
141	 * @param bool $showIfMax Whether to contributors if there more than $cnt
142	 * @return string Html
143	 */
144	protected function getContributors( $cnt, $showIfMax ) {
145		$contributors = $this->getWikiPage()->getContributors();
146
147		$others_link = false;
148
149		# Hmm... too many to fit!
150		if ( $cnt > 0 && $contributors->count() > $cnt ) {
151			$others_link = $this->othersLink();
152			if ( !$showIfMax ) {
153				return $this->msg( 'othercontribs' )->rawParams(
154					$others_link )->params( $contributors->count() )->escaped();
155			}
156		}
157
158		$real_names = [];
159		$user_names = [];
160		$anon_ips = [];
161
162		# Sift for real versus user names
163		/** @var User $user */
164		foreach ( $contributors as $user ) {
165			$cnt--;
166			if ( $user->isRegistered() ) {
167				$link = $this->link( $user );
168				if ( $this->canShowRealUserName() && $user->getRealName() ) {
169					$real_names[] = $link;
170				} else {
171					$user_names[] = $link;
172				}
173			} else {
174				$anon_ips[] = $this->link( $user );
175			}
176
177			if ( $cnt == 0 ) {
178				break;
179			}
180		}
181
182		$lang = $this->getLanguage();
183
184		if ( count( $real_names ) ) {
185			$real = $lang->listToText( $real_names );
186		} else {
187			$real = false;
188		}
189
190		# "ThisSite user(s) A, B and C"
191		if ( count( $user_names ) ) {
192			$user = $this->msg( 'siteusers' )->rawParams( $lang->listToText( $user_names ) )->params(
193				count( $user_names ) )->escaped();
194		} else {
195			$user = false;
196		}
197
198		if ( count( $anon_ips ) ) {
199			$anon = $this->msg( 'anonusers' )->rawParams( $lang->listToText( $anon_ips ) )->params(
200				count( $anon_ips ) )->escaped();
201		} else {
202			$anon = false;
203		}
204
205		# This is the big list, all mooshed together. We sift for blank strings
206		$fulllist = [];
207		foreach ( [ $real, $user, $anon, $others_link ] as $s ) {
208			if ( $s !== false ) {
209				array_push( $fulllist, $s );
210			}
211		}
212
213		$count = count( $fulllist );
214
215		# "Based on work by ..."
216		return $count
217			? $this->msg( 'othercontribs' )->rawParams(
218				$lang->listToText( $fulllist ) )->params( $count )->escaped()
219			: '';
220	}
221
222	/**
223	 * Get a link to $user's user page
224	 * @param User $user
225	 * @return string Html
226	 */
227	protected function link( User $user ) {
228		if ( $this->canShowRealUserName() && !$user->isAnon() ) {
229			$real = $user->getRealName();
230			if ( $real === '' ) {
231				$real = $user->getName();
232			}
233		} else {
234			$real = $user->getName();
235		}
236
237		return Linker::userLink( $user->getId(), $user->getName(), $real );
238	}
239
240	/**
241	 * Get a link to $user's user page
242	 * @param User $user
243	 * @return string Html
244	 */
245	protected function userLink( User $user ) {
246		$link = $this->link( $user );
247		if ( $user->isAnon() ) {
248			return $this->msg( 'anonuser' )->rawParams( $link )->parse();
249		} elseif ( $this->canShowRealUserName() && $user->getRealName() ) {
250			return $link;
251		} else {
252			return $this->msg( 'siteuser' )->rawParams( $link )->params( $user->getName() )->escaped();
253		}
254	}
255
256	/**
257	 * Get a link to action=credits of $article page
258	 * @return string HTML link
259	 */
260	protected function othersLink() {
261		return $this->linkRenderer->makeKnownLink(
262			$this->getTitle(),
263			$this->msg( 'others' )->text(),
264			[],
265			[ 'action' => 'credits' ]
266		);
267	}
268}
269