scala-news-reader
rss/atom news reader in scala
git clone https://9o.is/git/scala-news-reader.git
commit b4559315b30f1db1c7ffa61ee8297239a6e42f5c parent 4bb6c0af03145bdac77a495a91911703381a87f3 Author: Jul <jul@9o.is> Date: Sat, 10 Aug 2013 23:30:54 -0400 moved youtube video to blog writer Diffstat:
17 files changed, 189 insertions(+), 138 deletions(-)
diff --git a/src/main/scala/com/joereader/config/Site.scala b/src/main/scala/com/joereader/config/Site.scala @@ -89,14 +89,6 @@ object Site extends Locs { lazy val userProfileLoc = userProfileParamMenu.toLoc - /* /{user-name}/preview */ - private val userPreviewParamMenu = Menu.param[User]( - "User Preview", "User Preview", - User.findByUsername, _.username.get) / * / "preview" >> - TemplateBox(() => Templates("preview" :: Nil)) - - lazy val userPreviewLoc = userPreviewParamMenu.toLoc - /* /{user-name}/following */ private val userFollowingParamMenu = Menu.param[User]( "User Following", "User Following", @@ -119,6 +111,14 @@ object Site extends Locs { TemplateBox(() => Templates("blogwriter" :: Nil)) lazy val blogWriterProfileLoc = blogWriterProfileParamMenu.toLoc + + /* /blog/{blog-name}/{blog-writer-name}/preview */ + private val blogWriterPreviewParamMenu = Menu.params[BlogWriterUser]( + "Blog Writer Preview", "Blog Writer Preview", + BlogWriterUser.parse, BlogWriterUser.encode) / "blog" / * / * / "preview" >> + TemplateBox(() => Templates("preview" :: Nil)) + + lazy val blogWriterPreviewLoc = blogWriterPreviewParamMenu.toLoc /* /settings/blog/{blog-name} */ private val blogSettingsParamMenu = Menu.param[Blog]( @@ -138,9 +138,9 @@ object Site extends Locs { private def menus = List( blogSettingsParamMenu, - userPreviewParamMenu, userFollowingParamMenu, blogWriterProfileParamMenu, + blogWriterPreviewParamMenu, userProfileParamMenu, blogProfileParamMenu, categoriesParamMenu, diff --git a/src/main/scala/com/joereader/model/BlogWriter.scala b/src/main/scala/com/joereader/model/BlogWriter.scala @@ -19,6 +19,9 @@ class BlogWriter private() extends BsonRecord[BlogWriter] { // if email is present, assume he was invited object email extends StringField(this, 255) + + // youtube introduction video id + object introVid extends StringField(this, 20) // hex value of primary color object color extends StringField(this, 255) { diff --git a/src/main/scala/com/joereader/model/BlogWriterUser.scala b/src/main/scala/com/joereader/model/BlogWriterUser.scala @@ -190,10 +190,12 @@ object BlogWriterUser { // parse and encode functions are used by SiteMap def parse(path: List[String]): Box[BlogWriterUser] = { val blog = Blog.findByBlogName(path(0)) - val blogWriter = blog.map(_.writers.findStr(path(1).replace('+', ' '))).openOr(Empty) - - for (b <- blog; bw <- blogWriter) - yield new BlogWriterUser(b, bw) + val writer = blog.flatMap(_.writers.findStr(path(1). + replace('+', ' '))) + val user = writer.flatMap(_.user.obj) + + for (b <- blog; bw <- writer) + yield new BlogWriterUser(user, blog, writer) } def encode(bwu: BlogWriterUser): List[String] = List( diff --git a/src/main/scala/com/joereader/model/User.scala b/src/main/scala/com/joereader/model/User.scala @@ -35,8 +35,6 @@ class User private () extends ProtoAuthUser[User] with ObjectIdPk[User] { object bgImg extends StringField(this, 50) - object introVid extends StringField(this, 20) - object otherVid extends MongoListField[User, String](this) with MongoListFieldExtra[User, String] object blogs extends ObjectIdRefListField(this, Blog) with ObjectIdRefListFieldExtra[User, Blog] diff --git a/src/main/scala/com/joereader/snippet/BlogSnips.scala b/src/main/scala/com/joereader/snippet/BlogSnips.scala @@ -3,8 +3,10 @@ package com.joereader.snippet import scala.xml._ import net.liftweb._ +import http._ import common._ import util._ +import Helpers._ import net.liftmodules.extras.SnippetHelper @@ -84,6 +86,7 @@ trait BlogWriterUserSnip extends BlogWriterSnip { case Full(Some(t)) => Full(t) case _ => Empty } + } object ProfileLocBlog extends BlogSnipEdit with BlogWritersSnipView { @@ -98,6 +101,24 @@ object ProfileLocBlogWriter extends BlogWriterSnipView with FollowSnip { protected def bwu = Site.blogWriterProfileLoc.currentValue } +object PreviewLocBlogWriter extends BlogWriterSnipView with FollowSnip { + override def bwu = Site.blogWriterPreviewLoc.currentValue + + def about: NodeSeq = for (bwu <- bwu; user <- bwu.user) yield { + Text(user.about.get) + } + + def navbar = + "#profile-btn [href]" #> (for (bwu <- bwu; user <- bwu.user) yield { + Site.userProfileLoc.calcHref(user) + }) & + "#back-btn [href]" #> S.referer + + def redirect(html: NodeSeq) = + if (bwu.exists(_.user.isDefined)) html + else S.redirectTo(Site.notFound.url) +} + /* * (hack) Used during sign up wizard to handle the blog-name text input * field which rests outside of blog-profile.html diff --git a/src/main/scala/com/joereader/snippet/BlogWriterColorSnip.scala b/src/main/scala/com/joereader/snippet/BlogWriterColorSnip.scala @@ -19,7 +19,7 @@ import scala.xml._ */ object BlogWriterColorSnip extends BlogWriterSnip { - // in the meantime, color snippet is located beside categories snippet. + // color snippet is located beside categories snippet. protected def blog = Site.categoriesLoc.currentValue override def blogWriter: Box[BlogWriter] = diff --git a/src/main/scala/com/joereader/snippet/BlogWriterSnipView.scala b/src/main/scala/com/joereader/snippet/BlogWriterSnipView.scala @@ -26,7 +26,7 @@ trait BlogWriterSnipView extends BlogWriterUserSnip with BackgroundSnip { def url(html: NodeSeq): NodeSeq = serve(html) { (blog, blogWriter) => "a *" #> blog.urlHtml.get & - "a [href]" #> Site.blogProfileLoc.calcHref(blog) + "a [href]" #> Site.blogProfileLoc.calcHref(blog) }(test = true, NodeSeq.Empty) def img(html: NodeSeq): NodeSeq = serve(html) { @@ -39,6 +39,12 @@ trait BlogWriterSnipView extends BlogWriterUserSnip with BackgroundSnip { def bgImg(html: NodeSeq): NodeSeq = serve(html) { (b, bw) => "* [id]" #> imgBgId & - "* [src]" #> defaultBackground + "* [src]" #> defaultBackground + }(test = true, NodeSeq.Empty) + + def introVidUrl(html: NodeSeq) = serve(html) { + (b, bw) => + "* [src]" #> ("http://www.youtube.com/embed/" + + bw.introVid.get + "?autoplay=1&iv_load_policy=3&wmode=opaque") }(test = true, NodeSeq.Empty) } diff --git a/src/main/scala/com/joereader/snippet/BlogWriterVideoSnip.scala b/src/main/scala/com/joereader/snippet/BlogWriterVideoSnip.scala @@ -0,0 +1,59 @@ +package com.joereader +package snippet + +import lib._ +import config._ +import model._ + +import net.liftweb._ +import common._ +import http._ +import SHtml._ +import util.Helpers._ +import js._ +import JsCmds._ +import scala.xml._ + +/** + * Snippet to change blog writer's youtube video. + */ +object BlogWriterVideoSnip extends BlogWriterSnip { + + // video snippet is located beside categories snippet. + override def blog = Site.categoriesLoc.currentValue + + override def blogWriter: Box[BlogWriter] = + for { + blog <- blog + user <- User.currentUser + blogWriter <- blog.writers.find(user) + } yield blogWriter + + private def test: Boolean = blogWriter.exists( + bw => User.currentUser.exists(_.id.get == bw.user.get)) + + def render(html: NodeSeq) = serve(html) { + (blog, blogWriter) => + + def check(id: String): JsCmd = { + import VideoService._, dispatch._ + if (id.isEmpty) Noop + else { + Youtube.video.duration(id)() match { + case Left(msg) => S.error(msg) + case Right(time) => + if (time <= 30) { + blogWriter.introVid(id) + blog.save + Noop + } else + S.error("Video duration must be " + + "30 seconds or less") + } + } + } + "*" #> ajaxText(blogWriter.introVid.get, { + s => check(s); Noop + }) + }(test, NodeSeq.Empty) +} +\ No newline at end of file diff --git a/src/main/scala/com/joereader/snippet/UserReaderSnipEdit.scala b/src/main/scala/com/joereader/snippet/UserReaderSnipEdit.scala @@ -94,7 +94,12 @@ trait UserReaderSnipEdit extends UserReaderSnipView with UserPassword { def surround = "#profile-wrap [class+]" #> (if (test) "editable-page" else "") - def previewPage(html: NodeSeq) = serve(html)(user => - "* [href]" #> Site.userPreviewLoc.calcHref(user) - )(!test && user.exists(_.isWriter), html) + def previewPage(html: NodeSeq) = serve(html) { + user => + val blog = Blog.find(user.blogs.get.head) + val bw = blog.flatMap(_.writers.find(user)) + val bwu = for(blog <- blog; bw <- bw) + yield new BlogWriterUser(user, blog, bw) + "* [href]" #> bwu.map(Site.blogWriterPreviewLoc.calcHref).openOr("") + }(!test && user.exists(_.isWriter), html) } \ No newline at end of file diff --git a/src/main/scala/com/joereader/snippet/UserReaderSnipView.scala b/src/main/scala/com/joereader/snippet/UserReaderSnipView.scala @@ -23,9 +23,4 @@ trait UserReaderSnipView extends UserSnip { "* [class+]" #> "profile-img-size" & "* [src]" #> bwu.image }(test = true, NodeSeq.Empty) - - def introVidUrl(html: NodeSeq) = serve(html)(user => - "* [src]" #> ("http://www.youtube.com/embed/" + - user.introVid.is + "?autoplay=1&iv_load_policy=3") - )(test = true, NodeSeq.Empty) } diff --git a/src/main/scala/com/joereader/snippet/UserSnips.scala b/src/main/scala/com/joereader/snippet/UserSnips.scala @@ -54,19 +54,6 @@ object ProfileLocWriter extends UserWriterSnipEdit { protected def user = Site.userProfileLoc.currentValue } -object PreviewLocReader extends UserReaderSnipView with FollowSnip { - override protected def user = Site.userPreviewLoc.currentValue - protected def bwu = user.map(u => new BlogWriterUser(u)) -} - -object PreviewLocWriter extends UserWriterSnipView { - protected def user = Site.userPreviewLoc.currentValue - - def redirect(html: NodeSeq) = - if (user.exists(_.isWriter)) html - else S.redirectTo(Site.notFound.url) -} - /* * Go to a user's (must be a writer) following page * to view who they follows. diff --git a/src/main/scala/com/joereader/snippet/UserWriterSnipEdit.scala b/src/main/scala/com/joereader/snippet/UserWriterSnipEdit.scala @@ -60,33 +60,8 @@ trait UserWriterSnipEdit extends UserWriterSnipView with BackgroundSnip { }) }(test, super.username) - def introVid(html: NodeSeq) = serve(html) { - user => - - def check(id: String): JsCmd = { - import VideoService._, dispatch._ - if (id.isEmpty) Noop - else { - Youtube.video.duration(id)() match { - case Left(msg) => S.error(msg) - case Right(time) => - if (time <= 30) { - user.introVid(id).update - Noop - } else - S.error("Video duration must be " + - "30 seconds or less") - } - } - } - "*" #> ajaxText(user.introVid.is, { - s => check(s); Noop - }) - }(test, NodeSeq.Empty) - def otherVid(html: NodeSeq) = serve(html) { user => - var vids: List[String] = user.otherVid.is def addVideo(in: String): JsCmd = { diff --git a/src/main/webapp/preview.html b/src/main/webapp/preview.html @@ -1,33 +1,44 @@ <div data-lift="surround?with=base-wrap;at=content"> <div id="bg"> - <img data-lift="PreviewLocWriter.bgImg"/> + <img data-lift="PreviewLocBlogWriter.bgImg"/> </div> + + <div data-lift="PreviewLocBlogWriter.navbar" class="navbar navbar-fixed-top" style="margin:0"> + <div class="navbar-inner"> + <div class="boxed-articles"> + <div class="navbar-form pull-left"> + <a herf="" id="back-btn" class="btn btn-primary">Go Back</a> + <a herf="" id="profile-btn" class="btn nolink-decoration">Visit Profile</a> + </div> + </div> + </div> + </div> <div class="boxed-articles"> <div id="profile"> - <iframe data-lift="PreviewLocReader.introVidUrl" width="800" height="450" frameborder="0" allowfullscreen></iframe> + <iframe data-lift="PreviewLocBlogWriter.introVidUrl" width="800" height="450" frameborder="0" allowfullscreen></iframe> <div id="profile-inner"> <div class="row-fluid"> <div id="user-name" class="span12 text-center article up-separate" style="background: transparent; padding-top:0; padding-bottom:0; margin-bottom:0"> - <span data-lift="PreviewLocReader.name"></span> + <span data-lift="PreviewLocBlogWriter.name"></span> </div> </div> <div class="row-fluid"> <div id="user-about" class="span12 text-center article up-separate" style="background: transparent; padding-top:0; padding-bottom:0; margin-bottom:0"> - <span data-lift="PreviewLocWriter.about"></span> + <span data-lift="PreviewLocBlogWriter.about"></span> </div> </div> <div class="row-fluid"> <div class="span12 text-center"> - <span data-lift="PreviewLocReader.followButton"></span> + <span data-lift="PreviewLocBlogWriter.followButton"></span> <div id="following-amount"> - <span data-lift="PreviewLocReader.followersAmount"></span> + <span data-lift="PreviewLocBlogWriter.followersAmount"></span> </div> </div> </div> @@ -37,6 +48,6 @@ </div> </div> - <div data-lift="PreviewLocWriter.redirect"></div> + <div data-lift="PreviewLocBlogWriter.redirect"></div> </div> \ No newline at end of file diff --git a/src/main/webapp/settings/account.html b/src/main/webapp/settings/account.html @@ -4,43 +4,34 @@ <span data-lift="CurrentReader.email"></span> <div data-lift="CurrentWriter.showIfWriter"> - <br><br> + <br> <label>Username</label> <span data-lift="CurrentWriter.username"></span> <span class="help-inline"><small>www.readmeans.com/{username}</small></span> </div> - <br><br> + <br> <label>Name</label> <span data-lift="CurrentReader.name"></span> <div data-lift="CurrentWriter.showIfWriter"> - <br><br> + <br> <label>About</label> <span data-lift="CurrentWriter.about" class="user-about-settings"></span> <style>.user-about-settings{width:300px; height:150px;}</style> </div> - <br><br> + <br> <h3>Media</h3> - <div id="intro-vid" class="well well-small" data-lift="CurrentWriter.showIfWriter"> - <p>Enter a Youtube video id to influence people to follow you. Must be 30 seconds or less:</p> - <span> - <span data-lift="CurrentWriter.introVid"></span> - <img class="yt" src="/img/yt95x40.png"> - <span class="help-inline"><small>www.youtube.com/watch?v=<span style="color:red">0Bmhjf0rKe8</span></small></span> - </span> - </div> - <label>Upload a new profile image</label> <span data-lift="CurrentReader.uploadImg" id="pic-fileupload-outer"></span> <div data-lift="CurrentWriter.showIfWriter"> - <br><br> + <br> <label>Upload a new background image</label> <div data-lift="CurrentWriter.uploadBgImg" id="bg-fileupload-outer"> @@ -56,13 +47,13 @@ </div> <div data-lift="CurrentWriter.showIfWriter"> - <br><br> + <br> <label>Enter other Youtube video ids that shows your tone of voice and personality: interviews, speeches, etc.</label> <span data-lift="CurrentWriter.otherVid"></span> <span class="help-block"><small>Separate by comma</small></span> </div> - <br><br> + <br> <h3>Reset Password</h3> diff --git a/src/main/webapp/signup/categories.html b/src/main/webapp/signup/categories.html @@ -2,7 +2,6 @@ <div data-lift="SignUp.setupCategories"> <h4>Congratulations! You have verified yourself as a writer for <span id="blogname"></span></h4> - <p>Select a few categories that describe what you write for this blog.</p> <hr> <div id="categories-area"></div> diff --git a/src/main/webapp/templates-hidden/parts/categories.html b/src/main/webapp/templates-hidden/parts/categories.html @@ -1,45 +1,52 @@ <div> - <div data-lift="BlogWriterCategoriesSnip" class="row-fluid"> - - <div class="span8"> - <form data-lift="form.ajax?class=input-append"> - <input id="category-input" placeholder="Submit New Category"> - <input id="category-add" class="btn btn-primary"> - </form> - <br> - <div id="chosen-categories" class="well"> - <div class="inner"></div> - </div> - </div> - - <div class="span4"> - <h3>Advice</h3> - People will find you by category. Choose wisely. - </div> - - </div> - - <div data-lift="BlogWriterColorSnip" class="row-fluid"> - <div id="colorpicker-wrapper" class="span12"> - <h3>Primary Color</h3> - <p>Click box to choose a color</p> - <div class="preview"></div> - - <form data-lift="form.ajax?class=input-append"> - <input id="hexVal" placeholder="hex color value" /> - <input id="hexVal-add" class="btn btn-primary"> - </form> - - <span class="help-block"><small>Choose a color that represents your category or blog theme. - While people read your articles, they will see this color. - Color influences the reader's mood so choose wisely!</small></span> - - <div class="colorpicker" style="display:none"> - <canvas id="picker" width="300" height="300"></canvas> - </div> - - </div> - </div> + <div data-lift="BlogWriterCategoriesSnip"> + <h4>Choose Categories</h4> + <form data-lift="form.ajax?class=input-append"> + <input id="category-input" placeholder="Submit New Category"> + <input id="category-add" class="btn btn-primary"> + </form> + + <div id="chosen-categories" class="well"> + <span class="help-block"> <small> It's important you + choose the appropriate categories that you write for this blog. + Our search engine and recommendations are based on categories so + choose wisely if you want to be found. </small> + </span> + <div class="inner"></div> + </div> + </div> + + <h4>Introduction Video</h4> + <div id="intro-vid" class="well"> + <span class="help-block"> Enter a Youtube video id to influence + people to follow you. If you don't provide one, you will lose search + ranking. Must be 30 seconds or less: </span> <span> <span + data-lift="BlogWriterVideoSnip"></span> <img class="yt" + src="/img/yt95x40.png"> <span class="help-inline"><small>www.youtube.com/watch?v=<span + style="color: red">0Bmhjf0rKe8</span></small></span> + </span> + </div> + + <h4>Primary Color</h4> + <div data-lift="BlogWriterColorSnip"> + <div id="colorpicker-wrapper" class="well"> + <span class="help-block"><small>Choose a color that + represents your category or blog theme. While people read your + articles, they will see this color. Color influences the reader's + mood so choose wisely!</small></span> + + <div class="preview"></div> + <form data-lift="form.ajax?class=input-append"> + <input id="hexVal" placeholder="hex color value" /> <input + id="hexVal-add" class="btn btn-primary"> + </form> + + <div class="colorpicker" style="display: none"> + <canvas id="picker" width="300" height="300"></canvas> + </div> + + </div> + </div> </div> \ No newline at end of file diff --git a/src/main/webapp/templates-hidden/parts/user-profile.html b/src/main/webapp/templates-hidden/parts/user-profile.html @@ -78,15 +78,6 @@ </div> <div class="modal-body"> - <div id="intro-vid" class="well well-small"> - <p>Enter a Youtube video id to influence people to follow you. Must be 30 seconds or less:</p> - <span> - <span data-lift="ProfileLocWriter.introVid"></span> - <img class="yt" src="/img/yt95x40.png"> - <span class="help-inline"><small>www.youtube.com/watch?v=<span style="color:red">0Bmhjf0rKe8</span></small></span> - </span> - </div> - <p>Upload a new background image:</p> <div data-lift="ProfileLocWriter.uploadBgImg" id="bg-fileupload-outer">