浏览代码

merge with master

Jason Rivard 3 年之前
父节点
当前提交
214975ccf6

+ 5 - 2
server/src/main/java/password/pwm/http/servlet/admin/SystemAdminServlet.java

@@ -55,6 +55,8 @@ import password.pwm.svc.pwnotify.PwNotifyStoredJobState;
 import password.pwm.svc.stats.StatisticsService;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.MiscUtil;
+import password.pwm.util.java.PwmTimeUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.LocalDBLogger;
@@ -509,8 +511,9 @@ public class SystemAdminServlet extends ControlledPwmServlet
 
             if ( pwNotifyStoredJobState.getLastStart() != null && pwNotifyStoredJobState.getLastCompletion() != null )
             {
+                final TimeDuration lastJobDuration = TimeDuration.between( pwNotifyStoredJobState.getLastStart(), pwNotifyStoredJobState.getLastCompletion() );
                 statusData.add( new DisplayElement( String.valueOf( key++ ), DisplayElement.Type.timestamp,
-                        "Last Job Duration", TimeDuration.between( pwNotifyStoredJobState.getLastStart(), pwNotifyStoredJobState.getLastCompletion() ).asLongString( locale ) ) );
+                        "Last Job Duration", PwmTimeUtil.asLongString( lastJobDuration, locale ) ) );
             }
 
             if ( StringUtil.notEmpty( pwNotifyStoredJobState.getServerInstance() ) )
@@ -703,7 +706,7 @@ public class SystemAdminServlet extends ControlledPwmServlet
             break;
 
             default:
-                JavaHelper.unhandledSwitchStatement( logDownloadType );
+                MiscUtil.unhandledSwitchStatement( logDownloadType );
 
         }
 

+ 2 - 1
server/src/main/java/password/pwm/http/servlet/configeditor/function/UserMatchViewerFunction.java

@@ -48,6 +48,7 @@ import password.pwm.i18n.Display;
 import password.pwm.ldap.permission.UserPermissionType;
 import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.util.i18n.LocaleHelper;
+import password.pwm.util.java.PwmTimeUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 
@@ -87,7 +88,7 @@ public class UserMatchViewerFunction implements SettingUIFunction
         final String message = LocaleHelper.getLocalizedMessage(
                 Display.Display_SearchResultsInfo, pwmRequest,
                 String.valueOf( users.size() ),
-                searchDuration.asLongString( pwmRequest.getLocale() ) );
+                PwmTimeUtil.asLongString( searchDuration, pwmRequest.getLocale() ) );
 
         final boolean sizeExceeded = users.size() >= maxResultSize;
 

+ 9 - 8
server/src/main/java/password/pwm/svc/report/ReportCsvRecordWriter.java

@@ -26,7 +26,8 @@ import password.pwm.config.SettingReader;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.PwmDisplayBundle;
 import password.pwm.util.i18n.LocaleHelper;
-import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.MiscUtil;
+import password.pwm.util.java.StringUtil;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -43,7 +44,7 @@ class ReportCsvRecordWriter implements ReportRecordWriter
     ReportCsvRecordWriter( final OutputStream outputStream, final PwmApplication pwmApplication, final Locale locale )
             throws IOException
     {
-        this.csvPrinter = JavaHelper.makeCsvPrinter( outputStream );
+        this.csvPrinter = MiscUtil.makeCsvPrinter( outputStream );
         this.locale = locale;
         this.pwmApplication = pwmApplication;
     }
@@ -101,19 +102,19 @@ class ReportCsvRecordWriter implements ReportRecordWriter
         csvRow.add( userReportRecord.getUserGUID() );
         csvRow.add( userReportRecord.getAccountExpirationTime() == null
                 ? naField
-                : JavaHelper.toIsoDate( userReportRecord.getAccountExpirationTime() ) );
+                : StringUtil.toIsoDate( userReportRecord.getAccountExpirationTime() ) );
         csvRow.add( userReportRecord.getPasswordExpirationTime() == null
                 ? naField
-                : JavaHelper.toIsoDate( userReportRecord.getPasswordExpirationTime() ) );
+                : StringUtil.toIsoDate( userReportRecord.getPasswordExpirationTime() ) );
         csvRow.add( userReportRecord.getPasswordChangeTime() == null
                 ? naField
-                : JavaHelper.toIsoDate( userReportRecord.getPasswordChangeTime() ) );
+                : StringUtil.toIsoDate( userReportRecord.getPasswordChangeTime() ) );
         csvRow.add( userReportRecord.getResponseSetTime() == null
                 ? naField
-                : JavaHelper.toIsoDate( userReportRecord.getResponseSetTime() ) );
+                : StringUtil.toIsoDate( userReportRecord.getResponseSetTime() ) );
         csvRow.add( userReportRecord.getLastLoginTime() == null
                 ? naField
-                : JavaHelper.toIsoDate( userReportRecord.getLastLoginTime() ) );
+                : StringUtil.toIsoDate( userReportRecord.getLastLoginTime() ) );
         csvRow.add( userReportRecord.isHasResponses() ? trueField : falseField );
         csvRow.add( userReportRecord.isHasHelpdeskResponses() ? trueField : falseField );
         csvRow.add( userReportRecord.getResponseStorageMethod() == null
@@ -131,7 +132,7 @@ class ReportCsvRecordWriter implements ReportRecordWriter
         csvRow.add( userReportRecord.isRequiresProfileUpdate() ? trueField : falseField );
         csvRow.add( userReportRecord.getCacheTimestamp() == null
                 ? naField
-                : JavaHelper.toIsoDate( userReportRecord.getCacheTimestamp() ) );
+                : StringUtil.toIsoDate( userReportRecord.getCacheTimestamp() ) );
 
         csvPrinter.printRecord( csvRow );
     }

+ 26 - 11
server/src/main/java/password/pwm/svc/report/ReportProcess.java

@@ -42,6 +42,8 @@ import password.pwm.ldap.permission.UserPermissionUtility;
 import password.pwm.util.EventRateMeter;
 import password.pwm.util.java.ConditionalTaskExecutor;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.MiscUtil;
+import password.pwm.util.java.PwmTimeUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.json.JsonFactory;
 import password.pwm.util.json.JsonProvider;
@@ -113,7 +115,7 @@ public class ReportProcess implements AutoCloseable
         this.debugOutputLogger = ConditionalTaskExecutor.forPeriodicTask(
                 () -> LOGGER.trace( sessionLabel, () -> "live report #" + reportId + " in progress: " + recordCounter.longValue() + " records exported",
                         () -> TimeDuration.fromCurrent( startTime ) ),
-                TimeDuration.MINUTE );
+                TimeDuration.MINUTE.asDuration() );
 
     }
 
@@ -138,7 +140,7 @@ public class ReportProcess implements AutoCloseable
     {
 
         final List<ReportSummaryData.PresentationRow> outputList = reportSummaryData.asPresentableCollection( config, locale );
-        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter( outputStream );
+        final CSVPrinter csvPrinter = MiscUtil.makeCsvPrinter( outputStream );
 
         for ( final ReportSummaryData.PresentationRow presentationRow : outputList )
         {
@@ -213,13 +215,13 @@ public class ReportProcess implements AutoCloseable
                 ? new ReportJsonRecordWriter( zipOutputStream )
                 : new ReportCsvRecordWriter( zipOutputStream, pwmApplication, locale );
 
-        processReport( reportProcessRequest, zipOutputStream, recordWriter );
+        final boolean recordLimitReached = processReport( reportProcessRequest, zipOutputStream, recordWriter );
 
         checkCancel( zipOutputStream );
         outputSummary( zipOutputStream );
 
         checkCancel( zipOutputStream );
-        outputResult( reportProcessRequest, zipOutputStream );
+        outputResult( reportProcessRequest, zipOutputStream, recordLimitReached );
 
         LOGGER.trace( sessionLabel, () -> "completed liveReport generation with " + recordCounter.longValue() + " records",
                 () -> TimeDuration.fromCurrent( startTime ) );
@@ -237,7 +239,8 @@ public class ReportProcess implements AutoCloseable
 
     private void outputResult(
             final ReportProcessRequest request,
-            final ZipOutputStream zipOutputStream
+            final ZipOutputStream zipOutputStream,
+            final boolean recordLimitReached
     )
             throws IOException
     {
@@ -246,7 +249,8 @@ public class ReportProcess implements AutoCloseable
                 this.recordCounter.incrementAndGet(),
                 startTime,
                 Instant.now(),
-                TimeDuration.fromCurrent( startTime ) );
+                TimeDuration.fromCurrent( startTime ),
+                recordLimitReached );
 
         final String jsonData = JsonFactory.get().serialize( result, ReportProcessResult.class, JsonProvider.Flag.PrettyPrint );
 
@@ -272,7 +276,7 @@ public class ReportProcess implements AutoCloseable
         }
     }
 
-    private void processReport(
+    private boolean processReport(
             final ReportProcessRequest reportProcessRequest,
             final ZipOutputStream zipOutputStream,
             final ReportRecordWriter recordWriter
@@ -283,20 +287,30 @@ public class ReportProcess implements AutoCloseable
 
         recordWriter.outputHeader();
 
+        int processCounter = 0;
+        boolean recordLimitReached = false;
+
         for (
                 final Iterator<PwmDomain> domainIterator = applicableDomains( reportProcessRequest ).iterator();
-                domainIterator.hasNext() && !cancelFlag.get();
+                domainIterator.hasNext() && !cancelFlag.get() && !recordLimitReached;
         )
         {
             final PwmDomain pwmDomain = domainIterator.next();
 
             for (
                     final Iterator<UserReportRecord> reportRecordQueue = executeUserRecordReadJobs( reportProcessRequest, pwmDomain );
-                    reportRecordQueue.hasNext() && !cancelFlag.get();
+                    reportRecordQueue.hasNext() && !cancelFlag.get() && !recordLimitReached;
             )
             {
+                processCounter++;
+
+                if ( processCounter >= reportProcessRequest.getMaximumRecords() )
+                {
+                    recordLimitReached = true;
+                }
+
                 final UserReportRecord userReportRecord = reportRecordQueue.next();
-                final boolean lastRecord = !reportRecordQueue.hasNext() && !domainIterator.hasNext();
+                final boolean lastRecord = recordLimitReached || ( !reportRecordQueue.hasNext() && !domainIterator.hasNext() );
                 recordWriter.outputRecord( userReportRecord, lastRecord );
                 perRecordOutputTasks( userReportRecord, zipOutputStream );
             }
@@ -306,6 +320,7 @@ public class ReportProcess implements AutoCloseable
         recordWriter.close();
 
         zipOutputStream.closeEntry();
+        return recordLimitReached;
     }
 
     private Iterator<UserReportRecord> executeUserRecordReadJobs(
@@ -414,7 +429,7 @@ public class ReportProcess implements AutoCloseable
                     String.valueOf( recordCounter.longValue() ) ) );
             list.add( new DisplayElement( "duration", DisplayElement.Type.string,
                     "Duration",
-                    TimeDuration.fromCurrent( startTime ).asLongString( locale ) ) );
+                    PwmTimeUtil.asLongString( TimeDuration.fromCurrent( startTime ), locale ) ) );
             if ( recordCounter.get() > 0 )
             {
                 final String rate = processRateMeter.readEventRate()

+ 1 - 0
server/src/main/java/password/pwm/svc/report/ReportProcessResult.java

@@ -34,4 +34,5 @@ public class ReportProcessResult implements Serializable
     private final Instant startTime;
     private final Instant finishTime;
     private final TimeDuration timeDuration;
+    private final boolean recordLimitReached;
 }

+ 202 - 82
webapp/src/main/webapp/WEB-INF/jsp/admin-analysis.jsp

@@ -69,96 +69,205 @@
     <div id="centerbody" class="wide">
         <h1 id="page-content-title"><pwm:display key="Title_DataAnalysis" bundle="Admin"/></h1>
         <%@ include file="fragment/admin-nav.jsp" %>
-        <div class="tab-container" style="width: 100%; height: 100%;">
-            <input name="es_tabs" type="radio" id="tab-2.1" checked="checked" class="input"/>
-            <label for="tab-2.1" class="label"><pwm:display key="Title_RawStatistics" bundle="Admin"/></label>
-            <div class="tab-content-pane" title="<pwm:display key="Title_RawStatistics" bundle="Admin"/>" class="tabContent">
-                <div style="max-height: 500px; overflow-y: auto">
-                    <table>
-                        <tr>
-                            <td colspan="10" style="text-align: center">
-                                <form action="<pwm:current-url/>" method="GET" enctype="application/x-www-form-urlencoded"
-                                      name="statsUpdateForm" id="statsUpdateForm">
-                                    <select name="statsPeriodSelect"
-                                            style="width: 350px;">
-                                        <option value="<%=StatisticsService.KEY_CUMULATIVE%>" <%= StatisticsService.KEY_CUMULATIVE.equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
-                                            since installation - <span class="timestamp"><%= JavaHelper.toIsoDate(analysis_pwmRequest.getPwmApplication().getInstallTime()) %></span>
-                                        </option>
-                                        <option value="<%=StatisticsService.KEY_CURRENT%>" <%= StatisticsService.KEY_CURRENT.equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
-                                            since startup - <span class="timestamp"><%= JavaHelper.toIsoDate(analysis_pwmRequest.getPwmApplication().getStartupTime()) %></span>
-                                        </option>
-                                        <% final Map<DailyKey, String> availableKeys = statsManager.getAvailableKeys(locale); %>
-                                        <% for (final Map.Entry<DailyKey, String> entry : availableKeys.entrySet()) { %>
-                                        <% final DailyKey key = entry.getKey(); %>
-                                        <option value="<%=key%>" <%= key.toString().equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
-                                            <%=key.localDate().format(DateTimeFormatter.ISO_LOCAL_DATE)%>
-                                        </option>
-                                        <% } %>
-                                    </select>
-                                    <button class="btn" type="submit">
-                                        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
-                                        <pwm:display key="Button_Refresh" bundle="Admin"/>
-                                    </button>
-                                </form>
-                            </td>
-                        </tr>
-                        <% for (final Statistic loopStat : Statistic.sortedValues(locale)) { %>
-                        <tr>
-                            <td >
-                                        <span id="Statistic_Key_<%=loopStat.getKey()%>"><%= loopStat.getLabel(locale) %><span/>
-                            </td>
-                            <td>
-                                <%= stats.getStatistic(loopStat) %>
-                            </td>
-                        </tr>
-                        <% } %>
-                        <% for (final AvgStatistic loopStat : AvgStatistic.values()) { %>
-                        <tr>
-                            <td >
-                                        <span id="Statistic_Key_<%=loopStat.getKey()%>"><%= loopStat.getLabel(locale) %><span/>
-                            </td>
-                            <td>
-                                <%= stats.getAvgStatistic(loopStat) %><%= loopStat.getUnit() %>
-                            </td>
-                        </tr>
-                        <% } %>
-                    </table>
-                </div>
-                <div class="noticebar">
-                    <pwm:display key="Notice_EventStatistics" bundle="Admin"/>
-                </div>
-                <div style="text-align: center">
-                    <form class="submitToDownloadForm" action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
-                        <button type="submit" class="btn" id="button-downloadStatisticsLogCsv">
-                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
-                            <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
-                        </button>
-                        <input type="hidden" name="processAction" value="downloadStatisticsLogCsv"/>
-                        <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
-                    </form>
+        <div class="tab-container" style="width: 100%; height: 100%;" id="analysis-topLevelTab">
+            <input name="tabs" type="radio" id="tab-1" checked="checked" class="input"/>
+            <label for="tab-1" class="label"><pwm:display key="Title_DirectoryReporting" bundle="Admin"/></label>
+            <div class="tab-content-pane" style="width: 100%; height: 100%;" title="<pwm:display key="Title_DirectoryReporting" bundle="Admin"/>">
+                <div class="tab-container" style="width: 100%; height: 100%;">
+                    <input name="dr_tabs" type="radio" id="tab-1.1" checked="checked" class="input"/>
+                    <label for="tab-1.1" class="label"><pwm:display key="Title_ReportEngineStatus" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_ReportEngineStatus" bundle="Admin"/>" class="tabContent">
+                        <table style="width:450px" id="statusTable">
+                            <tr><td><pwm:display key="Display_PleaseWait"/></td></tr>
+                        </table>
+                        <table style="width:450px;">
+                            <tr><td style="text-align: center; cursor: pointer">
+                                <button id="reportStartButton" class="btn">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-play">&nbsp;</span></pwm:if>
+                                    <pwm:display key="Button_Report_Start" bundle="Admin"/>
+                                </button>
+                                &nbsp;&nbsp;
+                                <button id="reportStopButton" class="btn">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-stop">&nbsp;</span></pwm:if>
+                                    <pwm:display key="Button_Report_Stop" bundle="Admin"/>
+                                </button>
+                                &nbsp;&nbsp;
+                                <button id="reportClearButton" class="btn">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-trash-o">&nbsp;</span></pwm:if>
+                                    <pwm:display key="Button_Report_Clear" bundle="Admin"/>
+                                </button>
+                            </td></tr>
+                        </table>
+                    </div>
+
+                    <input name="dr_tabs" type="radio" id="tab-1.2" class="input"/>
+                    <label for="tab-1.2" class="label">Summary</label>
+                    <div class="tab-content-pane" title="Summary" class="tabContent">
+                        <div style="max-height: 500px; overflow-y: auto" id="summaryTableWrapper">
+                            <table id="summaryTable">
+                                <tr><td><pwm:display key="Display_PleaseWait"/></td></tr>
+                            </table>
+                        </div>
+                        <div class="noticebar">
+                            <pwm:display key="Notice_DynamicRefresh" bundle="Admin"/>
+                            <pwm:display key="Notice_ReportSummary" bundle="Admin"/>
+                        </div>
+                        <div style="text-align: center">
+                            <form class="submitToDownloadForm" action="<pwm:current-url/>" method="post">
+                                <button type="submit" class="btn" id="button-downloadUserSummaryCsv">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download">&nbsp;</span></pwm:if>
+                                    <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                                </button>
+                                <input type="hidden" name="processAction" value="downloadUserSummaryCsv"/>
+                                <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                            </form>
+                        </div>
+                    </div>
+
+                    <input name="dr_tabs" type="radio" id="tab-1.3" class="input"/>
+                    <label for="tab-1.3" class="label"><pwm:display key="Title_DataViewer" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_DataViewer" bundle="Admin"/>" class="tabContent">
+                        <div id="grid">
+                        </div>
+                        <div style="text-align: center">
+                            <input name="maxResults" id="maxReportDataResults" value="1000" type="number" style="width: 70px"
+                                   min="10" max="50000" step="100"/>
+                            Rows
+                            <button class="btn" type="button" id="button-refreshReportDataGrid">
+                                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
+                                <pwm:display key="Button_Refresh" bundle="Admin"/>
+                            </button>
+                            <form class="submitToDownloadForm" id="downloadUserReportCsvForm" action="<pwm:current-url/>" method="post">
+                                <button type="submit" class="btn" id="button-downloadUserReportCsv">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download">&nbsp;</span></pwm:if>
+                                    <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                                </button>
+                                <pwm:script>
+                                    <script type="application/javascript">
+                                        PWM_GLOBAL['startupFunctions'].push(function(){
+                                            PWM_MAIN.showTooltip({
+                                                id: 'button-downloadUserReportCsv',
+                                                text: '<pwm:display key="Tooltip_DownloadReportRecords" bundle="Admin"/>',
+                                                width: 350
+                                            });
+
+                                        });
+                                    </script>
+                                </pwm:script>
+                                <input type="hidden" name="processAction" value="downloadUserReportCsv"/>
+                                <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                                <input type="hidden" name="selectedColumns" value="" />
+                            </form>
+                        </div>
+                        <pwm:script>
+                            <script type="application/javascript">
+                                PWM_GLOBAL['startupFunctions'].push(function(){
+                                    PWM_ADMIN.initReportDataGrid();
+                                });
+                            </script>
+                        </pwm:script>
+                    </div>
+
+                    <div class="tab-end"></div>
                 </div>
             </div>
 
-            <input name="es_tabs" type="radio" id="tab-2.2" class="input"/>
-            <label for="tab-2.2" class="label"><pwm:display key="Title_StatisticsCharts" bundle="Admin"/></label>
-            <div class="tab-content-pane" title="<pwm:display key="Title_StatisticsCharts" bundle="Admin"/>" class="tabContent">
-                <div style="height:100%; width: 100%">
-                    <div id="statsChartOptionsDiv" style="width:580px; text-align: center; margin:0 auto;">
-                        <label for="statsChartSelect">Statistic</label>
-                        <select name="statsChartSelect" id="statsChartSelect" style="width: 300px;">
-                            <% for (final Statistic loopStat : Statistic.sortedValues(locale)) { %>
-                            <option value="<%=loopStat %>"><%=loopStat.getLabel(locale)%></option>
-                            <% } %>
-                        </select>
-                        <label for="statsChartDays" style="padding-left: 10px">Days</label>
-                        <input id="statsChartDays" value="30" type="number" style="width: 60px" min="7" max="120"/>
+            <input name="tabs" type="radio" id="tab-2" class="input"/>
+            <label for="tab-2" class="label"><pwm:display key="Title_EventStatistics" bundle="Admin"/></label>
+            <div class="tab-content-pane" style="width: 100%; height: 100%;" title="<pwm:display key="Title_EventStatistics" bundle="Admin"/>">
+                <div class="tab-container" style="width: 100%; height: 100%;">
+                    <input name="es_tabs" type="radio" id="tab-2.1" checked="checked" class="input"/>
+                    <label for="tab-2.1" class="label"><pwm:display key="Title_RawStatistics" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_RawStatistics" bundle="Admin"/>" class="tabContent">
+                        <div style="max-height: 500px; overflow-y: auto">
+                            <table>
+                                <tr>
+                                    <td colspan="10" style="text-align: center">
+                                        <form action="<pwm:current-url/>" method="GET" enctype="application/x-www-form-urlencoded"
+                                              name="statsUpdateForm" id="statsUpdateForm">
+                                            <select name="statsPeriodSelect"
+                                                    style="width: 350px;">
+                                                <option value="<%=StatisticsService.KEY_CUMULATIVE%>" <%= StatisticsService.KEY_CUMULATIVE.equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
+                                                    since installation - <span class="timestamp"><%= StringUtil.toIsoDate(analysis_pwmRequest.getPwmApplication().getInstallTime()) %></span>
+                                                </option>
+                                                <option value="<%=StatisticsService.KEY_CURRENT%>" <%= StatisticsService.KEY_CURRENT.equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
+                                                    since startup - <span class="timestamp"><%= StringUtil.toIsoDate(analysis_pwmRequest.getPwmApplication().getStartupTime()) %></span>
+                                                </option>
+                                                <% final Map<DailyKey, String> availableKeys = statsManager.getAvailableKeys(locale); %>
+                                                <% for (final Map.Entry<DailyKey, String> entry : availableKeys.entrySet()) { %>
+                                                <% final DailyKey key = entry.getKey(); %>
+                                                <option value="<%=key%>" <%= key.toString().equals(statsPeriodSelect) ? "selected=\"selected\"" : "" %>>
+                                                    <%=key.localDate().format(DateTimeFormatter.ISO_LOCAL_DATE)%>
+                                                </option>
+                                                <% } %>
+                                            </select>
+                                            <button class="btn" type="submit">
+                                                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh">&nbsp;</span></pwm:if>
+                                                <pwm:display key="Button_Refresh" bundle="Admin"/>
+                                            </button>
+                                        </form>
+                                    </td>
+                                </tr>
+                                <% for (final Statistic loopStat : Statistic.sortedValues(locale)) { %>
+                                <tr>
+                                    <td >
+                                        <span id="Statistic_Key_<%=loopStat.getKey()%>"><%= loopStat.getLabel(locale) %><span/>
+                                    </td>
+                                    <td>
+                                        <%= stats.getStatistic(loopStat) %>
+                                    </td>
+                                </tr>
+                                <% } %>
+                                <% for (final AvgStatistic loopStat : AvgStatistic.values()) { %>
+                                <tr>
+                                    <td >
+                                        <span id="Statistic_Key_<%=loopStat.getKey()%>"><%= loopStat.getLabel(locale) %><span/>
+                                    </td>
+                                    <td>
+                                        <%= stats.getAvgStatistic(loopStat) %><%= loopStat.getUnit() %>
+                                    </td>
+                                </tr>
+                                <% } %>
+                            </table>
+                        </div>
+                        <div class="noticebar">
+                            <pwm:display key="Notice_EventStatistics" bundle="Admin"/>
+                        </div>
+                        <div style="text-align: center">
+                            <form class="submitToDownloadForm" action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded">
+                                <button type="submit" class="btn" id="button-downloadStatisticsLogCsv">
+                                    <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
+                                    <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                                </button>
+                                <input type="hidden" name="processAction" value="downloadStatisticsLogCsv"/>
+                                <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                            </form>
+                        </div>
                     </div>
-                    <div id="statsChart">
+
+                    <input name="es_tabs" type="radio" id="tab-2.2" class="input"/>
+                    <label for="tab-2.2" class="label"><pwm:display key="Title_StatisticsCharts" bundle="Admin"/></label>
+                    <div class="tab-content-pane" title="<pwm:display key="Title_StatisticsCharts" bundle="Admin"/>" class="tabContent">
+                        <div style="height:100%; width: 100%">
+                            <div id="statsChartOptionsDiv" style="width:580px; text-align: center; margin:0 auto;">
+                                <label for="statsChartSelect">Statistic</label>
+                                <select name="statsChartSelect" id="statsChartSelect" style="width: 300px;">
+                                    <% for (final Statistic loopStat : Statistic.sortedValues(locale)) { %>
+                                    <option value="<%=loopStat %>"><%=loopStat.getLabel(locale)%></option>
+                                    <% } %>
+                                </select>
+                                <label for="statsChartDays" style="padding-left: 10px">Days</label>
+                                <input id="statsChartDays" value="30" type="number" style="width: 60px" min="7" max="120"/>
+                            </div>
+                            <div id="statsChart">
+                            </div>
+                        </div>
                     </div>
+
+                    <div class="tab-end"></div>
                 </div>
             </div>
 
-            <div class="tab-end"></div>
         </div>
     </div>
     <div class="push"></div>
@@ -181,10 +290,21 @@
                     refreshChart();
                 },5*1000);
 
+                PWM_ADMIN.refreshReportDataSummary();
+                PWM_ADMIN.refreshReportDataStatus();
+                setInterval(function () { PWM_ADMIN.refreshReportDataSummary() }, 5 * 1000);
+                setInterval(function () { PWM_ADMIN.refreshReportDataStatus() }, 5 * 1000);
+
                 <% for (final Statistic loopStat : Statistic.sortedValues(locale)) { %>
                 PWM_MAIN.showTooltip({id:'Statistic_Key_<%=loopStat.getKey()%>',width:400,position:'above',text:PWM_ADMIN.showString("Statistic_Description.<%=loopStat.getKey()%>")});
                 <% } %>
 
+                PWM_MAIN.addEventHandler('button-refreshReportDataGrid','click',function(){
+                    PWM_ADMIN.refreshReportDataGrid();
+                });
+                PWM_MAIN.addEventHandler('reportStartButton','click',function(){ PWM_ADMIN.reportAction('Start') });
+                PWM_MAIN.addEventHandler('reportStopButton','click',function(){ PWM_ADMIN.reportAction('Stop') });
+                PWM_MAIN.addEventHandler('reportClearButton','click',function(){ PWM_ADMIN.reportAction('Clear') });
                 PWM_MAIN.addEventHandler('statsChartSelect','change',function(){ refreshChart() });
 
                 if (dojo.isIE) {