diff --git a/NASA_IMAGES.txt b/NASA_IMAGES.txt new file mode 100644 index 000000000..c19087e58 --- /dev/null +++ b/NASA_IMAGES.txt @@ -0,0 +1,7 @@ +The images used as login screen backgrounds are from the Nasa Commons and do not fall under any copyright restrictions. + + * http://www.flickr.com/photos/nasacommons/4858567220 + +Fore more information see http://www.nasa.gov/audience/formedia/features/MP_Photo_Guidelines.html + +Thank you, NASA. \ No newline at end of file diff --git a/app/controllers/MessageCountsController.java b/app/controllers/MessageCountsController.java index 1b75b29ca..4bbcfe47f 100644 --- a/app/controllers/MessageCountsController.java +++ b/app/controllers/MessageCountsController.java @@ -9,13 +9,31 @@ import com.google.gson.Gson; import lib.APIException; -import models.MessageCount; +import models.MessageCountHistogram; import models.api.results.DateHistogramResult; +import models.MessageCount; +import models.api.results.MessageCountResult; import play.mvc.*; public class MessageCountsController extends AuthenticatedController { - public static Result total(String timerange) { + public static Result total() { + try { + MessageCount count = new MessageCount(); + MessageCountResult countResult = count.total(); + + Map result = Maps.newHashMap(); + result.put("events", countResult.getEventsCount()); + + return ok(new Gson().toJson(result)).as("application/json"); + } catch (IOException e) { + return internalServerError("io exception"); + } catch (APIException e) { + return internalServerError("api exception " + e); + } + } + + public static Result histogram(String timerange) { int range; try { range = Integer.parseInt(timerange); @@ -24,8 +42,8 @@ public static Result total(String timerange) { } try { - MessageCount count = new MessageCount("minute", range); - DateHistogramResult histogramResult = count.total(); + MessageCountHistogram count = new MessageCountHistogram("minute", range); + DateHistogramResult histogramResult = count.histogram(); List> lines = Lists.newArrayList(); Map r = Maps.newTreeMap(); @@ -37,9 +55,9 @@ public static Result total(String timerange) { return ok(new Gson().toJson(lines)).as("application/json"); } catch (IOException e) { - return ok("io exception"); + return internalServerError("io exception"); } catch (APIException e) { - return ok("api exception" + e); + return internalServerError("api exception " + e); } } diff --git a/app/controllers/SearchController.java b/app/controllers/SearchController.java index 72c8c0a3b..136577b95 100755 --- a/app/controllers/SearchController.java +++ b/app/controllers/SearchController.java @@ -35,7 +35,11 @@ public static Result index(String q, String timerange, String interval) { SearchResult searchResult = search.search(); DateHistogramResult histogramResult = search.dateHistogram(interval); - return ok(views.html.search.results.render(currentUser(), searchResult, histogramResult, q)); + if (searchResult.getTotalResultCount() > 0) { + return ok(views.html.search.results.render(currentUser(), searchResult, histogramResult, q)); + } else { + return ok(views.html.search.noresults.render(currentUser(), q)); + } } catch (IOException e) { return status(504, views.html.errors.error.render(Api.ERROR_MSG_IO, e, request())); } catch (APIException e) { diff --git a/app/controllers/SessionsController.java b/app/controllers/SessionsController.java index 5b59c6faa..aa1a34628 100644 --- a/app/controllers/SessionsController.java +++ b/app/controllers/SessionsController.java @@ -12,6 +12,11 @@ public class SessionsController extends Controller { final static Form userForm = form(LoginRequest.class); public static Result index() { + // Redirect if already logged in. + if (session("username") != null && !session("username").isEmpty()) { + return redirect("/"); + } + return ok(views.html.sessions.login.render(userForm)); } diff --git a/app/lib/Tools.java b/app/lib/Tools.java new file mode 100644 index 000000000..4f122f42b --- /dev/null +++ b/app/lib/Tools.java @@ -0,0 +1,36 @@ +/** + * Copyright 2013 Lennart Koopmann + * + * This file is part of Graylog2. + * + * Graylog2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Graylog2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Graylog2. If not, see . + * + */ +package lib; + +import java.util.Random; + +/** + * @author Lennart Koopmann + */ +public class Tools { + + private Tools() { /* pure utility class */ } + + public static int random(int min, int max) { + Random rand = new Random(); + return rand.nextInt(max - min + 1) + min; + } + +} diff --git a/app/models/MessageCount.java b/app/models/MessageCount.java index 3403cea05..3265ab8db 100644 --- a/app/models/MessageCount.java +++ b/app/models/MessageCount.java @@ -1,29 +1,29 @@ package models; -import java.io.IOException; -import java.net.URL; - import lib.APIException; import lib.Api; -import models.api.responses.DateHistogramResponse; -import models.api.results.DateHistogramResult; +import models.api.responses.MessageCountResponse; +import models.api.results.MessageCountResult; +import play.cache.Cache; + +import java.io.IOException; +import java.net.URL; public class MessageCount { - private final String interval; - private final int timerange; - - public MessageCount(String interval, int timerange) { - this.interval = interval; - this.timerange = timerange; - } - - public DateHistogramResult total() throws IOException, APIException { - String i = Api.urlEncode(interval); - URL url = Api.buildTarget("count/total?interval=" + i + "&timerange=" + timerange); - - DateHistogramResponse response = Api.get(url, new DateHistogramResponse()); - return new DateHistogramResult("match_all", response.time, response.interval, response.results); - } - + public static final int TOTAL_CNT_CACHE_TTL = 2; // seconds + public static final String TOTAL_CNT_CACHE_KEY = "counts.total"; + + public MessageCountResult total() throws IOException, APIException { + MessageCountResult cached = (MessageCountResult) Cache.get(TOTAL_CNT_CACHE_KEY); + if (cached != null) { + return cached; + } + + MessageCountResponse response = Api.get(Api.buildTarget("count/total"), new MessageCountResponse()); + MessageCountResult result = new MessageCountResult(response.events); + Cache.set(TOTAL_CNT_CACHE_KEY, result, TOTAL_CNT_CACHE_TTL); + return result; + } + } diff --git a/app/models/MessageCountHistogram.java b/app/models/MessageCountHistogram.java new file mode 100644 index 000000000..18b5dd58e --- /dev/null +++ b/app/models/MessageCountHistogram.java @@ -0,0 +1,29 @@ +package models; + +import java.io.IOException; +import java.net.URL; + +import lib.APIException; +import lib.Api; +import models.api.responses.DateHistogramResponse; +import models.api.results.DateHistogramResult; + +public class MessageCountHistogram { + + private final String interval; + private final int timerange; + + public MessageCountHistogram(String interval, int timerange) { + this.interval = interval; + this.timerange = timerange; + } + + public DateHistogramResult histogram() throws IOException, APIException { + String i = Api.urlEncode(interval); + URL url = Api.buildTarget("count/histogram?interval=" + i + "&timerange=" + timerange); + + DateHistogramResponse response = Api.get(url, new DateHistogramResponse()); + return new DateHistogramResult("match_all", response.time, response.interval, response.results); + } + +} diff --git a/app/models/api/responses/MessageCountResponse.java b/app/models/api/responses/MessageCountResponse.java new file mode 100644 index 000000000..3aee9d096 --- /dev/null +++ b/app/models/api/responses/MessageCountResponse.java @@ -0,0 +1,7 @@ +package models.api.responses; + +public class MessageCountResponse { + + public int events; + +} diff --git a/app/models/api/results/MessageCountResult.java b/app/models/api/results/MessageCountResult.java new file mode 100644 index 000000000..189d85892 --- /dev/null +++ b/app/models/api/results/MessageCountResult.java @@ -0,0 +1,15 @@ +package models.api.results; + +public class MessageCountResult { + + private final int eventsCount; + + public MessageCountResult(int eventsCount) { + this.eventsCount = eventsCount; + } + + public int getEventsCount() { + return eventsCount; + } + +} diff --git a/app/models/api/results/SearchResult.java b/app/models/api/results/SearchResult.java index 4e32f0ac0..ac2c00523 100644 --- a/app/models/api/results/SearchResult.java +++ b/app/models/api/results/SearchResult.java @@ -45,10 +45,12 @@ public List getFields() { private List buildFields(List sFields) { List fields = Lists.newArrayList(); - - for (String field : sFields) { - fields.add(new Field(field)); - } + + if (sFields != null) { + for (String field : sFields) { + fields.add(new Field(field)); + } + } return fields; } diff --git a/app/views/auth.scala.html b/app/views/auth.scala.html index ceededa94..d5ba623e3 100644 --- a/app/views/auth.scala.html +++ b/app/views/auth.scala.html @@ -8,11 +8,14 @@ + + + @content diff --git a/app/views/dashboard/index.scala.html b/app/views/dashboard/index.scala.html index 4a66a301c..7929b01fa 100755 --- a/app/views/dashboard/index.scala.html +++ b/app/views/dashboard/index.scala.html @@ -2,4 +2,6 @@ @main("Dashboard", sidebars.standard(currentUser), "", currentUser) { + Containing [] events. + } \ No newline at end of file diff --git a/app/views/errors/error.scala.html b/app/views/errors/error.scala.html index 2a02ca6a5..316ba8f4b 100644 --- a/app/views/errors/error.scala.html +++ b/app/views/errors/error.scala.html @@ -15,13 +15,7 @@

Reason: @message

-

What should I do now?

-

- Please consult the support pages if you don't - know how to fix this problem. Contact the - mailing list or file an issue if in - doubt about anything or you have questions left. -

+@partials.support_sources()

Stacktrace

diff --git a/app/views/partials/support_sources.scala.html b/app/views/partials/support_sources.scala.html new file mode 100644 index 000000000..96a5dffd8 --- /dev/null +++ b/app/views/partials/support_sources.scala.html @@ -0,0 +1,11 @@ +
+

Need help?

+ Do not hesitate to consult the Graylog2 community if your questions are not answered in the + documentation. + + +
\ No newline at end of file diff --git a/app/views/search/noresults.scala.html b/app/views/search/noresults.scala.html new file mode 100644 index 000000000..2a5c5c88c --- /dev/null +++ b/app/views/search/noresults.scala.html @@ -0,0 +1,16 @@ +@(currentUser: User, query: String) + +@main("Search results", sidebars.standard(currentUser), query, currentUser) { +

+ + Nothing found +

+ +

+ Your search returned no results. Take a look at the + documentation + if you need help with the search syntax. +

+ + @partials.support_sources() +} \ No newline at end of file diff --git a/app/views/sessions/login.scala.html b/app/views/sessions/login.scala.html index 5799cc860..4abca3ed2 100644 --- a/app/views/sessions/login.scala.html +++ b/app/views/sessions/login.scala.html @@ -1,28 +1,26 @@ @(loginForm: Form[LoginRequest]) -@auth(title = "Login") { +@auth(title = "Welcome to Graylog2 - Sign in") { -
+
-
- Welcome to Graylog2 - +
+ + Welcome to Graylog2 + + @if(flash.get("error") != null && !flash.get("error").isEmpty()) {
×@flash.get("error")
} - + @helper.form(action = controllers.routes.SessionsController.create()) { - - } - +
diff --git a/app/views/sidebars/searchresults.scala.html b/app/views/sidebars/searchresults.scala.html index b9ec12f94..ae6903188 100644 --- a/app/views/sidebars/searchresults.scala.html +++ b/app/views/sidebars/searchresults.scala.html @@ -9,6 +9,7 @@

Found @searchResult.getTotalResultCount() messages in @searchResult.getTookMs()ms.

+@if(searchResult.getTotalResultCount() > 0) {

Select fields

    @for(f <- searchResult.getFields()) { @@ -27,4 +28,5 @@

    Select fields


    Analyze field

    -... tba ... \ No newline at end of file +... tba ... +} \ No newline at end of file diff --git a/conf/routes b/conf/routes index bdaa6c4d3..319e965af 100755 --- a/conf/routes +++ b/conf/routes @@ -19,8 +19,9 @@ GET /messages/:index/:id/partial controllers.MessagesController.asPartial(index # Streams GET /streams controllers.StreamsController.index() -# API: Message counts -GET /a/messagecounts/total controllers.MessageCountsController.total(timerange ?= "") +# API: Message counts and histograms +GET /a/messagecounts/histogram controllers.MessageCountsController.histogram(timerange ?= "") +GET /a/messagecounts/total controllers.MessageCountsController.total() # API: Message analyzing GET /a/analyze/:index/:id/:field controllers.MessagesController.analyze(index: String, id: String, field: String) diff --git a/public/images/auth/loginbg.jpg b/public/images/auth/loginbg.jpg new file mode 100644 index 000000000..fb7b25a80 Binary files /dev/null and b/public/images/auth/loginbg.jpg differ diff --git a/public/images/auth/pattern.png b/public/images/auth/pattern.png deleted file mode 100644 index 47ab3a4a4..000000000 Binary files a/public/images/auth/pattern.png and /dev/null differ diff --git a/public/javascripts/graphs.js b/public/javascripts/graphs.js index 5927f02b8..11b9d7b81 100644 --- a/public/javascripts/graphs.js +++ b/public/javascripts/graphs.js @@ -1,6 +1,6 @@ $(document).ready(function() { - // Sidebar graph. (total message counts) + // Sidebar graph. (histogram message counts) if (document.querySelector("#sidebar-graph") != null) { var sidebarGraph = new Rickshaw.Graph.Ajax({ element: document.querySelector("#sidebar-graph"), @@ -9,7 +9,7 @@ $(document).ready(function() { renderer: 'bar', interpolation: 'linear', stroke: true, - dataURL: '/a/messagecounts/total?timerange=' + 2*60*60, // last two hours + dataURL: '/a/messagecounts/histogram?timerange=' + 2*60*60, // last two hours onData: function(d) { return transformData(d) }, onError: function(d) { error = " Could not load graph"; diff --git a/public/javascripts/main.js b/public/javascripts/main.js index a4c824414..59ca81bde 100755 --- a/public/javascripts/main.js +++ b/public/javascripts/main.js @@ -84,6 +84,24 @@ $(document).ready(function() { } }); + // Updating total event counts; + + (function updateTotalEvents() { + $.ajax({ + url: '/a/messagecounts/total', + success: function(data) { + $(".total-events").html(data.events); + }, + error: function() { + $(".total-events").html("?"); + }, + complete: function() { + setTimeout(updateTotalEvents, 2500); + } + }); + })(); + + function displayFailureInSidebar(message) { x = " " + message + "" $("#sidebar-inner").html(x); diff --git a/public/stylesheets/auth.css b/public/stylesheets/auth.css index 7c215825f..ab6278992 100644 --- a/public/stylesheets/auth.css +++ b/public/stylesheets/auth.css @@ -1,5 +1,26 @@ body { - background-color: #fff; - /* background-image: url("/assets/images/auth/pattern.png"); */ - margin-top: 30px; + font-family: 'Open Sans', sans-serif; + + /* we love science */ + background: url('/assets/images/auth/loginbg.jpg') no-repeat center center fixed; + -moz-background-size: cover; + -webkit-background-size: cover; + -o-background-size: cover; + background-size: cover; +} + +#login-box { + margin-top: 120px; +} + +#login-box-content { + background-color: #fff; + -moz-box-shadow: 0 0 5px #888; + -webkit-box-shadow: 0 0 5px#888; + box-shadow: 0 0 40px #000; +} + +.well { + padding-top: 8px; + padding-bottom: 2px; } \ No newline at end of file diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css index a4bbb99ca..96dcf2555 100644 --- a/public/stylesheets/main.css +++ b/public/stylesheets/main.css @@ -214,4 +214,8 @@ h3 { .terms-msg-modal .modal-body .as-list li { float: none !important; +} + +.support-sources ul { + margin-top: 5px; } \ No newline at end of file