';
$html .= renderPerforcePathLink( $path, TRUE );
$html .= '
[';
$html .= 'addText( getP4Variants( $path ) );
return getP4Variants( $path );
return $response;
}
function tagP4Change( $input, $argv, $parser )
{
global $wgP4EXEC;
global $wgP4PORT;
global $wgP4USER;
global $wgP4PASSWD;
if( isset($argv["style"]) )
{
$range = '@' . $input . ',' . $input;
$cmdline = wfEscapeShellArg( $wgP4EXEC ) .
" -u " . wfEscapeShellArg( $wgP4USER ) .
" -p " . wfEscapeShellArg( $wgP4PORT );
if( $wgP4PASSWD != "" )
$cmdline .= " -P " . wfEscapeShellArg( $wgP4PASSWD );
$cmdline .= " changes " . wfEscapeShellArg( $range );
$text = `{$cmdline}`;
return formatShortChange( $text );
}
else
return "" . htmlspecialchars( $input ) . "";
}
function renderRecentchanges( $input, $argv, &$parser, $format )
{
$parser->disableCache();
$path = isset($argv["path"]) ? $argv["path"] : "";
$num = isset($argv["num"]) ? $argv["num"] : "";
$desc = isset($argv["desc"])? $argv["desc"] : "";
$user = isset($argv["user"])? $argv["user"] : "";
if( $path == "" || $num == "" )
return "Please provide both a depot path(path attribute) and the number of changes to show(num attribute).";
$cmdline = buildP4Cmd();
$cmdline .= " changes -s submitted -m" . escapeshellarg( $num ) . " ";
if ( $desc == "long" )
$cmdline .= "-L ";
if ( $desc == "full" )
$cmdline .= "-l ";
if ( $user != "" )
$cmdline .= "-u " . escapeshellarg( $user ) . " ";
if ( $path != '//...' ) $cmdline .= escapeshellarg( $path ) . " ";
$cmdline .= '2>&1';
$changes = array();
exec( $cmdline, $changes );
$isDarkStripe = true;
$output = "";
$inList = false;
$inListList = false;
foreach( $changes as $value )
{
if ( !preg_match( '/^Change [0-9]+ on/', $value ) )
{
if ( !$inListList )
{
$output .= '';
$inListList = true;
}
$output .= htmlspecialchars( $value );
if ( $value )
$output .= '
';
continue;
}
if ( $inListList )
{
$output .= '
';
$inListList = false;
}
if ( $inList )
{
$output .= "\n";
$inList = false;
}
if( $isDarkStripe )
$output .= "- ";
else
$output .= "
- ";
$inList = true;
$output .= formatShortChange( $value, $format );
$isDarkStripe = !$isDarkStripe;
}
if ( $inListList )
{
$output .= '
';
$inListList = false;
}
if ( $inList )
{
$output .= "\n";
$inList = false;
}
$output .= "";
return $output;
}
function formatShortChange( $text, $format='html', $newwin=FALSE )
{
$target = '';
if ( $newwin )
$target = 'target="_blank" ';
$pattern[0] = '/Change (\d+) /';
$pattern[1] = '/ (\S+)@(\S+)/';
switch( $format )
{
default:
case 'html':
$replacement[0] = 'Change ${1} ';
$replacement[1] = ' ${1}@${2} ';
break;
case 'wiki':
$replacement[0] = 'Change [' . buildURL( 'change', ${1} ) . ' ${1}] ';
$replacement[1] = ' [' . buildURL( 'user', ${1} ) . ' ${1}]@[' . buildURL( 'client', ${2} ) . ' ${2}] ';
break;
}
return preg_replace( $pattern, $replacement, $text );
}
function renderPerforcePathLink( $path, $newwin=FALSE )
{
$target = '';
$type = 'depot_dir';
if ( ( strpos( $path, '...' ) !== FALSE ) || ( strpos( $path, '*' ) !== FALSE ) )
$type = 'depot_file';
if ( $newwin )
$target = 'target="_blank" ';
return ''.htmlspecialchars( $path ).'';
}
function getP4Variants( $path )
{
global $wgP4EXEC;
global $wgP4PORT;
global $wgP4USER;
global $wgP4CLIENT;
global $wgP4PASSWD;
$path = trim( $path );
if ( substr( $path, -1 ) == '/' )
{
$path .= '...';
}
$html = '';
$p4Cmd = $wgP4EXEC .
" -u " . $wgP4USER .
" -p " . $wgP4PORT .
" -c " . $wgP4CLIENT;
if( $wgP4PASSWD != "" )
$p4Cmd .= " -P " . $wgP4PASSWD;
#Infer branch relationships from integed output.
$toFile = '';
$fromFile = '';
$pleft = $path;
$pwild = '';
$pright = '';
if ( substr( $path, -1 ) == '*' )
{
$pleft = substr( $path, 0, -1 );
$pwild = substr( $path, -1 );
}
if ( substr( $path, -3 ) == '...' )
{
$pleft = substr( $path, 0, -3 );
$pwild = substr( $path, -3 );
}
$pllen = strlen( $pleft );
$prlen = strlen( $pright );
if ( ( strpos( $pleft, '...' ) !== FALSE ) || ( strpos( $pleft, '*' ) !== FALSE ) )
return 'Embedded wildcard in '.htmlspecialchars( $path ).' -- unable to figure out branch relationships.';
$cmdline = $p4Cmd . ' -Ztag integrated ' . escapeshellarg( $path );
$descriptors = array ( 1 => array("pipe", "w") );
$branches = array();
$integedproc = proc_open( $cmdline, $descriptors, $pipes );
if ( !is_resource($integedproc) ) return 'Unable to get branch information.';
while ( !feof($pipes[1]) )
{
$line = substr( fgets( $pipes[1] ), 0, -1 ); #chomp newline
if ( substr( $line, 0, 11 ) == '... toFile ' ) $toFile = substr( $line, 11 );
if ( substr( $line, 0, 13 ) == '... fromFile ' ) $fromFile = substr( $line, 13 );
if ( $toFile && $fromFile )
{
#toFile is our path, even if it's really the "from" of the integ. Handy!
if ( substr( $toFile, 0, $pllen ) == $pleft ) #this should always be true
{
$pright = substr( $toFile, $pllen );
$prlen = strlen( $pright );
if ( !$prlen || substr( $fromFile, -$prlen ) == $pright ) #check for a path match
{
if ( $prlen )
$branch = substr( $fromFile, 0, -$prlen ) . $pwild;
else
$branch = $fromFile;
$branches[$branch] = $branch;
}
}
$toFile = '';
$fromFile = '';
}
}
sort ( $branches );
if ( !count( $branches ) )
{
return '';
}
set_time_limit( 300 );
#We now have a list of $branches that each corresponds to $path.
foreach( $branches as $branch )
{
$cmdline = $p4Cmd . ' interchanges ' . escapeshellarg( $branch ) . ' ' . escapeshellarg( $path );
$changes = array();
exec( $cmdline, $changes );
$bvar = FALSE;
foreach( $changes as $change )
{
$cnum = '';
$cnums = array();
if ( preg_match( '/^Change (\d+) /', $change, $cnums ) ) $cnum = $cnums[1];
else continue;
$cvar = FALSE;
$cmdline = $p4Cmd . ' -s -Ztag integrate -n ' . escapeshellarg( $branch.'@'.$cnum.',@'.$cnum ) . ' ' . escapeshellarg( $path );
$integs = array();
exec( $cmdline, $integs );
foreach( $integs as $integ )
{
if ( $integ == 'info1: action integrate' )
{
$cvar = TRUE;
break;
}
$errors = array();
if( preg_match( '/^error: (.*)/', $integ, $errors ) &&
!preg_match( '/already integrated\.$/', $integ ) )
{
$html .= ''.$errors[1].'
';
}
}
if ( $cvar )
{
if ( !$bvar )
{
$bvar = TRUE;
$html .= '- ';
$html .= renderPerforcePathLink( $branch, TRUE );
$html .= '
';
}
$html .= '- ';
$html .= formatShortChange( $change, 'html', TRUE );
$html .= '
';
}
}
if ( $bvar )
{
$html .= '
';
}
}
if ( strpos( $html, '- ' ) === FALSE )
{
$html .= '
- No branches have outstanding changes.
';
}
$html .= '
';
return $html;
}
# Takes array of filelog output and running count
# of what files need doing and what files are done.
# Returns GraphViz output.
function getP4Graph( &$filelog, &$out, &$todo, &$done, &$changes, &$const )
{
$file = '';
$rev = '';
$error = '';
$dirty = FALSE;
foreach( $filelog as $line )
{
$regs = array();
if ( preg_match( '/^... ... (.*) (\/\/[^#]*)#(.*)/', $line, $regs ) )
{
if ( !in_array($regs[2],$todo) && !in_array($regs[2],$done) )
{
array_push ( $todo, $regs[2] );
}
if ( strpos( $regs[1], ' by' ) || strpos( $regs[1], ' into' ) )
continue;
$lbl = $regs[1];
$clr = 'blue';
$sty = 'solid';
switch ( $lbl )
{
case 'branch from':
case 'copy from':
case 'delete from':
$lbl = substr( $lbl, 0, -5 );
break;
case 'moved from':
$lbl = substr( $lbl, 0, -5 );
$clr = 'red';
break;
case 'merge from':
$lbl = substr( $lbl, 0, -5 );
$sty = 'dashed';
break;
case 'ignored':
$lbl = substr( $lbl, 0, -1 );
$sty = 'dotted';
break;
case 'edit from':
$lbl = substr( $lbl, 0, -5 );
$sty = 'dashed';
$clr = 'red';
break;
}
if ( $dirty ) $clr = 'red';
$src = $regs[3];
if ( strpos( $src, '#' ) )
$src = substr( $src, strpos( $src, '#' ) + 1 );
if ( $file && $rev )
{
$out .= ' "'.$regs[2].'#'.$src.'" -> "'.$file.'#'.$rev.'"';
$out .= ' [label="'.$lbl.'" color="'.$clr.'" fontcolor="'.$clr.'" style="'.$sty.'" weight=1];';
$out .= "\n";
}
}
else if ( preg_match( '/^... #([0-9]+) change ([0-9]+) ([a-z\/]+) /', $line, $regs ) )
{
if ( $file && $rev )
{
$out .= ' "'.$file.'#'.$regs[1].'" -> "'.$file.'#'.$rev.'" [weight=1000];'."\n";
}
$rev = $regs[1];
$dirty = ( $regs[3] == 'edit' || $regs[3] == 'add' );
if ( $const == 'change' )
{
array_push( $changes, $regs[2] );
$out .= ' { rank=same "'.$file.'#'.$regs[1].'" -> "'.$regs[2].'" [style=invis]; }'."\n";
}
if ( $const == 'file' )
{
$out .= ' { rank=same "'.$file.'#'.$regs[1].'" -> "'.$file.'" [style=invis]; }'."\n";
}
}
else if ( preg_match( '/^\/\//', $line, $regs ) )
{
$file = $line;
$rev = '';
array_push( $done, $file );
if ( in_array( $file, $todo ) )
{
unset( $todo[array_search($file,$todo)] );
}
}
else $error .= ' ' .$line . "\n";
}
if ( $error )
{
$out = ' ' . substr( $error, 1 ) . "\n" . $out;
}
return $out;
}
# build a URL for the Perforce web tool of your choosing
# if you specify both Swarm and P4Web, it will choose Swarm
function buildURL( $type, $value )
{
global $wgP4WEBURL;
global $wgSWARMURL;
if( $wgSWARMURL != "" )
return buildSwarmURL( $type, $value );
elseif( $wgP4WEBURL != "" )
return buildP4WebURL( $type, $value );
else
return "http://www.priceisrightfail.com";
}
function buildP4WebURL( $type, $value )
{
global $wgP4WEBURL;
$target = "";
switch( $type )
{
case 'change':
$target = "/$value?ac=10";
break;
case 'job':
$target = "/$value?ac=111";
break;
case 'depot_file':
$target = "$value?ac=22";
break;
case 'depot_dir':
$target = "$value?ac=22";
break;
case 'user':
$target = "/$value?ac=17";
break;
case 'client':
$target = "/$value?ac=15";
break;
}
return $wgP4WEBURL . $target;
}
function buildSwarmURL( $type, $value )
{
global $wgSWARMURL;
$target = "";
switch( $type )
{
case 'change':
$target = "/changes/$value";
break;
case 'job':
$target = "/jobs/$value";
break;
case 'depot_file':
$target = "/files$value";
break;
case 'depot_dir':
$target = "files" . preg_replace('/[\.*]+\Z/', '', $value);
break;
case 'user':
$target = "/users/$value";
break;
case 'client':
# waiting on support for this from the Swarm team
break;
}
return $wgSWARMURL . $target;
}
function buildP4Cmd( $server='' )
{
global $wgP4EXEC;
global $wgP4PORT;
global $wgP4USER;
global $wgP4PASSWD;
global $wgP4ALTPORTS;
$cmdline = $wgP4EXEC .
" -u " . $wgP4USER ;
if( $wgP4PASSWD != "" )
$cmdline .= " -P " . $wgP4PASSWD ;
if ( $server == '' || $server == $wgP4PORT )
{
$cmdline .= " -p " . $wgP4PORT;
}
else
{
if ( is_array( $wgP4ALTPORTS) && in_array( $server, $wgP4ALTPORTS ) )
$cmdline .= " -p " . $server;
else
$cmdline .= " -p " . $wgP4PORT;
}
return $cmdline;
}
#returns the common leading substring of two strings, split on '/'.
function strFirstCommon( $str1, $str2 )
{
$path1 = explode( '/', $str1 );
$path2 = explode( '/', $str2 );
$cmn = array();
while ( count( $path1 ) > 1 && count( $path2 ) > 1 )
{
$s1 = array_shift( $path1 );
$s2 = array_shift( $path2 );
if ( $s1 != $s2 ) break;
array_push( $cmn, $s1 );
}
if ( !count( $cmn ) ) return '';
return implode( '/', $cmn ) . '/';
}
#returns the common trailing substring of two strings, split on '/'.
function strLastCommon( $str1, $str2 )
{
$path1 = explode( '/', $str1 );
$path2 = explode( '/', $str2 );
$cmn = array();
while ( count( $path1 ) > 1 && count( $path2 ) > 1 )
{
$s1 = array_pop( $path1 );
$s2 = array_pop( $path2 );
if ( $s1 != $s2 ) break;
array_unshift( $cmn, $s1 );
}
if ( !count( $cmn ) ) return '';
return '/' . implode( '/', $cmn );
}
#takes an array of "p4 -Ztag -Zspecstring specs..." output,
#returns an array of arrays of spec fields.
function getSpecFields( $output )
{
$fields = array();
$fields['@'] = array();
$specstring = array_shift( $output );
$specstring = substr( $specstring, 12 ); # remove '... specdef '
$fields['@'] = preg_split( '/;;/', $specstring );
foreach( $fields['@'] as &$field )
{
$var = preg_split( '/;/', $field );
$field = array_shift( $var );
}
$id = $fields['@'][0]; # name of spec type, e.g. 'job'
$i = -1;
$spec = array();
foreach( $output as $line )
{
$line .= "\n";
$m = array();
if ( preg_match( '/^\.\.\. ([^\s]+)/', $line, $m ) )
{
if ( $m[1] == $id )
{
# Start of a new spec.
if ( array_key_exists( strtolower($id), $spec ) )
{
foreach( $spec as &$f ) { $f = chop( $f ); }
$fields[$spec[strtolower($id)]] = $spec;
}
$i = -1;
$spec = array();
}
if ( in_array( $m[1], $fields['@'] ) &&
array_search( $m[1], $fields['@'] ) > $i )
{
# Start of a new field.
$line = substr( $line, 5 + strlen($m[1]) ); #remove '... Field '
$i = array_search( $m[1], $fields['@'] );
$spec[strtolower($fields['@'][$i])] = '';
}
}
if ( $i < 0 ) { continue; }
$spec[strtolower($fields['@'][$i])] .= $line;
}
if ( array_key_exists( strtolower($id), $spec ) )
{
foreach( $spec as &$f ) { $f = chop( $f ); }
$fields[$spec[strtolower($id)]] = $spec;
}
return $fields;
}
# Compare two arrays by value associated with a particular key.
function compKey( $arr1, $arr2, $key, $order = 1 )
{
# Sort arrays that don't have the key to the front for easy removal.
if ( !array_key_exists( $key, $arr1 ) &&
!array_key_exists( $key, $arr2 ) ) return 0;
if ( !array_key_exists( $key, $arr1 ) ) return -1;
if ( !array_key_exists( $key, $arr2 ) ) return +1;
if ( $arr1[$key] < $arr2[$key] ) return -1 * $order;
if ( $arr1[$key] > $arr2[$key] ) return +1 * $order;
# Existing order is kept in ['@']; preserve if there.
if ( array_key_exists( '@', $arr1 ) && array_key_exists( '@', $arr2 ) )
{
if ( $arr1['@'] < $arr2['@'] ) return -1;
if ( $arr1['@'] > $arr2['@'] ) return +1;
}
return 0;
}
# Takes a formatting string and field to format (in-place).
function formatField( $format, &$field, $fname = '' )
{
$field = str_replace( '!', '!', str_replace( '|', '|', $field ) );
$fname = strtolower( $fname );
$fmt = preg_split( '/\s+/', $format );
foreach( $fmt as $f )
{
$hpos = strrpos( $f, '#' );
if ( $hpos )
{
$flimit = strtolower( substr( $f, $hpos + 1 ) );
if ( $flimit && $flimit != $fname ) continue;
$f = substr( $f, 0, $hpos );
}
if ( !strncasecmp( $f, 'template:', 9 ) )
{
$f = substr( $f, 9 );
$field = '{{'.$f.'|'.$field.'}}';
continue;
}
if ( strpos( $f, 'chars' ) )
{
$field = substr( $field, 0, intval( substr( $f, 0, -5 ) ) );
}
if ( strpos( $f, 'words' ) )
{
$words = explode( ' ', $field );
$words = array_slice( $words, 0, intval( substr( $f, 0, -5 ) ) );
$field = implode( ' ', $words );
}
if ( strpos( $f, 'lines' ) )
{
$lines = explode( "\n", $field );
$lines = array_slice( $lines, 0, intval( substr( $f, 0, -5 ) ) );
$field = implode( "\n", $lines );
}
if ( strpos( $f, 'paras' ) )
{
$paras = explode( "\n\n", $field );
$paras = array_slice( $paras, 0, intval( substr( $f, 0, -5 ) ) );
$field = implode( "\n\n", $paras );
}
if ( strpos( $f, 'sents' ) )
{
$sents = preg_split( '/(\s+[^\s]+\s+[^\s]+[\.!?]+\s+)/',
$field, 0, PREG_SPLIT_DELIM_CAPTURE );
$sents = array_slice( $sents, 0, 2 * intval( substr( $f, 0, -5 ) ) );
$field = implode( '', $sents );
}
if ( $f == 'line' )
{
$field = trim( preg_replace( '/\s+/', ' ', $field ) );
}
if ( $f == 'raw' && strpos( $field, "\n" ) )
{
$field = ''.$field.'
';
}
if ( $f == 'text' && strpos( $field, "\n" ) )
{
$lines = explode( "\n", $field );
$field = "\n ".implode( "\n ", $lines );
}
}
}
# Annotate an rule array with list of matching jobs.
function applyJobQuery( &$rule, $table, $basequery, $maxjobs )
{
$query = $rule['query'];
$query = trim( $query );
if ( $query == 'ALL' || $query == 'ODD' || $query == 'EVEN' ) return;
$query = strtolower( $query );
$field = '';
$value = '';
if ( !strpos( $query, ' ' ) && !strpos( $query, '&' ) &&
!strpos( $query, '|' ) && !strpos( $query, '^' ) &&
!strpos( $query, '*' ) )
{
// We can handle simple queries by ourselves. I hope.
$pair = explode( '=', $query );
if ( count( $pair ) == 2 )
{
$field = $pair[0];
$value = $pair[1];
}
else if ( count( $pair ) == 1 )
{
$value = $pair[0];
}
}
if ( $value )
{
// Handle it.
foreach ( $table as $row )
{
$found = false;
if ( $field )
{
if ( array_key_exists( $field, $row ) &&
stripos( $row[$field], $value ) !== false )
$found = true;
}
else
{
foreach ( $row as $k => $fv )
{
if ( stripos( $fv, $value ) !== false )
{
$found = true;
}
}
}
if ( $found ) $rule['jobs'][] = $row['job'];
}
}
else
{
// Couldn't handle it. Run a new job query.
if ( $basequery ) $newquery = '('.$basequery.') ('.$query.')';
else $newquery = $query;
$cmdline = buildP4Cmd();
$cmdline .= ' jobs -m '.$maxjobs.' -e '.escapeshellarg( $newquery );
$jobs = array();
exec( $cmdline, $jobs );
foreach( $jobs as $j )
{
$jf = explode( ' ', $j );
if ( array_key_exists( $jf[0], $table ) )
$rule['jobs'][] = $jf[0];
}
}
}
# Process per-row job "rules" and annotate job table with results.
function applyJobRules( $rules, &$table, $index )
{
foreach( $rules as $rule )
{
$key = '';
if ( !strcasecmp( substr( $rule['action'], 0, 7 ), 'attrib:' ) )
{
$key = '@attrib';
$action = substr( $rule['action'], 7 );
}
if ( !strcasecmp( substr( $rule['action'], 0, 7 ), 'format:' ) )
{
$key = '@format';
$action = substr( $rule['action'], 7 );
}
if ( !$key ) continue;
foreach( $rule['jobs'] as $j )
{
$table[$index[$j]][$key] .= ' '.$action;
}
}
}
?>