]> granicus.if.org Git - ejabberd/commitdiff
* src/web/ejabberd_web_admin.erl: Images now specified via CSS,
authorAlexey Shchepin <alexey@process-one.net>
Sun, 8 May 2005 23:39:46 +0000 (23:39 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Sun, 8 May 2005 23:39:46 +0000 (23:39 +0000)
design slightly updated, added last activity statistics (thanks to
Sergei Golovan)

SVN Revision: 344

ChangeLog
src/web/ejabberd_web_admin.erl

index 4e14eb4a0a498813c07c1e2c5142723282e219ba..195db8b22244065b665c2e5d652cfad8c390f62b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-05-09  Alexey Shchepin  <alexey@sevcom.net>
+
+       * src/web/ejabberd_web_admin.erl: Images now specified via CSS,
+       design slightly updated, added last activity statistics (thanks to
+       Sergei Golovan)
+
 2005-05-07  Alexey Shchepin  <alexey@sevcom.net>
 
        * src/stringprep/stringprep_drv.c: Added check for bidi
index 7fab28e3a706784957eec59cde1cb5b5369a2e9a..754e89c372f232c7d1475caf3b9a5d91ccac0ea2 100644 (file)
@@ -64,154 +64,178 @@ make_xhtml(Els, Lang) ->
                               {"type", "text/css"},
                               {"rel", "stylesheet"}], []}]},
        ?XE("body",
-          [?XAE("table",
-                [{"id", "main"}],
-                [?XE("tbody",
-                     [?XAE("tr",
-                           [{"id", "top"}],
-                           [?XE("td",
-                                [?XE("table",
-                                     [?XE("tbody",
-                                          [?XE("tr",
-                                               [?XE("td",
-                                                    [?XAE("a", [{"href", "/admin/"}],
-                                                          [?XA("img", [{"src", "/admin/logo.png"},
-                                                                       {"width", "343"},
-                                                                       {"height", "55"},
-                                                                       {"alt", "ejabberd"},
-                                                                       {"border", "0"}])])]),
-                                                ?XAE("td", [{"width", "100%"},
-                                                            {"background", "/admin/logo-fill.png"}],
-                                                    [?XAE("a", [{"href", "/admin/"}],
-                                                          [?XA("img", [{"src", "/admin/1x1tr.gif"},
-                                                                       {"width", "100%"},
-                                                                       {"height", "55"},
-                                                                       {"alt", ""},
-                                                                       {"border", "0"}])])]
-                                                    )])])
-                                     ])])]),
-                      ?XAE("tr",
-                           [{"id", "middle"}],
-                           [?XE("td",
-                                [?XAE("table",
-                                      [{"id", "middle-table"}],
-                                      [?XE("tbody",
-                                           [?XE("tr",
-                                                [?XAE("td",
-                                                      [{"id", "middle-td1"}],
-                                                      [?XAE("ul",
-                                                            [{"id", "navlist"}],
-                                                            [?LI([?ACT("/admin/acls/", "Access Control Lists")]),
-                                                             ?LI([?ACT("/admin/access/", "Access Rules")]),
-                                                             ?LI([?ACT("/admin/users/", "Users")]),
-                                                             ?LI([?ACT("/admin/online-users/", "Online Users")]),
-                                                             ?LI([?ACT("/admin/nodes/", "Nodes")]),
-                                                             ?LI([?ACT("/admin/stats/", "Statistics")])
-                                                            ] ++
-                                                            case lists:member(mod_shared_roster,
-                                                                              gen_mod:loaded_modules()) of
-                                                                true ->
-                                                                    [?LI([?ACT("/admin/shared-roster/", "Shared Roster")])];
-                                                                false ->
-                                                                    []
-                                                            end
-                                                           )]),
-                                                 ?XAE("td",
-                                                      [{"id", "middle-td2"}],
-                                                      [?XAE("div", [{"id", "content"}], Els)])])])
-                                      ])])]),
-                      ?XAE("tr",
-                           [{"id", "bottom"}],
-                           [?XE("td",
-                                [?XE("table",
-                                     [?XE("tbody",
-                                          [?XE("tr",
-                                               [?XCT("td",
-                                                     "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One")
-                                               ])])
-                                     ])])])])])])
+           [?XAE("div",
+                [{"id", "container"}],
+                [?XAE("div",
+                      [{"id", "header"}],
+                      [?XE("h1",
+                           [?ACT("/admin/", "ejabberd administration")]
+                          )]),
+                 ?XAE("div",
+                      [{"id", "navigation"}],
+                      [?XE("ul",
+                           [?LI([?ACT("/admin/acls/", "Access Control Lists")]),
+                            ?LI([?ACT("/admin/access/", "Access Rules")]),
+                            ?LI([?ACT("/admin/users/", "Users")]),
+                            ?LI([?ACT("/admin/online-users/", "Online Users")]),
+                            ?LI([?ACT("/admin/last-activity/", "Last Activity")]),
+                            ?LI([?ACT("/admin/nodes/", "Nodes")]),
+                            ?LI([?ACT("/admin/stats/", "Statistics")])
+                           ] ++
+                           case lists:member(mod_shared_roster,
+                                             gen_mod:loaded_modules()) of
+                               true ->
+                                   [?LI([?ACT("/admin/shared-roster/", "Shared Roster")])];
+                               false ->
+                                   []
+                           end
+                          )]),
+                 ?XAE("div",
+                      [{"id", "content"}],
+                      Els),
+                 ?XAE("div",
+                      [{"id", "clearcopyright"}],
+                      [{xmlcdata, ""}])]),
+           ?XAE("div",
+                [{"id", "copyrightouter"}],
+                [?XAE("div",
+                      [{"id", "copyright"}],
+                      [?XCT("p",
+                            "ejabberd (c) 2002-2005 Alexey Shchepin, 2004-2005 Process One")
+                      ])])])
       ]}}.
 
 css() -> "
-body {
-  margin-left: 0;
-  margin-right: 0;
-  margin-top: 0;
-  margin-bottom: 0;
+html,body {
+  background: white;
+  margin: 0;
+  padding: 0;
+  height: 100%;
 }
 
-#main {
-  border: none;
-  border-spacing: 0;
-  border-collapse: collapse;
-  background-color: #fe8a00;
-  width: 100%;
+#container {
+  padding: 0;
+  margin: 0;
+  min-height: 100%;
   height: 100%;
+  margin-bottom: -30px;
+}
+
+html>body #container {
+  height: auto;
+}
+
+#header h1 {
+  width: 100%;
+  height: 55px;
   padding: 0;
+  margin: 0;
+  background: transparent url(\"/admin/logo-fill.png\");
 }
 
-#main > tbody > tr > td {
+#header h1 a {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 55px;
   padding: 0;
+  margin: 0;
+  background: transparent url(\"/admin/logo.png\") no-repeat;
+  display: block;
+  text-indent: -700em;
 }
 
-#top > td > table {
-  border: none;
-  border-spacing: 0;
-  border-collapse: collapse;
-  background-color: #fe8a00;
+#clearcopyright {
+  display: block;
   width: 100%;
-  padding-top: 2px;
+  height: 30px;
 }
 
-#top table {
-  border: none;
-  border-spacing: 0;
-  border-collapse: collapse;
-  background-color: #fe8a00;
+#copyrightouter {
+  display: table;
   width: 100%;
-  padding-top: 2px;
+  height: 30px;
 }
 
-#top td {
-  padding: 0;
+#copyright {
+  display: table-cell;
+  vertical-align: bottom;
+  width: 100%;
+  height: 30px;
 }
 
-#top img {
-  margin-bottom: 0px;
+#copyright p {
+  margin-left: 0;
+  margin-right: 0;
+  margin-top: 5px;
+  margin-bottom: 0;
+  padding-left: 0;
+  padding-right: 0;
+  padding-top: 1px;
+  padding-bottom: 1px;
+  width: 100%;
+  color: #ffffff;
+  background-color: #fe8a00;
+  font-family: Verdana, Arial, Helvetica, sans-serif; 
+  font-size: 7pt;
+  font-weight: bold;
+  text-align: center;
 }
 
-#middle {
-  height: 100%;
+#navigation ul {
+  position: absolute;
+  top: 55px;
+  left: 0;
+  padding: 0 1px 1px;
+  margin: 0;
+  font-family: Verdana, Arial, Helvetica, sans-serif; 
+  font-size: 7.5pt;
+  font-weight: bold;
+  background: #d47911;
+  width: 13em;
 }
 
-#middle-table {
-  border: none;
-  border-spacing: 0;
-  border-collapse: collapse;
-  width: 100%;
-  height: 100%;
-  empty-cells: show;
+#navigation ul li {
+  list-style: none;
+  margin: 0;
+  text-align: left;
+  display: inline;
 }
 
-#middle-td1, #middle-td2 {
-  padding: 0;
-  background-color: #ffffff;
-  vertical-align: top;
+#navigation ul li a {
+  margin: 0;
+  display: block;
+  padding: 0.25em 0.5em 0.25em 0.75em;
+  border-left: 1em solid #ffc78c;
+  border-top: 1px solid gray;
+  background: #ffe3c9;
+  text-decoration: none;
 }
 
-#middle-td2 {
-  width: 100%;
+#navigation ul li a:link {
+  color: #844;
 }
 
-#bottom table {
-  border: none;
-  border-spacing: 0;
-  border-collapse: collapse;
-  width: 100%;
+#navigation ul li a:visited {
+ color: #766;
 }
 
-#bottom table td {
-  padding: 0;
+#navigation ul li a:hover {
+  border-color: #fc8800;
+  color: #FFF;
+  background: #332;
+}
+
+#lastactivity li {
+  font-weight: bold;
+  border: 1px solid #d6760e;
+  background-color: #fff2e8;
+  padding: 2px;
+  margin-bottom: -1px;
+}
+
+td.copy {
   color: #ffffff;
   background-color: #fe8a00;
   font-family: Verdana, Arial, Helvetica, sans-serif; 
@@ -220,86 +244,15 @@ body {
   text-align: center;
 }
 
-    /*td{
-      font-size: 3pt;
-    }
-    td.a{
-      color: #fc8800;
-      background-color: #fe8a00;
-    }
-    td.b{
-      color: #333333;
-      background-color: #000000;
-    }
-    td.c{
-      color: #743300;
-      background-color: #723100;
-    }
-    td.d{
-      color: #fdc58a;
-      background-color: #ffc78c;
-    }
-    td.e{
-      color: #fde1c7;
-      background-color: #ffe3c9;
-    }
-    td.f{
-      color: #fdfdfd;
-      background-color: #ffffff;
-    }*/
-    td.copy{
-      color: #ffffff;
-      background-color: #fe8a00;
-      font-family: Verdana, Arial, Helvetica, sans-serif; 
-      font-size: 7pt;
-      font-weight: bold;
-      text-align: center;
-    }
-
-    #navlist
-    {
-    padding: 0 1px 1px;
-    margin-left: 0;
-    font: bold 10px Verdana, sans-serif;
-    background: #d47911;
-    width: 13em;
-    }
-
-    #navlist li
-    {
-    list-style: none;
-    margin: 0;
-    text-align: left;
-    display: inline;
-    }
-
-    #navlist li a
-    {
-    display: block;
-    padding: 0.25em 0.5em 0.25em 0.75em;
-    border-left: 1em solid #ffc78c;
-    border-top: 1px solid gray;
-    background: #ffe3c9;
-    text-decoration: none;
-    }
-
-    #navlist li a:link { color: #844; }
-    #navlist li a:visited { color: #766; }
-
-    #navlist li a:hover
-    {
-    border-color: #fc8800;
-    color: #FFF;
-    background: #332;
-    }
-
 input {
-    border: 1px solid #d6760e;
-    color: #723202;
-    background-color: #fff2e8;
-    vertical-align: middle;
-    margin-bottom: 0px;
-    padding: 0.1em;
+  font-family: Verdana, Arial, Helvetica, sans-serif; 
+  font-size: 10pt;
+  border: 1px solid #d6760e;
+  color: #723202;
+  background-color: #fff2e8;
+  vertical-align: middle;
+  margin-bottom: 0px;
+  padding: 0.1em;
 }
 
 input[type=submit] {
@@ -312,24 +265,24 @@ input[type=submit] {
 }
 
 textarea {
-    border: 1px solid #d6760e;
-    color: #723202;
-    background-color: #fff2e8;
-    vertical-align: middle;
-    margin-top: 7px;
-    /*margin-left: 7px;
-    margin-right: 7px;*/
-    margin-bottom: 5px;
-    padding: 0.1em;
+  font-family: Verdana, Arial, Helvetica, sans-serif; 
+  font-size: 10pt;
+  border: 1px solid #d6760e;
+  color: #723202;
+  background-color: #fff2e8;
+  vertical-align: middle;
+  margin-top: 7px;
+  margin-bottom: 5px;
+  padding: 0.1em;
 }
 
 select {
-    border: 1px solid #d6760e;
-    color: #723202;
-    background-color: #fff2e8;
-    vertical-align: middle;
-    margin-bottom: 0px; 
-    padding: 0.1em;
+  border: 1px solid #d6760e;
+  color: #723202;
+  background-color: #fff2e8;
+  vertical-align: middle;
+  margin-bottom: 0px; 
+  padding: 0.1em;
 }
 
 thead {
@@ -412,7 +365,6 @@ h1 {
   font-size: 14pt;
   font-weight: bold;
   text-align: center;
-  padding-left: 5px;
   padding-top: 2px;
   padding-bottom: 2px;
   margin-top: 0px;
@@ -425,7 +377,6 @@ h2 {
   font-size: 12pt;
   font-weight: bold;
   text-align: center;
-  padding-left: 5px;
   padding-top: 2px;
   padding-bottom: 2px;
   margin-top: 0px;
@@ -438,7 +389,6 @@ h3 {
   font-size: 10pt;
   font-weight: bold;
   text-align: left;
-  padding-left: 5px;
   padding-top: 20px;
   padding-bottom: 2px;
   margin-top: 0px;
@@ -446,21 +396,21 @@ h3 {
 }
 
 #content a:link {
-  color: #444466
+  color: #990000
   font-family: Verdana, Arial, Helvetica, sans-serif; 
   font-size: 10pt;
   font-weight: bold;
   text-decoration: underlined;
 }
 #content a:visited {
-  color: #444466;  
+  color: #990000;  
   font-family: Verdana, Arial, Helvetica, sans-serif; 
   font-size: 10pt;
   font-weight: bold;
   text-decoration: underlined;
 }
 #content a:hover {
-  color: #222266;  
+  color: #cc6600;  
   font-family: Verdana, Arial, Helvetica, sans-serif; 
   font-size: 10pt;
   font-weight: bold;
@@ -468,7 +418,7 @@ h3 {
 }
 
 
-#content li {
+#content ul li {
   list-style-type: dot;
   font-size: 10pt;
   /*font-size: 7pt;*/
@@ -479,9 +429,11 @@ h3 {
   font-size: 10pt;
 }
 
-div#content {
-  margin-left: 10px;
-  margin-top: 5px;
+#content {
+  font-family: Verdana, Arial, Helvetica, sans-serif; 
+  font-size: 10pt;
+  padding-left: 13em;
+  padding-top: 5px;
 }
 
 *.alignright {
@@ -492,15 +444,27 @@ div#content {
 
 logo() ->
     jlib:decode_base64(
-      "iVBORw0KGgoAAAANSUhEUgAAAVcAAAA3CAMAAACPbPnEAAAAYFBMVEX///8CAgJyMgL+vm7Wdg7+igL+/v7+slb+qkb+4sr+ojP+nir+lhr+1qb+khL+wnb+wn7+zpb+jgb+yoz+xo7+tmL+pj7+mib+jg7+5sb+rlL+rkr+mh7+tl7+2q7+umpJ0uikAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfUBAUJBhWzc9qJAAABQ0lEQVR42u2bXU/CQBBFUUZFURAU5Ev4//+S3Ow+tFl3s6adtE3Oebghzc4DJ/Nw04WZgQczexJkz4lXvOKVxKuXV6APTCFXAq94xSte8ermFYbrA6+ilemZRxGz+fxBxMydL0/Vz5anvkUrPfb1IPCKV7ziFa9uXsG/DzyLPz7ndjS3tc3tSbcwPdl9tmYq3dHmk9x3r8mtiM11KfCKV7ziFa9uXmEc7wf+u6+5TtlXf62fKu9rl3wX9ibsLPCKV7ziFa9uXmF87wf67aBT6a+hp4bOehFxU0/CbgKveMUrXvHq5hXG+vuBcpss75zH/VZ5X7vcb4W7q5A/wvbCXoTNhX0JvOIVr3jFq5tX4P8Fw2V6g7UQ9itsLeKmfgi84hWveMWrm1egDwyX6Q3WTtinsI2wq7CjwCte8YpXvLp5BQ/utIiGbwh9RAEAAAAASUVORK5CYII=").
+      "iVBORw0KGgoAAAANSUhEUgAAAVcAAAA3CAMAAACPbPnEAAAAYFBMVEX///8C"
+      "AgJyMgL+vm7Wdg7+igL+/v7+slb+qkb+4sr+ojP+nir+lhr+1qb+khL+wnb+"
+      "wn7+zpb+jgb+yoz+xo7+tmL+pj7+mib+jg7+5sb+rlL+rkr+mh7+tl7+2q7+"
+      "umpJ0uikAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIA"
+      "AAsSAdLdfvwAAAAHdElNRQfUBAUJBhWzc9qJAAABQ0lEQVR42u2bXU/CQBBF"
+      "UUZFURAU5Ev4//+S3Ow+tFl3s6adtE3Oebghzc4DJ/Nw04WZgQczexJkz4lX"
+      "vOKVxKuXV6APTCFXAq94xSte8ermFYbrA6+ilemZRxGz+fxBxMydL0/Vz5an"
+      "vkUrPfb1IPCKV7ziFa9uXsG/DzyLPz7ndjS3tc3tSbcwPdl9tmYq3dHmk9x3"
+      "r8mtiM11KfCKV7ziFa9uXmEc7wf+u6+5TtlXf62fKu9rl3wX9ibsLPCKV7zi"
+      "Fa9uXmF87wf67aBT6a+hp4bOehFxU0/CbgKveMUrXvHq5hXG+vuBcpss75zH"
+      "/VZ5X7vcb4W7q5A/wvbCXoTNhX0JvOIVr3jFq5tX4P8Fw2V6g7UQ9itsLeKm"
+      "fgi84hWveMWrm1egDwyX6Q3WTtinsI2wq7CjwCte8YpXvLp5BQ/utIiGbwh9"
+      "RAEAAAAASUVORK5CYII=").
 
 logo_fill() ->
     jlib:decode_base64(
-      "iVBORw0KGgoAAAANSUhEUgAAAAYAAAA3BAMAAADdxCZzAAAAIVBMVEX////Wdg7+igL+khL+jg7+nir+rkr+umr+yoz+1qb+5sYp3v/aAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfUBAYHDzOol2bZAAAASElEQVR42mMQFBRkUFJSxMAgcWNjQwwMEndxccTAIPHQ0EAMDBJPS0vEwCDx8vJCDAwS7+hoxMAg8ZkzJ2JgkPiqVQsxMFAcABvNNugXg2QkAAAAAElFTkSuQmCC").
-
-empty() ->
-    jlib:decode_base64(
-      "R0lGODlhAQABAIAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgABACwAAAAAAQABAAACAkwBADs=").
+      "iVBORw0KGgoAAAANSUhEUgAAAAYAAAA3BAMAAADdxCZzAAAAHlBMVEXWdg7+"
+      "igL+jg7+khL+nir+rkr+umr+yoz+1qb+5sbOf9L8AAAACXBIWXMAAA9hAAAP"
+      "YQGoP6dpAAAAQUlEQVQI12XDSxHAIBAFQT6BJEcsYAELWMACFtYCFnAL7zxd"
+      "1c5dvhSU2BpKqBXl6R0ljYGS50R5zVC+tVD+vfE6YyUexE9x7g4AAAAASUVO"
+      "RK5CYII=").
 
 process_admin(#request{us = US,
                       path = [],
@@ -508,19 +472,20 @@ process_admin(#request{us = US,
                       lang = Lang} = Request) ->
     make_xhtml([?XCT("h1", "ejabberd administration"),
                ?XE("ul",
-                   [?LI([?ACT("acls/", "Access Control Lists"), ?C(" "),
-                         ?ACT("acls-raw/", "(raw)")]),
-                    ?LI([?ACT("access/", "Access Rules"), ?C(" "),
-                         ?ACT("access-raw/", "(raw)")]),
-                    ?LI([?ACT("users/", "Users")]),
-                    ?LI([?ACT("online-users/", "Online Users")]),
-                    ?LI([?ACT("nodes/", "Nodes")]),
-                    ?LI([?ACT("stats/", "Statistics")])
+                   [?LI([?ACT("/admin/acls/", "Access Control Lists"), ?C(" "),
+                         ?ACT("/admin/acls-raw/", "(raw)")]),
+                    ?LI([?ACT("/admin/access/", "Access Rules"), ?C(" "),
+                         ?ACT("/admin/access-raw/", "(raw)")]),
+                    ?LI([?ACT("/admin/users/", "Users")]),
+                    ?LI([?ACT("/admin/online-users/", "Online Users")]),
+                    ?LI([?ACT("/admin/last-activity/", "Last Activity")]),
+                    ?LI([?ACT("/admin/nodes/", "Nodes")]),
+                    ?LI([?ACT("/admin/stats/", "Statistics")])
                    ] ++
                    case lists:member(mod_shared_roster,
                                      gen_mod:loaded_modules()) of
                        true ->
-                           [?LI([?ACT("shared-roster/", "Shared Roster")])];
+                           [?LI([?ACT("/admin/shared-roster/", "Shared Roster")])];
                        false ->
                            []
                    end
@@ -545,12 +510,6 @@ process_admin(#request{us = US,
                       lang = Lang} = Request) ->
     {200, [{"Content-Type", "image/png"}], logo_fill()};
 
-process_admin(#request{us = US,
-                      path = ["1x1tr.gif"],
-                      q = Query,
-                      lang = Lang} = Request) ->
-    {200, [{"Content-Type", "image/gif"}], empty()};
-
 process_admin(#request{us = US,
                       path = ["acls-raw"],
                       q = Query,
@@ -798,6 +757,46 @@ process_admin(#request{us = US,
     Res = list_online_users(Lang),
     make_xhtml([?XCT("h1", "ejabberd users")] ++ Res, Lang);
 
+process_admin(#request{method = Method,
+                      us = US,
+                      path = ["last-activity"],
+                      q = Query,
+                      lang = Lang} = Request) ->
+    ?INFO_MSG("query: ~p", [Query]),
+    Month = case lists:keysearch("period", 1, Query) of
+               {value, {_, Val}} ->
+                   Val;
+               _ ->
+                   "month"
+           end,
+    Res = case lists:keysearch("ordinary", 1, Query) of
+             {value, {_, _}} ->
+                 list_last_activity(Lang, false, Month);
+             _ ->
+                 list_last_activity(Lang, true, Month)
+         end,
+    make_xhtml([?XCT("h1", "Users last activity")] ++
+              [?XAE("form", [{"method", "post"}],
+                    [?CT("Period: "),
+                     ?XAE("select", [{"name", "period"}],
+                          lists:map(
+                            fun({O, V}) ->
+                                   Sel = if
+                                             O == Month -> [{"selected", "selected"}];
+                                             true -> []
+                                         end,
+                                   ?XAC("option",
+                                        Sel ++ [{"value", O}], V)
+                            end, [{"month", ?T("Last month")},
+                                  {"year", ?T("Last year")},
+                                  {"all", ?T("All activity")}])),
+                     ?C(" "),
+                     ?INPUTT("submit", "ordinary", "Show Ordinary Table"),
+                     ?C(" "),
+                     ?INPUTT("submit", "integral", "Show Integral Table")
+                    ])] ++
+              Res, Lang);
+
 process_admin(#request{us = US,
                       path = ["stats"],
                       q = Query,
@@ -1557,6 +1556,74 @@ user_roster_item_parse_query(User, Server, Items, Query) ->
     nothing.
 
 
+list_last_activity(_Lang, Integral, Period) ->
+    {MegaSecs, Secs, _MicroSecs} = now(),
+    TimeStamp = MegaSecs * 1000000 + Secs,
+    case Period of
+       "all" ->
+           TS = 0,
+           Days = infinity;
+       "year" ->
+           TS = TimeStamp - 366 * 86400,
+           Days = 366;
+       _ ->
+           TS = TimeStamp - 31 * 86400,
+           Days = 31
+    end,
+    case catch mnesia:dirty_select(
+                last_activity, [{{last_activity, '_', '$1', '_'},
+                                 [{'>', '$1', TS}],
+                                 [{'trunc', {'/',
+                                             {'-', TimeStamp, '$1'},
+                                             86400}}]}]) of
+       {'EXIT', _Reason} ->
+           [];
+       Vals ->
+           Hist = histogram(Vals, Integral),
+           Left = case Days of
+                      infinity ->
+                          0;
+                      _ ->
+                          Days - length(Hist)
+                  end,
+           Tail = case Integral andalso Hist /= [] of
+                      true ->
+                          lists:duplicate(Left, lists:last(Hist));
+                      _ ->
+                          lists:duplicate(Left, 0)
+                  end,
+           Max = lists:max(Hist),
+           [?XAE("ol",
+                 [{"id", "lastactivity"}, {"start", "0"}],
+                 [?XAE("li",
+                       [{"style", "width:" ++ integer_to_list(
+                                                trunc(90 * V / Max)) ++ "%;"}],
+                       [{xmlcdata, integer_to_list(V)}])
+                  || V <- Hist ++ Tail])]
+    end.
+
+histogram(Values, Integral) ->
+    histogram(lists:sort(Values), Integral, 0, 0, []).
+
+histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
+    histogram(T, Integral, Current, Count + 1, Hist);
+           
+histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
+    if
+       Integral ->
+           histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
+       true ->
+           histogram(Values, Integral, Current + 1, 0, [Count | Hist])
+    end;
+
+histogram([], _Integral, _Current, Count, Hist) ->
+    if
+       Count > 0 ->
+           lists:reverse([Count | Hist]);
+       true ->
+           lists:reverse(Hist)
+    end.
+
 
 get_nodes(Lang) ->
     RunningNodes = mnesia:system_info(running_db_nodes),