scala-news-reader

rss/atom news reader in scala

git clone https://9o.is/git/scala-news-reader.git

BlogWriterUser.scala

(5883B)


      1 package com.joereader.model
      2 
      3 import com.joereader._
      4 import lib._
      5 import Helper._
      6 import rss._
      7 import config._, S3Config._
      8 
      9 import net.liftmodules.extras.Gravatar
     10 import net.liftweb._
     11 import util._
     12 import common._
     13 
     14 import dispatch._
     15 import Defaults._
     16 
     17 /**
     18  * A container of a user, blog, blog writer.
     19  * A user is allowed to follow another user's blog, but
     20  * for flexibility, we allow user's to follow BlogWriter's that haven't
     21  * registered. So what type are we following? User or BlogWriter?
     22  * BlogWriterUser solves that problem.
     23  */
     24 class BlogWriterUser(
     25   _user: Option[User],
     26   _blog: Option[Blog] = None,
     27   _blogWriter: Option[BlogWriter] = None) {
     28 
     29   def user = _user
     30   def blog = _blog
     31   def blogWriter = _blogWriter
     32 
     33   def this(user: User) =
     34     this(Some(user))
     35 
     36   def this(blog: Blog, blogWriter: BlogWriter) =
     37     this(None, Some(blog), Some(blogWriter))
     38 
     39   def this(user: User, blog: Blog, blogWriter: BlogWriter) =
     40     this(Some(user), Some(blog), Some(blogWriter))
     41 
     42   def gravatarSize = 300
     43   private def defaultColor: String =
     44     BlogWriter.createRecord.color.defaultValue
     45 
     46   /*
     47    * Creates a link that points to the user's page
     48    * if present else the blog writer's page.
     49    */
     50   def link: String =
     51     user.map(Site.userProfileLoc.calcHref) orElse {
     52       for {
     53         blogWriter <- blogWriter
     54         blog <- blog
     55       } yield Site.blogWriterProfileLoc.calcHref(this)
     56     } getOrElse "/"
     57 
     58   /*
     59    * We try everything possible to display the writer's image.
     60    * Below is the order of importance.
     61    * 1. user's image
     62    * 2. user's email gravatar
     63    * 3. blogWriters's image
     64    * 4. blogWriter's email gravatar
     65    * 5. mrnoman
     66    */
     67 
     68   private def userImage: Option[String] = {
     69     val img = user.map(_.img.get) getOrElse ""
     70     if (img.isEmpty) None
     71     else Some(s3.fileUrl(img))
     72   }
     73 
     74   private def userEmailImage: Option[String] = {
     75     val email = user.map(_.email.get) getOrElse ""
     76     if (email.isEmpty) None
     77     else Some(Gravatar.imageUrl(email, gravatarSize))
     78   }
     79 
     80   private def blogWriterImage: Option[String] = {
     81     val img = blogWriter.map(_.img.get) getOrElse ""
     82     if (img.isEmpty) None
     83     else Some(img)
     84   }
     85 
     86   private def blogWriterEmailImage: Option[String] = {
     87     val email = blogWriter.map(_.email.get) getOrElse ""
     88     if (email.isEmpty) None
     89     else Some(Gravatar.imageUrl(email, gravatarSize))
     90   }
     91 
     92   def image: String =
     93     userImage orElse
     94       userEmailImage orElse
     95       blogWriterImage orElse
     96       blogWriterEmailImage getOrElse
     97       s3.fileUrl("mr_noman")
     98 
     99   def name: String =
    100     user.map(_.name.get) orElse
    101       blogWriter.map(_.name.get) getOrElse
    102       ""
    103 
    104   def dashName = name.split(" ").mkString("-")
    105 
    106   def categories =
    107     blogWriter.map(_.categories.get.mkString(", ")).
    108       getOrElse("")
    109 
    110   def color = blogWriter.map(_.color.get).getOrElse(defaultColor)
    111 
    112   def url = blog.map(_.urlHtml.get).getOrElse("")
    113 
    114   override def toString =
    115     user.map(BlogWriterUser.create) orElse {
    116       for {
    117         blogWriter <- blogWriter
    118         blog <- blog
    119       } yield BlogWriterUser.create(blogWriter, blog)
    120     } getOrElse (Helpers.nextFuncName).split(" ").mkString("-")
    121 
    122 }
    123 
    124 object BlogWriterUser {
    125 
    126   import collection.breakOut
    127 
    128   def partDivider = "~"
    129 
    130   /* 
    131    * Indicator that this BlogWriterUser only has user, no blog 
    132    * or BlogWriter. This must be used to view the user's shared 
    133    * articles, hence the abbreviation "sa"
    134    */
    135   def sa = "sa"
    136 
    137   def empty = new BlogWriterUser(User.createRecord)
    138 
    139   /* Creates string format given the type */
    140   def create(writer: BlogWriter, blog: Blog): String =
    141     blog.id.get + partDivider + writer.name.get.str2bytes.bytes2hex
    142 
    143   /* Creates string format given the type */
    144   def create(user: User): String =
    145     user.id.get + partDivider + sa.str2bytes.bytes2hex
    146 
    147   def fromString(s: String): BlogWriterUser = s.split(partDivider).toList match {
    148     case id :: nameHex :: Nil =>
    149       val name = nameHex.hex2bytes.bytes2str
    150       val blog = if (name != sa) Blog.findByStringId(id) else Empty
    151       val user = if (name == sa) User.findByStringId(id) else Empty
    152       val blogWriter = blog.flatMap(_.writers.findStr(name))
    153       val blogUser = blogWriter.flatMap(_.user.obj)
    154       new BlogWriterUser(user or blogUser, blog, blogWriter)
    155     case bwu =>
    156       error("Invalid BlogWriterUser in data store -> " + bwu.mkString(partDivider))
    157       BlogWriterUser.empty
    158   }
    159 
    160   /*
    161    * Removes repeats by grouping user id if user is available, else by
    162    * grouping the string format of blog-writer to blog
    163    * This is needed in case we're following a user's multiple blogs
    164    * so the user may appear multiple times in our list of following. 
    165    * This function assures the user is not repeated. 
    166    */
    167   def uniqueOnly(l: Seq[BlogWriterUser]): List[BlogWriterUser] =
    168     l.groupBy {
    169       bwu =>
    170         val userId = bwu.user.map(_.id.get)
    171         val writer =
    172           for (bw <- bwu.blogWriter; blog <- bwu.blog)
    173             yield create(bw, blog)
    174 
    175         userId.getOrElse(writer getOrElse "")
    176     }.map {
    177       _._2.head
    178     }(breakOut)
    179 
    180   def fromBlogFeedEntry(blog: Box[Blog], entry: Box[FeedEntry]): BlogWriterUser = {
    181     val writer = for {
    182       blog <- blog
    183       entry <- entry
    184       blogWriter <- blog.writers.findStr(entry.author.name)
    185     } yield blogWriter
    186     val user = writer.flatMap(_.user.obj)
    187     new BlogWriterUser(user, blog, writer)
    188   }
    189 
    190   // parse and encode functions are used by SiteMap
    191   def parse(path: List[String]): Box[BlogWriterUser] = {
    192     val blog = Blog.findByBlogName(path(0))
    193     val writer = blog.flatMap(_.writers.findStr(path(1).
    194         replace('+', ' ')))
    195     val user = writer.flatMap(_.user.obj)
    196     
    197     for (b <- blog; bw <- writer)
    198       yield new BlogWriterUser(user, blog, writer)
    199   }
    200 
    201   def encode(bwu: BlogWriterUser): List[String] = List(
    202     bwu.blog.map(_.blogname.get),
    203     bwu.blogWriter.map(_.name.get)).flatten
    204 }