scala-news-reader
rss/atom news reader in scala
git clone https://9o.is/git/scala-news-reader.git
Verification.scala
(8334B)
1 package com.joereader.snippet
2
3 import net.liftweb._
4 import util._
5 import Helpers._
6 import http._
7 import SHtml._
8 import js._, JsCmds._
9 import common._
10
11 import com.joereader._
12 import lib._
13 import api.rest.wordpress._
14 import rss._
15 import config._
16 import model._
17
18 import scala.xml._
19 import dispatch._
20 import Defaults._
21
22 /*
23 * A session var had to be included so we can keep state between Wordpress
24 * signin and callback. Reset after done using it!
25 */
26 object CurrentVerification extends SessionVar(VerificationSettings())
27
28 case class VerificationSettings() {
29 val user: User = Verification.setUser()
30 var blog: Blog = Blog.createRecord
31 var searched, verified = false
32 var urlHtml = ""
33 }
34
35 /**
36 * Verifies a blog.
37 */
38 class Verification extends VerificationDesigns {
39
40 var set = VerificationSettings()
41
42 val writerName = ValueCell("")
43
44 val owner = ValueCell(false)
45
46 val metaName = "readmeans"
47
48 val metaContent = (writerName lift owner)(
49 (w, o) => set.user.id.is + ":" + w + {
50 if (o) ":owner" else ""
51 })
52
53 val verification = metaContent.lift(metaContent =>
54 s"""<meta name="$metaName" content="$metaContent"/>""")
55
56 def render = "#verify-state" #> idMemoize(verifyBlog)
57
58 def process(): JsCmd = {
59
60 // check if wordpress auth works
61 for {
62 me <- WordpressClient.me().right
63 site <- WordpressClient.site(me)().right
64 } {
65 if (URLFormatter.same(set.urlHtml, site.URL.toString) &&
66 set.blog.writers.names.exists(_ == me.username)) {
67 writerName.set(me.username)
68 set.verified = true
69 }
70 }
71
72 set.verified =
73 if (set.verified) true
74 else MetaVerification(metaName, metaContent.get, set.urlHtml).verified
75
76 if (set.verified) verifiedProcess()
77 else S.error("Verification was unsuccessful.")
78 }
79
80 def verifiedProcess(): JsCmd = {
81 // save blog stuff
82 val bw = BlogWriter.createRecord.name(writerName.is).user(set.user.id.is)
83
84 if (owner) set.blog.owner(set.user.id.is)
85 set.blog.writers.addSafely(bw)
86
87 if (set.blog.blogname.is.isEmpty)
88 set.blog.blogname(StringHelpers.randomString(15).toLowerCase)
89
90 set.blog.save
91
92 WordpressClient.resetToken // reset
93 CurrentVerification(VerificationSettings())
94
95 onVerified() & onVerifiedSignUp() // support signup too
96 }
97
98 def verifyBlog(outer: IdMemoizeTransform) = {
99
100 // if idmemoize updated, get urlhtml and find feed
101 if (set.urlHtml.nonEmpty) {
102 val existing = Blog.findByUrl(set.urlHtml)
103 val res = set.urlHtml.response
104
105 val links = res.right.map(_.content.rssLinks)
106 val feed = links.right.map(_.feed)
107 val writers = feed.right.map(_.writers.map(
108 w =>
109 BlogWriter.createRecord.
110 name(w.name).img(w.imgUrl)
111 ))
112
113 if (existing.isEmpty) {
114 for {
115 res <- res.right
116 feed <- feed.right
117 writers <- writers.right
118 } set.blog = Blog.createRecord
119 .urlHtml(res.loc)
120 .name(feed.name)
121 .description(feed.description)
122 .writers(writers)
123 .urlRss(feed.links)
124 } else {
125 existing.map(set.blog = _)
126 writers.right.map(_.map(
127 writer => set.blog.writers.addSafely(writer)))
128 }
129
130 set.searched = true
131 set.urlHtml = set.blog.urlHtml.get // nicely formatted
132 CurrentVerification(set)
133 } else
134 set = CurrentVerification.is
135
136 "#blog-url" #> text(set.urlHtml, set.urlHtml = _) &
137 "#search-blog" #> ajaxSubmit("Search Blog", () => ajaxInvoke(outer.setHtml)) &
138 "#writer-list *" #> listWriters &
139 "#blog-owner-q-yes" #> radios(0) &
140 "#blog-owner-q-no" #> radios(1) &
141 "#verification-info [class]" #> hideVerification &
142 "#verification-help [class]" #> hideHelp &
143 "#wordpress-login [href]" #> Site.wordpressSignIn.url &
144 "#blogger-link [href]" #> Site.bloggerHelp.url &
145 "#tumblr-link [href]" #> Site.tumblrHelp.url &
146 "#blog-owner-q [class]" #> hideOwnerQuestion &
147 "#verification-info-input" #> WiringUI.asText(verification) &
148 "#verify-blog" #> ajaxSubmit("Verify", process)
149 }
150
151 def writersRadios = ajaxRadio[String](
152 set.blog.writers.unregisteredNames,
153 Full(set.blog.writers.unregisteredNames headOr ""), {
154 s => onWritersRadiosChange(s) & onWritersRadiosChangeSignUp(s)
155 }).unregisteredWritersChoicesToPictureForm(set.blog)
156
157 val radios = ajaxRadio[Boolean](
158 Seq(true, false),
159 Full(false), {
160 bool =>
161 owner.set(bool)
162 Noop
163 })
164
165 def registeredUsers =
166 set.blog.writers.registered.
167 registeredWritersChoicesToPictureForm(set.blog)
168
169 def completedMsg =
170 if (set.blog.writers.unregistered.isEmpty && set.searched)
171 Text("Looks like all writers have joined")
172 else NodeSeq.Empty
173
174 def hideVerification: String = {
175 set.blog.writers.unregisteredNames.nonEmpty ? "" | "hide"
176 }
177
178 def hideHelp: String = hideVerification
179
180 def hideOwnerQuestion: String = {
181 val size = set.blog.writers.unregisteredNames.size
182 owner.set((size == 1) ? true | false)
183 (size < 2) ? "hide" | ""
184 }
185
186 // returns the radio group of writers
187 def listWriters(): NodeSeq =
188 if (set.blog.writers.names.isEmpty && set.searched)
189 Text("Writers not found. Double check your url.")
190 else {
191 writerName.set(set.blog.writers.unregisteredNames headOr "")
192 if (!EmailVar.is.isEmpty) {
193 set.user.name(writerName.is)
194 }
195 writersRadios ++ registeredUsers ++ completedMsg
196 }
197
198 // Below is a hack to separate verification between signup and a logged in user
199 // who has multiple blogs. I chose to do it this way to avoid multiple templates.
200 // We differentiate by checking if EmailVar is set (which only happens during signup.)
201 // including setUser function
202
203 def onVerifiedSignUp(): JsCmd =
204 if (!EmailVar.is.isEmpty) {
205 BetaUser.find(EmailVar.is).map(_.delete_!)
206
207 set.user
208 .blogs.add(set.blog)
209 .email(EmailVar.is)
210 .password(Helpers.randomString(20))
211 .username(StringHelpers.randomString(15).toLowerCase)
212 set.user.password.hashIt
213 set.user.save
214 User.logUserIn(set.user, isAuthed = true)
215
216 RedirectTo(Site.signUp3.url, () => {
217 BlogIdVar(set.blog.id.is.toString)
218 VerifiedVar(set.verified)
219 })
220 } else Noop
221
222 def onWritersRadiosChangeSignUp(str: String): JsCmd =
223 if (!EmailVar.is.isEmpty) {
224 writerName.set(str)
225 set.user.name(str)
226 Noop
227 } else Noop
228
229 protected def onVerified(): JsCmd =
230 if (EmailVar.is.isEmpty) {
231 set.user.blogs.add(set.blog).update
232
233 if (owner) S.redirectTo(Site.editBlogs.url)
234 else S.notice("Congratulations! You have verified your blog!")
235 } else Noop
236
237 protected def onWritersRadiosChange(str: String): JsCmd =
238 if (EmailVar.is.isEmpty) {
239 writerName.set(str)
240 Noop
241 } else Noop
242 }
243
244 object Verification {
245 def setUser(): User = {
246 if (EmailVar.is.isEmpty) User.currentUser.openOr(User.createRecord)
247 else User.createRecord
248 }
249 }
250
251 sealed trait VerificationDesigns {
252
253 implicit class InputRadioDesignImplicit(choices: ChoiceHolder[String]) {
254
255 def unregisteredWritersChoicesToPictureForm(blog: Blog): NodeSeq = {
256
257 val blogWriters = blog.writers.unregistered
258
259 if (blogWriters.size != choices.items.size) NodeSeq.Empty
260 else
261 for (i <- 0 until choices.items.size) yield {
262 val item = choices.items(i)
263 val bwu = new BlogWriterUser(blog, blogWriters(i))
264
265 <label>
266 { item.xhtml }<div>
267 { item.key.toString }
268 </div>
269 <img src={ bwu.image } class="writer"/>
270 </label>
271 }
272 }
273 }
274
275 implicit class InputRadioDesign2Implicit(writers: List[BlogWriter]) {
276
277 def registeredWritersChoicesToPictureForm(blog: Blog): NodeSeq = {
278
279 for (i <- 0 until writers.size) yield {
280 val blogWriter = writers(i)
281 val user = User.findByStringId(blogWriter.user.get.toString)
282
283 val bwu = new BlogWriterUser(user, Some(blog), Some(blogWriter))
284
285 <label>
286 <div>
287 { bwu.name }
288 </div>
289 <a href={ bwu.link } target="_blank">
290 <img src={ bwu.image } class="writer"/>
291 </a>
292 </label>
293 }
294 }
295 }
296
297 }