supported directory download
This commit is contained in:
		| @ -665,7 +665,7 @@ class Code extends Controller | ||||
| 			if ($file === FALSE) | ||||
| 			{ | ||||
| 				$data['project'] = $project; | ||||
| 				$data['message'] = 'Failed to get file'; | ||||
| 				$data['message'] = "Failed to get a file - $path"; | ||||
| 				$this->load->view ($this->VIEW_ERROR, $data); | ||||
| 			} | ||||
| 			else if ($file['type'] == 'file') | ||||
| @ -682,33 +682,49 @@ class Code extends Controller | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				$data['project'] = $project; | ||||
| 				$data['headpath'] = $path; | ||||
| 				$data['file'] = $file; | ||||
| 				$forced_name = $projectid . $file['fullpath']; | ||||
| 				$forced_name = str_replace ('/', '-', $forced_name); | ||||
| 				//$tag = $this->subversion->getRevProp ( | ||||
| 					//	$projectid, $file['created_rev'], CODEPOT_SVN_TAG_PROPERTY); | ||||
| 				//if ($tag === FALSE) $tag = ''; | ||||
| 				//if (!empty($tag))  | ||||
| 				//{ | ||||
| 				//	$forced_name = $forced_name . '-' . $tag; | ||||
| 				//} | ||||
| 				//else | ||||
| 				//{ | ||||
| 					$forced_name = $forced_name . '-r' . $file['created_rev']; | ||||
| 				//} | ||||
|  | ||||
| 				$data['revision'] = $rev; | ||||
| 				$data['prev_revision'] = | ||||
| 					$this->subversion->getPrevRev ($projectid, $path, $rev); | ||||
| 				$data['next_revision'] = | ||||
| 					$this->subversion->getNextRev ($projectid, $path, $rev); | ||||
|  | ||||
| 				$data['readme_text'] = ''; | ||||
| 				$data['readme_file'] = ''; | ||||
| 				foreach (explode(',', CODEPOT_CODE_FOLDER_README) as $rf) | ||||
| 				$filename = $this->subversion->zipSubdir ($projectid, $path, $rev, $forced_name); | ||||
| 				if ($filename === FALSE) | ||||
| 				{ | ||||
| 					$rf = trim($rf); | ||||
| 					if (strlen($rf) > 0) | ||||
| 					{ | ||||
| 						$readme = $this->subversion->getFile ($projectid, $path . '/' . $rf, $rev); | ||||
| 						if ($readme !== FALSE) | ||||
| 						{ | ||||
| 							$data['readme_text'] = $readme['content']; | ||||
| 							$data['readme_file'] = $rf; | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					$data['project'] = $project; | ||||
| 					$data['message'] = "Failed to zip a directory for $path"; | ||||
| 					$this->load->view ($this->VIEW_ERROR, $data); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					$dir_name = $filename . '.d'; | ||||
| 					$zip_name = $filename . '.zip'; | ||||
|  | ||||
| 					$forced_zip_name = $forced_name . '.zip'; | ||||
|  | ||||
| 					header ('Content-Description: File Transfer'); | ||||
| 					header ('Content-Type: application/zip'); | ||||
| 					header ('Content-Disposition: attachment; filename='. $forced_zip_name); | ||||
| 					header ('Content-Transfer-Encoding: binary'); | ||||
| 					header ('Content-Length: ' . filesize($zip_name)); | ||||
| 					flush (); | ||||
|  | ||||
| 					@readfile ($zip_name); | ||||
| 					// meaningless to show the error page after headers | ||||
| 					// have been sent event if readfile fails. | ||||
|  | ||||
| 					codepot_delete_files ($dir_name, TRUE); | ||||
| 					@unlink ($zip_name); | ||||
| 					@unlink ($filename); | ||||
| 				} | ||||
| 				$this->load->view ($this->VIEW_FOLDER, $data); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @ -737,7 +753,7 @@ class Code extends Controller | ||||
| 			if ($file === FALSE) | ||||
| 			{ | ||||
| 				$data['project'] = $project; | ||||
| 				$data['message'] = "Failed to get file - %path"; | ||||
| 				$data['message'] = "Failed to get file - $path"; | ||||
| 				$this->load->view ($this->VIEW_ERROR, $data); | ||||
| 			} | ||||
|  | ||||
| @ -983,7 +999,6 @@ class Code extends Controller | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 	 | ||||
| 				ksort ($stats); | ||||
| 				$stats_count = count($stats); | ||||
| 				$idx = 1; | ||||
| @ -1001,7 +1016,6 @@ class Code extends Controller | ||||
| 						$max_month = substr($k, 5, 2); | ||||
| 					} | ||||
|  | ||||
|  | ||||
| 					$idx++;	 | ||||
| 					$total_commits += $v; | ||||
| 				} | ||||
|  | ||||
| @ -143,6 +143,82 @@ if ( !function_exists ('codepot_delete_files')) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| if ( !function_exists ('codepot_zip_dir')) | ||||
| { | ||||
| 	// $output_file: zip file to create | ||||
| 	// $path: directory to zip recursively | ||||
| 	// $local_path: the leading $path part is translated to $local_path | ||||
| 	// $exclude: file names to exclude. string or array of strings | ||||
| 	function codepot_zip_dir ($output_file, $path, $local_path = NULL, $exclude = NULL) | ||||
| 	{ | ||||
| 		$stack = array (); | ||||
|  | ||||
| 		if (!is_dir($path)) return FALSE; | ||||
|  | ||||
| 		array_push ($stack, $path);  | ||||
| 		$prefix = strlen($path); | ||||
|  | ||||
| 		$zip = new ZipArchive(); | ||||
| 		if (@$zip->open ($output_file, ZipArchive::OVERWRITE) === FALSE) return FALSE; | ||||
|  | ||||
| 		while (!empty($stack)) | ||||
| 		{ | ||||
| 			$dir = array_pop($stack); | ||||
|  | ||||
| 			$d = @opendir ($dir); | ||||
| 			if ($d === FALSE) continue; | ||||
|  | ||||
| 			$new_path = empty($local_path)? $dir: substr_replace($dir, $local_path, 0, $prefix); | ||||
| 			if (@$zip->addEmptyDir($new_path) == FALSE) | ||||
| 			{ | ||||
| 				@closedir ($dir); | ||||
| 				$zip->close (); | ||||
| 				return FALSE; | ||||
| 			} | ||||
|  | ||||
| 			//printf (">> [%s] [%s]\n", $dir, $new_path); | ||||
| 			while (($f = @readdir($d)) !== FALSE) | ||||
| 			{ | ||||
| 				if ($f == '.' || $f == '..') continue; | ||||
| 				if (!empty($exclude)) | ||||
| 				{ | ||||
| 					$found = FALSE; | ||||
| 					if (is_array($exclude)) | ||||
| 					{ | ||||
| 						foreach ($exclude as $ex) | ||||
| 						{ | ||||
| 							if (fnmatch ($ex, $f, FNM_PERIOD | FNM_PATHNAME))  | ||||
| 							{ | ||||
| 								$found = TRUE; | ||||
| 								break; | ||||
| 							} | ||||
| 						} | ||||
| 						if ($found) continue; | ||||
| 					} | ||||
| 					else if (fnmatch($exclude, $f, FNM_PERIOD | FNM_PATHNAME)) continue; | ||||
| 				} | ||||
|  | ||||
| 				$full_path = $dir . DIRECTORY_SEPARATOR . $f; | ||||
| 				if (is_dir($full_path)) | ||||
| 				{ | ||||
| 					array_push ($stack, $full_path); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					$new_path = empty($local_path)? $dir: substr_replace($full_path, $local_path, 0, $prefix); | ||||
| 					@$zip->addFile ($full_path, $new_path); | ||||
| 					//printf ("[%s] [%s]\n", $full_path, $new_path); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			@closedir ($dir); | ||||
| 		} | ||||
|  | ||||
| 		$zip->close (); | ||||
| 		return TRUE; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| if ( !function_exists ('codepot_find_longest_matching_sequence')) | ||||
| { | ||||
| 	function codepot_find_longest_matching_sequence ($old, $old_start, $old_len, $new, $new_start, $new_len) | ||||
|  | ||||
| @ -921,25 +921,25 @@ class SubversionModel extends Model | ||||
| 		$cloc = @popen ($cloc_cmd, 'r'); | ||||
| 		if ($cloc === FALSE) | ||||
| 		{ | ||||
| 				codepot_delete_files ($actual_tfname, TRUE); | ||||
| 				@unlink ($tfname); | ||||
| 				return FALSE; | ||||
| 			codepot_delete_files ($actual_tfname, TRUE); | ||||
| 			@unlink ($tfname); | ||||
| 			return FALSE; | ||||
| 		} | ||||
|  | ||||
| 		$line_count = 0; | ||||
| 		$cloc_data = array (); | ||||
| 		while (!feof($cloc)) | ||||
| 		{ | ||||
| 				$line = @fgets ($cloc); | ||||
| 				if ($line === FALSE) break; | ||||
| 			$line = @fgets ($cloc); | ||||
| 			if ($line === FALSE) break; | ||||
|  | ||||
| 				$line_count++; | ||||
| 				$line = trim($line); | ||||
| 				if ($line_count >= 3) | ||||
| 				{ | ||||
| 					$counter = explode (':', $line); | ||||
| 					$cloc_data[$counter[1]] = array ($counter[0], $counter[2], $counter[3], $counter[4]); | ||||
| 				} | ||||
| 			$line_count++; | ||||
| 			$line = trim($line); | ||||
| 			if ($line_count >= 3) | ||||
| 			{ | ||||
| 				$counter = explode (':', $line); | ||||
| 				$cloc_data[$counter[1]] = array ($counter[0], $counter[2], $counter[3], $counter[4]); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		@pclose ($cloc); | ||||
| @ -999,6 +999,61 @@ class SubversionModel extends Model | ||||
|  | ||||
| 		return $cloc_data; | ||||
| 	} | ||||
|  | ||||
| 	function zipSubdir ($projectid, $path, $rev, $topdir) | ||||
| 	{ | ||||
| 		$orgurl = 'file://'.$this->_canonical_path(CODEPOT_SVNREPO_DIR."/{$projectid}/{$path}"); | ||||
|  | ||||
| 		$workurl = ($path == '')? $orgurl: "{$orgurl}@"; // trailing @ for collision prevention | ||||
| 		$info = @svn_info ($workurl, FALSE, $rev); | ||||
| 		if ($info === FALSE || count($info) != 1)  | ||||
| 		{ | ||||
| 			// If a URL is at the root of repository and the url type is file:// | ||||
| 			// (e.g. file:///svnrepo/codepot/ where /svnrepo/codepot is a project root), | ||||
| 			// some versions of libsvn end up with an assertion failure like | ||||
| 			//  ... libvvn_subr/path.c:114: svn_path_join: Assertion `svn_path_is_canonical(base, pool)' failed.  | ||||
| 			//  | ||||
| 			// Since the root directory is guaranteed to exist at the head revision, | ||||
| 			// the information can be acquired without using a peg revision. | ||||
| 			// In this case, a normal operational revision is used to work around | ||||
| 			// the assertion failure. Other functions that has to deal with  | ||||
| 			// the root directory should implement this check to work around it. | ||||
| 			// | ||||
| 			if ($rev == SVN_REVISION_HEAD || $path == '') return FALSE; | ||||
|  | ||||
| 			// rebuild the URL with a peg revision and retry it. | ||||
| 			$workurl = "{$orgurl}@{$rev}"; | ||||
| 			$info = @svn_info ($workurl, FALSE, $rev); | ||||
| 			if ($info === FALSE || count($info) != 1)  return FALSE; | ||||
| 		} | ||||
|  | ||||
| 		// pass __FILE__ as the first argument so that tempnam creates a name | ||||
| 		// in the system directory. __FILE__ can never be a valid directory. | ||||
| 		$tfname = @tempnam(__FILE__, 'codepot-fetch-folder-'); | ||||
| 		if ($tfname === FALSE) return FALSE; | ||||
|  | ||||
| 		$actual_tfname = $tfname . '.d'; | ||||
| 		codepot_delete_files ($actual_tfname, TRUE); // delete the directory in case it exists | ||||
|  | ||||
| 		if (@svn_checkout ($workurl, $actual_tfname, $rev, 0) === FALSE) | ||||
| 		{ | ||||
| 			codepot_delete_files ($actual_tfname, TRUE); | ||||
| 			@unlink ($tfname); | ||||
| 			return FALSE; | ||||
| 		} | ||||
|  | ||||
| 		//exec ("zip {$tfname}.zip -r {$actual_tfname}"); | ||||
| 		if (codepot_zip_dir ("{$tfname}.zip", $actual_tfname, $topdir, array('.svn')) === FALSE)  | ||||
| 		{ | ||||
| 			codepot_delete_files ($actual_tfname, TRUE); | ||||
| 			@unlink ($tfname); | ||||
| 			@unlink ("{$tfname}.zip"); // delete potentially residual zip file  | ||||
| 			return FALSE; | ||||
| 		} | ||||
|  | ||||
| 		//codepot_delete_files ($actual_tfname, TRUE); // delete the directory in case it exists | ||||
| 		return $tfname; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
|  | ||||
| @ -350,6 +350,11 @@ $this->load->view ( | ||||
| 		else | ||||
| 			print anchor ("code/history/{$project->id}/{$xpar}", $this->lang->line('History')); | ||||
|  | ||||
| 		print ' | '; | ||||
| 		print anchor ( | ||||
| 			"code/fetch/{$project->id}/${xpar}{$revreq}", | ||||
| 			$this->lang->line('Download')); | ||||
| 		 | ||||
| 		print '</div>'; | ||||
|  | ||||
| 		usort ($file['content'], 'comp_files'); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user