diff --git a/codepot/etc/codepot.oracle b/codepot/etc/codepot.oracle
index 4cde82d4..9b4a5c12 100644
--- a/codepot/etc/codepot.oracle
+++ b/codepot/etc/codepot.oracle
@@ -103,14 +103,17 @@ CREATE TABLE "cpot_issue" (
CREATE INDEX cpot_issue_index_1 ON "cpot_issue"("projectid", "status", "type", "summary");
CREATE INDEX cpot_issue_index_2 ON "cpot_issue"("projectid", "summary");
-CREATE TABLE "cpot_issue_attachment" (
- "projectid" VARCHAR(32) NOT NULL,
- "issueid" NUMBER(20,0) NOT NULL,
- "name" VARCHAR(255) NOT NULL,
- "encname" VARCHAR(255) NOT NULL,
- "createdon" TIMESTAMP NOT NULL,
- "createdby" VARCHAR(32) NOT NULL,
- UNIQUE ("projectid", "issueid", "name"),
+CREATE TABLE "cpot_issue_file_list" (
+ "projectid" VARCHAR(32) NOT NULL,
+ "issueid" NUMBER(20,0) NOT NULL,
+ "filename" VARCHAR(255) NOT NULL,
+ "encname" VARCHAR(255) NOT NULL,
+ "md5sum" CHAR(32) NOT NULL,
+ "description" CLOB NOT NULL,
+ "createdon" TIMESTAMP NOT NULL,
+ "createdby" VARCHAR(32) NOT NULL,
+ UNIQUE ("projectid", "issueid", "filename"),
+ UNIQUE ("encname"),
CONSTRAINT issue_attachment_projectid FOREIGN KEY ("projectid") REFERENCES "cpot_project"("id"),
CONSTRAINT issue_attachment_issueid FOREIGN KEY ("projectid","issueid") REFERENCES "cpot_issue"("projectid","id")
);
@@ -242,7 +245,7 @@ END;
CREATE OR REPLACE TRIGGER cpot_upon_issue_id_update AFTER UPDATE OF "id" ON "cpot_issue" FOR EACH ROW
BEGIN
- UPDATE "cpot_issue_attachment" SET "issueid" = :new."id" WHERE "projectid" = :old."projectid" AND "issueid" = :old."id";
+ UPDATE "cpot_issue_file_list" SET "issueid" = :new."id" WHERE "projectid" = :old."projectid" AND "issueid" = :old."id";
UPDATE "cpot_issue_change" SET "id" = :new."id" WHERE "projectid" = :old."projectid" AND "id" = :old."id";
UPDATE "cpot_issue_change_attachment" SET "issueid" = :new."id" WHERE "projectid" = :old."projectid" AND "issueid" = :old."id";
END;
diff --git a/codepot/etc/codepot.pgsql b/codepot/etc/codepot.pgsql
index 73d8b7bb..4a629774 100644
--- a/codepot/etc/codepot.pgsql
+++ b/codepot/etc/codepot.pgsql
@@ -135,6 +135,27 @@ CREATE TABLE issue_attachment (
ON DELETE RESTRICT ON UPDATE CASCADE
);
+CREATE TABLE issue_file_list (
+ projectid VARCHAR(32) NOT NULL,
+ issueid BIGINT NOT NULL,
+ filename VARCHAR(255) NOT NULL,
+ encname VARCHAR(255) NOT NULL,
+ md5sum CHAR(32) NOT NULL,
+ description VARCHAR(255) NOT NULL,
+
+ createdon DATETIME NOT NULL,
+ createdby VARCHAR(32) NOT NULL,
+
+ UNIQUE (projectid, issueid, filename),
+ UNIQUE (encname),
+
+ CONSTRAINT issue_file_list_projectid FOREIGN KEY (projectid) REFERENCES project(id)
+ ON DELETE RESTRICT ON UPDATE CASCADE,
+
+ CONSTRAINT issue_file_list_issueid FOREIGN KEY (projectid,issueid) REFERENCES issue(projectid,id)
+ ON DELETE RESTRICT ON UPDATE CASCADE
+);
+
CREATE TABLE issue_change (
projectid VARCHAR(32) NOT NULL,
id BIGINT NOT NULL,
diff --git a/codepot/src/codepot/controllers/issue.php b/codepot/src/codepot/controllers/issue.php
index 5b8b887f..92764bae 100644
--- a/codepot/src/codepot/controllers/issue.php
+++ b/codepot/src/codepot/controllers/issue.php
@@ -257,6 +257,8 @@ class Issue extends Controller
}
}
+/*
+DEPRECATED
function _edit_issue ($projectid, $hexid, $mode)
{
$this->load->helper ('form');
@@ -414,6 +416,7 @@ class Issue extends Controller
return $this->_edit_issue ($projectid, $hexid, 'update');
}
+
function delete ($projectid = '', $hexid = '')
{
$this->load->helper ('form');
@@ -516,6 +519,7 @@ class Issue extends Controller
}
}
+*/
function xhr_create ($projectid = '')
{
@@ -609,7 +613,7 @@ class Issue extends Controller
if ($status == '')
{
- if ($this->issues->create_with_files ($login['id'], $issue, $attached_files, $this->upload) === FALSE)
+ if ($this->issues->createWithFiles ($login['id'], $issue, $attached_files, $this->upload) === FALSE)
{
$status = 'error - ' . $this->issues->getErrorMessage();
}
@@ -681,7 +685,7 @@ class Issue extends Controller
if ($status == '')
{
- if ($this->issues->update_summary_and_description ($login['id'], $issue) === FALSE)
+ if ($this->issues->updateSummaryAndDescription ($login['id'], $issue) === FALSE)
{
$status = 'error - ' . $this->issues->getErrorMessage();
}
@@ -697,6 +701,223 @@ class Issue extends Controller
print $status;
}
+ function xhr_delete ($projectid = '', $issueid = '')
+ {
+ $this->load->model ('ProjectModel', 'projects');
+ $this->load->model ('IssueModel', 'issues');
+
+ $login = $this->login->getUser ();
+
+ if ($login['id'] == '')
+ {
+ $status = 'error - anonymous user';
+ }
+ else
+ {
+ $issueid = $this->converter->HexToAscii ($issueid);
+
+ $project = $this->projects->get ($projectid);
+ if ($project === FALSE)
+ {
+ $status = "error - failed to get the project {$projectid}";
+ }
+ else if ($project === NULL)
+ {
+ $status = "error - no such project {$projectid}";
+ }
+ else if (!$login['sysadmin?'] &&
+ $this->projects->projectHasMember($projectid, $login['id']) === FALSE)
+ {
+ $status = "error - not a member {$login['id']}";
+ }
+ else
+ {
+ $post_delete_confirm = $this->input->post('issue_delete_confirm');
+
+ if ($post_delete_confirm !== FALSE && $post_delete_confirm == 'Y')
+ {
+ if ($this->issues->deleteWithFiles ($login['id'], $projectid, $issueid) === FALSE)
+ {
+ $status = 'error - ' . $this->issues->getErrorMessage();
+ }
+ else
+ {
+ $status = 'ok';
+ }
+ }
+ else
+ {
+ $status = 'error - not confirmed';
+ }
+ }
+ }
+
+ print $status;
+ }
+
+ function xhr_add_file ($projectid = '', $issueid = '')
+ {
+ $this->load->model ('ProjectModel', 'projects');
+ $this->load->model ('IssueModel', 'issues');
+ $this->load->library ('upload');
+
+ $login = $this->login->getUser ();
+ $revision_saved = -1;
+
+ if ($login['id'] == '')
+ {
+ $status = 'error - anonymous user';
+ }
+ else
+ {
+ $issueid = $this->converter->HexToAscii ($issueid);
+
+ $project = $this->projects->get ($projectid);
+ if ($project === FALSE)
+ {
+ $status = "error - failed to get the project {$projectid}";
+ }
+ else if ($project === NULL)
+ {
+ $status = "error - no such project {$projectid}";
+ }
+ else if (!$login['sysadmin?'] &&
+ $this->projects->projectHasMember($projectid, $login['id']) === FALSE)
+ {
+ $status = "error - not a member {$login['id']}";
+ }
+ else
+ {
+ $post_add_file_count = $this->input->post('issue_add_file_count');
+ if ($post_add_file_count === FALSE || $post_add_file_count <= 0) $post_add_file_count = 0;
+
+ $status = '';
+ $add_files = array ();
+ for ($i = 0; $i < $post_add_file_count; $i++)
+ {
+ $fid = "issue_add_file_{$i}";
+ if (array_key_exists($fid, $_FILES) && $_FILES[$fid]['name'] != '')
+ {
+ $d = $this->input->post("file_add_file_desc_{$i}");
+ if ($d === FALSE || ($d = trim($d)) == '') $d = '';
+
+ if (strpos($_FILES[$fid]['name'], ':') !== FALSE ||
+ strpos($_FILES[$fid]['name'], '/') !== FALSE)
+ {
+ // prevents these letters for wiki creole
+ $status = "error - colon or slash not allowed - {$_FILES[$fid]['name']}";
+ break;
+ }
+
+ array_push ($add_files, array ('fid' => $fid, 'name' => $_FILES[$fid]['name'], 'desc' => $d));
+ }
+ }
+
+ if ($status == '')
+ {
+ if (count($add_files) <= 0)
+ {
+ $status = 'error - no files uploaded';
+ }
+ else if ($this->issues->addFiles ($login['id'], $projectid, $issueid, $add_files, $this->upload) === FALSE)
+ {
+ $status = 'error - ' . $this->issues->getErrorMessage();
+ }
+ else
+ {
+ $status = 'ok';
+ }
+ }
+ }
+ }
+
+ print $status;
+ }
+
+ function xhr_edit_file ($projectid = '', $issueid = '')
+ {
+ $this->load->model ('ProjectModel', 'projects');
+ $this->load->model ('IssueModel', 'issues');
+
+ $login = $this->login->getUser ();
+ $revision_saved = -1;
+
+ if ($login['id'] == '')
+ {
+ $status = 'error - anonymous user';
+ }
+ else
+ {
+ $issueid = $this->converter->HexToAscii ($issueid);
+
+ $project = $this->projects->get ($projectid);
+ if ($project === FALSE)
+ {
+ $status = "error - failed to get the project {$projectid}";
+ }
+ else if ($project === NULL)
+ {
+ $status = "error - no such project {$projectid}";
+ }
+ else if (!$login['sysadmin?'] &&
+ $this->projects->projectHasMember($projectid, $login['id']) === FALSE)
+ {
+ $status = "error - not a member {$login['id']}";
+ }
+ else
+ {
+ $post_edit_file_count = $this->input->post('issue_edit_file_count');
+ if ($post_edit_file_count === FALSE || $post_edit_file_count <= 0) $post_edit_file_count = 0;
+
+ $status = '';
+ $edit_files = array ();
+ for ($i = 0; $i < $post_edit_file_count; $i++)
+ {
+ $n = $this->input->post("issue_edit_file_name_{$i}");
+ $k = $this->input->post("issue_edit_file_kill_{$i}");
+ $d = $this->input->post("issue_edit_file_desc_{$i}");
+
+ if ($n != '')
+ {
+ if ($k == 'yes')
+ {
+ array_push ($edit_files, array ('name' => $n, 'kill' => $k));
+ }
+ else if ($d !== FALSE)
+ {
+ if (($d = trim($d)) == '')
+ {
+ $status = "error - no short description for {$n}";
+ break;
+ }
+
+ array_push ($edit_files, array ('name' => $n, 'desc' => $d));
+ }
+ }
+ }
+
+ if ($status == '')
+ {
+ if (count($edit_files) <= 0)
+ {
+ //$status = 'error - no input avaialble';
+ $status = 'ok';
+ }
+ else if ($this->issues->editFiles ($login['id'], $projectid, $issueid, $edit_files) === FALSE)
+ {
+ $status = 'error - ' . $this->issues->getErrorMessage();
+ }
+ else
+ {
+ $status = 'ok';
+ }
+ }
+ }
+ }
+
+ print $status;
+ }
+
private function _handle_file ($login, $projectid, $issueid, $filename)
{
$this->load->model ('ProjectModel', 'projects');
diff --git a/codepot/src/codepot/models/filemodel.php b/codepot/src/codepot/models/filemodel.php
index 35e65380..59071936 100644
--- a/codepot/src/codepot/models/filemodel.php
+++ b/codepot/src/codepot/models/filemodel.php
@@ -241,7 +241,6 @@ class FileModel extends Model
{
if ($i == 0)
{
- $this->errmsg = $this->db->_error_message();
$this->db->trans_rollback ();
return FALSE;
}
diff --git a/codepot/src/codepot/models/issuemodel.php b/codepot/src/codepot/models/issuemodel.php
index a7460643..5e4c160c 100644
--- a/codepot/src/codepot/models/issuemodel.php
+++ b/codepot/src/codepot/models/issuemodel.php
@@ -451,6 +451,7 @@ class IssueModel extends Model
return $this->db->trans_status();
}
+
private function delete_all_files ($files)
{
foreach ($files as $f) @unlink ($f);
@@ -580,7 +581,7 @@ class IssueModel extends Model
return $newid;
}
- function create_with_files ($userid, $issue, $attached_files, $uploader)
+ function createWithFiles ($userid, $issue, $attached_files, $uploader)
{
set_error_handler (array ($this, 'capture_error'));
$errmsg = '';
@@ -589,7 +590,7 @@ class IssueModel extends Model
return $x;
}
- function update_summary_and_description ($userid, $issue)
+ function updateSummaryAndDescription ($userid, $issue)
{
// TODO: check if userid can do this..
$this->db->trans_start ();
@@ -614,6 +615,278 @@ class IssueModel extends Model
return $issue->id;
}
+
+ private function _delete_issue ($userid, $projectid, $issueid)
+ {
+ $this->db->trans_begin (); // manual transaction. not using trans_start().
+
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('issueid', $issueid);
+ $query = $this->db->get ('issue_file_list');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $result = $query->result ();
+ $file_names = array ();
+ foreach ($result as $f)
+ {
+ array_push ($file_names, $f->encname);
+ }
+
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('issueid', $issueid);
+ $this->db->delete ('issue_file_list');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('id', $issueid);
+ $this->db->delete ('issue_change');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('id', $issueid);
+ $this->db->delete ('issue');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $this->db->set ('createdon', date('Y-m-d H:i:s'));
+ $this->db->set ('type', 'issue');
+ $this->db->set ('action', 'delete');
+ $this->db->set ('projectid', $projectid);
+ $this->db->set ('userid', $userid);
+ $this->db->set ('message', $issueid);
+ $this->db->insert ('log');
+
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $file_name_count = count($file_names);
+ for ($i = 0; $i < $file_name_count; $i++)
+ {
+ $encname = $file_names[$i];
+ $path = CODEPOT_ISSUE_FILE_DIR . '/' . $encname;
+ if (@unlink ($path) === FALSE)
+ {
+ if ($i == 0)
+ {
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+ else
+ {
+ // there is no good way to recover from the error.
+ // carry on. some files will get orphaned.
+ }
+ }
+ }
+
+ $this->db->trans_commit ();
+ return TRUE;
+ }
+
+ function deleteWithFiles ($userid, $projectid, $issueid)
+ {
+ set_error_handler (array ($this, 'capture_error'));
+ $errmsg = '';
+ $x = $this->_delete_issue ($userid, $projectid, $issueid);
+ restore_error_handler ();
+ return $x;
+ }
+
+
+ private function _add_files ($userid, $projectid, $issueid, $add_files, $uploader)
+ {
+ $this->db->trans_begin (); // manual transaction. not using trans_start().
+
+ $config['allowed_types'] = '*';
+ $config['upload_path'] = CODEPOT_ISSUE_FILE_DIR;
+ $config['max_size'] = CODEPOT_MAX_UPLOAD_SIZE;
+ $config['encrypt_name'] = TRUE;
+ $config['overwrite'] = FALSE;
+ $config['remove_spaces'] = FALSE;
+ $uploader->initialize ($config);
+
+ $ok_files = array();
+ $file_count = count($add_files);
+ for ($i = 0; $i < $file_count; $i++)
+ {
+ $f = $add_files[$i];
+ if (!$uploader->do_upload($f['fid']))
+ {
+ $this->errmsg = "Failed to upload {$f['name']}";
+ $this->db->trans_rollback ();
+ $this->delete_all_files ($ok_files);
+ return FALSE;
+ }
+
+ $ud = $uploader->data();
+ array_push ($ok_files, $ud['full_path']);
+
+ $md5sum = @md5_file ($ud['full_path']);
+ if ($md5sum === FALSE)
+ {
+ $this->db->trans_rollback ();
+ $this->delete_all_files ($ok_files);
+ return FALSE;
+ }
+
+ $this->db->set ('projectid', $projectid);
+ $this->db->set ('issueid', $issueid);
+ $this->db->set ('filename', $f['name']);
+ $this->db->set ('encname', $ud['file_name']);
+
+ $this->db->set ('md5sum', $md5sum);
+ $this->db->set ('description', $f['desc']);
+ $this->db->insert ('issue_file_list');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ $this->delete_all_files ($ok_files);
+ return FALSE;
+ }
+ }
+
+ $this->db->set ('createdon', date('Y-m-d H:i:s'));
+ $this->db->set ('type', 'issue');
+ $this->db->set ('action', 'update');
+ $this->db->set ('projectid', $projectid);
+ $this->db->set ('userid', $userid);
+ $this->db->set ('message', $issueid);
+ $this->db->insert ('log');
+
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ $this->delete_all_files ($ok_files);
+ return FALSE;
+ }
+
+ $this->db->trans_commit ();
+ return TRUE;
+ }
+
+ function addFiles ($userid, $projectid, $issueid, $add_files, $uploader)
+ {
+ set_error_handler (array ($this, 'capture_error'));
+ $errmsg = '';
+ $x = $this->_add_files ($userid, $projectid, $issueid, $add_files, $uploader);
+ restore_error_handler ();
+ return $x;
+ }
+
+ private function _edit_files ($userid, $projectid, $issueid, $edit_files)
+ {
+ $this->db->trans_begin (); // manual transaction. not using trans_start().
+
+ $kill_files = array();
+ $file_count = count($edit_files);
+ for ($i = 0; $i < $file_count; $i++)
+ {
+ $f = $edit_files[$i];
+
+ if (array_key_exists('kill', $f))
+ {
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('issueid', $issueid);
+ $this->db->where ('filename', $f['name']);
+ $this->db->select ('encname');
+ $query = $this->db->get('issue_file_list');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $result = $query->result ();
+ if (empty($result))
+ {
+ $this->errmsg = "no such file - {$f['name']}";
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ array_push ($kill_files, CODEPOT_ISSUE_FILE_DIR . '/' . $result[0]->encname);
+
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('issueid', $issueid);
+ $this->db->where ('filename', $f['name']);
+ $query = $this->db->delete('issue_file_list');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+ }
+ else if (array_key_exists('desc', $f))
+ {
+ $this->db->where ('projectid', $projectid);
+ $this->db->where ('issueid', $issueid);
+ $this->db->where ('filename', $f['name']);
+ $this->db->set ('description', $f['desc']);
+ $this->db->update ('issue_file_list');
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+ }
+ }
+
+ $this->db->set ('createdon', date('Y-m-d H:i:s'));
+ $this->db->set ('type', 'issue');
+ $this->db->set ('action', 'update');
+ $this->db->set ('projectid', $projectid);
+ $this->db->set ('userid', $userid);
+ $this->db->set ('message', $issueid);
+ $this->db->insert ('log');
+
+ if ($this->db->trans_status() === FALSE)
+ {
+ $this->errmsg = $this->db->_error_message();
+ $this->db->trans_rollback ();
+ return FALSE;
+ }
+
+ $this->delete_all_files ($kill_files);
+ $this->db->trans_commit ();
+ return TRUE;
+ }
+
+ function editFiles ($userid, $projectid, $issueid, $edit_files)
+ {
+ set_error_handler (array ($this, 'capture_error'));
+ $errmsg = '';
+ $x = $this->_edit_files ($userid, $projectid, $issueid, $edit_files);
+ restore_error_handler ();
+ return $x;
+ }
}
?>
diff --git a/codepot/src/codepot/views/file_show.php b/codepot/src/codepot/views/file_show.php
index 459f3e33..cd1f37af 100644
--- a/codepot/src/codepot/views/file_show.php
+++ b/codepot/src/codepot/views/file_show.php
@@ -124,32 +124,32 @@ function kill_edit_file (no)
d.prop ('disabled', true);
}
}
-
}
var delete_in_progress = false;
var add_file_in_progress = false;
var edit_file_in_progress = false;
-var original_file_name = [file_list[$i];
- printf ("%s\t'%s'", (($i == 0)? '': ",\n"), addslashes($f->filename));
-}
-print "\n";
-?>
+var original_file_name = [
+ file_list[$i];
+ printf ("%s\t'%s'", (($i == 0)? '': ",\n"), addslashes($f->filename));
+ }
+ print "\n";
+ ?>
];
var original_file_desc = [
-file_list[$i];
- printf ("%s\t'%s'", (($i == 0)? '': ",\n"), addslashes($f->description));
-}
-print "\n";
-?>
+ file_list[$i];
+ printf ("%s\t'%s'", (($i == 0)? '': ",\n"), addslashes($f->description));
+ }
+ print "\n";
+ ?>
];
$(function () {
diff --git a/codepot/src/codepot/views/issue_home.php b/codepot/src/codepot/views/issue_home.php
index 5e2ceb16..06fc6031 100644
--- a/codepot/src/codepot/views/issue_home.php
+++ b/codepot/src/codepot/views/issue_home.php
@@ -290,7 +290,8 @@ $this->load->view (
),
'ctxmenuitems' => array (
- array ("issue/create/{$project->id}", ' ' . $this->lang->line('New'), 'issue_home_new')
+ // DEPRECATED
+ //array ("issue/create/{$project->id}", ' ' . $this->lang->line('New'), 'issue_home_new')
)
)
);
diff --git a/codepot/src/codepot/views/issue_show.php b/codepot/src/codepot/views/issue_show.php
index 1c3eedca..5d275058 100644
--- a/codepot/src/codepot/views/issue_show.php
+++ b/codepot/src/codepot/views/issue_show.php
@@ -23,6 +23,7 @@
converter->AsciiToHex ($issue->id);
+$issue_file_count = count ($issue->files);
?>