added a primitive revision graph in a code folder
This commit is contained in:
parent
3fab2b34ea
commit
dd619e2e19
@ -168,4 +168,33 @@ class Graph extends Controller
|
||||
$cloc = $cloc = $this->subversion->clocRevByFile ($projectid, $path, $rev);
|
||||
print codepot_json_encode ($cloc);
|
||||
}
|
||||
|
||||
|
||||
function enjson_revision_graph ($projectid = '', $path = '', $rev = SVN_REVISION_HEAD)
|
||||
{
|
||||
$this->load->model ('ProjectModel', 'projects');
|
||||
|
||||
$login = $this->login->getUser ();
|
||||
if (CODEPOT_SIGNIN_COMPULSORY && $login['id'] == '')
|
||||
{
|
||||
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
|
||||
return;
|
||||
}
|
||||
|
||||
$project = $this->projects->get ($projectid);
|
||||
if ($project === FALSE || ($project->public !== 'Y' && $login['id'] == ''))
|
||||
{
|
||||
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->load->model ('SubversionModel', 'subversion');
|
||||
|
||||
$path = $this->converter->HexToAscii ($path);
|
||||
if ($path == '.') $path = ''; /* treat a period specially */
|
||||
$path = $this->_normalize_path ($path);
|
||||
|
||||
$rg = $this->subversion->revisionGraph ($projectid, $path, $rev);
|
||||
print codepot_json_encode ($rg);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
$lang['CODE_COMMIT'] = 'Commit';
|
||||
$lang['CODE_PROPERTIES'] = 'Properties';
|
||||
$lang['CODE_PROPERTY'] = 'Property';
|
||||
$lang['CODE_REVISION_GRAPH'] = 'Revision graph';
|
||||
|
||||
$lang['CODE_MSG_COMMITTED_BY'] = 'Committed by %s';
|
||||
$lang['CODE_MSG_COMMITTED_BY_ON'] = 'Committed by %s on %s';
|
||||
|
@ -2,6 +2,7 @@
|
||||
$lang['CODE_COMMIT'] = '커밋';
|
||||
$lang['CODE_PROPERTIES'] = '속성';
|
||||
$lang['CODE_PROPERTY'] = '속성';
|
||||
$lang['CODE_REVISION_GRAPH'] = '리비전 그래프';
|
||||
|
||||
$lang['CODE_MSG_COMMITTED_BY'] = '커밋터 %s';
|
||||
$lang['CODE_MSG_COMMITTED_BY_ON'] = '커밋터 %s 시간 %s';
|
||||
|
@ -686,8 +686,7 @@ class SubversionModel extends Model
|
||||
}
|
||||
else if ($info[0]['kind'] == SVN_NODE_DIR)
|
||||
{
|
||||
$fileinfo['fullpath'] = substr (
|
||||
$info[0]['url'], strlen($info[0]['repos']));
|
||||
$fileinfo['fullpath'] = substr ($info[0]['url'], strlen($info[0]['repos']));
|
||||
$fileinfo['name'] = $info[0]['path'];
|
||||
$fileinfo['type'] = 'dir';
|
||||
$fileinfo['size'] = 0;
|
||||
@ -1544,7 +1543,6 @@ class SubversionModel extends Model
|
||||
function clocRevByFile ($projectid, $path, $rev)
|
||||
{
|
||||
// this function composes the data as CodeFlower requires
|
||||
|
||||
$stack = array();
|
||||
$cloc = new stdClass();
|
||||
|
||||
@ -1638,6 +1636,166 @@ class SubversionModel extends Model
|
||||
return $cloc;
|
||||
}
|
||||
|
||||
function _add_rg_node (&$nodeids, &$nodes, $name)
|
||||
{
|
||||
if (array_key_exists($name, $nodeids)) return $nodeids[$name];
|
||||
$nid = count($nodeids);
|
||||
array_push ($nodes, array ('id' => $nid, 'label' => $name));
|
||||
$nodeids[$name] = $nid;
|
||||
return $nid;
|
||||
}
|
||||
|
||||
function _add_rg_edge (&$edges, $from, $to, $label)
|
||||
{
|
||||
array_push ($edges, array ('from' => $from, 'to' => $to, 'label' => $label));
|
||||
}
|
||||
|
||||
function revisionGraph ($projectid, $path, $rev)
|
||||
{
|
||||
// this function is almost blind translation of svn-graph.pl
|
||||
|
||||
/* we should get the history from the entire project */
|
||||
$orgurl = 'file://'.$this->_canonical_path(CODEPOT_SVNREPO_DIR."/{$projectid}");
|
||||
|
||||
$startpath = $path;
|
||||
$interesting = array ("{$startpath}:1" => 1);
|
||||
$tracking = array ($startpath => 1);
|
||||
|
||||
$codeline_changes_forward = array ();
|
||||
$codeline_changes_back = array ();
|
||||
$copysource = array ();
|
||||
$copydest = array ();
|
||||
|
||||
$nodeids = array ();
|
||||
$nodes = array ();
|
||||
$edges = array ();
|
||||
|
||||
$log = @svn_log ($orgurl, 1, $rev, 0, SVN_DISCOVER_CHANGED_PATHS);
|
||||
if ($log === FALSE || count($log) <= 0) return FALSE;
|
||||
|
||||
foreach ($log as $l)
|
||||
{
|
||||
$deleted = array();
|
||||
$currev = $l['rev'];
|
||||
|
||||
foreach ($l['paths'] as $p)
|
||||
{
|
||||
$curpath = $p['path'];
|
||||
if ($p['action'] == 'D' && array_key_exists($curpath, $tracking))
|
||||
{
|
||||
/* when an item is moved, D and A are listed in order.
|
||||
* [20] => Array
|
||||
(
|
||||
[rev] => 21
|
||||
[author] => khinsanwai
|
||||
[msg] => Mov tags/1.0.0 to tags/ATI_POC/1.0.0
|
||||
[date] => 2013-09-18T06:39:41.553616Z
|
||||
[paths] => Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
[action] => D
|
||||
[path] => /tags/1.0.0
|
||||
)
|
||||
[1] => Array
|
||||
(
|
||||
[action] => A
|
||||
[path] => /tags/ATI_POC/1.0.0
|
||||
[copyfrom] => /tags/1.0.0
|
||||
[rev] => 20
|
||||
)
|
||||
)
|
||||
)
|
||||
*/
|
||||
//print ("{$curpath}:{$tracking[$curpath]} [label=\"{$curpath}:{$tracking[$curpath]}\\nDelete in {$currev}\", color=red];\n");
|
||||
|
||||
$id1 = $this->_add_rg_node ($nodeids, $nodes, "{$curpath}:{$tracking[$curpath]}");
|
||||
$id2 = $this->_add_rg_node ($nodeids, $nodes, "<<deleted>>");
|
||||
$this->_add_rg_edge ($edges, $id1, $id2, "deleted");
|
||||
|
||||
// i can't simply remove the item from the tracking list.
|
||||
//unset ($tracking[$curpath]);
|
||||
//$deleted["{$curpath}:{$tracking[$curpath]}"] = $id1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists('copyfrom', $p))
|
||||
{
|
||||
$copyfrom_path = $p['copyfrom'];
|
||||
if (array_key_exists($copyfrom_path, $tracking))
|
||||
{
|
||||
$copyfrom_rev = $tracking[$copyfrom_path];
|
||||
if (array_key_exists ("{$copyfrom_path}:{$copyfrom_rev}", $interesting))
|
||||
{
|
||||
$interesting["{$curpath}:{$currev}"] = 1;
|
||||
$tracking[$curpath] = $currev;
|
||||
//print ("{$copyfrom_path}:{$copyfrom_rev} -> {$curpath}:{$currev} [label=\"copy at {$currev}\", color=green];\n");
|
||||
|
||||
$id1 = $this->_add_rg_node ($nodeids, $nodes, "{$copyfrom_path}:{$copyfrom_rev}");
|
||||
$id2 = $this->_add_rg_node ($nodeids, $nodes, "{$curpath}:{$currev}");
|
||||
$this->_add_rg_edge ($edges, $id1, $id2, "copied");
|
||||
|
||||
$copysource["{$copyfrom_path}:{$copyfrom_rev}"] = 1;
|
||||
$copydest["{$curpath}:{$currev}"] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (array_key_exists($curpath, $tracking) && $tracking[$curpath] != $currev)
|
||||
{
|
||||
$codeline_changes_forward["{$curpath}:{$tracking[$curpath]}"] = "{$curpath}:{$currev}";
|
||||
$codeline_changes_back["{$curpath}:{$currev}"] = "{$curpath}:{$tracking[$curpath]}";
|
||||
$interesting["{$curpath}:{$currev}"] = 1;
|
||||
$tracking[$curpath] = $currev;
|
||||
}
|
||||
|
||||
if ($curpath == '/') break;
|
||||
$curpath = dirname ($curpath);
|
||||
}
|
||||
while (1);
|
||||
}
|
||||
|
||||
/*foreach ($deleted as $d => $v)
|
||||
{
|
||||
$id2 = $this->_add_rg_node ($nodeids, $nodes, "<<deleted>>");
|
||||
$this->_add_rg_edge ($edges, $id1, $id2, "deleted");
|
||||
}*/
|
||||
}
|
||||
|
||||
foreach ($codeline_changes_forward as $k => $v)
|
||||
{
|
||||
if (array_key_exists ($k, $codeline_changes_back) && !array_key_exists($k, $copysource)) continue;
|
||||
|
||||
if (!array_key_exists ($k, $codeline_changes_back) || array_key_exists($k, $copysource))
|
||||
{
|
||||
if (array_key_exists($k, $codeline_changes_forward))
|
||||
{
|
||||
$nextchange = $codeline_changes_forward[$k];
|
||||
$changecount = 1;
|
||||
while (1)
|
||||
{
|
||||
if (array_key_exists($nextchange, $copysource) || !array_key_exists($nextchange, $codeline_changes_forward))
|
||||
{
|
||||
//print "{$k} -> {$nextchange} [label={$changecount} change(s)]\n";
|
||||
$id1 = $this->_add_rg_node ($nodeids, $nodes, $k);
|
||||
$id2 = $this->_add_rg_node ($nodeids, $nodes, $nextchange);
|
||||
$this->_add_rg_edge ($edges, $id1, $id2, "{$changecount} change(s)");
|
||||
break;
|
||||
}
|
||||
$changecount++;
|
||||
if (!array_key_exists($nextchange, $codeline_changes_forward)) break;
|
||||
$nextchange = $codeline_changes_forward[$nextchange];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return array ('nodes' => $nodes, 'edges' => $edges);
|
||||
}
|
||||
|
||||
function zipSubdir ($projectid, $path, $rev, $topdir)
|
||||
{
|
||||
// codepot_zip_dir() uses ZipArchive. Check if the class
|
||||
|
@ -37,6 +37,9 @@
|
||||
<script type="text/javascript" src="<?php print base_url_make('/js/d3.min.js')?>"></script>
|
||||
<script type="text/javascript" src="<?php print base_url_make('/js/CodeFlower.js')?>"></script>
|
||||
|
||||
<script type="text/javascript" src="<?php print base_url_make('/js/vis.min.js')?>"></script>
|
||||
<link type="text/css" rel="stylesheet" href="<?php print base_url_make('/css/vis.min.css')?>" />
|
||||
|
||||
<?php
|
||||
$file_count = count($file['content']);
|
||||
|
||||
@ -198,6 +201,67 @@ function show_loc_by_file_graph (response)
|
||||
$("#code_folder_loc_by_file_spin" ).removeClass ("fa-cog fa-spin");
|
||||
}
|
||||
|
||||
function show_revision_graph (response)
|
||||
{
|
||||
var data = $.parseJSON(response);
|
||||
if (data == null)
|
||||
{
|
||||
show_alert ('Invalid data received', "<?php print $this->lang->line('Error')?>");
|
||||
}
|
||||
else if (data.nodes.length <= 0)
|
||||
{
|
||||
show_alert ('No data to show', "<?php print $this->lang->line('Info')?>");
|
||||
}
|
||||
else
|
||||
{
|
||||
var options = {
|
||||
autoResize: true,
|
||||
height: '500px',
|
||||
width: '100%',
|
||||
layout: {
|
||||
hierarchical: {
|
||||
enabled: true,
|
||||
levelSeparation: 150,
|
||||
nodeSpacing: 200,
|
||||
treeSpacing: 400,
|
||||
direction: 'LR', //'LR' 'UD', 'DU', 'RL'
|
||||
sortMethod: 'directed' // 'hubsize'
|
||||
}
|
||||
},
|
||||
edges: {
|
||||
smooth: {
|
||||
type: 'cubicBezier',
|
||||
forceDirection: 'horizontal', // 'vertical',
|
||||
roundness: 0.4
|
||||
}
|
||||
},
|
||||
physics: true
|
||||
};
|
||||
|
||||
var i, j;
|
||||
|
||||
j = data.nodes.length;
|
||||
for (i = 0; i < j; i++)
|
||||
{
|
||||
data.nodes[i].shape = 'box';
|
||||
}
|
||||
|
||||
j = data.edges.length;
|
||||
for (i = 0; i < j; i++)
|
||||
{
|
||||
data.edges[i].length = 60;
|
||||
data.edges[i].width = 1;
|
||||
data.edges[i].arrows = 'to';
|
||||
data.edges[i].font = { color: 'red' };
|
||||
}
|
||||
|
||||
var network = new vis.Network(document.getElementById('code_folder_result_revision_graph'), data, options);
|
||||
}
|
||||
|
||||
$("#code_folder_revision_graph_button").button("enable");
|
||||
$("#code_folder_revision_graph_spin" ).removeClass ("fa-cog fa-spin");
|
||||
}
|
||||
|
||||
function render_readme()
|
||||
{
|
||||
<?php
|
||||
@ -662,7 +726,12 @@ $(function () {
|
||||
"<?php print site_url(); ?>",
|
||||
"/graph/enjson_loc_by_lang/<?php print $project->id; ?>/<?php print $hex_headpath;?><?php print $revreq?>"),
|
||||
context: document.body,
|
||||
success: show_loc_by_lang_graph
|
||||
success: show_loc_by_lang_graph,
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
show_alert (xhr.status + ' ' + thrownError, "<?php print $this->lang->line('Error')?>");
|
||||
$("#code_folder_loc_by_lang_button").button("enable");
|
||||
$("#code_folder_loc_by_lang_spin" ).removeClass ("fa-cog fa-spin");
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
@ -676,7 +745,31 @@ $(function () {
|
||||
"<?php print site_url(); ?>",
|
||||
"/graph/enjson_loc_by_file/<?php print $project->id; ?>/<?php print $hex_headpath;?><?php print $revreq?>"),
|
||||
context: document.body,
|
||||
success: show_loc_by_file_graph
|
||||
success: show_loc_by_file_graph,
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
show_alert (xhr.status + ' ' + thrownError, "<?php print $this->lang->line('Error')?>");
|
||||
$("#code_folder_loc_by_file_button").button("enable");
|
||||
$("#code_folder_loc_by_file_spin" ).removeClass ("fa-cog fa-spin");
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#code_folder_revision_graph_button").button().click (function () {
|
||||
$("#code_folder_revision_graph_button").button("disable");
|
||||
$("#code_folder_revision_graph_spin").addClass ("fa-cog fa-spin");
|
||||
var ajax_req = $.ajax ({
|
||||
url: codepot_merge_path (
|
||||
"<?php print site_url(); ?>",
|
||||
"/graph/enjson_revision_graph/<?php print $project->id; ?>/<?php print $hex_headpath;?><?php print $revreq?>"),
|
||||
context: document.body,
|
||||
success: show_revision_graph,
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
show_alert (xhr.status + ' ' + thrownError, "<?php print $this->lang->line('Error')?>");
|
||||
$("#code_folder_revision_graph_button").button("enable");
|
||||
$("#code_folder_revision_graph_spin" ).removeClass ("fa-cog fa-spin");
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
@ -875,6 +968,11 @@ $this->load->view (
|
||||
print $this->lang->line('File');
|
||||
print '</a>';
|
||||
|
||||
print '<a id="code_folder_revision_graph_button" href="#">';
|
||||
print '<i id="code_folder_revision_graph_spin" class="fa"></i>';
|
||||
print $this->lang->line('CODE_REVISION_GRAPH');
|
||||
print '</a>';
|
||||
|
||||
if ($show_search)
|
||||
{
|
||||
print '<a id="code_folder_search_button" href="#">';
|
||||
@ -921,6 +1019,7 @@ $this->load->view (
|
||||
<div id="code_folder_graph" class="graph">
|
||||
<div id="code_folder_result_loc_by_lang_graph"></div>
|
||||
<div id="code_folder_result_loc_by_file_graph"></div>
|
||||
<div id="code_folder_result_revision_graph"></div>
|
||||
</div>
|
||||
|
||||
<div id="code_folder_search">
|
||||
|
@ -351,9 +351,10 @@ foreach ($urls as $url)
|
||||
</div>
|
||||
|
||||
<?php if (isset($login['id']) && $login['id'] != ''): ?>
|
||||
<!--
|
||||
<div id='project_home_new_form'>
|
||||
<div style='line-height: 2em;'>
|
||||
<?php
|
||||
<?php/*
|
||||
print form_dropdown (
|
||||
'project_home_new_type',
|
||||
$project_type_array,
|
||||
@ -369,7 +370,7 @@ foreach ($urls as $url)
|
||||
$tmpmemb,
|
||||
set_value('project_home_new_owner', (in_array($login['id'], $project->members)? $login['id']: '')),
|
||||
'id="project_home_new_owner"'
|
||||
);
|
||||
);*/
|
||||
?>
|
||||
|
||||
<input type='text' id='project_home_new_summary' name='project_home_new_summary' size='50' placeholder='<?php print $this->lang->line('Summary'); ?>'/>
|
||||
@ -394,6 +395,7 @@ foreach ($urls as $url)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<?php endif; ?>
|
||||
|
||||
<div id='project_home_alert'></div>
|
||||
|
@ -16,6 +16,7 @@ www_DATA = \
|
||||
project.css \
|
||||
site.css \
|
||||
user.css \
|
||||
vis.min.css \
|
||||
wiki.css
|
||||
|
||||
EXTRA_DIST = $(www_DATA)
|
||||
|
1
codepot/src/css/vis.min.css
vendored
Normal file
1
codepot/src/css/vis.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -31,6 +31,7 @@ www_DATA = \
|
||||
CodeFlower.js \
|
||||
pdf.min.js \
|
||||
pdf.worker.min.js \
|
||||
vis.min.js \
|
||||
webodf.js
|
||||
|
||||
EXTRA_DIST = $(www_DATA)
|
||||
|
45
codepot/src/js/vis.min.js
vendored
Normal file
45
codepot/src/js/vis.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user