summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaria Odea B. Ching <oching@apache.org>2008-10-22 07:03:57 +0000
committerMaria Odea B. Ching <oching@apache.org>2008-10-22 07:03:57 +0000
commitce43e1a9e0fd38a63b600fe098e7b8d1698237ce (patch)
tree94d958ddf6661171cdc187d2758736569056c07d
parentc303ba78c70a220c54b0501bf5239f5870d03ad9 (diff)
downloadarchiva-ce43e1a9e0fd38a63b600fe098e7b8d1698237ce.tar.gz
archiva-ce43e1a9e0fd38a63b600fe098e7b8d1698237ce.zip
[MRM-84]
- add export to CSV - migrated statistics report to struts2 - added date picker git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@706873 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/RepositoryStatisticsReportGenerator.java26
-rw-r--r--archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGenerator.java17
-rw-r--r--archiva-modules/archiva-reporting/archiva-report-manager/src/test/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGeneratorTest.java2
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/GenerateReportAction.java364
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml18
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/pickReport.jsp44
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp54
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/datepicker.css143
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.left.pngbin0 -> 181 bytes
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.right.pngbin0 -> 181 bytes
-rw-r--r--archiva-modules/archiva-web/archiva-webapp/src/main/webapp/js/datepicker/datepicker.js618
11 files changed, 1173 insertions, 113 deletions
diff --git a/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/RepositoryStatisticsReportGenerator.java b/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/RepositoryStatisticsReportGenerator.java
index 1b1e0aa78..dd11df8d8 100644
--- a/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/RepositoryStatisticsReportGenerator.java
+++ b/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/RepositoryStatisticsReportGenerator.java
@@ -42,9 +42,31 @@ public interface RepositoryStatisticsReportGenerator
public static final String ARCHETYPE = "archetype";
+ /**
+ * Generate report with limits. Used for pagination.
+ *
+ * @param repoContentStats
+ * @param repository
+ * @param startDate
+ * @param endDate
+ * @param limits
+ * @return
+ * @throws ArchivaReportException
+ */
public List<RepositoryStatistics> generateReport( List<RepositoryContentStatistics> repoContentStats, String repository, Date startDate, Date endDate, DataLimits limits )
throws ArchivaReportException;
- public List<RepositoryStatistics> generateReport( List<RepositoryContentStatistics> repoContentStats, String repository, Date startDate, Date endDate )
- throws ArchivaReportException;
+ /**
+ * Generate report without limits. Used for exporting the report.
+ *
+ * @param repoContentStats
+ * @param repository
+ * @param startDate
+ * @param endDate
+ * @param firstStatsOnly TODO
+ * @return
+ * @throws ArchivaReportException
+ */
+ public List<RepositoryStatistics> generateReport( List<RepositoryContentStatistics> repoContentStats, String repository, Date startDate, Date endDate, boolean firstStatsOnly )
+ throws ArchivaReportException;
}
diff --git a/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGenerator.java b/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGenerator.java
index 8eb75864b..3bf6d265b 100644
--- a/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGenerator.java
+++ b/archiva-modules/archiva-reporting/archiva-report-manager/src/main/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGenerator.java
@@ -80,14 +80,23 @@ public class SimpleRepositoryStatisticsReportGenerator
* {@inheritDoc}
*
* @see org.apache.maven.archiva.reporting.RepositoryStatisticsReportGenerator#generateReport(java.util.List
- * repoContentStats, java.util.String repository, java.util.Date startDate, java.util.Date endDate)
+ * repoContentStats, java.util.String repository, java.util.Date startDate, java.util.Date endDate, boolean firstStatsOnly)
*/
- public List<RepositoryStatistics> generateReport( List<RepositoryContentStatistics> repoContentStats, String repository, Date startDate, Date endDate )
+ public List<RepositoryStatistics> generateReport( List<RepositoryContentStatistics> repoContentStats,
+ String repository, Date startDate, Date endDate,
+ boolean firstStatsOnly )
throws ArchivaReportException
{
- return constructRepositoryStatistics( repoContentStats, repository, endDate, 0, repoContentStats.size() - 1 );
+ if( firstStatsOnly )
+ {
+ return constructRepositoryStatistics( repoContentStats, repository, endDate, 0, 0 );
+ }
+ else
+ {
+ return constructRepositoryStatistics( repoContentStats, repository, endDate, 0, repoContentStats.size() - 1 );
+ }
}
-
+
private List<RepositoryStatistics> constructRepositoryStatistics(
List<RepositoryContentStatistics> repoContentStats,
String repository, Date endDate,
diff --git a/archiva-modules/archiva-reporting/archiva-report-manager/src/test/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGeneratorTest.java b/archiva-modules/archiva-reporting/archiva-report-manager/src/test/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGeneratorTest.java
index 49828e0dc..4673e5564 100644
--- a/archiva-modules/archiva-reporting/archiva-report-manager/src/test/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGeneratorTest.java
+++ b/archiva-modules/archiva-reporting/archiva-report-manager/src/test/java/org/apache/maven/archiva/reporting/SimpleRepositoryStatisticsReportGeneratorTest.java
@@ -261,7 +261,7 @@ public class SimpleRepositoryStatisticsReportGeneratorTest
daoControl.replay();
artifactDaoControl.replay();
- List<RepositoryStatistics> data = generator.generateReport( repoContentStats, REPO, startDate, endDate );
+ List<RepositoryStatistics> data = generator.generateReport( repoContentStats, REPO, startDate, endDate, false );
daoControl.verify();
artifactDaoControl.verify();
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/GenerateReportAction.java b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/GenerateReportAction.java
index e33004110..89e2bad3a 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/GenerateReportAction.java
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/java/org/apache/maven/archiva/web/action/reports/GenerateReportAction.java
@@ -20,7 +20,9 @@ package org.apache.maven.archiva.web.action.reports;
*/
import com.opensymphony.xwork2.Preparable;
-import org.apache.commons.lang.time.DateFormatUtils;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.maven.archiva.configuration.ArchivaConfiguration;
import org.apache.maven.archiva.database.ArchivaDAO;
@@ -48,6 +50,10 @@ import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -141,6 +147,10 @@ public class GenerateReportAction
private String[] datePatterns = new String[] { "MM/dd/yy", "MM/dd/yyyy", "MMMMM/dd/yyyy", "MMMMM/dd/yy",
"dd MMMMM yyyy", "dd/MM/yy", "dd/MM/yyyy", "yyyy/MM/dd" };
+ public static final String SEND_FILE = "send-file";
+
+ private InputStream inputStream;
+
public void prepare()
{
repositoryIds = new ArrayList<String>();
@@ -190,47 +200,14 @@ public class GenerateReportAction
addFieldError( "rowCount", "Row count must be larger than 10." );
return INPUT;
}
- reposSize = selectedRepositories.size();
- Date startDateInDateFormat = null;
- Date endDateInDateFormat = null;
-
- if( startDate == null || "".equals( startDate ) )
- {
- startDateInDateFormat = getDefaultStartDate();
- }
- else
- {
- try
- {
- startDateInDateFormat = DateUtils.parseDate( startDate, datePatterns );
- }
- catch ( ParseException e )
- {
- addFieldError( "startDate", "Invalid date format.");
- return INPUT;
- }
- }
-
- if( endDate == null || "".equals( endDate ) )
- {
- endDateInDateFormat = getDefaultEndDate();
- }
- else
- {
- try
- {
- endDateInDateFormat = DateUtils.parseDate( endDate, datePatterns );
- }
- catch ( ParseException e )
- {
- addFieldError( "endDate", "Invalid date format.");
- return INPUT;
- }
- }
+ reposSize = selectedRepositories.size();
try
{
- RepositoryContentStatisticsDAO repoContentStatsDao = dao.getRepositoryContentStatisticsDAO();
+ RepositoryContentStatisticsDAO repoContentStatsDao = dao.getRepositoryContentStatisticsDAO();
+ Date startDateInDF = null;
+ Date endDateInDF = null;
+
if( selectedRepositories.size() > 1 )
{
limits.setTotalCount( selectedRepositories.size() );
@@ -238,34 +215,19 @@ public class GenerateReportAction
limits.setPerPageCount( 1 );
limits.setCountOfPages( 1 );
- // multiple repos
- for( String repo : selectedRepositories )
- {
- try
- {
- List contentStats = repoContentStatsDao.queryRepositoryContentStatistics(
- new RepositoryContentStatisticsByRepositoryConstraint( repo, startDateInDateFormat, endDateInDateFormat ) );
-
- if( contentStats == null || contentStats.isEmpty() )
- {
- log.info( "No statistics available for repository '" + repo + "'." );
- // TODO set repo's stats to 0
-
- continue;
- }
- repositoryStatistics.addAll( generator.generateReport( contentStats, repo, startDateInDateFormat, endDateInDateFormat, limits ) );
- }
- catch ( ObjectNotFoundException oe )
- {
- log.error( "No statistics available for repository '" + repo + "'." );
- // TODO set repo's stats to 0
- }
- catch ( ArchivaDatabaseException ae )
- {
- log.error( "Error encountered while querying statistics of repository '" + repo + "'." );
- // TODO set repo's stats to 0
- }
+ try
+ {
+ startDateInDF = getStartDateInDateFormat();
+ endDateInDF = getEndDateInDateFormat();
}
+ catch ( ParseException e )
+ {
+ addActionError( "Error parsing date(s)." );
+ return ERROR;
+ }
+
+ // multiple repos
+ generateReportForMultipleRepos(repoContentStatsDao, startDateInDF, endDateInDF, true);
}
else if ( selectedRepositories.size() == 1 )
{
@@ -274,9 +236,12 @@ public class GenerateReportAction
selectedRepo = selectedRepositories.get( 0 );
try
- {
+ {
+ startDateInDF = getStartDateInDateFormat();
+ endDateInDF = getEndDateInDateFormat();
+
List<RepositoryContentStatistics> contentStats = repoContentStatsDao.queryRepositoryContentStatistics(
- new RepositoryContentStatisticsByRepositoryConstraint( selectedRepo, startDateInDateFormat, endDateInDateFormat ) );
+ new RepositoryContentStatisticsByRepositoryConstraint( selectedRepo, startDateInDF, endDateInDF ) );
if( contentStats == null || contentStats.isEmpty() )
{
@@ -289,7 +254,7 @@ public class GenerateReportAction
int totalPages = ( limits.getTotalCount() / limits.getPerPageCount() ) + extraPage;
limits.setCountOfPages( totalPages );
- repositoryStatistics = generator.generateReport( contentStats, selectedRepo, startDateInDateFormat, endDateInDateFormat, limits );
+ repositoryStatistics = generator.generateReport( contentStats, selectedRepo, startDateInDF, endDateInDF, limits );
}
catch ( ObjectNotFoundException oe )
{
@@ -301,6 +266,11 @@ public class GenerateReportAction
addActionError( de.getMessage() );
return ERROR;
}
+ catch ( ParseException pe )
+ {
+ addActionError( pe.getMessage() );
+ return ERROR;
+ }
}
else
{
@@ -311,26 +281,261 @@ public class GenerateReportAction
if( repositoryStatistics.isEmpty() )
{
return BLANK;
- }
+ }
+ }
+ catch ( ArchivaReportException e )
+ {
+ addActionError( "Error encountered while generating report :: " + e.getMessage() );
+ return ERROR;
+ }
+
+ return SUCCESS;
+ }
+
+ /**
+ * Export report to CSV.
+ *
+ * @return
+ */
+ public String downloadStatisticsReport()
+ {
+ try
+ {
+ Date startDateInDF = null;
+ Date endDateInDF = null;
- if( startDate.equals( getDefaultStartDate() ) )
- {
- startDate = null;
+ selectedRepositories = parseSelectedRepositories();
+ repositoryStatistics = new ArrayList<RepositoryStatistics>();
+
+ RepositoryContentStatisticsDAO repoContentStatsDao = dao.getRepositoryContentStatisticsDAO();
+ if( selectedRepositories.size() > 1 )
+ {
+ try
+ {
+ startDateInDF = getStartDateInDateFormat();
+ endDateInDF = getEndDateInDateFormat();
+ }
+ catch ( ParseException e )
+ {
+ addActionError( "Error parsing date(s)." );
+ return ERROR;
+ }
+
+ // multiple repos
+ generateReportForMultipleRepos( repoContentStatsDao, startDateInDF, endDateInDF, false );
}
- else
+ else if ( selectedRepositories.size() == 1 )
{
- startDate = DateFormatUtils.format( startDateInDateFormat, "MM/dd/yyyy" );
+ selectedRepo = selectedRepositories.get( 0 );
+ try
+ {
+ startDateInDF = getStartDateInDateFormat();
+ endDateInDF = getEndDateInDateFormat();
+
+ List<RepositoryContentStatistics> contentStats = repoContentStatsDao.queryRepositoryContentStatistics(
+ new RepositoryContentStatisticsByRepositoryConstraint( selectedRepo, startDateInDF, endDateInDF ) );
+
+ if( contentStats == null || contentStats.isEmpty() )
+ {
+ addActionError( "No statistics available for repository. Repository might not have been scanned." );
+ return ERROR;
+ }
+
+ repositoryStatistics = generator.generateReport( contentStats, selectedRepo, startDateInDF, endDateInDF, false );
+ }
+ catch ( ObjectNotFoundException oe )
+ {
+ addActionError( oe.getMessage() );
+ return ERROR;
+ }
+ catch ( ArchivaDatabaseException de )
+ {
+ addActionError( de.getMessage() );
+ return ERROR;
+ }
+ catch ( ParseException pe )
+ {
+ addActionError( pe.getMessage() );
+ return ERROR;
+ }
}
+ else
+ {
+ addFieldError( "availableRepositories", "Please select a repository (or repositories) from the list." );
+ return INPUT;
+ }
- endDate = DateFormatUtils.format( endDateInDateFormat, "MM/dd/yyyy" );
+ if( repositoryStatistics.isEmpty() )
+ {
+ return BLANK;
+ }
}
catch ( ArchivaReportException e )
{
addActionError( "Error encountered while generating report :: " + e.getMessage() );
return ERROR;
+ }
+
+ // write output stream depending on single or comparison report
+ StringBuffer input = getInput();
+ StringReader reader = new StringReader( input.toString() );
+
+ try
+ {
+ inputStream = new ByteArrayInputStream( IOUtils.toByteArray( reader ) );
+ }
+ catch ( IOException i )
+ {
+ addActionError( "Error occurred while generating CSV file." );
+ return ERROR;
}
- return SUCCESS;
+ return SEND_FILE;
+ }
+
+ // hack for parsing the struts list passed as param in <s:url ../>
+ private List<String> parseSelectedRepositories()
+ {
+ List<String> pasedSelectedRepos = new ArrayList<String>();
+
+ for( String repo : selectedRepositories )
+ {
+ String[] tokens = StringUtils.split( repo, ',' );
+ if( tokens.length > 1 )
+ {
+ for( int i = 0; i < tokens.length; i++ )
+ {
+ pasedSelectedRepos.add( StringUtils.remove( StringUtils.remove( tokens[i], '[' ), ']' ).trim() );
+ }
+ }
+ else
+ {
+ pasedSelectedRepos.add( StringUtils.remove( StringUtils.remove( repo, '[' ), ']' ).trim() );
+ }
+ }
+ return pasedSelectedRepos;
+ }
+
+ private void generateReportForMultipleRepos( RepositoryContentStatisticsDAO repoContentStatsDao,
+ Date startDateInDF, Date endDateInDF, boolean useLimits )
+ throws ArchivaReportException
+ {
+ for ( String repo : selectedRepositories )
+ {
+ try
+ {
+ List contentStats = repoContentStatsDao.queryRepositoryContentStatistics(
+ new RepositoryContentStatisticsByRepositoryConstraint( repo, startDateInDF, endDateInDF ) );
+
+ if ( contentStats == null || contentStats.isEmpty() )
+ {
+ log.info( "No statistics available for repository '" + repo + "'." );
+ // TODO set repo's stats to 0
+ continue;
+ }
+
+ if( useLimits )
+ {
+ repositoryStatistics.addAll( generator.generateReport( contentStats, repo, startDateInDF, endDateInDF,
+ limits ) );
+ }
+ else
+ {
+ repositoryStatistics.addAll( generator.generateReport( contentStats, repo, startDateInDF, endDateInDF, true ) );
+ }
+ }
+ catch ( ObjectNotFoundException oe )
+ {
+ log.error( "No statistics available for repository '" + repo + "'." );
+ // TODO set repo's stats to 0
+ }
+ catch ( ArchivaDatabaseException ae )
+ {
+ log.error( "Error encountered while querying statistics of repository '" + repo + "'." );
+ // TODO set repo's stats to 0
+ }
+ }
+ }
+
+ private Date getStartDateInDateFormat()
+ throws ParseException
+ {
+ Date startDateInDF;
+ if ( startDate == null || "".equals( startDate ) )
+ {
+ startDateInDF = getDefaultStartDate();
+ }
+ else
+ {
+ startDateInDF = DateUtils.parseDate( startDate, datePatterns );
+ }
+ return startDateInDF;
+ }
+
+ private Date getEndDateInDateFormat()
+ throws ParseException
+ {
+ Date endDateInDF;
+ if ( endDate == null || "".equals( endDate ) )
+ {
+ endDateInDF = getDefaultEndDate();
+ }
+ else
+ {
+ endDateInDF = DateUtils.parseDate( endDate, datePatterns );
+ }
+
+ return endDateInDF;
+ }
+
+ private StringBuffer getInput()
+ {
+ StringBuffer input = null;
+
+ if( selectedRepositories.size() == 1 )
+ {
+ input = new StringBuffer( "Date of Scan,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
+ "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
+
+ for( RepositoryStatistics stats : repositoryStatistics )
+ {
+ input.append( stats.getDateOfScan() ).append( "," );
+ input.append( stats.getFileCount() ).append( "," );
+ input.append( stats.getTotalSize() ).append( "," );
+ input.append( stats.getArtifactCount() ).append( "," );
+ input.append( stats.getGroupCount() ).append( "," );
+ input.append( stats.getProjectCount() ).append( "," );
+ input.append( stats.getPluginCount() ).append( "," );
+ input.append( stats.getArchetypeCount() ).append( "," );
+ input.append( stats.getJarCount() ).append( "," );
+ input.append( stats.getWarCount() ).append( "," );
+ input.append( stats.getDeploymentCount() ).append( "," );
+ input.append( stats.getDownloadCount() ).append( "\n" );
+ }
+ }
+ else if( selectedRepositories.size() > 1 )
+ {
+ input = new StringBuffer( "Repository,Total File Count,Total Size,Artifact Count,Group Count,Project Count," +
+ "Plugins,Archetypes,Jars,Wars,Deployments,Downloads\n" );
+
+ for( RepositoryStatistics stats : repositoryStatistics )
+ {
+ input.append( stats.getRepositoryId() ).append( "," );
+ input.append( stats.getFileCount() ).append( "," );
+ input.append( stats.getTotalSize() ).append( "," );
+ input.append( stats.getArtifactCount() ).append( "," );
+ input.append( stats.getGroupCount() ).append( "," );
+ input.append( stats.getProjectCount() ).append( "," );
+ input.append( stats.getPluginCount() ).append( "," );
+ input.append( stats.getArchetypeCount() ).append( "," );
+ input.append( stats.getJarCount() ).append( "," );
+ input.append( stats.getWarCount() ).append( "," );
+ input.append( stats.getDeploymentCount() ).append( "," );
+ input.append( stats.getDownloadCount() ).append( "\n" );
+ }
+ }
+
+ return input;
}
private Date getDefaultStartDate()
@@ -645,4 +850,9 @@ public class GenerateReportAction
{
this.limits = limits;
}
+
+ public InputStream getInputStream()
+ {
+ return inputStream;
+ }
}
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml
index 8c867863c..af05d4e0c 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/resources/struts.xml
@@ -124,6 +124,7 @@
include a result for 'error' -->
<result name="error">/WEB-INF/jsp/generalError.jsp</result>
<result name="access_to_no_repos">/WEB-INF/jsp/accessToNoRepos.jsp</result>
+
</global-results>
</package>
@@ -503,6 +504,23 @@
<result name="blank">/WEB-INF/jsp/reports/blankReport.jsp</result>
<result>/WEB-INF/jsp/reports/basicReport.jsp</result>
</action>
+
+ <action name="generateStatisticsReport" class="generateReport" method="generateStatistics">
+ <result name="input">/WEB-INF/jsp/reports/pickReport.jsp</result>
+ <result name="blank">/WEB-INF/jsp/reports/blankReport.jsp</result>
+ <result>/WEB-INF/jsp/reports/statisticsReport.jsp</result>
+ </action>
+
+ <!-- TODO: make report filename dynamic -->
+ <action name="downloadStatsReport" class="generateReport" method="downloadStatisticsReport">
+ <result name="input">/WEB-INF/jsp/reports/pickReport.jsp</result>
+ <result name="blank">/WEB-INF/jsp/reports/blankReport.jsp</result>
+ <result name="send-file" type="stream">
+ <param name="contentType">${contentType}</param>
+ <param name="contentDisposition">attachment; filename="archiva_statistics_report.csv"</param>
+ </result>
+ </action>
+
</package>
</struts>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/pickReport.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/pickReport.jsp
index ee67fd314..0769f38a0 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/pickReport.jsp
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/pickReport.jsp
@@ -19,6 +19,9 @@
<%@ taglib prefix="s" uri="/struts-tags" %>
+<link type="text/css" rel="StyleSheet" href="/archiva/css/datepicker.css" />
+<script type="text/javascript" src="/archiva/js/datepicker/datepicker.js"></script>
+
<html>
<head>
<title>Reports</title>
@@ -31,16 +34,45 @@
<div id="contentArea">
<h2>Repository Statistics</h2>
- <s:form action="generateStatisticsReport" namespace="/report" validate="true">
+ <s:form action="generateStatisticsReport" namespace="/report" validate="false">
<s:optiontransferselect label="Repositories To Be Compared" name="availableRepositories"
list="availableRepositories" doubleName="selectedRepositories"
doubleList="selectedRepositories" size="8" doubleSize="8"/>
-
- <s:datetimepicker label="Start Date" name="startDate" id="startDate"/>
- <s:datetimepicker label="End Date" name="endDate" id="endDate" />
- <s:textfield label="Row Count" name="rowCount" />
-
+
+ <s:textfield label="Row Count" name="rowCount" />
+ <s:textfield label="Start Date" name="startDate" disabled="true"/>
+ <script type="text/javascript">
+ var d1 = new Date();
+ var dp1 = new DatePicker(d1);
+
+ var tables = document.forms[0].getElementsByTagName("table");
+ var myRow = tables[0].insertRow(3);
+ var actionsCell = myRow.insertCell(0);
+ var startDateCell = myRow.insertCell(1);
+ startDateCell.appendChild(dp1.create());
+
+ dp1.onchange = function () {
+ document.forms[0].startDate.value = dp1.getDate();
+ };
+ </script>
+
+ <s:textfield label="End Date" name="endDate" disabled="true"/>
+ <script type="text/javascript">
+ var d2 = new Date();
+ var dp2 = new DatePicker(d2);
+
+ var tables = document.forms[0].getElementsByTagName("table");
+ var myRow = tables[0].insertRow(5);
+ var actionsCell = myRow.insertCell(0);
+ var startDateCell = myRow.insertCell(1);
+ startDateCell.appendChild(dp2.create());
+
+ dp2.onchange = function () {
+ document.forms[0].endDate.value = dp2.getDate();
+ };
+ </script>
+
<s:submit value="View Statistics"/>
</s:form>
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp
index dd70758c2..d2ef79e4e 100644
--- a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/WEB-INF/jsp/reports/statisticsReport.jsp
@@ -17,14 +17,14 @@
~ under the License.
--%>
-<%@ taglib prefix="ww" uri="/webwork" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="archiva" uri="http://archiva.apache.org" %>
+<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Reports</title>
- <ww:head/>
+ <s:head/>
</head>
<body>
@@ -44,22 +44,22 @@
<%-- Set Prev & Next icons --%>
<c:set var="prevPageUrl">
- <ww:url action="generateStatisticsReport" namespace="/report">
- <ww:param name="selectedRepositories" value="%{'${selectedRepositories}'}"/>
- <ww:param name="rowCount" value="%{'${rowCount}'}"/>
- <ww:param name="startDate" value="%{'${startDate}'}"/>
- <ww:param name="endDate" value="%{'${endDate}'}"/>
- <ww:param name="page" value="%{'${page - 1}'}"/>
- </ww:url>
+ <s:url action="generateStatisticsReport" namespace="/report">
+ <s:param name="selectedRepositories" value="%{#attr.selectedRepositories}"/>
+ <s:param name="rowCount" value="%{#attr.rowCount}"/>
+ <s:param name="startDate" value="%{#attr.startDate}"/>
+ <s:param name="endDate" value="%{#attr.endDate}"/>
+ <s:param name="page" value="%{#attr.page - 1}"/>
+ </s:url>
</c:set>
<c:set var="nextPageUrl">
- <ww:url action="generateStatisticsReport" namespace="/report">
- <ww:param name="selectedRepositories" value="%{'${selectedRepositories}'}"/>
- <ww:param name="rowCount" value="%{'${rowCount}'}"/>
- <ww:param name="startDate" value="%{'${startDate}'}"/>
- <ww:param name="endDate" value="%{'${endDate}'}"/>
- <ww:param name="page" value="%{'${page + 1}'}"/>
- </ww:url>
+ <s:url action="generateStatisticsReport" namespace="/report">
+ <s:param name="selectedRepositories" value="%{#attr.selectedRepositories}"/>
+ <s:param name="rowCount" value="%{#attr.rowCount}"/>
+ <s:param name="startDate" value="%{#attr.startDate}"/>
+ <s:param name="endDate" value="%{#attr.endDate}"/>
+ <s:param name="page" value="%{#attr.page + 1}"/>
+ </s:url>
</c:set>
<c:choose>
@@ -101,13 +101,13 @@
<c:choose>
<c:when test="${i != (page - 1)}">
<c:set var="specificPageUrl">
- <ww:url action="generateStatisticsReport" namespace="/report">
- <ww:param name="selectedRepositories" value="%{'${selectedRepositories}'}"/>
- <ww:param name="rowCount" value="%{'${rowCount}'}"/>
- <ww:param name="startDate" value="%{'${startDate}'}"/>
- <ww:param name="endDate" value="%{'${endDate}'}"/>
- <ww:param name="page" value="%{'${page + 1}'}"/>
- </ww:url>
+ <s:url action="generateStatisticsReport" namespace="/report">
+ <s:param name="selectedRepositories" value="%{#attr.selectedRepositories}"/>
+ <s:param name="rowCount" value="%{#attr.rowCount}"/>
+ <s:param name="startDate" value="%{#attr.startDate}"/>
+ <s:param name="endDate" value="%{#attr.endDate}"/>
+ <s:param name="page" value="%{#attr.page + 1}"/>
+ </s:url>
</c:set>
<a href="${specificPageUrl}">${i + 1}</a>
</c:when>
@@ -130,6 +130,14 @@
</p>
<%-- Pagination - end --%>
+ <%-- Export to CSV link --%>
+ <s:url id="downloadStatsReportUrl" action="downloadStatsReport" namespace="/report">
+ <s:param name="selectedRepositories" value="%{#attr.selectedRepositories}"/>
+ <s:param name="startDate" value="%{#attr.startDate}"/>
+ <s:param name="endDate" value="%{#attr.endDate}"/>
+ </s:url>
+ <s:a href="%{downloadStatsReportUrl}">Export to CSV</s:a>
+
<c:choose>
<c:when test="${reposSize > 1}">
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/datepicker.css b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/datepicker.css
new file mode 100644
index 000000000..92bf74f96
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/datepicker.css
@@ -0,0 +1,143 @@
+.datePicker {
+ border: 1px solid WindowText;
+ background: Window;
+ width: 170px;
+ padding: 0px;
+ cursor: default;
+ -moz-user-focus: normal;
+}
+
+
+.datePicker td {
+ font: smallcaption;
+ font: small-caption;
+ text-align: center;
+ color: WindowText;
+ cursor: default;
+ font-weight: normal !important;
+ -moz-user-select: none;
+ padding: 0;
+}
+
+.datePicker td.red {
+ color: red;
+}
+
+.datePicker .header {
+ background: ActiveCaption;
+ padding: 3px;
+ border-bottom: 1px solid WindowText;
+}
+
+.datePicker .headerTable {
+ width: 100%;
+}
+
+.datePicker .footer {
+ padding: 3px;
+}
+
+.datePicker .footerTable {
+ width: 100%;
+}
+
+.datePicker .grid {
+ padding: 3px;
+}
+.datePicker .gridTable {
+ width: 100%;
+}
+
+.datePicker .gridTable td {
+ width: 14.3%;
+}
+
+.datePicker .gridTable .daysRow td {
+ font-weight: bold !important;
+ border-bottom: 1px solid ThreeDDarkShadow;
+}
+
+.datePicker .grid .gridTable .upperLine {
+ width: 100%;
+ height: 2px;
+ overflow: hidden;
+ background: transparent;
+}
+
+.datePicker td.today {
+ font-weight: bold !important;
+}
+
+.datePicker td.selected {
+ background: Highlight;
+ color: HighlightText !important;
+}
+
+.datePicker td.labelContainer {
+ width: 100%;
+}
+
+.datePicker td .topLabel {
+ color: CaptionText;
+ display: block;
+ font-weight: bold !important;
+ width: 100%;
+ text-decoration: none;
+
+}
+
+.datePicker td.filler {
+ width: 100%;
+}
+
+.datePicker button {
+ border-width: 1px;
+ font: Caption;
+ font-weight: normal !important;
+ display: block;
+}
+
+.datePicker .previousButton {
+ background: buttonface url("../images/arrow.left.png") no-repeat center center;
+}
+
+.datePicker .nextButton {
+ background: buttonface url("../images/arrow.right.png") no-repeat center center;
+}
+.datePicker .previousButton,
+.datePicker .nextButton {
+ width: 14px;
+ height: 14px;
+}
+
+.datePicker .todayButton,
+.datePicker .noneButton {
+ width: 50px;
+}
+
+
+.datePicker .labelPopup {
+ position: absolute;
+ min-width: 130px;
+ background: Window;
+ border: 1px solid WindowText;
+ padding: 1px;
+}
+
+.datePicker .labelPopup a {
+ width: 100%;
+ display: block;
+ color: WindowText;
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+.datePicker .labelPopup a:hover {
+ background: Highlight;
+ color: HighlightText;
+}
+
+.datePicker .labelPopup a.selected {
+ font-weight: bold;
+}
+
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.left.png b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.left.png
new file mode 100644
index 000000000..93085aaaa
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.left.png
Binary files differ
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.right.png b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.right.png
new file mode 100644
index 000000000..79abee507
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.right.png
Binary files differ
diff --git a/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/js/datepicker/datepicker.js b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/js/datepicker/datepicker.js
new file mode 100644
index 000000000..97c442122
--- /dev/null
+++ b/archiva-modules/archiva-web/archiva-webapp/src/main/webapp/js/datepicker/datepicker.js
@@ -0,0 +1,618 @@
+/*----------------------------------------------------------------------------\
+| Date Picker 1.06 |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| A DOM based Date Picker |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 1999, 2002, 2002, 2003, 2004, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| 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. |
+|-----------------------------------------------------------------------------|
+| Dependencies: datepicker.css Date picker style declarations |
+|-----------------------------------------------------------------------------|
+| 2002-02-10 | Changed _update method to only update the text nodes instead |
+| | rewriting the entire table. Also added support for mouse wheel |
+| | in IE6. |
+| 2002-01-14 | Cleaned up for 1.0 public version |
+| 2002-01-15 | Replace all innerHTML calls with DOM1 methods |
+| 2002-01-18 | Minor IE6 bug that occured when dragging the mouse |
+| 2002-01-19 | Added a popup that is shown when the user clicks on the month. |
+| | This allows navigation to 6 adjacent months. |
+| 2002-04-10 | Fixed a bug that occured in the popup when a date was selected |
+| | that caused surroundung months to "overflow" |
+| | This had the effect that one could get two October months |
+| | listed. |
+| 2002-09-06 | I had missed one place were window was used instead of |
+| | doc.parentWindow |
+| 2003-08-28 | Added support for ensurin no date overflow when changing |
+| | months. |
+| 2004-01-10 | Adding type on the buttons to ensure they are not submit |
+| | buttons. Minor CSS change for CSS2 |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2001-10-?? | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+// The DatePicker constructor
+// oDate : Date Optional argument representing the date to select
+function DatePicker( oDate ) {
+ // check arguments
+ if ( arguments.length == 0 ) {
+ this._selectedDate = new Date;
+ this._none = false;
+ }
+ else {
+ this._selectedDate = oDate || new Date();
+ this._none = oDate == null;
+ }
+
+ this._matrix = [[],[],[],[],[],[],[]];
+ this._showNone = true;
+ this._showToday = true;
+ this._firstWeekDay = 0; // start week with monday according to standards
+ this._redWeekDay = 6; // sunday is the default red day.
+
+ this._dontChangeNone = false;
+}
+
+// two static fields describing the name of the months abd days
+DatePicker.months = [
+ "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December"];
+DatePicker.days = ["m", "t", "w", "t", "f", "s", "s"];
+
+
+DatePicker.prototype.onchange = function () {};
+
+// create the nodes inside the date picker
+DatePicker.prototype.create = function ( doc ) {
+ if ( doc == null ) doc = document;
+
+ this._document = doc;
+
+ // create elements
+ this._el = doc.createElement( "div" );
+ this._el.className = "datePicker";
+
+ // header
+ var div = doc.createElement( "div" );
+ div.className = "header";
+ this._el.appendChild( div );
+
+ var headerTable = doc.createElement( "table" );
+ headerTable.className = "headerTable";
+ headerTable.cellSpacing = 0;
+ div.appendChild( headerTable );
+
+ var tBody = doc.createElement( "tbody" );
+ headerTable.appendChild( tBody );
+
+ var tr = doc.createElement( "tr" );
+ tBody.appendChild( tr );
+
+ var td = doc.createElement( "td" );
+ this._previousMonth = doc.createElement( "button" );
+ this._previousMonth.className = "previousButton";
+ this._previousMonth.setAttribute("type", "button");
+ td.appendChild( this._previousMonth );
+ tr.appendChild( td );
+
+ td = doc.createElement( "td" );
+ td.className = "labelContainer";
+ tr.appendChild( td );
+
+ this._topLabel = doc.createElement( "a" );
+ this._topLabel.className = "topLabel";
+ this._topLabel.href = "#";
+ this._topLabel.appendChild( doc.createTextNode( String.fromCharCode( 160 ) ) );
+ td.appendChild( this._topLabel );
+
+ this._labelPopup = doc.createElement( "div" );
+ this._labelPopup.className = "labelPopup";
+ // no insertion
+
+ td = doc.createElement( "td" );
+ this._nextMonth = doc.createElement( "button" );
+ this._nextMonth.className = "nextButton";
+ this._nextMonth.setAttribute("type", "button");
+ td.appendChild( this._nextMonth );
+ tr.appendChild( td );
+
+ // grid
+ div = doc.createElement( "div" );
+ div.className = "grid";
+ this._el.appendChild( div );
+ this._table = div;
+
+ // footer
+ div = doc.createElement( "div" );
+ div.className = "footer";
+ this._el.appendChild( div );
+
+ var footerTable = doc.createElement( "table" );
+ footerTable.className = "footerTable";
+ footerTable.cellSpacing = 0;
+ div.appendChild( footerTable );
+
+ tBody = doc.createElement( "tbody" );
+ footerTable.appendChild( tBody );
+
+ tr = doc.createElement( "tr" );
+ tBody.appendChild( tr );
+
+ td = doc.createElement( "td" );
+ this._todayButton = doc.createElement( "button" );
+ this._todayButton.className = "todayButton";
+ this._todayButton.setAttribute("type", "button");
+ this._todayButton.appendChild( doc.createTextNode( "Today" ) );
+ td.appendChild( this._todayButton );
+ tr.appendChild( td );
+
+ td = doc.createElement( "td" );
+ td.className = "filler";
+ td.appendChild( doc.createTextNode( String.fromCharCode( 160 ) ) );
+ tr.appendChild( td );
+
+ td = doc.createElement( "td" );
+ this._noneButton = doc.createElement( "button" );
+ this._noneButton.className = "noneButton";
+ this._noneButton.setAttribute("type", "button");
+ this._noneButton.appendChild( doc.createTextNode( "None" ) );
+ td.appendChild( this._noneButton );
+ tr.appendChild( td );
+
+
+
+ this._createTable( doc );
+
+ this._updateTable();
+ this._setTopLabel();
+
+ if ( !this._showNone )
+ this._noneButton.style.visibility = "hidden";
+ if ( !this._showToday )
+ this._todayButton.style.visibility = "hidden";
+
+ // IE55+ extension
+ this._previousMonth.hideFocus = true;
+ this._nextMonth.hideFocus = true;
+ this._todayButton.hideFocus = true;
+ this._noneButton.hideFocus = true;
+ // end IE55+ extension
+
+ // hook up events
+ var dp = this;
+ // buttons
+ this._previousMonth.onclick = function () {
+ dp._dontChangeNone = true;
+ dp.goToPreviousMonth();
+ dp._dontChangeNone = false;
+ };
+ this._nextMonth.onclick = function () {
+ dp._dontChangeNone = true;
+ dp.goToNextMonth();
+ dp._dontChangeNone = false;
+ };
+ this._todayButton.onclick = function () {
+ dp.goToToday();
+ };
+ this._noneButton.onclick = function () {
+ dp.setDate( null );
+ };
+
+ this._el.onselectstart = function () {
+ return false;
+ };
+
+ this._table.onclick = function ( e ) {
+ // find event
+ if ( e == null ) e = doc.parentWindow.event;
+
+ // find td
+ var el = e.target != null ? e.target : e.srcElement;
+ while ( el.nodeType != 1 )
+ el = el.parentNode;
+ while ( el != null && el.tagName && el.tagName.toLowerCase() != "td" )
+ el = el.parentNode;
+
+ // if no td found, return
+ if ( el == null || el.tagName == null || el.tagName.toLowerCase() != "td" )
+ return;
+
+ var d = new Date( dp._selectedDate );
+ var n = Number( el.firstChild.data );
+ if ( isNaN( n ) || n <= 0 || n == null )
+ return;
+
+ d.setDate( n );
+ dp.setDate( d );
+ };
+
+ // show popup
+ this._topLabel.onclick = function ( e ) {
+ dp._showLabelPopup();
+ return false;
+ };
+
+ this._el.onkeydown = function ( e ) {
+ if ( e == null ) e = doc.parentWindow.event;
+ var kc = e.keyCode != null ? e.keyCode : e.charCode;
+
+ if ( kc < 37 || kc > 40 ) return true;
+
+ var d = new Date( dp._selectedDate ).valueOf();
+ if ( kc == 37 ) // left
+ d -= 24 * 60 * 60 * 1000;
+ else if ( kc == 39 ) // right
+ d += 24 * 60 * 60 * 1000;
+ else if ( kc == 38 ) // up
+ d -= 7 * 24 * 60 * 60 * 1000;
+ else if ( kc == 40 ) // down
+ d += 7 * 24 * 60 * 60 * 1000;
+
+ dp.setDate( new Date( d ) );
+ return false;
+ }
+
+ // ie6 extension
+ this._el.onmousewheel = function ( e ) {
+ if ( e == null ) e = doc.parentWindow.event;
+ var n = - e.wheelDelta / 120;
+ var d = new Date( dp._selectedDate );
+ var m = d.getMonth() + n;
+ d.setMonth( m );
+
+
+ dp._dontChangeNone = true;
+ dp.setDate( d );
+ dp._dontChangeNone = false;
+
+ return false;
+ }
+
+ return this._el;
+};
+
+DatePicker.prototype.setDate = function ( oDate ) {
+
+ this._hideLabelPopup();
+
+ // if null then set None
+ if ( oDate == null ) {
+ if ( !this._none ) {
+ this._none = true;
+ this._setTopLabel();
+ this._updateTable();
+
+ if ( typeof this.onchange == "function" )
+ this.onchange();
+ }
+ return;
+ }
+
+ // if string or number create a Date object
+ if ( typeof oDate == "string" || typeof oDate == "number" ) {
+ oDate = new Date( oDate );
+ }
+
+
+ // do not update if not really changed
+ if ( this._selectedDate.getDate() != oDate.getDate() ||
+ this._selectedDate.getMonth() != oDate.getMonth() ||
+ this._selectedDate.getFullYear() != oDate.getFullYear() ||
+ this._none ) {
+
+ if ( !this._dontChangeNone )
+ this._none = false;
+
+ this._selectedDate = new Date( oDate );
+
+ this._setTopLabel();
+ this._updateTable();
+
+ if ( typeof this.onchange == "function" )
+ this.onchange();
+ }
+
+ if ( !this._dontChangeNone )
+ this._none = false;
+}
+
+
+DatePicker.prototype.getDate = function () {
+ if ( this._none ) return null;
+ return new Date( this._selectedDate ); // create a new instance
+}
+
+// creates the table elements and inserts them into the date picker
+DatePicker.prototype._createTable = function ( doc ) {
+ var str, i;
+ var rows = 6;
+ var cols = 7;
+ var currentWeek = 0;
+
+ var table = doc.createElement( "table" );
+ table.className = "gridTable";
+ table.cellSpacing = 0;
+
+ var tBody = doc.createElement( "tbody" );
+ table.appendChild( tBody );
+
+ // days row
+ var tr = doc.createElement( "tr" );
+ tr.className = "daysRow";
+
+ var td, tn;
+ var nbsp = String.fromCharCode( 160 );
+ for ( i = 0; i < cols; i++ ) {
+ td = doc.createElement( "td" );
+ td.appendChild( doc.createTextNode( nbsp ) );
+ tr.appendChild( td );
+ }
+ tBody.appendChild( tr );
+
+ // upper line
+ tr = doc.createElement( "tr" );
+ td = doc.createElement( "td" );
+ td.className = "upperLine";
+ td.colSpan = 7;
+ tr.appendChild( td );
+ tBody.appendChild( tr );
+
+ // rest
+ for ( i = 0; i < rows; i++ ) {
+ tr = doc.createElement( "tr" );
+ for ( var j = 0; j < cols; j++ ) {
+ td = doc.createElement( "td" );
+ td.appendChild( doc.createTextNode( nbsp ) );
+ tr.appendChild( td );
+ }
+ tBody.appendChild( tr );
+ }
+ str += "</table>";
+
+ if ( this._table != null )
+ this._table.appendChild( table )
+};
+// this method updates all the text nodes inside the table as well
+// as all the classNames on the tds
+DatePicker.prototype._updateTable = function () {
+ // if no element no need to continue
+ if ( this._table == null ) return;
+
+ var i;
+ var str = "";
+ var rows = 6;
+ var cols = 7;
+ var currentWeek = 0;
+
+ var cells = new Array( rows );
+ this._matrix = new Array( rows )
+ for ( i = 0; i < rows; i++ ) {
+ cells[i] = new Array( cols );
+ this._matrix[i] = new Array( cols );
+ }
+
+ // Set the tmpDate to this month
+ var tmpDate = new Date( this._selectedDate.getFullYear(),
+ this._selectedDate.getMonth(), 1 );
+ var today = new Date();
+ // go thorugh all days this month and store the text
+ // and the class name in the cells matrix
+ for ( i = 1; i < 32; i++ ) {
+ tmpDate.setDate( i );
+ // convert to ISO, Monday is 0 and 6 is Sunday
+ var weekDay = ( tmpDate.getDay() + 6 ) % 7;
+ var colIndex = ( weekDay - this._firstWeekDay + 7 ) % 7;
+ if ( tmpDate.getMonth() == this._selectedDate.getMonth() ) {
+
+ var isToday = tmpDate.getDate() == today.getDate() &&
+ tmpDate.getMonth() == today.getMonth() &&
+ tmpDate.getFullYear() == today.getFullYear();
+
+ cells[currentWeek][colIndex] = { text: "", className: "" };
+
+ if ( this._selectedDate.getDate() == tmpDate.getDate() && !this._none )
+ cells[currentWeek][colIndex].className += "selected ";
+ if ( isToday )
+ cells[currentWeek][colIndex].className += "today ";
+ if ( ( tmpDate.getDay() + 6 ) % 7 == this._redWeekDay ) // ISO
+ cells[currentWeek][colIndex].className += "red";
+
+ cells[currentWeek][colIndex].text =
+ this._matrix[currentWeek][colIndex] = tmpDate.getDate();
+
+ if ( colIndex == 6 )
+ currentWeek++;
+ }
+ }
+
+ // fix day letter order if not standard
+ var weekDays = DatePicker.days;
+ if (this._firstWeekDay != 0) {
+ weekDays = new Array(7);
+ for ( i = 0; i < 7; i++)
+ weekDays[i] = DatePicker.days[ (i + this._firstWeekDay) % 7];
+ }
+
+ // update text in days row
+ var tds = this._table.firstChild.tBodies[0].rows[0].cells;
+ for ( i = 0; i < cols; i++ )
+ tds[i].firstChild.data = weekDays[i];
+
+ // update the text nodes and class names
+ var trs = this._table.firstChild.tBodies[0].rows;
+ var tmpCell;
+ var nbsp = String.fromCharCode( 160 );
+ for ( var y = 0; y < rows; y++ ) {
+ for (var x = 0; x < cols; x++) {
+ tmpCell = trs[y + 2].cells[x];
+ if ( typeof cells[y][x] != "undefined" ) {
+ tmpCell.className = cells[y][x].className;
+ tmpCell.firstChild.data = cells[y][x].text;
+ }
+ else {
+ tmpCell.className = "";
+ tmpCell.firstChild.data = nbsp;
+ }
+ }
+ }
+}
+
+// sets the label showing the year and selected month
+DatePicker.prototype._setTopLabel = function () {
+ var str = this._selectedDate.getFullYear() + " " + DatePicker.months[ this._selectedDate.getMonth() ];
+ if ( this._topLabel != null )
+ this._topLabel.lastChild.data = str;
+}
+
+DatePicker.prototype.goToNextMonth = function () {
+ var d = new Date( this._selectedDate );
+ d.setDate( Math.min(d.getDate(), DatePicker.getDaysPerMonth(d.getMonth() + 1,
+ d.getFullYear())) ); // no need to catch dec -> jan for the year
+ d.setMonth( d.getMonth() + 1 );
+ this.setDate( d );
+}
+
+DatePicker.prototype.goToPreviousMonth = function () {
+ var d = new Date( this._selectedDate );
+ d.setDate( Math.min(d.getDate(), DatePicker.getDaysPerMonth(d.getMonth() - 1,
+ d.getFullYear())) ); // no need to catch jan -> dec for the year
+ d.setMonth( d.getMonth() - 1 );
+ this.setDate( d );
+}
+
+DatePicker.prototype.goToToday = function () {
+ if ( this._none )
+ // change the selectedDate to force update if none was true
+ this._selectedDate = new Date( this._selectedDate + 10000000000 );
+ this._none = false;
+ this.setDate( new Date() );
+}
+
+DatePicker.prototype.setShowToday = function ( bShowToday ) {
+ if ( typeof bShowToday == "string" )
+ bShowToday = !/false|0|no/i.test( bShowToday );
+
+ if ( this._todayButton != null )
+ this._todayButton.style.visibility = bShowToday ? "visible" : "hidden";
+ this._showToday = bShowToday;
+}
+
+DatePicker.prototype.getShowToday = function () {
+ return this._showToday;
+}
+
+DatePicker.prototype.setShowNone = function ( bShowNone ) {
+ if ( typeof bShowNone == "string" )
+ bShowNone = !/false|0|no/i.test( bShowNone );
+
+ if ( this._noneButton != null )
+ this._noneButton.style.visibility = bShowNone ? "visible" : "hidden";
+ this._showNone = bShowNone;
+}
+
+DatePicker.prototype.getShowNone = function () {
+ return this._showNone;
+}
+
+// 0 is monday and 6 is sunday as in the ISO standard
+DatePicker.prototype.setFirstWeekDay = function ( nFirstWeekDay ) {
+ if ( this._firstWeekDay != nFirstWeekDay ) {
+ this._firstWeekDay = nFirstWeekDay;
+ this._updateTable();
+ }
+}
+
+DatePicker.prototype.getFirstWeekDay = function () {
+ return this._firstWeekDay;
+}
+
+// 0 is monday and 6 is sunday as in the ISO standard
+DatePicker.prototype.setRedWeekDay = function ( nRedWeekDay ) {
+ if ( this._redWeekDay != nRedWeekDay ) {
+ this._redWeekDay = nRedWeekDay;
+ this._updateTable();
+ }
+}
+
+DatePicker.prototype.getRedWeekDay = function () {
+ return this._redWeekDay;
+}
+
+
+DatePicker.prototype._showLabelPopup = function () {
+
+ /*
+ this._labelPopup document.createElement( "DIV" );
+ div.className = "month-popup";
+ div.noWrap = true;
+ el.unselectable = div.unselectable = "on";
+ el.onselectstart = div.onselectstart = function () { return false; };
+ */
+
+ var dateContext = function ( dp, d ) {
+ return function ( e ) {
+ dp._dontChangeNone = true;
+ dp._hideLabelPopup();
+ dp.setDate( d );
+ dp._dontChangeNone = false;
+ return false;
+ };
+ };
+
+ var dp = this;
+
+ // clear all old elements in the popup
+ while ( this._labelPopup.hasChildNodes() )
+ this._labelPopup.removeChild( this._labelPopup.firstChild );
+
+ var a, tmp, tmp2;
+ for ( var i = -3; i < 4; i++ ) {
+ tmp = new Date( this._selectedDate );
+ tmp2 = new Date( this._selectedDate ); // need another tmp to catch year change when checking leap
+ tmp2.setDate(1);
+ tmp2.setMonth( tmp2.getMonth() + i );
+ tmp.setDate( Math.min(tmp.getDate(), DatePicker.getDaysPerMonth(tmp.getMonth() + i,
+ tmp2.getFullYear())) );
+ tmp.setMonth( tmp.getMonth() + i );
+
+ a = this._document.createElement( "a" );
+ a.href = "javascript:void 0;";
+ a.onclick = dateContext( dp, tmp );
+ a.appendChild( this._document.createTextNode( tmp.getFullYear() + " " +
+ DatePicker.months[ tmp.getMonth() ] ) );
+ if ( i == 0 )
+ a.className = "selected";
+ this._labelPopup.appendChild( a );
+ }
+
+ this._topLabel.parentNode.insertBefore( this._labelPopup, this._topLabel.parentNode.firstChild );
+};
+
+DatePicker.prototype._hideLabelPopup = function () {
+ if ( this._labelPopup.parentNode )
+ this._labelPopup.parentNode.removeChild( this._labelPopup );
+};
+
+DatePicker._daysPerMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
+DatePicker.getDaysPerMonth = function (nMonth, nYear) {
+ nMonth = (nMonth + 12) % 12;
+ var res = DatePicker._daysPerMonth[nMonth];
+ if (nMonth == 1) {
+ res += nYear % 4 == 0 && !(nYear % 400 == 0) ? 1 : 0;
+ }
+ return res;
+}; \ No newline at end of file