From ce43e1a9e0fd38a63b600fe098e7b8d1698237ce Mon Sep 17 00:00:00 2001 From: "Maria Odea B. Ching" Date: Wed, 22 Oct 2008 07:03:57 +0000 Subject: [PATCH] [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 --- .../RepositoryStatisticsReportGenerator.java | 26 +- ...leRepositoryStatisticsReportGenerator.java | 17 +- ...positoryStatisticsReportGeneratorTest.java | 2 +- .../action/reports/GenerateReportAction.java | 364 ++++++++--- .../src/main/resources/struts.xml | 18 + .../webapp/WEB-INF/jsp/reports/pickReport.jsp | 44 +- .../WEB-INF/jsp/reports/statisticsReport.jsp | 54 +- .../src/main/webapp/css/datepicker.css | 143 ++++ .../src/main/webapp/images/arrow.left.png | Bin 0 -> 181 bytes .../src/main/webapp/images/arrow.right.png | Bin 0 -> 181 bytes .../main/webapp/js/datepicker/datepicker.js | 618 ++++++++++++++++++ 11 files changed, 1173 insertions(+), 113 deletions(-) create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/webapp/css/datepicker.css create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.left.png create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/webapp/images/arrow.right.png create mode 100644 archiva-modules/archiva-web/archiva-webapp/src/main/webapp/js/datepicker/datepicker.js 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 generateReport( List repoContentStats, String repository, Date startDate, Date endDate, DataLimits limits ) throws ArchivaReportException; - public List generateReport( List 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 generateReport( List 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 generateReport( List repoContentStats, String repository, Date startDate, Date endDate ) + public List generateReport( List 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 constructRepositoryStatistics( List 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 data = generator.generateReport( repoContentStats, REPO, startDate, endDate ); + List 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(); @@ -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 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(); + + 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 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 + private List parseSelectedRepositories() + { + List pasedSelectedRepos = new ArrayList(); + + 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' --> /WEB-INF/jsp/generalError.jsp /WEB-INF/jsp/accessToNoRepos.jsp + @@ -503,6 +504,23 @@ /WEB-INF/jsp/reports/blankReport.jsp /WEB-INF/jsp/reports/basicReport.jsp + + + /WEB-INF/jsp/reports/pickReport.jsp + /WEB-INF/jsp/reports/blankReport.jsp + /WEB-INF/jsp/reports/statisticsReport.jsp + + + + + /WEB-INF/jsp/reports/pickReport.jsp + /WEB-INF/jsp/reports/blankReport.jsp + + ${contentType} + attachment; filename="archiva_statistics_report.csv" + + + 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" %> + + + Reports @@ -31,16 +34,45 @@

Repository Statistics

- + - - - - - + + + + + + + + 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" %> Reports - + @@ -44,22 +44,22 @@ <%-- Set Prev & Next icons --%> - - - - - - - + + + + + + + - - - - - - - + + + + + + + @@ -101,13 +101,13 @@ - - - - - - - + + + + + + + ${i + 1} @@ -130,6 +130,14 @@

<%-- Pagination - end --%> + <%-- Export to CSV link --%> + + + + + + Export to CSV + 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 0000000000000000000000000000000000000000..93085aaaa86a42abe02bf445922b5a946812c3b6 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y>0U`~puh;=8mUKs7M+S!VC(K#9UIO`&C9V-A z!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$Z9paj_=LCu>Hq)#FTFYcE|A4k666>B zpW*3t11}&?-P6S}q~camLP`n)hXR`$8(RZI5(85M1Ct^UZ)9ZZVANm}NMK+{VPFrq S6l4w5!r0U`~puh;=8mUKs7M+S!VC(K#9UIO`&C9V-A z!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$Z9paj_=LCu>Hq)#FTFYcE|A4k666>B zpW*3t11}&?-P6S}q~camLP81)hXNZLn_B||QxY4HPz2(Qj7%II5{yhd3=A<0?4cd& RGJskbJYD@<);T3K0RY=JE`9(2 literal 0 HcmV?d00001 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 += ""; + + 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 -- 2.39.5