* added 'force_project_delete'.

* added the pre-revprop-change hook and the post-revprop-change hook.
This commit is contained in:
hyung-hwan 2010-04-18 12:43:37 +00:00
parent b4fdd8f888
commit c02b59a9ca
16 changed files with 220 additions and 95 deletions

View File

@ -169,7 +169,7 @@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
wwwdir = @wwwdir@
cfg_DATA = codepot.ini codepot.sql codepot.a2ldap
cfg_SCRIPTS = start-commit pre-commit post-commit
cfg_SCRIPTS = start-commit pre-commit post-commit pre-revprop-change post-revprop-change
EXTRA_DIST = $(cfg_DATA) $(cfg_SCRIPTS)
all: all-am

View File

@ -136,6 +136,14 @@ file_dir = "@DEPOTDIR@/files"
;------------------------------------------------------------------------------
log_threshold = 0
;------------------------------------------------------------------------------
; When yes, a project member can delete a non-empty project containing
; wiki pages, file uploads, etc. An empty project can be deleted any time
; regardless of this option. A system administrator(sysadmin_userids) is
; allowed to delete a non-empty project also regardless of this option.
;------------------------------------------------------------------------------
force_project_delete = "no"
;------------------------------------------------------------------------------
; customized footer
;------------------------------------------------------------------------------

View File

@ -1,7 +1,7 @@
#!/bin/sh
REPOBASE="`basename "${1}"`"
REV="${2}"
REVISION="${2}"
USER="${3}"
PROPNAME="${4}"
ACTION="${5}"
@ -9,5 +9,5 @@ ACTION="${5}"
# [STDIN] PROPVAL ** the old property value is passed via STDIN.
# does not care if logging has failed.
wget -q -O- "%API%/logRevpropChange/svn/${REPOBASE}/${REV}" 2>/dev/null
wget -q -O- "%API%/logCodeRevpropChange/svn/${REPOBASE}/${REVISION}/${USER}/${PROPNAME}/${ACTION}" 2>/dev/null
exit 0

View File

@ -1,7 +1,7 @@
#!/bin/sh
REPOBASE="`basename "${1}"`"
USER="${2}"
REVISION="${2}"
USER="${3}"
PROPNAME="${4}"
ACTION="${5}"

View File

@ -54,15 +54,14 @@ class API extends Controller
*/
}
function logCodeRevpropChange ($type, $repo, $rev, $propname)
function logCodeRevpropChange ($type, $repo, $rev, $userid, $propname, $action)
{
$this->check_access ();
if (!isset($repo) || !isset($rev) || !isset($propname)) return;
if (!isset($repo) || !isset($rev) || !isset($propname) || !isset($action)) return;
$this->load->model ('LogModel', 'logs');
// TODO:
//$this->logs->writeCodeRevpropChange ($type, $repo, $rev, '');
$this->logs->writeCodeRevpropChange ($type, $repo, $rev, $userid, $propname, $action);
}
}

View File

@ -304,10 +304,13 @@ class Project extends Controller
{
if ($data['project_confirm'] == 'yes')
{
$result = $this->projects->delete ($login['id'], $project);
$result = $this->projects->delete (
$login['id'], $project,
($login['sysadmin?'] || CODEPOT_FORCE_PROJECT_DELETE));
if ($result === FALSE)
{
$data['message'] = 'DATABASE ERROR';
$data['project'] = $project;
$this->load->view ($this->VIEW_DELETE, $data);
}
else

View File

@ -429,7 +429,7 @@ class Site extends Controller
$this->load->view ($this->VIEW_LOG, $data);
}
function wiki ($xlink)
function wiki ($xlink = '')
{
$login = $this->login->getUser ();
if (CODEPOT_SIGNIN_COMPULSORY && $login['id'] == '')

View File

@ -84,6 +84,7 @@ $lang['MSG_LOG_CHANGE_BY'] = 'Changed by %s';
$lang['MSG_LOG_CREATE_BY'] = 'Created by %s';
$lang['MSG_LOG_DELETE_BY'] = 'Deleted by %s';
$lang['MSG_LOG_UPDATE_BY'] = 'Updated by %s';
$lang['MSG_LOG_REVPROP_CHANGE_BY'] = 'Revision property %s changed by %s';
$lang['MSG_NO_DIFF'] = 'No difference found';
$lang['MSG_NO_CODE_AVAIL'] = 'No source code available';

View File

@ -78,11 +78,13 @@ $lang['Username'] = '사용자명';
$lang['Wiki'] = '위키';
$lang['Wikis'] = '위키';
$lang['MSG_LOG_COMMIT_BY'] = '%s에 의해 커밋되었습니다';
$lang['MSG_LOG_CHANGE_BY'] = '%s에 의해 변경되었습니다';
$lang['MSG_LOG_CREATE_BY'] = '%s에 의해 생성되었습니다';
$lang['MSG_LOG_DELETE_BY'] = '%s에 의해 삭제되었습니다';
$lang['MSG_LOG_UPDATE_BY'] = '%s에 의해 갱신되었습니다';
$lang['MSG_LOG_COMMIT_BY'] = '%s이(가) 커밋했습니다';
$lang['MSG_LOG_CHANGE_BY'] = '%s이(가) 변경했습니다';
$lang['MSG_LOG_CREATE_BY'] = '%s이(가) 생성했습니다';
$lang['MSG_LOG_DELETE_BY'] = '%s이(가) 삭제했습니다';
$lang['MSG_LOG_UPDATE_BY'] = '%s이(가) 갱신했습니다';
$lang['MSG_LOG_REVPROP_CHANGE_BY'] = '리비전 속성 %s을(를) %s이(가) 변경했습니다';
$lang['MSG_NO_DIFF'] = '차이점이 없습니다';
$lang['MSG_NO_CODE_AVAIL'] = '소스코드가 없습니다';

View File

@ -53,6 +53,12 @@ class IssueModel extends Model
if ($search->summary != '') $this->db->like ('summary', $search->summary);
$this->db->select ('count(id) as count');
$query = $this->db->get ('issue');
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_complete ();
return FALSE;
}
$result = $query->result();
$num = empty($result)? 0: $result[0]->count;

View File

@ -23,6 +23,12 @@ class LogModel extends Model
$this->db->select ('count(id) as count');
$query = $this->db->get ('log');
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_complete ();
return FALSE;
}
$result = $query->result();
$num = empty($result)? 0: $result[0]->count;
@ -66,25 +72,44 @@ class LogModel extends Model
if ($row->type == 'code')
{
list($type,$repo,$rev) = split('[,]', $row->message);
$tmp['type'] = $type;
$tmp['repo'] = $repo;
$tmp['rev'] = $rev;
$log = @svn_log (
'file:///'.CODEPOT_SVNREPO_DIR."/{$repo}",
$rev, $rev, 1,SVN_DISCOVER_CHANGED_PATHS);
if ($log === FALSE || count($log) < 1)
if ($row->action == 'commit')
{
$tmp['time'] = '';
$tmp['author'] = '';
$tmp['message'] = '';
list($type,$repo,$rev) = split('[,]', $row->message);
$tmp['type'] = $type;
$tmp['repo'] = $repo;
$tmp['rev'] = $rev;
$log = @svn_log (
'file:///'.CODEPOT_SVNREPO_DIR."/{$repo}",
$rev, $rev, 1,SVN_DISCOVER_CHANGED_PATHS);
if ($log === FALSE || count($log) < 1)
{
$tmp['time'] = '';
$tmp['author'] = '';
$tmp['message'] = '';
}
else
{
$tmp['time'] = $log[0]['date'];
$tmp['author'] = $log[0]['author'];
$tmp['message'] = $log[0]['msg'];
}
}
else
{
$tmp['time'] = $log[0]['date'];
$tmp['author'] = $log[0]['author'];
$tmp['message'] = $log[0]['msg'];
list($type,$repo,$rev,$propname,$action) = split('[,]', $row->message);
$tmp['type'] = $type;
$tmp['repo'] = $repo;
$tmp['rev'] = $rev;
$tmp['propname'] = $propname;
$tmp['action'] = $action;
$tmp['time'] = $row->createdon;
$tmp['author'] = $row->userid;
}
$commits[$count]['message'] = $tmp;
@ -110,6 +135,16 @@ class LogModel extends Model
$this->write ($log);
}
function writeCodeRevpropChange ($type, $repo, $rev, $userid, $propname, $action)
{
$log->type = 'code';
$log->action = 'revpropchange';
$log->projectid = $repo;
$log->userid = $userid;
$log->message = "{$type},{$repo},{$rev},{$propname},{$action}";
$this->write ($log);
}
function write ($log)
{
$this->db->trans_begin ();
@ -119,6 +154,7 @@ class LogModel extends Model
$this->db->set ('projectid', $log->projectid);
$this->db->set ('message', $log->message);
$this->db->set ('createdon', date('Y-m-d H:i:s'));
$this->db->set ('userid', $log->userid);
$this->db->insert ('log');
if ($this->db->trans_status() === FALSE)

View File

@ -57,7 +57,14 @@ class ProjectModel extends Model
if ($search->id != '') $this->db->like ('id', $search->id);
if ($search->name != '') $this->db->like ('name', $search->name);
if ($search->summary != '') $this->db->like ('summary', $search->summary);
$query = $this->db->get ('project');
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_complete ();
return FALSE;
}
$result = $query->result();
$num = empty($result)? 0: $result[0]->count;
@ -146,55 +153,43 @@ class ProjectModel extends Model
return FALSE;
}
// copy hook scripts to the top repository directory
// overwriting existing scripts are ok as they are
// just updated to the latest scripts anyway.
$contents = @file_get_contents("{$cfgdir}/start-commit");
if ($contents === FALSE)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
$hooks = array (
"start-commit",
"post-commit",
"pre-revprop-change",
"post-revprop-change"
);
if (@file_put_contents (
"{$repodir}/start-commit",
str_replace('%API%', $api, $contents)) === FALSE)
foreach ($hooks as $hook)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
// copy hook scripts to the top repository directory
// overwriting existing scripts are ok as they are
// just updated to the latest scripts anyway.
$contents = @file_get_contents("{$cfgdir}/${hook}");
if ($contents === FALSE)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
$contents = @file_get_contents("{$cfgdir}/post-commit");
if ($contents === FALSE)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
if (@file_put_contents (
"{$repodir}/${hook}",
str_replace('%API%', $api, $contents)) === FALSE)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
if (@file_put_contents(
"{$repodir}/post-commit",
str_replace('%API%', $api, $contents)) === FALSE)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
// install hook scripts to the new project repository
if (@chmod ("{$repodir}/start-commit", 0755) === FALSE ||
@chmod ("{$repodir}/post-commit", 0755) === FALSE ||
@symlink ("../../start-commit", "{$repodir}/{$project->id}/hooks/start-commit") === FALSE ||
@symlink ("../../post-commit", "{$repodir}/{$project->id}/hooks/post-commit") === FALSE)
{
// keep {$repodir}/start-commit, {$repodir}/post-commit
// to minimize impact on other projects. just delete the attempted
// project repository directory.
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
// install the hook script to the new project repository
if (@chmod ("{$repodir}/{$hook}", 0755) === FALSE ||
@symlink ("../../{$hook}", "{$repodir}/{$project->id}/hooks/${hook}") === FALSE)
{
$this->deleteDirectory ("{$repodir}/{$project->id}");
$this->db->trans_rollback ();
return FALSE;
}
}
$this->db->trans_commit ();
@ -257,11 +252,36 @@ class ProjectModel extends Model
}
}
function delete ($userid, $project)
function delete ($userid, $project, $force = FALSE)
{
// TODO: check if userid can do this..
$this->db->trans_begin ();
if ($force)
{
$this->db->where ('projectid', $project->id);
$this->db->delete ('wiki');
$this->db->where ('projectid', $project->id);
$this->db->delete ('issue_change');
$this->db->where ('projectid', $project->id);
$this->db->delete ('issue');
$this->db->where ('projectid', $project->id);
$query = $this->db->get ('file');
if ($this->db->trans_status() === FALSE)
{
$this->db->trans_rollback ();
return FALSE;
}
$result = $query->result ();
$this->db->where ('projectid', $project->id);
$this->db->delete ('file');
}
$this->db->where ('id', $project->id);
$this->db->delete ('project');
@ -288,6 +308,13 @@ class ProjectModel extends Model
}
else
{
if ($force && count($result) > 0)
{
// no way to roll back file delete.
// so deletion is done here.
$this->_delete_files_uploaded ($result);
}
$this->db->trans_commit ();
return TRUE;
}
@ -394,6 +421,13 @@ class ProjectModel extends Model
if ($this->db->trans_status() === FALSE) return FALSE;
return ($count == 1)? TRUE: FALSE;
}
function _delete_files_uploaded ($files)
{
foreach ($files as $file)
@unlink (CODEPOT_FILE_DIR . "/{$file->encname}");
}
}
?>

View File

@ -200,14 +200,26 @@ $this->load->view (
print '<td class="details">';
print '<span class="description">';
$fmt = $this->lang->line (
'MSG_LOG_'.strtoupper($log['action']).'_BY');
print htmlspecialchars (sprintf($fmt, $code['author']));
if ($log['action'] == 'revpropchange')
{
$fmt = $this->lang->line ('MSG_LOG_REVPROP_CHANGE_BY');
print htmlspecialchars (sprintf($fmt, $code['propname'], $code['author']));
//$code['action']
}
else
{
$fmt = $this->lang->line (
'MSG_LOG_'.strtoupper($log['action']).'_BY');
print htmlspecialchars (sprintf($fmt, $code['author']));
}
print '</span>';
print '<pre class="message">';
print htmlspecialchars ($code['message']);
print '</pre>';
if ($log['action'] != 'revpropchange')
{
print '<pre class="message">';
print htmlspecialchars ($code['message']);
print '</pre>';
}
}
else
{

View File

@ -118,15 +118,26 @@ $this->load->view (
print '<td></td>';
print '<td colspan=1 class="details">';
print '<span class="description">';
$fmt = $this->lang->line (
'MSG_LOG_'.strtoupper($log['action']).'_BY');
print htmlspecialchars (sprintf($fmt, $x['author']));
if ($log['action'] == 'revpropchange')
{
$fmt = $this->lang->line ('MSG_LOG_REVPROP_CHANGE_BY');
print htmlspecialchars (sprintf($fmt, $x['propname'], $x['author']));
}
else
{
$fmt = $this->lang->line (
'MSG_LOG_'.strtoupper($log['action']).'_BY');
print htmlspecialchars (sprintf($fmt, $x['author']));
}
print '</span>';
print '<pre class="message">';
$sm = strtok (trim ($x['message']), "\r\n");
print htmlspecialchars ($sm);
print '</pre>';
if ($log['action'] != 'revpropchange')
{
print '<pre class="message">';
$sm = strtok (trim ($x['message']), "\r\n");
print htmlspecialchars ($sm);
print '</pre>';
}
print '</td>';
print '</tr>';
}

View File

@ -116,15 +116,27 @@ foreach ($latest_projects as $project)
print '<td></td>';
print '<td colspan=2 class="details">';
print '<span class="description">';
$fmt = $this->lang->line (
'MSG_LOG_'.strtoupper($log['action']).'_BY');
print htmlspecialchars (sprintf($fmt, $x['author']));
if ($log['action'] == 'revpropchange')
{
$fmt = $this->lang->line ('MSG_LOG_REVPROP_CHANGE_BY');
print htmlspecialchars (sprintf($fmt, $x['propname'], $x['author']));
}
else
{
$fmt = $this->lang->line (
'MSG_LOG_'.strtoupper($log['action']).'_BY');
print htmlspecialchars (sprintf($fmt, $x['author']));
}
print '</span>';
print '<pre class="message">';
$sm = strtok (trim ($x['message']), "\r\n");
print htmlspecialchars ($sm);
print '</pre>';
if ($log['action'] != 'revpropchange')
{
print '<pre class="message">';
$sm = strtok (trim ($x['message']), "\r\n");
print htmlspecialchars ($sm);
print '</pre>';
}
print '</td>';
print '</tr>';
}

View File

@ -61,6 +61,7 @@ function load_ini ($file)
array ('log_threshold', 'integer', 0),
array ('force_project_delete', 'boolean', FALSE),
array ('footer', 'string', '')
);