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.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

class IndexController extends IndexControllerBase 
  with RepositoryService with AccountService with SystemSettingsService with ActivityService with IssuesService
  with ReferrerAuthenticator

trait IndexControllerBase extends ControllerBase { self: RepositoryService 
  with SystemSettingsService with ActivityService with IssuesService
  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 query  = params("q")
    val target = params.getOrElse("type", "Code")

    target.toLowerCase match {
      case "issue" => {
        // TODO search issue
        val lowerQueries = query.toLowerCase.split("[ \\t ]+")

        search.html.issues(queryIssues(repository.owner, repository.name, query).map { case (issue, commentCount, content) =>
          val lowerText = content.toLowerCase
          val indices = lowerQueries.map { lowerQuery =>
            lowerText.indexOf(lowerQuery)
          }
          val highlightText = if(!indices.exists(_ < 0)){
            val lineNumber = content.substring(0, indices.min).split("\n").size - 1
            StringUtil.escapeHtml(content.split("\n").drop(lineNumber).take(5).mkString("\n"))
              .replaceAll("(?i)(" + lowerQueries.map("\\Q" + _ + "\\E").mkString("|") +  ")",
              "<span style=\"background-color: yellow;\">$1</span>")
          } else content.split("\n").take(5).mkString("\n")

          IssueSearchResult(issue.issueId, issue.title, issue.openedUserName, issue.registeredDate, commentCount, highlightText)
        }, query, repository)

      }
      case _ => {
        JGitUtil.withGit(getRepositoryDir(repository.owner, repository.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 IssueSearchResult(issueId: Int, title: String, openedUserName: String, registeredDate: java.util.Date,
                             commentCount: Int, highlightText: String)

case class FileSearchResult(path: String, lastModified: java.util.Date, highlightText: String)