diff --git a/config/zentaopms.php b/config/zentaopms.php index 6a14237d24..a59c81cb38 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -379,6 +379,7 @@ $config->objectTables['kanbangroup'] = TABLE_KANBANGROUP; $config->objectTables['kanbancard'] = TABLE_KANBANCARD; $config->objectTables['sonarqube'] = TABLE_PIPELINE; $config->objectTables['gitea'] = TABLE_PIPELINE; +$config->objectTables['gogs'] = TABLE_PIPELINE; $config->objectTables['gitlab'] = TABLE_PIPELINE; $config->objectTables['jebkins'] = TABLE_PIPELINE; $config->objectTables['stage'] = TABLE_STAGE; @@ -387,7 +388,7 @@ $config->objectTables['repo'] = TABLE_REPO; $config->newFeatures = array('introduction', 'tutorial', 'youngBlueTheme', 'visions'); -$config->pipelineTypeList = array('gitlab', 'sonarqube', 'jenkins', 'gitea'); +$config->pipelineTypeList = array('gitlab', 'gogs', 'gitea', 'jenkins', 'sonarqube'); /* Program privs.*/ $config->programPriv = new stdclass(); diff --git a/db/update17.6.sql b/db/update17.6.sql new file mode 100644 index 0000000000..26a955a070 --- /dev/null +++ b/db/update17.6.sql @@ -0,0 +1,2 @@ +ALTER TABLE `zt_job` ADD `lastSyncDate` datetime DEFAULT NULL AFTER `lastTag`; +INSERT INTO `zt_cron` (`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`, `lastTime`) VALUES ('*/5', '*', '*', '*', '*', 'moduleName=compile&methodName=syncCompile', '定时同步构建记录', 'zentao', 1, 'normal', '0000-00-00 00:00:00'); diff --git a/framework/base/helper.class.php b/framework/base/helper.class.php index c787cc08fe..b39d02977d 100644 --- a/framework/base/helper.class.php +++ b/framework/base/helper.class.php @@ -77,7 +77,12 @@ class baseHelper /* 处理$viewType和$vars。Set $viewType and $vars. */ if(empty($viewType)) $viewType = $app->getViewType(); - if(!is_array($vars)) parse_str($vars, $vars); + if(!is_array($vars)) + { + /* Prevent + from converting to spaces. */ + $vars = str_replace('+', '%2B', $vars); + parse_str($vars, $vars); + } /* 生成url链接的开始部分。Set the begin parts of the link. */ if($config->requestType == 'PATH_INFO') $link = $config->webRoot . $appName; diff --git a/lib/pclzip/pclzip.class.php b/lib/pclzip/pclzip.class.php index 8c8614982f..33f3b28ba1 100644 --- a/lib/pclzip/pclzip.class.php +++ b/lib/pclzip/pclzip.class.php @@ -29,7 +29,7 @@ if (!defined('PCLZIP_READ_BLOCK_SIZE')) { define( 'PCLZIP_READ_BLOCK_SIZE', 2048 ); } - + // ----- File list separator // In version 1.x of PclZip, the separator for file list is a space // (which is not a very smart choice, specifically for windows paths !). @@ -68,7 +68,7 @@ // ----- Optional threshold ratio for use of temporary files // Pclzip sense the size of the file to add/extract and decide to - // use or not temporary file. The algorythm is looking for + // use or not temporary file. The algorythm is looking for // memory_limit of PHP and apply a ratio. // threshold = memory_limit * ratio. // Recommended values are under 0.5. Default 0.47. @@ -152,7 +152,7 @@ define( 'PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021 ); // alias define( 'PCLZIP_OPT_TEMP_FILE_OFF', 77022 ); define( 'PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022 ); // alias - + // ----- File description attributes define( 'PCLZIP_ATT_FILE_NAME', 79001 ); define( 'PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002 ); @@ -198,7 +198,7 @@ // ----- Internal error handling var $error_code = 1; var $error_string = ''; - + // ----- Current status of the magic_quotes_runtime // This value store the php configuration for magic_quotes // The class can then disable the magic_quotes and reset it after @@ -332,7 +332,7 @@ } } } - + // ----- Look for default option values $this->privOptionDefaultThreshold($v_options); @@ -341,16 +341,16 @@ $v_att_list = array(); $v_filedescr_list = array(); $p_result_list = array(); - + // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { - + // ----- Look if the first element is also an array // This will mean that this is a file description entry if (isset($p_filelist[0]) && is_array($p_filelist[0])) { $v_att_list = $p_filelist; } - + // ----- The list is a list of string names else { $v_string_list = $p_filelist; @@ -368,7 +368,7 @@ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); return 0; } - + // ----- Reformat the string list if (sizeof($v_string_list) != 0) { foreach ($v_string_list as $v_string) { @@ -379,7 +379,7 @@ } } } - + // ----- For each file in the list check the attributes $v_supported_attributes = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' @@ -528,16 +528,16 @@ $v_att_list = array(); $v_filedescr_list = array(); $p_result_list = array(); - + // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { - + // ----- Look if the first element is also an array // This will mean that this is a file description entry if (isset($p_filelist[0]) && is_array($p_filelist[0])) { $v_att_list = $p_filelist; } - + // ----- The list is a list of string names else { $v_string_list = $p_filelist; @@ -555,14 +555,14 @@ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); return 0; } - + // ----- Reformat the string list if (sizeof($v_string_list) != 0) { foreach ($v_string_list as $v_string) { $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; } } - + // ----- For each file in the list check the attributes $v_supported_attributes = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' @@ -996,7 +996,7 @@ // Options : // PCLZIP_OPT_BY_INDEX : // PCLZIP_OPT_BY_NAME : - // PCLZIP_OPT_BY_EREG : + // PCLZIP_OPT_BY_EREG : // PCLZIP_OPT_BY_PREG : // Return Values : // 0 on failure, @@ -1064,7 +1064,7 @@ // -------------------------------------------------------------------------------- function deleteByIndex($p_index) { - + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); // ----- Return @@ -1114,7 +1114,7 @@ if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { $this->privSwapBackMagicQuotes(); - + // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); @@ -1426,7 +1426,7 @@ function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) { $v_result=1; - + // ----- Read the options $i=0; while ($i<$p_size) { @@ -1466,13 +1466,13 @@ PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); return PclZip::errorCode(); } - + // ----- Check for incompatible options if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); return PclZip::errorCode(); } - + // ----- Check the value $v_value = $p_options_list[$i+1]; if ((!is_integer($v_value)) || ($v_value<0)) { @@ -1491,7 +1491,7 @@ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); return PclZip::errorCode(); } - + $v_result_list[$p_options_list[$i]] = true; break; @@ -1506,7 +1506,7 @@ PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); return PclZip::errorCode(); } - + $v_result_list[$p_options_list[$i]] = true; break; @@ -1655,7 +1655,7 @@ // ----- Return return PclZip::errorCode(); } - + // ----- Reduce the index list // each index item in the list must be a couple with a start and // an end value : [0,3], [5-5], [8-10], ... @@ -1666,10 +1666,10 @@ // ----- Explode the item $v_item_list = explode("-", $v_work_list[$j]); $v_size_item_list = sizeof($v_item_list); - + // ----- TBC : Here we might check that each item is a // real integer ... - + // ----- Look for single value if ($v_size_item_list == 1) { // ----- Set the option value @@ -1703,7 +1703,7 @@ } $v_sort_value = $v_result_list[$p_options_list[$i]][$j]['start']; } - + // ----- Sort the items if ($v_sort_flag) { // TBC : To Be Completed @@ -1806,10 +1806,10 @@ } } } - + // ----- Look for default values if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { - + } // ----- Return @@ -1826,12 +1826,12 @@ function privOptionDefaultThreshold(&$p_options) { $v_result=1; - + if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { return $v_result; } - + // ----- Get 'memory_limit' configuration value $v_memory_limit = ini_get('memory_limit'); $v_memory_limit = trim($v_memory_limit); @@ -1846,15 +1846,15 @@ $v_memory_limit = $v_memory_limit*1048576; if($last == 'k') $v_memory_limit = $v_memory_limit*1024; - + $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); - + // ----- Sanity check : No threshold if value lower than 1M if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); } - + // ----- Return return $v_result; } @@ -1871,10 +1871,10 @@ function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) { $v_result=1; - + // ----- For each file in the list check the attributes foreach ($p_file_list as $v_key => $v_value) { - + // ----- Check if the option is supported if (!isset($v_requested_options[$v_key])) { // ----- Error log @@ -1893,7 +1893,7 @@ } $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); - + if ($p_filedescr['filename'] == '') { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); @@ -1974,10 +1974,10 @@ } } } - + // end foreach } - + // ----- Return return $v_result; } @@ -1990,7 +1990,7 @@ // or a string to be added as file. For any other type of files (link, other) // just ignore the item. // Then prepare the information that will be stored for that file. - // When its a folder, expand the folder with all the files that are in that + // When its a folder, expand the folder with all the files that are in that // folder (recursively). // Parameters : // Return Values : @@ -2000,20 +2000,20 @@ function privFileDescrExpand(&$p_filedescr_list, &$p_options) { $v_result=1; - + // ----- Create a result list $v_result_list = array(); - + // ----- Look each entry for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); - + // ----- Add the descriptor in result list $v_result_list[sizeof($v_result_list)] = $v_descr; - + // ----- Look for folder if ($v_descr['type'] == 'folder') { // ----- List of items in folder @@ -2064,10 +2064,10 @@ if (($v_item_handler == '.') || ($v_item_handler == '..')) { continue; } - + // ----- Compose the full filename $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; - + // ----- Look for different stored filename // Because the name of the folder was changed, the name of the // files/sub-folders also change @@ -2080,34 +2080,34 @@ $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; } } - + $v_dirlist_nb++; } - + @closedir($v_folder_handler); } else { // TBC : unable to open folder in read mode } - + // ----- Expand each element of the list if ($v_dirlist_nb != 0) { // ----- Expand if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { return $v_result; } - + // ----- Concat the resulting list $v_result_list = array_merge($v_result_list, $v_dirlist_descr); } else { } - + // ----- Free local array unset($v_dirlist_descr); } } - + // ----- Get the result list $p_filedescr_list = $v_result_list; @@ -2126,7 +2126,7 @@ { $v_result=1; $v_list_detail = array(); - + // ----- Magic quotes trick $this->privDisableMagicQuotes(); @@ -2456,7 +2456,7 @@ // Function : privAddFileList() // Description : // Parameters : - // $p_filedescr_list : An array containing the file description + // $p_filedescr_list : An array containing the file description // or directory names to add in the zip // $p_result_list : list of added files with their properties (specially the status field) // Return Values : @@ -2474,7 +2474,7 @@ // ----- Format the filename $p_filedescr_list[$j]['filename'] = PclZipUtilTranslateWinPath($p_filedescr_list[$j]['filename'], false); - + // ----- Skip empty file names // TBC : Can this be possible ? not checked in DescrParseAtt ? @@ -2526,7 +2526,7 @@ function privAddFile($p_filedescr, &$p_header, &$p_options) { $v_result=1; - + // ----- Working variable $p_filename = $p_filedescr['filename']; @@ -2538,8 +2538,8 @@ // ----- Return return PclZip::errorCode(); } - - // ----- Look for a stored different filename + + // ----- Look for a stored different filename /* TBC : Removed if (isset($p_filedescr['stored_filename'])) { $v_stored_filename = $p_filedescr['stored_filename']; @@ -2564,7 +2564,7 @@ $p_header['offset'] = 0; $p_header['filename'] = $p_filename; // TBC : Removed $p_header['stored_filename'] = $v_stored_filename; - $p_header['stored_filename'] = $p_filedescr['stored_filename']; + $p_header['stored_filename'] = strpos(helper::getOS(), 'Windows') !== false ? mb_convert_encoding($p_filedescr['stored_filename'],'GB2312','UTF-8') : $p_filedescr['stored_filename']; $p_header['extra'] = ''; $p_header['status'] = 'ok'; $p_header['index'] = -1; @@ -2574,20 +2574,20 @@ $p_header['external'] = 0x00000000; $p_header['size'] = filesize($p_filename); } - + // ----- Look for regular folder else if ($p_filedescr['type']=='folder') { $p_header['external'] = 0x00000010; $p_header['mtime'] = filemtime($p_filename); $p_header['size'] = filesize($p_filename); } - + // ----- Look for virtual file else if ($p_filedescr['type'] == 'virtual_file') { $p_header['external'] = 0x00000000; $p_header['size'] = strlen($p_filedescr['content']); } - + // ----- Look for filetime if (isset($p_filedescr['mtime'])) { @@ -2639,7 +2639,7 @@ if ($p_header['stored_filename'] == "") { $p_header['status'] = "filtered"; } - + // ----- Check the path length if (strlen($p_header['stored_filename']) > 0xFF) { $p_header['status'] = 'filename_too_long'; @@ -2651,7 +2651,7 @@ // ----- Look for a file if ($p_filedescr['type'] == 'file') { // ----- Look for using temporary file to zip - if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { @@ -2660,7 +2660,7 @@ return $v_result; } } - + // ----- Use "in memory" zip algo else { @@ -2678,14 +2678,14 @@ // ----- Calculate the CRC $p_header['crc'] = @crc32($v_content); - + // ----- Look for no compression if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { // ----- Set header parameters $p_header['compressed_size'] = $p_header['size']; $p_header['compression'] = 0; } - + // ----- Look for normal compression else { // ----- Compress the content @@ -2695,7 +2695,7 @@ $p_header['compressed_size'] = strlen($v_content); $p_header['compression'] = 8; } - + // ----- Call the header generation if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { @fclose($v_file); @@ -2711,19 +2711,19 @@ // ----- Look for a virtual file (a file from string) else if ($p_filedescr['type'] == 'virtual_file') { - + $v_content = $p_filedescr['content']; // ----- Calculate the CRC $p_header['crc'] = @crc32($v_content); - + // ----- Look for no compression if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { // ----- Set header parameters $p_header['compressed_size'] = $p_header['size']; $p_header['compression'] = 0; } - + // ----- Look for normal compression else { // ----- Compress the content @@ -2733,7 +2733,7 @@ $p_header['compressed_size'] = strlen($v_content); $p_header['compression'] = 8; } - + // ----- Call the header generation if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { @fclose($v_file); @@ -2799,7 +2799,7 @@ function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) { $v_result=PCLZIP_ERR_NO_ERROR; - + // ----- Working variable $p_filename = $p_filedescr['filename']; @@ -2894,7 +2894,7 @@ // ----- Unlink the temporary file @unlink($v_gzip_temp_name); - + // ----- Return return $v_result; } @@ -2911,7 +2911,7 @@ function privCalculateStoredFilename(&$p_filedescr, &$p_options) { $v_result=1; - + // ----- Working variables $p_filename = $p_filedescr['filename']; if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { @@ -2939,7 +2939,7 @@ // ----- Remove drive letter if any $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); } - + // ----- Look for path and/or short name change else { @@ -2969,7 +2969,7 @@ if ( (substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) { - + if ( (substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) { $p_remove_dir = "./".$p_remove_dir; @@ -2992,10 +2992,10 @@ } } } - + // ----- Remove drive letter if any $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); - + // ----- Look for path to add if ($p_add_dir != "") { if (substr($p_add_dir, -1) == "/") @@ -3008,7 +3008,7 @@ // ----- Filename (reduce the path of stored name) $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); $p_filedescr['stored_filename'] = $v_stored_filename; - + // ----- Return return $v_result; } @@ -3159,7 +3159,7 @@ { // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); - + // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); @@ -3390,7 +3390,7 @@ // ----- Look for extract by ereg rule // ereg() is deprecated with PHP 5.3 - /* + /* else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { @@ -3412,7 +3412,7 @@ // ----- Look for extract by index rule else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { - + // ----- Look if the index is in the list for ($j=$j_start; ($jprivSwapBackMagicQuotes(); - + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, "Filename '".$v_header['stored_filename']."' is " ."compressed by an unsupported compression " @@ -3454,7 +3454,7 @@ return PclZip::errorCode(); } } - + // ----- Check encrypted files if (($v_extract) && (($v_header['flag'] & 1) == 1)) { $v_header['status'] = 'unsupported_encryption'; @@ -3486,7 +3486,7 @@ $v_extract = false; } - + // ----- Look for real extraction if ($v_extract) { @@ -3535,7 +3535,7 @@ // ----- Next extracted file $v_nb_extracted++; - + // ----- Look for user callback abort if ($v_result1 == 2) { break; @@ -3671,12 +3671,12 @@ if ($p_path != '') { $p_entry['filename'] = $p_path."/".$p_entry['filename']; } - + // ----- Check a base_dir_restriction if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { $v_inclusion = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], - $p_entry['filename']); + $p_entry['filename']); if ($v_inclusion == 0) { PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, @@ -3704,7 +3704,7 @@ $p_entry['status'] = "skipped"; $v_result = 1; } - + // ----- Look for abort result if ($v_result == 2) { // ----- This status is internal and will be changed in 'skipped' @@ -3731,7 +3731,7 @@ // ----- Change the file status $p_entry['status'] = "already_a_directory"; - + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR // For historical reason first PclZip implementation does not stop // when this kind of error occurs. @@ -3804,10 +3804,10 @@ $v_dir_to_check = dirname($p_entry['filename']); if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { - + // ----- Change the file status $p_entry['status'] = "path_creation_fail"; - + // ----- Return //return $v_result; $v_result = 1; @@ -3846,7 +3846,7 @@ $v_binary_data = pack('a'.$v_read_size, $v_buffer); @fwrite($v_dest_file, $v_binary_data, $v_read_size); */ - @fwrite($v_dest_file, $v_buffer, $v_read_size); + @fwrite($v_dest_file, $v_buffer, $v_read_size); $v_size -= $v_read_size; } @@ -3855,7 +3855,7 @@ // ----- Change the file mtime touch($p_entry['filename'], $p_entry['mtime']); - + } else { @@ -3868,7 +3868,7 @@ // ----- Look for using temporary file to unzip - if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { @@ -3877,42 +3877,42 @@ return $v_result; } } - + // ----- Look for extract in memory else { - + // ----- Read the compressed file in a buffer (one shot) $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - + // ----- Decompress the file $v_file_content = @gzinflate($v_buffer); unset($v_buffer); if ($v_file_content === FALSE) { - + // ----- Change the file status // TBC $p_entry['status'] = "error"; - + return $v_result; } - + // ----- Opening destination file if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { - + // ----- Change the file status $p_entry['status'] = "write_error"; - + return $v_result; } - + // ----- Write the uncompressed data @fwrite($v_dest_file, $v_file_content, $p_entry['size']); unset($v_file_content); - + // ----- Closing the destination file @fclose($v_dest_file); - + } // ----- Change the file mtime @@ -3933,7 +3933,7 @@ if ($p_entry['status'] == "aborted") { $p_entry['status'] = "skipped"; } - + // ----- Look for post-extract callback elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { @@ -3967,7 +3967,7 @@ function privExtractFileUsingTempFile(&$p_entry, &$p_options) { $v_result=1; - + // ----- Creates a temporary file $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { @@ -4028,7 +4028,7 @@ // ----- Delete the temporary file @unlink($v_gzip_temp_name); - + // ----- Return return $v_result; } @@ -4106,7 +4106,7 @@ // ----- Read the compressed file in a buffer (one shot) $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); - + // ----- Decompress the file $v_file_content = gzinflate($v_buffer); unset($v_buffer); @@ -4187,7 +4187,7 @@ $p_entry['status'] = "skipped"; $v_result = 1; } - + // ----- Look for abort result if ($v_result == 2) { // ----- This status is internal and will be changed in 'skipped' @@ -4209,41 +4209,41 @@ // ----- Look for not compressed file // if ($p_entry['compressed_size'] == $p_entry['size']) if ($p_entry['compression'] == 0) { - + // ----- Reading the file $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); } else { - + // ----- Reading the file $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); - + // ----- Decompress the file if (($p_string = @gzinflate($v_data)) === FALSE) { // TBC } } - + // ----- Trace } else { // TBC : error : can not extract a folder in a string } - + } // ----- Change abort status if ($p_entry['status'] == "aborted") { $p_entry['status'] = "skipped"; } - + // ----- Look for post-extract callback elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); - + // ----- Swap the content to header $v_local_header['content'] = $p_string; $p_string = ''; @@ -4504,7 +4504,7 @@ } if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { } - + // ----- Look for flag bit 3 if (($p_local_header['flag'] & 8) == 8) { $p_local_header['size'] = $p_central_header['size']; @@ -4590,9 +4590,9 @@ // ----- Add the byte //$v_bytes = ($v_bytes << 8) | Ord($v_byte); - // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number - // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. - $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); + // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number + // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. + $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); // ----- Compare the bytes if ($v_bytes == 0x504b0506) @@ -4870,7 +4870,7 @@ // ----- Return return $v_result; } - + // ----- Check that local file header is same as central file header if ($this->privCheckFileHeaders($v_local_header, $v_header_list[$i]) != 1) { @@ -4954,11 +4954,11 @@ // TBC : I should test the result ... //@rename($v_zip_temp_name, $this->zipname); PclZipUtilRename($v_zip_temp_name, $this->zipname); - + // ----- Destroy the temporary archive unset($v_temp_zip); } - + // ----- Remove every files : reset the file else if ($v_central_dir['entries'] != 0) { $this->privCloseFd(); @@ -5445,7 +5445,7 @@ } } } - + // ----- Look for skip if ($v_skip > 0) { while ($v_skip > 0) { @@ -5478,7 +5478,7 @@ function PclZipUtilPathInclusion($p_dir, $p_path) { $v_result = 1; - + // ----- Look for path beginning by ./ if ( ($p_dir == '.') || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { @@ -5646,7 +5646,7 @@ // -------------------------------------------------------------------------------- function PclZipUtilOptionText($p_option) { - + $v_list = get_defined_constants(); for (reset($v_list); $v_key = key($v_list); next($v_list)) { $v_prefix = substr($v_key, 0, 10); @@ -5657,7 +5657,7 @@ return $v_key; } } - + $v_result = 'Unknown'; return $v_result; diff --git a/lib/scm/gitea.class.php b/lib/scm/gitea.class.php index 3e8709bd70..6541ded16a 100644 --- a/lib/scm/gitea.class.php +++ b/lib/scm/gitea.class.php @@ -3,6 +3,7 @@ class Gitea { public $client; public $root; + public $repo; /** * Construct @@ -28,8 +29,11 @@ class Gitea $project = $app->control->loadModel('gitea')->apiGetSingleProject($repo->serviceHost, $repo->serviceProject); if(isset($project->tokenCloneUrl)) { - $cmd = 'git clone --progress -v "' . $project->tokenCloneUrl . '" "' . $this->root . '"'; + $cmd = 'git clone --progress -v "' . $project->tokenCloneUrl . '" "' . $this->root . '" > "' . $app->getTmpRoot() . "log/clone.progress." . strtolower($repo->SCM) . ".{$repo->name}.log\" 2>&1 &"; + if(PHP_OS == 'WINNT') $cmd = "start /b $cmd"; exec($cmd); + + return $app->control->locate($app->control->createLink('repo', 'showSyncCommit', "repoID={$repo->id}")); } } @@ -40,6 +44,7 @@ class Gitea if(isset($branches[$branch])) $branch = "origin/$branch"; } $this->branch = $branch; + $this->repo = $repo; chdir($this->root); exec("{$this->client} config core.quotepath false"); @@ -159,7 +164,7 @@ class Gitea { $localBranch = trim($localBranch); if(substr($localBranch, 0, 19) == 'remotes/origin/HEAD') continue; - if(substr($localBranch, 0, 1) == '*') $localBranch = substr($localBranch, 1); + if(substr($localBranch, 0, 1) == '*') continue; if(substr($localBranch, 0, 15) == 'remotes/origin/') $localBranch = substr($localBranch, 15); $localBranch = trim($localBranch); @@ -551,9 +556,15 @@ class Gitea { if(!scm::checkRevision($revision)) return array(); - if($revision == 'HEAD' and $branch) $revision = $branch; - $revision = is_numeric($revision) ? "--skip=$revision $branch" : $revision; - $count = $count == 0 ? '' : "-n $count"; + if($revision == 'HEAD' and $branch) + { + $revision = $branch; + } + elseif(is_numeric($revision)) + { + $revision = "--skip=$revision $branch"; + } + $count = $count == 0 ? '' : "-n $count"; chdir($this->root); if($branch) @@ -598,11 +609,11 @@ class Gitea list($action, $path) = $file; $parsedFile = new stdclass(); - $parsedFile->revision = $hash; - $parsedFile->path = '/' . trim($path); - $parsedFile->type = 'file'; - $parsedFile->action = $action; - $logs['files'][$hash][] = $parsedFile; + $parsedFile->revision = $hash; + $parsedFile->path = '/' . trim($path); + $parsedFile->type = 'file'; + $parsedFile->action = $action; + $logs['files'][$hash][] = $parsedFile; } } return $logs; @@ -690,4 +701,20 @@ class Gitea return $parsedLogs; } + + /** + * Get download url. + * + * @param string $branch + * @param string $savePath + * @param string $ext + * @access public + * @return string + */ + public function getDownloadUrl($branch = 'master', $savePath = '', $ext = 'zip') + { + global $app; + $api = $app->control->loadModel('gitea')->getApiRoot($this->repo->serviceHost); + return sprintf($api, "/repos/{$this->repo->serviceProject}/archive/{$branch}.zip"); + } } diff --git a/lib/scm/gitlab.class.php b/lib/scm/gitlab.class.php index a27cb2259d..730156ae46 100644 --- a/lib/scm/gitlab.class.php +++ b/lib/scm/gitlab.class.php @@ -840,11 +840,12 @@ class gitlab * Get download url. * * @param string $branch + * @param string $savePath * @param string $ext * @access public * @return string */ - public function getDownloadUrl($branch = 'master', $ext = 'zip') + public function getDownloadUrl($branch = 'master', $savePath = '', $ext = 'zip') { $params = (array) $params; $params['private_token'] = $this->token; diff --git a/lib/scm/gitrepo.class.php b/lib/scm/gitrepo.class.php index aaafee2639..335768ffb5 100644 --- a/lib/scm/gitrepo.class.php +++ b/lib/scm/gitrepo.class.php @@ -3,6 +3,7 @@ class GitRepo { public $client; public $root; + public $repo; /** * Construct @@ -30,6 +31,7 @@ class GitRepo if(isset($branches[$branch])) $branch = "origin/$branch"; } $this->branch = $branch; + $this->repo = $repo; chdir($this->root); exec("{$this->client} config core.quotepath false"); @@ -540,9 +542,16 @@ class GitRepo { if(!scm::checkRevision($revision)) return array(); - if($revision == 'HEAD' and $branch) $revision = $branch; - $revision = is_numeric($revision) ? "--skip=$revision $branch" : $revision; - $count = $count == 0 ? '' : "-n $count"; + if($revision == 'HEAD' and $branch) + { + $revision = $branch; + } + elseif(is_numeric($revision)) + { + $revision = "--skip=$revision $branch"; + } + $count = $count == 0 ? '' : "-n $count"; + chdir($this->root); if($branch) execCmd(escapeCmd("$this->client checkout $branch"), 'array'); @@ -603,7 +612,8 @@ class GitRepo { $url = new stdclass(); $remote = execCmd(escapeCmd("$this->client remote -v"), 'array'); - $pregHttp = '/http(s)?:\/\/(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+)*\.git/'; + if(strpos($remote[0], 'oauth2:')) $remote[0] = preg_replace('/oauth2.*@/', '', $remote[0]); + $pregHttp = '/http(s)?:\/\/(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+)*(\.git)?/'; $pregSSH = '/ssh:\/\/git@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+)*\.git/'; if(preg_match($pregHttp, $remote[0], $matches)) $url->http = $matches[0]; @@ -675,4 +685,31 @@ class GitRepo return $parsedLogs; } + + /** + * Get download url. + * + * @param string $branch + * @param string $savePath + * @param string $ext + * @access public + * @return string + */ + public function getDownloadUrl($branch = 'master', $savePath = '', $ext = 'zip') + { + global $app, $config; + $gitDir = scandir($this->root); + $files = ''; + foreach($gitDir as $path) + { + if(!in_array($path, array('.', '..', '.git'))) $files .= $this->root . DS . "$path,"; + } + + $app->loadClass('pclzip', true); + $fileName = $savePath . DS . "{$this->repo->name}_$branch.zip"; + $zip = new pclzip($fileName); + $zip->create($files, PCLZIP_OPT_REMOVE_PATH, $this->root); + + return $config->webRoot . $app->getAppName() . 'data' . DS . 'repo' . DS . "{$this->repo->name}_$branch.zip"; + } } diff --git a/lib/scm/gogs.class.php b/lib/scm/gogs.class.php new file mode 100644 index 0000000000..5deff82719 --- /dev/null +++ b/lib/scm/gogs.class.php @@ -0,0 +1,755 @@ +client = $client; + $this->root = rtrim($root, DIRECTORY_SEPARATOR); + if(!realpath($this->root) and !empty($repo)) + { + global $app; + $project = $app->control->loadModel('gogs')->apiGetSingleProject($repo->serviceHost, $repo->serviceProject); + if(isset($project->tokenCloneUrl)) + { + $cmd = 'git clone --progress -v "' . $project->tokenCloneUrl . '" "' . $this->root . '" > "' . $app->getTmpRoot() . "log/clone.progress." . strtolower($repo->SCM) . ".{$repo->name}.log\" 2>&1 &"; + if(PHP_OS == 'WINNT') $cmd = "start /b $cmd"; + exec($cmd); + + return $app->control->locate($app->control->createLink('repo', 'showSyncCommit', "repoID={$repo->id}")); + } + } + + $branch = isset($_COOKIE['repoBranch']) ? $_COOKIE['repoBranch'] : ''; + if($branch) + { + $branches = $this->branch(); + if(isset($branches[$branch])) $branch = "origin/$branch"; + } + $this->branch = $branch; + $this->repo = $repo; + + chdir($this->root); + exec("{$this->client} config core.quotepath false"); + } + + /** + * List files. + * + * @param string $path + * @param string $revision + * @access public + * @return array + */ + public function ls($path, $revision = 'HEAD') + { + if(!scm::checkRevision($revision)) return array(); + + $path = ltrim($path, DIRECTORY_SEPARATOR); + $sub = ''; + chdir($this->root); + if(!empty($path)) $sub = ":$path"; + if(!empty($this->branch))$revision = $this->branch; + execCmd(escapeCmd("$this->client pull")); + $cmd = escapeCmd("$this->client ls-tree -l $revision$sub"); + $list = execCmd($cmd . ' 2>&1', 'array', $result); + if($result) return array(); + + $infos = array(); + foreach($list as $entry) + { + list($mod, $kind, $revision, $size, $name) = preg_split('/[\t ]+/', $entry); + + /* Get commit info. */ + $pathName = ltrim($path . DIRECTORY_SEPARATOR . $name, DIRECTORY_SEPARATOR); + $cmd = escapeCmd("$this->client log -1 $this->branch -- $pathName"); + $commit = execCmd($cmd, 'array'); + $logs = $this->parseLog($commit); + + if($size > 1024 * 1024) + { + $size = round($size / (1024 * 1024), 2) . 'MB'; + } + else if($size > 1024) + { + $size = round($size / 1024, 2) . 'KB'; + } + else + { + $size .= 'Bytes'; + } + + $info = new stdClass(); + $info->name = $name; + $info->kind = $kind == 'tree' ? 'dir' : 'file'; + $info->revision = $logs ? $logs[0]->revision : $revision; + $info->size = $size; + $info->account = $logs ? $logs[0]->committer : ''; + $info->date = $logs ? $logs[0]->time : ''; + $info->comment = $logs ? $logs[0]->comment : ''; + $infos[] = $info; + unset($info); + } + + /* Sort by kind */ + foreach($infos as $key => $info) $kinds[$key] = $info->kind; + if($infos) array_multisort($kinds, SORT_ASC, $infos); + + return $infos; + } + + /** + * Get tags + * + * @param string $path + * @param string $revision + * @access public + * @return array + */ + public function tags($path, $revision = 'HEAD') + { + if(!scm::checkRevision($revision)) return array(); + + chdir($this->root); + $cmd = escapeCmd("$this->client tag --sort=taggerdate"); + $list = execCmd($cmd . ' 2>&1', 'array', $result); + if($result) return array(); + + foreach($list as $key => $tag) + { + if(!$tag) unset($list[$key]); + } + + return $list; + } + + /** + * Get branch. + * + * @access public + * @return array + */ + public function branch() + { + chdir($this->root); + + /* Get local branch. */ + $cmd = escapeCmd("$this->client branch -a"); + $list = execCmd($cmd . ' 2>&1', 'array', $result); + if($result) return array(); + + /* Get default branch. */ + $defaultBranch = execCmd("$this->client symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'"); + $defaultBranch = trim($defaultBranch); + + $branches = array(); + foreach($list as $localBranch) + { + $localBranch = trim($localBranch); + if(substr($localBranch, 0, 19) == 'remotes/origin/HEAD') continue; + if(substr($localBranch, 0, 1) == '*') continue; + if(substr($localBranch, 0, 15) == 'remotes/origin/') $localBranch = substr($localBranch, 15); + + $localBranch = trim($localBranch); + if(empty($localBranch))continue; + if($localBranch != $defaultBranch) $branches[$localBranch] = $localBranch; + } + + asort($branches); + if($defaultBranch) $branches = array($defaultBranch => $defaultBranch) + $branches; + + return $branches; + } + + /** + * Get last log. + * + * @param string $path + * @param int $count + * @access public + * @return array + */ + public function getLastLog($path, $count = 10) + { + $path = ltrim($path, DIRECTORY_SEPARATOR); + $revision = $this->branch ? $this->branch : 'HEAD'; + + chdir($this->root); + $list = execCmd(escapeCmd("$this->client log -10 $revision -- $path"), 'array'); + $logs = $this->parseLog($list); + + return $logs; + } + + /** + * Get logs + * + * @param string $path + * @param string $fromRevision + * @param string $toRevision + * @param int $count + * @access public + * @return array + */ + public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0) + { + if(!scm::checkRevision($fromRevision)) return array(); + if(!scm::checkRevision($toRevision)) return array(); + + $path = ltrim($path, DIRECTORY_SEPARATOR); + $count = $count == 0 ? '' : "-n $count"; + /* compatible with svn. */ + if($fromRevision == 'HEAD' and $this->branch) $fromRevision = $this->branch; + if($toRevision == 'HEAD' and $this->branch) $toRevision = $this->branch; + if($fromRevision === $toRevision) + { + $logs = array(); + chdir($this->root); + + $list = execCmd(escapeCmd("$this->client log --stat=1024 --name-status --stat-name-width=1000 -1 $fromRevision -- $path"), 'array'); + $logs = $this->parseLog($list); + return $logs; + } + + if(!$fromRevision) + { + $revisions = " $toRevision"; + } + else + { + $revisions = "$fromRevision..$toRevision"; + } + chdir($this->root); + $list = execCmd(escapeCmd("$this->client log --stat=1024 --name-status --stat-name-width=1000 $count $revisions -- $path"), 'array'); + $logs = $this->parseLog($list); + + return $logs; + } + + /** + * Blame file + * + * @param string $path + * @param string $revision + * @access public + * @return array + */ + public function blame($path, $revision) + { + if(!scm::checkRevision($revision)) return array(); + + $path = ltrim($path, DIRECTORY_SEPARATOR); + chdir($this->root); + $list = execCmd(escapeCmd("$this->client blame -l $revision -- $path"), 'array'); + + $blames = array(); + $revLine = 0; + $revision = ''; + foreach($list as $line) + { + if(empty($line)) continue; + if($line[0] == '^') $line = substr($line, 1); + preg_match('/^([0-9a-f]{39,40})\s.*\((\S+)\s+([\d-]+)\s(.*)\s(\d+)\)(.*)$/U', $line, $matches); + + if(isset($matches[1]) and $matches[1] != $revision) + { + $blame = array(); + $blame['revision'] = $matches[1]; + $blame['committer'] = $matches[2]; + $blame['time'] = $matches[3]; + $blame['line'] = $matches[5]; + $blame['lines'] = 1; + $blame['content'] = strpos($matches[6], ' ') === false ? $matches[6] : substr($matches[6], 1); + + $revision = $matches[1]; + $revLine = $matches[5]; + $blames[$revLine] = $blame; + } + elseif(isset($matches[5])) + { + $blame = array(); + $blame['line'] = $matches[5]; + $blame['content'] = strpos($matches[6], ' ') === false ? $matches[6] : substr($matches[6], 1); + + $blames[$matches[5]] = $blame; + $blames[$revLine]['lines'] ++; + } + } + return $blames; + } + + /** + * Diff file. + * + * @param string $path + * @param string $fromRevision + * @param string $toRevision + * @param string $extra + * @access public + * @return array + */ + public function diff($path, $fromRevision, $toRevision, $extra = '') + { + if(!scm::checkRevision($fromRevision) and $extra != 'isBranchOrTag') return array(); + if(!scm::checkRevision($toRevision) and $extra != 'isBranchOrTag') return array(); + + $path = ltrim($path, DIRECTORY_SEPARATOR); + chdir($this->root); + if($toRevision == 'HEAD' and $this->branch) $toRevision = $this->branch; + if($fromRevision == '^') $fromRevision = $toRevision . '^'; + if(strpos($fromRevision, '^') !== false) + { + $list = execCmd(escapeCmd("$this->client log -2 $toRevision --pretty=format:%H -- $path"), 'array'); + if(isset($list[1])) $fromRevision = $list[1]; + } + $lines = execCmd(escapeCmd("$this->client diff $fromRevision $toRevision -- $path"), 'array'); + return $lines; + } + + /** + * Cat file. + * + * @param string $entry + * @param string $revision + * @access public + * @return string + */ + public function cat($entry, $revision = 'HEAD') + { + if(!scm::checkRevision($revision)) return false; + + chdir($this->root); + if($revision == 'HEAD' and $this->branch) $revision = $this->branch; + $cmd = escapeCmd("$this->client show $revision:$entry"); + $content = execCmd($cmd); + if(is_array($content)) $content = implode("\n", $content); + return $content; + } + + /** + * Get info. + * + * @param string $entry + * @param string $revision + * @access public + * @return object + */ + public function info($entry, $revision = 'HEAD') + { + if(!scm::checkRevision($revision)) return false; + + chdir($this->root); + if($revision == 'HEAD' and $this->branch) $revision = $this->branch; + $path = ltrim($entry, DIRECTORY_SEPARATOR); + $cmd = escapeCmd("$this->client ls-tree $revision -- $path"); + $result = execCmd($cmd); + $kind = ''; + if($result) + { + $results = explode("\n", trim($result)); + if(count($results) >= 2) + { + $kind = 'dir'; + } + else + { + list($mode, $type) = explode(' ', $results[0]); + $kind = $type == 'tree' ? 'dir' : 'file'; + } + } + + $list = execCmd(escapeCmd("$this->client log -1 $revision --pretty=format:%H -- $path"), 'array'); + $revision = $list[0]; + $info = new stdclass(); + $info->kind = $kind; + $info->path = $entry; + $info->revision = $revision; + $info->root = $this->root; + return $info; + } + + /** + * Exec git cmd. + * + * @param string $cmd + * @access public + * @return array + */ + public function exec($cmd) + { + chdir($this->root); + return execCmd(escapeCmd("$this->client $cmd"), 'array'); + } + + /** + * Parse diff. + * + * @param array $lines + * @access public + * @return array + */ + public function parseDiff($lines) + { + if(empty($lines)) return array(); + $diffs = array(); + $num = count($lines); + $endLine = end($lines); + if(strpos($endLine, '\ No newline at end of file') === 0) $num -= 1; + + $newFile = false; + $allFiles = array(); + for($i = 0; $i < $num; $i ++) + { + $diffFile = new stdclass(); + if(strpos($lines[$i], "diff --git ") === 0) + { + $fileInfo = explode(' ',$lines[$i]); + $fileName = substr($fileInfo[2], strpos($fileInfo[2], '/') + 1); + + /* Prevent duplicate display of files. */ + if(in_array($fileName, $allFiles)) continue; + $allFiles[] = $fileName; + + $diffFile->fileName = $fileName; + for($i++; $i < $num; $i ++) + { + $diff = new stdclass(); + /* Fix bug #1757. */ + if($lines[$i] == '+++ /dev/null') $newFile = true; + if(strpos($lines[$i], '+++', 0) !== false) continue; + if(strpos($lines[$i], '---', 0) !== false) continue; + if(strpos($lines[$i], '======', 0) !== false) continue; + if(preg_match('/^@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@/A', $lines[$i])) + { + $startLines = trim(str_replace(array('@', '+', '-'), '', $lines[$i])); + list($oldStartLine, $newStartLine) = explode(' ', $startLines); + list($diff->oldStartLine) = explode(',', $oldStartLine); + list($diff->newStartLine) = explode(',', $newStartLine); + $oldCurrentLine = $diff->oldStartLine; + $newCurrentLine = $diff->newStartLine; + if($newFile) + { + $oldCurrentLine = $diff->newStartLine; + $newCurrentLine = $diff->oldStartLine; + } + $newLines = array(); + for($i++; $i < $num; $i ++) + { + if(preg_match('/^@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@/A', $lines[$i])) + { + $i --; + break; + } + if(strpos($lines[$i], "diff --git ") === 0) break; + + $line = $lines[$i]; + if(strpos($line, '\ No newline at end of file') === 0)continue; + $sign = empty($line) ? '' : $line[0]; + if($sign == '-' and $newFile) $sign = '+'; + $type = $sign != '-' ? $sign == '+' ? 'new' : 'all' : 'old'; + if($sign == '-' || $sign == '+') + { + $line = substr_replace($line, ' ', 1, 0); + if($newFile) $line = preg_replace('/^\-/', '+', $line); + } + + $newLine = new stdclass(); + $newLine->type = $type; + $newLine->oldlc = $type != 'new' ? $oldCurrentLine : ''; + $newLine->newlc = $type != 'old' ? $newCurrentLine : ''; + $newLine->line = htmlSpecialString($line); + + if($type != 'new') $oldCurrentLine++; + if($type != 'old') $newCurrentLine++; + + $newLines[] = $newLine; + } + + $diff->lines = $newLines; + $diffFile->contents[] = $diff; + } + + if(isset($lines[$i]) and strpos($lines[$i], "diff --git ") === 0) + { + $i --; + $newFile = false; + break; + } + } + $diffs[] = $diffFile; + } + } + return $diffs; + } + + /** + * Get commit count. + * + * @param int $commits + * @param string $lastVersion + * @access public + * @return int + */ + public function getCommitCount($commits = 0, $lastVersion = '') + { + if(!scm::checkRevision($lastVersion)) return false; + + chdir($this->root); + $revision = $this->branch ? $this->branch : 'HEAD'; + return execCmd(escapeCmd("$this->client rev-list --count $revision -- ./"), 'string'); + } + + /** + * Get first revision. + * + * @access public + * @return string + */ + public function getFirstRevision() + { + chdir($this->root); + $list = execCmd(escapeCmd("$this->client rev-list --reverse HEAD -- ./"), 'array'); + return $list[0]; + } + + /** + * Get latest revision + * + * @access public + * @return string + */ + public function getLatestRevision() + { + chdir($this->root); + $revision = $this->branch ? $this->branch : 'HEAD'; + $list = execCmd(escapeCmd("$this->client rev-list -1 $revision -- ./"), 'array'); + return $list[0]; + } + + /** + * Get commits. + * + * @param string $rversion + * @param int $count + * @param string $branch + * @access public + * @return array + */ + public function getCommits($revision = '', $count = 0, $branch = '') + { + if(!scm::checkRevision($revision)) return array(); + + if($revision == 'HEAD' and $branch) + { + $revision = $branch; + } + elseif(is_numeric($revision)) + { + $revision = "--skip=$revision $branch"; + } + $count = $count == 0 ? '' : "-n $count"; + + chdir($this->root); + if($branch) + { + execCmd(escapeCmd("$this->client checkout $branch")); + execCmd(escapeCmd("$this->client pull")); + } + + $list = execCmd(escapeCmd("$this->client log $count $revision -- ./"), 'array'); + $commits = $this->parseLog($list); + + $logs = array(); + foreach($commits as $commit) + { + $hash = $commit->revision; + $log = new stdClass(); + $log->committer = $commit->committer; + $log->revision = $commit->revision; + $log->comment = $commit->comment; + $log->time = $commit->time; + $logs['commits'][$hash] = $log; + $logs['files'][$hash] = array(); + } + if(empty($logs)) return $logs; + + $hash = ''; + $files = execCmd(escapeCmd("$this->client whatchanged $count $revision --pretty=format:%an@_@%cd@_@%H@_@%s -- ./"), 'array'); + foreach($files as $commit) + { + $commit = trim($commit); + if(empty($commit)) continue; + $parsedCommit = explode('@_@', $commit); + if(count($parsedCommit) == 4) + { + list($account, $date, $hash, $comment) = $parsedCommit; + } + else + { + $file = explode(' ', $commit); + $file = explode("\t", end($file)); + if(!isset($file[1])) $file[1] = ''; + list($action, $path) = $file; + + $parsedFile = new stdclass(); + $parsedFile->revision = $hash; + $parsedFile->path = '/' . trim($path); + $parsedFile->type = 'file'; + $parsedFile->action = $action; + $logs['files'][$hash][] = $parsedFile; + } + } + return $logs; + } + + /** + * Get commits by MR branches. + * + * @param string $sourceBranch + * @param string $targetBranch + * @access public + * @return array + */ + public function getMRCommits($sourceBranch, $targetBranch) + { + execCmd(escapeCmd("$this->client checkout $sourceBranch")); + execCmd(escapeCmd("$this->client pull")); + execCmd(escapeCmd("$this->client checkout $targetBranch")); + execCmd(escapeCmd("$this->client pull")); + + $commits = execCmd(escapeCmd("$this->client log origin/$sourceBranch ^origin/$targetBranch"), 'array'); + $commits = $this->parseLog($commits); + foreach($commits as $commit) $commit->id = $commit->revision; + return $commits; + } + + /** + * Get clone url. + * + * @access public + * @return string + */ + public function getCloneUrl() + { + $url = new stdclass(); + $remote = execCmd(escapeCmd("$this->client remote -v"), 'array'); + $pregHttp = '/http(s)?:\/\/(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+)*\.git/'; + $pregSSH = '/ssh:\/\/git@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+)*\.git/'; + + if(preg_match($pregHttp, $remote[0], $matches)) $url->http = $matches[0]; + if(preg_match($pregSSH, $remote[0], $matches)) $url->ssh = $matches[0]; + + return $url; + } + + /** + * Parse log. + * + * @param array $logs + * @access public + * @return array + */ + public function parseLog($logs) + { + $parsedLogs = array(); + $i = 0; + foreach($logs as $line) + { + if(strpos($line, 'commit ') === 0) + { + if(isset($log)) + { + $log->comment = trim($comment); + $log->change = $changes; + $parsedLogs[$i] = $log; + $i++; + } + + $log = new stdclass(); + $comment = ''; + $changes = array(); + + $log->revision = trim(preg_replace('/^commit/', '', $line)); + } + elseif(strpos($line, 'Author:') === 0) + { + $account = preg_replace('/^Author:/', '', $line); + $log->committer = trim(preg_replace('/<[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+>/', '', $account)); + } + elseif(strpos($line, 'Date:') === 0) + { + $date = trim(preg_replace('/^Date:/', '', $line)); + $log->time = date('Y-m-d H:i:s', strtotime($date)); + } + elseif(preg_match('/^\s{2,}/', $line)) + { + $comment .= $line; + } + elseif(strpos($line, "\t") !== false) + { + list($action, $entry) = explode("\t", $line); + $entry = '/' . trim($entry); + $pathInfo = array(); + $pathInfo['action'] = $action; + $pathInfo['kind'] = 'file'; + $changes[$entry] = $pathInfo; + } + } + + if(isset($log)) + { + $log->comment = trim($comment); + $log->change = $changes; + $parsedLogs[$i] = $log; + } + + return $parsedLogs; + } + + /** + * Get download url. + * + * @param string $branch + * @param string $savePath + * @param string $ext + * @access public + * @return string + */ + public function getDownloadUrl($branch = 'master', $savePath = '', $ext = 'zip') + { + execCmd(escapeCmd("$this->client checkout $branch")); + execCmd(escapeCmd("$this->client pull")); + + global $app, $config; + $gitDir = scandir($this->root); + $files = ''; + foreach($gitDir as $path) + { + if(!in_array($path, array('.', '..', '.git'))) $files .= $this->root . DS . "$path,"; + } + + $app->loadClass('pclzip', true); + $fileName = $savePath . DS . "{$this->repo->name}_$branch.zip"; + $zip = new pclzip($fileName); + $zip->create($files, PCLZIP_OPT_REMOVE_PATH, $this->root); + + return $config->webRoot . $app->getAppName() . 'data' . DS . 'repo' . DS . "{$this->repo->name}_$branch.zip"; + } +} diff --git a/lib/scm/scm.class.php b/lib/scm/scm.class.php index 950c6b740a..11c481e695 100644 --- a/lib/scm/scm.class.php +++ b/lib/scm/scm.class.php @@ -220,6 +220,19 @@ class scm return $this->engine->getCommits($version, $count, $branch); } + /** + * Get commits by MR branches. + * + * @param string $sourceBranch + * @param string $targetBranch + * @access public + * @return array + */ + public function getMRCommits($sourceBranch, $targetBranch) + { + return $this->engine->getMRCommits($sourceBranch, $targetBranch); + } + /** * Get clone url. * @@ -249,13 +262,14 @@ class scm * Get download url. * * @param string $branch + * @param string $savePath * @param string $ext * @access public * @return string */ - public function getDownloadUrl($branch = '', $ext = 'zip') + public function getDownloadUrl($branch = '', $savePath = '', $ext = 'zip') { - return $this->engine->getDownloadUrl($branch, $ext); + return $this->engine->getDownloadUrl($branch, $savePath, $ext); } } diff --git a/lib/scm/subversion.class.php b/lib/scm/subversion.class.php index 120e5d4a88..092fdfa7e6 100644 --- a/lib/scm/subversion.class.php +++ b/lib/scm/subversion.class.php @@ -9,6 +9,7 @@ class Subversion public $remote; public $encoding; public $svnVersion; + public $repo; /** * Construct @@ -29,6 +30,7 @@ class Subversion $this->account = $account; $this->password = $password; $this->encoding = $encoding; + $this->repo = $repo; $this->ssh = (stripos($this->root, 'svn') === 0 or stripos($this->root, 'https') === 0) ? true : false; $this->remote = !(stripos($this->root, 'file') === 0); $this->client = $this->remote ? $client . " --username @account@ --password @password@" : $client; @@ -682,4 +684,32 @@ class Subversion return end($versionOutput); } + + /** + * Get download url. + * + * @param string $branch + * @param string $savePath + * @param string $ext + * @access public + * @return string + */ + public function getDownloadUrl($branch = '', $savePath = '', $ext = 'zip') + { + global $app, $config; + + /* Get repo name. */ + $pathList = explode('/', trim($this->root, '/')); + $repoDir = $savePath . DS . end($pathList); + execCmd($this->replaceAuth(escapeCmd("$this->client export $this->root $repoDir"))); + + $fileName = $savePath . DS . "{$this->repo->name}.zip"; + $app->loadClass('pclzip', true); + $zip = new pclzip($fileName); + $zip->create($repoDir, PCLZIP_OPT_REMOVE_PATH, $repoDir); + + $zfile = $app->loadClass('zfile'); + $zfile->removeDir($repoDir); + return $config->webRoot . $app->getAppName() . 'data' . DS . 'repo' . DS . $this->repo->name . '.zip'; + } } diff --git a/module/action/config.php b/module/action/config.php index 000cdcd5cf..2565ee1d5d 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -44,6 +44,7 @@ $config->action->objectNameFields['kanbancard'] = 'name'; $config->action->objectNameFields['sonarqube'] = 'name'; $config->action->objectNameFields['gitlab'] = 'name'; $config->action->objectNameFields['gitea'] = 'name'; +$config->action->objectNameFields['gogs'] = 'name'; $config->action->objectNameFields['stage'] = 'name'; $config->action->objectNameFields['apistruct'] = 'name'; $config->action->objectNameFields['repo'] = 'name'; @@ -62,7 +63,7 @@ $config->action->majorList['execution'] = array('opened', 'edited'); $config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis,researchplan,researchreport,'; $config->action->needGetRelateField = ',story,productplan,release,task,build,bug,testcase,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,module,'; -$config->action->noLinkModules = ',doclib,module,webhook,gitlab,gitea,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,'; +$config->action->noLinkModules = ',doclib,module,webhook,gitlab,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,'; $config->action->preferredTypeNum = 10; diff --git a/module/action/lang/de.php b/module/action/lang/de.php index e92cc40c4c..86e6b08025 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -57,6 +57,7 @@ $lang->action->historyEdit = 'Der Verlauf darf nicht leer sein.'; $lang->action->noDynamic = 'Kein Verlauf. '; $lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.'; $lang->action->executionNoProject = 'The execution does not belong to a project,please restore the project first'; +$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first'; $lang->action->repeatChange = '%s with the same name and code already exists in the system, After recovery, the name are \"%s\",the code are \"%s\".'; $lang->action->nameRepeatChange = '%s with the same name already exists in the system, After recovery, the name are \"%s\".'; @@ -128,6 +129,7 @@ $lang->action->objectTypes['whitelist'] = 'Whitelist'; $lang->action->objectTypes['pipeline'] = 'GitLib'; $lang->action->objectTypes['gitlab'] = 'GitLab Server'; $lang->action->objectTypes['gitea'] = 'Gitea Server'; +$lang->action->objectTypes['gogs'] = 'Gogs Server'; $lang->action->objectTypes['jenkins'] = 'Jenkins'; $lang->action->objectTypes['mr'] = 'Merge Request'; $lang->action->objectTypes['gitlabproject'] = 'GitLab Project'; @@ -138,6 +140,7 @@ $lang->action->objectTypes['gitlabbranchpriv'] = 'GitLab Protected Branches'; $lang->action->objectTypes['gitlabtag'] = 'GitLab Tag'; $lang->action->objectTypes['gitlabtagpriv'] = 'GitLab Tag Protected'; $lang->action->objectTypes['giteauser'] = 'Gitea User'; +$lang->action->objectTypes['gogsuser'] = 'Gogs User'; $lang->action->objectTypes['kanbanspace'] = 'Kanban Space'; $lang->action->objectTypes['kanban'] = 'Kanban'; $lang->action->objectTypes['kanbanregion'] = 'Kanban Region'; @@ -637,19 +640,32 @@ $lang->action->dynamicAction->job['executed'] = 'Execute Job'; $lang->action->dynamicAction->job['deleted'] = 'Delete Job'; $lang->action->dynamicAction->job['undeleted'] = 'Undelete Job'; -$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; -$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; -$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; +$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; +$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['undeleted'] = 'Undelete SonarQube Server'; $lang->action->dynamicAction->sonarqubeproject['deleted'] = 'Delete SonarQube Project'; -$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; -$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; -$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; +$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; +$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['undeleted'] = 'Undelete GitLab Server'; -$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; -$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; -$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; +$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; +$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['undeleted'] = 'Undelete Gitea Server'; + +$lang->action->dynamicAction->gogs['created'] = 'Create Gogs Server'; +$lang->action->dynamicAction->gogs['edited'] = 'Edit Gogs Server'; +$lang->action->dynamicAction->gogs['deleted'] = 'Delete Gogs Server'; +$lang->action->dynamicAction->gogs['undeleted'] = 'Undelete Gogs Server'; + +$lang->action->dynamicAction->repo['created'] = 'Create Repo'; +$lang->action->dynamicAction->repo['edited'] = 'Edit Repo'; +$lang->action->dynamicAction->repo['deleted'] = 'Delete Repo'; +$lang->action->dynamicAction->repo['undeleted'] = 'Undelete Repo'; /* Generate the corresponding object link. */ $lang->action->label->product = $lang->productCommon . '|product|view|productID=%s'; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 5d0d27fa41..f6c1e1ad00 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -57,6 +57,7 @@ $lang->action->historyEdit = 'The history EditBy cannot be empty.'; $lang->action->noDynamic = 'No dynamics. '; $lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.'; $lang->action->executionNoProject = 'The execution does not belong to a project,please restore the project first'; +$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first'; $lang->action->repeatChange = '%s with the same name and code already exists in the system, After recovery, the name are \"%s\",the code are \"%s\".'; $lang->action->nameRepeatChange = '%s with the same name already exists in the system, After recovery, the name are \"%s\".'; @@ -128,6 +129,7 @@ $lang->action->objectTypes['whitelist'] = 'Whitelist'; $lang->action->objectTypes['pipeline'] = 'GitLab Server'; $lang->action->objectTypes['gitlab'] = 'GitLab Server'; $lang->action->objectTypes['gitea'] = 'Gitea Server'; +$lang->action->objectTypes['gogs'] = 'Gogs Server'; $lang->action->objectTypes['jenkins'] = 'Jenkins'; $lang->action->objectTypes['mr'] = 'Merge Request'; $lang->action->objectTypes['gitlabproject'] = 'GitLab Project'; @@ -138,6 +140,7 @@ $lang->action->objectTypes['gitlabbranchpriv'] = 'GitLab Protected Branches'; $lang->action->objectTypes['gitlabtag'] = 'GitLab Tag'; $lang->action->objectTypes['gitlabtagpriv'] = 'GitLab Tag Protected'; $lang->action->objectTypes['giteauser'] = 'Gitea User'; +$lang->action->objectTypes['gogsuser'] = 'Gogs User'; $lang->action->objectTypes['kanbanspace'] = 'Kanban Space'; $lang->action->objectTypes['kanban'] = 'Kanban'; $lang->action->objectTypes['kanbanregion'] = 'Kanban Region'; @@ -637,19 +640,32 @@ $lang->action->dynamicAction->job['executed'] = 'Execute Job'; $lang->action->dynamicAction->job['deleted'] = 'Delete Job'; $lang->action->dynamicAction->job['undeleted'] = 'Undelete Job'; -$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; -$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; -$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; +$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; +$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['undeleted'] = 'Undelete SonarQube Server'; $lang->action->dynamicAction->sonarqubeproject['deleted'] = 'Delete SonarQube Project'; -$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; -$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; -$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; +$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; +$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['undeleted'] = 'Undelete GitLab Server'; -$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; -$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; -$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; +$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; +$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['undeleted'] = 'Undelete Gitea Server'; + +$lang->action->dynamicAction->gogs['created'] = 'Create Gogs Server'; +$lang->action->dynamicAction->gogs['edited'] = 'Edit Gogs Server'; +$lang->action->dynamicAction->gogs['deleted'] = 'Delete Gogs Server'; +$lang->action->dynamicAction->gogs['undeleted'] = 'Undelete Gogs Server'; + +$lang->action->dynamicAction->repo['created'] = 'Create Repo'; +$lang->action->dynamicAction->repo['edited'] = 'Edit Repo'; +$lang->action->dynamicAction->repo['deleted'] = 'Delete Repo'; +$lang->action->dynamicAction->repo['undeleted'] = 'Undelete Repo'; /* Generate the corresponding object link. */ $lang->action->label->product = $lang->productCommon . '|product|view|productID=%s'; diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index 3efb026564..3a2db412b6 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -57,6 +57,7 @@ $lang->action->historyEdit = "l'historique Edité Par ne peut pas être v $lang->action->noDynamic = "Pas d'historique."; $lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.'; $lang->action->executionNoProject = 'The execution does not belong to a project,please restore the project first'; +$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first'; $lang->action->repeatChange = '%s with the same name and code already exists in the system, After recovery, the name are \"%s\",the code are \"%s\".'; $lang->action->nameRepeatChange = '%s with the same name already exists in the system, After recovery, the name are \"%s\".'; @@ -128,6 +129,7 @@ $lang->action->objectTypes['whitelist'] = 'Whitelist'; $lang->action->objectTypes['pipeline'] = 'GitLib'; $lang->action->objectTypes['gitlab'] = 'GitLab Server'; $lang->action->objectTypes['gitea'] = 'Gitea Server'; +$lang->action->objectTypes['gogs'] = 'Gogs Server'; $lang->action->objectTypes['jenkins'] = 'Jenkins'; $lang->action->objectTypes['mr'] = 'Merge Request'; $lang->action->objectTypes['gitlabproject'] = 'GitLab Project'; @@ -138,6 +140,7 @@ $lang->action->objectTypes['gitlabbranchpriv'] = 'GitLab Protected Branches'; $lang->action->objectTypes['gitlabtag'] = 'GitLab Tag'; $lang->action->objectTypes['gitlabtagpriv'] = 'GitLab Tag Protected'; $lang->action->objectTypes['giteauser'] = 'Gitea User'; +$lang->action->objectTypes['gogsuser'] = 'Gogs User'; $lang->action->objectTypes['kanbanspace'] = 'Kanban Space'; $lang->action->objectTypes['kanban'] = 'Kanban'; $lang->action->objectTypes['kanbanregion'] = 'Kanban Region'; @@ -637,19 +640,32 @@ $lang->action->dynamicAction->job['executed'] = 'Execute Job'; $lang->action->dynamicAction->job['deleted'] = 'Delete Job'; $lang->action->dynamicAction->job['undeleted'] = 'Undelete Job'; -$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; -$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; -$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; +$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; +$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['undeleted'] = 'Undelete SonarQube Server'; $lang->action->dynamicAction->sonarqubeproject['deleted'] = 'Delete SonarQube Project'; -$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; -$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; -$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; +$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; +$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['undeleted'] = 'Undelete GitLab Server'; -$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; -$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; -$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; +$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; +$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['undeleted'] = 'Undelete Gitea Server'; + +$lang->action->dynamicAction->gogs['created'] = 'Create Gogs Server'; +$lang->action->dynamicAction->gogs['edited'] = 'Edit Gogs Server'; +$lang->action->dynamicAction->gogs['deleted'] = 'Delete Gogs Server'; +$lang->action->dynamicAction->gogs['undeleted'] = 'Undelete Gogs Server'; + +$lang->action->dynamicAction->repo['created'] = 'Create Repo'; +$lang->action->dynamicAction->repo['edited'] = 'Edit Repo'; +$lang->action->dynamicAction->repo['deleted'] = 'Delete Repo'; +$lang->action->dynamicAction->repo['undeleted'] = 'Undelete Repo'; /* Generate the corresponding object link. */ $lang->action->label->product = $lang->productCommon . '|product|view|productID=%s'; diff --git a/module/action/lang/vi.php b/module/action/lang/vi.php index 09f6928001..fdaccee1bb 100644 --- a/module/action/lang/vi.php +++ b/module/action/lang/vi.php @@ -30,13 +30,16 @@ $lang->action->editComment = 'Sửa nhận xét'; $lang->action->create = 'Thêm nhận xét'; $lang->action->comment = 'Nhận xét'; -$lang->action->trashTips = 'Ghi chú: Xóa trong ZenTao là hợp lệ.'; -$lang->action->textDiff = 'Định dạng văn bản'; -$lang->action->original = 'Định dạng gốc'; -$lang->action->confirmHideAll = 'Bạn có muốn ẩn tất cả ghi nhận này?'; -$lang->action->needEdit = '%s mà bạn muốn khôi phục. Vui lòng edit it.'; -$lang->action->historyEdit = 'Lịch sử người cập nhật không thể trống.'; -$lang->action->noDynamic = 'Không có lịch sử. '; +$lang->action->trashTips = 'Ghi chú: Xóa trong ZenTao là hợp lệ.'; +$lang->action->textDiff = 'Định dạng văn bản'; +$lang->action->original = 'Định dạng gốc'; +$lang->action->confirmHideAll = 'Bạn có muốn ẩn tất cả ghi nhận này?'; +$lang->action->needEdit = '%s mà bạn muốn khôi phục. Vui lòng edit it.'; +$lang->action->historyEdit = 'Lịch sử người cập nhật không thể trống.'; +$lang->action->noDynamic = 'Không có lịch sử. '; +$lang->action->undeletedTips = 'This data did not participate in the merging process during the version upgrade process, so restore is not supported.'; +$lang->action->executionNoProject = 'The execution does not belong to a project,please restore the project first'; +$lang->action->repoNoServer = 'The repo does not belong to a server,please restore the server first'; $lang->action->history = new stdclass(); $lang->action->history->action = 'Liên kết'; @@ -102,6 +105,7 @@ $lang->action->objectTypes['whitelist'] = 'Whitelist'; $lang->action->objectTypes['pipeline'] = 'GitLib'; $lang->action->objectTypes['gitlab'] = 'GitLab Server'; $lang->action->objectTypes['gitea'] = 'Gitea Server'; +$lang->action->objectTypes['gogs'] = 'Gogs Server'; $lang->action->objectTypes['jenkins'] = 'Jenkins'; $lang->action->objectTypes['mr'] = 'Merge Request'; $lang->action->objectTypes['gitlabproject'] = 'GitLab Project'; @@ -112,6 +116,7 @@ $lang->action->objectTypes['gitlabbranchpriv'] = 'GitLab Protected Branches'; $lang->action->objectTypes['gitlabtag'] = 'GitLab Tag'; $lang->action->objectTypes['gitlabtagpriv'] = 'GitLab Tag Protected'; $lang->action->objectTypes['giteauser'] = 'Gitea User'; +$lang->action->objectTypes['gogsuser'] = 'Gogs User'; $lang->action->objectTypes['kanbanspace'] = 'Kanban Space'; $lang->action->objectTypes['kanban'] = 'Kanban'; $lang->action->objectTypes['kanbanregion'] = 'Kanban Region'; @@ -493,19 +498,32 @@ $lang->action->dynamicAction->job['executed'] = 'Execute Job'; $lang->action->dynamicAction->job['deleted'] = 'Delete Job'; $lang->action->dynamicAction->job['undeleted'] = 'Undelete Job'; -$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; -$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; -$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['created'] = 'Create SonarQube Server'; +$lang->action->dynamicAction->sonarqube['edited'] = 'Edit SonarQube Server'; +$lang->action->dynamicAction->sonarqube['deleted'] = 'Delete SonarQube Server'; +$lang->action->dynamicAction->sonarqube['undeleted'] = 'Undelete SonarQube Server'; $lang->action->dynamicAction->sonarqubeproject['deleted'] = 'Delete SonarQube Project'; -$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; -$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; -$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['created'] = 'Create GitLab Server'; +$lang->action->dynamicAction->gitlab['edited'] = 'Edit GitLab Server'; +$lang->action->dynamicAction->gitlab['deleted'] = 'Delete GitLab Server'; +$lang->action->dynamicAction->gitlab['undeleted'] = 'Undelete GitLab Server'; -$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; -$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; -$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['created'] = 'Create Gitea Server'; +$lang->action->dynamicAction->gitea['edited'] = 'Edit Gitea Server'; +$lang->action->dynamicAction->gitea['deleted'] = 'Delete Gitea Server'; +$lang->action->dynamicAction->gitea['undeleted'] = 'Undelete Gitea Server'; + +$lang->action->dynamicAction->gogs['created'] = 'Create Gogs Server'; +$lang->action->dynamicAction->gogs['edited'] = 'Edit Gogs Server'; +$lang->action->dynamicAction->gogs['deleted'] = 'Delete Gogs Server'; +$lang->action->dynamicAction->gogs['undeleted'] = 'Undelete Gogs Server'; + +$lang->action->dynamicAction->repo['created'] = 'Create Repo'; +$lang->action->dynamicAction->repo['edited'] = 'Edit Repo'; +$lang->action->dynamicAction->repo['deleted'] = 'Delete Repo'; +$lang->action->dynamicAction->repo['undeleted'] = 'Undelete Repo'; /* Generate the corresponding object link. */ global $config; diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index 69c382104d..dee6daa7ef 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -57,6 +57,7 @@ $lang->action->historyEdit = '历史记录编辑不能为空。'; $lang->action->noDynamic = '暂时没有动态。'; $lang->action->undeletedTips = '该数据在版本升级过程中未参与数据归并流程,不支持还原。'; $lang->action->executionNoProject = '该执行没有所属的项目,请先还原项目再还原执行'; +$lang->action->repoNoServer = '该代码库没有所属的服务器,请先还原服务器再还原代码库'; $lang->action->repeatChange = '系统内已有同名、同代号的%s,恢复后名称为\"%s\"、代号为\"%s\"。'; $lang->action->nameRepeatChange = '系统内已有同名的%s,恢复后名称为\"%s\"。'; @@ -128,6 +129,7 @@ $lang->action->objectTypes['whitelist'] = '白名单'; $lang->action->objectTypes['pipeline'] = 'GitLab服务器'; $lang->action->objectTypes['gitlab'] = 'GitLab服务器'; $lang->action->objectTypes['gitea'] = 'Gitea服务器'; +$lang->action->objectTypes['gogs'] = 'Gogs服务器'; $lang->action->objectTypes['jenkins'] = 'Jenkins'; $lang->action->objectTypes['mr'] = '合并请求'; $lang->action->objectTypes['gitlabproject'] = 'GitLab项目'; @@ -138,6 +140,7 @@ $lang->action->objectTypes['gitlabbranchpriv'] = 'GitLab保护分支'; $lang->action->objectTypes['gitlabtag'] = 'GitLab标签'; $lang->action->objectTypes['gitlabtagpriv'] = 'GitLab标签保护'; $lang->action->objectTypes['giteauser'] = 'Gitea用户'; +$lang->action->objectTypes['gogsuser'] = 'Gogs用户'; $lang->action->objectTypes['kanbanspace'] = '看板空间'; $lang->action->objectTypes['kanban'] = '看板'; $lang->action->objectTypes['kanbanregion'] = '看板区域'; @@ -637,19 +640,32 @@ $lang->action->dynamicAction->job['executed'] = '执行构建任务'; $lang->action->dynamicAction->job['deleted'] = '删除构建任务'; $lang->action->dynamicAction->job['undeleted'] = '还原构建任务'; -$lang->action->dynamicAction->sonarqube['created'] = '创建SonarQube服务器'; -$lang->action->dynamicAction->sonarqube['edited'] = '设置SonarQube服务器'; -$lang->action->dynamicAction->sonarqube['deleted'] = '删除SonarQube服务器'; +$lang->action->dynamicAction->sonarqube['created'] = '创建SonarQube服务器'; +$lang->action->dynamicAction->sonarqube['edited'] = '设置SonarQube服务器'; +$lang->action->dynamicAction->sonarqube['deleted'] = '删除SonarQube服务器'; +$lang->action->dynamicAction->sonarqube['undeleted'] = '还原SonarQube服务器'; $lang->action->dynamicAction->sonarqubeproject['deleted'] = '删除SonarQube项目'; -$lang->action->dynamicAction->gitlab['created'] = '创建GitLab服务器'; -$lang->action->dynamicAction->gitlab['edited'] = '编辑GitLab服务器'; -$lang->action->dynamicAction->gitlab['deleted'] = '删除GitLab服务器'; +$lang->action->dynamicAction->gitlab['created'] = '创建GitLab服务器'; +$lang->action->dynamicAction->gitlab['edited'] = '编辑GitLab服务器'; +$lang->action->dynamicAction->gitlab['deleted'] = '删除GitLab服务器'; +$lang->action->dynamicAction->gitlab['undeleted'] = '还原GitLab服务器'; -$lang->action->dynamicAction->gitea['created'] = '创建Gitea服务器'; -$lang->action->dynamicAction->gitea['edited'] = '编辑Gitea服务器'; -$lang->action->dynamicAction->gitea['deleted'] = '删除Gitea服务器'; +$lang->action->dynamicAction->gitea['created'] = '创建Gitea服务器'; +$lang->action->dynamicAction->gitea['edited'] = '编辑Gitea服务器'; +$lang->action->dynamicAction->gitea['deleted'] = '删除Gitea服务器'; +$lang->action->dynamicAction->gitea['undeleted'] = '还原Gitea服务器'; + +$lang->action->dynamicAction->gogs['created'] = '创建Gogs服务器'; +$lang->action->dynamicAction->gogs['edited'] = '编辑Gogs服务器'; +$lang->action->dynamicAction->gogs['deleted'] = '删除Gogs服务器'; +$lang->action->dynamicAction->gogs['undeleted'] = '还原Gogs服务器'; + +$lang->action->dynamicAction->repo['created'] = '创建代码库'; +$lang->action->dynamicAction->repo['edited'] = '编辑代码库'; +$lang->action->dynamicAction->repo['deleted'] = '删除代码库'; +$lang->action->dynamicAction->repo['undeleted'] = '还原代码库'; /* 用来生成相应对象的链接。*/ $lang->action->label->product = $lang->productCommon . '|product|view|productID=%s'; diff --git a/module/action/model.php b/module/action/model.php index acd67125b3..4debe68768 100755 --- a/module/action/model.php +++ b/module/action/model.php @@ -1258,6 +1258,10 @@ class actionModel extends model $modules = $this->dao->select('id,name')->from(TABLE_MODULE)->where('id')->in(explode(',', $action->extra))->fetchPairs('id'); $action->objectName = implode(',', $modules); } + elseif($action->objectType == 'mr' and $action->action == 'deleted') + { + $action->objectName = $action->extra; + } $projectID = isset($relatedProjects[$action->objectType][$action->objectID]) ? $relatedProjects[$action->objectType][$action->objectID] : 0; @@ -1273,14 +1277,14 @@ class actionModel extends model /* If action type is login or logout, needn't link. */ if($actionType == 'svncommited' or $actionType == 'gitcommited') $action->actor = zget($commiters, $action->actor); - /* Get gitlab or gitea objectname. */ - if(empty($action->objectName) and (substr($objectType, 0, 6) == 'gitlab' or substr($objectType, 0, 5) == 'gitea')) $action->objectName = $action->extra; + /* Get gitlab, gitea or gogs objectname. */ + if(empty($action->objectName) and (substr($objectType, 0, 6) == 'gitlab' or substr($objectType, 0, 5) == 'gitea' or substr($objectType, 0, 4) == 'gogs')) $action->objectName = $action->extra; /* Other actions, create a link. */ $this->setObjectLink($action, $deptUsers); /* Set merge request link. */ - if(empty($action->objectName) and $action->objectType == 'mr') $action->objectLink = ''; + if((empty($action->objectName) or $action->action == 'deleted') and $action->objectType == 'mr') $action->objectLink = ''; $action->major = (isset($this->config->action->majorList[$action->objectType]) && in_array($action->action, $this->config->action->majorList[$action->objectType])) ? 1 : 0; } @@ -1723,6 +1727,16 @@ class actionModel extends model if((int)$projectCount == 0) return print(js::error($this->lang->action->executionNoProject)); } + if($action->objectType == 'repo') + { + $repo = $this->dao->select('*')->from(TABLE_REPO)->where('id')->eq($action->objectID)->fetch(); + if($repo and in_array($repo->SCM, array('Gitlab', 'Gitea', 'Gogs'))) + { + $server = $this->dao->select('*')->from(TABLE_PIPELINE)->where('id')->eq($repo->serviceHost)->andWhere('deleted')->eq('0')->fetch(); + if(empty($server)) return print(js::error($this->lang->action->repoNoServer)); + } + } + if($action->objectType == 'product') { $product = $this->dao->select('id,name,code,acl')->from(TABLE_PRODUCT)->where('id')->eq($action->objectID)->fetch(); diff --git a/module/ci/model.php b/module/ci/model.php index 0d27513edc..472a3449a2 100644 --- a/module/ci/model.php +++ b/module/ci/model.php @@ -29,9 +29,11 @@ class ciModel extends model if($this->session->repoID) { $repo = $this->loadModel('repo')->getRepoByID($this->session->repoID); - if(!in_array(strtolower($repo->SCM), $this->config->repo->gitServiceList)) unset($this->lang->devops->menu->mr); + if(!empty($repo) and !in_array(strtolower($repo->SCM), $this->config->repo->gitServiceList)) unset($this->lang->devops->menu->mr); - $this->lang->switcherMenu = $this->loadModel('repo')->getSwitcher($this->session->repoID); + $tab = $this->app->tab; + $repos = $this->repo->getRepoPairs($tab); + if(count($repos) > 1) $this->lang->switcherMenu = $this->loadModel('repo')->getSwitcher($this->session->repoID); } } @@ -180,11 +182,12 @@ class ciModel extends model $data = new stdclass; $data->status = $pipeline->status; $data->updateDate = $now; + $data->logs = ''; foreach($jobs as $job) { if(empty($job->duration) or $job->duration == '') $job->duration = '-'; - $data->logs = ">>> Job: $job->name, Stage: $job->stage, Status: $job->status, Duration: $job->duration Sec\r\n "; + $data->logs .= ">>> Job: $job->name, Stage: $job->stage, Status: $job->status, Duration: $job->duration Sec\r\n "; $data->logs .= "Job URL: web_url\" target='_blank'>$job->web_url \r\n"; $data->logs .= $this->transformAnsiToHtml($this->gitlab->apiGetJobLog($compile->server, $compile->project, $job->id)); } diff --git a/module/common/lang/common.php b/module/common/lang/common.php index aee6ec0b58..43ad3a3234 100644 --- a/module/common/lang/common.php +++ b/module/common/lang/common.php @@ -44,6 +44,7 @@ $lang->repo = new stdclass(); $lang->jenkins = new stdclass(); $lang->gitlab = new stdclass(); $lang->gitea = new stdclass(); +$lang->gogs = new stdclass(); $lang->mr = new stdclass(); $lang->compile = new stdclass(); $lang->job = new stdclass(); diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index dda70b11b2..3df031e508 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -411,7 +411,7 @@ $lang->devops->menu->code = array('link' => "{$lang->repo->common}|repo|brows $lang->devops->menu->mr = array('link' => "{$lang->devops->mr}|mr|browse|repoID=%s"); $lang->devops->menu->compile = array('link' => "{$lang->devops->compile}|job|browse|repoID=%s", 'subModule' => 'compile,job'); $lang->devops->menu->app = array('link' => "{$lang->app->common}|app|serverlink|%s"); -$lang->devops->menu->set = array('link' => "{$lang->devops->set}|repo|maintain", 'subModule' => 'gitlab,jenkins,sonarqube,gitea', 'alias' => 'setrules,create,edit'); +$lang->devops->menu->set = array('link' => "{$lang->devops->set}|repo|maintain", 'subModule' => 'gitlab,jenkins,sonarqube,gitea,gogs', 'alias' => 'setrules,create,edit'); $lang->devops->menuOrder[5] = 'code'; $lang->devops->menuOrder[10] = 'mr'; @@ -424,6 +424,7 @@ $lang->devops->dividerMenu = ',set,'; $lang->devops->menu->set['subMenu'] = new stdclass(); $lang->devops->menu->set['subMenu']->repo = array('link' => "{$lang->devops->repo}|repo|maintain", 'alias' => 'create,edit'); $lang->devops->menu->set['subMenu']->gitlab = array('link' => 'GitLab|gitlab|browse', 'subModule' => 'gitlab'); +$lang->devops->menu->set['subMenu']->gogs = array('link' => 'Gogs|gogs|browse', 'subModule' => 'gogs'); $lang->devops->menu->set['subMenu']->gitea = array('link' => 'Gitea|gitea|browse', 'subModule' => 'gitea'); $lang->devops->menu->set['subMenu']->jenkins = array('link' => 'Jenkins|jenkins|browse', 'subModule' => ''); $lang->devops->menu->set['subMenu']->sonarqube = array('link' => 'SonarQube|sonarqube|browse', 'subModule' => 'sonarqube'); @@ -662,6 +663,7 @@ $lang->navGroup->job = 'devops'; $lang->navGroup->jenkins = 'devops'; $lang->navGroup->mr = 'devops'; $lang->navGroup->gitlab = 'devops'; +$lang->navGroup->gogs = 'devops'; $lang->navGroup->gitea = 'devops'; $lang->navGroup->sonarqube = 'devops'; $lang->navGroup->sonarqubeproject = 'devops'; diff --git a/module/compile/control.php b/module/compile/control.php index c640359571..0eaa972775 100644 --- a/module/compile/control.php +++ b/module/compile/control.php @@ -47,6 +47,8 @@ class compile extends control $this->view->job = $job; } + $this->compile->syncCompile($repoID, $jobID); + $this->app->loadLang('job'); $this->loadModel('ci')->setMenu($repoID); @@ -77,7 +79,11 @@ class compile extends control $build = $this->compile->getByID($buildID); $job = $this->loadModel('job')->getByID($build->job); - $this->view->logs = str_replace("\r\n","
", $build->logs); + if(empty($build->logs) and !in_array($build->status, array('created', 'pending'))) $build->logs = $this->compile->getLogs($job, $build); + $logs = str_replace("\r\n", "
", $build->logs); + $logs = str_replace("\n", "
", $logs); + + $this->view->logs = $logs; $this->view->build = $build; $this->view->job = $job; @@ -87,5 +93,23 @@ class compile extends control $this->view->position[] = $this->lang->compile->logs; $this->display(); } + + /** + * Sync compiles. + * + * @access public + * @return bool + */ + public function syncCompile() + { + $this->compile->syncCompile(); + + if(dao::isError()) + { + echo json_encode(dao::getError()); + return true; + } + echo 'success'; + } } diff --git a/module/compile/lang/de.php b/module/compile/lang/de.php index dfd19b4832..6ca3125a19 100644 --- a/module/compile/lang/de.php +++ b/module/compile/lang/de.php @@ -1,7 +1,8 @@ compile->common = 'Compile'; -$lang->compile->browse = 'History'; -$lang->compile->logs = 'Logs'; +$lang->compile->common = 'Compile'; +$lang->compile->browse = 'History'; +$lang->compile->logs = 'Logs'; +$lang->compile->syncCompile = 'Interface: Sync Compiles.'; $lang->compile->id = 'ID'; $lang->compile->name = 'Name'; diff --git a/module/compile/lang/en.php b/module/compile/lang/en.php index 68e5655bbb..cb397946ca 100644 --- a/module/compile/lang/en.php +++ b/module/compile/lang/en.php @@ -1,7 +1,8 @@ compile->common = 'Compile'; -$lang->compile->browse = 'History'; -$lang->compile->logs = 'Log'; +$lang->compile->common = 'Compile'; +$lang->compile->browse = 'History'; +$lang->compile->logs = 'Log'; +$lang->compile->syncCompile = 'Interface: Sync Compiles.'; $lang->compile->id = 'ID'; $lang->compile->name = 'Name'; diff --git a/module/compile/lang/fr.php b/module/compile/lang/fr.php index b53d0e038c..6d8eccec72 100644 --- a/module/compile/lang/fr.php +++ b/module/compile/lang/fr.php @@ -1,7 +1,8 @@ compile->common = 'Build'; -$lang->compile->browse = 'Historique Builds'; -$lang->compile->logs = 'Logs des Builds'; +$lang->compile->common = 'Build'; +$lang->compile->browse = 'Historique Builds'; +$lang->compile->logs = 'Logs des Builds'; +$lang->compile->syncCompile = 'Interface: Sync Compiles.'; $lang->compile->id = 'ID'; $lang->compile->name = 'Nom'; diff --git a/module/compile/lang/vi.php b/module/compile/lang/vi.php index b5f3389644..05d762a745 100644 --- a/module/compile/lang/vi.php +++ b/module/compile/lang/vi.php @@ -1,7 +1,8 @@ compile->common = 'Compile'; -$lang->compile->browse = 'Lịch sử'; -$lang->compile->logs = 'Nhật ký'; +$lang->compile->common = 'Compile'; +$lang->compile->browse = 'Lịch sử'; +$lang->compile->logs = 'Nhật ký'; +$lang->compile->syncCompile = 'Interface: Sync Compiles.'; $lang->compile->id = 'ID'; $lang->compile->name = 'Tên'; diff --git a/module/compile/lang/zh-cn.php b/module/compile/lang/zh-cn.php index e4e328cf01..395474b4d6 100644 --- a/module/compile/lang/zh-cn.php +++ b/module/compile/lang/zh-cn.php @@ -1,7 +1,8 @@ compile->common = '构建'; -$lang->compile->browse = '构建历史'; -$lang->compile->logs = '构建日志'; +$lang->compile->common = '构建'; +$lang->compile->browse = '构建历史'; +$lang->compile->logs = '构建日志'; +$lang->compile->syncCompile = '接口:同步构建记录'; $lang->compile->id = 'ID'; $lang->compile->name = '构建名称'; diff --git a/module/compile/model.php b/module/compile/model.php index 44a1e37ee5..c46cb33c72 100644 --- a/module/compile/model.php +++ b/module/compile/model.php @@ -134,6 +134,59 @@ class compileModel extends model return $url; } + /** + * Get compile logs. + * + * @param object $job + * @param object $compile + * @access public + * @return string + */ + public function getLogs($job, $compile) + { + $logs = ''; + + if($job->engine == 'jenkins') + { + $jenkins = $this->loadModel('jenkins')->getByID($job->server); + $jenkinsUser = $jenkins->account; + $jenkinsPassword = $jenkins->token ? $jenkins->token : base64_decode($jenkins->password); + $userPWD = "$jenkinsUser:$jenkinsPassword"; + + $infoUrl = sprintf('%s/job/%s/api/xml?tree=builds[id,number,queueId]&xpath=//build[queueId=%s]', $jenkins->url, $job->pipeline, $compile->queue); + $response = common::http($infoUrl, '', array(CURLOPT_USERPWD => $userPWD)); + if($response) + { + $buildInfo = simplexml_load_string($response); + $buildNumber = $buildInfo->number; + if(empty($buildNumber)) return ''; + + $logUrl = sprintf('%s/job/%s/%s/consoleText', $jenkins->url, $job->pipeline, $buildNumber); + $logs = common::http($logUrl, '', array(CURLOPT_USERPWD => $userPWD)); + $this->dao->update(TABLE_COMPILE)->set('logs')->eq($logs)->where('id')->eq($compile->id)->exec(); + } + } + else + { + /* Get jobs by pipeline. */ + $pipeline = json_decode($job->pipeline); + $projectID = isset($pipeline->project) ? $pipeline->project : ''; + $jobs = $this->loadModel('gitlab')->apiGetJobs($job->server, $projectID, $compile->queue); + + $this->loadModel('ci'); + foreach($jobs as $gitlabJob) + { + if(empty($gitlabJob->duration) or $gitlabJob->duration == '') $gitlabJob->duration = '-'; + $logs .= ">>> Job: $gitlabJob->name, Stage: $gitlabJob->stage, Status: $gitlabJob->status, Duration: $gitlabJob->duration Sec\r\n "; + $logs .= "Job URL: web_url\" target='_blank'>$gitlabJob->web_url \r\n"; + $logs .= $this->ci->transformAnsiToHtml($this->gitlab->apiGetJobLog($job->server, $projectID, $gitlabJob->id)); + } + } + + if(!empty($logs)) $this->dao->update(TABLE_COMPILE)->set('logs')->eq($logs)->where('id')->eq($compile->id)->exec(); + return $logs; + } + /** * Save build by job * @@ -157,6 +210,137 @@ class compileModel extends model $this->dao->insert(TABLE_COMPILE)->data($build)->exec(); } + /** + * Sync compiles. + * + * @param int $repoID + * @param int $repoID + * @access public + * @return void + */ + public function syncCompile($repoID = 0, $jobID = 0) + { + if($jobID) + { + $jobList[$jobID] = $this->loadModel('job')->getByID($jobID); + } + else + { + $jobList = $this->loadModel('job')->getList($repoID); + } + + $jenkinsPairs = $this->loadModel('pipeline')->getList('jenkins'); + $gitlabPairs = $this->loadModel('pipeline')->getList('gitlab'); + + foreach($jobList as $job) + { + if($job->engine == 'jenkins') + { + $server = zget($jenkinsPairs, $job->server); + $this->syncJenkinsBuildList($server, $job); + } + else + { + $server = zget($gitlabPairs, $job->server); + $this->syncGitLabBuildList($server, $job); + } + } + } + + /** + * Sync jenkins build list. + * + * @param object $jenkins + * @param object $job + * @access public + * @return void + */ + public function syncJenkinsBuildList($jenkins, $job) + { + $jenkinsUser = $jenkins->account; + $jenkinsPassword = $jenkins->token ? $jenkins->token : base64_decode($jenkins->password); + + /* Get build list by API. */ + $url = sprintf('%s/job/%s/api/json?tree=builds[id,number,result,queueId,timestamp]', $jenkins->url, $job->pipeline); + $response = common::http($url, '', array(CURLOPT_USERPWD => "$jenkinsUser:$jenkinsPassword")); + if(!$response) return false; + + $compilePairs = $this->dao->select('queue,job')->from(TABLE_COMPILE)->where('job')->eq($job->id)->andWhere('queue')->gt(0)->fetchPairs(); + $jobInfo = json_decode($response); + foreach($jobInfo->builds as $build) + { + $lastSyncTime = strtotime($job->lastSyncDate); + if($build->timestamp < $lastSyncTime * 1000) break; + if(isset($compilePairs[$build->queueId])) continue; + + $data = new stdclass(); + $data->name = $job->name; + $data->job = $job->id; + $data->queue = $build->queueId; + $data->status = $build->result == 'SUCCESS' ? 'success' : 'failure'; + $data->createdBy = 'guest'; + $data->createdDate = date('Y-m-d H:i:s', $build->timestamp / 1000); + $data->updateDate = $data->createdDate; + + $this->dao->insert(TABLE_COMPILE)->data($data)->exec(); + } + + $now = helper::now(); + $this->dao->update(TABLE_JOB)->set('lastSyncDate')->eq($now)->where('id')->eq($job->id)->exec(); + } + + /** + * Sync gitlab build list. + * + * @param object $gitlab + * @param object $job + * @access public + * @return void + */ + public function syncGitlabBuildList($gitlab, $job) + { + $pipeline = json_decode($job->pipeline); + $projectID = isset($pipeline->project) ? $pipeline->project : ''; + $ref = isset($pipeline->reference) ? $pipeline->reference : ''; + $url = sprintf($this->loadModel('gitlab')->getApiRoot($gitlab->id, false), "/projects/{$projectID}/pipelines"); + + /* Get build list by API. */ + for($page = 1; true; $page++) + { + $param = $job->lastSyncDate ? '&updated_after=' . date('YYYY-MM-DDThh:mm:ssZ', strtotime($job->lastSyncDate)) : ''; + $builds = json_decode(commonModel::http($url . "&ref={$ref}&order_by=id&sort=asc&page={$page}&per_page=100" . $param)); + if(!is_array($builds)) break; + + if(!empty($builds)) + { + $queueIDList = array(); + foreach($builds as $build) $queueIDList[] = $build->id; + $compilePairs = $this->dao->select('queue,job')->from(TABLE_COMPILE)->where('job')->eq($job->id)->andWhere('queue')->in($queueIDList)->fetchPairs(); + + foreach($builds as $build) + { + if(isset($compilePairs[$build->id])) continue; + + $data = new stdclass(); + $data->name = $job->name; + $data->job = $job->id; + $data->queue = $build->id; + $data->status = $build->status; + $data->createdBy = 'guest'; + $data->createdDate = date('Y-m-d H:i:s', strtotime($build->created_at)); + $data->updateDate = date('Y-m-d H:i:s', strtotime($build->updated_at)); + + $this->dao->insert(TABLE_COMPILE)->data($data)->exec(); + } + } + + if(count($builds) < 100) break; + } + + $now = helper::now(); + $this->dao->update(TABLE_JOB)->set('lastSyncDate')->eq($now)->where('id')->eq($job->id)->exec(); + } + /** * Execute compile * diff --git a/module/git/control.php b/module/git/control.php index 3858b35fcb..102621c7b0 100644 --- a/module/git/control.php +++ b/module/git/control.php @@ -34,24 +34,30 @@ class git extends control { if(isset($_GET['repoUrl'])) $path = $this->get->repoUrl; - $path = helper::safe64Decode($path); - if(common::hasPriv('repo', 'diff')) + $path = helper::safe64Decode($path); + $repos = $this->loadModel('repo')->getListBySCM('Git,Gitlab,Gogs,Gitea', 'haspriv'); + $currentRepo = null; + foreach($repos as $repo) { - $repos = $this->loadModel('repo')->getListBySCM('Git,Gitlab', 'haspriv'); - foreach($repos as $repo) - { - if(strpos($path, $repo->path) === 0) - { - $entry = $this->repo->encodePath(str_replace($repo->path, '', $path)); - $oldRevision = "$revision^"; - $this->locate($this->repo->createLink('diff', "repoID=$repo->id&objectID=0&entry=$entry&oldRevision=$oldRevision&revision=$revision", 'html', 'true')); - } - } + if(!empty($repo->path) and strpos($path, $repo->path) === 0) $currentRepo = $repo; + } + + if($currentRepo and common::hasPriv('repo', 'diff')) + { + $entry = $this->repo->encodePath(str_replace($currentRepo->path, '', $path)); + $oldRevision = "$revision^"; + return $this->locate($this->repo->createLink('diff', "repoID=$currentRepo->id&objectID=0&entry=$entry&oldRevision=$oldRevision&revision=$revision", 'html', 'true')); + } + + if($currentRepo) + { + $scm = $this->app->loadClass('scm'); + $scm->setEngine($currentRepo); } $this->view->path = $path; $this->view->revision = $revision; - $this->view->diff = $this->git->diff($path, $revision); + $this->view->diff = $currentRepo ? $scm->diff($path, $revision) : ''; $this->display(); } @@ -68,23 +74,28 @@ class git extends control { if(isset($_GET['repoUrl'])) $path = $this->get->repoUrl; - $path = helper::safe64Decode($path); - if(common::hasPriv('repo', 'view')) + $path = helper::safe64Decode($path); + $repos = $this->loadModel('repo')->getListBySCM('Git,Gitlab,Gogs,Gitea', 'haspriv'); + $currentRepo = null; + foreach($repos as $repo) { - $repos = $this->loadModel('repo')->getListBySCM('Git,Gitlab', 'haspriv'); - foreach($repos as $repo) - { - if(strpos($path, $repo->path) === 0) - { - $entry = $this->repo->encodePath(str_replace($repo->path, '', $path)); - $this->locate($this->repo->createLink('view', "repoID=$repo->id&objectID=0&entry=$entry&revision=$revision", 'html', true)); - } - } + if(!empty($repo->path) and strpos($path, $repo->path) === 0) $currentRepo = $repo; } + if($currentRepo and common::hasPriv('repo', 'view')) + { + $entry = $this->repo->encodePath(str_replace($currentRepo->path, '', $path)); + return $this->locate($this->repo->createLink('view', "repoID=$currentRepo->id&objectID=0&entry=$entry&revision=$revision", 'html', true)); + } + + if($currentRepo) + { + $scm = $this->app->loadClass('scm'); + $scm->setEngine($currentRepo); + } $this->view->path = $path; $this->view->revision = $revision; - $this->view->code = $this->git->cat($path, $revision); + $this->view->code = $currentRepo ? $scm->cat($path, $revision) : ''; $this->display(); } diff --git a/module/git/model.php b/module/git/model.php index 231e79682f..28797cae14 100644 --- a/module/git/model.php +++ b/module/git/model.php @@ -128,18 +128,24 @@ class gitModel extends model $branches = $this->repo->getBranches($repo); $commits = $repo->commits; - $gitlabAccountPairs = array(); + $accountPairs = array(); if($repo->SCM == 'Gitlab') { - $gitlabUserList = $this->loadModel('gitlab')->apiGetUsers($repo->gitService); - $acountIDPairs = $this->gitlab->getUserIdAccountPairs($repo->gitService); - foreach($gitlabUserList as $gitlabUser) $gitlabAccountPairs[$gitlabUser->realname] = zget($acountIDPairs, $gitlabUser->id, ''); + $userList = $this->loadModel('gitlab')->apiGetUsers($repo->gitService); + $acountIDPairs = $this->gitlab->getUserIdAccountPairs($repo->gitService); + foreach($userList as $gitlabUser) $accountPairs[$gitlabUser->realname] = zget($acountIDPairs, $gitlabUser->id, ''); } elseif($repo->SCM == 'Gitea') { - $gitlabUserList = $this->loadModel('gitea')->apiGetUsers($repo->gitService); - $acountIDPairs = $this->gitea->getUserAccountIdPairs($repo->gitService, 'openID,account'); - foreach($gitlabUserList as $gitlabUser) $gitlabAccountPairs[$gitlabUser->realname] = zget($acountIDPairs, $gitlabUser->id, ''); + $userList = $this->loadModel('gitea')->apiGetUsers($repo->gitService); + $acountIDPairs = $this->gitea->getUserAccountIdPairs($repo->gitService, 'openID,account'); + foreach($userList as $gitlabUser) $accountPairs[$gitlabUser->realname] = zget($acountIDPairs, $gitlabUser->id, ''); + } + elseif($repo->SCM == 'Gogs') + { + $userList = $this->loadModel('gogs')->apiGetUsers($repo->gitService); + $acountIDPairs = $this->gogs->getUserAccountIdPairs($repo->gitService, 'openID,account'); + foreach($userList as $gitlabUser) $accountPairs[$gitlabUser->realname] = zget($acountIDPairs, $gitlabUser->id, ''); } /* Update code commit history. */ @@ -183,7 +189,7 @@ class gitModel extends model ' task:' . join(' ', $objects['tasks']) . ' bug:' . join(',', $objects['bugs'])); - if($lastVersion != $version) $this->repo->saveAction2PMS($objects, $log, $this->repoRoot, $repo->encoding, 'git', $gitlabAccountPairs); + if($lastVersion != $version) $this->repo->saveAction2PMS($objects, $log, $this->repoRoot, $repo->encoding, 'git', $accountPairs); } else { diff --git a/module/gitea/control.php b/module/gitea/control.php index cd18b89de5..7d7f851726 100644 --- a/module/gitea/control.php +++ b/module/gitea/control.php @@ -141,9 +141,9 @@ class gitea extends control { if($confirm != 'yes') return print(js::confirm($this->lang->gitea->confirmDelete, inlink('delete', "id=$giteaID&confirm=yes"))); - $oldGitea = $this->loadModel('pipeline')->getByID($giteaID); $actionID = $this->pipeline->delete($giteaID, 'gitea'); + if(!$actionID) return print(js::error($this->lang->pipeline->delError)); $gitea = $this->pipeline->getByID($giteaID); $changes = common::createChanges($oldGitea, $gitea); diff --git a/module/gitea/lang/de.php b/module/gitea/lang/de.php index 5eb0f97d89..6ed177b6f3 100644 --- a/module/gitea/lang/de.php +++ b/module/gitea/lang/de.php @@ -1,5 +1,4 @@ gitea = new stdclass; $lang->gitea->common = 'Gitea'; $lang->gitea->browse = 'Gitea Browse'; $lang->gitea->search = 'Search'; diff --git a/module/gitea/lang/en.php b/module/gitea/lang/en.php index 5eb0f97d89..6ed177b6f3 100644 --- a/module/gitea/lang/en.php +++ b/module/gitea/lang/en.php @@ -1,5 +1,4 @@ gitea = new stdclass; $lang->gitea->common = 'Gitea'; $lang->gitea->browse = 'Gitea Browse'; $lang->gitea->search = 'Search'; diff --git a/module/gitea/lang/fr.php b/module/gitea/lang/fr.php index 5eb0f97d89..6ed177b6f3 100644 --- a/module/gitea/lang/fr.php +++ b/module/gitea/lang/fr.php @@ -1,5 +1,4 @@ gitea = new stdclass; $lang->gitea->common = 'Gitea'; $lang->gitea->browse = 'Gitea Browse'; $lang->gitea->search = 'Search'; diff --git a/module/gitea/lang/vi.php b/module/gitea/lang/vi.php index ad89c301d3..599193344d 100644 --- a/module/gitea/lang/vi.php +++ b/module/gitea/lang/vi.php @@ -1,5 +1,4 @@ gitea = new stdclass; $lang->gitea->common = 'Gitea'; $lang->gitea->browse = 'Gitea Browse'; $lang->gitea->search = 'Search'; diff --git a/module/gitea/lang/zh-cn.php b/module/gitea/lang/zh-cn.php index 13e4c187a5..b51208fc93 100644 --- a/module/gitea/lang/zh-cn.php +++ b/module/gitea/lang/zh-cn.php @@ -1,5 +1,4 @@ gitea = new stdclass; $lang->gitea->common = 'Gitea'; $lang->gitea->browse = '浏览Gitea'; $lang->gitea->search = '搜索'; diff --git a/module/gitea/lang/zh-tw.php b/module/gitea/lang/zh-tw.php index 7e273df350..e6af6242d3 100644 --- a/module/gitea/lang/zh-tw.php +++ b/module/gitea/lang/zh-tw.php @@ -1,5 +1,4 @@ gitea = new stdclass; $lang->gitea->common = 'Gitea'; $lang->gitea->browse = '浏览Gitea'; $lang->gitea->search = '搜索'; diff --git a/module/gitlab/control.php b/module/gitlab/control.php index 6cd8195de4..279d7db0a6 100644 --- a/module/gitlab/control.php +++ b/module/gitlab/control.php @@ -228,11 +228,10 @@ class gitlab extends control { if($confirm != 'yes') return print(js::confirm($this->lang->gitlab->confirmDelete, inlink('delete', "id=$id&confirm=yes"))); - $oldGitLab = $this->gitlab->getByID($id); - $this->loadModel('action'); - $this->gitlab->delete(TABLE_PIPELINE, $id); + $oldGitLab = $this->loadModel('pipeline')->getByID($id); + $actionID = $this->pipeline->delete($id, 'gitlab'); + if(!$actionID) return print(js::error($this->lang->pipeline->delError)); - $actionID = $this->dao->lastInsertID(); $gitLab = $this->gitlab->getByID($id); $changes = common::createChanges($oldGitLab, $gitLab); $this->action->logHistory($actionID, $changes); @@ -722,7 +721,7 @@ class gitlab extends control $gitlab = $this->gitlab->getByID($gitlabID); $repos = $this->loadModel('repo')->getRepoListByClient($gitlabID); $repoPairs = array(); - foreach($repos as $repo) $repoPairs[$repo->path] = $repo->id; + foreach($repos as $repo) $repoPairs[$repo->serviceProject] = $repo->id; $this->view->gitlab = $gitlab; $this->view->keyword = urldecode(urldecode($keyword)); @@ -966,17 +965,13 @@ class gitlab extends control /** * Import gitlab issue to zentaopms. * - * @param int $repoID + * @param int $gitlabID + * @param int $projectID * @access public * @return void */ - public function importIssue($repoID) + public function importIssue($gitlabID, $projectID) { - $repo = $this->loadModel('repo')->getRepoByID($repoID); - $productIDList = explode(',', $repo->product); - $gitlabID = $repo->gitService; - $projectID = $repo->project; - $gitlab = $this->gitlab->getByID($gitlabID); if($gitlab) $user = $this->gitlab->apiGetCurrentUser($gitlab->url, $gitlab->token); if(empty($user->is_admin)) return print(js::alert($this->lang->gitlab->tokenLimit) . js::locate($this->createLink('gitlab', 'edit', array('gitlabID' => $gitlabID)))); @@ -1053,13 +1048,11 @@ class gitlab extends control } } - $products = array('' => ''); - $this->loadModel("product"); - foreach($productIDList as $productID) $products[$productID] = $this->product->getByID($productID)->name; + $products = $this->loadModel("product")->getPairs(); $this->view->title = $this->lang->gitlab->common . $this->lang->colon . $this->lang->gitlab->importIssue; $this->view->importable = empty($gitlabIssues) ? false : true; - $this->view->products = $products; + $this->view->products = array('' => '') + $products; $this->view->gitlabID = $gitlabID; $this->view->gitlabProjectID = $projectID; $this->view->objectTypes = $this->config->gitlab->objectTypes; diff --git a/module/gitlab/lang/de.php b/module/gitlab/lang/de.php index ef5d63cc87..f3b3b2d451 100644 --- a/module/gitlab/lang/de.php +++ b/module/gitlab/lang/de.php @@ -1,5 +1,4 @@ gitlab = new stdclass; $lang->gitlab->common = 'GitLab'; $lang->gitlab->browse = 'GitLab Browse'; $lang->gitlab->search = 'Search'; diff --git a/module/gitlab/lang/en.php b/module/gitlab/lang/en.php index ef5d63cc87..f3b3b2d451 100644 --- a/module/gitlab/lang/en.php +++ b/module/gitlab/lang/en.php @@ -1,5 +1,4 @@ gitlab = new stdclass; $lang->gitlab->common = 'GitLab'; $lang->gitlab->browse = 'GitLab Browse'; $lang->gitlab->search = 'Search'; diff --git a/module/gitlab/lang/fr.php b/module/gitlab/lang/fr.php index ef5d63cc87..f3b3b2d451 100644 --- a/module/gitlab/lang/fr.php +++ b/module/gitlab/lang/fr.php @@ -1,5 +1,4 @@ gitlab = new stdclass; $lang->gitlab->common = 'GitLab'; $lang->gitlab->browse = 'GitLab Browse'; $lang->gitlab->search = 'Search'; diff --git a/module/gitlab/lang/vi.php b/module/gitlab/lang/vi.php index ef5d63cc87..f3b3b2d451 100644 --- a/module/gitlab/lang/vi.php +++ b/module/gitlab/lang/vi.php @@ -1,5 +1,4 @@ gitlab = new stdclass; $lang->gitlab->common = 'GitLab'; $lang->gitlab->browse = 'GitLab Browse'; $lang->gitlab->search = 'Search'; diff --git a/module/gitlab/lang/zh-cn.php b/module/gitlab/lang/zh-cn.php index ef8637489c..402c550fbf 100644 --- a/module/gitlab/lang/zh-cn.php +++ b/module/gitlab/lang/zh-cn.php @@ -1,5 +1,4 @@ gitlab = new stdclass; $lang->gitlab->common = 'GitLab'; $lang->gitlab->browse = '浏览GitLab'; $lang->gitlab->search = '搜索'; diff --git a/module/gitlab/lang/zh-tw.php b/module/gitlab/lang/zh-tw.php index cb886aeab1..3ceb69d896 100644 --- a/module/gitlab/lang/zh-tw.php +++ b/module/gitlab/lang/zh-tw.php @@ -1,5 +1,4 @@ gitlab = new stdclass; $lang->gitlab->common = 'GitLab'; $lang->gitlab->browse = '瀏覽GitLab'; $lang->gitlab->search = '搜索'; diff --git a/module/gitlab/model.php b/module/gitlab/model.php index 00c4e51721..d5ab4d0eb6 100644 --- a/module/gitlab/model.php +++ b/module/gitlab/model.php @@ -986,7 +986,7 @@ class gitlabModel extends model */ public function apiGetSingleProject($gitlabID, $projectID) { - $url = sprintf($this->getApiRoot($gitlabID), "/projects/$projectID"); + $url = sprintf($this->getApiRoot($gitlabID, false), "/projects/$projectID"); return json_decode(commonModel::http($url)); } @@ -2879,4 +2879,20 @@ class gitlabModel extends model $html .= ''; return $html; } + + /** + * Get pipeline with api. + * + * @param int $gitlabID + * @param int $projectID + * @param string $branch + * @access public + * @return object|array + */ + public function apiGetPipeline($gitlabID, $projectID, $branch) + { + $apiRoot = $this->getApiRoot($gitlabID); + $url = sprintf($apiRoot, "/projects/$projectID/pipelines") . "&ref=$branch"; + return json_decode(commonModel::http($url)); + } } diff --git a/module/gitlab/view/binduser.html.php b/module/gitlab/view/binduser.html.php index 714b43a683..4f551b24d7 100644 --- a/module/gitlab/view/binduser.html.php +++ b/module/gitlab/view/binduser.html.php @@ -56,7 +56,7 @@ email;?> id]", $userPairs, $gitlabUser->zentaoAccount, "class='form-control select chosen'" );?> - zentaoAccount])):?> + id, $bindedUsers)):?> zentaoAccount, '');?> gitlab->binded;?> diff --git a/module/gitlab/view/browseproject.html.php b/module/gitlab/view/browseproject.html.php index 5dd1948d3f..95c299ad62 100644 --- a/module/gitlab/view/browseproject.html.php +++ b/module/gitlab/view/browseproject.html.php @@ -60,15 +60,19 @@ last_activity_at, 0, 10);?> id", '', 'list', 'treemap', '', '', false, '', $this->lang->gitlab->browseBranch, 0, $gitlabProject->default_branch); - echo common::buildIconButton('gitlab', 'browseTag', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'tag', '', '', false, '', $this->lang->gitlab->browseTag, 0, $gitlabProject->default_branch); - echo common::buildIconButton('gitlab', 'manageBranchPriv', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'branch-lock', '', '', false, '', $this->lang->gitlab->browseBranchPriv, 0, ($gitlabProject->isMaintainer and $gitlabProject->default_branch)); - echo common::buildIconButton('gitlab', 'manageTagPriv', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'tag-lock', '', '', false, '', $this->lang->gitlab->browseTagPriv, 0, ($gitlabProject->isMaintainer and $gitlabProject->default_branch)); - echo common::buildIconButton('gitlab', 'manageProjectMembers', 'repoID=' . zget($repoPairs, $gitlabProject->id), '', 'list', 'team', '', '', false, '', '', 0, isset($repoPairs[$gitlabProject->id])); - echo common::buildIconButton('gitlab', 'createWebhook', 'repoID=' . zget($repoPairs, $gitlabProject->id), '', 'list', 'change', 'hiddenwin', '', false, '', '', 0, isset($repoPairs[$gitlabProject->id])); - echo common::buildIconButton('gitlab', 'importIssue', 'repoID=' . zget($repoPairs, $gitlabProject->id), '', 'list', 'link', '', '', false, '', '', 0, isset($repoPairs[$gitlabProject->id])); - echo common::buildIconButton('gitlab', 'editProject', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'edit', '', '', false, '', '', 0, $gitlabProject->adminer); - echo common::buildIconButton('gitlab', 'deleteProject', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'trash', 'hiddenwin', '', false, '', '', 0, $gitlabProject->adminer); + $hasRepoClass = isset($repoPairs[$gitlabProject->id]) ? '' : 'disabled'; + $adminerClass = $gitlabProject->adminer ? '' : 'disabled'; + $maintainerClass = $gitlabProject->isMaintainer ? '' : 'disabled'; + $defaultBranchClass = $gitlabProject->adminer ? '' : 'disabled'; + echo common::printIcon('gitlab', 'browseBranch', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'treemap', '', $defaultBranchClass, false, '', $this->lang->gitlab->browseBranch); + echo common::printIcon('gitlab', 'browseTag', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'tag', '', $defaultBranchClass, false, '', $this->lang->gitlab->browseTag); + echo common::printIcon('gitlab', 'manageBranchPriv', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'branch-lock', '', $defaultBranchClass . ' ' . $maintainerClass, false, '', $this->lang->gitlab->browseBranchPriv); + echo common::printIcon('gitlab', 'manageTagPriv', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'tag-lock', '', $defaultBranchClass . ' ' . $maintainerClass, false, '', $this->lang->gitlab->browseTagPriv); + echo common::printIcon('gitlab', 'manageProjectMembers', 'repoID=' . zget($repoPairs, $gitlabProject->id), '', 'list', 'team', '', $hasRepoClass); + echo common::printIcon('gitlab', 'createWebhook', 'repoID=' . zget($repoPairs, $gitlabProject->id), '', 'list', 'change', 'hiddenwin', $hasRepoClass); + echo common::printIcon('gitlab', 'importIssue', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'link'); + echo common::printIcon('gitlab', 'editProject', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'edit', '', $adminerClass); + echo common::printIcon('gitlab', 'deleteProject', "gitlabID=$gitlabID&projectID=$gitlabProject->id", '', 'list', 'trash', 'hiddenwin', $adminerClass); ?> diff --git a/module/gogs/config.php b/module/gogs/config.php new file mode 100644 index 0000000000..88dddfa1f6 --- /dev/null +++ b/module/gogs/config.php @@ -0,0 +1,6 @@ +gogs->create = new stdclass; +$config->gogs->create->requiredFields = 'name,url,token'; + +$config->gogs->edit = new stdclass; +$config->gogs->edit->requiredFields = 'name,url,token'; diff --git a/module/gogs/control.php b/module/gogs/control.php new file mode 100644 index 0000000000..8944cc130e --- /dev/null +++ b/module/gogs/control.php @@ -0,0 +1,222 @@ + + * @package product + * @version $Id: ${FILE_NAME} 5144 2022-08-01 liyuchun@easycorp.ltd $ + * @link http://www.zentao.net + */ +class gogs extends control +{ + /** + * The gogs constructor. + * @param string $moduleName + * @param string $methodName + */ + public function __construct($moduleName = '', $methodName = '') + { + parent::__construct($moduleName, $methodName); + + /* This is essential when changing tab(menu) from gogs to repo. */ + /* Optional: common::setMenuVars('devops', $this->session->repoID); */ + $this->loadModel('ci')->setMenu(); + } + + /** + * Browse gogs. + * + * @param string $orderBy + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @access public + * @return void + */ + public function browse($orderBy = 'id_desc', $recTotal = 0, $recPerPage = 20, $pageID = 1) + { + + $this->app->loadClass('pager', $static = true); + $pager = new pager($recTotal, $recPerPage, $pageID); + + /* Admin user don't need bind. */ + $gogsList = $this->gogs->getList($orderBy, $pager); + $myGogses = $this->gogs->getGogsListByAccount(); + foreach($gogsList as $gogs) + { + $gogs->isBindUser = true; + if(!$this->app->user->admin and !isset($myGogses[$gogs->id])) $gogs->isBindUser = false; + } + + $this->view->title = $this->lang->gogs->common . $this->lang->colon . $this->lang->gogs->browse; + $this->view->gogsList = $gogsList; + $this->view->orderBy = $orderBy; + $this->view->pager = $pager; + + $this->display(); + } + + /** + * Create a gogs. + * + * @access public + * @return void + */ + public function create() + { + if($_POST) + { + $this->checkToken(); + $gogsID = $this->gogs->create(); + + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + $actionID = $this->loadModel('action')->create('gogs', $gogsID, 'created'); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse'))); + } + + $this->view->title = $this->lang->gogs->common . $this->lang->colon . $this->lang->gogs->lblCreate; + + $this->display(); + } + + /** + * View a gogs. + * @param int $gogsID + * @access public + * @return void + */ + public function view($gogsID) + { + $gogs = $this->gogs->getByID($gogsID); + + $this->view->title = $this->lang->gogs->common . $this->lang->colon . $this->lang->gogs->view; + $this->view->gogs = $gogs; + $this->view->users = $this->loadModel('user')->getPairs('noclosed'); + $this->view->actions = $this->loadModel('action')->getList('gogs', $gogsID); + $this->view->preAndNext = $this->loadModel('common')->getPreAndNextObject('pipeline', $gogsID); + $this->display(); + } + + /** + * Edit a gogs. + * + * @param int $gogsID + * @access public + * @return void + */ + public function edit($gogsID) + { + $oldGogs = $this->gogs->getByID($gogsID); + + if($_POST) + { + $this->checkToken(); + $this->gogs->update($gogsID); + $gogs = $this->gogs->getByID($gogsID); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + + $this->loadModel('action'); + $actionID = $this->action->create('gogs', $gogsID, 'edited'); + $changes = common::createChanges($oldGogs, $gogs); + $this->action->logHistory($actionID, $changes); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('browse'))); + } + + $this->view->title = $this->lang->gogs->common . $this->lang->colon . $this->lang->gogs->edit; + $this->view->gogs = $oldGogs; + + $this->display(); + } + + /** + * Delete a gogs. + * + * @param int $gogsID + * @access public + * @return void + */ + public function delete($gogsID, $confirm = 'no') + { + if($confirm != 'yes') return print(js::confirm($this->lang->gogs->confirmDelete, inlink('delete', "id=$gogsID&confirm=yes"))); + + $oldGogs = $this->loadModel('pipeline')->getByID($gogsID); + $actionID = $this->pipeline->delete($gogsID, 'gogs'); + if(!$actionID) return print(js::error($this->lang->pipeline->delError)); + + $gogs = $this->pipeline->getByID($gogsID); + $changes = common::createChanges($oldGogs, $gogs); + $this->loadModel('action')->logHistory($actionID, $changes); + return print(js::reload('parent')); + } + + /** + * Check post token has admin permissions. + * + * @access protected + * @return void + */ + protected function checkToken() + { + $gogs = fixer::input('post')->trim('url,token')->get(); + $this->dao->update('gogs')->data($gogs)->batchCheck($this->config->gogs->create->requiredFields, 'notempty'); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + + $result = $this->gogs->checkTokenAccess($gogs->url, $gogs->token); + + if($result === false) return $this->send(array('result' => 'fail', 'message' => array('url' => array($this->lang->gogs->hostError)))); + if(!$result) return $this->send(array('result' => 'fail', 'message' => array('token' => array($this->lang->gogs->tokenLimit)))); + + return true; + } + + /** + * Bind gogs user to zentao users. + * + * @param int $gogsID + * @access public + * @return void + */ + public function bindUser($gogsID) + { + $zentaoUsers = $this->dao->select('account,email,realname')->from(TABLE_USER)->fetchAll('account'); + $userPairs = $this->loadModel('user')->getPairs('noclosed|noletter'); + + if($_POST) + { + $this->gogs->bindUser($gogsID); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->server->http_referer)); + } + + $this->view->title = $this->lang->gogs->bindUser; + $this->view->userPairs = $userPairs; + $this->view->gogsUsers = $this->gogs->apiGetUsers($gogsID); + $this->view->bindedUsers = $this->gogs->getUserAccountIdPairs($gogsID); + $this->view->matchedResult = $this->gogs->getMatchedUsers($gogsID, $this->view->gogsUsers, $zentaoUsers); + $this->display(); + } + + /** + * Ajax getProjectBranches + * + * @param int $gogsID + * @param string $project + * @access public + * @return void + */ + public function ajaxGetProjectBranches($gogsID, $project) + { + if(!$gogsID or !$project) return $this->send(array('message' => array())); + + $project = urldecode(base64_decode($project)); + $branches = $this->gogs->apiGetBranches($gogsID, $project); + $options = ""; + foreach($branches as $branch) + { + $options .= ""; + } + $this->send($options); + } +} diff --git a/module/gogs/css/browse.css b/module/gogs/css/browse.css new file mode 100644 index 0000000000..cb15b6808d --- /dev/null +++ b/module/gogs/css/browse.css @@ -0,0 +1,2 @@ +.c-id {width: 60px;} +.c-name {width: 200px;} diff --git a/module/gogs/css/common.css b/module/gogs/css/common.css new file mode 100644 index 0000000000..ad50c2a1d2 --- /dev/null +++ b/module/gogs/css/common.css @@ -0,0 +1,2 @@ +#publicTip {padding-left: 10px;} +.table-form>tbody>tr>th {width: 110px;} diff --git a/module/gogs/lang/de.php b/module/gogs/lang/de.php new file mode 100644 index 0000000000..4f884b7518 --- /dev/null +++ b/module/gogs/lang/de.php @@ -0,0 +1,35 @@ +gogs->common = 'Gogs'; +$lang->gogs->browse = 'Gogs Browse'; +$lang->gogs->search = 'Search'; +$lang->gogs->create = 'Create Gogs'; +$lang->gogs->edit = 'Edit Gogs'; +$lang->gogs->view = 'View Gogs'; +$lang->gogs->delete = 'Delete Gogs'; +$lang->gogs->confirmDelete = 'Do you want to delete this Gogs server?'; +$lang->gogs->bindUser = 'Bind User'; +$lang->gogs->gogsAvatar = 'Avatar'; +$lang->gogs->gogsAccount = 'Gogs Account'; +$lang->gogs->gogsEmail = 'Email'; +$lang->gogs->zentaoAccount = 'Zentao Account'; +$lang->gogs->bindingStatus = 'Binding Status'; +$lang->gogs->notBind = 'Not bind'; +$lang->gogs->binded = 'Binded'; +$lang->gogs->bindDynamic = '%s and Zentao user %s'; + +$lang->gogs->browseAction = 'Gogs List'; +$lang->gogs->deleteAction = 'Delete Gogs'; + +$lang->gogs->id = 'ID'; +$lang->gogs->name = "Server Name"; +$lang->gogs->url = 'Server URL'; +$lang->gogs->token = 'Token'; + +$lang->gogs->tokenLimit = "The current token has no admin privilege. Please regenerate one with root user in Gogs."; +$lang->gogs->hostError = "So the current Gogs server address is invalid, please confirm that the current server can be accessed and try again."; +$lang->gogs->bindUserError = "Can not bind users repeatedly %s"; + +$lang->gogs->server = "Server List"; +$lang->gogs->lblCreate = 'Create Gogs Server'; +$lang->gogs->emptyError = " cannot be empty"; +$lang->gogs->createSuccess = "Create success"; diff --git a/module/gogs/lang/en.php b/module/gogs/lang/en.php new file mode 100644 index 0000000000..4f884b7518 --- /dev/null +++ b/module/gogs/lang/en.php @@ -0,0 +1,35 @@ +gogs->common = 'Gogs'; +$lang->gogs->browse = 'Gogs Browse'; +$lang->gogs->search = 'Search'; +$lang->gogs->create = 'Create Gogs'; +$lang->gogs->edit = 'Edit Gogs'; +$lang->gogs->view = 'View Gogs'; +$lang->gogs->delete = 'Delete Gogs'; +$lang->gogs->confirmDelete = 'Do you want to delete this Gogs server?'; +$lang->gogs->bindUser = 'Bind User'; +$lang->gogs->gogsAvatar = 'Avatar'; +$lang->gogs->gogsAccount = 'Gogs Account'; +$lang->gogs->gogsEmail = 'Email'; +$lang->gogs->zentaoAccount = 'Zentao Account'; +$lang->gogs->bindingStatus = 'Binding Status'; +$lang->gogs->notBind = 'Not bind'; +$lang->gogs->binded = 'Binded'; +$lang->gogs->bindDynamic = '%s and Zentao user %s'; + +$lang->gogs->browseAction = 'Gogs List'; +$lang->gogs->deleteAction = 'Delete Gogs'; + +$lang->gogs->id = 'ID'; +$lang->gogs->name = "Server Name"; +$lang->gogs->url = 'Server URL'; +$lang->gogs->token = 'Token'; + +$lang->gogs->tokenLimit = "The current token has no admin privilege. Please regenerate one with root user in Gogs."; +$lang->gogs->hostError = "So the current Gogs server address is invalid, please confirm that the current server can be accessed and try again."; +$lang->gogs->bindUserError = "Can not bind users repeatedly %s"; + +$lang->gogs->server = "Server List"; +$lang->gogs->lblCreate = 'Create Gogs Server'; +$lang->gogs->emptyError = " cannot be empty"; +$lang->gogs->createSuccess = "Create success"; diff --git a/module/gogs/lang/fr.php b/module/gogs/lang/fr.php new file mode 100644 index 0000000000..4f884b7518 --- /dev/null +++ b/module/gogs/lang/fr.php @@ -0,0 +1,35 @@ +gogs->common = 'Gogs'; +$lang->gogs->browse = 'Gogs Browse'; +$lang->gogs->search = 'Search'; +$lang->gogs->create = 'Create Gogs'; +$lang->gogs->edit = 'Edit Gogs'; +$lang->gogs->view = 'View Gogs'; +$lang->gogs->delete = 'Delete Gogs'; +$lang->gogs->confirmDelete = 'Do you want to delete this Gogs server?'; +$lang->gogs->bindUser = 'Bind User'; +$lang->gogs->gogsAvatar = 'Avatar'; +$lang->gogs->gogsAccount = 'Gogs Account'; +$lang->gogs->gogsEmail = 'Email'; +$lang->gogs->zentaoAccount = 'Zentao Account'; +$lang->gogs->bindingStatus = 'Binding Status'; +$lang->gogs->notBind = 'Not bind'; +$lang->gogs->binded = 'Binded'; +$lang->gogs->bindDynamic = '%s and Zentao user %s'; + +$lang->gogs->browseAction = 'Gogs List'; +$lang->gogs->deleteAction = 'Delete Gogs'; + +$lang->gogs->id = 'ID'; +$lang->gogs->name = "Server Name"; +$lang->gogs->url = 'Server URL'; +$lang->gogs->token = 'Token'; + +$lang->gogs->tokenLimit = "The current token has no admin privilege. Please regenerate one with root user in Gogs."; +$lang->gogs->hostError = "So the current Gogs server address is invalid, please confirm that the current server can be accessed and try again."; +$lang->gogs->bindUserError = "Can not bind users repeatedly %s"; + +$lang->gogs->server = "Server List"; +$lang->gogs->lblCreate = 'Create Gogs Server'; +$lang->gogs->emptyError = " cannot be empty"; +$lang->gogs->createSuccess = "Create success"; diff --git a/module/gogs/lang/vi.php b/module/gogs/lang/vi.php new file mode 100644 index 0000000000..e59bfec93d --- /dev/null +++ b/module/gogs/lang/vi.php @@ -0,0 +1,35 @@ +gogs->common = 'Gogs'; +$lang->gogs->browse = 'Gogs Browse'; +$lang->gogs->search = 'Search'; +$lang->gogs->create = 'Create Gogs'; +$lang->gogs->edit = 'Edit Gogs'; +$lang->gogs->view = 'View Gogs'; +$lang->gogs->delete = 'Delete Gogs'; +$lang->gogs->confirmDelete = 'Do you want to delete this Gogs server?'; +$lang->gogs->gogsAvatar = 'Avatar'; +$lang->gogs->bindUser = 'Bind User'; +$lang->gogs->gogsEmail = 'Email'; +$lang->gogs->gogsAccount = 'Gogs Account'; +$lang->gogs->zentaoAccount = 'Zentao Account'; +$lang->gogs->bindingStatus = 'Binding Status'; +$lang->gogs->notBind = 'Not bind'; +$lang->gogs->binded = 'Binded'; +$lang->gogs->bindDynamic = '%s and Zentao user %s'; + +$lang->gogs->browseAction = 'Gogs List'; +$lang->gogs->deleteAction = 'Delete Gogs'; + +$lang->gogs->id = 'ID'; +$lang->gogs->name = "Server Name"; +$lang->gogs->url = 'Server URL'; +$lang->gogs->token = 'Token'; + +$lang->gogs->tokenLimit = "The current token has no admin privilege. Please regenerate one with root user in Gogs."; +$lang->gogs->hostError = "So the current Gogs server address is invalid, please confirm that the current server can be accessed and try again."; +$lang->gogs->bindUserError = "Can not bind users repeatedly %s"; + +$lang->gogs->server = "Server List"; +$lang->gogs->lblCreate = 'Create Gogs Server'; +$lang->gogs->emptyError = " cannot be empty"; +$lang->gogs->createSuccess = "Create success"; diff --git a/module/gogs/lang/zh-cn.php b/module/gogs/lang/zh-cn.php new file mode 100644 index 0000000000..a21dc80ba6 --- /dev/null +++ b/module/gogs/lang/zh-cn.php @@ -0,0 +1,35 @@ +gogs->common = 'Gogs'; +$lang->gogs->browse = '浏览Gogs'; +$lang->gogs->search = '搜索'; +$lang->gogs->create = '添加Gogs'; +$lang->gogs->edit = '编辑Gogs'; +$lang->gogs->view = '查看Gogs'; +$lang->gogs->delete = '删除Gogs'; +$lang->gogs->confirmDelete = '确认删除该Gogs吗?'; +$lang->gogs->bindUser = '绑定用户'; +$lang->gogs->gogsAvatar = '头像'; +$lang->gogs->gogsAccount = 'Gogs用户'; +$lang->gogs->gogsEmail = '邮箱'; +$lang->gogs->zentaoAccount = '禅道用户'; +$lang->gogs->bindingStatus = '绑定状态'; +$lang->gogs->notBind = '未绑定'; +$lang->gogs->binded = '已绑定'; +$lang->gogs->bindDynamic = '%s与禅道用户%s'; + +$lang->gogs->browseAction = 'Gogs列表'; +$lang->gogs->deleteAction = '删除Gogs'; + +$lang->gogs->id = 'ID'; +$lang->gogs->name = "服务器名称"; +$lang->gogs->url = '服务器地址'; +$lang->gogs->token = 'Token'; + +$lang->gogs->tokenLimit = "Gogs Token权限不足。"; +$lang->gogs->hostError = "当前Gogs服务器地址无效,请确认当前服务器可被访问"; +$lang->gogs->bindUserError = "不能重复绑定用户 %s"; + +$lang->gogs->server = "服务器列表"; +$lang->gogs->lblCreate = '添加Gogs服务器'; +$lang->gogs->emptyError = "不能为空"; +$lang->gogs->createSuccess = "创建成功"; diff --git a/module/gogs/lang/zh-tw.php b/module/gogs/lang/zh-tw.php new file mode 100644 index 0000000000..ee8ef6470c --- /dev/null +++ b/module/gogs/lang/zh-tw.php @@ -0,0 +1,32 @@ +gogs->common = 'Gogs'; +$lang->gogs->browse = '浏览Gogs'; +$lang->gogs->search = '搜索'; +$lang->gogs->create = '添加Gogs'; +$lang->gogs->edit = '编辑Gogs'; +$lang->gogs->view = '查看Gogs'; +$lang->gogs->delete = '删除Gogs'; +$lang->gogs->confirmDelete = '确认删除该Gogs吗?'; +$lang->gogs->bindUser = '绑定用户'; +$lang->gogs->gogsAccount = 'Gogs用户'; +$lang->gogs->zentaoAccount = '禅道用户'; +$lang->gogs->bindingStatus = '绑定状态'; +$lang->gogs->notBind = '未绑定'; +$lang->gogs->binded = '已绑定'; +$lang->gogs->bindDynamic = '%s与禅道用户%s'; + +$lang->gogs->browseAction = 'Gogs列表'; +$lang->gogs->deleteAction = '删除Gogs'; + +$lang->gogs->id = 'ID'; +$lang->gogs->name = "服务器名称"; +$lang->gogs->url = '服务器地址'; +$lang->gogs->token = 'Token'; + +$lang->gogs->tokenLimit = "Gogs Token权限不足。"; +$lang->gogs->hostError = "当前Gogs服务器地址无效,请确认当前服务器可被访问"; + +$lang->gogs->server = "服务器列表"; +$lang->gogs->lblCreate = '添加Gogs服务器'; +$lang->gogs->emptyError = "不能为空"; +$lang->gogs->createSuccess = "创建成功"; diff --git a/module/gogs/model.php b/module/gogs/model.php new file mode 100644 index 0000000000..52d17580cb --- /dev/null +++ b/module/gogs/model.php @@ -0,0 +1,583 @@ + + * @package product + * @version $Id: $ + * @link http://www.zentao.net + */ + +class gogsModel extends model +{ + /** + * Get a gogs by id. + * + * @param int $id + * @access public + * @return object + */ + public function getByID($id) + { + return $this->loadModel('pipeline')->getByID($id); + } + + /** + * Get gogs list. + * + * @param string $orderBy + * @param object $pager + * @access public + * @return array + */ + public function getList($orderBy = 'id_desc', $pager = null) + { + $gogsList = $this->loadModel('pipeline')->getList('gogs', $orderBy, $pager); + + return $gogsList; + } + + /** + * Get gogs pairs. + * + * @access public + * @return array + */ + public function getPairs() + { + return $this->loadModel('pipeline')->getPairs('gogs'); + } + + /** + * Get gogs api base url by gogs id. + * + * @param int $gogsID + * @access public + * @return string + */ + public function getApiRoot($gogsID) + { + $gogs = $this->getByID($gogsID); + if(!$gogs) return ''; + + return rtrim($gogs->url, '/') . '/api/v1%s' . "?token={$gogs->token}"; + } + + /** + * Create a gogs. + * + * @access public + * @return bool + */ + public function create() + { + return $this->loadModel('pipeline')->create('gogs'); + } + + /** + * Update a gogs. + * + * @param int $id + * @access public + * @return bool + */ + public function update($id) + { + return $this->loadModel('pipeline')->update($id); + } + + /** + * Bind users. + * + * @param int $gogsID + * @access public + * @return array + */ + public function bindUser($gogsID) + { + $userPairs = $this->loadModel('user')->getPairs('noclosed|noletter'); + $users = $this->post->zentaoUsers; + $gogsNames = $this->post->gogsUserNames; + $accountList = array(); + $repeatUsers = array(); + foreach($users as $openID => $user) + { + if(empty($user)) continue; + if(isset($accountList[$user])) $repeatUsers[] = zget($userPairs, $user); + $accountList[$user] = $openID; + } + + if(count($repeatUsers)) + { + dao::$errors[] = sprintf($this->lang->gogs->bindUserError, join(',', $repeatUsers)); + return false; + } + + $user = new stdclass; + $user->providerID = $gogsID; + $user->providerType = 'gogs'; + + $oldUsers = $this->dao->select('*')->from(TABLE_OAUTH)->where('providerType')->eq($user->providerType)->andWhere('providerID')->eq($user->providerID)->fetchAll('openID'); + foreach($users as $openID => $account) + { + $existAccount = isset($oldUsers[$openID]) ? $oldUsers[$openID] : ''; + + if($existAccount and $existAccount->account != $account) + { + $this->dao->delete() + ->from(TABLE_OAUTH) + ->where('openID')->eq($openID) + ->andWhere('providerType')->eq($user->providerType) + ->andWhere('providerID')->eq($user->providerID) + ->exec(); + $this->loadModel('action')->create('gogsuser', $gogsID, 'unbind', '', sprintf($this->lang->gogs->bindDynamic, $gogsNames[$openID], $zentaoUsers[$existAccount->account]->realname)); + } + if(!$existAccount or $existAccount->account != $account) + { + if(!$account) continue; + $user->account = $account; + $user->openID = $openID; + $this->dao->insert(TABLE_OAUTH)->data($user)->exec(); + $this->loadModel('action')->create('gogsuser', $gogsID, 'bind', '', sprintf($this->lang->gogs->bindDynamic, $gogsNames[$openID], $zentaoUsers[$account]->realname)); + } + } + } + + /** + * Api error handling. + * + * @param object $response + * @access public + * @return bool + */ + public function apiErrorHandling($response) + { + if(!empty($response->error)) + { + dao::$errors[] = $response->error; + return false; + } + if(!empty($response->message)) + { + if(is_string($response->message)) + { + $errorKey = array_search($response->message, $this->lang->gogs->apiError); + dao::$errors[] = $errorKey === false ? $response->message : zget($this->lang->gogs->errorLang, $errorKey); + } + else + { + foreach($response->message as $field => $fieldErrors) + { + if(is_string($fieldErrors)) + { + $errorKey = array_search($fieldErrors, $this->lang->gogs->apiError); + if($fieldErrors) dao::$errors[$field][] = $errorKey === false ? $fieldErrors : zget($this->lang->gogs->errorLang, $errorKey); + } + else + { + foreach($fieldErrors as $error) + { + $errorKey = array_search($error, $this->lang->gogs->apiError); + if($error) dao::$errors[$field][] = $errorKey === false ? $error : zget($this->lang->gogs->errorLang, $errorKey); + } + } + } + } + } + + if(!$response) dao::$errors[] = false; + return false; + } + + /** + * Check token access. + * + * @param string $url + * @param string $token + * @access public + * @return void + */ + public function checkTokenAccess($url = '', $token = '') + { + $apiRoot = rtrim($url, '/') . '/api/v1%s' . "?token={$token}"; + $url = sprintf($apiRoot, "/user"); + $httpData = commonModel::httpWithHeader($url); + $user = json_decode($httpData['body']); + if(empty($httpData['header'])) return false; + if(empty($user)) return null; + + /* Check whether the token belongs to the administrator by edit user. */ + $editUserUrl = sprintf($apiRoot, "/admin/users/" . $user->username); + $data = new stdclass(); + $data->login_name = $user->login; + $data->email = $user->email; + + $result = commonModel::http($editUserUrl, $data, array(), array(), 'data', 'PATCH'); + $user = json_decode($result); + if(empty($user)) return null; + + return true; + } + + /** + * Get Gogs id list by user account. + * + * @param string $account + * @access public + * @return array + */ + public function getGogsListByAccount($account = '') + { + if(!$account) $account = $this->app->user->account; + + return $this->dao->select('providerID,openID')->from(TABLE_OAUTH) + ->where('providerType')->eq('gogs') + ->andWhere('account')->eq($account) + ->fetchPairs('providerID'); + } + + /** + * Get zentao account gogs user id pairs of one gogs. + * + * @param int $gogsID + * @access public + * @return array + */ + public function getUserAccountIdPairs($gogsID, $fields = 'account,openID') + { + return $this->dao->select($fields)->from(TABLE_OAUTH) + ->where('providerType')->eq('gogs') + ->andWhere('providerID')->eq($gogsID) + ->fetchPairs(); + } + + /** + * Get gogs user id by zentao account. + * + * @param int $gogsID + * @param string $zentaoAccount + * @access public + * @return array + */ + public function getUserIDByZentaoAccount($gogsID, $zentaoAccount) + { + return $this->dao->select('openID')->from(TABLE_OAUTH) + ->where('providerType')->eq('gogs') + ->andWhere('providerID')->eq($gogsID) + ->andWhere('account')->eq($zentaoAccount) + ->fetch('openID'); + } + + /** + * Get matched gogs users. + * + * @param int $gogsID + * @param array $gogsUsers + * @param array $zentaoUsers + * @access public + * @return array + */ + public function getMatchedUsers($gogsID, $gogsUsers, $zentaoUsers) + { + $matches = new stdclass; + foreach($gogsUsers as $gogsUser) + { + foreach($zentaoUsers as $zentaoUser) + { + if($gogsUser->account == $zentaoUser->account) $matches->accounts[$gogsUser->account][] = $zentaoUser->account; + if($gogsUser->realname == $zentaoUser->realname) $matches->names[$gogsUser->realname][] = $zentaoUser->account; + if($gogsUser->email == $zentaoUser->email) $matches->emails[$gogsUser->email][] = $zentaoUser->account; + } + } + + $bindedUsers = $this->getUserAccountIdPairs($gogsID, 'openID,account'); + $matchedUsers = array(); + foreach($gogsUsers as $gogsUser) + { + if(isset($bindedUsers[$gogsUser->account])) + { + $gogsUser->zentaoAccount = $bindedUsers[$gogsUser->account]; + $matchedUsers[] = $gogsUser; + continue; + } + + $matchedZentaoUsers = array(); + if(isset($matches->accounts[$gogsUser->account])) $matchedZentaoUsers = array_merge($matchedZentaoUsers, $matches->accounts[$gogsUser->account]); + if(isset($matches->emails[$gogsUser->email])) $matchedZentaoUsers = array_merge($matchedZentaoUsers, $matches->emails[$gogsUser->email]); + if(isset($matches->names[$gogsUser->realname])) $matchedZentaoUsers = array_merge($matchedZentaoUsers, $matches->names[$gogsUser->realname]); + + $matchedZentaoUsers = array_unique($matchedZentaoUsers); + if(count($matchedZentaoUsers) == 1) + { + $gogsUser->zentaoAccount = current($matchedZentaoUsers); + $matchedUsers[] = $gogsUser; + } + } + + return $matchedUsers; + } + + /** + * Get project by api. + * + * @param int $gogsID + * @param int $projectID + * @access public + * @return void + */ + public function apiGetSingleProject($gogsID, $projectID) + { + $apiRoot = $this->getApiRoot($gogsID); + if(!$apiRoot) return array(); + + $url = sprintf($apiRoot, "/repos/$projectID"); + $project = json_decode(commonModel::http($url)); + if(isset($project->name)) + { + $project->name_with_namespace = $project->full_name; + $project->path_with_namespace = $project->full_name; + $project->http_url_to_repo = $project->html_url; + $project->name_with_namespace = $project->full_name; + + $gogs = $this->getByID($gogsID); + $oauth = "oauth2:{$gogs->token}@"; + $project->tokenCloneUrl = preg_replace('/(http(s)?:\/\/)/', "\$1$oauth", $project->html_url); + } + + return $project; + } + + /** + * Get projects by api. + * + * @param int $gogsID + * @access public + * @return array + */ + public function apiGetProjects($gogsID) + { + $apiRoot = $this->getApiRoot($gogsID); + if(!$apiRoot) return array(); + + $user = $this->apiGetAdminer($gogsID); + if(!$user) return array(); + + $url = sprintf($apiRoot, "/users/{$user->username}/repos"); + $allResults = array(); + for($page = 1; true; $page++) + { + $results = json_decode(commonModel::http($url . "&page={$page}&limit=50")); + if(!is_array($results)) break; + if(!empty($results)) $allResults = array_merge($allResults, $results); + if(count($results) < 50) break; + } + + return $allResults; + } + + /** + * Api get adminer. + * + * @param int $gogsID + * @access public + * @return void + */ + public function apiGetAdminer($gogsID) + { + $apiRoot = $this->getApiRoot($gogsID); + if(!$apiRoot) return array(); + + $url = sprintf($apiRoot, "/user"); + $user = json_decode(commonModel::http($url)); + + return isset($user->username) ? $user : null; + } + + /** + * Get gogs user list. + * + * @param int $gogsID + * @param bool $onlyLinked + * @access public + * @return array + */ + public function apiGetUsers($gogsID, $onlyLinked = false) + { + $response = array(); + $apiRoot = $this->getApiRoot($gogsID); + + for($page = 1; true; $page++) + { + $url = sprintf($apiRoot, "/admin/users") . "&page={$page}&limit=20"; + $result = json_decode(commonModel::http($url)); + if(empty($result->data)) break; + $response = array_merge($response, $result->data); + $page += 1; + } + + if(empty($response)) return array(); + + /* Get linked users. */ + $linkedUsers = array(); + if($onlyLinked) $linkedUsers = $this->getUserAccountIdPairs($gogsID, 'openID,account'); + + $users = array(); + foreach($response as $gogsUser) + { + if($onlyLinked and !isset($linkedUsers[$gogsUser->id])) continue; + + $user = new stdclass; + $user->id = $gogsUser->id; + $user->realname = $gogsUser->full_name ? $gogsUser->full_name : $gogsUser->username; + $user->account = $gogsUser->username; + $user->email = zget($gogsUser, 'email', ''); + $user->avatar = $gogsUser->avatar_url; + $user->createdAt = zget($gogsUser, 'created', ''); + $user->lastActivityOn = zget($gogsUser, 'login', ''); + + $users[] = $user; + } + + return $users; + } + + /** + * Get project repository branches by api. + * + * @param int $gogsID + * @param string $project + * @access public + * @return object + */ + public function apiGetBranches($gogsID, $project, $pager = null) + { + $url = sprintf($this->getApiRoot($gogsID), "/repos/{$project}/branches"); + $allResults = array(); + for($page = 1; true; $page++) + { + $results = json_decode(commonModel::http($url . "&page={$page}&limit=50")); + if(!is_array($results)) break; + if(!empty($results)) $allResults = array_merge($allResults, $results); + if(count($results) < 100) break; + } + + return $allResults; + } + + /** + * Get upstream project by API. + * + * @param int $gogsID + * @param string $project + * @access public + * @return void + */ + public function apiGetUpstream($gogsID, $project) + { + $currentProject = $this->apiGetSingleProject($gogsID, $project); + if(isset($currentProject->parent->full_name)) return $currentProject->parent->full_name; + return array(); + } + + /** + * Get branches. + * + * @param int $gogsID + * @param string $project + * @access public + * @return array + */ + public function getBranches($gogsID, $project) + { + $rawBranches = $this->apiGetBranches($gogsID, $project); + + $branches = array(); + foreach($rawBranches as $branch) $branches[] = $branch->name; + + return $branches; + } + + /** + * Get gogs user id and realname pairs of one gogs. + * + * @param int $gogsID + * @access public + * @return array + */ + public function getUserIdRealnamePairs($gogsID) + { + return $this->dao->select('oauth.openID as openID,user.realname as realname') + ->from(TABLE_OAUTH)->alias('oauth') + ->leftJoin(TABLE_USER)->alias('user') + ->on("oauth.account = user.account") + ->where('providerType')->eq('gogs') + ->andWhere('providerID')->eq($gogsID) + ->fetchPairs(); + } + + /** + * Get single branch by API. + * + * @param int $gogsID + * @param string $project + * @param string $branchName + * @access public + * @return object + */ + public function apiGetSingleBranch($gogsID, $project, $branchName) + { + $url = sprintf($this->getApiRoot($gogsID), "/repos/$project/branches/$branchName"); + $branch = json_decode(commonModel::http($url)); + if($branch) + { + $gogs = $this->getByID($gogsID); + $branch->web_url = "{$gogs->url}/$project/src/$branchName"; + } + + return $branch; + } + + /** + * Get protect branches of one project. + * + * @param int $gogsID + * @param string $project + * @param string $keyword + * @access public + * @return array + */ + public function apiGetBranchPrivs($gogsID, $project, $keyword = '') + { + $keyword = urlencode($keyword); + $url = sprintf($this->getApiRoot($gogsID), "/repos/$project/branch_protections"); + $branches = json_decode(commonModel::http($url)); + + if(!is_array($branches)) return array(); + + $newBranches = array(); + foreach($branches as $branch) + { + $branch->name = $branch->Name; + if(empty($keyword) || stristr($branch->name, $keyword)) $newBranches[] = $branch; + } + + return $newBranches; + } + + /** + * Api delete branch. + * + * @param int $gogsID + * @param string $project + * @param string $branch + * @access public + * @return void + */ + public function apiDeleteBranch($gogsID, $project, $branch) + { + $url = sprintf($this->getApiRoot($gogsID), "/repos/$project/branches/$branch"); + return json_decode(commonModel::http($url, null, array(CURLOPT_CUSTOMREQUEST => 'DELETE'))); + } +} diff --git a/module/gogs/view/binduser.html.php b/module/gogs/view/binduser.html.php new file mode 100644 index 0000000000..b06735dd9b --- /dev/null +++ b/module/gogs/view/binduser.html.php @@ -0,0 +1,85 @@ + + * @package gogs + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +
+
+

gogs->bindUser;?>

+
+
+
+ + + + + + + + + + + + + zentaoAccount)) continue;?> + account]", $gogsUser->realname);?> + + + + + + + + + + zentaoAccount)) continue;?> + account]", $gogsUser->realname);?> + + + + + + + + + + + + + + +
gogs->gogsAvatar;?>gogs->gogsAccount;?>gogs->gogsEmail;?>gogs->zentaoAccount;?>gogs->bindingStatus;?>
avatar, "height=40");?> + realname;?> +
+ account;?> +
email;?>account]", $userPairs, '', "class='form-control select chosen'" );?>gogs->notBind;?>
avatar, "height=40");?> + realname;?> +
+ account;?> +
email;?>account]", $userPairs, $gogsUser->zentaoAccount, "class='form-control select chosen'" );?> + zentaoAccount])):?> + zentaoAccount, '');?> + + gogs->binded;?> + + ' . $lang->gogs->bindedError . '';?> + + + gogs->notBind;?> + +
+ + goback, '', 'class="btn btn-wide"');?> +
+
+
+
+ diff --git a/module/gogs/view/browse.html.php b/module/gogs/view/browse.html.php new file mode 100644 index 0000000000..d925058381 --- /dev/null +++ b/module/gogs/view/browse.html.php @@ -0,0 +1,73 @@ + + * @package gogs + * @version $Id$ + * @link http://www.zentao.net + */ +?> + + + +
+

+ noData;?> + + createLink('gogs', 'create'), " " . $lang->gogs->create, '', "class='btn btn-info'");?> + +

+
+ +
+
+ + + + recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}";?> + + + + + + + + $gogs): ?> + + + + + + + + +
gogs->id);?>gogs->name);?>gogs->url);?>actions;?>
+ + ">name;?> + + name;?> + + url, $gogs->url, '_target');?> + isBindUser); + common::printIcon('gogs', 'delete', "gogsID=$id", '', 'list', 'trash', 'hiddenwin'); + ?> +
+ + + +
+
+ + diff --git a/module/gogs/view/create.html.php b/module/gogs/view/create.html.php new file mode 100644 index 0000000000..df7c376c66 --- /dev/null +++ b/module/gogs/view/create.html.php @@ -0,0 +1,45 @@ + + * @package gogs + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +
+
+
+
+

gogs->lblCreate;?>

+
+
+ + + + + + + + + + + + + + + + + +
gogs->name;?>
gogs->url;?>
gogs->token;?>
+ +
+
+
+
+
+ diff --git a/module/gogs/view/edit.html.php b/module/gogs/view/edit.html.php new file mode 100644 index 0000000000..aafb2fe439 --- /dev/null +++ b/module/gogs/view/edit.html.php @@ -0,0 +1,45 @@ + + * @package gogs + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +
+
+
+
+

gogs->edit;?>

+
+
+ + + + + + + + + + + + + + + + + +
gogs->name;?>name) ? $gogs->name : '', "class='form-control'");?>
gogs->url;?>url) ? $gogs->url : '', "class='form-control'");?>
gogs->token;?>token) ? $gogs->token : '', "class='form-control'");?>
+ +
+
+
+
+
+ diff --git a/module/gogs/view/view.html.php b/module/gogs/view/view.html.php new file mode 100644 index 0000000000..66cb881716 --- /dev/null +++ b/module/gogs/view/view.html.php @@ -0,0 +1,37 @@ + + * @package GitLab + * @version $Id: view.html.php 4728 2013-05-03 06:14:34Z david18810279601@gmail.com $ + * @link http://www.zentao.net + * */ +?> + + + +
+
+
+
+
gogs->url;?>
+
url, $gogs->url, '_target');?>
+
+
+
+
+
+ diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index 56080b05d5..05bdbd7aad 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -69,7 +69,8 @@ $lang->moduleOrder[215] = 'message'; $lang->moduleOrder[220] = 'gitlab'; $lang->moduleOrder[225] = 'mr'; $lang->moduleOrder[230] = 'app'; -$lang->moduleOrder[235] = 'gitea'; +$lang->moduleOrder[235] = 'gogs'; +$lang->moduleOrder[240] = 'gitea'; $lang->resource = new stdclass(); @@ -1180,11 +1181,13 @@ $lang->ci->methodOrder[5] = 'commitResult'; $lang->ci->methodOrder[10] = 'checkCompileStatus'; $lang->resource->compile = new stdclass(); -$lang->resource->compile->browse = 'browse'; -$lang->resource->compile->logs = 'logs'; +$lang->resource->compile->browse = 'browse'; +$lang->resource->compile->logs = 'logs'; +$lang->resource->compile->syncCompile = 'syncCompile'; $lang->compile->methodOrder[5] = 'browse'; $lang->compile->methodOrder[10] = 'logs'; +$lang->compile->methodOrder[15] = 'syncCompile'; $lang->resource->jenkins = new stdclass(); $lang->resource->jenkins->browse = 'browseAction'; @@ -1391,6 +1394,22 @@ $lang->gitlab->methodOrder[145] = 'browseTag'; $lang->gitlab->methodOrder[150] = 'createTag'; $lang->gitlab->methodOrder[155] = 'deleteTag'; +/* Gogs. */ +$lang->resource->gogs = new stdclass(); +$lang->resource->gogs->browse = 'browse'; +$lang->resource->gogs->create = 'create'; +$lang->resource->gogs->edit = 'edit'; +$lang->resource->gogs->view = 'view'; +$lang->resource->gogs->delete = 'delete'; +$lang->resource->gogs->bindUser = 'bindUser'; + +$lang->gogs->methodOrder[5] = 'browse'; +$lang->gogs->methodOrder[10] = 'create'; +$lang->gogs->methodOrder[15] = 'edit'; +$lang->gogs->methodOrder[20] = 'view'; +$lang->gogs->methodOrder[25] = 'delete'; +$lang->gogs->methodOrder[30] = 'bindUser'; + /* Gitea. */ $lang->resource->gitea = new stdclass(); $lang->resource->gitea->browse = 'browse'; diff --git a/module/jenkins/config.php b/module/jenkins/config.php index 874d71aea1..f86ffd1b7c 100644 --- a/module/jenkins/config.php +++ b/module/jenkins/config.php @@ -1,5 +1,5 @@ jenkins->create = new stdclass(); $config->jenkins->edit = new stdclass(); -$config->jenkins->create->requiredFields = 'name,url'; -$config->jenkins->edit->requiredFields = 'name,url'; +$config->jenkins->create->requiredFields = 'name,url,account'; +$config->jenkins->edit->requiredFields = 'name,url,account'; diff --git a/module/job/control.php b/module/job/control.php index f6440f3af3..3a2b28a1e1 100644 --- a/module/job/control.php +++ b/module/job/control.php @@ -49,12 +49,24 @@ class job extends control $pager = new pager($recTotal, $recPerPage, $pageID); $this->app->loadLang('compile'); - $this->view->jobList = $this->job->getList($repoID, $orderBy, $pager); + $jobList = $this->job->getList($repoID, $orderBy, $pager); + $this->loadModel('gitlab'); + foreach($jobList as $job) + { + $job->canExec = true; + if($job->engine == 'gitlab') + { + $pipeline = json_decode($job->pipeline); + $branch = $this->gitlab->apiGetSingleBranch($job->server, $pipeline->project, $pipeline->reference); + if($branch and isset($branch->can_push) and !$branch->can_push) $job->canExec = false; + } + } $this->view->title = $this->lang->ci->job . $this->lang->colon . $this->lang->job->browse; $this->view->position[] = $this->lang->ci->job; $this->view->position[] = $this->lang->job->browse; $this->view->repoID = $repoID; + $this->view->jobList = $jobList; $this->view->orderBy = $orderBy; $this->view->pager = $pager; diff --git a/module/job/css/create.css b/module/job/css/create.css index a70ae4bc4d..188dd0855c 100644 --- a/module/job/css/create.css +++ b/module/job/css/create.css @@ -5,6 +5,6 @@ #server_chosen .chosen-single {border-right: 0px;} #pipelineBox {width: 385px;} #svnDirBox .input-group {width: unset !important;} -#paramDiv .input-group {padding: 3px;} +#jenkinsServerTR .input-group-addon {border-left-width: 0 !important;} .chosen-container .chosen-drop{z-index: 1100;} /* from c20b7e7a5 */ diff --git a/module/job/css/edit.css b/module/job/css/edit.css index 2eaabdfeb4..57c8a09567 100644 --- a/module/job/css/edit.css +++ b/module/job/css/edit.css @@ -5,4 +5,4 @@ #server_chosen .chosen-single {border-right: 0px;} #pipelineBox {width: 385px;} #svnDirBox .input-group {width: unset !important;} -#paramDiv .input-group {padding: 3px;} +#jenkinsServerTR .input-group-addon {border-left-width: 0 !important;} diff --git a/module/job/model.php b/module/job/model.php index c3d53774e7..7bae1cf900 100644 --- a/module/job/model.php +++ b/module/job/model.php @@ -181,6 +181,15 @@ class jobModel extends model { $repo = $this->loadModel('repo')->getRepoByID($job->repo); $project = zget($repo, 'project'); + if(!empty($repo)) + { + $pipeline = $this->loadModel('gitlab')->apiGetPipeline($repo->serviceHost, $repo->serviceProject, $this->post->reference); + if(!is_array($pipeline) or empty($pipeline)) + { + dao::$errors['repo'] = $this->lang->job->engineTips->error; + return false; + } + } $job->server = (int)zget($repo, 'serviceHost', 0); $job->pipeline = json_encode(array('project' => $project, 'reference' => $this->post->reference)); @@ -298,6 +307,15 @@ class jobModel extends model { $repo = $this->loadModel('repo')->getRepoByID($job->gitlabRepo); $project = zget($repo, 'project'); + if(!empty($repo)) + { + $pipeline = $this->loadModel('gitlab')->apiGetPipeline($repo->serviceHost, $repo->serviceProject, $this->post->reference); + if(!is_array($pipeline) or empty($pipeline)) + { + dao::$errors['repo'] = $this->lang->job->engineTips->error; + return false; + } + } $job->repo = $job->gitlabRepo; $job->server = (int)zget($repo, 'serviceHost', 0); diff --git a/module/job/view/browse.html.php b/module/job/view/browse.html.php index 0a1e92e209..34daa1d6bc 100644 --- a/module/job/view/browse.html.php +++ b/module/job/view/browse.html.php @@ -74,7 +74,7 @@ repo}&jobID=$id", '', 'list', 'history'); common::printIcon('job', 'edit', "jobID=$id", '', 'list', 'edit'); - common::printIcon('job', 'exec', "jobID=$id", '', 'list', 'play'); + common::printIcon('job', 'exec', "jobID=$id", '', 'list', 'play', '', $job->canExec ? '' : 'disabled'); if(common::hasPriv('job', 'delete')) echo html::a($this->createLink('job', 'delete', "jobID=$id"), '', 'hiddenwin', "title='{$lang->job->delete}' class='btn'"); ?> diff --git a/module/job/view/create.html.php b/module/job/view/create.html.php index fddeb4505b..5bdd794655 100644 --- a/module/job/view/create.html.php +++ b/module/job/view/create.html.php @@ -62,7 +62,7 @@ job->triggerType; ?> - job->triggerTypeList, '', "class='form-control'");?> + job->triggerTypeList, '', "class='form-control chosen'");?> @@ -122,9 +122,9 @@ job->customParam;?>
- job->paramName; ?> + '>job->paramName; ?> - job->paramValue; ?> + '>job->paramValue; ?> job->paramValueList, '', "class='form-control' onchange='setParamName(this)'"); ?> diff --git a/module/job/view/edit.html.php b/module/job/view/edit.html.php index 15bd823043..82398bcdd7 100644 --- a/module/job/view/edit.html.php +++ b/module/job/view/edit.html.php @@ -63,7 +63,7 @@ job->triggerType;?> job->triggerTypeList['tag'] = $lang->job->dirChange;?> - job->triggerTypeList, $job->triggerType, "class='form-control'");?> + job->triggerTypeList, $job->triggerType, "class='form-control chosen'");?> @@ -170,9 +170,9 @@
- job->paramName;?> + '>job->paramName; ?> - job->paramValue;?> + '>job->paramValue; ?> job->paramValueList, '', "class='form-control' onchange='setParamName(this)'");?> diff --git a/module/mr/config.php b/module/mr/config.php index 3ffe3fd9bd..b0cb590a0b 100644 --- a/module/mr/config.php +++ b/module/mr/config.php @@ -33,4 +33,4 @@ $config->mrapproval->create = new stdclass(); $config->mrapproval->create->skippedFields = ''; $config->mrapproval->create->requiredFields = 'mrID,account,date,action'; -$config->mr->gitServiceList = array('gitlab', 'gitea'); +$config->mr->gitServiceList = array('gitlab', 'gitea', 'gogs'); diff --git a/module/mr/control.php b/module/mr/control.php index b9494df0ef..37fde7fcb8 100644 --- a/module/mr/control.php +++ b/module/mr/control.php @@ -34,7 +34,7 @@ class mr extends control $this->app->loadClass('pager', $static = true); $pager = new pager($recTotal, $recPerPage, $pageID); - $repos = $this->loadModel('repo')->getListBySCM(array('Gitlab', 'Gitea')); + $repos = $this->loadModel('repo')->getListBySCM(array('Gitlab', 'Gitea', 'Gogs')); if(empty($repos)) $this->locate($this->repo->createLink('create')); $repoID = $this->repo->saveState($repoID, $objectID); @@ -69,10 +69,14 @@ class mr extends control { $openIDList = $this->loadModel('gitlab')->getGitLabListByAccount($this->app->user->account); } - else + elseif($repo->SCM == 'Gitea') { $openIDList = $this->loadModel('gitea')->getGiteaListByAccount($this->app->user->account); } + elseif($repo->SCM == 'Gogs') + { + $openIDList = $this->loadModel('gogs')->getGogsListByAccount($this->app->user->account); + } } $this->view->title = $this->lang->mr->common . $this->lang->colon . $this->lang->mr->browse; @@ -108,22 +112,26 @@ class mr extends control $repoID = $this->loadModel('repo')->saveState(0); $repo = $this->repo->getRepoByID($repoID); + $this->loadModel('gitlab'); $this->loadModel('gitea'); + $this->loadModel('gogs'); if($repo->SCM == 'Gitea') { $project = $this->gitea->apiGetSingleProject($repo->gitService, $repo->project); if(empty($project) or !$project->allow_merge_commits) $repo = array(); } - $hosts = $this->loadModel('pipeline')->getList(array('gitea', 'gitlab')); + $hosts = $this->loadModel('pipeline')->getList(array('gitea', 'gitlab', 'gogs')); if(!$this->app->user->admin) { - $gitlabUsers = $this->loadModel('gitlab')->getGitLabListByAccount(); + $gitlabUsers = $this->gitlab->getGitLabListByAccount(); $giteaUsers = $this->gitea->getGiteaListByAccount(); + $gogsUsers = $this->gogs->getGogsListByAccount(); foreach($hosts as $hostID => $host) { if($host->type == 'gitlab' and isset($gitlabUsers[$hostID])) continue; - if($host->type == 'gitea' and isset($giteaUsers[$hostID])) continue; + if($host->type == 'gitea' and isset($giteaUsers[$hostID])) continue; + if($host->type == 'gogs' and isset($gogsUsers[$hostID])) continue; unset($hosts[$hostID]); } @@ -243,6 +251,7 @@ class mr extends control if(isset($res->message)) return print(js::alert($this->mr->convertApiError($res->message))); } $this->dao->delete()->from(TABLE_MR)->where('id')->eq($MRID)->exec(); + $this->loadModel('action')->create('mr', $MRID, 'deleted', '', $MR->title); echo js::locate(inlink('browse'), 'parent'); } @@ -359,7 +368,7 @@ class mr extends control } } - if(isset($MR->hostID)) $rawMR = $this->mr->apiAcceptMR($MR->hostID, $MR->targetProject, $MR->mriid, $MR); + if(isset($MR->hostID)) $rawMR = $this->mr->apiAcceptMR($MR); if(isset($rawMR->state) and $rawMR->state == 'merged') { $this->mr->logMergedAction($MR); @@ -621,7 +630,7 @@ class mr extends control $this->loadModel('search')->setSearchParams($this->config->product->search); $MR = $this->mr->getByID($MRID); - $relatedStories = $this->mr->getCommitedLink($MR->hostID, $MR->targetProject, $MR->mriid, 'story'); + $relatedStories = $this->mr->getCommitedLink($MR, 'story'); $linkedStories = $this->mr->getLinkList($MRID, $product->id, 'story'); if($browseType == 'bySearch') @@ -706,7 +715,7 @@ class mr extends control $this->loadModel('search')->setSearchParams($this->config->bug->search); $MR = $this->mr->getByID($MRID); - $relatedBugs = $this->mr->getCommitedLink($MR->hostID, $MR->targetProject, $MR->mriid, 'bug'); + $relatedBugs = $this->mr->getCommitedLink($MR, 'bug'); $linkedBugs = $this->mr->getLinkList($MRID, $product->id, 'bug'); if($browseType == 'bySearch') @@ -778,7 +787,7 @@ class mr extends control $this->loadModel('search')->setSearchParams($this->config->execution->search); $MR = $this->mr->getByID($MRID); - $relatedTasks = $this->mr->getCommitedLink($MR->hostID, $MR->targetProject, $MR->mriid, 'task'); + $relatedTasks = $this->mr->getCommitedLink($MR, 'task'); $linkedTasks = $this->mr->getLinkList($MRID, $product->id, 'task'); /* Get executions by product. */ @@ -1036,7 +1045,7 @@ class mr extends control $targetBranch = $this->post->targetBranch; $result = $this->mr->checkSameOpened($hostID, $sourceProject, $sourceBranch, $targetProject, $targetBranch); - echo json_encode($result); + return print(json_encode($result)); } /** @@ -1051,11 +1060,11 @@ class mr extends control { $host = $this->loadModel('pipeline')->getByID($hostID); $scm = $host->type; - if($scm == 'gitea') $project = urldecode(base64_decode($project)); + if(in_array($scm, array('gitea', 'gogs'))) $project = urldecode(base64_decode($project)); - $branches = $this->loadModel($scm)->apiGetBranchPrivs($hostID, $project); $branchPrivs = array(); + $branches = $this->loadModel($scm)->apiGetBranchPrivs($hostID, $project); foreach($branches as $branch) $branchPrivs[$branch->name] = $branch->name; - echo json_encode($branchPrivs); + return print(json_encode($branchPrivs)); } } diff --git a/module/mr/js/create.js b/module/mr/js/create.js index 316ecb0368..5c237ca2f6 100644 --- a/module/mr/js/create.js +++ b/module/mr/js/create.js @@ -156,10 +156,14 @@ $(function() { var url = createLink('repo', 'ajaxGetGitlabProjects', "gitlabID=" + hostID + "&projectIdList=&filter=IS_DEVELOPER"); } - else + else if(hosts[hostID].type == 'gitea') { var url = createLink('repo', 'ajaxGetGiteaProjects', "giteaID=" + hostID); } + else if(hosts[hostID].type == 'gogs') + { + var url = createLink('repo', 'ajaxGetGogsProjects', "gogsID=" + hostID); + } $.get(url, function(response) { if(response == "" && confirm(mrLang.addForApp) == true) window.open(hosts[hostID].url); diff --git a/module/mr/model.php b/module/mr/model.php index e85a33f5a6..7506d041d3 100644 --- a/module/mr/model.php +++ b/module/mr/model.php @@ -126,6 +126,19 @@ class mrModel extends model return array($hostID => array_column($projects, null, 'full_name')); } + /** + * Get gogs projects. + * + * @param int $hostID + * @access public + * @return array + */ + public function getGogsProjects($hostID = 0) + { + $projects = $this->loadModel('gogs')->apiGetProjects($hostID); + return array($hostID => array_column($projects, null, 'full_name')); + } + /** * Get gitlab projects. * @@ -424,9 +437,9 @@ class mrModel extends model { $gitUsers = $this->loadModel('gitlab')->getUserIdAccountPairs($MR->hostID); } - elseif($rawMR->gitService == 'gitea') + else { - $gitUsers = $this->loadModel('gitea')->getUserAccountIdPairs($MR->hostID, 'openID,account'); + $gitUsers = $this->loadModel($rawMR->gitService)->getUserAccountIdPairs($MR->hostID, 'openID,account'); } $newMR = new stdclass; @@ -472,6 +485,7 @@ class mrModel extends model $this->loadModel('gitlab'); $this->loadModel('gitea'); + $this->loadModel('gogs'); foreach($MRList as $key => $MR) { if($MR->status != 'opened') continue; @@ -499,7 +513,8 @@ class mrModel extends model } else { - $users = $this->gitea->getUserAccountIdPairs($MR->hostID, 'openID,account'); + $scm = strtolower($scm); + $users = $this->$scm->getUserAccountIdPairs($MR->hostID, 'openID,account'); } $newMR = new stdclass; @@ -657,21 +672,21 @@ class mrModel extends model } return json_decode(commonModel::http($url, $MRObject)); } - else + elseif(in_array($host->type, array('gitea', 'gogs'))) { - $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls"); + $url = sprintf($this->loadModel($host->type)->getApiRoot($hostID), "/repos/$projectID/pulls"); $MRObject->head = $MR->sourceBranch; $MRObject->base = $MR->targetBranch; $MRObject->body = $MR->description; if($MR->assignee) { - $assignee = $this->gitea->getUserIDByZentaoAccount($this->post->hostID, $MR->assignee); + $assignee = $this->{$host->type}->getUserIDByZentaoAccount($this->post->hostID, $MR->assignee); if($assignee) $MRObject->assignee = $assignee; } $mergeResult = json_decode(commonModel::http($url, $MRObject)); - if(isset($mergeResult->number)) $mergeResult->iid = $mergeResult->number; + if(isset($mergeResult->number)) $mergeResult->iid = $host->type == 'gitea' ? $mergeResult->number : $mergeResult->id; if(isset($mergeResult->mergeable)) { if($mergeResult->mergeable) $mergeResult->merge_status = 'can_be_merged'; @@ -722,6 +737,22 @@ class mrModel extends model $MR->target_project_id = $projectID; } } + elseif($scm == 'Gogs') + { + foreach($response as $MR) + { + $MR->iid = $MR->id; + $MR->state = $MR->state == 'open' ? 'opened' : $MR->state; + if($MR->merged) $MR->state = 'merged'; + + $MR->merge_status = $MR->mergeable ? 'can_be_merged' : 'cannot_be_merged'; + $MR->description = $MR->body; + $MR->target_branch = $MR->base_branch; + $MR->source_branch = $MR->head_branch; + $MR->source_project_id = $projectID; + $MR->target_project_id = $projectID; + } + } return $response; } @@ -773,7 +804,7 @@ class mrModel extends model $url = sprintf($this->gitlab->getApiRoot($hostID), "/projects/$projectID/merge_requests/$MRID"); $MR = json_decode(commonModel::http($url)); } - else + else if($host->type == 'gitea') { $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); $MR = json_decode(commonModel::http($url)); @@ -796,6 +827,29 @@ class mrModel extends model $MR->has_conflicts = empty($diff) ? true : false; } } + else if($host->type == 'gogs') + { + $url = sprintf($this->loadModel('gogs')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); + $MR = json_decode(commonModel::http($url)); + if(isset($MR->html_url)) + { + $diff = $this->apiGetDiffs($hostID, $projectID, $MRID); + + $MR->web_url = $MR->html_url; + $MR->iid = $MR->id; + $MR->state = $MR->state == 'open' ? 'opened' : $MR->state; + if($MR->merged) $MR->state = 'merged'; + + $MR->merge_status = $MR->mergeable ? 'can_be_merged' : 'cannot_be_merged'; + $MR->changes_count = empty($diff) ? 0 : 1; + $MR->description = $MR->body; + $MR->target_branch = $MR->base_branch; + $MR->source_branch = $MR->head_branch; + $MR->source_project_id = $projectID; + $MR->target_project_id = $projectID; + $MR->has_conflicts = empty($diff) ? true : false; + } + } $MR->gitService = $host->type; return $MR; @@ -849,18 +903,18 @@ class mrModel extends model } else { - $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); + $url = sprintf($this->loadModel($host->type)->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); $newMR->base = $MR->targetBranch; $newMR->body = $MR->description; if($MR->assignee) { - $assignee = $this->gitea->getUserIDByZentaoAccount($this->post->hostID, $MR->assignee); + $assignee = $this->{$host->type}->getUserIDByZentaoAccount($this->post->hostID, $MR->assignee); if($assignee) $newMR->assignee = $assignee; } $mergeResult = json_decode(commonModel::http($url, $newMR, array(), array(), 'json', 'PATCH')); - if(isset($mergeResult->number)) $mergeResult->iid = $mergeResult->number; + if(isset($mergeResult->number)) $mergeResult->iid = $host->type == 'gitea' ? $mergeResult->number : $mergeResult->id; if(isset($mergeResult->mergeable)) { if($mergeResult->mergeable) $mergeResult->merge_status = 'can_be_merged'; @@ -923,7 +977,7 @@ class mrModel extends model } else { - $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); + $url = sprintf($this->loadModel($host->type)->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); return json_decode(commonModel::http($url, array('state' => 'closed'), array(), array(), 'json', 'PATCH')); } } @@ -946,7 +1000,7 @@ class mrModel extends model $url = sprintf($this->gitlab->getApiRoot($hostID), "/projects/$projectID/merge_requests/$MRID") . '&state_event=reopen'; return json_decode(commonModel::http($url, null, array(CURLOPT_CUSTOMREQUEST => 'PUT'))); } - else + elseif($host->type == 'gitea') { $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); $MR = json_decode(commonModel::http($url, array('state' => 'open'), array(), array(), 'json', 'PATCH')); @@ -961,6 +1015,23 @@ class mrModel extends model $MR->source_project_id = $projectID; $MR->target_project_id = $projectID; + return $MR; + } + elseif($host->type == 'gogs') + { + $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID"); + $MR = json_decode(commonModel::http($url, array('state' => 'open'), array(), array(), 'json', 'PATCH')); + $MR->iid = $MR->id; + $MR->state = $MR->state == 'open' ? 'opened' : $MR->state; + if($MR->merged) $MR->state = 'merged'; + + $MR->merge_status = $MR->mergeable ? 'can_be_merged' : 'cannot_be_merged'; + $MR->description = $MR->body; + $MR->target_branch = $MR->base_branch; + $MR->source_branch = $MR->head_branch; + $MR->source_project_id = $projectID; + $MR->target_project_id = $projectID; + return $MR; } } @@ -968,37 +1039,37 @@ class mrModel extends model /** * Accept MR by API. * - * @link https://docs.gitlab.com/ee/api/merge_requests.html#accept-mr - * @param int $hostID - * @param int $projectID - * @param int $MRID * @param object $MR * @access public * @return object */ - public function apiAcceptMR($hostID, $projectID, $MRID, $MR = null) + public function apiAcceptMR($MR) { - $host = $this->loadModel('pipeline')->getByID($hostID); + $host = $this->loadModel('pipeline')->getByID($MR->hostID); if($host->type == 'gitlab') { - $apiRoot = $this->gitlab->getApiRoot($hostID); - $approveUrl = sprintf($apiRoot, "/projects/$projectID/merge_requests/$MRID/approved"); + $apiRoot = $this->gitlab->getApiRoot($MR->hostID); + $approveUrl = sprintf($apiRoot, "/projects/$MR->targetProject/merge_requests/$MR->mriid/approved"); commonModel::http($approveUrl, null, array(CURLOPT_CUSTOMREQUEST => 'POST')); - $url = sprintf($apiRoot, "/projects/$projectID/merge_requests/$MRID/merge"); + $url = sprintf($apiRoot, "/projects/$MR->targetProject/merge_requests/$MR->mriid/merge"); return json_decode(commonModel::http($url, null, array(CURLOPT_CUSTOMREQUEST => 'PUT'))); } - elseif($host->type == 'gitea') + else { - $apiRoot = $this->loadModel('gitea')->getApiRoot($hostID); - $url = sprintf($apiRoot, "/repos/$projectID/pulls/$MRID/merge"); + $apiRoot = $this->loadModel($host->type)->getApiRoot($MR->hostID); + $url = sprintf($apiRoot, "/repos/$MR->targetProject/pulls/$MR->mriid/merge"); $merge = ($MR and $MR->squash == '1') ? 'squash' : 'merge'; $data = array('Do' => $merge); - if($MR and $MR->removeSourceBranch == '1') $data['delete_branch_after_merge'] = true; + if($MR->removeSourceBranch == '1') $data['delete_branch_after_merge'] = true; $rowMR = json_decode(commonModel::http($url, $data, array(), array(), 'json', 'POST')); - if(!isset($rowMR->massage)) $rowMR = $this->apiGetSingleMR($hostID, $projectID, $MRID); + if(!isset($rowMR->massage)) + { + $rowMR = $this->apiGetSingleMR($MR->hostID, $MR->targetProject, $MR->mriid); + if($data['delete_branch_after_merge'] == true) $this->loadModel('gogs')->apiDeleteBranch($MR->hostID, $MR->targetProject, $MR->sourceBranch); + } return $rowMR; } @@ -1155,29 +1226,6 @@ class mrModel extends model return json_decode(commonModel::http($url)); } - /** - * Get diff commits of MR from GitLab API. - * - * @param int $hostID - * @param int $projectID - * @param int $MRID - * @access public - * @return object - */ - public function apiGetDiffCommits($hostID, $projectID, $MRID) - { - $host = $this->loadModel('pipeline')->getByID($hostID); - if($host->type == 'gitlab') - { - $url = sprintf($this->gitlab->getApiRoot($hostID), "/projects/$projectID/merge_requests/$MRID/commits"); - } - else - { - $url = sprintf($this->loadModel('gitea')->getApiRoot($hostID), "/repos/$projectID/pulls/$MRID/commits"); - } - return json_decode(commonModel::http($url)); - } - /** * Get diff of MR from Gitea API. * @@ -1666,6 +1714,33 @@ class mrModel extends model ->fetchPairs(); } + /** + * Get diff commits of MR. + * + * @param object $MR + * @access public + * @return array + */ + public function getDiffCommits($MR) + { + $host = $this->loadModel('pipeline')->getByID($MR->hostID); + if($host->type == 'gogs') + { + $repo = $this->loadModel('repo')->getRepoByID($MR->repoID); + $scm = $this->app->loadClass('scm'); + $scm->setEngine($repo); + return $scm->getMRCommits($MR->sourceBranch, $MR->targetBranch); + } + else + { + $projectID = $MR->targetProject; + $MRID = $MR->mriid; + if($host->type == 'gitlab') $url = sprintf($this->loadModel('gitlab')->getApiRoot($MR->hostID), "/projects/$projectID/merge_requests/$MRID/commits"); + if($host->type == 'gitea') $url = sprintf($this->loadModel('gitea')->getApiRoot($MR->hostID), "/repos/$projectID/pulls/$MRID/commits"); + return json_decode(commonModel::http($url)); + } + } + /** * Create an mr link. * @@ -1797,21 +1872,19 @@ class mrModel extends model /** * Get links by mr commites. * - * @param int $hostID - * @param int $projectID - * @param int $MRID - * @param string $type + * @param object $MR + * @param string $type * @access public * @return array */ - public function getCommitedLink($hostID, $projectID, $MRID, $type) + public function getCommitedLink($MR, $type) { - $DiffCommits = $this->apiGetDiffCommits($hostID, $projectID, $MRID); + $diffCommits = $this->getDiffCommits($MR); $commits = array(); - foreach($DiffCommits as $DiffCommit) + foreach($diffCommits as $diffCommit) { - if(isset($DiffCommit->id)) $commits[] = substr($DiffCommit->id, 0, 10); + if(isset($diffCommit->id)) $commits[] = substr($diffCommit->id, 0, 10); } return $this->dao->select('objectID')->from(TABLE_ACTION)->where('objectType')->eq($type)->andWhere('extra')->in($commits)->fetchPairs('objectID'); @@ -1863,7 +1936,7 @@ class mrModel extends model */ public function logMergedAction($MR) { - $this->loadModel('action')->create('mr', $MR->id, 'merged'); + $this->loadModel('action')->create('mr', $MR->id, 'mergedmr'); $product = $this->getMRProduct($MR); $stories = $this->getLinkList($MR->id, $product->id, 'story'); diff --git a/module/mr/view/browse.html.php b/module/mr/view/browse.html.php index 5bf5ff0f78..a290054b81 100644 --- a/module/mr/view/browse.html.php +++ b/module/mr/view/browse.html.php @@ -91,17 +91,21 @@ createdDate;?> user->admin or (isset($projects[$MR->hostID][$MR->sourceProject]->owner->id) and $projects[$MR->hostID][$MR->sourceProject]->owner->id == $openIDList[$MR->hostID])) ? '' : 'disabled'; + $canDelete = ($app->user->admin or (isset($openIDList[$MR->hostID]) and isset($projects[$MR->hostID][$MR->sourceProject]->owner->id) and $projects[$MR->hostID][$MR->sourceProject]->owner->id == $openIDList[$MR->hostID])) ? '' : 'disabled'; if($repo->SCM == 'Gitlab') { $canEdit = (isset($projects[$MR->hostID][$MR->sourceProject]->isDeveloper) and $projects[$MR->hostID][$MR->sourceProject]->isDeveloper == true) ? '' : 'disabled'; } - else + elseif($repo->SCM == 'Gitea') { $canEdit = (isset($projects[$MR->hostID][$MR->sourceProject]->allow_merge_commits) and $projects[$MR->hostID][$MR->sourceProject]->allow_merge_commits == true) ? '' : 'disabled'; } + elseif($repo->SCM == 'Gogs') + { + $canEdit = (isset($projects[$MR->hostID][$MR->sourceProject]->permissions->push) and $projects[$MR->hostID][$MR->sourceProject]->permissions->push) ? '' : 'disabled'; + } common::printLink('mr', 'view', "MRID={$MR->id}", '', '', "title='{$lang->mr->view}' class='btn btn-info'"); - common::printIcon('mr', 'edit', "MRID={$MR->id}", $MR, 'list', '', '', '', false, "{$canEdit}"); + common::printIcon('mr', 'edit', "MRID={$MR->id}", $MR, 'list', '', '', $canEdit); common::printLink('mr', 'diff', "MRID={$MR->id}", '', '', "title='{$lang->mr->viewDiff}' class='btn btn-info'"); common::printLink('mr', 'link', "MRID={$MR->id}", '', '', "title='{$lang->mr->link}' class='btn btn-info'" . ($MR->linkButton == false ? 'disabled' : '')); common::printLink('mr', 'delete', "MRID={$MR->id}", '', 'hiddenwin', "title='{$lang->mr->delete}' class='btn btn-info {$canDelete}'"); diff --git a/module/mr/view/edit.html.php b/module/mr/view/edit.html.php index f25de0ee27..f770f763fa 100644 --- a/module/mr/view/edit.html.php +++ b/module/mr/view/edit.html.php @@ -50,7 +50,7 @@ mr->targetProject;?>
- status == 'merged' or $MR->status == 'closed'):?> + status == 'merged' or $MR->status == 'closed' or $host->type == 'gogs'):?> type == 'gitlab' ? $this->loadModel('gitlab')->apiGetSingleProject($MR->hostID, $MR->targetProject)->name_with_namespace : $MR->targetProject;?>: targetBranch;?> @@ -79,9 +79,13 @@ mr->removeSourceBranch;?> + canDeleteBranch and $MR->removeSourceBranch == '1') $attr .= 'checked '; + if(!$MR->canDeleteBranch) $attr .= 'disabled'; + ?>
- canDeleteBranch and $MR->removeSourceBranch== '1' ? 'checked' : '' ?> - canDeleteBranch) echo 'disabled';?> name="removeSourceBranch" value="1" id="removeSourceBranch"> + name="removeSourceBranch" value="1" id="removeSourceBranch">
diff --git a/module/mr/view/link.html.php b/module/mr/view/link.html.php index f9350b3730..57ffa878a5 100644 --- a/module/mr/view/link.html.php +++ b/module/mr/view/link.html.php @@ -89,7 +89,7 @@ ?> createLink('story', 'view', "storyID=$story->id"); + $viewLink = $this->createLink('story', 'view', "storyID=$story->id", '', true); $totalEstimate += $story->estimate; ?> @@ -102,7 +102,7 @@ parent > 0) echo "story->children}>{$lang->story->childrenAB}"; - echo html::a($viewLink , $story->title); + echo html::a($viewLink , $story->title, '', 'class="iframe"'); ?> openedBy);?> @@ -168,7 +168,7 @@ id);?> bug->priList, $bug->pri, $bug->pri);?> - createLink('bug', 'view', "bugID=$bug->id"), $bug->title);?> + createLink('bug', 'view', "bugID=$bug->id", '', true), $bug->title, '', 'class="iframe"');?> openedBy);?> assignedTo);?> @@ -230,7 +230,7 @@ id);?> task->priList, $task->pri, $task->pri);?> - createLink('task', 'view', "taskID=$task->id"), $task->name);?> + createLink('task', 'view', "taskID=$task->id", '', true), $task->name, '', 'class="iframe"');?> finishedBy);?> assignedTo);?> diff --git a/module/pipeline/lang/de.php b/module/pipeline/lang/de.php index c3a226f3fd..34072e6ece 100644 --- a/module/pipeline/lang/de.php +++ b/module/pipeline/lang/de.php @@ -5,3 +5,4 @@ $lang->pipeline->url = 'Server URL'; $lang->pipeline->token = 'Token'; $lang->pipeline->account = 'Username'; $lang->pipeline->password = 'Password'; +$lang->pipeline->delError = 'The server has some repos, please delete them and operate'; diff --git a/module/pipeline/lang/en.php b/module/pipeline/lang/en.php index c3a226f3fd..34072e6ece 100644 --- a/module/pipeline/lang/en.php +++ b/module/pipeline/lang/en.php @@ -5,3 +5,4 @@ $lang->pipeline->url = 'Server URL'; $lang->pipeline->token = 'Token'; $lang->pipeline->account = 'Username'; $lang->pipeline->password = 'Password'; +$lang->pipeline->delError = 'The server has some repos, please delete them and operate'; diff --git a/module/pipeline/lang/fr.php b/module/pipeline/lang/fr.php index c3a226f3fd..34072e6ece 100644 --- a/module/pipeline/lang/fr.php +++ b/module/pipeline/lang/fr.php @@ -5,3 +5,4 @@ $lang->pipeline->url = 'Server URL'; $lang->pipeline->token = 'Token'; $lang->pipeline->account = 'Username'; $lang->pipeline->password = 'Password'; +$lang->pipeline->delError = 'The server has some repos, please delete them and operate'; diff --git a/module/pipeline/lang/vi.php b/module/pipeline/lang/vi.php index c3a226f3fd..34072e6ece 100644 --- a/module/pipeline/lang/vi.php +++ b/module/pipeline/lang/vi.php @@ -5,3 +5,4 @@ $lang->pipeline->url = 'Server URL'; $lang->pipeline->token = 'Token'; $lang->pipeline->account = 'Username'; $lang->pipeline->password = 'Password'; +$lang->pipeline->delError = 'The server has some repos, please delete them and operate'; diff --git a/module/pipeline/lang/zh-cn.php b/module/pipeline/lang/zh-cn.php index bef6c98880..75d1d9a52f 100644 --- a/module/pipeline/lang/zh-cn.php +++ b/module/pipeline/lang/zh-cn.php @@ -5,3 +5,4 @@ $lang->pipeline->url = '服务器地址'; $lang->pipeline->token = 'Token'; $lang->pipeline->account = '用户名'; $lang->pipeline->password = '密码'; +$lang->pipeline->delError = '当前服务器下有代码库,请删除代码库后操作'; diff --git a/module/pipeline/model.php b/module/pipeline/model.php index 3d858314f2..b6b2e9274c 100644 --- a/module/pipeline/model.php +++ b/module/pipeline/model.php @@ -87,6 +87,9 @@ class pipelineModel extends model ->batchCheck($this->config->pipeline->create->requiredFields, 'notempty') ->batchCheck("url", 'URL') ->check('name', 'unique', "`type` = '$type'") + ->checkIF($type == 'jenkins', 'account', 'notempty') + ->checkIF($type == 'jenkins' and !$pipeline->token, 'password', 'notempty') + ->checkIF($type == 'jenkins' and !$pipeline->password, 'token', 'notempty') ->autoCheck() ->exec(); if(dao::isError()) return false; @@ -118,6 +121,9 @@ class pipelineModel extends model ->batchCheck($this->config->pipeline->edit->requiredFields, 'notempty') ->batchCheck("url", 'URL') ->check('name', 'unique', "`type` = '$type' and id <> $id") + ->checkIF($type == 'jenkins', 'account', 'notempty') + ->checkIF($type == 'jenkins' and !$pipeline->token, 'password', 'notempty') + ->checkIF($type == 'jenkins' and !$pipeline->password, 'token', 'notempty') ->autoCheck() ->where('id')->eq($id) ->exec(); @@ -131,10 +137,28 @@ class pipelineModel extends model * @param string $id the id to be deleted * @param string $object the action object * @access public - * @return int + * @return int|bool */ public function delete($id, $object = 'gitlab') { + if(in_array($object, array('gitlab', 'gitea', 'gogs'))) + { + $repo = $this->dao->select('*')->from(TABLE_REPO) + ->where('deleted')->eq('0') + ->andWhere('SCM')->eq(ucfirst($object)) + ->andWhere('serviceHost')->eq($id) + ->fetch(); + if($repo) return false; + } + elseif($object == 'sonarqube') + { + $job = $this->dao->select('id,name,repo,deleted')->from(TABLE_JOB) + ->where('frame')->eq('sonarqube') + ->andWhere('server')->eq($id) + ->andWhere('deleted')->eq('0') + ->fetch(); + if($job) return false; + } $this->dao->update(TABLE_PIPELINE)->set('deleted')->eq(1)->where('id')->eq($id)->exec(); $this->loadModel('action')->create($object, $id, 'deleted', '', ACTIONMODEL::CAN_UNDELETED); diff --git a/module/repo/config.php b/module/repo/config.php index 13855f8028..c4b6af2dc6 100644 --- a/module/repo/config.php +++ b/module/repo/config.php @@ -52,7 +52,8 @@ $config->repo->gitlab->apiPath = "%s/api/v4/projects/%s/repository/"; $config->repo->gitea = new stdclass; $config->repo->gitea->apiPath = "%s/api/v1/repos/%s/"; -$config->repo->gitServiceList = array('gitlab', 'gitea'); +$config->repo->gitServiceList = array('gitlab', 'gitea', 'gogs'); +$config->repo->gitTypeList = array('Gitlab', 'Gitea', 'Gogs', 'Git'); $config->repo->rules['module']['task'] = 'Task'; $config->repo->rules['module']['bug'] = 'Bug'; diff --git a/module/repo/control.php b/module/repo/control.php index e699afa723..f43d5d220c 100644 --- a/module/repo/control.php +++ b/module/repo/control.php @@ -46,8 +46,7 @@ class repo extends control public function commonAction($repoID = 0, $objectID = 0) { $tab = $this->app->tab; - $this->repos = $this->repo->getRepoPairs($tab, $objectID); - $this->repoGroup = $this->repo->getRepoGroup($tab, $objectID); + $this->repos = $this->repo->getRepoPairs($tab, $objectID); if($tab == 'project') { @@ -129,6 +128,7 @@ class repo extends control if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + $actionID = $this->loadModel('action')->create('repo', $repoID, 'created'); if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'id' => $repoID)); $link = $this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID", '', false) . '#app=' . $this->app->tab; return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $link)); @@ -172,6 +172,10 @@ class repo extends control $noNeedSync = $this->repo->update($repoID); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + $newRepo = $this->repo->getRepoByID($repoID); + $actionID = $this->loadModel('action')->create('repo', $repoID, 'edited'); + $changes = common::createChanges($repo, $newRepo); + $this->action->logHistory($actionID, $changes); if(!$noNeedSync) { $link = $this->repo->createLink('showSyncCommit', "repoID=$repoID"); @@ -192,6 +196,7 @@ class repo extends control { if($scm == 'gitlab') $options[$project->id] = $project->name_with_namespace; if($scm == 'gitea') $options[$project->full_name] = $project->full_name; + if($scm == 'gogs') $options[$project->full_name] = $project->full_name; } $this->view->projects = $options; @@ -239,7 +244,7 @@ class repo extends control $this->repo->delete(TABLE_REPO, $repoID); if(dao::isError()) return print(js::error(dao::getError())); - echo js::reload('parent'); + return print(js::reload('parent')); } /** @@ -299,7 +304,7 @@ class repo extends control } $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 8, 1); + $pager = new pager(0, 10, 1); $logType = 'file'; $revisions = $this->repo->getCommits($repo, '/' . $entry, 'HEAD', $logType, $pager); @@ -308,13 +313,13 @@ class repo extends control foreach($revisions as $log) { if($revision == 'HEAD' and $i == 0) $revision = $log->revision; - if($revision == $log->revision) $revisionName = strpos($repo->SCM, 'Git') !== false ? $this->repo->getGitRevisionName($log->revision, $log->commit) : $log->revision; + if($revision == $log->revision) $revisionName = in_array($repo->SCM, $this->config->repo->gitTypeList) ? $this->repo->getGitRevisionName($log->revision, $log->commit) : $log->revision; $i++; } if(!isset($revisionName)) { - if(strpos($repo->SCM, 'Git') !== false) $gitCommit = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('revision')->eq($revision)->andWhere('repo')->eq($repo->id)->fetch('commit'); - $revisionName = (strpos($repo->SCM, 'Git') !== false and isset($gitCommit)) ? $this->repo->getGitRevisionName($revision, $gitCommit) : $revision; + if(in_array($repo->SCM, $this->config->repo->gitTypeList)) $gitCommit = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('revision')->eq($revision)->andWhere('repo')->eq($repo->id)->fetch('commit'); + $revisionName = (in_array($repo->SCM, $this->config->repo->gitTypeList) and isset($gitCommit)) ? $this->repo->getGitRevisionName($revision, $gitCommit) : $revision; } $this->view->revisions = $revisions; @@ -337,6 +342,7 @@ class repo extends control $this->view->pager = $pager; $this->view->logType = $logType; $this->view->info = $info; + $this->view->pathInfo = $pathInfo; $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->view; $this->view->position[] = $this->lang->repo->common; @@ -386,7 +392,7 @@ class repo extends control /* Set branch or tag for git. */ $branches = $tags = $branchesAndTags = array(); - if(strpos($repo->SCM, 'Git') !== false) + if(in_array($repo->SCM, $this->config->repo->gitTypeList)) { $scm = $this->app->loadClass('scm'); $scm->setEngine($repo); @@ -415,7 +421,7 @@ class repo extends control /* Load pager. */ $this->app->loadClass('pager', $static = true); - $pager = new pager(0, 8, 1); + $pager = new pager(0, 10, 1); if($_POST) { @@ -436,7 +442,7 @@ class repo extends control /* Update code commit history. */ $commentGroup = $this->loadModel('job')->getTriggerGroup('commit', array($repo->id)); - if($refresh and strpos($repo->SCM, 'Git') !== false) + if($refresh and in_array($repo->SCM, $this->config->repo->gitTypeList)) { $branch = $this->cookie->repoBranch; $this->loadModel('git')->updateCommit($repo, $commentGroup, false); @@ -479,13 +485,13 @@ class repo extends control $revisions = $this->repo->getCommits($repo, $path, $revision, $logType, $pager); /* Synchronous commit only in root path. */ - if(strpos($repo->SCM, 'Git') !== false and empty($path) and $infos and empty($revisions)) $this->locate($this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID&branch=" . base64_encode($this->cookie->repoBranch))); + if(in_array($repo->SCM, $this->config->repo->gitTypeList) and empty($path) and $infos and empty($revisions)) $this->locate($this->repo->createLink('showSyncCommit', "repoID=$repoID&objectID=$objectID&branch=" . helper::safe64Encode(base64_encode($this->cookie->repoBranch)))); $this->view->title = $this->lang->repo->common; $this->view->repo = $repo; $this->view->repos = $this->repos; $this->view->revisions = $revisions; - $this->view->repoGroup = $this->repoGroup; + $this->view->repoGroup = $this->repo->getRepoGroup($this->app->tab, $objectID); $this->view->revision = $revision; $this->view->infos = $infos; $this->view->repoID = $repoID; @@ -590,7 +596,7 @@ class repo extends control $history = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('revision')->eq($log[0]->revision)->andWhere('repo')->eq($repoID)->fetch(); if($history) { - if(strpos($repo->SCM, 'Git') !== false) + if(in_array($repo->SCM, $this->config->repo->gitTypeList)) { $thisAndPrevRevisions = $this->scm->exec("rev-list -n 2 {$history->revision} --"); @@ -608,7 +614,7 @@ class repo extends control if(empty($oldRevision)) { $oldRevision = '^'; - if($history and strpos($repo->SCM, 'Git') !== false) $oldRevision = "{$history->revision}^"; + if($history and in_array($repo->SCM, $this->config->repo->gitTypeList)) $oldRevision = "{$history->revision}^"; } $changes = array(); @@ -694,7 +700,7 @@ class repo extends control if($encoding != 'utf-8') $blames[$i]['content'] = helper::convertEncoding($blame['content'], $encoding); } - $log = strpos($repo->SCM, 'Git') !== false ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->eq($revision)->andWhere('repo')->eq($repo->id)->fetch() : ''; + $log = in_array($repo->SCM, $this->config->repo->gitTypeList) ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->eq($revision)->andWhere('repo')->eq($repo->id)->fetch() : ''; $this->view->title = $this->lang->repo->common; $this->view->repoID = $repoID; @@ -705,8 +711,8 @@ class repo extends control $this->view->entry = $entry; $this->view->file = $file; $this->view->encoding = str_replace('-', '_', $encoding); - $this->view->historys = strpos($repo->SCM, 'Git') !== false ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in($revisions)->andWhere('repo')->eq($repo->id)->fetchPairs() : ''; - $this->view->revisionName = ($log and strpos($repo->SCM, 'Git') !== false) ? $this->repo->getGitRevisionName($log->revision, $log->commit) : $revision; + $this->view->historys = in_array($repo->SCM, $this->config->repo->gitTypeList) ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in($revisions)->andWhere('repo')->eq($repo->id)->fetchPairs() : ''; + $this->view->revisionName = ($log and in_array($repo->SCM, $this->config->repo->gitTypeList)) ? $this->repo->getGitRevisionName($log->revision, $log->commit) : $revision; $this->view->blames = $blames; $this->display(); } @@ -736,6 +742,12 @@ class repo extends control $repo = $this->repo->getRepoByID($repoID); $entry = $this->repo->decodePath($entry); + if($repo->SCM == 'Git' and !is_dir($repo->path)) + { + $error = sprintf($this->lang->repo->error->notFound, $repo->name, $repo->path); + return print(js::error($error) . js::locate($this->repo->createLink('maintain'))); + } + $pathInfo = pathinfo($entry); $suffix = ''; if(isset($pathInfo["extension"])) $suffix = strtolower($pathInfo["extension"]); @@ -822,7 +834,7 @@ class repo extends control $this->view->newRevision = $newRevision; $this->view->oldRevision = $oldRevision; $this->view->revision = $newRevision; - $this->view->historys = strpos($repo->SCM, 'Git') !== false ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in("$oldRevision,$newRevision")->andWhere('repo')->eq($repo->id)->fetchPairs() : ''; + $this->view->historys = in_array($repo->SCM, $this->config->repo->gitTypeList) ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in("$oldRevision,$newRevision")->andWhere('repo')->eq($repo->id)->fetchPairs() : ''; $this->view->info = $info; $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->diff; @@ -909,7 +921,7 @@ class repo extends control $this->commonAction($repoID, $objectID); if($repoID == 0) $repoID = $this->session->repoID; - if($branch) $branch = base64_decode($branch); + if($branch) $branch = base64_decode(helper::safe64Decode($branch)); $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->showSyncCommit; $this->view->position[] = $this->lang->repo->showSyncCommit; @@ -938,11 +950,33 @@ class repo extends control if(empty($repo)) return; if($repo->synced) return print('finish'); + if(in_array($repo->SCM, array('Gitea', 'Gogs'))) + { + $logFile = realPath($this->app->getTmpRoot() . "/log/clone.progress." . strtolower($repo->SCM) . ".{$repo->name}.log"); + if($logFile) + { + $content = file($logFile); + $lastLine = $content[count($content) - 1]; + if(!strpos($lastLine, 'done')) + { + return print(1); + } + elseif(strpos($lastLine, 'fatal') !== false) + { + return print('finish'); + } + else + { + @unlink($logFile); + } + } + } + $this->commonAction($repoID); $this->scm->setEngine($repo); $branchID = ''; - if(strpos($repo->SCM, 'Git') !== false and empty($branchID)) + if(in_array($repo->SCM, $this->config->repo->gitTypeList) and empty($branchID)) { $branches = $this->scm->branch(); if($branches) @@ -979,7 +1013,7 @@ class repo extends control $version = empty($latestInDB) ? 1 : $latestInDB->commit + 1; $logs = array(); - $revision = $version == 1 ? 'HEAD' : ($repo->SCM == 'Git' ? $latestInDB->commit : $latestInDB->revision); + $revision = $version == 1 ? 'HEAD' : (in_array($repo->SCM, array('Git', 'Gitea', 'Gogs')) ? $latestInDB->commit : $latestInDB->revision); if($type == 'batch') { $logs = $this->scm->getCommits($revision, $this->config->repo->batchNum, $branchID); @@ -994,7 +1028,7 @@ class repo extends control { if(!$repo->synced) { - if(strpos($repo->SCM, 'Git') !== false) + if(in_array($repo->SCM, $this->config->repo->gitTypeList)) { if($branchID) $this->repo->saveExistCommits4Branch($repo->id, $branchID); @@ -1029,8 +1063,8 @@ class repo extends control set_time_limit(0); $repo = $this->repo->getRepoByID($repoID); if(empty($repo)) return; - if(strpos($repo->SCM, 'Git') === false) return print('finish'); - if($branch) $branch = base64_decode($branch); + if(!in_array($repo->SCM, $this->config->repo->gitTypeList)) return print('finish'); + if($branch) $branch = base64_decode(helper::safe64Decode($branch)); $this->scm->setEngine($repo); @@ -1040,7 +1074,7 @@ class repo extends control $latestInDB = $this->dao->select('DISTINCT t1.*')->from(TABLE_REPOHISTORY)->alias('t1') ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') ->where('t1.repo')->eq($repoID) - ->beginIF(strpos($repo->SCM, 'Git') !== false and $this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->beginIF(in_array($repo->SCM, $this->config->repo->gitTypeList) and $this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() ->orderBy('t1.time') ->limit(1) ->fetch(); @@ -1078,7 +1112,7 @@ class repo extends control * @access public * @return void */ - public function ajaxSideCommits($repoID, $path, $objectID = 0, $type = 'dir', $recTotal = 0, $recPerPage = 8, $pageID = 1) + public function ajaxSideCommits($repoID, $path, $objectID = 0, $type = 'dir', $recTotal = 0, $recPerPage = 10, $pageID = 1) { if($this->get->repoPath) $path = $this->get->repoPath; $this->app->loadClass('pager', $static = true); @@ -1198,8 +1232,8 @@ class repo extends control /** * Ajax get gitea projects. * - * @param string $gitlabID - * @param string $projectIdList + * @param string $gitlabID + * @param string $projectIdList * @access public * @return void */ @@ -1214,11 +1248,31 @@ class repo extends control return print($options); } + /** + * Ajax get gogs projects. + * + * @param string $gitlabID + * @param string $projectIdList + * @access public + * @return void + */ + public function ajaxGetGogsProjects($gogsID) + { + $projects = $this->loadModel('gogs')->apiGetProjects($gogsID); + $options = ""; + if(!empty($projects)) + { + foreach($projects as $project) $options .= ""; + } + + return print($options); + } + /** * Ajax get gitlab projects. * - * @param string $gitlabID - * @param string $token + * @param string $gitlabID + * @param string $token * @access public * @return void */ @@ -1356,7 +1410,6 @@ class repo extends control { $repo = $this->repo->getRepoByID($repoID); $savePath = $this->app->getDataRoot() . 'repo'; - $fileName = $savePath . DS . $repo->name . '.zip'; if(!is_dir($savePath)) { if(!is_writable($this->app->getDataRoot())) return print(js::alert(sprintf($this->lang->repo->error->noWritable, dirname($savePath))) . js::close()); @@ -1364,50 +1417,9 @@ class repo extends control } $repo = $this->repo->getRepoByID($repoID); - if($repo->SCM == 'Gitlab') - { - $this->scm = $this->app->loadClass('scm'); - $this->scm->setEngine($repo); - $url = $this->scm->getDownloadUrl($branch); - } - elseif($repo->SCM == 'Gitea') - { - $api = $this->loadModel('gitea')->getApiRoot($repo->serviceHost); - $url = sprintf($api, "/repos/{$repo->serviceProject}/archive/{$branch}.zip"); - } - elseif($repo->SCM == 'Git') - { - $gitDir = scandir($repo->path); - $files = ''; - foreach($gitDir as $path) - { - if(!in_array($path, array('.', '..', '.git'))) $files .= $repo->path . DS . "$path,"; - } - - $this->app->loadClass('pclzip', true); - $zip = new pclzip($fileName); - if($zip->create($files, PCLZIP_OPT_REMOVE_PATH, $repo->path) === 0) return print(js::alert($zip->errorInfo()) . js::close()); - - $url = $this->config->webRoot . $this->app->getAppName() . 'data' . DS . 'repo' . DS . $repo->name . '.zip'; - } - else - { - /* Checkout repo. */ - chdir($savePath); - $this->scm = $this->app->loadClass('scm'); - $this->scm->setEngine($repo); - $this->scm->exec('export ' . $repo->path); - - /* Get repo name. */ - $pathList = explode('/', trim($repo->path, '/')); - $repoDir = end($pathList); - - $this->app->loadClass('pclzip', true); - $zip = new pclzip($fileName); - if($zip->create($repoDir, PCLZIP_OPT_REMOVE_PATH, $repoDir) === 0) return print(js::alert($zip->errorInfo()) . js::close()); - - $url = $this->config->webRoot . $this->app->getAppName() . 'data' . DS . 'repo' . DS . $repo->name . '.zip'; - } + $this->scm = $this->app->loadClass('scm'); + $this->scm->setEngine($repo); + $url = $this->scm->getDownloadUrl($branch, $savePath); $this->locate($url); } diff --git a/module/repo/css/blame.css b/module/repo/css/blame.css index d5e6bc17c5..f3d9b6eacb 100644 --- a/module/repo/css/blame.css +++ b/module/repo/css/blame.css @@ -9,3 +9,4 @@ pre {margin-bottom: 0px; display: block; white-space: pre-wrap; padding: 0px 3px; border: 0px; background: none;} .line {font-weight: bold; border: 1px solid #E4E4E4; background-color: #fafafa; color: #999; text-align: center;} .panel-actions.pull-right {margin-top: -5px;} +.action-onlybody {right: 30px;} diff --git a/module/repo/css/common.css b/module/repo/css/common.css index a717e4e8b4..4e8c17c3a0 100644 --- a/module/repo/css/common.css +++ b/module/repo/css/common.css @@ -64,12 +64,12 @@ h3 {font-size: 16px;} /* Pre row */ .repoCode table > tbody > tr.over > td {background: #f8eec7;} -.repoCode table > tbody > tr.over > th {background: #cdcdcd; color: #333;} +.repoCode table > tbody > tr.over > th {background: #cdcdcd !important; color: #333 !important;} /* Comment-btn */ .comment-btn {position: relative; margin: 0; padding: 0; display: none;} .repoCode tr.over .comment-btn {display: block;} -.comment-btn .icon-wrapper {display: block; position: absolute; background: #4183C4; border-radius: 2px; width: 24px; height: 20px; left: -6px; top: -2; line-height: 20px; text-align: center; color: #fff; cursor: pointer; transition: transform 0.2s;} +.comment-btn .icon-wrapper {display: block; position: absolute; background: #4183C4; border-radius: 2px; width: 24px; height: 20px; left: -6px; top: -2; line-height: 20px; text-align: center; color: #fff; cursor: pointer;} .comment-btn .icon-wrapper:before {display: block; content: ' '; right: -4px; top: 6px; position: absolute; border-left: 4px solid #4183C4; border-right: 0 solid transparent; border-bottom: 4px solid transparent; border-top: 4px solid transparent; width: 0; height: 0;} .comment-btn .icon-wrapper:hover {background: #169; transform: scale(1.1);} .comment-btn .icon-wrapper:hover:before {border-left-color: #169;} @@ -79,7 +79,7 @@ h3 {font-size: 16px;} .repoCode tr.commented .comment-btn {display: block; float:left;} .repoCode tr.commented .comment-btn .icon-wrapper {background: none; border: none; width: 24px; line-height: 18px; height: 18px; left: -6px; color: #4183c4;} .repoCode tr.commented .comment-btn .icon-wrapper:hover {border-color: #169; color: #169;} -.repoCode tr.commented .comment-btn .icon-wrapper > i:before {font-size: 18px; transform: scale(-1, 1); display: inline-block;} +.repoCode tr.commented .comment-btn .icon-wrapper > i:before {font-size: 18px; display: inline-block;} .repoCode tr.commented .comment-btn .icon-wrapper:before {display: none;} .repoCode tr.over.commented .comment-btn {margin-left: -20px; width: 25px; height:18px;} @@ -89,7 +89,9 @@ h3 {font-size: 16px;} .repoCode tr.over.commented .comment-btn .icon-wrapper:before, .repoCode tr.selected.commented .comment-btn .icon-wrapper:before {display: block;} /* repo action form */ -.repoCode .comment-list, .repoCode .comment-actions {max-width: 900px;} +.repoCode .comment-actions {max-width: 900px;} +.repoCode table {table-layout: fixed;} +.repoCode table td {white-space: pre-line;} .repoCode .bugFormContainer {border: 1px solid #bbb; margin: 0 0 0 15px; padding: 10px 20px 10px 10px; max-width: 880px; background: #fff;} .repoCode .bugFormContainer th {width: 70px;} @@ -106,17 +108,17 @@ h3 {font-size: 16px;} .repoCode .comment-cell {background: #eee; white-space: normal;} .repoCode .comment-cell .panel {margin: 10px; border-color: #bbb;} .repoCode .comment-cell .panel-body {padding: 6px 10px;} -.repoCode .comment-cell .panel-actions.pull-right {margin-right: 0; margin-top: 0;} +.repoCode .comment-cell .panel-actions.pull-right {margin-right: 0; margin-top: -25px; position: unset;} .repoCode .comment-cell .editing .panel-body, .repoCode .comment-cell .commentContainer.show-form .panel-body {display: none;} .repoCode .comment-cell .bug-edit-form, {padding: 10px; display: none;} .repoCode .comment-cell .editing .bug-edit-form, .repoCode .comment-cell .commentContainer.show-form .comment-edit-form {display: block;} -.repoCode .comment {border: 1px solid #e5e5e5; background: #fafafa; padding: 5px 10px; margin-bottom: 10px;} +.repoCode .comment {border: 1px solid #e5e5e5; background: #fafafa; padding: 5px 10px 1px; margin-bottom: 10px;} .repoCode .comment .comment-edit-form {margin-top: 10px;} .repoCode .panel-bug .steps {background: #f1f1f1; padding: 5px 10px;} .repoCode .panel-bug .bug-edit-form {margin-bottom: 10px;} .repoCode .panel-bug .panel-body {display: none;} -.repoCode .panel-bug .panel-heading {cursor: pointer;} +.repoCode .panel-bug .panel-heading {cursor: pointer; border-bottom: 1px solid #edf3ff;} .repoCode .panel-bug.show .panel-body {display: block;} .repoCode .panel-bug.show .icon-chevron-sign-down:before {content: '\e711';} .repoCode .panel-bug.show-edit-form .bug-edit-form, @@ -128,7 +130,7 @@ h3 {font-size: 16px;} .repoCode .comment .comment-edit-form, .repoCode .panel-bug.show-edit-form .panel-body .title, .repoCode .panel-bug.show-edit-form .bug-date, -.repoCode .comment.show-form .comment-content, .repoCode .comment.show-form .comment-date {display: none;} +.repoCode .comment.show-form .comment-content {display: none;} .repoCode .text-content {white-space: normal; white-space: pre-line;} .repoCode .text-muted {color: #aaa;} @@ -175,8 +177,8 @@ h3 {font-size: 16px;} .transparent {border-color:transparent; background: none repeat scroll 0 0 transparent;} .transparent:hover {border-color:transparent; background: none repeat scroll 0 0 transparent;} -.side-col {width: 600px;} -#sidebar > .side-body {width: 580px;} +.side-col {width: 550px;} +#sidebar > .side-body {width: 530px;} .hide-sidebar #sidebar > .side-body {display: none;} #sidebar>.sidebar-toggle {left: 5px; right: auto;} @@ -192,3 +194,7 @@ h3 {font-size: 16px;} .dropdown-menu li > a.selected {color: #3123ae !important; background: #f2eafc !important; line-height: 20px; border-radius: 5px;} .tips-git a {padding-bottom: 1px;} +.bug-lines {font-size: revert;} + +.comment.show-form .comment-form-div {margin-top: 20px; padding-bottom: 10px;} +.comment .avatar {margin-right: 10px;} diff --git a/module/repo/js/create.js b/module/repo/js/create.js index 296337d8e2..3c9acf4838 100644 --- a/module/repo/js/create.js +++ b/module/repo/js/create.js @@ -54,17 +54,15 @@ $(function() */ function scmChanged(scm) { - if(scm == 'Git' || scm == 'Gitea') + if(scm == 'Git' || scm == 'Gitea' || scm == 'Gogs') { $('.account-fields').addClass('hidden'); - $('.tips-git').removeClass('hidden'); $('.tips-svn').addClass('hidden'); } else { $('.account-fields').removeClass('hidden'); - $('.tips-git').addClass('hidden'); $('.tips-svn').removeClass('hidden'); } @@ -76,11 +74,12 @@ function scmChanged(scm) } else { + $('.tips').addClass('hidden'); $('tr.service').toggle(true); - if(scm == 'Gitea') + if(scm == 'Gitea' || scm == 'Gogs') { - $('tr.hide-service:not(".hide-gitea")').toggle(true); - $('tr.hide-gitea').toggle(false); + $('tr.hide-service:not(".hide-git")').toggle(true); + $('tr.hide-git').toggle(false); } else { diff --git a/module/repo/js/edit.js b/module/repo/js/edit.js index 2c5510ca06..0a37664742 100644 --- a/module/repo/js/edit.js +++ b/module/repo/js/edit.js @@ -38,7 +38,7 @@ $(function() */ function scmChanged(scm, isFirstRequest = false) { - if(scm == 'Git' || scm == 'Gitea') + if(scm == 'Git' || scm == 'Gitea' || scm == 'Gogs') { $('.account-fields').addClass('hidden'); @@ -60,11 +60,12 @@ function scmChanged(scm, isFirstRequest = false) } else { + $('.tips').addClass('hidden'); $('tr.service').toggle(true); - if(scm == 'Gitea') + if(scm == 'Gitea' || scm == 'Gogs') { - $('tr.hide-service:not(".hide-gitea")').toggle(true); - $('tr.hide-gitea').toggle(false); + $('tr.hide-service:not(".hide-git")').toggle(true); + $('tr.hide-git').toggle(false); } else { diff --git a/module/repo/js/view.js b/module/repo/js/view.js index ae6b0bea3e..59959c139c 100644 --- a/module/repo/js/view.js +++ b/module/repo/js/view.js @@ -13,7 +13,7 @@ $(document).ready(function() for(var i = 0 ; i < arr.length ; i++) { line = i + 1; - code += "
" + line + "" + (arr[i] || ' ') + rowTip + ""; + code += "0" + line + "" + (arr[i] || ' ') + rowTip + ""; } $pre.html("" + code + "
"); } diff --git a/module/repo/lang/de.php b/module/repo/lang/de.php index 54c8aad4ea..eef3df7db5 100644 --- a/module/repo/lang/de.php +++ b/module/repo/lang/de.php @@ -142,6 +142,7 @@ $lang->repo->encodingList['utf_8'] = 'UTF-8'; $lang->repo->encodingList['gbk'] = 'GBK'; $lang->repo->scmList['Gitlab'] = 'GitLab'; +$lang->repo->scmList['Gogs'] = 'Gogs'; $lang->repo->scmList['Gitea'] = 'Gitea'; $lang->repo->scmList['Git'] = 'Git'; $lang->repo->scmList['Subversion'] = 'SVN'; diff --git a/module/repo/lang/en.php b/module/repo/lang/en.php index 5d9b5bbd87..025d4901ab 100644 --- a/module/repo/lang/en.php +++ b/module/repo/lang/en.php @@ -142,6 +142,7 @@ $lang->repo->encodingList['utf_8'] = 'UTF-8'; $lang->repo->encodingList['gbk'] = 'GBK'; $lang->repo->scmList['Gitlab'] = 'GitLab'; +$lang->repo->scmList['Gogs'] = 'Gogs'; $lang->repo->scmList['Gitea'] = 'Gitea'; $lang->repo->scmList['Git'] = 'Git'; $lang->repo->scmList['Subversion'] = 'SVN'; diff --git a/module/repo/lang/fr.php b/module/repo/lang/fr.php index ff2f8c8246..28e405a979 100644 --- a/module/repo/lang/fr.php +++ b/module/repo/lang/fr.php @@ -142,6 +142,7 @@ $lang->repo->encodingList['utf_8'] = 'UTF-8'; $lang->repo->encodingList['gbk'] = 'GBK'; $lang->repo->scmList['Gitlab'] = 'GitLab'; +$lang->repo->scmList['Gogs'] = 'Gogs'; $lang->repo->scmList['Gitea'] = 'Gitea'; $lang->repo->scmList['Git'] = 'Git'; $lang->repo->scmList['Subversion'] = 'Subversion'; diff --git a/module/repo/lang/vi.php b/module/repo/lang/vi.php index 16eb83b534..aa05c51e62 100644 --- a/module/repo/lang/vi.php +++ b/module/repo/lang/vi.php @@ -142,6 +142,7 @@ $lang->repo->encodingList['utf_8'] = 'UTF-8'; $lang->repo->encodingList['gbk'] = 'GBK'; $lang->repo->scmList['Gitlab'] = 'GitLab'; +$lang->repo->scmList['Gogs'] = 'Gogs'; $lang->repo->scmList['Gitea'] = 'Gitea'; $lang->repo->scmList['Git'] = 'Git'; $lang->repo->scmList['Subversion'] = 'SVN'; diff --git a/module/repo/lang/zh-cn.php b/module/repo/lang/zh-cn.php index f94930c892..e791a70c1d 100644 --- a/module/repo/lang/zh-cn.php +++ b/module/repo/lang/zh-cn.php @@ -142,6 +142,7 @@ $lang->repo->encodingList['utf_8'] = 'UTF-8'; $lang->repo->encodingList['gbk'] = 'GBK'; $lang->repo->scmList['Gitlab'] = 'GitLab'; +$lang->repo->scmList['Gogs'] = 'Gogs'; $lang->repo->scmList['Gitea'] = 'Gitea'; $lang->repo->scmList['Git'] = '本地 Git'; $lang->repo->scmList['Subversion'] = 'Subversion'; diff --git a/module/repo/lang/zh-tw.php b/module/repo/lang/zh-tw.php index 9811b114f1..b900b9cd6d 100644 --- a/module/repo/lang/zh-tw.php +++ b/module/repo/lang/zh-tw.php @@ -135,6 +135,7 @@ $lang->repo->encodingList['utf_8'] = 'UTF-8'; $lang->repo->encodingList['gbk'] = 'GBK'; $lang->repo->scmList['Gitlab'] = 'GitLab'; +$lang->repo->scmList['Gogs'] = 'Gogs'; $lang->repo->scmList['Gitea'] = 'Gitea'; $lang->repo->scmList['Git'] = '本地 Git'; $lang->repo->scmList['Subversion'] = 'Subversion'; diff --git a/module/repo/model.php b/module/repo/model.php index 817276d1e8..c131ae26d5 100644 --- a/module/repo/model.php +++ b/module/repo/model.php @@ -58,7 +58,7 @@ class repoModel extends model } if(!in_array(strtolower($repo->SCM), $this->config->repo->gitServiceList)) unset($this->lang->devops->menu->mr); - $this->lang->switcherMenu = $this->getSwitcher($repoID); + if(count($repos) > 1) $this->lang->switcherMenu = $this->getSwitcher($repoID); } common::setMenuVars('devops', $repoID); @@ -188,18 +188,12 @@ class repoModel extends model if(!$this->checkConnection()) return false; $isPipelineServer = in_array(strtolower($this->post->SCM), $this->config->repo->gitServiceList) ? true : false; - if($isPipelineServer) - { - if($this->post->serviceHost == '') dao::$errors['serviceHost'] = sprintf($this->lang->error->notempty, $this->lang->repo->serviceHost); - if($this->post->serviceProject == '') dao::$errors['serviceProject'] = sprintf($this->lang->error->notempty, $this->lang->repo->serviceProject); - if(dao::isError()) return false; - } $data = fixer::input('post') ->setIf($isPipelineServer, 'password', $this->post->serviceToken) - ->setIf($isPipelineServer and $this->post->SCM == 'Gitlab', 'path', '') - ->setIf($isPipelineServer and $this->post->SCM == 'Gitlab', 'client', '') - ->setIf($isPipelineServer, 'extra', $this->post->serviceProject) + ->setIf($this->post->SCM == 'Gitlab', 'path', '') + ->setIf($this->post->SCM == 'Gitlab', 'client', '') + ->setIf($this->post->SCM == 'Gitlab', 'extra', $this->post->serviceProject) ->setIf($isPipelineServer, 'prefix', '') ->setIf($this->post->SCM == 'Git', 'account', '') ->setIf($this->post->SCM == 'Git', 'password', '') @@ -208,19 +202,7 @@ class repoModel extends model ->join('product', ',') ->get(); - if($isPipelineServer) - { - $repo = $this->dao->select('id')->from(TABLE_REPO) - ->where('SCM')->eq($this->post->SCM) - ->andWhere('serviceHost')->eq($data->client) - ->andWhere('serviceProject')->eq($data->path) - ->fetch(); - if(!empty($repo)) dao::$errors['serviceProject'] = sprintf($this->lang->error->unique, $this->lang->repo->serviceProject, $repo->name); - if(dao::isError()) return false; - } - $data->acl = empty($data->acl) ? '' : json_encode($data->acl); - if($data->SCM == 'Subversion') { $scm = $this->app->loadClass('scm'); @@ -234,12 +216,12 @@ class repoModel extends model if($data->encrypt == 'base64') $data->password = base64_encode($data->password); $this->dao->insert(TABLE_REPO)->data($data, $skip = 'serviceToken') ->batchCheck($this->config->repo->create->requiredFields, 'notempty') - ->checkIF($data->SCM != 'Gitlab', 'path,client', 'notempty') - ->checkIF($isPipelineServer, 'serviceHost,serviceProject', 'notempty') - ->checkIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty') - ->checkIF($data->SCM == 'Gitea', $this->config->repo->gitea->requiredFields, 'notempty') - ->checkIF($data->SCM == 'Git', 'path', 'unique', "`SCM` = 'Git'") - ->checkIF($data->SCM == 'Subversion', 'path', 'unique', "`SCM` = 'Subversion'") + ->batchCheckIF($data->SCM != 'Gitlab', 'path,client', 'notempty') + ->batchCheckIF($isPipelineServer, 'serviceHost,serviceProject', 'notempty') + ->batchCheckIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty') + ->check('name', 'unique', "`SCM` = '{$data->SCM}'") + ->checkIF($isPipelineServer, 'serviceProject', 'unique', "`SCM` = '{$data->SCM}' and `serviceHost` = '{$data->serviceHost}'") + ->checkIF(!$isPipelineServer, 'path', 'unique', "`SCM` = '{$data->SCM}' and `serviceHost` = '{$data->serviceHost}'") ->autoCheck() ->exec(); @@ -271,17 +253,12 @@ class repoModel extends model $repo = $this->getRepoByID($id); $isPipelineServer = in_array(strtolower($this->post->SCM), $this->config->repo->gitServiceList) ? true : false; - if($isPipelineServer) - { - if($this->post->serviceHost == '') dao::$errors['serviceHost'] = sprintf($this->lang->error->notempty, $this->lang->repo->serviceHost); - if($this->post->serviceProject == '') dao::$errors['serviceProject'] = sprintf($this->lang->error->notempty, $this->lang->repo->serviceProject); - } $data = fixer::input('post') ->setIf($isPipelineServer, 'password', $this->post->serviceToken) - ->setIf($isPipelineServer and $this->post->SCM == 'Gitlab', 'path', '') - ->setIf($isPipelineServer and $this->post->SCM == 'Gitlab', 'client', '') - ->setIf($isPipelineServer, 'extra', $this->post->serviceProject) + ->setIf($this->post->SCM == 'Gitlab', 'path', '') + ->setIf($this->post->SCM == 'Gitlab', 'client', '') + ->setIf($this->post->SCM == 'Gitlab', 'extra', $this->post->serviceProject) ->setDefault('prefix', $repo->prefix) ->setIf($this->post->SCM == 'Gitlab', 'prefix', '') ->setDefault('client', 'svn') @@ -308,29 +285,18 @@ class repoModel extends model $data->prefix = ''; } - if($isPipelineServer) - { - $repo = $this->dao->select('id')->from(TABLE_REPO) - ->where('SCM')->eq($this->post->SCM) - ->andWhere('serviceHost')->eq($data->client) - ->andWhere('serviceProject')->eq($data->path) - ->andWhere('id')->ne($id) - ->fetch(); - if(!empty($repo)) dao::$errors['serviceProject'] = sprintf($this->lang->error->unique, $this->lang->repo->serviceProject, $repo->name); - if(dao::isError()) return false; - } - if($data->client != $repo->client and !$this->checkClient()) return false; if(!$this->checkConnection()) return false; if($data->encrypt == 'base64') $data->password = base64_encode($data->password); $this->dao->update(TABLE_REPO)->data($data, $skip = 'serviceToken') ->batchCheck($this->config->repo->edit->requiredFields, 'notempty') - ->checkIF($data->SCM != 'Gitlab', 'path,client', 'notempty') - ->checkIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty') - ->checkIF($data->SCM == 'Gitlab', 'extra', 'notempty') - ->checkIF($data->SCM == 'Git', 'path', 'unique', "`SCM` = 'Git' and `id` <> $id") - ->checkIF($data->SCM == 'Subversion', 'path', 'unique', "`SCM` = 'Subversion' and `id` <> $id") + ->batchCheckIF($data->SCM != 'Gitlab', 'path,client', 'notempty') + ->batchCheckIF($isPipelineServer, 'serviceHost,serviceProject', 'notempty') + ->batchCheckIF($data->SCM == 'Subversion', $this->config->repo->svn->requiredFields, 'notempty') + ->check('name', 'unique', "`SCM` = '{$data->SCM}' and `id` <> $id") + ->checkIF($isPipelineServer, 'serviceProject', 'unique', "`SCM` = '{$data->SCM}' and `serviceHost` = '{$data->serviceHost}' and `id` <> $id") + ->checkIF(!$isPipelineServer, 'path', 'unique', "`SCM` = '{$data->SCM}' and `serviceHost` = '{$data->serviceHost}' and `id` <> $id") ->autoCheck() ->where('id')->eq($id)->exec(); @@ -436,17 +402,22 @@ class repoModel extends model $repo = str_replace('[gitlab]', '', $repo); $repos['Gitlab'][$id] = $repo; } - if(strpos($repo, '[gitea]') !== false) + elseif(strpos($repo, '[gogs]') !== false) + { + $repo = str_replace('[gogs]', '', $repo); + $repos['Gogs'][$id] = $repo; + } + elseif(strpos($repo, '[gitea]') !== false) { $repo = str_replace('[gitea]', '', $repo); $repos['Gitea'][$id] = $repo; } - if(strpos($repo, '[svn]') !== false) + elseif(strpos($repo, '[svn]') !== false) { $repo = str_replace('[svn]', '', $repo); $repos['SVN'][$id] = $repo; } - if(strpos($repo, '[git]') !== false) + elseif(strpos($repo, '[git]') !== false) { $repo = str_replace('[git]', '', $repo); $repos['Git'][$id] = $repo; @@ -1415,17 +1386,19 @@ class repoModel extends model return false; } } - elseif($scm == 'Gitea') + elseif(in_array($scm, array('Gitea', 'Gogs'))) { if($this->post->name != '' and $this->post->serviceProject != '') { - $project = $this->loadModel('gitea')->apiGetSingleProject($this->post->serviceHost, $this->post->serviceProject); + $module = strtolower($scm); + $project = $this->loadModel($module)->apiGetSingleProject($this->post->serviceHost, $this->post->serviceProject); if(isset($project->tokenCloneUrl)) { - $path = dirname(dirname(dirname(__FILE__))) . '/tmp/repo/' . $this->post->name . '_gitea'; + $path = $this->app->getAppRoot() . 'www/data/repo/' . $this->post->name . '_' . $module; if(!realpath($path)) { - $cmd = 'git clone --progress -v "' . $project->tokenCloneUrl . '" "' . $path . '"'; + $cmd = 'git clone --progress -v "' . $project->tokenCloneUrl . '" "' . $path . '" > "' . $this->app->getTmpRoot() . "log/clone.progress.$module.{$this->post->name}.log\" 2>&1 &"; + if(PHP_OS == 'WINNT') $cmd = "start /b $cmd"; exec($cmd); } $_POST['path'] = $path; @@ -2073,7 +2046,7 @@ class repoModel extends model $repo->password = $service ? $service->token : ''; $repo->codePath = $project ? $project->web_url : $repo->path; } - elseif($repo->SCM == 'Gitea') + elseif(in_array($repo->SCM, array('Gitea', 'Gogs'))) { $repo->codePath = $service ? "{$service->url}/{$repo->serviceProject}" : $repo->path; } @@ -2091,8 +2064,10 @@ class repoModel extends model */ public function getRepoListByClient($gitlabID, $projectID = 0) { + $server = $this->loadModel('pipeline')->getByID($gitlabID); return $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq('0') ->andWhere('synced')->eq(1) + ->beginIF($server)->andWhere('SCM')->eq(ucfirst($server->type))->fi() ->andWhere('serviceHost')->eq($gitlabID) ->beginIF($projectID)->andWhere('serviceProject')->eq($projectID)->fi() ->fetchAll(); @@ -2214,6 +2189,15 @@ class repoModel extends model $url->ssh = $project->ssh_url; } } + elseif($repo->SCM == 'Gogs') + { + $project = $this->loadModel('gogs')->apiGetSingleProject($repo->gitService, $repo->project); + if(isset($project->id)) + { + $url->http = $project->clone_url; + $url->ssh = $project->ssh_url; + } + } else { $this->scm = $this->app->loadClass('scm'); diff --git a/module/repo/view/ajaxsidecommits.html.php b/module/repo/view/ajaxsidecommits.html.php index 6321a9f742..1d038ba9dd 100644 --- a/module/repo/view/ajaxsidecommits.html.php +++ b/module/repo/view/ajaxsidecommits.html.php @@ -53,7 +53,7 @@ if(isset($entry)) $pathInfo .= '&type=file'; repo->diff, '', count($revisions) < 2 ? 'disabled btn btn-primary' : 'btn btn-primary')?> repo->createLink('log', "repoID=$repoID&objectID=$objectID&entry=" . $this->repo->encodePath($path) . "&revision=HEAD&type=$logType"), $lang->repo->allLog, '', "class='allLogs' data-app='{$this->app->tab}'");?>
-
    +
      pageID == 1 ? 1 : $pager->pageID - 1; diff --git a/module/repo/view/blame.html.php b/module/repo/view/blame.html.php index c6de16bf52..db8f06a94b 100644 --- a/module/repo/view/blame.html.php +++ b/module/repo/view/blame.html.php @@ -54,7 +54,7 @@ css::import($jsRoot . 'misc/highlight/styles/github.css');
      repo->encodePath($entry);?> -
      +
      repo->encodingList, $encoding, $lang->repo->encoding) . "", "id='encoding' data-toggle='dropdown'", 'btn dropdown-toggle')?>