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 }