320 lines
11 KiB
Plaintext
320 lines
11 KiB
Plaintext
package es.amuz.tacking.service.route
|
|
|
|
import java.net.URLDecoder
|
|
import java.text.Normalizer
|
|
import java.util.UUID
|
|
|
|
import es.amuz.tacking.frame.auth.{SecurityContext, SecurityInfo}
|
|
import es.amuz.tacking.frame.jade4j.JadeContext
|
|
import es.amuz.tacking.frame.pegdown.PegDownProcessorExt
|
|
import es.amuz.tacking.frame.scalikejdbc.JDBCContext
|
|
import es.amuz.tacking.model.structured_page.{PageStatus, StructuredPageAction, StructuredPages}
|
|
import es.amuz.tacking.protocols._
|
|
import es.amuz.tacking.util.FileAndPath
|
|
import org.joda.time.DateTime
|
|
import org.pegdown.Extensions
|
|
import scalikejdbc._
|
|
import shapeless.{:: => ->:}
|
|
import spray.http.StatusCodes._
|
|
import spray.http.Uri
|
|
import spray.httpx.encoding.Gzip
|
|
import spray.routing.{Directive1, Route}
|
|
|
|
import scala.annotation.tailrec
|
|
import scala.reflect.io.Path
|
|
|
|
|
|
/**
|
|
* Created by company on 12/29/14.
|
|
*/
|
|
|
|
|
|
class WikiRouteService extends RouteService with JadeContext with SecurityContext with JDBCContext {
|
|
|
|
def queryResult(names: List[String]) = {
|
|
val matchedPages = queryPageId(names)
|
|
val matched = names.take(matchedPages.size).zip(matchedPages).map(MatchedPage.tupled)
|
|
val unmatched = names.drop(matchedPages.size)
|
|
QueryPagesRes(matched, unmatched)
|
|
}
|
|
|
|
def queryPageId(names: List[String]): List[UUID] = queryPagesId(names, None).flatten.reverse
|
|
|
|
@tailrec
|
|
private def queryPagesId(names: List[String], parentId: Option[UUID], accum: List[Option[UUID]] = Nil): List[Option[UUID]] = {
|
|
names match {
|
|
case Nil =>
|
|
accum
|
|
case tname :: rnames =>
|
|
getRequestPage(tname, parentId) match {
|
|
case None =>
|
|
accum
|
|
case pageId =>
|
|
queryPagesId(rnames, pageId, pageId :: accum)
|
|
}
|
|
}
|
|
}
|
|
|
|
def getRequestPage(urlSegment: String, parentId: Option[UUID]): Option[UUID] = {
|
|
val column = StructuredPages.column
|
|
StructuredPages.where(
|
|
sqls
|
|
.eq(column.parentId, parentId).and
|
|
.eq(column.title, urlSegment)
|
|
).apply().headOption.map(_.id)
|
|
}
|
|
|
|
def getPlaceHolder = {
|
|
val column = StructuredPages.column
|
|
StructuredPages.where(
|
|
sqls
|
|
.isNull(column.parentId).and
|
|
.eq(column.title, "PlaceHolder")
|
|
).apply().head.content
|
|
}
|
|
|
|
// todo : DeleteView에서 자식페이지를 보여준다
|
|
// annotation.tailrec
|
|
// def traverseTR[A](graph : Map[A,Set[A]], toVisit : Seq[A], visited : Set[A], accumulator : Seq[A]) : Seq[A] = {
|
|
// if(toVisit.isEmpty) {
|
|
// accumulator
|
|
// } else {
|
|
// val next = toVisit.head
|
|
// val succ = (graph(next) -- visited -- toVisit).toSeq
|
|
// // DFS :
|
|
// //traverseTR(graph, succ ++ toVisit.tail, visited + next, accumulator :+ next)
|
|
// // BFS :
|
|
// traverseTR(graph, toVisit.tail ++ succ, visited + next, accumulator :+ next)
|
|
// }
|
|
// }
|
|
//
|
|
// def traverseFrom[A](graph : Map[A,Set[A]], initial : A) =
|
|
// traverseTR(graph, Seq(initial), Set.empty, Seq.empty)
|
|
|
|
def createAction(implicit wContext: WikiPageInfo): Route =
|
|
formFields('code.as[String]) { (code) =>
|
|
complete {
|
|
log.debug("create Action!")
|
|
val name = wContext.newNameSpace.get
|
|
val parentId = wContext.exists.reverse match {
|
|
case Nil =>
|
|
None
|
|
case MatchedPage(parentName, parentId) :: acester =>
|
|
Some(parentId)
|
|
}
|
|
val column = StructuredPages.column
|
|
StructuredPages.createWithNamedValues(
|
|
column.title -> name,
|
|
column.content -> code,
|
|
column.parentId -> parentId,
|
|
column.status -> PageStatus.Show.id
|
|
)
|
|
FileAndPath.innerRedirect(wContext.uri.withQuery(), Found)
|
|
}
|
|
}
|
|
|
|
def deleteAction(implicit wContext: WikiPageInfo): Route =
|
|
withJadeContext { context =>
|
|
complete {
|
|
log.debug("delete Action!")
|
|
val keyword = wContext.trailingPath.last
|
|
context += ("title" -> s"${keyword} - Tigris")
|
|
context += ("keyword" -> keyword)
|
|
context += ("role" -> wContext.authInfo.role)
|
|
render("wiki/view", context)
|
|
}
|
|
}
|
|
|
|
def updateAction(implicit wContext: WikiPageInfo): Route =
|
|
formFields('code.as[String]) { (code) =>
|
|
complete {
|
|
val MatchedPage(pageName, pageId) = wContext.exists.last
|
|
val column = StructuredPages.column
|
|
StructuredPages.updateById(pageId.toString)
|
|
.withNamedValues(
|
|
column.content -> code,
|
|
column.modified -> Some(DateTime.now)
|
|
)
|
|
FileAndPath.innerRedirect(wContext.uri.withQuery(), Found)
|
|
}
|
|
}
|
|
|
|
def createView(implicit wContext: WikiPageInfo): Route =
|
|
withJadeContext { context =>
|
|
complete {
|
|
log.debug("create View!")
|
|
val sample = getPlaceHolder
|
|
val keyword = wContext.trailingPath.last
|
|
context += ("title" -> s"Create / ${keyword} - Tigris")
|
|
context += ("keyword" -> keyword)
|
|
context += ("content" -> sample)
|
|
context += ("role" -> wContext.authInfo.role)
|
|
render("wiki/editor", context)
|
|
}
|
|
}
|
|
|
|
def deleteView(implicit wContext: WikiPageInfo): Route =
|
|
withJadeContext { context =>
|
|
complete {
|
|
log.debug("delete View!")
|
|
val MatchedPage(pageName, pageId) = wContext.exists.last
|
|
StructuredPages.findById(pageId.toString).get
|
|
// context += ("title" -> s"${keyword} - Tigris")
|
|
// context += ("keyword" -> keyword)
|
|
render("wiki/view", context)
|
|
}
|
|
}
|
|
|
|
def updateView(implicit wContext: WikiPageInfo): Route =
|
|
withJadeContext { context =>
|
|
complete {
|
|
log.debug("update View!")
|
|
val MatchedPage(pageName, pageId) = wContext.exists.last
|
|
val pageObj = StructuredPages.findById(pageId.toString).get
|
|
val keyword = wContext.trailingPath.last
|
|
context += ("title" -> s"Update / ${keyword} - Tigris")
|
|
context += ("keyword" -> keyword)
|
|
context += ("content" -> pageObj.content)
|
|
context += ("role" -> wContext.authInfo.role)
|
|
render("wiki/editor", context)
|
|
}
|
|
}
|
|
|
|
def view(implicit wContext: WikiPageInfo): Route =
|
|
withJadeContext { context =>
|
|
complete {
|
|
log.debug("View!")
|
|
val MatchedPage(pageName, pageId) = wContext.exists.last
|
|
val pageObj = StructuredPages.findById(pageId.toString).get
|
|
|
|
val processor = new PegDownProcessorExt(Extensions.ALL)
|
|
context += ("title" -> s"${pageName} - Tigris")
|
|
context += ("keyword" -> pageName)
|
|
context += ("content" -> processor.markdownToHtml(pageObj.content))
|
|
context += ("role" -> wContext.authInfo.role)
|
|
render("wiki/view", context)
|
|
}
|
|
}
|
|
|
|
def urlsplitter(urls: List[String]): Directive1[Seq[String]] =
|
|
provide(
|
|
urls.map(URLDecoder.decode(_, "UTF-8"))
|
|
.map(Normalizer.normalize(_, Normalizer.Form.NFKC))
|
|
)
|
|
|
|
def dropSegment(orig: Uri, drop: Int): Uri = {
|
|
val parentPath = Path(orig.path.toString()).segments.dropRight(drop).mkString("/", "/", "")
|
|
orig.withPath(Uri.Path(parentPath))
|
|
}
|
|
|
|
def routeFilter(
|
|
trailingPath: Seq[String],
|
|
uri: Uri,
|
|
action: StructuredPageAction.value,
|
|
authInfo: SecurityInfo
|
|
): Directive1[(List[MatchedPage], Option[String])] =
|
|
queryResult(trailingPath.toList) match {
|
|
case QueryPagesRes(pageIdList, Nil) =>
|
|
(authInfo.userSession, action) match {
|
|
case (_, StructuredPageAction.Retrieve) =>
|
|
provide(pageIdList -> None)
|
|
case (Some(us), StructuredPageAction.Create) =>
|
|
redirect(uri.withQuery("action" -> StructuredPageAction.Update), Found)
|
|
case (Some(us), _) =>
|
|
provide(pageIdList -> None)
|
|
case (None, _) =>
|
|
requireLogin()
|
|
}
|
|
case QueryPagesRes(parentIdList, "favicon.ico" :: Nil) =>
|
|
redirect("/public/common/img/favicon.ico", SeeOther)
|
|
case QueryPagesRes(parentIdList, newName :: Nil) =>
|
|
(authInfo.userSession, action) match {
|
|
case (None, StructuredPageAction.Create) =>
|
|
requireLogin()
|
|
case (Some(us), StructuredPageAction.Create) =>
|
|
provide(parentIdList -> Some(newName))
|
|
case _ =>
|
|
redirect(
|
|
uri.withQuery("action" -> StructuredPageAction.Create), Found
|
|
)
|
|
}
|
|
case QueryPagesRes(parentIdList, newNames) =>
|
|
redirect(
|
|
dropSegment(uri, newNames.size - 1)
|
|
.withQuery("action" -> StructuredPageAction.Create),
|
|
Found)
|
|
}
|
|
|
|
def userSessionRedirector(authInfo: SecurityContext): Directive1[SecurityContext] = {
|
|
provide(authInfo)
|
|
}
|
|
|
|
def wikiRouter(implicit context: WikiPageInfo): Route =
|
|
context.action match {
|
|
case StructuredPageAction.Create =>
|
|
get(createView(context)) ~
|
|
post(createAction(context))
|
|
case StructuredPageAction.Delete =>
|
|
get(deleteView(context)) ~
|
|
post(deleteAction(context))
|
|
case StructuredPageAction.Update =>
|
|
get(updateView(context)) ~
|
|
post(updateAction(context))
|
|
case StructuredPageAction.Retrieve =>
|
|
get(view(context))
|
|
case _ =>
|
|
redirect(context.uri.copy(query = Uri.Query.Empty), SeeOther)
|
|
}
|
|
|
|
|
|
def wikiContextProvider(trailingPath: Seq[String],
|
|
uri: Uri,
|
|
action: StructuredPageAction.value,
|
|
authInfo: SecurityInfo,
|
|
exists: List[MatchedPage],
|
|
newNameSpace: Option[String]
|
|
): Directive1[WikiPageInfo] =
|
|
provide(
|
|
WikiPageInfo(trailingPath, uri, action, authInfo, exists, newNameSpace)
|
|
)
|
|
|
|
|
|
val wikiContextExtrator =
|
|
path(Segments ~ Slash.?).flatMap(urlsplitter) &
|
|
requestUri &
|
|
parameters('action.as[StructuredPageAction.value] ? StructuredPageAction.Retrieve) &
|
|
withUserSession
|
|
|
|
def wikiContextProvider(
|
|
trailingPath: Seq[String],
|
|
uri: Uri,
|
|
action: StructuredPageAction.value,
|
|
authInfo: SecurityInfo): Directive1[WikiPageInfo] = {
|
|
routeFilter(trailingPath, uri, action, authInfo).flatMap {
|
|
case (exists, newNameSpace) =>
|
|
wikiContextProvider(trailingPath, uri, action, authInfo, exists, newNameSpace)
|
|
}
|
|
}
|
|
|
|
override def receive = runRoute {
|
|
pathEndOrSingleSlash {
|
|
get {
|
|
encodeResponse(Gzip) {
|
|
withJadeContext { context =>
|
|
complete {
|
|
context += ("title" -> "Stay tuned")
|
|
render("wiki/main", context)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} ~
|
|
wikiContextExtrator {
|
|
wikiContextProvider(_, _, _, _) {
|
|
context =>
|
|
wikiRouter(context)
|
|
}
|
|
}
|
|
}
|
|
}
|