Newer
Older
gitbucket_jkp / src / main / scala / app / IndexController.scala
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)