--- a/src/main/scala/bootstrap/liftweb/Boot.scala Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala Wed Nov 30 16:59:10 2011 +0100
@@ -17,11 +17,14 @@
import net.liftweb.common._
import net.liftweb.http._
+import net.liftweb.http.js.JsObj
+import net.liftweb.http.js.JE.JsObj
import net.liftweb.mapper._
import net.liftweb.sitemap._
import net.liftweb.sitemap.Loc._
+import net.liftweb.widgets.menu.MenuStyle
+import net.liftweb.widgets.menu.{MenuWidget => LiftMenuWidget}
import net.liftweb.util._
-import net.liftweb.widgets.menu.MenuWidget
import net.tz.lift.snippet.ActionLinks
import net.tz.lift.util.YmdDateTimeConverter
import radview.model._
@@ -57,7 +60,9 @@
LiftRules.snippetDispatch.append {
case "Menubar" => new AnyRef with DispatchSnippet {
def dispatch: DispatchIt = {
- case _ => { xhtml => MenuWidget() }
+ case _ => { xhtml =>
+ new MenuWidget(MenuStyle.HORIZONTAL, JsObj(), Nil) render
+ }
}
}
case "action-links" => ActionLinks
@@ -72,11 +77,12 @@
Menu.i("Cells") / "cell",
Menu(SessionSnippet),
Menu(AccountSnippet),
- Menu.i("Accounts") / "account"
+ Menu.i("Accounts") / "account",
+ DynIpSnippet.menu
))
/* Menu widget */
- MenuWidget.init()
+ LiftMenuWidget.init()
/* Http conf */
LiftRules.logServiceRequestTiming = false
@@ -94,4 +100,18 @@
}
}
+class MenuWidget(style: MenuStyle.Value, jsObj: JsObj, groups : List[String])
+ extends LiftMenuWidget(style, jsObj, groups) {
+
+ override def render: NodeSeq = {
+ head ++ <div>{groups match {
+ case Nil =>
+ <lift:Menu.builder expandAll="true" linkToSelf="true" top:class={style.toString} />
+ case xs => groups.flatMap(group =>
+ <lift:Menu.builder expandAll="true" linkToSelf="true" top:class={style.toString} group={group} />)
+ }}</div>
+ }
+}
+
+
// vim: set ts=2 sw=2 et:
--- a/src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala Wed Nov 30 16:59:10 2011 +0100
@@ -21,6 +21,8 @@
trait SnippetHelpers {
def mkPath(prefix: String, l: String*) =
(prefix :: l.toList) mkString ("/", "/", "")
+
+ type CssTr = (NodeSeq => NodeSeq)
}
class A(href: => String) extends Function1[NodeSeq, NodeSeq] {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/radview/model/DynIp.scala Wed Nov 30 16:59:10 2011 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package radview.model
+
+import java.sql.Types
+import net.liftweb.common._
+import net.liftweb.mapper._
+import org.joda.time.DateMidnight
+import radview.snippet.SessionSnippet
+import scala.xml.Text
+
+object DynIp extends DynIp with MetaMapper[DynIp] {
+ override def dbTableName = "radacct"
+ override def dbDefaultConnectionIdentifier = RadAcctConnectionIdentifier
+
+ def byImsi(i: String, from: DateMidnight, to: DateMidnight) =
+ findAllByPreparedStatement { c =>
+ val sql = "SELECT MIN(%s) AS %s, %s, %s FROM %s" +
+ " WHERE %s = ? AND %s BETWEEN ? AND ? GROUP BY %s ORDER BY %s"
+ val pars = ((List(ts, ts, framedIp, callingNo) map (_.dbColumnName)) :+
+ dbTableName) ::: (List(callingNo, ts, framedIp, ts) map (_.dbColumnName))
+ val st = c.connection.prepareStatement(sql.format(pars :_*))
+ st.setString(1, i)
+ st.setObject(2, from.toDate, Types.DATE)
+ st.setObject(3, to.toDate, Types.DATE)
+ st
+ }
+
+ def byIp(i: String, from: DateMidnight, to: DateMidnight) =
+ findAllByPreparedStatement { c =>
+ val sql = "SELECT MIN(%s) AS %s, %s, %s FROM %s" +
+ " WHERE %s = ? AND %s BETWEEN ? AND ? GROUP BY %s ORDER BY %s"
+ val pars = ((List(ts, ts, framedIp, callingNo) map (_.dbColumnName)) :+
+ dbTableName) ::: (List(framedIp, ts, callingNo, ts) map (_.dbColumnName))
+ val st = c.connection.prepareStatement(sql.format(pars :_*))
+ st.setString(1, i)
+ st.setObject(2, from.toDate, Types.DATE)
+ st.setObject(3, to.toDate, Types.DATE)
+ st
+ }
+}
+
+class DynIp extends Mapper[DynIp] {
+ def getSingleton = DynIp
+
+ object framedIp extends MappedPoliteStringColName(this, 15,
+ "framedipaddress", "Framed IP")
+
+ object ts extends MappedDateTime(this) {
+ override def dbColumnName = "Event_Timestamp"
+ override def displayName = "Timestamp"
+ }
+
+ object callingNo extends MappedLongColName(this, "callingstationid",
+ "Calling No.")
+
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/radview/snippet/AccountSnippet.scala Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/scala/radview/snippet/AccountSnippet.scala Wed Nov 30 16:59:10 2011 +0100
@@ -30,7 +30,8 @@
import scala.xml.{NodeSeq, Text}
object AsAccountImsi {
- def unapply(in: String): Option[RadCheckSub] = RadCheckSub.byImsi(in)
+ def unapply(in: String) = apply(in)
+ def apply(in: String): Option[RadCheckSub] = RadCheckSub.byImsi(in)
}
abstract sealed class AccountLoc
@@ -38,12 +39,14 @@
case class ViewAccount(acc: RadCheckSub) extends AccountLoc
case class SyslogAccount(acc: RadCheckSub) extends AccountLoc
case class CdrAccount(acc: RadCheckSub) extends AccountLoc
+case class DynIpAccount(acc: RadCheckSub) extends AccountLoc
object AsAccount {
def unapply(loc: Box[AccountLoc]): Option[RadCheckSub] = loc flatMap { _ match {
case SyslogAccount(a) => Some(a)
case ViewAccount(a) => Some(a)
case CdrAccount(a) => Some(a)
+ case DynIpAccount(a) => Some(a)
case _ => None
}}
}
@@ -61,6 +64,7 @@
case ViewAccount(a) => mkPath(prefix, "imsi", a.imsi.is)
case SyslogAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "syslog")
case CdrAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "cdr")
+ case DynIpAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "dyn-ip")
}
}
@@ -71,6 +75,7 @@
case ViewAccount(a) => "Account " + a.imsi
case SyslogAccount(a) => "Syslog search for account " + a.imsi
case CdrAccount(a) => "CDR search for account " + a.imsi
+ case DynIpAccount(a) => "Dynamic IP assignments for account " + a.imsi
}))
override def rewrite: LocRewrite = Full({
@@ -85,9 +90,13 @@
case "cdr" :: xs =>
ActionLinks.append(A(url(ViewAccount(a)), "Back to account"))
CdrAccount(a)
+ case "dyn-ip" :: xs =>
+ ActionLinks.append(A(url(ViewAccount(a)), "Back to account"))
+ DynIpAccount(a)
case _ =>
ActionLinks.append(A(url(SyslogAccount(a)), "Syslog search"))
ActionLinks.append(A(url(CdrAccount(a)), "CDR search"))
+ ActionLinks.append(A(url(DynIpAccount(a)), "Dynamic IP address assignments"))
ViewAccount(a)
}
(RResp(List(tpl, "view")), l)
@@ -104,6 +113,7 @@
case ("form", Full(SyslogAccount(a))) => syslogForm(a)
case ("cdr-list", Full(CdrAccount(a))) => cdrList(a)
case ("form", Full(CdrAccount(a))) => cdrForm(a)
+ case ("dyn-ip-list", Full(DynIpAccount(a))) => dynIpList(a)
case ("form", _) => ClearNodes
case ("syslog-list", _) => ClearNodes
@@ -111,6 +121,7 @@
case ("sub", _) => ClearNodes
case ("if-nai", _) => ClearNodes
case ("nai", _) => ClearNodes
+ case ("dyn-ip-list", _) => ClearNodes
}
/* Shared in all searches. */
@@ -309,6 +320,20 @@
"attr-name-wide", "attr-value") } ) &
".list" #> CdrTable(Cdr.findAll( (qp:+MaxRows(mxCnt.is) ):_*), a.isAdsl)
} openOr ClearNodes
+
+ def dynIpList(a: RadCheckSub): CssTr = {
+ import DynIp._
+ val from = (S param "from") flatMap (AsDateMidnight(_)) openOr (
+ (new DateTime).minusDays(7).toDateMidnight)
+ val to = (S param "to") flatMap (AsDateMidnight(_)) openOr (
+ (new DateTime).toDateMidnight)
+
+ "#dyn-ip-from [value]" #> AsDateMidnight(from) &
+ "#dyn-ip-to [value]" #> AsDateMidnight(to) &
+ ".list" #> Table[DynIp](List(framedIp, ts) map { f =>
+ Column[DynIp](f.displayName, { v: DynIp => f.actualField(v).asHtml })
+ }, byImsi(a.imsi, from, to.plusDays(1)))
+ }
}
object AccountTable {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/radview/snippet/DynIpSnippet.scala Wed Nov 30 16:59:10 2011 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2011 Tomas Zeman <tzeman@volny.cz>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package radview.snippet
+
+import net.liftweb.common._
+import net.liftweb.http._
+import net.liftweb.sitemap._
+import net.liftweb.sitemap.Loc._
+import net.liftweb.util.ClearNodes
+import net.liftweb.util.Helpers._
+import net.tz.lift.snippet._
+import net.tz.lift.util._
+import org.joda.time.DateTime
+import radview.model._
+import scala.xml.{NodeSeq, Text}
+
+object DynIpSnippet extends SnippetHelpers {
+
+ val menu = Menu("Dynamic IP Search") / "dyn-ip" >>
+ Snippet("form", form) >> Snippet("res", res)
+
+ def from = (S param "from") flatMap (AsDateMidnight(_)) openOr (
+ (new DateTime).minusDays(7).toDateMidnight)
+ def to = (S param "to") flatMap (AsDateMidnight(_)) openOr (
+ (new DateTime).toDateMidnight)
+
+ def form: CssTr = {
+ "#ip [value]" #> (S param "ip") &
+ "#from [value]" #> AsDateMidnight(from) &
+ "#to [value]" #> AsDateMidnight(to)
+ }
+
+ def res: CssTr = {
+ import DynIp._
+ "*" #> (for {
+ ip <- S param "ip"
+ } yield {
+ Table[DynIp](List(
+ Column[DynIp](callingNo.displayName, { v: DynIp =>
+ val n = v.callingNo.is.toString
+ val h = v.callingNo.asHtml
+ AsAccountImsi(n) map { a =>
+ <a href={AccountSnippet.url(ViewAccount(a))}>{h}</a>
+ } getOrElse h
+ }),
+ Column[DynIp](ts.displayName, { v: DynIp => v.ts.asHtml })
+ ), byIp(ip, from, to.plusDays(1)))
+ })
+ }
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/webapp/account/view.html Thu Apr 14 15:42:46 2011 +0200
+++ b/src/main/webapp/account/view.html Wed Nov 30 16:59:10 2011 +0100
@@ -47,6 +47,32 @@
<span class="num-rows-panel"/><br/>
<span class="list"/><br/>
</div>
+ <div class="span-24 last lift:dyn-ip-list">
+ <form method="get">
+ <table>
+ <tr>
+ <td class="form-name">
+ <label for="dyn-ip-from" accesskey="F">From</label>
+ </td>
+ <td class="form-value">
+ <input type="text" id="dyn-ip-from" name="from" class="date"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="form-name">
+ <label for="dyn-ip-to" accesskey="T">To</label>
+ </td>
+ <td class="form-value">
+ <input type="text" id="dyn-ip-to" name="to" class="date"/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2"><input type="submit"/></td>
+ </tr>
+ </table>
+ </form>
+ <span class="list"/><br/>
+ </div>
</div>
</body>
</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/dyn-ip.html Wed Nov 30 16:59:10 2011 +0100
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
+ <title>Dyn IP Search</title>
+ </head>
+ <body class="lift:content_id=main">
+ <div id="main" class="lift:surround?with=default;at=content">
+ <head>
+ <script type="text/javascript" src="/js/jquery-ui-1.8.9.custom.min.js"></script>
+ <script type="text/javascript">
+ $(document).ready(function() {
+ $.datepicker.setDefaults({
+ dateFormat: "yy-mm-dd"
+ });
+ $(".date").datepicker();
+ });
+ </script>
+ <link type="text/css" href="/css/jquery-ui-1.8.9.custom.css" rel="stylesheet" />
+ </head>
+ <div class="span-24 last">
+ <h2><span class="lift:Menu.title"/></h2>
+ </div>
+ <div class="span-24 last lift:form">
+ <form method="get">
+ <table>
+ <tr>
+ <td class="form-name">
+ <label for="ip" accesskey="I">IP (exact match)</label>
+ </td>
+ <td class="form-value">
+ <input type="text" id="ip" name="ip"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="form-name">
+ <label for="from" accesskey="F">From</label>
+ </td>
+ <td class="form-value">
+ <input type="text" id="from" name="from" class="date"/>
+ </td>
+ </tr>
+ <tr>
+ <td class="form-name">
+ <label for="to" accesskey="T">To</label>
+ </td>
+ <td class="form-value">
+ <input type="text" id="to" name="to" class="date"/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2"><input type="submit"/></td>
+ </tr>
+ </table>
+ </form>
+ </div>
+ <div class="span-24 last">
+ <span class="lift:res"/>
+ </div>
+ </div>
+ </body>
+ </html>