1<?php
2
3# Copyright (c) 2012 John Reese
4# Licensed under the MIT license
5
6/**
7 * Display a list of changeset objects in tabular format.
8 * Assumes that a table with four columns has already been defined.
9 * @param array Changeset objects
10 * @param array Repository objects
11 */
12function Source_View_Changesets( $p_changesets, $p_repos=null, $p_show_repos=true ) {
13	if ( !is_array( $p_changesets ) ) {
14		return;
15	}
16
17	plugin_push_current( 'Source' );
18	$t_can_update = access_has_global_level( plugin_config_get( 'update_threshold' ) );
19	plugin_pop_current();
20
21	if ( is_null( $p_repos ) || !is_array( $p_repos ) ) {
22		$t_repos = SourceRepo::load_by_changesets( $p_changesets );
23	} else {
24		$t_repos = $p_repos;
25	}
26
27	$t_use_porting = config_get( 'plugin_Source_enable_porting' );
28
29	foreach( $p_changesets as $t_changeset ) {
30		$t_repo = $t_repos[ $t_changeset->repo_id ];
31		$t_vcs = SourceVCS::repo( $t_repo );
32
33		$t_changeset->load_bugs();
34		$t_changeset->load_files();
35
36		bug_cache_array_rows( $t_changeset->bugs );
37
38		$t_author = Source_View_Author( $t_changeset, false );
39		$t_committer = Source_View_Committer( $t_changeset, false );
40		?>
41
42<tr>
43
44<td class="category" width="25%" rowspan="<?php echo count( $t_changeset->files ) + 1 ?>">
45	<a id="<?php echo $t_changeset->revision; ?>"></a>
46	<p class="no-margin" name="changeset<?php echo $t_changeset->id ?>"><?php echo string_display(
47		( $p_show_repos ? $t_repo->name . ': ' : '' ) .
48		$t_vcs->show_changeset( $t_repo, $t_changeset )
49		) ?></p>
50	<p class="no-margin small lighter">
51		<i class="fa fa-clock-o grey"></i> <?php echo string_display_line( $t_changeset->timestamp ) ?>
52	</p>
53	<p class="no-margin lighter">
54		<i class="fa fa-user grey"></i> <?php echo $t_author ?></a>
55	</p>
56	<?php if ( $t_committer && $t_committer != $t_author ) { ?><br/><span class="small"><?php echo plugin_lang_get( 'committer', 'Source' ), ': ', $t_committer ?></span><?php } ?>
57	<?php if ( $t_use_porting ) { ?>
58		<p class="no-margin small lighter"><?php echo plugin_lang_get( 'ported', 'Source' ), ': ',
59		( $t_changeset->ported ? string_display_line( $t_changeset->ported ) :
60			( is_null( $t_changeset->ported ) ? plugin_lang_get( 'pending', 'Source' ) : plugin_lang_get( 'na', 'Source' ) ) ) ?>
61		</p>
62	<?php } ?>
63		<a class="btn btn-xs btn-primary btn-white btn-round" href="<?php echo plugin_page( 'view', false, 'Source' ) . '&id=' . $t_changeset->id ?>">
64			<?php echo plugin_lang_get( 'details', 'Source' ) ?>
65		</a>
66		<?php
67		if ( $t_url = $t_vcs->url_changeset( $t_repo, $t_changeset ) ) { ?>
68		<a class="btn btn-xs btn-primary btn-white btn-round" href="<?php echo $t_url ?>">
69			<?php echo plugin_lang_get( 'diff', 'Source' ) ?>
70		</a>
71		<?php }
72		?>
73</td>
74
75<?php
76		# Build list of related issues the user has access to, with link
77		$t_view_bug_threshold = config_get('view_bug_threshold');
78		$t_bugs = array_map(
79			'string_get_bug_view_link',
80			array_filter(
81				$t_changeset->bugs,
82				function( $p_bug_id ) use ( $t_view_bug_threshold ) {
83					return bug_exists( $p_bug_id )
84						&& access_has_bug_level( $t_view_bug_threshold, $p_bug_id );
85				}
86			)
87		);
88?>
89<td colspan=2><?php
90	# The commit message is manually transformed (adding href, bug and bugnote
91	# links + nl2br) instead of calling string_display_links(), which avoids
92	# unwanted html tags processing by the MantisCoreFormatting plugin.
93	# Rationale: commit messages being plain text, any html they may contain
94	# should not be considered as formatting and must be displayed as-is.
95	echo string_nl2br(
96			string_process_bugnote_link(
97				string_process_bug_link(
98					string_insert_hrefs(
99						string_html_specialchars( $t_changeset->message )
100		) ) ) );
101	?>
102</td>
103<td>
104<?php
105		if( $t_bugs ) {
106			echo '<span class="bold">',
107				plugin_lang_get( 'affected_issues', 'Source' ),
108				'</span><br>';
109			echo '<span>', implode( ', ', $t_bugs ), '</span>';
110		} elseif( $t_can_update ) {
111?>
112		<form action="<?php echo plugin_page( 'attach' )  ?>" method="post">
113			<?php echo form_security_field( 'plugin_Source_attach' ) ?>
114			<input type="hidden" name="id" value="<?php echo $t_changeset->id ?>"/>
115			<input type="hidden" name="redirect" value="<?php echo $t_changeset->revision ?>"/>
116			<?php echo plugin_lang_get( 'attach_to_issue' ) ?><br>
117			<input type="text" class="input-sm" name="bug_ids" size="12"/>
118			<input type="submit"
119				   class="btn btn-sm btn-primary btn-white btn-round"
120				   value="<?php echo plugin_lang_get( 'attach' ) ?>" />
121		</form>
122<?php
123		}
124?>
125</td>
126</tr>
127
128		<?php foreach ( $t_changeset->files as $t_file ) { ?>
129<tr>
130<td class="small" colspan="2"><?php echo string_display_line( $t_vcs->show_file( $t_repo, $t_changeset, $t_file ) ) ?></td>
131<td class="center" width="15%">
132		<?php
133		if ( $t_url = $t_vcs->url_diff( $t_repo, $t_changeset, $t_file ) ) { ?>
134			<a class="btn btn-xs btn-primary btn-white btn-round" href="<?php echo $t_url ?>">
135				<?php echo plugin_lang_get( 'diff', 'Source' ) ?>
136			</a>
137		<?php }
138		if ( $t_url = $t_vcs->url_file( $t_repo, $t_changeset, $t_file ) ) { ?>
139			<a class="btn btn-xs btn-primary btn-white btn-round" href="<?php echo $t_url ?>">
140				<?php echo plugin_lang_get( 'file', 'Source' ) ?>
141			</a>
142		<?php }
143		?></td>
144</tr>
145		<?php } ?>
146		<?php
147	}
148}
149
150/**
151 * Display the author information for a changeset.
152 * @param object Changeset object
153 * @param boolean Echo information
154 */
155function Source_View_Author( $p_changeset, $p_echo=true ) {
156	$t_author_name = !is_blank( $p_changeset->author ) ? string_display_line( $p_changeset->author ) : false;
157	$t_author_email = !is_blank( $p_changeset->author_email ) ? string_display_line( $p_changeset->author_email ) : false;
158	$t_author_username = $p_changeset->user_id > 0 ? prepare_user_name( $p_changeset->user_id ) : false;
159
160	if ( $t_author_username ) {
161		$t_output =  $t_author_username;
162
163	} else if ( $t_author_name ) {
164		$t_output =  $t_author_name;
165
166	} else {
167		$t_output =  $t_author_email;
168	}
169
170	if ( $p_echo ) {
171		echo $t_output;
172	} else {
173		return $t_output;
174	}
175}
176
177/**
178 * Display the committer information for a changeset.
179 * @param object Changeset object
180 * @param boolean Echo information
181 */
182function Source_View_Committer( $p_changeset, $p_echo=true ) {
183	$t_committer_name = !is_blank( $p_changeset->committer ) ? string_display_line( $p_changeset->committer ) : false;
184	$t_committer_email = !is_blank( $p_changeset->committer_email ) ? string_display_line( $p_changeset->committer_email ) : false;
185	$t_committer_username = $p_changeset->committer_id > 0 ? prepare_user_name( $p_changeset->committer_id ) : false;
186
187	if ( $t_committer_username ) {
188		$t_output =  $t_committer_username;
189
190	} else if ( $t_committer_name ) {
191		$t_output =  $t_committer_name;
192
193	} else {
194		$t_output =  $t_committer_email;
195	}
196
197	if ( $p_echo ) {
198		echo $t_output;
199	} else {
200		return $t_output;
201	}
202}
203
204/**
205 * Display pagination links for changesets
206 * @param string $p_link       URL to target page
207 * @param int    $p_count      Total number of changesets
208 * @param int    $p_current    Current page number
209 * @param int    $p_perpage    Number of changesets per page
210 */
211function Source_View_Pagination( $p_link, $p_current, $p_count, $p_perpage = 25 ) {
212	if( $p_count > $p_perpage ) {
213
214		$t_pages = ceil( $p_count / $p_perpage );
215		$t_block = max( 5, min( round( $t_pages / 10, -1 ), ceil( $t_pages / 6 ) ) );
216		$t_page_set = array();
217
218		$p_link .= '&offset=';
219
220		$t_page_link = function( $p_page, $p_text = null ) use( $p_current, $p_link ) {
221			if( is_null( $p_text ) ) {
222				$p_text = $p_page;
223			}
224			if( is_null( $p_page ) ) {
225				return '...';
226			} elseif( $p_page == $p_current ) {
227				return "<strong>$p_page</strong>";
228			} else {
229				$page_button = '<a class="btn btn-xs btn-primary btn-white btn-round" href="'. $p_link . $p_page .'">'.$p_text.'</a>';
230				return $page_button;
231			}
232		};
233
234		if( $t_pages > 15 ) {
235			$t_used_page = false;
236			$t_pages_per_block = 3;
237			for( $i = 1; $i <= $t_pages; $i++ ) {
238				if( $i <= $t_pages_per_block
239				 || $i > $t_pages - $t_pages_per_block
240				 || ( $i >= $p_current - $t_pages_per_block && $i <= $p_current + $t_pages_per_block )
241				 || $i % $t_block == 0)
242				{
243					$t_page_set[] = $i;
244					$t_used_page = true;
245				} else if( $t_used_page ) {
246					$t_page_set[] = null;
247					$t_used_page = false;
248				}
249			}
250
251		} else {
252			$t_page_set = range( 1, $t_pages );
253		}
254
255		if( $p_current > 1 ) {
256			echo '&nbsp;', $t_page_link( 1, lang_get( 'first' ) );
257			echo '&nbsp;&nbsp;', $t_page_link( $p_current - 1, lang_get( 'prev' ) );
258			echo '&nbsp;&nbsp;';
259		}
260
261		$t_page_set = array_map( $t_page_link, $t_page_set );
262		echo join( ' ', $t_page_set );
263
264		if( $p_current < $t_pages ) {
265			echo '&nbsp;&nbsp;', $t_page_link( $p_current + 1, lang_get( 'next' ) );
266			echo '&nbsp;&nbsp;', $t_page_link( $t_pages, lang_get( 'last' ) );
267		}
268	}
269}
270