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) } } } }