· 8 years ago · Mar 11, 2018, 12:22 PM
1package liftblog.snippet
2
3import net.liftweb.http._
4import net.liftweb.mapper._
5import net.liftweb.util._
6import net.liftweb.util.Helpers._
7import net.liftweb.sitemap._
8import net.liftweb.sitemap.Loc._
9import scala.xml._
10import liftblog.model._
11
12abstract class BaseUIObject[ModelType <: KeyedMapper[Long, ModelType]] {
13 def key: String;
14
15 def id(x: ModelType): String;
16 def title(x: ModelType): String;
17
18 def urlPrefix = "/" + key + "/"
19 def singular = key.replace('_', ' ')
20 def plural = singular + "s"
21
22 def link(x: ModelType) = <a href={url(x)}>{title(x)}</a>
23
24 def listLine(x: ModelType) =
25 <tr><td>{link(x)}</td>{x.htmlLine}</tr>
26 def listLines(xs: Seq[ModelType]) =
27 <table>{xs.flatMap(listLine _)}</table>
28
29 def url(x: ModelType): String = url(x, Empty)
30 def url(x: ModelType, action: Can[String]): String = {
31 val main = urlPrefix + action.map(_ + "/").openOr("") + id(x)
32 val slug = title(x).take(20).toString.replaceAll("[^A-Za-z0-9 ]", "").replace(" ", "-")
33 if (slug.length > 0)
34 main + "-" + slug
35 else
36 main
37 }
38
39 def form(title: String, defaultVal: => ModelType): NodeSeq = {
40 object current extends RequestVar[ModelType](Empty)
41
42 current.openOr(defaultVal).toForm(
43 Full(title),
44 (x: ModelType) => {
45 x.validate match {
46 case Nil => x.save; S.redirectTo(url(x))
47 case xs => S.error(xs); current(x)
48 }
49 }
50 )
51 }
52
53 def locs =
54 Loc("list", urlPrefix + "list", "list " + plural) ::
55 Loc("create", urlPrefix + "create", "create " + singular, Hidden) ::
56 Loc("edit", urlPrefix + "edit", "edit " + singular, Hidden) ::
57 Loc("show", urlPrefix + "show", "show " + singular, Hidden) :: Nil
58
59 def rewritePF: LiftServlet.RewritePf = {
60 case RewriteRequest(_, path @ ParsePath(head :: id :: action :: _,_,_), _,_) if (head == key && idParseable(id)) =>
61 RewriteResponse(urlPrefix + action, ParsePath(key :: action :: Nil, true, false),
62 Map("id" -> id))
63 case RewriteRequest(_, path @ ParsePath(head :: id :: _,_,_), _,_) if (head == key && idParseable(id)) =>
64 RewriteResponse(urlPrefix, ParsePath(key :: "show" :: Nil, true, false),
65 Map("id" -> id))
66 }
67
68 def idParseable(s: String): Boolean = parseNumber(s) > 0
69
70 val templateDefaults = Map("list" -> listXhtml _,
71 "create" -> createXhtml _,
72 "edit" -> editXhtml _,
73 "show" -> showXhtml _)
74
75 def templatePF: LiftServlet.TemplatePf = {
76 case RequestMatcher(_, ParsePath(head :: action :: _,_,_), _,_) if (head == key && templateDefaults.contains(action)) =>
77 () => {
78 // If file-based template is missing and templateDefaults(action) exists,
79 // then provide the default template.
80 //
81 if (LiftServlet.getResource(urlPrefix + action + ".html").isEmpty)
82 templateDefaults(action)().map(surroundXhtml _)
83 else // Otherwise, continue normal processing that uses the file template.
84 Empty // In short, file-based template has priority.
85 }
86 }
87
88 def boot = {
89 LiftServlet.addTemplateBefore(templatePF)
90 LiftServlet.addRewriteBefore(rewritePF)
91 }
92
93 // -----------------------------------------------
94
95 def listXhtml: Can[NodeSeq] = snippet("list")
96 def createXhtml: Can[NodeSeq] = <table>{snippetForm("create")}</table>
97 def editXhtml: Can[NodeSeq] = <table>{snippetForm("edit")}</table>
98 def showXhtml: Can[NodeSeq] = snippet("show")
99
100 def snippet(action: String) = <lift:snippet type={key + "UI:" + action}/>
101 def snippetForm(action: String) = <lift:snippet type={key + "UI:" + action} form="POST"/>
102
103 def surroundXhtml(x: NodeSeq) =
104 <lift:surround with="default" at="content">{x}</lift:surround>
105}
106
107abstract class BaseUI[ModelType <: KeyedMapper[Long, ModelType]] {
108 // Example:
109 // def ui = BlogUIObject[Blog]
110 //
111 def ui: BaseUIObject[ModelType];
112
113 // Example:
114 // def model = Blog.getSingleton
115 //
116 def model: ModelType;
117
118 def param(p: String) = S.param(p)
119 def paramId = param("id")
120
121 def urlPrefix = ui.urlPrefix
122 def singular = ui.singular
123 def plural = ui.plural
124
125 def withLoggedInUser(f: (User) => NodeSeq) = (
126 for (u <- User.currentUser)
127 yield f(u)
128 ) openOr <span>please log in first</span>
129
130 // -----------------------------------------------
131
132 def create =
133 withLoggedInUser(u => form("create a new " + singular, model.getSingleton.create))
134
135 def edit =
136 withLoggedInUser(u => (
137 for (x <- model.getSingleton.find(paramId))
138 yield form("edit " + singular, x)
139 ) openOr <span>{singular} not found</span>)
140
141 def show = (
142 for (x <- model.getSingleton.find(paramId))
143 yield x.asHtml
144 ) openOr <span>unknown {singular}</span>
145
146 def list =
147 <div>
148 {listQuery()}
149 {createLink}
150 </div>
151
152 // -----------------------------------------------
153
154 def listQuery(q: QueryParam[ModelType]*) =
155 model.getSingleton.findAll(q:_*) match {
156 case Nil => <span>no {plural}</span>
157 case xs => <div>{plural}
158 {ui.listLines(xs)}
159 </div>
160 }
161
162 def form(title: String, defaultVal: => ModelType): NodeSeq = ui.form(title, defaultVal)
163
164 def createLink: NodeSeq =
165 if (User.loggedIn_?)
166 <a href={urlPrefix + "create"}>Create a new {singular}</a>
167 else Nil
168}