pyc-website

main website for pyc inc.

git clone https://9o.is/git/pyc-website.git

UserSnip.scala

(10759B)


      1 package inc.pyc
      2 package snippet
      3 
      4 import lib._
      5 import rest._
      6 import model._
      7 import field._
      8 import xml._
      9 import net.liftweb._
     10 import common._
     11 import http._
     12 import util._
     13 import Helpers._
     14 import json._
     15 import JsonDSL._
     16 import JsonAST.{JValue, JString, JBool}
     17 import net.liftmodules.mongoauth.model.ExtSession
     18 import net.liftmodules.extras.SnippetHelper
     19 import inc.pyc.rest.IdVerificationFiles
     20 
     21 /**
     22  * Basic User information from a snippet.
     23  */
     24 sealed trait UserSnippet extends SnippetHelper with Logger {
     25 
     26   protected def user: Box[User]
     27 
     28   protected def serve(snip: User => NodeSeq): NodeSeq =
     29     (for {
     30       u <- user ?~ "User not found"
     31     } yield {
     32       snip(u)
     33     }): NodeSeq
     34     
     35   protected def serve(snip: User => CssSel): CssSel =
     36     (for {
     37       u <- user ?~ "User not found"
     38     } yield {
     39       snip(u)
     40     }): CssSel
     41 
     42   def username(in: NodeSeq): NodeSeq = serve { user =>
     43     Text(user.username.get)
     44   }
     45 
     46   def name(in: NodeSeq): NodeSeq = serve { user =>
     47     val name = user.fname.get + " " + user.lname.get
     48     if (name.length > 1) Text(name)
     49     else Text(user.username.get)
     50   }
     51   
     52   def limit(in: NodeSeq): NodeSeq = serve {
     53     user => Text(user.purchaseLimit.get.toString)
     54   }
     55   
     56   def identifier(in: NodeSeq): NodeSeq = serve {
     57     user =>
     58       if(user.fname.get.isEmpty) Text(user.email.get)
     59       else name(in)
     60   }
     61 }
     62 
     63 /**
     64  * Basic logged in user information from a snippet.
     65  */
     66 trait CurrentUser extends UserSnippet {
     67   protected def user = User.currentUser
     68 }
     69 
     70 object CurrentUser extends CurrentUser
     71 
     72 /**
     73  * Angular snippet for accessing user information.
     74  */
     75 trait AngularUserSnippet extends AngularSnippet {
     76   
     77   protected def user: Box[User]
     78   
     79   protected def serve(snip: User => JValue): JValue =
     80     (for {
     81       u <- user ?~ "User not found"
     82     } yield {
     83       snip(u)
     84     }): JValue
     85     
     86   /** Validates and saves the currently signed in user. */
     87   protected def validateAndSave(f: () => Unit): JValue = validateUser {
     88     user =>
     89       f()
     90       user.save()
     91       NgAlert.success
     92   }
     93   
     94   /** Validates and updates the currently signed in user. */
     95   protected def validateAndUpdate(f: () => Unit): JValue = validateUser {
     96     user =>
     97       f()
     98       user.update
     99       NgAlert.success
    100   }
    101   
    102   protected def validateUser(f: User => JValue): JValue = serve {
    103     user =>
    104       user.validate match {
    105         case Nil => f(user)        
    106         case errors =>
    107           NgAlert.danger(S ? "Invalid_submission", errors)
    108       }
    109   }
    110 }
    111 
    112 /**
    113  * Angular snippet for accessing logged in user information.
    114  */
    115 trait AngularCurrentUser extends AngularUserSnippet {
    116   protected def user = User.currentUser
    117 }
    118 
    119 class UserLogin extends AngularSnippet {
    120   
    121   def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
    122     
    123   def submit(model: JValue): JValue = {
    124     for {
    125       JString(e) <- model \ "email"
    126       JString(password) <- model \ "password"
    127       JBool(remember) <- model \ "remember"
    128     } yield {
    129       val email = e.toLowerCase.trim
    130       User.loginCredentials(LoginCredentials(email, remember))
    131       
    132       User.findByEmail(email) match {
    133           case Full(user) if !user.verified.get =>
    134             NgAlert.danger(
    135               <span>{S ? "unverified.title"}:</span> ++
    136               <p>{S ? "unverified.message"}.</p>, Nil)
    137               
    138           case Full(user) if (user.password.isMatch(password)) =>
    139             User.logUserIn(user, remember)
    140             if (remember) User.createExtSession(user.id.get)
    141             else ExtSession.deleteExtCookie()
    142             NgAlert.success
    143           
    144           case _ =>
    145             NgAlert.danger(
    146               <span>{S ? "invalid.title"}:</span> ++
    147               <p>{S ? "invalid.message"}.</p>, Nil)
    148         }
    149     }
    150   }
    151 }
    152 
    153 class PasswordChange extends AngularCurrentUser {
    154   
    155   /** 
    156    *  Passwords must match this pattern.
    157    *  - makes sure there are no white-space characters
    158    *  - minimum length of 8
    159    *  - at least one non-alpha character
    160    */
    161   def passwordPattern: String = "^(?=.*[^a-zA-Z])\\S{8,}$"
    162   
    163   def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
    164       
    165   def submit(model: JValue): JValue = serve {
    166     user =>  
    167       for {
    168         JString(password) <- model \ "password"
    169       } yield {
    170         
    171         if(password matches passwordPattern) {
    172           
    173           user.password(password)
    174           user.password.hashIt
    175           user.save()
    176           
    177           NgAlert.success(
    178         	    <span>{S ? "password_updated"}</span>)
    179         } else {
    180           NgAlert.danger(
    181         	    <span>{S ? "password_req"}:</span> ++
    182         	    <ul><li>{S ? "password_whitespace"}</li><li>{S ? "password_atleast8"}</li><li>{S ? "password_nonalpha"}</li></ul>, Nil)
    183         }
    184         
    185       }
    186     }
    187 }
    188 
    189 class PasswordRecovery extends AngularSnippet {
    190   
    191   def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
    192       
    193   def submit(model: JValue): JValue = {
    194     for {
    195       JString(e) <- model \ "email"
    196     } yield {
    197       val email = e.toLowerCase.trim
    198       
    199       def msg = NgAlert.success(
    200             <span>{S ? "sent.title"}</span> ++
    201             <p>{S ? "sent.message"}.</p>)
    202       
    203       User.findByEmail(email) match {
    204         case Full(user) =>
    205           User.sendLoginToken(user)
    206           User.loginCredentials.remove()
    207           msg
    208           
    209         case _ => msg
    210       }
    211     }
    212   }
    213 }
    214 
    215 class UserRegistration extends AngularSnippet {
    216   
    217   def roundTrips: List[RoundTripInfo] = List("submit" -> submit _)
    218   
    219   def submit(model: JValue): JValue = {
    220     for {
    221       JString(e) <- model \ "email"
    222     } yield {
    223       val email = e.toLowerCase.trim
    224       val user = User.createRecord
    225       user.email(email)
    226       user.username(Helpers.randomString(20))
    227       user.password(Helpers.randomString(20))
    228       user.password.hashIt
    229       
    230       user.validate match {
    231         case Nil =>
    232           user.save()
    233           User.sendRegistrationToken(user)
    234         
    235           NgAlert.success(
    236         	<span>{S ? "thanks.title"}.</span> ++
    237         	<p>{S ? "thanks.message"}.</p>
    238           )
    239         case errors =>
    240           NgAlert.danger(
    241         	Text(S ? "Invalid_submission"), 
    242         	errors
    243           )
    244       }
    245     }
    246   }
    247 }
    248 
    249 class UserSettings extends AngularSnippet {
    250   import com.foursquare._
    251   import rogue.LiftRogue._
    252     
    253   def roundTrips: List[RoundTripInfo] = List(
    254       "submit" -> submit _)
    255     
    256   def submit(model: JValue): JValue = 
    257     for {
    258       JString(id) <- model \ "id"
    259       JString(fname) <- model \ "fname"
    260       JString(lname) <- model \ "lname"
    261       JString(dob) <- model \ "dob"
    262       JString(license) <- model \ "driversLicense"
    263     } yield {
    264       
    265       val exists = User.
    266     	where(_.fname eqs fname).
    267     	and(_.lname eqs lname).
    268     	and(_.dob eqs dob).
    269     	and(_.driversLicense eqs license).
    270     	exists()
    271     	
    272       if(exists) {
    273         NgAlert.danger(S ? "verify_already_exists", Nil)
    274       } else {
    275         User.findByStringId(id).map {
    276           user =>
    277             user.
    278               fname(fname).
    279               lname(lname).
    280               dob(dob).
    281               driversLicense(license).
    282               save()
    283 
    284             NgAlert.success("updated")
    285 
    286         } openOr JNull
    287       }
    288     }
    289 }
    290 
    291 class UserSettingsEmail extends AngularCurrentUser {
    292   
    293   def roundTrips: List[RoundTripInfo] = List(
    294       "submit" -> submit _, 
    295       "init" -> init _)
    296   
    297   def submit(model: JValue): JValue = serve {
    298     user =>
    299       for {
    300         JString(e) <- model \ "email"
    301       } yield {
    302         val email = e.toLowerCase.trim
    303         EmailResetToken.sendToken(user, email)
    304         
    305         NgAlert.success(
    306         	Text(S ? "verify_email_msg")
    307         )
    308       }
    309   }
    310   
    311   def init(model: JValue): JValue = serve {
    312     user =>
    313       ("email" -> user.email.get)
    314   }
    315 }
    316 
    317 
    318 class UserVerifyPassword extends AngularCurrentUser with UserSnippet {
    319     
    320   def roundTrips: List[RoundTripInfo] = List(
    321       "init" -> init _)
    322       
    323   def init(ignore: JValue): JValue = serve {
    324     user: User =>
    325       user.verifypass.reset.update // set a new password
    326       
    327       ("password" -> user.verifypass.get) ~
    328       ("limit" -> user.userLimitAsString): JValue
    329   }
    330   
    331   def show(in: NodeSeq): NodeSeq = serve {
    332     user =>
    333       if(user.limit1k) NodeSeq.Empty else in
    334   }
    335 }
    336 
    337 
    338 class VerificationSteps extends CurrentUser {
    339   def render = serve {
    340     user: User =>
    341       "wizard [current-step]" #> s"'${user.userLimitAsString}'"
    342   } 
    343 }
    344 
    345 
    346 class PhoneVerification extends AngularCurrentUser with Logger {
    347   
    348   lazy val smscode = (100000 + new scala.util.Random().nextInt(900000)).toString
    349   var phone = ""
    350   
    351   def roundTrips: List[RoundTripInfo] = List(
    352       "sendsms" -> sendsms _,
    353       "verifyphone" -> verifyphone _,
    354       "init" -> init _)
    355       
    356   
    357   def sendsms(model: JValue): JValue = serve {
    358     user =>
    359       for {
    360         JString(newPhone) <- model \ "phone"
    361       } yield {
    362         val msg = S ? "sms_code_msg" format (user.fname.get, smscode)
    363         
    364         if (phone == newPhone) {
    365           NgAlert.success
    366         } else if (Twilio.sms(newPhone, msg)) {
    367           phone = newPhone
    368           NgAlert.success
    369         } else {
    370           NgAlert.danger(
    371         	  Text(S ? "technical_difficulties"), Nil)
    372         }
    373         
    374       }
    375   }
    376   
    377   def verifyphone(model: JValue): JValue = serve {
    378     user =>
    379       for {
    380         JString(smscode) <- model \ "smscode"
    381       } yield 
    382         
    383         if(this.smscode == smscode) {
    384           user.
    385             purchaseLimit(USAPurchaseLimit.D3k).
    386             phone(phone)
    387           validateAndUpdate(() => {})
    388           
    389           NgAlert.success(("purchaseLimit" -> user.purchaseLimit.get.toString))
    390         } else {
    391           NgAlert.danger(
    392             Text(S ? "sms_verify_fail"), Nil)
    393         }
    394       
    395   }
    396   
    397   def init(model: JValue): JValue = serve {
    398     user =>
    399       ("phone" -> user.phone.get)
    400   }
    401 }
    402 
    403 
    404 class IdVerification extends AngularCurrentUser {
    405   
    406   def roundTrips: List[RoundTripInfo] = List(
    407       "submit" -> submit _)
    408       
    409   def submit(model: JValue): JValue = serve {
    410     user =>
    411       
    412       import IdVerificationHelper._
    413       val service = s3(createS3Folder(user))
    414       val success = upload(service)
    415 
    416       if (success) {
    417         user.purchaseLimit(USAPurchaseLimit.D10k_?)
    418         user.update
    419         notifyIdentityRequest(user)
    420         NgAlert.success(
    421           <span>{S ? "file_received"}:</span> ++
    422           <p>{S ? "file_review"}.</p>, 
    423           ("purchaseLimit" -> user.userLimitAsString))
    424       } else {
    425         NgAlert.danger(Text(S ? "file_upload_fail"), Nil)
    426       }
    427   }
    428 }
    429 
    430 
    431 class PurchaseLimit extends AngularCurrentUser {
    432   
    433   def roundTrips: List[RoundTripInfo] = List("init" -> init _)
    434       
    435   def init(model: JValue): JValue = serve {
    436     user =>  
    437       ("purchaseLimit" -> user.userLimitAsString)
    438     }
    439 }