From cf532ebaf9d8096b95e8bdf41d24733a1c6a2d8d Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sun, 16 Aug 2015 11:06:39 +0000 Subject: [PATCH] added a new feature that allows you to create files by uploading files. this also allows creation of directories. added new configuration items: commit_notification and commit_review_notification --- codepot/etc/codepot.ini.in | 10 + codepot/etc/post-commit.in | 31 ++- codepot/src/codepot/controllers/code.php | 188 +++++++++++------- .../src/codepot/helpers/codepot_helper.php | 29 ++- .../codepot/language/english/common_lang.php | 3 + .../language/indonesian/common_lang.php | 13 +- .../codepot/language/korean/common_lang.php | 3 + codepot/src/codepot/models/projectmodel.php | 2 +- .../src/codepot/models/subversionmodel.php | 145 ++++++++++++++ codepot/src/codepot/views/code_folder.php | 144 +++++++++++++- codepot/src/config.php.in | 3 + 11 files changed, 481 insertions(+), 90 deletions(-) diff --git a/codepot/etc/codepot.ini.in b/codepot/etc/codepot.ini.in index fd9a0871..7c7e4aea 100644 --- a/codepot/etc/codepot.ini.in +++ b/codepot/etc/codepot.ini.in @@ -257,6 +257,16 @@ code_folder_readme = "README.wiki,README.txt,README" ;------------------------------------------------------------------------------ email_sender = "" +;------------------------------------------------------------------------------ +; Send notification upon a new commit if yes +;------------------------------------------------------------------------------ +commit_notification = "yes" + +;------------------------------------------------------------------------------ +; Send commit review notification if yes +;------------------------------------------------------------------------------ +commit_review_notification = "yes" + ;------------------------------------------------------------------------------ ; URL to include when sending a commit notification message. ; You can specify multiple urls. in fact, it's a free text. diff --git a/codepot/etc/post-commit.in b/codepot/etc/post-commit.in index a2256150..bec9c3f3 100644 --- a/codepot/etc/post-commit.in +++ b/codepot/etc/post-commit.in @@ -55,6 +55,7 @@ sub get_config database_prefix => $cfg->param ("database_prefix"), email_sender => $cfg->param ("email_sender"), + commit_notification => $cfg->param ("commit_notification"), commit_notification_url => $cfg->param ("commit_notification_url") }; @@ -340,6 +341,11 @@ sub email_message_to_project_members Message => $message ); + if (length($cfg->{email_sender}) > 0) + { + $mail{From} .= $cfg->{email_sender}; + } + Mail::Sendmail::sendmail (%mail); return (1, undef); } @@ -383,19 +389,22 @@ if (!defined($dbh)) write_commit_log ($dbh, $cfg->{database_prefix}, $REPOBASE, $REV, $AUTHOR); -my $commit_subject = "Commit r$REV by $AUTHOR in $REPOBASE"; -my $commit_message = ''; - -if ($cfg->{commit_notification_url} eq '') +if (lc($cfg->{commit_notification}) eq 'yes') { - $commit_message = $commit_subject; -} -else -{ - $commit_message = 'See ' . format_commit_url($cfg->{commit_notification_url}, $REPOBASE, $AUTHOR, $REV); -} + my $commit_subject = "Commit r$REV by $AUTHOR in $REPOBASE"; + my $commit_message = ''; -email_message_to_project_members ($cfg, $dbh, $cfg->{database_prefix}, $REPOBASE, $commit_subject, $commit_message); + if ($cfg->{commit_notification_url} eq '') + { + $commit_message = $commit_subject; + } + else + { + $commit_message = 'See ' . format_commit_url($cfg->{commit_notification_url}, $REPOBASE, $AUTHOR, $REV); + } + + email_message_to_project_members ($cfg, $dbh, $cfg->{database_prefix}, $REPOBASE, $commit_subject, $commit_message); +} close_database ($dbh); diff --git a/codepot/src/codepot/controllers/code.php b/codepot/src/codepot/controllers/code.php index 0287ee54..d3da417f 100644 --- a/codepot/src/codepot/controllers/code.php +++ b/codepot/src/codepot/controllers/code.php @@ -35,7 +35,8 @@ class Code extends Controller { $this->load->model ('ProjectModel', 'projects'); $this->load->model ('SubversionModel', 'subversion'); - + $this->load->library ('upload'); + $login = $this->login->getUser (); if (CODEPOT_SIGNIN_COMPULSORY && $login['id'] == '') redirect ("main/signin/" . $this->converter->AsciiTohex(current_url())); @@ -73,72 +74,119 @@ class Code extends Controller $data['message'] = 'Failed to get file'; $this->load->view ($this->VIEW_ERROR, $data); } - else if ($file['type'] == 'file') + else { - $head_rev = $this->subversion->getHeadRev ($projectid, $path, $rev); - if ($head_rev === FALSE) + $popup_error_message = ''; + + $post_new_message = $this->input->post('code_folder_new_message'); + $post_max_item_no = $this->input->post('code_folder_new_item_count'); + $post_unzip = $this->input->post('code_folder_new_item_unzip'); + if ($post_new_message !== FALSE && $post_max_item_no !== FALSE) { - $data['project'] = $project; - $data['message'] = 'Failed to get head revision'; - $this->load->view ($this->VIEW_ERROR, $data); - } - else - { - $file['head_rev'] = $head_rev; - $file['prev_rev'] = $this->subversion->getPrevRev ( - $projectid, $path, $file['created_rev']); - $file['next_rev'] = $this->subversion->getNextRev ( - $projectid, $path, $file['created_rev']); - - $file['created_tag'] = $this->subversion->getRevProp ($projectid, $file['created_rev'], CODEPOT_SVN_TAG_PROPERTY); - if ($file['created_tag'] === FALSE) $file['created_tag'] = ''; - - $file['head_tag'] = $this->subversion->getRevProp ($projectid, $file['head_rev'], CODEPOT_SVN_TAG_PROPERTY); - if ($file['head_tag'] === FALSE) $file['head_tag'] = ''; - - - $data['project'] = $project; - $data['headpath'] = $path; - $data['file'] = $file; - $data['revision'] = $rev; - - $this->load->view ($this->VIEW_FILE, $data); - } - } - else - { - $file['created_tag'] = $this->subversion->getRevProp ($projectid, $file['created_rev'], CODEPOT_SVN_TAG_PROPERTY); - if ($file['created_tag'] === FALSE) $file['created_tag'] = ''; - - $data['project'] = $project; - $data['headpath'] = $path; - $data['file'] = $file; - - $data['revision'] = $rev; - $data['prev_revision'] = - $this->subversion->getPrevRev ($projectid, $path, $rev); - $data['next_revision'] = - $this->subversion->getNextRev ($projectid, $path, $rev); - - $data['readme_text'] = ''; - $data['readme_file'] = ''; - foreach (explode(',', CODEPOT_CODE_FOLDER_README) as $rf) - { - $rf = trim($rf); - if (strlen($rf) > 0) + $import_files = array (); + for ($i = 0; $i < $post_max_item_no; $i++) { - $readme = $this->subversion->getFile ($projectid, $path . '/' . $rf, $rev); - if ($readme !== FALSE && $readme['type'] == 'file') + $d = $this->input->post("code_folder_new_item_dir_$i"); + + if (strlen($d) > 0) { - $data['readme_text'] = $readme['content']; - $data['readme_file'] = $rf; - break; + array_push ($import_files, array ('type' => 'dir', 'name' => $d)); } + + $fid = "code_folder_new_item_file_$i"; + if (array_key_exists($fid, $_FILES) && $_FILES[$fid]['name'] != '') + { + array_push ($import_files, array ('type' => 'file', 'name' => $_FILES[$fid]['name'], 'fid' => $fid, 'unzip' => $post_unzip)); + } + } + + if (count($import_files) > 0 && $this->subversion->importFiles ($projectid, $path, $login['id'], $post_new_message, $import_files, $this->upload) === FALSE) + { + $popup_error_message = '
' . $this->subversion->import_files_errmsg . '
'; + } + else + { + $refreshed_file = $this->subversion->getFile ($projectid, $path, $rev); + if ($refreshed_file === FALSE) + { + $data['project'] = $project; + $data['message'] = 'Failed to get file'; + $this->load->view ($this->VIEW_ERROR, $data); + return; /* EXIT HERE */ + } + + $file = $refreshed_file; } } - $data['wildcard_pattern'] = '*'; - $this->load->view ($this->VIEW_FOLDER, $data); + $data['popup_error_message'] = $popup_error_message; + if ($file['type'] == 'file') + { + $head_rev = $this->subversion->getHeadRev ($projectid, $path, $rev); + if ($head_rev === FALSE) + { + $data['project'] = $project; + $data['message'] = 'Failed to get head revision'; + $this->load->view ($this->VIEW_ERROR, $data); + } + else + { + $file['head_rev'] = $head_rev; + $file['prev_rev'] = $this->subversion->getPrevRev ( + $projectid, $path, $file['created_rev']); + $file['next_rev'] = $this->subversion->getNextRev ( + $projectid, $path, $file['created_rev']); + + $file['created_tag'] = $this->subversion->getRevProp ($projectid, $file['created_rev'], CODEPOT_SVN_TAG_PROPERTY); + if ($file['created_tag'] === FALSE) $file['created_tag'] = ''; + + $file['head_tag'] = $this->subversion->getRevProp ($projectid, $file['head_rev'], CODEPOT_SVN_TAG_PROPERTY); + if ($file['head_tag'] === FALSE) $file['head_tag'] = ''; + + + $data['project'] = $project; + $data['headpath'] = $path; + $data['file'] = $file; + $data['revision'] = $rev; + + $this->load->view ($this->VIEW_FILE, $data); + } + } + else + { + $file['created_tag'] = $this->subversion->getRevProp ($projectid, $file['created_rev'], CODEPOT_SVN_TAG_PROPERTY); + if ($file['created_tag'] === FALSE) $file['created_tag'] = ''; + + $data['project'] = $project; + $data['headpath'] = $path; + $data['file'] = $file; + + $data['revision'] = $rev; + $data['prev_revision'] = + $this->subversion->getPrevRev ($projectid, $path, $rev); + $data['next_revision'] = + $this->subversion->getNextRev ($projectid, $path, $rev); + + $data['readme_text'] = ''; + $data['readme_file'] = ''; + foreach (explode(',', CODEPOT_CODE_FOLDER_README) as $rf) + { + $rf = trim($rf); + if (strlen($rf) > 0) + { + $readme = $this->subversion->getFile ($projectid, $path . '/' . $rf, $rev); + if ($readme !== FALSE && $readme['type'] == 'file') + { + $data['readme_text'] = $readme['content']; + $data['readme_file'] = $rf; + break; + } + } + } + + $data['wildcard_pattern'] = '*'; + $this->load->view ($this->VIEW_FOLDER, $data); + } } } } @@ -430,7 +478,6 @@ class Code extends Controller } } - $data['project'] = $project; $data['fullpath'] = $path; $data['file'] = $file; @@ -568,15 +615,18 @@ class Code extends Controller // this is a hack to clear form data upon success $this->form_validation->_field_data = array(); - // TODO: message localization - $email_subject = sprintf ( - 'New review message #%d for r%d by %s in %s', - $review_sno, $rev, $login['id'], $projectid - ); - $email_message = 'See ' . current_url(); - $this->projects->emailMessageToMembers ( - $projectid, $this->login, $email_subject, $email_message - ); + if (CODEPOT_COMMIT_REVIEW_NOTIFICATION) + { + // TODO: message localization + $email_subject = sprintf ( + 'New review message #%d for r%d by %s in %s', + $review_sno, $rev, $login['id'], $projectid + ); + $email_message = 'See ' . current_url(); + $this->projects->emailMessageToMembers ( + $projectid, $this->login, $email_subject, $email_message + ); + } } } else diff --git a/codepot/src/codepot/helpers/codepot_helper.php b/codepot/src/codepot/helpers/codepot_helper.php index 1249920a..c05a0d86 100644 --- a/codepot/src/codepot/helpers/codepot_helper.php +++ b/codepot/src/codepot/helpers/codepot_helper.php @@ -219,6 +219,33 @@ if ( !function_exists ('codepot_zip_dir')) } } + +if ( !function_exists ('codepot_unzip_file')) +{ + function codepot_unzip_file ($output_dir, $path) + { + $zip = new ZipArchive(); + if ($zip->open ($path) === FALSE) return FALSE; + + + + if ($zip->extractTo ($output_dir) === FALSE) + { + $zip->close (); + return FALSE; + } + + $names = array(); + for ($i = 0; $i < $zip->numFiles; $i++) + { + array_push ($names, $zip->getNameIndex($i)); + } + + $zip->close (); + return $names; + } +} + if ( !function_exists ('codepot_find_longest_matching_sequence')) { function codepot_find_longest_matching_sequence ($old, $old_start, $old_len, $new, $new_start, $new_len) @@ -229,7 +256,7 @@ if ( !function_exists ('codepot_find_longest_matching_sequence')) $match_start_in_old = $old_start; $match_start_in_new = $new_start; $match_len = 0; - + $runs = array (); for ($i = $old_start; $i < $old_end; $i++) { diff --git a/codepot/src/codepot/language/english/common_lang.php b/codepot/src/codepot/language/english/common_lang.php index a65a6360..c33fadc4 100644 --- a/codepot/src/codepot/language/english/common_lang.php +++ b/codepot/src/codepot/language/english/common_lang.php @@ -64,6 +64,8 @@ $lang['My issues'] = 'My issues'; $lang['My projects'] = 'My projects'; $lang['Name'] = 'Name'; $lang['New'] = 'New'; +$lang['New directory'] = 'New directory'; +$lang['New file'] = 'New file'; $lang['OK'] = 'OK'; $lang['Open issues'] = 'Open issues'; $lang['Other projects'] = 'Other projects'; @@ -100,6 +102,7 @@ $lang['Text'] = 'Text'; $lang['Time'] = 'Time'; $lang['Type'] = 'Type'; $lang['Undo'] = 'Undo'; +$lang['Unzip a zip file'] = 'Unzip a zip file'; $lang['Update'] = 'Update'; $lang['Username'] = 'Username'; $lang['Wiki'] = 'Wiki'; diff --git a/codepot/src/codepot/language/indonesian/common_lang.php b/codepot/src/codepot/language/indonesian/common_lang.php index 3a429b1f..b6cc329a 100644 --- a/codepot/src/codepot/language/indonesian/common_lang.php +++ b/codepot/src/codepot/language/indonesian/common_lang.php @@ -1,13 +1,13 @@ store_file_errmsg = $errmsg; } + function capture_import_error ($errno, $errmsg) + { + $this->import_files_errmsg = $errmsg; + } + function storeFile ($projectid, $path, $committer, $commit_message, $text) { + $store_file_errmsg = ''; + //$url = 'file://'.CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"; $canon_path = $this->_canonical_path(CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"); $canon_dir = dirname($canon_path); @@ -293,6 +301,143 @@ class SubversionModel extends Model return TRUE; } + + function importFiles ($projectid, $path, $committer, $commit_message, $files, $uploader) + { + $import_files_errmsg = ''; + + //$url = 'file://'.CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"; + $full_path = CODEPOT_SVNREPO_DIR."/{$projectid}"; + if (strlen($path) > 0) $full_path .= "/{$path}"; + $canon_path = $this->_canonical_path($full_path); + $dirurl = 'file://' . $canon_path; + + set_error_handler (array ($this, 'capture_import_error')); + $tfname = @tempnam(__FILE__, 'codepot-import-files-'); + restore_error_handler (); + if ($tfname === FALSE) + { + return FALSE; + } + + $actual_tfname = $tfname . '.d'; + codepot_delete_files ($actual_tfname, TRUE); // delete the directory in case it exists + + mkdir ($actual_tfname); + + set_error_handler (array ($this, 'capture_import_error')); + if (@svn_auth_set_parameter (SVN_AUTH_PARAM_DEFAULT_USERNAME, $committer) === FALSE || + @svn_checkout ($dirurl, $actual_tfname, SVN_REVISION_HEAD, SVN_NON_RECURSIVE) === FALSE) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + + foreach ($files as $f) + { + $xname = $actual_tfname . '/' . $f['name']; + if ($f['type'] == 'dir') + { + if (@mkdir($xname) === FALSE || + @svn_add ($xname, TRUE, TRUE) === FALSE) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + } + else if ($f['type'] == 'file') + { + $config['allowed_types'] = '*'; + $config['max_size'] = 0; + $config['overwrite'] = FALSE; + + $config['remove_spaces'] = FALSE; + $config['encrypt_name'] = TRUE; + + if (strcasecmp($f['unzip'], 'yes') == 0 && + strcasecmp(substr($f['name'], -4), '.zip') == 0) + { + $unzip = TRUE; + if (function_exists('sys_get_temp_dir')) + $config['upload_path'] = sys_get_temp_dir(); + else + $config['upload_path'] = '/tmp'; + } + else + { + $unzip = FALSE; + $config['upload_path'] = $actual_tfname; + } + + $uploader->initialize ($config); + + if (!$uploader->do_upload($f['fid'])) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + + $ud = $uploader->data(); + + if ($unzip) + { + $x = codepot_unzip_file ($actual_tfname, $ud['full_path']); + @unlink ($ud['full_path']); + if ($x === FALSE) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + + foreach ($x as $y) + { + if (@svn_add ($actual_tfname . '/' . $y, TRUE, TRUE) === FALSE) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + } + } + else + { + @rename ($ud['full_path'], $xname); + if (@svn_add ($xname, TRUE, TRUE) === FALSE) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + } + } + + // ignore other types + } + + if (($result = @svn_commit ($commit_message, $actual_tfname)) === FALSE) + { + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); + @unlink ($tfname); + return FALSE; + } + + restore_error_handler (); + codepot_delete_files ($actual_tfname, TRUE); // delete the directory in case it exists + @unlink ($tfname); + return TRUE; + } + function getRevHistory ($projectid, $path, $rev) { //$url = 'file:///'.CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"; diff --git a/codepot/src/codepot/views/code_folder.php b/codepot/src/codepot/views/code_folder.php index 55b28a6f..a6a0a41a 100644 --- a/codepot/src/codepot/views/code_folder.php +++ b/codepot/src/codepot/views/code_folder.php @@ -180,8 +180,81 @@ function render_readme() } +var new_file_item_no = 0; +var new_dir_item_no = 0; + +function get_new_item_html(no, type, name) +{ + return codepot_sprintf ('
  • ', type, name, no); +} + $(function () { + + + new_file_item_no = 0; + new_dir_item_no = 0; + $('#code_folder_mainarea_new_file_form_item_list').append (get_new_item_html(new_file_item_no, 'file', 'file')); + $('#code_folder_mainarea_new_dir_form_item_list').append (get_new_item_html(new_dir_item_no, 'text', 'dir')); + + $("#code_folder_mainarea_new_file_form_div").dialog ( + { + title: 'lang->line('File');?>', + resizable: true, + autoOpen: false, + modal: true, + buttons: { + 'More': function () { + ++new_file_item_no; + $('#code_folder_mainarea_new_file_form_item_list').append (get_new_item_html(new_file_item_no, 'file', 'file')); + }, + 'lang->line('OK')?>': function () { + $('#code_folder_mainarea_new_file_item_count').val (new_file_item_no + 1); + $('#code_folder_mainarea_new_file_form').submit(); + $(this).dialog('close'); + }, + 'lang->line('Cancel')?>': function () { + $(this).dialog('close'); + } + + }, + clsoe: function() {} + } + ); + + $("#code_folder_mainarea_new_dir_form_div").dialog ( + { + title: 'lang->line('Directory');?>', + resizable: true, + autoOpen: false, + modal: true, + buttons: { + 'More': function () { + ++new_dir_item_no; + $('#code_folder_mainarea_new_dir_form_item_list').append (get_new_item_html(new_dir_item_no, 'text', 'dir')); + }, + 'lang->line('OK')?>': function () { + $('#code_folder_mainarea_new_dir_item_count').val (new_dir_item_no + 1); + $('#code_folder_mainarea_new_dir_form').submit(); + $(this).dialog('close'); + }, + 'lang->line('Cancel')?>': function () { + $(this).dialog('close'); + } + + }, + clsoe: function() {} + } + ); + + $("#code_folder_mainarea_new_file_button").button().click (function() { + $("#code_folder_mainarea_new_file_form_div").dialog('open'); + }); + $("#code_folder_mainarea_new_dir_button").button().click (function() { + $("#code_folder_mainarea_new_dir_form_div").dialog('open'); + }); + + 0): ?> code_hide_metadata == 'Y') @@ -194,7 +267,6 @@ $(function () { btn_label = "lang->line('Show metadata')?>"; btn = $("#code_folder_mainarea_metadata_button").button({"label": btn_label}).click (function () { - if ($("#code_folder_mainarea_result_info").is(":visible")) { $("#code_folder_mainarea_result_info").hide("blind",{},200); @@ -271,6 +343,23 @@ $(function () { }); render_readme (); + + + + 0): ?> + $("#code_folder_popup_error_div").dialog( { + title: 'lang->line('Error')?>', + width: 'auto', + height: 'auto', + modal: true, + autoOpen: true, + buttons: { + "lang->line('OK')?>": function() { + $( this ).dialog( "close" ); + } + } + }); + }); @@ -451,10 +540,21 @@ $this->load->view ( print ' '; print anchor ("code/file/{$project->id}/${xpar}/{$next_revision}", ''); - if ($file_count > 0) + + if ((isset($login['id']) && $login['id'] != '') || $file_count > 0) { print ' | '; - printf ('%s', $this->lang->line('Metadata')); + + if (isset($login['id']) && $login['id'] != '') + { + printf ('%s', $this->lang->line('New file')); + printf ('%s', $this->lang->line('New directory')); + } + + if ($file_count > 0) + { + printf ('%s', $this->lang->line('Metadata')); + } } print form_close(); @@ -677,6 +777,38 @@ $this->load->view ( + + + +
    + 'code_folder_mainarea_new_file_form'); + // this posts to the head revision regardless of the revision being shown + print form_open_multipart("code/file/{$project->id}/".$this->converter->AsciiToHex($headpath), $attrs); + ?> + +
    lang->line('Message'); ?>:
    +
    +
    lang->line('Unzip a zip file'); ?>
    +
      + +
      + +
      + 'code_folder_mainarea_new_dir_form'); + // this posts to the head revision regardless of the revision being shown + print form_open_multipart("code/file/{$project->id}/".$this->converter->AsciiToHex($headpath), $attrs); + ?> + +
      lang->line('Message'); ?>:
      +
      +
        + +
        + + + @@ -689,6 +821,12 @@ $this->load->view ( + 0): ?> +
        + +
        + + diff --git a/codepot/src/config.php.in b/codepot/src/config.php.in index 76f965cd..8b9d5896 100644 --- a/codepot/src/config.php.in +++ b/codepot/src/config.php.in @@ -84,7 +84,10 @@ function load_ini ($file) array ('footer', 'string', ''), array ('cloc_command_path', 'string', CODEPOT_CFG_DIR.'/cloc.pl'), array ('code_folder_readme', 'string', 'README'), + array ('email_sender', 'string', ''), + array ('commit_notification', 'boolean', TRUE), + array ('commit_review_notification', 'boolean', TRUE), array ('commit_notification_url', 'string', ''), array ('svn_tag_property', 'string', 'codepot:tag'),