All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.server.view.IndexView.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.server.view

import com.twitter.concurrent.AsyncStream
import com.twitter.finagle.{Service, SimpleFilter}
import com.twitter.finagle.http.{Request, Response}
import com.twitter.io.{Reader, Buf}
import com.twitter.server.util.HttpUtils.{expectsHtml, newResponse}
import com.twitter.util.Future

object IndexView {
  sealed trait Entry
  case class Link(id: String, href: String) extends Entry
  case class Group(id: String, links: Seq[Entry]) extends Entry

  implicit object EntryOrdering extends Ordering[Entry] {
    def compare(a: Entry, b: Entry) = (a, b) match {
      case (Link(_, _), Group(_, _)) => -1
      case (Group(_, _), Link(_, _)) => 1
      case (Link(id1, _), Link(id2, _)) => id1 compare id2
      case (Group(id1, _), Group(id2, _)) => id1 compare id2
    }
  }

  /** Render nav and contents into an html template. */
  def render(title: String, uri: String, nav: Seq[Entry], contents: Reader): Reader = {

    def renderNav(
      ls: Seq[Entry],
      sb: StringBuilder = new StringBuilder
    ): String = ls match {
        case Seq() => sb.toString

        case Link(id, href) +: rest =>
          val selected = if (href == uri) "selected" else ""
          sb ++= s"""
            
  • ${id}
  • """ renderNav(rest, sb) case Group(id, links) +: rest => val isChild = links exists { // Instead of strictly checking for href == uri, // we are a bit more liberal to allow for "catch-all" // endpoints (ex. /admin/clients/). case Link(_, href) => !href.stripPrefix(uri).contains("/") case _ => false } val active = if (isChild) "active" else "" val collapse = if (isChild) "glyphicon-collapse-up" else "" sb ++= s""" """ renderNav(rest, sb) } Reader.concat( Reader.fromBuf( Buf.Utf8( s""" ${title} · Twitter Server Admin
    """ ) ) +:: contents +:: AsyncStream.of(Reader.fromBuf( Buf.Utf8("""
    """)) ) ) } } /** * Wraps content returned from the underlying service with a view of * the index. Note, the view is wrapped around the underlying service * selectively. For example, if it's a request from a browser and * the content is an html fragment. */ class IndexView(title: String, uri: String, index: () => Seq[IndexView.Entry]) extends SimpleFilter[Request, Response] { import IndexView._ private[this] def isFragment(contentType: String, content: String): Boolean = { contentType.toLowerCase.contains("text/html") && !content.contains("") } def apply(req: Request, svc: Service[Request, Response]) = if (!expectsHtml(req)) svc(req) else svc(req) flatMap { res => val contentType = res.headerMap.get("content-type").orNull val content = res.contentString res match { case _ if !isFragment(contentType, content) => Future.value(res) case res if res.isChunked => val response = Response() response.contentType = "text/html;charset=UTF-8" response.setChunked(true) val reader = render(title, uri, index().sorted, res.reader) Reader.copy(reader, response.writer) ensure response.writer.close() Future.value(response) case res => val body = Reader.fromBuf(Buf.Utf8(content)) val reader = render(title, uri, index().sorted, body) Reader.readAll(reader).flatMap { html => newResponse( contentType = "text/html;charset=UTF-8", content = html ) } } } }




    © 2015 - 2025 Weber Informatics LLC | Privacy Policy