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 }