From fd8cea4761b5382f23d06ed52608d1f556c4dbe5 Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Thu, 22 May 2014 17:31:30 -0400
Subject: [PATCH] Render GFM links using Markdown, not direct HTML

---
 src/main/java/com/gitblit/manager/RepositoryManager.java |  155 +++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 137 insertions(+), 18 deletions(-)

diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java
index 438d885..c141f08 100644
--- a/src/main/java/com/gitblit/manager/RepositoryManager.java
+++ b/src/main/java/com/gitblit/manager/RepositoryManager.java
@@ -157,6 +157,8 @@
 		configureJGit();
 		configureCommitCache();
 
+		confirmWriteAccess();
+
 		return this;
 	}
 
@@ -167,6 +169,7 @@
 		gcExecutor.close();
 		mirrorExecutor.close();
 
+		closeAll();
 		return this;
 	}
 
@@ -418,8 +421,9 @@
 
 			// update the fork origin repository with this repository clone
 			if (!StringUtils.isEmpty(model.originRepository)) {
-				if (repositoryListCache.containsKey(model.originRepository)) {
-					RepositoryModel origin = repositoryListCache.get(model.originRepository);
+				String originKey = model.originRepository.toLowerCase();
+				if (repositoryListCache.containsKey(originKey)) {
+					RepositoryModel origin = repositoryListCache.get(originKey);
 					origin.addFork(model.name);
 				}
 			}
@@ -447,6 +451,19 @@
 	private void clearRepositoryMetadataCache(String repositoryName) {
 		repositorySizeCache.remove(repositoryName);
 		repositoryMetricsCache.remove(repositoryName);
+		CommitCache.instance().clear(repositoryName);
+	}
+
+	/**
+	 * Reset all caches for this repository.
+	 *
+	 * @param repositoryName
+	 * @since 1.5.1
+	 */
+	@Override
+	public void resetRepositoryCache(String repositoryName) {
+		removeFromCachedRepositoryList(repositoryName);
+		clearRepositoryMetadataCache(repositoryName);
 	}
 
 	/**
@@ -457,6 +474,9 @@
 	public void resetRepositoryListCache() {
 		logger.info("Repository cache manually reset");
 		repositoryListCache.clear();
+		repositorySizeCache.clear();
+		repositoryMetricsCache.clear();
+		CommitCache.instance().clear();
 	}
 
 	/**
@@ -527,8 +547,9 @@
 				// rebuild fork networks
 				for (RepositoryModel model : repositoryListCache.values()) {
 					if (!StringUtils.isEmpty(model.originRepository)) {
-						if (repositoryListCache.containsKey(model.originRepository)) {
-							RepositoryModel origin = repositoryListCache.get(model.originRepository);
+						String originKey = model.originRepository.toLowerCase();
+						if (repositoryListCache.containsKey(originKey)) {
+							RepositoryModel origin = repositoryListCache.get(originKey);
 							origin.addFork(model.name);
 						}
 					}
@@ -778,10 +799,11 @@
 		model.projectPath = StringUtils.getFirstPathElement(repositoryName);
 
 		StoredConfig config = r.getConfig();
-		boolean hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));
+		boolean hasOrigin = false;
 
 		if (config != null) {
 			// Initialize description from description file
+			hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));
 			if (getConfig(config,"description", null) == null) {
 				File descFile = new File(r.getDirectory(), "description");
 				if (descFile.exists()) {
@@ -794,6 +816,10 @@
 			model.description = getConfig(config, "description", "");
 			model.originRepository = getConfig(config, "originRepository", null);
 			model.addOwners(ArrayUtils.fromString(getConfig(config, "owner", "")));
+			model.acceptNewPatchsets = getConfig(config, "acceptNewPatchsets", true);
+			model.acceptNewTickets = getConfig(config, "acceptNewTickets", true);
+			model.requireApproval = getConfig(config, "requireApproval", settings.getBoolean(Keys.tickets.requireApproval, false));
+			model.mergeTo = getConfig(config, "mergeTo", null);
 			model.useIncrementalPushTags = getConfig(config, "useIncrementalPushTags", false);
 			model.incrementalPushTagPrefix = getConfig(config, "incrementalPushTagPrefix", null);
 			model.allowForks = getConfig(config, "allowForks", true);
@@ -844,6 +870,9 @@
 			}
 		}
 		model.HEAD = JGitUtils.getHEADRef(r);
+		if (StringUtils.isEmpty(model.mergeTo)) {
+			model.mergeTo = model.HEAD;
+		}
 		model.availableRefs = JGitUtils.getAvailableHeadTargets(r);
 		model.sparkleshareId = JGitUtils.getSparkleshareId(r);
 		model.hasCommits = JGitUtils.hasCommits(r);
@@ -928,26 +957,31 @@
 	 */
 	@Override
 	public String getFork(String username, String origin) {
+		if (StringUtils.isEmpty(origin)) {
+			return null;
+		}
 		String userProject = ModelUtils.getPersonalPath(username);
 		if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {
+			String originKey = origin.toLowerCase();
 			String userPath = userProject + "/";
 
 			// collect all origin nodes in fork network
 			Set<String> roots = new HashSet<String>();
-			roots.add(origin);
-			RepositoryModel originModel = repositoryListCache.get(origin);
+			roots.add(originKey);
+			RepositoryModel originModel = repositoryListCache.get(originKey);
 			while (originModel != null) {
 				if (!ArrayUtils.isEmpty(originModel.forks)) {
 					for (String fork : originModel.forks) {
 						if (!fork.startsWith(userPath)) {
-							roots.add(fork);
+							roots.add(fork.toLowerCase());
 						}
 					}
 				}
 
 				if (originModel.originRepository != null) {
-					roots.add(originModel.originRepository);
-					originModel = repositoryListCache.get(originModel.originRepository);
+					String ooKey = originModel.originRepository.toLowerCase();
+					roots.add(ooKey);
+					originModel = repositoryListCache.get(ooKey);
 				} else {
 					// break
 					originModel = null;
@@ -958,7 +992,7 @@
 				if (repository.startsWith(userPath)) {
 					RepositoryModel model = repositoryListCache.get(repository);
 					if (!StringUtils.isEmpty(model.originRepository)) {
-						if (roots.contains(model.originRepository)) {
+						if (roots.contains(model.originRepository.toLowerCase())) {
 							// user has a fork in this graph
 							return model.name;
 						}
@@ -975,7 +1009,7 @@
 					settings.getStrings(Keys.git.searchExclusions));
 			for (String repository : repositories) {
 				RepositoryModel model = getRepositoryModel(userProject + "/" + repository);
-				if (model.originRepository.equalsIgnoreCase(origin)) {
+				if (model.originRepository != null && model.originRepository.equalsIgnoreCase(origin)) {
 					// user has a fork
 					return model.name;
 				}
@@ -998,7 +1032,7 @@
 			// find the root, cached
 			RepositoryModel model = repositoryListCache.get(repository.toLowerCase());
 			while (model.originRepository != null) {
-				model = repositoryListCache.get(model.originRepository);
+				model = repositoryListCache.get(model.originRepository.toLowerCase());
 			}
 			ForkModel root = getForkModelFromCache(model.name);
 			return root;
@@ -1078,12 +1112,49 @@
 	}
 
 	/**
+	 * Returns true if the repository is idle (not being accessed).
+	 *
+	 * @param repository
+	 * @return true if the repository is idle
+	 */
+	@Override
+	public boolean isIdle(Repository repository) {
+		try {
+			// Read the use count.
+			// An idle use count is 2:
+			// +1 for being in the cache
+			// +1 for the repository parameter in this method
+			Field useCnt = Repository.class.getDeclaredField("useCnt");
+			useCnt.setAccessible(true);
+			int useCount = ((AtomicInteger) useCnt.get(repository)).get();
+			return useCount == 2;
+		} catch (Exception e) {
+			logger.warn(MessageFormat
+					.format("Failed to reflectively determine use count for repository {0}",
+							repository.getDirectory().getPath()), e);
+		}
+		return false;
+	}
+
+	/**
+	 * Ensures that all cached repository are completely closed and their resources
+	 * are properly released.
+	 */
+	@Override
+	public void closeAll() {
+		for (String repository : getRepositoryList()) {
+			close(repository);
+		}
+	}
+
+	/**
 	 * Ensure that a cached repository is completely closed and its resources
 	 * are properly released.
 	 *
 	 * @param repositoryName
 	 */
-	private void closeRepository(String repositoryName) {
+	@Override
+	public void close(String repositoryName) {
 		Repository repository = getRepository(repositoryName);
 		if (repository == null) {
 			return;
@@ -1108,7 +1179,7 @@
 							repositoryName), e);
 		}
 		if (uses > 0) {
-			logger.info(MessageFormat
+			logger.debug(MessageFormat
 					.format("{0}.useCnt={1}, calling close() {2} time(s) to close object and ref databases",
 							repositoryName, uses, uses));
 			for (int i = 0; i < uses; i++) {
@@ -1246,7 +1317,7 @@
 							"Failed to rename ''{0}'' because ''{1}'' already exists.",
 							repositoryName, repository.name));
 				}
-				closeRepository(repositoryName);
+				close(repositoryName);
 				File folder = new File(repositoriesFolder, repositoryName);
 				File destFolder = new File(repositoriesFolder, repository.name);
 				if (destFolder.exists()) {
@@ -1292,7 +1363,7 @@
 
 				// update this repository's origin's fork list
 				if (!StringUtils.isEmpty(repository.originRepository)) {
-					RepositoryModel origin = repositoryListCache.get(repository.originRepository);
+					RepositoryModel origin = repositoryListCache.get(repository.originRepository.toLowerCase());
 					if (origin != null && !ArrayUtils.isEmpty(origin.forks)) {
 						origin.forks.remove(repositoryName);
 						origin.forks.add(repository.name);
@@ -1362,6 +1433,18 @@
 		config.setString(Constants.CONFIG_GITBLIT, null, "description", repository.description);
 		config.setString(Constants.CONFIG_GITBLIT, null, "originRepository", repository.originRepository);
 		config.setString(Constants.CONFIG_GITBLIT, null, "owner", ArrayUtils.toString(repository.owners));
+		config.setBoolean(Constants.CONFIG_GITBLIT, null, "acceptNewPatchsets", repository.acceptNewPatchsets);
+		config.setBoolean(Constants.CONFIG_GITBLIT, null, "acceptNewTickets", repository.acceptNewTickets);
+		if (settings.getBoolean(Keys.tickets.requireApproval, false) == repository.requireApproval) {
+			// use default
+			config.unset(Constants.CONFIG_GITBLIT, null, "requireApproval");
+		} else {
+			// override default
+			config.setBoolean(Constants.CONFIG_GITBLIT, null, "requireApproval", repository.requireApproval);
+		}
+		if (!StringUtils.isEmpty(repository.mergeTo)) {
+			config.setString(Constants.CONFIG_GITBLIT, null, "mergeTo", repository.mergeTo);
+		}
 		config.setBoolean(Constants.CONFIG_GITBLIT, null, "useIncrementalPushTags", repository.useIncrementalPushTags);
 		if (StringUtils.isEmpty(repository.incrementalPushTagPrefix) ||
 				repository.incrementalPushTagPrefix.equals(settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r"))) {
@@ -1450,6 +1533,17 @@
 	}
 
 	/**
+	 * Returns true if the repository can be deleted.
+	 *
+	 * @return true if the repository can be deleted
+	 */
+	@Override
+	public boolean canDelete(RepositoryModel repository) {
+		return settings.getBoolean(Keys.web.allowDeletingNonEmptyRepositories, true)
+					|| !repository.hasCommits;
+	}
+
+	/**
 	 * Deletes the repository from the file system and removes the repository
 	 * permission from all repository users.
 	 *
@@ -1470,8 +1564,14 @@
 	 */
 	@Override
 	public boolean deleteRepository(String repositoryName) {
+		RepositoryModel repository = getRepositoryModel(repositoryName);
+		if (!canDelete(repository)) {
+			logger.warn("Attempt to delete {} rejected!", repositoryName);
+			return false;
+		}
+
 		try {
-			closeRepository(repositoryName);
+			close(repositoryName);
 			// clear the repository cache
 			clearRepositoryMetadataCache(repositoryName);
 
@@ -1754,4 +1854,23 @@
 					daysToCache, commitCount, repoCount, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
 		}
 	}
+
+	protected void confirmWriteAccess() {
+		if (runtimeManager.isServingRepositories()) {
+			try {
+				if (!getRepositoriesFolder().exists()) {
+					getRepositoriesFolder().mkdirs();
+				}
+				File file = File.createTempFile(".test-", ".txt", getRepositoriesFolder());
+				file.delete();
+			} catch (Exception e) {
+				logger.error("");
+				logger.error(Constants.BORDER2);
+				logger.error("Please check filesystem permissions!");
+				logger.error("FAILED TO WRITE TO REPOSITORIES FOLDER!!", e);
+				logger.error(Constants.BORDER2);
+				logger.error("");
+			}
+		}
+	}
 }

--
Gitblit v1.9.1