pyc-website
main website for pyc inc.
git clone https://9o.is/git/pyc-website.git
commit f314be546513e409ca0d269a109119fca0912b76 parent 4530d5c34222fd5018b82b2e59bff587866ecedd Author: Jul <jul@9o.is> Date: Tue, 12 May 2026 17:32:10 +0800 implemented registration and login. faqs links. twitter link. minor ng-routing changes Diffstat:
35 files changed, 529 insertions(+), 232 deletions(-)
diff --git a/sbt-debug.sh b/sbt-debug.sh @@ -0,0 +1 @@ +java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=127.0.0.1:5005 -Dfile.encoding=UTF8 -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384m -Xms512M -Xss1M -Xmx1536M -jar `dirname $0`/project/sbt-launch.jar "$@" diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -32,7 +32,7 @@ class Boot extends Loggable { MongoAuth.loginTokenAfterUrl.default.set(Site.password.url) MongoAuth.siteName.default.set("PYD") MongoAuth.systemEmail.default.set(SystemUser.user.email.get) - MongoAuth.systemUsername.default.set(SystemUser.user.name.get) + MongoAuth.systemUsername.default.set(SystemUser.user.fname.get) // For S.loggedIn_? and TestCond.loggedIn/Out builtin snippet LiftRules.loggedInTest = Full(() => User.isLoggedIn) @@ -40,9 +40,6 @@ class Boot extends Loggable { // checks for ExtSession cookie LiftRules.earlyInStateful.append(User.testForExtSession) - // Gravatar - Gravatar.defaultImage.default.set("wavatar") - // config an email sender SmtpMailer.init diff --git a/src/main/scala/com/pyd/config/Site.scala b/src/main/scala/com/pyd/config/Site.scala @@ -32,10 +32,11 @@ object Site extends Locs { val home = MenuLoc(Menu.i("Home") / "index") val applyATM = MenuLoc(Menu.i("Apply for Bitcoin ATM") / "apply-atm" >> TopBarGroup >> UiRouterGroup >> UiRouterController("AtmApplicationCtrl")) val locations = MenuLoc(Menu.i("Locations") / "locations" >> TopBarGroup >> SiteMapGroup >> UiRouterGroup >> UiRouterController("NearAtmNotifyCtrl")) - val whatsBitcoin = MenuLoc(Menu.i("What Is Bitcoin") / "whatisbitcoin" >> SiteMapGroup >> UiRouterGroup) + val whatsBitcoin = MenuLoc(Menu.i("What Is Bitcoin") / "what-is-bitcoin" >> SiteMapGroup >> UiRouterGroup) val about = MenuLoc(Menu.i("About Us") / "about" >> TopBarGroup >> SiteMapGroup >> UiRouterGroup) val blog = MenuLoc(Menu.i("Blog") / "blog" >> TopBarGroup >> RedirectBlog >> SiteMapGroup) val faqs = MenuLoc(Menu.i("FAQs") / "faqs" >> TopBarGroup >> SiteMapGroup >> UiRouterGroup) + val atmHowTo = MenuLoc(Menu.i("How To Use ATM") / "how-to-use-atm" >> SiteMapGroup >> UiRouterGroup) val loginToken = MenuLoc(buildLoginTokenMenu) val logout = MenuLoc(buildLogoutMenu) @@ -45,10 +46,15 @@ object Site extends Locs { ) / "user" >> Loc.CalcValue(() => User.currentUser) lazy val profileLoc = profileParamMenu.toLoc - val password = MenuLoc(Menu.i("Password") / "settings" / "password" >> RequireLoggedIn >> SettingsGroup) + val settings = MenuLoc(Menu.i("Settings") / "settings" >> SettingsGroup >> UiRouterGroup >> RequireLoggedIn) + val password = MenuLoc(Menu.i("Password") / "settings" / "password" >> SettingsGroup >> UiRouterGroup >> RequireLoggedIn) + val account = MenuLoc(Menu.i("Account") / "settings" / "account" >> SettingsGroup >> RequireLoggedIn) val editProfile = MenuLoc(Menu("EditProfile", "Profile") / "settings" / "profile" >> SettingsGroup >> RequireLoggedIn) - val register = MenuLoc(Menu.i("Register") / "register" >> RequireNotLoggedIn) + + val register = MenuLoc(Menu.i("Register") / "register" >> RequireNotLoggedIn >> SiteMapGroup >> UiRouterGroup) + val login = MenuLoc(Menu.i("Login") / "login" >> RequireNotLoggedIn >> SiteMapGroup >> UiRouterGroup) + val forgotPassword = MenuLoc(Menu.i("Forgot Password") / "forgot-password" >> RequireNotLoggedIn >> UiRouterGroup) private def menus = List( home.menu, @@ -58,14 +64,14 @@ object Site extends Locs { about.menu, blog.menu, faqs.menu, - //Menu.i("Login") / "login" >> RequireNotLoggedIn, - //register.menu, - //loginToken.menu, - //logout.menu, - //profileParamMenu, - //account.menu, - //password.menu, - //editProfile.menu, + login.menu, + register.menu, + atmHowTo.menu, + loginToken.menu, + logout.menu, + forgotPassword.menu, + settings.menu, + password.menu, Menu.i("Error") / "error" >> Hidden, Menu.i("404") / "404" >> Hidden, Menu.i("Status") / "ping" >> Hidden >> CalcStateless(() => true ) >> EarlyResponse(() => Full(OkResponse())), diff --git a/src/main/scala/com/pyd/lib/NgUIRouter.scala b/src/main/scala/com/pyd/lib/NgUIRouter.scala @@ -36,7 +36,7 @@ object NgUIRouterFactory extends Factory { /* Finds menu items that are in UiRouterGroup group. */ private def findRoutes = LiftRules.siteMap map { - _.locForGroup(UiRouterGroup.group.head).map(_.menu) + _.menus.filter(_.loc.inGroup_?(UiRouterGroup.group.head)) } openOr Nil } @@ -63,17 +63,11 @@ trait NgUIRouterSnip extends SnippetHelper { otherwise+"$stateProvider"+ routes.vend.map { menu => - def controller: Option[String] = menu.loc.allParams. - filter(_.isInstanceOf[UiRouterController]). - map(_.asInstanceOf[UiRouterController].controller).headOption - - def url: String = S.contextPath+menu.loc.calcDefaultHref - def state: String = url.replace('/', '.').tail - + val templateUrl = S.contextPath+menu.loc.calcDefaultHref + val state: String = menu.loc.name.replaceAll(" ","").toLowerCase - ".state('"+state+"',{url:'"+url+"',views:"+ - "{'viewA':{templateUrl:'"+url+".html?ajax'}}"+ - controller.map(",controller:'"+_+"'").getOrElse("")+ + ".state('"+state+"',{url:'"+templateUrl+"',views:"+ + "{'viewA':{templateUrl:'"+templateUrl+".html?ajax'}}"+ "})" }.mkString+ ";});" @@ -129,7 +123,7 @@ trait NgUIRouterMenu extends SnippetHelper { def uiSref(el: Elem): Elem = { import NgUIRouterFactory._ if(loc.inGroup_?(UiRouterGroup.group.head)) - el % ("ui-sref" -> link.toString.replace('/', '.').tail) + el % ("ui-sref" -> loc.name.replaceAll(" ","").toLowerCase) else el } @@ -148,7 +142,7 @@ trait NgUIRouterMenu extends SnippetHelper { loc <- SiteMap.findAndTestLoc(name) link <- loc.createDefaultLink } yield { - "* [ui-sref]" #> link.toString.replace('/', '.').tail & + "* [ui-sref]" #> loc.name.replaceAll(" ","").toLowerCase & "* [href]" #> link }) openOr Empty options diff --git a/src/main/scala/com/pyd/model/AtmApplication.scala b/src/main/scala/com/pyd/model/AtmApplication.scala @@ -7,6 +7,7 @@ import net.liftweb._ import mongodb.record._ import mongodb.record.field._ import record.field._ +import org.joda.time.DateTime class AtmApplication private () extends MongoRecord[AtmApplication] with ObjectIdPk[AtmApplication] { def meta = AtmApplication @@ -50,6 +51,8 @@ class AtmApplication private () extends MongoRecord[AtmApplication] with ObjectI valMaxLen(255, "Website must be 255 characters or less") _ :: super.validations } + + def whenCreated: DateTime = new DateTime(id.get.getTime) } object AtmApplication extends AtmApplication with RogueMetaRecord[AtmApplication] { diff --git a/src/main/scala/com/pyd/model/NearAtmNotify.scala b/src/main/scala/com/pyd/model/NearAtmNotify.scala @@ -3,11 +3,11 @@ package model import lib.RogueMetaRecord import field.USStatesField - import net.liftweb._ import mongodb.record._ import mongodb.record.field.ObjectIdPk import record.field._ +import org.joda.time.DateTime class NearAtmNotify private () extends MongoRecord[NearAtmNotify] with ObjectIdPk[NearAtmNotify] { def meta = NearAtmNotify @@ -40,6 +40,8 @@ class NearAtmNotify private () extends MongoRecord[NearAtmNotify] with ObjectIdP } object state extends USStatesField(this) + + def whenCreated: DateTime = new DateTime(id.get.getTime) } object NearAtmNotify extends NearAtmNotify with RogueMetaRecord[NearAtmNotify] { diff --git a/src/main/scala/com/pyd/model/User.scala b/src/main/scala/com/pyd/model/User.scala @@ -11,7 +11,6 @@ import common._ import http.{StringField => _, BooleanField => _, _} import mongodb.record.field._ import record.field._ -import util.FieldContainer import net.liftmodules.mongoauth._ import net.liftmodules.mongoauth.field._ @@ -19,55 +18,21 @@ import net.liftmodules.mongoauth.model._ class User private () extends ProtoAuthUser[User] with ObjectIdPk[User] { def meta = User - + def userIdAsString: String = id.toString - object locale extends LocaleField(this) { - override def displayName = "Locale" - override def defaultValue = "en_US" - } - object timezone extends TimeZoneField(this) { - override def displayName = "Time Zone" - override def defaultValue = "America/Chicago" - } - - object name extends StringField(this, 64) { - override def displayName = "Name" - + object fname extends StringField(this, 64) { override def validations = - valMaxLen(64, "Name must be 64 characters or less") _ :: + valMaxLen(64, "First Name must be 64 characters or less") _ :: super.validations } - object location extends StringField(this, 64) { - override def displayName = "Location" - - override def validations = - valMaxLen(64, "Location must be 64 characters or less") _ :: - super.validations - } - object bio extends TextareaField(this, 160) { - override def displayName = "Bio" - + + object lname extends StringField(this, 64) { override def validations = - valMaxLen(160, "Bio must be 160 characters or less") _ :: + valMaxLen(64, "First Name must be 64 characters or less") _ :: super.validations } - /* - * FieldContainers for various LiftScreeens. - */ - def accountScreenFields = new FieldContainer { - def allFields = List(username, email, locale, timezone) - } - - def profileScreenFields = new FieldContainer { - def allFields = List(name, location, bio) - } - - def registerScreenFields = new FieldContainer { - def allFields = List(username, email) - } - def whenCreated: DateTime = new DateTime(id.get.getTime) } @@ -140,7 +105,7 @@ object User extends User with ProtoAuthUserMeta[User] with RogueMetaRecord[User] val msgTxt = """ - |Someone requested a link to change your password on the %s website. + |Someone requested a link to log in to your %s account. | |If you did not request this, you can safely ignore it. It will expire 48 hours from the time this message was sent. | @@ -148,13 +113,14 @@ object User extends User with ProtoAuthUserMeta[User] with RogueMetaRecord[User] | |%s | - |Thanks, + |Kindest Regards, + | |%s """.format(siteName, token.url, sysUsername).stripMargin sendMail( From(MongoAuth.systemFancyEmail), - Subject("%s Password Help".format(siteName)), + Subject("%s Account: Login".format(siteName)), To(user.fancyEmail), PlainMailBodyType(msgTxt) ) @@ -190,15 +156,13 @@ case class LoginCredentials(email: String, isRememberMe: Boolean = false) object SystemUser { private val username = "pyd" - private val email = "" + private val email = "noreply@pydcoin.com" lazy val user: User = User.find("username", username) openOr { User.createRecord - .name("PYD") + .fname("PYD") .username(username) .email(email) - .locale("en_US") - .timezone("America/Chicago") .verified(true) .password("abc123", true) .save diff --git a/src/main/scala/com/pyd/snippet/AngularSnips.scala b/src/main/scala/com/pyd/snippet/AngularSnips.scala @@ -32,7 +32,10 @@ class AngularServerSide extends Factory { private val snips = new FactoryMaker[List[AngularSnippet]](List( new SearchedPostalSnip, new AtmApplicationSnip, - new NearAtmNotifySnip + new NearAtmNotifySnip, + new UserRegistrationSnip, + new UserLoginSnip, + new ForgotPasswordSnip )) {} def js: CssSel = diff --git a/src/main/scala/com/pyd/snippet/ForgotPasswordSnip.scala b/src/main/scala/com/pyd/snippet/ForgotPasswordSnip.scala @@ -0,0 +1,40 @@ +package com.pyd +package snippet + +import model._ +import net.liftweb._ +import common._ +import http.S +import json._ +import JsonAST.{JValue, JNull} + +class ForgotPasswordSnip extends AngularSnippet { + + def forgotPassword(model: JValue): JValue = { + (for { + JString(e) <- model \ "email" + } yield { + val email = e.toLowerCase.trim + + User.findByEmail(email) match { + case Full(user) => + User.sendLoginToken(user) + User.loginCredentials.remove() + + NgAlert.success( + <i class="fa-fw fa fa-thumbs-o-up"></i> ++ + <strong>An email has been sent to you with instructions for accessing your account.</strong>) + case _ => + NgAlert.danger( + <i class="fa-fw fa fa-thumbs-o-down"></i> ++ + <strong>Your email could not be found.</strong>, Nil) + } + }).headOption.getOrElse { + NgAlert.danger( + <i class="fa-fw fa fa-thumbs-o-down"></i> ++ + <strong>The information submitted could not be processed.</strong>, Nil) + } + } + + override def roundTrips = ("forgotPassword" -> forgotPassword _) +} +\ No newline at end of file diff --git a/src/main/scala/com/pyd/snippet/UserLoginSnip.scala b/src/main/scala/com/pyd/snippet/UserLoginSnip.scala @@ -0,0 +1,49 @@ +package com.pyd +package snippet + +import model._ +import net.liftweb._ +import common._ +import json._ +import JsonAST.{JValue, JString, JBool} +import net.liftmodules.mongoauth.model.ExtSession + +class UserLoginSnip extends AngularSnippet { + + def login(model: JValue): JValue = { + (for { + JString(e) <- model \ "email" + JString(password) <- model \ "password" + JBool(remember) <- model \ "remember" + } yield { + val email = e.toLowerCase.trim + User.loginCredentials(LoginCredentials(email, remember)) + + if(!(email.length > 0) || !(password.length > 0)) { + NgAlert.danger( + <i class="fa-fw fa fa-thumbs-o-down"></i> ++ + <strong>{"Your login information could not be processed."}</strong>, Nil) + } + else { + User.findByEmail(email) match { + case Full(user) if (user.password.isMatch(password)) => + User.logUserIn(user, remember) + if (remember) User.createExtSession(user.id.get) + else ExtSession.deleteExtCookie() + NgAlert.success + case _ => + NgAlert.danger( + <i class="fa-fw fa fa-thumbs-o-down"></i> ++ + <strong>{"Invalid Credentials: "}</strong> + <p>Assure your email and password are correct.</p>, Nil) + } + } + }).headOption.getOrElse { + NgAlert.danger( + <i class="fa-fw fa fa-thumbs-o-down"></i> ++ + <strong>{"Your login information could not be processed."}</strong>, Nil) + } + } + + override def roundTrips = ("loginUser" -> login _) +} +\ No newline at end of file diff --git a/src/main/scala/com/pyd/snippet/UserRegistrationSnip.scala b/src/main/scala/com/pyd/snippet/UserRegistrationSnip.scala @@ -0,0 +1,45 @@ +package com.pyd +package snippet + +import model._ +import field._ +import net.liftweb._ +import common._ +import json.JsonAST._ +import util._ +import Helpers._ +import http._ +import js._ +import JsCmds._ +import SHtml._ +import JE.JsVar + +class UserRegistrationSnip extends AngularSnippet { + + def register(model: JValue): JValue = { + val rec = User.createRecord + rec.setFieldsFromJValue(model) + rec.password(Helpers.randomString(20)) + rec.password.hashIt + + rec.validate match { + case Nil => + rec.save + User.sendLoginToken(rec) + + NgAlert.success( + <i class="fa-fw fa fa-thumbs-o-up"></i> ++ + <strong>{ s"Thank you for registering, ${rec.fname.get}." }</strong> ++ + <span>{" An email has been sent for you to log in."}</span> + ) + case errors => + NgAlert.danger( + <i class="fa-fw fa fa-thumbs-o-down"></i> ++ + <strong>{"Your application could not be processed."}</strong>, + errors + ) + } + } + + override def roundTrips = ("registerUser" -> register _) +} +\ No newline at end of file diff --git a/src/main/scala/com/pyd/snippet/UserScreens.scala b/src/main/scala/com/pyd/snippet/UserScreens.scala @@ -25,6 +25,7 @@ sealed trait BaseCurrentUserScreen extends BaseScreen { } } +/* object AccountScreen extends BaseCurrentUserScreen { addFields(() => userVar.is.accountScreenFields) @@ -33,12 +34,13 @@ object AccountScreen extends BaseCurrentUserScreen { S.notice("Account settings saved") } } +*/ sealed trait BasePasswordScreen { this: LiftScreen => def pwdName: String = "Password" - def pwdMinLength: Int = 6 + def pwdMinLength: Int = 8 def pwdMaxLength: Int = 32 val passwordField = password(pwdName, "", trim, @@ -71,6 +73,7 @@ object PasswordScreen extends BaseCurrentUserScreen with BasePasswordScreen { /* * Use for editing the currently logged in user only. */ +/* object ProfileScreen extends BaseCurrentUserScreen { def gravatarHtml = <span> @@ -94,17 +97,20 @@ object ProfileScreen extends BaseCurrentUserScreen { S.notice("Profile settings saved") } } +*/ // this is needed to keep these fields and the password fields in the proper order +/* trait BaseRegisterScreen extends BaseScreen { object userVar extends ScreenVar(User.regUser.is) addFields(() => userVar.is.registerScreenFields) } - +*/ /* * Use for creating a new user. */ +/* object RegisterScreen extends BaseRegisterScreen with BasePasswordScreen { override def validations = passwordsMustMatch _ :: super.validations @@ -126,3 +132,4 @@ object RegisterScreen extends BaseRegisterScreen with BasePasswordScreen { S.notice("Thanks for signing up!") } } +*/ +\ No newline at end of file diff --git a/src/main/scala/com/pyd/snippet/UserSnips.scala b/src/main/scala/com/pyd/snippet/UserSnips.scala @@ -36,36 +36,18 @@ sealed trait UserSnippet extends SnippetHelper with Loggable { snip(u)(html) }): NodeSeq - def header(xhtml: NodeSeq): NodeSeq = serve { user => - <div id="user-header"> - {gravatar(xhtml)} - <h3>{name(xhtml)}</h3> - </div> - } - - def gravatar(xhtml: NodeSeq): NodeSeq = { - val size = S.attr("size").map(toInt) openOr Gravatar.defaultSize.vend - - serve { user => - Gravatar.imgTag(user.email.get, size) - } - } - def username(xhtml: NodeSeq): NodeSeq = serve { user => Text(user.username.get) } def name(xhtml: NodeSeq): NodeSeq = serve { user => - if (user.name.get.length > 0) - Text("%s (%s)".format(user.name.get, user.username.get)) - else - Text(user.username.get) + val name = user.fname.get + " " + user.lname.get + if (name.length > 1) Text(name) + else Text(user.username.get) } - + def title(xhtml: NodeSeq): NodeSeq = serve { user => - <lift:head> - <title lift="Menu.title">{"PYD: %*% - "+user.username.get}</title> - </lift:head> + <title data-lift="Menu.title">PYD: %*% - {user.username.get}</title> } } @@ -89,10 +71,8 @@ object ProfileLocUser extends UserSnippet { NodeSeq.Empty "#id_avatar *" #> Gravatar.imgTag(user.email.get) & - "#id_name *" #> <h3>{user.name.get}</h3> & - "#id_location *" #> user.location.get & + "#id_name *" #> <h3>{user.fname.get}</h3> & "#id_whencreated" #> df.format(user.whenCreated.toDate).toString & - "#id_bio *" #> user.bio.get & "#id_editlink *" #> editLink } } diff --git a/src/main/webapp/app/App.js b/src/main/webapp/app/App.js @@ -37,7 +37,7 @@ app.controller('AlertCtrl', ['$scope', function($scope) { }; }]); -app.controller('FormCtrl', ['$scope', '$rootScope', function($scope, $rootScope) { +app.controller('FormCtrl', ['$scope', function($scope) { $scope.stateSuccess = function(el) { return "{'state-success':form."+el+".$valid && !form."+el+".$pristine}"; }; @@ -45,10 +45,20 @@ app.controller('FormCtrl', ['$scope', '$rootScope', function($scope, $rootScope) $scope.stateSuccessError = function(el) { return "{'state-error':form."+el+".$invalid && !form."+el+".$pristine,'state-success':form."+el+".$valid && !form."+el+".$pristine}"; }; - + + $scope.reset = function() { + $scope.model = {}; + $scope.form.$setPristine(); + }; +}]); + +app.controller('NearAtmNotifyCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { + $controller('FormCtrl', {$scope: $scope}); + $scope.zip_code_regex = ZIP_CODE_REGEXP; + $scope.save = function() { $scope.loading = true; - window.backend.save($scope.model).then(function(alert) { + window.backend.saveNearAtmNotify($scope.model).then(function(alert) { $scope.$apply(function() { $scope.loading = false; if(alert.msg_type === "success") { @@ -60,20 +70,34 @@ app.controller('FormCtrl', ['$scope', '$rootScope', function($scope, $rootScope) }); }); }; - - $scope.reset = function() { - $scope.model = {}; - $scope.form.$setPristine(); +}]); + +app.controller('AtmApplicationCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { + $controller('FormCtrl', {$scope: $scope}); + $scope.url_regex = UNSAFE_URL_REGEXP; + + $scope.save = function() { + $scope.loading = true; + window.backend.saveAtmApplication($scope.model).then(function(alert) { + $scope.$apply(function() { + $scope.loading = false; + if(alert.msg_type === "success") { + $rootScope.$broadcast('alertDialog', alert); + $scope.reset(); + } else { + $rootScope.$broadcast('alertDialog', alert); + } + }); + }); }; }]); -app.controller('NearAtmNotifyCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { - $controller('FormCtrl', {$scope: $scope, $rootScope: $rootScope}); - $scope.zip_code_regex = ZIP_CODE_REGEXP; +app.controller('UserRegistrationCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { + $controller('FormCtrl', {$scope: $scope}); $scope.save = function() { $scope.loading = true; - window.backend.saveNearAtmNotify($scope.model).then(function(alert) { + window.backend.registerUser($scope.model).then(function(alert) { $scope.$apply(function() { $scope.loading = false; if(alert.msg_type === "success") { @@ -87,13 +111,12 @@ app.controller('NearAtmNotifyCtrl', ['$scope', '$controller', '$rootScope', func }; }]); -app.controller('AtmApplicationCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { - $controller('FormCtrl', {$scope: $scope, $rootScope: $rootScope}); - $scope.url_regex = UNSAFE_URL_REGEXP; +app.controller('ForgotPasswordCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { + $controller('FormCtrl', {$scope: $scope}); $scope.save = function() { $scope.loading = true; - window.backend.saveAtmApplication($scope.model).then(function(alert) { + window.backend.forgotPassword($scope.model).then(function(alert) { $scope.$apply(function() { $scope.loading = false; if(alert.msg_type === "success") { @@ -107,6 +130,26 @@ app.controller('AtmApplicationCtrl', ['$scope', '$controller', '$rootScope', fun }; }]); +app.controller('UserLoginCtrl', ['$scope', '$controller', '$rootScope', function($scope, $controller, $rootScope) { + $controller('FormCtrl', {$scope: $scope}); + + $scope.model = {remember: false}; + + $scope.save = function() { + $scope.loading = true; + window.backend.loginUser($scope.model).then(function(alert) { + $scope.$apply(function() { + $scope.loading = false; + if(alert.msg_type === "success") { + window.location.href = "#/settings"; + } else { + $rootScope.$broadcast('alertDialog', alert); + } + }); + }); + }; +}]); + app.controller('FindAtmCtrl', ['$scope', '$state', '$rootScope', function($scope, $state, $rootScope) { $scope.zip_code_regex = ZIP_CODE_REGEXP; $scope.model = {}; diff --git a/src/main/webapp/apply-atm.html b/src/main/webapp/apply-atm.html @@ -61,8 +61,8 @@ We provide Bitcoin services that are safe and secure, easy to use and user-friendly. Sign up to buy and sell Bitcoin today. </p> - <p> - <a class="btn btn-primary btn-lg" role="button">Free Sign Up Today</a> + <p data-lift="test_cond.loggedOut"> + <a data-lift="Menus.item?name=Register" class="btn btn-primary btn-lg" role="button">Free Sign Up Today</a> </p> </div> </div> @@ -75,9 +75,11 @@ <h1 class="semibig-text text-white">Find ATM Locations</h1> </div> </div> - - <div class="padding-20"> - <span data-lift="embed?what=/templates-hidden/parts/find-atm-form"></span> + + <div class="row"> + <div class="col-xs-12 padding-20"> + <span data-lift="embed?what=/templates-hidden/parts/find-atm-form"></span> + </div> </div> <div class="row"> diff --git a/src/main/webapp/faqs.html b/src/main/webapp/faqs.html @@ -19,8 +19,8 @@ <p> PYD ATMs are currently available in the state of New Jersey. If you’re located in another state but would like to be - the next customer to benefit from our service, <a href="#">fill - out this form so we can know you’re interested</a>. + the next customer to benefit from our service, + <a data-lift="Menus.item?name=Locations">fill out this form so we can know you’re interested</a>. </p> </li> @@ -42,15 +42,15 @@ <p> At the moment, the maximum amount of money that can be taken out per transaction is 500 USD. If you would like to - transfer more money, you will have to <a href="#">sign up - and verify your identity</a>. + transfer more money, you will have to + <a data-lift="Menus.item?name=Register">sign up and verify your identity</a>. </p> </li> <li> <h3>How do I use the ATM?</h3> <p> - <a href="#">Follow our tutorial</a> so we can help you + <a data-lift="Menus.item?name=How To Use ATM">Follow our tutorial</a> so we can help you find an ATM, set up a wallet, instantly retrieve your bitcoin and find local merchants. </p> @@ -59,7 +59,7 @@ <li> <h3>What is Bitcoin?</h3> <p> - <a href="#">Discover Bitcoin’s purpose and usability</a> + <a data-lift="Menus.item?name=What Is Bitcoin">Discover Bitcoin’s purpose and usability</a> in our easy to follow tutorial. </p> </li> diff --git a/src/main/webapp/forgot-password.html b/src/main/webapp/forgot-password.html @@ -0,0 +1,16 @@ +<div data-lift="NgUIRouter.surround?withAjax=no-base-default&with=base-default&at=content"> + <div class="row margin-top-10"> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <div class="well"> + <span data-lift="embed?what=/templates-hidden/parts/forgot-password-form"></span> + </div> + </div> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <p class="lead"> + Did you forget your password? No problem, we'll email you with + instructions for accessing your account. + </p> + <p class="lead">Thank you for using our Bitcoin services.</p> + </div> + </div> +</div> +\ No newline at end of file diff --git a/src/main/webapp/how-to-use-atm.html b/src/main/webapp/how-to-use-atm.html @@ -0,0 +1,3 @@ +<div data-lift="NgUIRouter.surround?withAjax=no-base-default&with=base-default&at=content"> + <h1>coming soon</h1> +</div> +\ No newline at end of file diff --git a/src/main/webapp/less/custom.less b/src/main/webapp/less/custom.less @@ -91,6 +91,12 @@ .text-primary { color: @brand-primary; + a { + color: @brand-primary; + &:hover { + color: lighten(@brand-primary, 5%); + } + } } .text-secondary { diff --git a/src/main/webapp/less/overrides.less b/src/main/webapp/less/overrides.less @@ -78,6 +78,10 @@ body { background-color: @white; } } + + .btn-topbar { + margin-left: 5px; + } } @media (min-width: @screen-sm) { diff --git a/src/main/webapp/less/pages/index.less b/src/main/webapp/less/pages/index.less @@ -35,6 +35,10 @@ } } + // for jumbotron + #buysell-index { + padding-left: 0; + } #atm-benefits { diff --git a/src/main/webapp/login.html b/src/main/webapp/login.html @@ -1,67 +1,16 @@ -<div data-lift="surround?with=base-default;at=content"> - <div data-lift="Notices"></div> - <div class="row"> - <form data-lift="Form.ajax?class=form-horizontal"> - <span lift="UserLogin"> - <fieldset> - <div class="form-group"> - <label class="control-label" for="email"></label> - <div class="controls"> - <p><strong>Enter your email address:</strong></p> - <input class="form-control" maxlength="254" id="id_email" size="32" name="email" type="text" value="" /> - <span data-alertid="id_email_err" form-alert=""></span> - </div> - </div> - <div class="form-group"> - <label class="control-label"></label> - <div class="controls"> - <p><strong>Do you have a PYD password?</strong></p> - </div> - </div> - <div class="form-group"> - <label class="control-label"></label> - <div class="controls"> - <input id="no_password" /> <span>No, help me log in.</span> - </div> - </div> - <div class="form-group"> - <label class="control-label"></label> - <div class="controls"> - <input id="yes_password" /> <span>Yes, I have a <strong>password</strong>:</span> - </div> - </div> - <div class="form-group"> - <label class="control-label"></label> - <div class="controls"> - <input class="form-control" id="id_password" /> - <span data-alertid="id_password_err" form-alert=""></span> - </div> - </div> - <div class="form-group"> - <label class="control-label"></label> - <div class="controls"> - <label class="checkbox"> - <input class="xlarge" name="remember" /> - Remember me when I come back later. - </label> - </div> - </div> - </fieldset> - <div class="form-actions"> - <div class="actions"> - <input id="id_submit" /> - <input type="submit" class="btn btn-primary" value="Submit"></input> - <span lift="Menu.item?name=Home;a:class=btn btn-default">Cancel</span> - </div> - </div> - </span> - </form> - </div> - <tail> - <script> - $(document).ready(function() { - App.views.user.Login.init(); - }); - </script> - </tail> -</div> +<div data-lift="NgUIRouter.surround?withAjax=no-base-default&with=base-default&at=content"> + <div class="row margin-top-10"> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <div class="well"> + <span data-lift="embed?what=/templates-hidden/parts/user-login-form"></span> + </div> + </div> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <p class="lead"> + Welcome Back Bitcoin User! Remember, if you have any questions, be sure to check + our <a data-lift="Menus.item?name=FAQs">frequently asked questions</a> and contact us if you need any other help. + </p> + <p class="lead">Thank you for using our Bitcoin services.</p> + </div> + </div> +</div> +\ No newline at end of file diff --git a/src/main/webapp/register.html b/src/main/webapp/register.html @@ -1,4 +1,16 @@ -<div data-lift="surround?with=base-default;at=content"> - <div data-lift="Notices"></div> - <div data-lift="RegisterScreen"></div> +<div data-lift="NgUIRouter.surround?withAjax=no-base-default&with=base-default&at=content"> + <div class="row margin-top-10"> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <div class="well"> + <span data-lift="embed?what=/templates-hidden/parts/user-registration-form"></span> + </div> + </div> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <p class="lead"> + We believe Bitcoin is the future of all cash payments that will create healthier + businesses and happier customers. + </p> + <p class="lead">Thank you for being part of that future.</p> + </div> + </div> </div> diff --git a/src/main/webapp/settings.html b/src/main/webapp/settings.html @@ -0,0 +1,6 @@ +<div data-lift="NgUIRouter.surround?withAjax=no-base-settings-wrap&with=settings-wrap&at=content"> + <div class="padding-20"> + <h1>You are logged in</h1> + </div> + <div ui-view="viewA"></div> +</div> +\ No newline at end of file diff --git a/src/main/webapp/settings.password.html b/src/main/webapp/settings.password.html @@ -0,0 +1 @@ +<span data-lift="embed?what=/settings/password"></span> +\ No newline at end of file diff --git a/src/main/webapp/settings/password.html b/src/main/webapp/settings/password.html @@ -1,3 +1,14 @@ -<div data-lift="surround?with=settings-wrap;at=content"> - <div data-lift="PasswordScreen?ajax=true"></div> +<div data-lift="NgUIRouter.surround?withAjax=no-base-settings-wrap&with=settings-wrap&at=content"> + <div class="row margin-top-10"> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <div class="well"> + <div data-lift="PasswordScreen?ajax=true"></div> + </div> + </div> + <div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-0"> + <p class="lead"> + A strong password is needed to keep your information safe and secure. + </p> + </div> + </div> </div> diff --git a/src/main/webapp/templates-hidden/base-wrap.html b/src/main/webapp/templates-hidden/base-wrap.html @@ -40,11 +40,18 @@ <ul class="nav navbar-nav" data-lift="Menus.group?group=topbar"></ul> </div> </div> - <div class="row bottom-row hidden-sm hidden-xs"> - <div class="col-md-12 text-secondary"> + <div class="row bottom-row"> + <div class="col-xs-12"> + <span data-lift="test_cond.loggedOut"> + <a data-lift="Menus.item?name=Login" class="btn btn-default btn-sm btn-topbar pull-right" role="button">Log In</a> + <a data-lift="Menus.item?name=Register" class="btn btn-primary btn-topbar btn-sm text-white pull-right" role="button">Sign Up</a> + </span> + <span data-lift="test_cond.loggedIn"> + <a href="/logout" class="btn btn-default btn-sm btn-topbar pull-right" role="button">Log Out</a> + </span> <a href="https://plus.google.com/+Pydcoin1" alt="Our Google+ Page" target="_blank"><i class="fa fa-google-plus fa-2x pull-right"></i></a> - <a href="#" alt="Our Twitter Page" target="_blank"><i class="fa fa-twitter fa-2x pull-right"></i></a> - <a href="https://www.facebook.com/PYDINC" alt="Our Facebook Page" target="_blank"><i class="fa fa-facebook fa-2x pull-right"></i></a> + <a href="https://twitter.com/pydcoin" alt="Our Twitter Page" target="_blank"><i class="fa fa-twitter fa-2x pull-right"></i></a> + <a href="https://www.facebook.com/PYDINC" alt="Our Facebook Page" target="_blank"><i class="fa fa-facebook fa-2x pull-right"></i></a> </div> </div> diff --git a/src/main/webapp/templates-hidden/no-base-settings-wrap.html b/src/main/webapp/templates-hidden/no-base-settings-wrap.html @@ -0,0 +1,30 @@ +<div data-lift="surround?with=empty;at=content"> + <lift:head> + <span data-lift="CurrentUser.title"></span> + </lift:head> + <div class="row"> + <div class="col-xs-10 col-xs-offset-1"> + <div class="main-content"> + <div class="page-header"> + <div id="user-header"> + <h3> + <span lift="CurrentUser.name"></span> + </h3> + </div> + <div style="display:none; float:right;" id="ajax-spinner"> + <i class="fa fa-spinner fa-spin"></i> + </div> + </div> + <div class="row"> + <div class="settings-secondary"> + <ul data-lift="Menus.group?group=settings" + class="nav nav-pills"></ul> + </div> + <div class="settings-main"> + <div id="content"></div> + </div> + </div> + </div> + </div> + </div> +</div> diff --git a/src/main/webapp/templates-hidden/parts/find-atm-form.html b/src/main/webapp/templates-hidden/parts/find-atm-form.html @@ -1,6 +1,6 @@ <div ng-controller="FindAtmCtrl" ng-cloak> <form name="form" class="smart-form client-form" ng-submit="search()" novalidate> - <div class="row"> + <div class="row" style="margin:0"> <div class="col-xs-5"> <section> <label class="input"> @@ -12,7 +12,7 @@ <button type="submit" class="btn btn-primary btn-sm btn-block" ng-disabled="form.$invalid" disabler ng-model="loading">Find ATM</button> </div> </div> - <div class="row"> + <div class="row" style="margin:0"> <div class="col-xs-12" style="margin-top:-10px;"> <span class="ng-hide help-block text-white" ng-show="form.postal.$error.pattern">Zip code is not valid</span> <span class="help-block text-white">Find nearby ATMs by searching with your zip code.</span> diff --git a/src/main/webapp/templates-hidden/parts/forgot-password-form.html b/src/main/webapp/templates-hidden/parts/forgot-password-form.html @@ -0,0 +1,17 @@ +<div ng-controller="ForgotPasswordCtrl" ng-cloak> + <form name="form" class="smart-form client-form" ng-submit="save()" novalidate> + <fieldset> + <section> + <label>E-mail</label> + <label class="input"> + <i class="icon-append fa fa-envelope-o"></i> + <input name="email" ng-model="model.email" type="email" required> + </label> + </section> + </fieldset> + + <footer> + <button type="submit" class="btn btn-primary" ng-disabled="form.$invalid" disabler ng-model="loading">Reset Password</button> + </footer> + </form> +</div> +\ No newline at end of file diff --git a/src/main/webapp/templates-hidden/parts/user-login-form.html b/src/main/webapp/templates-hidden/parts/user-login-form.html @@ -0,0 +1,36 @@ +<div ng-controller="UserLoginCtrl" ng-cloak> + <form name="form" class="smart-form client-form" ng-submit="save()" novalidate> + <fieldset> + <section> + <label>E-mail</label> + <label class="input"> + <i class="icon-append fa fa-envelope-o"></i> + <input name="email" ng-model="model.email" type="email" required> + </label> + </section> + + <section> + <label>Password</label> + <label class="input"> + <i class="icon-append fa fa-key"></i> + <input name="password" ng-model="model.password" type="password" required> + </label> + <div class="note"> + <a data-lift="Menus.item?name=Forgot Password">Forgot Password?</a> + </div> + </section> + + <section> + <label class="checkbox"> + <input name="remember" ng-model="model.remember" type="checkbox"></input> + <i></i> + Stay signed in + </label> + </section> + </fieldset> + + <footer> + <button type="submit" class="btn btn-primary" ng-disabled="form.$invalid" disabler ng-model="loading"><i class="fa fa-lock fa-fw"></i>Log In</button> + </footer> + </form> +</div> +\ No newline at end of file diff --git a/src/main/webapp/templates-hidden/parts/user-registration-form.html b/src/main/webapp/templates-hidden/parts/user-registration-form.html @@ -0,0 +1,36 @@ +<div ng-controller="UserRegistrationCtrl" ng-cloak> + <form name="form" class="smart-form client-form" ng-submit="save()" novalidate> + <fieldset> + <div class="row"> + <section class="col col-6"> + <label class="input" ng-class="{{ stateSuccessError('fname') }}"> + <input name="fname" ng-model="model.fname" placeholder="First name" type="text" autofocus required> + </label> + </section> + <section class="col col-6"> + <label class="input" ng-class="{{ stateSuccessError('lname') }}"> + <input name="lname" ng-model="model.lname" placeholder="Last name" type="text" required> + </label> + </section> + </div> + + <section> + <label class="input" ng-class="{{ stateSuccessError('email') }}"> + <i class="icon-append fa fa-envelope-o"></i> + <input name="email" ng-model="model.email" placeholder="E-Mail" type="email" required> + </label> + </section> + + <section> + <label class="input" ng-class="{{ stateSuccessError('username') }}"> + <i class="icon-append fa fa-user"></i> + <input name="username" ng-model="model.username" placeholder="Username" type="text" required> + </label> + </section> + </fieldset> + + <footer> + <button type="submit" class="btn btn-primary" ng-disabled="form.$invalid" disabler ng-model="loading"><i class="fa fa-lock fa-fw"></i>Register</button> + </footer> + </form> +</div> +\ No newline at end of file diff --git a/src/main/webapp/templates-hidden/settings-wrap.html b/src/main/webapp/templates-hidden/settings-wrap.html @@ -1,21 +1,31 @@ <div data-lift="surround?with=base-wrap;at=content"> - <span data-lift="CurrentUser.title"></span> - <div class="main-content"> - <div class="page-header"> - <span lift="CurrentUser.header"></span> - <div id="ajax-spinner" class="pull-right" style="display: none;"> - <img src="/img/spinner.gif" width="16" height="16"/> - </div> - </div> - <div class="row"> - <div class="settings-main"> - <div data-lift="Notices"></div> - <div id="content"></div> - </div> - <div class="settings-secondary"> - <ul data-lift="Menus.group?group=settings" class="nav nav-pills nav-stacked"></ul> - </div> - </div> + <lift:head> + <span data-lift="CurrentUser.title"></span> + </lift:head> + <div class="row"> + <div class="col-xs-10 col-xs-offset-1"> + <div class="main-content"> + <div class="page-header"> + <div id="user-header"> + <h3> + <span lift="CurrentUser.name"></span> + </h3> + </div> + <div style="display:none; float:right;" id="ajax-spinner"> + <i class="fa fa-spinner fa-spin"></i> + </div> + </div> + <div class="row"> + <div class="settings-secondary"> + <ul data-lift="Menus.group?group=settings" + class="nav nav-pills"></ul> + </div> + <div class="settings-main"> + <div id="content"></div> + </div> + </div> + </div> + </div> </div> <span data-lift="embed?what=/templates-hidden/parts/footer"></span> -</div> +</div> +\ No newline at end of file diff --git a/src/main/webapp/what-is-bitcoin.html b/src/main/webapp/what-is-bitcoin.html @@ -0,0 +1,3 @@ +<div data-lift="NgUIRouter.surround?withAjax=no-base-default&with=base-default&at=content"> + <h1>coming soon</h1> +</div> +\ No newline at end of file diff --git a/src/main/webapp/whatisbitcoin.html b/src/main/webapp/whatisbitcoin.html @@ -1,3 +0,0 @@ -<div data-lift="NgUIRouter.surround?withAjax=no-base-default&with=base-default&at=content"> - content for what is bitcoin goes here -</div> -\ No newline at end of file