package app import util._ import util.Directory._ import service._ import jp.sf.amateras.scalatra.forms._ import org.eclipse.jgit.api.Git import org.apache.commons.io.FileUtils import org.eclipse.jgit.treewalk.TreeWalk import org.eclipse.jgit.revwalk.RevWalk import scala.collection.mutable.ListBuffer import org.eclipse.jgit.lib.FileMode import java.util.regex.Pattern class IndexController extends IndexControllerBase with RepositoryService with AccountService with SystemSettingsService with ActivityService with ReferrerAuthenticator trait IndexControllerBase extends ControllerBase { self: RepositoryService with SystemSettingsService with ActivityService with ReferrerAuthenticator => val searchForm = mapping( "query" -> trim(text(required)), // TODO optional? "owner" -> trim(text(required)), "repository" -> trim(text(required)) )(SearchForm.apply) case class SearchForm(query: String, owner: String, repository: String) get("/"){ val loginAccount = context.loginAccount html.index(getRecentActivities(), getAccessibleRepositories(loginAccount, baseUrl), loadSystemSettings(), loginAccount.map{ account => getRepositoryNamesOfUser(account.userName) }.getOrElse(Nil) ) } post("/search", searchForm){ form => redirect(s"${form.owner}/${form.repository}/search?q=${StringUtil.urlEncode(form.query)}") } // TODO readable only get("/:owner/:repository/search")(referrersOnly { repository => val owner = params("owner") val name = params("repository") val query = params("q") val target = params.getOrElse("type", "Code") target.toLowerCase match { case "issue" => { // TODO search issue } case _ => { JGitUtil.withGit(getRepositoryDir(owner, name)){ git => val revWalk = new RevWalk(git.getRepository) val objectId = git.getRepository.resolve("HEAD") val revCommit = revWalk.parseCommit(objectId) val treeWalk = new TreeWalk(git.getRepository) treeWalk.setRecursive(true) treeWalk.addTree(revCommit.getTree) val lowerQueries = query.toLowerCase.split("[ \\t ]+") val list = new ListBuffer[(String, String)] while (treeWalk.next()) { if(treeWalk.getFileMode(0) != FileMode.TREE){ JGitUtil.getContent(git, treeWalk.getObjectId(0), false).foreach { bytes => if(FileUtil.isText(bytes)){ val text = new String(bytes, "UTF-8") val lowerText = text.toLowerCase val indices = lowerQueries.map { lowerQuery => lowerText.indexOf(lowerQuery) } if(!indices.exists(_ < 0)){ val lineNumber = text.substring(0, indices.min).split("\n").size - 1 val highlightText = StringUtil.escapeHtml(text.split("\n").drop(lineNumber).take(5).mkString("\n")) .replaceAll("(?i)(" + lowerQueries.map("\\Q" + _ + "\\E").mkString("|") + ")", "<span style=\"background-color: yellow;\">$1</span>") list.append((treeWalk.getPathString, highlightText)) } } } } } treeWalk.release revWalk.release val commits = JGitUtil.getLatestCommitFromPaths(git, list.toList.map(_._1), "HEAD") search.html.code(list.toList.map { case (path, highlightText) => FileSearchResult(path, commits(path).getCommitterIdent.getWhen, highlightText) }, query, repository) } } } }) private def searchDirectory(query: String, dir: java.io.File, matched: List[java.io.File] = Nil): List[java.io.File] = { dir.listFiles.toList.flatMap { case file if(file.isDirectory && file.getName != ".git") => searchDirectory(query, file, matched) case file if(file.isFile && FileUtils.readFileToString(file).contains(query)) => matched :+ file case _ => matched } } } case class FileSearchResult(path: String, lastModified: java.util.Date, highlightText: String)