From e2985e2ecdca5b0f7b91f5857024c1b4f52874c6 Mon Sep 17 00:00:00 2001 From: Graham Triggs Date: Tue, 9 Sep 2008 12:45:34 +0000 Subject: [PATCH] integrated from branch 1.5 Statistics viewer for XMLUI, based on existing DStat. Note that this generates the view from the analysis files (.dat), does not require HTML report generation. git-svn-id: http://scm.dspace.org/svn/repo/trunk@3102 9c30dcfa-912a-0410-8fc2-9e0234be79fd --- .../org/dspace/app/statistics/HTMLReport.java | 33 +- .../org/dspace/app/statistics/Report.java | 24 +- .../app/statistics/ReportGenerator.java | 154 ++--- .../app/statistics/StatisticsLoader.java | 397 +++++++++++ .../aspect/administrative/Navigation.java | 111 +-- .../aspect/artifactbrowser/Navigation.java | 12 - .../artifactbrowser/StatisticsViewer.java | 640 ++++++++++++++++++ .../aspects/ArtifactBrowser/sitemap.xmap | 70 +- .../src/main/webapp/i18n/messages.xml | 266 ++++---- .../webapp/themes/Reference/lib/style.css | 4 + 10 files changed, 1366 insertions(+), 345 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/app/statistics/StatisticsLoader.java create mode 100644 dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/StatisticsViewer.java diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/HTMLReport.java b/dspace-api/src/main/java/org/dspace/app/statistics/HTMLReport.java index 388d81ba72..08c9d930d0 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/HTMLReport.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/HTMLReport.java @@ -44,6 +44,7 @@ import org.dspace.app.statistics.Report; import org.dspace.app.statistics.Stat; import org.dspace.app.statistics.Statistics; import org.dspace.app.statistics.ReportTools; +import org.dspace.core.ConfigurationManager; import java.text.DateFormat; @@ -54,13 +55,14 @@ import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.io.*; /** * This class provides HTML reports for the ReportGenerator class * * @author Richard Jones */ -public class HTMLReport extends Report +public class HTMLReport implements Report { // FIXME: all of these methods should do some content escaping before // outputting anything @@ -79,6 +81,10 @@ public class HTMLReport extends Report /** end date for report */ private Date end = null; + + /** the output file to which to write aggregation data */ + private static String output = ConfigurationManager.getProperty("dspace.dir") + + File.separator + "log" + File.separator + "report"; /** * constructor for HTML reporting @@ -87,6 +93,14 @@ public class HTMLReport extends Report { // empty constructor } + + public void setOutput(String newOutput) + { + if (newOutput != null) + { + output = newOutput; + } + } /** * return a string containing the report as generated by this class @@ -119,6 +133,23 @@ public class HTMLReport extends Report // output the footer and return frag.append(footer()); + + // NB: HTMLReport now takes responsibility to write the output file, + // so that the Report/ReportGenerator can have more general usage + // finally write the string into the output file + try + { + FileOutputStream fos = new FileOutputStream(output); + OutputStreamWriter osr = new OutputStreamWriter(fos, "UTF-8"); + PrintWriter out = new PrintWriter(osr); + out.write(frag.toString()); + out.close(); + } + catch (IOException e) + { + System.out.println("Unable to write to output file " + output); + System.exit(0); + } return frag.toString(); } diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/Report.java b/dspace-api/src/main/java/org/dspace/app/statistics/Report.java index 8685fac0ff..69cc9bb4b8 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/Report.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/Report.java @@ -48,30 +48,18 @@ import java.util.Date; import java.util.List; /** - * Abstract class to define an interface to a generic report generating + * Sn interface to a generic report generating * class, and to provide the polymorphism necessary to allow the report * generator to generate any number of different formats of report * + * Note: This used to be an abstract class, but has been made an interface as there wasn't + * any logic contained within it. It's also been made public, so that you can create a Report + * type without monkeying about in the statistics package. + * * @author Richard Jones */ -abstract class Report +public interface Report { - - /** a list of the statistics blocks being managed by this class */ - private List blocks = new ArrayList(); - - /** the title for the page */ - private String pageTitle = null; - - /** the main title for the page */ - private String mainTitle = null; - - /** start date for report */ - private Date start = null; - - /** end date for report */ - private Date end = null; - /** * output any top headers that this page needs * diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/ReportGenerator.java b/dspace-api/src/main/java/org/dspace/app/statistics/ReportGenerator.java index e655f5ebef..407d48297d 100644 --- a/dspace-api/src/main/java/org/dspace/app/statistics/ReportGenerator.java +++ b/dspace-api/src/main/java/org/dspace/app/statistics/ReportGenerator.java @@ -70,6 +70,7 @@ import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.sql.SQLException; /** * This class performs the action of coordinating a usage report being @@ -172,16 +173,9 @@ public class ReportGenerator // report generator config data //////////////// - /** the format of the report to be output */ - private static String format = null; - /** the input file to build the report from */ private static String input = null; - /** the output file to which to write aggregation data */ - private static String output = ConfigurationManager.getProperty("dspace.dir") + - File.separator + "log" + File.separator + "report"; - /** the log file action to human readable action map */ private static String map = ConfigurationManager.getProperty("dspace.dir") + File.separator + "config" + File.separator + "dstat.map"; @@ -240,6 +234,9 @@ public class ReportGenerator * aggregation data and output a file containing the report in the * requested format * + * this method is retained for backwards compatibility, but delegates the actual + * wprk to a new method + * * @param context the DSpace context in which this action is performed * @param myFormat the desired output format (currently on HTML supported) * @param myInput the aggregation file to be turned into a report @@ -249,6 +246,32 @@ public class ReportGenerator String myInput, String myOutput, String myMap) throws Exception + { + // create the relevant report type + // FIXME: at the moment we only support HTML report generation + Report report = null; + if (myFormat.equals("html")) + { + report = new HTMLReport(); + ((HTMLReport)report).setOutput(myOutput); + } + + if (myMap != null) + { + map = myMap; + } + + ReportGenerator.processReport(context, report, myInput); + } + + /** + * using the pre-configuration information passed here, read in the + * aggregation data and output a file containing the report in the + * requested format + */ + public static void processReport(Context context, Report report, + String myInput) + throws Exception, SQLException { startTime = new GregorianCalendar(); @@ -264,7 +287,7 @@ public class ReportGenerator generalSummary = new ArrayList(); // set the parameters for this analysis - setParameters(myFormat, myInput, myOutput, myMap); + setParameters(myInput); // pre prepare our standard file readers and buffered readers FileReader fr = null; @@ -276,14 +299,6 @@ public class ReportGenerator // load the log file action to human readable action map readMap(map); - // create the relevant report type - // FIXME: at the moment we only support HTML report generation - Report report = null; - if (format.equals("html")) - { - report = new HTMLReport(); - } - report.setStartDate(startDate); report.setEndDate(endDate); report.setMainTitle(name, serverName); @@ -352,7 +367,8 @@ public class ReportGenerator String info = null; for (i = 0; i < items.length; i++) { - if (i < itemLookup) + // Allow negative value to say that all items should be looked up + if (itemLookup < 0 || i < itemLookup) { info = getItemInfo(context, items[i].getKey()); } @@ -459,21 +475,8 @@ public class ReportGenerator process.add(proc); report.addBlock(process); - - // finally write the string into the output file - try - { - FileOutputStream fos = new FileOutputStream(output); - OutputStreamWriter osr = new OutputStreamWriter(fos, "UTF-8"); - PrintWriter out = new PrintWriter(osr); - out.write(report.render()); - out.close(); - } - catch (IOException e) - { - System.out.println("Unable to write to output file " + output); - System.exit(0); - } + + report.render(); return; } @@ -595,34 +598,15 @@ public class ReportGenerator * the command line with args or calling the processReport method statically * from elsewhere * - * @param myFormat the log file directory to be analysed * @param myInput regex for log file names - * @param myOutput config file to use for dstat - * @param myMap the action map file to use for translations */ - public static void setParameters(String myFormat, String myInput, - String myOutput, String myMap) + public static void setParameters(String myInput) { - if (myFormat != null) - { - format = myFormat; - } - if (myInput != null) { input = myInput; } - if (myOutput != null) - { - output = myOutput; - } - - if (myMap != null) - { - map = myMap; - } - return; } @@ -650,9 +634,13 @@ public class ReportGenerator catch (IOException e) { System.out.println("Failed to read input file: " + input); - System.exit(0); + return; } + // first initialise a date format object to do our date processing + // if necessary + SimpleDateFormat sdf = new SimpleDateFormat("dd'/'MM'/'yyyy"); + // FIXME: although this works, it is not very elegant // loop through the aggregator file and read in the values while ((record = br.readLine()) != null) @@ -699,64 +687,48 @@ public class ReportGenerator } // if the line is real, then we carry on - - // first initialise a date format object to do our date processing - // if necessary - SimpleDateFormat sdf = new SimpleDateFormat("dd'/'MM'/'yyyy"); - // read the analysis contents in if (section.equals("archive")) { archiveStats.put(key, value); } - - if (section.equals("action")) + else if (section.equals("action")) { actionAggregator.put(key, value); } - - if (section.equals("user")) + else if (section.equals("user")) { userAggregator.put(key, value); } - - if (section.equals("search")) + else if (section.equals("search")) { searchAggregator.put(key, value); } - - if (section.equals("item")) + else if (section.equals("item")) { itemAggregator.put(key, value); } - - // read the config details used to make this report in - if (section.equals("user_email")) + else if (section.equals("user_email")) { userEmail = value; } - - if (section.equals("item_floor")) + else if (section.equals("item_floor")) { itemFloor = Integer.parseInt(value); } - - if (section.equals("search_floor")) + else if (section.equals("search_floor")) { searchFloor = Integer.parseInt(value); } - - if (section.equals("host_url")) + else if (section.equals("host_url")) { url = value; } - - if (section.equals("item_lookup")) + else if (section.equals("item_lookup")) { itemLookup = Integer.parseInt(value); } - - if (section.equals("avg_item_views")) + else if (section.equals("avg_item_views")) { try { @@ -767,43 +739,35 @@ public class ReportGenerator avgItemViews = 0; } } - - if (section.equals("server_name")) + else if (section.equals("server_name")) { serverName = value; } - - if (section.equals("service_name")) + else if (section.equals("service_name")) { name = value; } - - if (section.equals("start_date")) + else if (section.equals("start_date")) { startDate = sdf.parse(value); } - - if (section.equals("end_date")) + else if (section.equals("end_date")) { endDate = sdf.parse(value); } - - if (section.equals("analysis_process_time")) + else if (section.equals("analysis_process_time")) { processTime = Integer.parseInt(value); } - - if (section.equals("general_summary")) + else if (section.equals("general_summary")) { generalSummary.add(value); } - - if (section.equals("log_lines")) + else if (section.equals("log_lines")) { logLines = Integer.parseInt(value); } - - if (section.equals("warnings")) + else if (section.equals("warnings")) { warnings = Integer.parseInt(value); } diff --git a/dspace-api/src/main/java/org/dspace/app/statistics/StatisticsLoader.java b/dspace-api/src/main/java/org/dspace/app/statistics/StatisticsLoader.java new file mode 100644 index 0000000000..0c39c8939d --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/statistics/StatisticsLoader.java @@ -0,0 +1,397 @@ +/* + * StatisticsLoader.java + * + * Version: $Revision: 1.0 $ + * + * Date: $Date: 2007/08/28 10:00:00 $ + * + * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +package org.dspace.app.statistics; + +import org.apache.commons.lang.time.DateUtils; +import org.dspace.core.ConfigurationManager; + +import java.util.*; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.io.File; +import java.io.FilenameFilter; +import java.text.SimpleDateFormat; +import java.text.ParseException; + +/** + * Helper class for loading the analysis / report files from the reports directory + */ +public class StatisticsLoader +{ + private static Map monthlyAnalysis = null; + private static Map monthlyReports = null; + + private static StatsFile generalAnalysis = null; + private static StatsFile generalReport = null; + + private static Date lastLoaded = null; + private static int fileCount = 0; + + private static Pattern analysisMonthlyPattern; + private static Pattern analysisGeneralPattern; + private static Pattern reportMonthlyPattern; + private static Pattern reportGeneralPattern; + + private static SimpleDateFormat monthlySDF; + private static SimpleDateFormat generalSDF; + + // one time initialisation of the regex patterns and formatters we will use + static + { + analysisMonthlyPattern = Pattern.compile("dspace-log-monthly-([0-9][0-9][0-9][0-9]-[0-9]+)\\.dat"); + analysisGeneralPattern = Pattern.compile("dspace-log-general-([0-9]+-[0-9]+-[0-9]+)\\.dat"); + reportMonthlyPattern = Pattern.compile("report-([0-9][0-9][0-9][0-9]-[0-9]+)\\.html"); + reportGeneralPattern = Pattern.compile("report-general-([0-9]+-[0-9]+-[0-9]+)\\.html"); + + monthlySDF = new SimpleDateFormat("yyyy'-'M"); + generalSDF = new SimpleDateFormat("yyyy'-'M'-'dd"); + } + + /** + * Get an array of the dates of the report files + * @return + */ + public static Date[] getMonthlyReportDates() + { + return sortDatesDescending(getDatesFromMap(monthlyReports)); + } + + /** + * Get an array of the dates of the analysis files + * @return + */ + public static Date[] getMonthlyAnalysisDates() + { + return sortDatesDescending(getDatesFromMap(monthlyAnalysis)); + } + + /** + * Convert the formatted dates that are the keys of the map into a date array + * @param monthlyMap + * @return + */ + protected static Date[] getDatesFromMap(Map monthlyMap) + { + Set keys = monthlyMap.keySet(); + Date[] dates = new Date[keys.size()]; + int i = 0; + for (String date : keys) + { + try + { + dates[i] = monthlySDF.parse(date); + } + catch (ParseException pe) + { + } + + i++; + } + + return dates; + } + + /** + * Sort the date array in descending (reverse chronological) order + * @param dates + * @return + */ + protected static Date[] sortDatesDescending(Date[] dates) + { + Arrays.sort(dates, new Comparator() { + SimpleDateFormat sdf = monthlySDF; + public int compare(Date d1, Date d2) + { + if (d1 == null && d2 == null) + return 0; + else if (d1 == null) + return -1; + else if (d2 == null) + return 1; + else if (d1.before(d2)) + return 1; + else if (d2.before(d1)) + return -1; + + return 0; + } + }); + return dates; + } + + /** + * Get the analysis file for a given date + * @param date + * @return + */ + public static File getAnalysisFor(String date) + { + StatisticsLoader.syncFileList(); + StatsFile sf = (monthlyAnalysis == null ? null : monthlyAnalysis.get(date)); + return sf == null ? null : sf.file; + } + + /** + * Get the report file for a given date + * @param date + * @return + */ + public static File getReportFor(String date) + { + StatisticsLoader.syncFileList(); + StatsFile sf = (monthlyReports == null ? null : monthlyReports.get(date)); + return sf == null ? null : sf.file; + } + + /** + * Get the current general analysis file + * @return + */ + public static File getGeneralAnalysis() + { + StatisticsLoader.syncFileList(); + return generalAnalysis == null ? null : generalAnalysis.file; + } + + /** + * Get the current general report file + * @return + */ + public static File getGeneralReport() + { + StatisticsLoader.syncFileList(); + return generalReport == null ? null : generalReport.file; + } + + /** + * Syncronize the cached list of analysis / report files with the reports directory + * + * We synchronize if: + * + * 1) The number of files is different (ie. files have been added or removed) + * 2) We haven't cached anything yet + * 3) The cache was last generate over an hour ago + */ + private static void syncFileList() + { + // Get an array of all the analysis and report files present + File[] fileList = StatisticsLoader.getAnalysisAndReportFileList(); + + if (fileList != null && fileList.length != fileCount) + StatisticsLoader.loadFileList(fileList); + else if (lastLoaded == null) + StatisticsLoader.loadFileList(fileList); + else if (DateUtils.addHours(lastLoaded, 1).before(new Date())) + StatisticsLoader.loadFileList(fileList); + } + + /** + * Generate the cached file list from the array of files + * @param fileList + */ + private static synchronized void loadFileList(File[] fileList) + { + // If we haven't been passed an array of files, get one now + if (fileList == null || fileList.length == 0) + { + fileList = StatisticsLoader.getAnalysisAndReportFileList(); + } + + // Create new maps for the monthly analyis / reports + Map newMonthlyAnalysis = new HashMap(); + Map newMonthlyReports = new HashMap(); + + StatsFile newGeneralAnalysis = null; + StatsFile newGeneralReport = null; + + if (fileList != null) + { + for (File thisFile : fileList) + { + StatsFile statsFile = null; + + // If we haven't identified this file yet + if (statsFile == null) + { + // See if it is a monthly analysis file + statsFile = makeStatsFile(thisFile, analysisMonthlyPattern, monthlySDF); + if (statsFile != null) + { + // If it is, add it to the map + newMonthlyAnalysis.put(statsFile.dateStr, statsFile); + } + } + + // If we haven't identified this file yet + if (statsFile == null) + { + // See if it is a monthly report file + statsFile = makeStatsFile(thisFile, reportMonthlyPattern, monthlySDF); + if (statsFile != null) + { + // If it is, add it to the map + newMonthlyReports.put(statsFile.dateStr, statsFile); + } + } + + // If we haven't identified this file yet + if (statsFile == null) + { + // See if it is a general analysis file + statsFile = makeStatsFile(thisFile, analysisGeneralPattern, generalSDF); + if (statsFile != null) + { + // If it is, ensure that we are pointing to the most recent file + if (newGeneralAnalysis == null || statsFile.date.after(newGeneralAnalysis.date)) + { + newGeneralAnalysis = statsFile; + } + } + } + + // If we haven't identified this file yet + if (statsFile == null) + { + // See if it is a general report file + statsFile = makeStatsFile(thisFile, reportGeneralPattern, generalSDF); + if (statsFile != null) + { + // If it is, ensure that we are pointing to the most recent file + if (newGeneralReport == null || statsFile.date.after(newGeneralReport.date)) + { + newGeneralReport = statsFile; + } + } + } + } + } + + // Store the newly discovered values in the member cache + monthlyAnalysis = newMonthlyAnalysis; + monthlyReports = newMonthlyReports; + generalAnalysis = newGeneralAnalysis; + generalReport = newGeneralReport; + lastLoaded = new Date(); + } + + /** + * Generate a StatsFile entry for this file. The pattern and date formatters are used to + * identify the file as a particular type, and extract the relevant information. + * If the file is not identified by the formatter provided, then we return null + * @param thisFile + * @param thisPattern + * @param sdf + * @return + */ + private static StatsFile makeStatsFile(File thisFile, Pattern thisPattern, SimpleDateFormat sdf) + { + Matcher matcher = thisPattern.matcher(thisFile.getName()); + if (matcher.matches()) + { + StatsFile sf = new StatsFile(); + sf.file = thisFile; + sf.path = thisFile.getPath(); + sf.dateStr = matcher.group(1).trim(); + + try + { + sf.date = sdf.parse(sf.dateStr); + } + catch (ParseException e) + { + + } + + return sf; + } + + return null; + } + + /** + * Get an array of all the analysis and report files + * @return + */ + private static File[] getAnalysisAndReportFileList() + { + File reportDir = new File(ConfigurationManager.getProperty("report.dir")); + if (reportDir != null) + { + return reportDir.listFiles(new AnalysisAndReportFilter()); + } + + return null; + } + + /** + * Simple class for holding information about an analysis/report file + */ + private static class StatsFile + { + File file; + String path; + Date date; + String dateStr; + } + + /** + * Filter used to restrict files in the reports directory to just analysis or report types + */ + private static class AnalysisAndReportFilter implements FilenameFilter + { + public boolean accept(File dir, String name) + { + if (analysisMonthlyPattern.matcher(name).matches()) + return true; + + if (analysisGeneralPattern.matcher(name).matches()) + return true; + + if (reportMonthlyPattern.matcher(name).matches()) + return true; + + if (reportGeneralPattern.matcher(name).matches()) + return true; + + return false; + } + } +} diff --git a/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/administrative/Navigation.java b/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/administrative/Navigation.java index 2ea3ea1c72..79e222e1c7 100644 --- a/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/administrative/Navigation.java +++ b/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/administrative/Navigation.java @@ -73,10 +73,10 @@ import java.sql.SQLException; import java.util.Map; /** - * - * Create the navigation options for everything in the administrative aspects. This includes + * + * Create the navigation options for everything in the administrative aspects. This includes * Epeople, group, item, access control, and registry management. - * + * * @author Scott Phillips * @author Afonso Araujo Neto (internationalization) * @author Alexey Maslov @@ -108,27 +108,29 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr private static final Message T_context_export_collection = message("xmlui.administrative.Navigation.context_export_collection"); private static final Message T_context_export_community = message("xmlui.administrative.Navigation.context_export_community"); private static final Message T_account_export = message("xmlui.administrative.Navigation.account_export"); - - + + private static final Message T_statistics = message("xmlui.administrative.Navigation.statistics"); + + /** exports available for download */ java.util.List availableExports = null; /** Cached validity object */ private SourceValidity validity; - + /** * Generate the unique cache key. * * @return The generated key hashes the src */ - public Serializable getKey() + public Serializable getKey() { Request request = ObjectModelHelper.getRequest(objectModel); - - // Special case, don't cache anything if the user is logging + + // Special case, don't cache anything if the user is logging // in. The problem occures because of timming, this cache key - // is generated before we know whether the operation has - // succeded or failed. So we don't know whether to cache this + // is generated before we know whether the operation has + // succeded or failed. So we don't know whether to cache this // under the user's specific cache or under the anonymous user. if (request.getParameter("login_email") != null || request.getParameter("login_password") != null || @@ -136,7 +138,7 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr { return "0"; } - + String key; if (context.getCurrentUser() != null) { @@ -149,17 +151,17 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr } else key = "anonymous"; - + return HashUtil.hash(key); } - + /** * Generate the validity object. * * @return The generated validity object or null if the * component is currently not cacheable. */ - public SourceValidity getValidity() + public SourceValidity getValidity() { if (this.validity == null) { @@ -168,17 +170,17 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr { try { DSpaceValidity validity = new DSpaceValidity(); - + validity.add(eperson); - + Group[] groups = Group.allMemberGroups(context, eperson); for (Group group : groups) { validity.add(group); } - + this.validity = validity.complete(); - } + } catch (SQLException sqle) { // Just ignore it and return invalid. @@ -191,8 +193,8 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr } return this.validity; } - - + + public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectModel, src, parameters); try{ @@ -202,12 +204,12 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr // nothing to do } } - + public void addOptions(Options options) throws SAXException, WingException, UIException, SQLException, IOException, AuthorizeException { /* Create skeleton menu structure to ensure consistent order between aspects, - * even if they are never used + * even if they are never used */ options.addList("browse"); List account = options.addList("account"); @@ -223,7 +225,7 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr DSpaceObject dso = URIUtil.resolve(objectModel); if (dso instanceof Item) { - + Item item = (Item) dso; if (item.canEdit()) { @@ -235,83 +237,84 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr else if (dso instanceof Collection) { Collection collection = (Collection) dso; - - + + // can they admin this collection? if (AuthorizeManager.authorizeActionBoolean(this.context, collection, Constants.COLLECTION_ADMIN)) { context.setHead(T_context_head); - context.addItemXref(contextPath+"/admin/collection?collectionID=" + collection.getID(), T_context_edit_collection); - context.addItemXref(contextPath+"/admin/mapper?collectionID="+collection.getID(), T_context_item_mapper); + context.addItemXref(contextPath+"/admin/collection?collectionID=" + collection.getID(), T_context_edit_collection); + context.addItemXref(contextPath+"/admin/mapper?collectionID="+collection.getID(), T_context_item_mapper); context.addItem().addXref(contextPath+"/admin/export?collectionID="+collection.getID(), T_context_export_collection ); } } else if (dso instanceof Community) { Community community = (Community) dso; - + // can they admin this collection? if (community.canEditBoolean()) { context.setHead(T_context_head); - context.addItemXref(contextPath+"/admin/community?communityID=" + community.getID(), T_context_edit_community); + context.addItemXref(contextPath+"/admin/community?communityID=" + community.getID(), T_context_edit_community); context.addItem().addXref(contextPath+"/admin/export?communityID="+community.getID(), T_context_export_community ); } - + // can they add to this community? if (AuthorizeManager.authorizeActionBoolean(this.context, community,Constants.ADD)) { context.setHead(T_context_head); - context.addItemXref(contextPath+"/admin/collection?createNew&communityID=" + community.getID(), T_context_create_collection); + context.addItemXref(contextPath+"/admin/collection?createNew&communityID=" + community.getID(), T_context_create_collection); } - + // Only administrators can create communities if (AuthorizeManager.isAdmin(this.context)) { context.setHead(T_context_head); - context.addItemXref(contextPath+"/admin/community?createNew&communityID=" + community.getID(), T_context_create_subcommunity); + context.addItemXref(contextPath+"/admin/community?createNew&communityID=" + community.getID(), T_context_create_subcommunity); } } - + if ("community-list".equals(this.sitemapURI)) { if (AuthorizeManager.isAdmin(this.context)) { context.setHead(T_context_head); - context.addItemXref(contextPath+"/admin/community?createNew", T_context_create_community); + context.addItemXref(contextPath+"/admin/community?createNew", T_context_create_community); } } - - + + // System Administrator options! if (AuthorizeManager.isAdmin(this.context)) { admin.setHead(T_administrative_head); - + List epeople = admin.addList("epeople"); List registries = admin.addList("registries"); - - epeople.setHead(T_administrative_access_control); - epeople.addItemXref(contextPath+"/admin/epeople", T_administrative_people); - epeople.addItemXref(contextPath+"/admin/groups", T_administrative_groups); - epeople.addItemXref(contextPath+"/admin/authorize", T_administrative_authorizations); - - registries.setHead(T_administrative_registries); - registries.addItemXref(contextPath+"/admin/metadata-registry",T_administrative_metadata); - registries.addItemXref(contextPath+"/admin/format-registry",T_administrative_format); - - admin.addItemXref(contextPath+"/admin/item", T_administrative_items); - admin.addItemXref(contextPath+"/admin/withdrawn", T_administrative_withdrawn); + + epeople.setHead(T_administrative_access_control); + epeople.addItemXref(contextPath+"/admin/epeople", T_administrative_people); + epeople.addItemXref(contextPath+"/admin/groups", T_administrative_groups); + epeople.addItemXref(contextPath+"/admin/authorize", T_administrative_authorizations); + + registries.setHead(T_administrative_registries); + registries.addItemXref(contextPath+"/admin/metadata-registry",T_administrative_metadata); + registries.addItemXref(contextPath+"/admin/format-registry",T_administrative_format); + + admin.addItemXref(contextPath+"/admin/item", T_administrative_items); + admin.addItemXref(contextPath+"/admin/withdrawn", T_administrative_withdrawn); admin.addItemXref(contextPath+"/admin/panel", T_administrative_control_panel); + admin.addItemXref(contextPath+"/statistics", T_statistics); } } - - + + public int addContextualOptions(List context) throws SQLException, WingException { // How many options were added. int options = 0; - + DSpaceObject dso = URIUtil.resolve(objectModel); if (dso instanceof Item) diff --git a/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/Navigation.java b/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/Navigation.java index 2b5c7fa98f..9d4cc06059 100644 --- a/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/Navigation.java +++ b/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/Navigation.java @@ -88,18 +88,6 @@ public class Navigation extends AbstractDSpaceTransformer implements CacheablePr private static final Message T_communities_and_collections = message("xmlui.ArtifactBrowser.Navigation.communities_and_collections"); - private static final Message T_browse_titles = - message("xmlui.ArtifactBrowser.Navigation.browse_titles"); - - private static final Message T_browse_authors = - message("xmlui.ArtifactBrowser.Navigation.browse_authors"); - - private static final Message T_browse_subjects = - message("xmlui.ArtifactBrowser.Navigation.browse_subjects"); - - private static final Message T_browse_dates = - message("xmlui.ArtifactBrowser.Navigation.browse_dates"); - private static final Message T_head_this_collection = message("xmlui.ArtifactBrowser.Navigation.head_this_collection"); diff --git a/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/StatisticsViewer.java b/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/StatisticsViewer.java new file mode 100644 index 0000000000..f16c07451c --- /dev/null +++ b/dspace-xmlui/dspace-xmlui-api/src/main/java/org/dspace/app/xmlui/aspect/artifactbrowser/StatisticsViewer.java @@ -0,0 +1,640 @@ +/* + * Statistics.java + * + * Version: $Revision: 1.0 $ + * + * Date: $Date: 2007/08/28 10:00:00 $ + * + * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts + * Institute of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Hewlett-Packard Company nor the name of the + * Massachusetts Institute of Technology nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +package org.dspace.app.xmlui.aspect.artifactbrowser; + +import org.dspace.app.xmlui.cocoon.AbstractDSpaceTransformer; +import org.dspace.app.xmlui.wing.element.*; +import org.dspace.app.xmlui.wing.element.List; +import org.dspace.app.xmlui.wing.WingException; +import org.dspace.app.xmlui.wing.Message; +import org.dspace.app.xmlui.utils.UIException; +import org.dspace.app.xmlui.utils.DSpaceValidity; +import org.dspace.app.statistics.*; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.AuthorizeManager; +import org.dspace.core.ConfigurationManager; +import org.apache.cocoon.caching.CacheableProcessingComponent; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.excalibur.source.SourceValidity; +import org.apache.log4j.Logger; +import org.xml.sax.SAXException; + +import java.io.*; +import java.io.File; +import java.sql.SQLException; +import java.util.*; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.text.ParseException; + +/** + * Transformer to display statistics data in XML UI. + * + * Unlike the JSP interface that pre-generates HTML and stores in the reports folder, + * this class transforms the raw analysis data into a Wing representation + */ +public class StatisticsViewer extends AbstractDSpaceTransformer implements CacheableProcessingComponent +{ + private final static Logger log = Logger.getLogger(StatisticsViewer.class); + + private final static Message T_dspace_home = message("xmlui.general.dspace_home"); + + private final static Message T_choose_report = message("xmlui.ArtifactBrowser.StatisticsViewer.choose_month"); + private final static Message T_page_title = message("xmlui.ArtifactBrowser.StatisticsViewer.report.title"); + + private final static Message T_empty_title = message("xmlui.ArtifactBrowser.StatisticsViewer.no_report.title"); + private final static Message T_empty_text = message("xmlui.ArtifactBrowser.StatisticsViewer.no_report.text"); + + private final static SimpleDateFormat sdfDisplay = new SimpleDateFormat("MM'/'yyyy"); + private final static SimpleDateFormat sdfLink = new SimpleDateFormat("yyyy'-'M"); + + private boolean initialised = false; + private String reportDate = null; + private SourceValidity validity; + + /** + * Get the caching key for this report + * @return + */ + public Serializable getKey() + { + initialise(); + + if (reportDate != null) + return reportDate; + + return "general"; + } + + /** + * Generate the validity for this cached entry + * @return + */ + public SourceValidity getValidity() + { + if (validity == null) + { + try + { + initialise(); + boolean showReport = ConfigurationManager.getBooleanProperty("report.public"); + + // If the report isn't public + if (!showReport) + { + try + { + // Administrators can always view reports + showReport = AuthorizeManager.isAdmin(context); + } + catch (SQLException sqle) + { + log.error("Unable to check for administrator", sqle); + } + } + + // Only generate a validity if the report is visible + if (showReport) + { + File analysisFile = null; + + // Get a file for the report data + if (reportDate != null) + analysisFile = StatisticsLoader.getAnalysisFor(reportDate); + else + analysisFile = StatisticsLoader.getGeneralAnalysis(); + + if (analysisFile != null) + { + // Generate the validity based on the properties of the report data file + DSpaceValidity newValidity = new DSpaceValidity(); + newValidity.add(Long.toString(analysisFile.lastModified())); + newValidity.add("-"); + newValidity.add(Long.toString(analysisFile.length())); + validity = newValidity.complete(); + } + } + } + catch (Exception e) + { + + } + } + + return validity; + } + + /** + * Add additional navigation options. This is to allow selection of a monthly report + * + * @param options + * @throws SAXException + * @throws WingException + * @throws UIException + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ + public void addOptions(Options options) throws SAXException, WingException, + UIException, SQLException, IOException, AuthorizeException + { + Date[] monthlyDates = StatisticsLoader.getMonthlyAnalysisDates(); + + if (monthlyDates != null && monthlyDates.length > 0) + { + List statList = options.addList("statsreports"); + statList.setHead(T_choose_report); + + HashMap params = new HashMap(); + for (Date date : monthlyDates) + { + params.put("date", sdfLink.format(date)); + statList.addItemXref(super.generateURL("statistics", params), sdfDisplay.format(date)); + } + } + } + + /** + * Add title, etc. metadata + * + * @param pageMeta + * @throws SAXException + * @throws WingException + * @throws UIException + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ + public void addPageMeta(PageMeta pageMeta) throws SAXException, WingException, UIException, + SQLException, IOException, AuthorizeException + { + initialise(); + + pageMeta.addMetadata("title").addContent(T_page_title); + pageMeta.addTrailLink(contextPath + "/", T_dspace_home); + pageMeta.addTrail().addContent(T_page_title); + } + + /** + * Output the body of the report + * + * @param body + * @throws SAXException + * @throws WingException + * @throws UIException + * @throws SQLException + * @throws IOException + * @throws AuthorizeException + */ + public void addBody(Body body) throws SAXException, WingException, UIException, SQLException, + IOException, AuthorizeException + { + initialise(); + boolean publicise = ConfigurationManager.getBooleanProperty("report.public"); + + // Check that the reports are either public, or user is an administrator + if (!publicise && !AuthorizeManager.isAdmin(context)) + throw new AuthorizeException(); + + // Retrieve the report data to display + File analysisFile; + if (reportDate != null) + analysisFile = StatisticsLoader.getAnalysisFor(reportDate); + else + analysisFile = StatisticsLoader.getGeneralAnalysis(); + + // Create the renderer for the results + Division div = body.addDivision("statistics", "primary"); + + if (analysisFile != null) + { + try + { + // Generate the XML stream + Report myRep = new XMLUIReport(div); + ReportGenerator.processReport(context, myRep, analysisFile.getCanonicalPath()); + } + catch (Exception e) + { + throw new UIException(e); + } + } + else + { + div.setHead(T_empty_title); + div.addPara(T_empty_text); + } + } + + /** + * Initialise the member variables from the request + */ + private void initialise() + { + if (!initialised) + { + Request request = ObjectModelHelper.getRequest(objectModel); + reportDate = (String) request.getParameter("date"); + + initialised = true; + } + } + + /** + * Clear the member variables so that the instance can be reused + */ + public void recycle() + { + initialised = false; + reportDate = null; + validity = null; + super.recycle(); + } + + /** + * Implementation of the Report interface, to output the statistics data for xmlui + * Note that all methods that return Strings return 'null' in this implementation, as + * all the outputting is done directly using the Wing framework. + */ + class XMLUIReport implements Report + { + private ArrayList blocks = new ArrayList(); + + private String mainTitle = null; + private String pageTitle = null; + + /** start date for report */ + private Date start = null; + + /** end date for report */ + private Date end = null; + + private Division rootDiv; + private Division currDiv; + + /** + * Hide the default constructor, so that you have to pass in a Division + */ + private XMLUIReport() {} + + /** + * Create instance, providing the Wing element that we will be adding to + * + * @param myDiv + */ + public XMLUIReport(Division myDiv) + { + rootDiv = myDiv; + currDiv = myDiv; + } + + /** + * Get the header for the report - currently not supported + * + * @return + */ + public String header() + { + return header(""); + } + + // Currently not supported + public String header(String title) + { + return ""; + } + + /** + * Add the main title to the report + * @return + */ + public String mainTitle() + { + try + { + rootDiv.setHead(mainTitle); + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + return null; + } + + /** + * Output the date range for this report + * @return + */ + public String dateRange() + { + StringBuilder content = new StringBuilder(); + DateFormat df = DateFormat.getDateInstance(); + if (start != null) + { + content.append(df.format(start)); + } + else + { + content.append("from start of records "); + } + + content.append(" to "); + + if (end != null) + { + content.append(df.format(end)); + } + else + { + content.append(" end of records"); + } + + try + { + rootDiv.addDivision("reportDate").addPara(content.toString()); + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + + return null; + } + + /** + * Output the section header + * @param title + * @return + */ + public String sectionHeader(String title) + { + try + { + currDiv.setHead(title); + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + + return null; + } + + /** + * Output the current statistics block + * @param content + * @return + */ + public String statBlock(Statistics content) + { + Stat[] stats = content.getStats(); + try + { + int rows = stats.length; + if (content.getStatName() != null || content.getResultName() != null) + rows++; + + Table block = currDiv.addTable("reportBlock", rows, 2); + + // prepare the table headers + if (content.getStatName() != null || content.getResultName() != null) + { + Row row = block.addRow(); + if (content.getStatName() != null) + { + row.addCellContent(content.getStatName()); + } + else + { + row.addCellContent(" "); + } + + if (content.getResultName() != null) + { + row.addCellContent(content.getResultName()); + } + else + { + row.addCellContent(" "); + } + } + + // output the statistics in the table + for (int i = 0; i < stats.length; i++) + { + Row row = block.addRow(); + if (stats[i].getReference() != null) + { + row.addCell().addXref(stats[i].getReference()).addContent(stats[i].getKey()); + } + else + { + row.addCell().addContent(stats[i].getKey()); + } + + if (stats[i].getUnits() != null) + { + row.addCell(null, null, "right").addContent(stats[i].getValue() + " " + stats[i].getUnits()); + } + else + { + row.addCell(null, null, "right").addContent(ReportTools.numberFormat(stats[i].getValue())); + } + } + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + + return null; + } + + /** + * Output any information about the lower boundary restriction for this section + * @param floor + * @return + */ + public String floorInfo(int floor) + { + try + { + if (floor > 0) + { + currDiv.addDivision("reportFloor").addPara("(more than " + ReportTools.numberFormat(floor) + " times)"); + } + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + + return null; + } + + /** + * Output an explanation for this section + * + * @param explanation + * @return + */ + public String blockExplanation(String explanation) + { + try + { + if (explanation != null) + { + currDiv.addDivision("reportExplanation").addPara(explanation); + } + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + + return null; + } + + /** + * Output the footer + * + * @return + */ + public String footer() + { + return ""; + } + + /** + * Set the main title for this report + * + * @param name + * @param serverName + */ + public void setMainTitle(String name, String serverName) + { + mainTitle = "Statistics for " + name; + + if (ConfigurationManager.getBooleanProperty("report.show.server", true)) + mainTitle += " on " + serverName; + + if (pageTitle == null) + { + pageTitle = mainTitle; + } + return; + } + + /** + * Add a block to report on + * + * @param stat + */ + public void addBlock(Statistics stat) + { + blocks.add(stat); + return; + } + + /** + * Render the statistics into an XML stream + * @return + */ + public String render() + { + Pattern space = Pattern.compile(" "); + + // Output the heading information + header(pageTitle); + mainTitle(); + dateRange(); + + // Loop through all the sections + for (Statistics stats : blocks) + { + // navigation(); + try + { + String title = stats.getSectionHeader(); + String aName = title.toLowerCase(); + Matcher matchSpace = space.matcher(aName); + aName = matchSpace.replaceAll("_"); + + // Create a new division for each section + currDiv = rootDiv.addDivision(aName); + sectionHeader(title); + // topLink(); + blockExplanation(stats.getExplanation()); + floorInfo(stats.getFloor()); + statBlock(stats); + currDiv = rootDiv; + } + catch (WingException we) + { + log.error("Error creating XML for report", we); + } + } + + return null; + } + + /** + * Set the start date for this report + * @param start + */ + public void setStartDate(Date start) + { + this.start = start; + } + + /** + * Set the end date for this report + * @param end + */ + public void setEndDate(Date end) + { + this.end = end; + } + } +} diff --git a/dspace-xmlui/dspace-xmlui-api/src/main/resources/aspects/ArtifactBrowser/sitemap.xmap b/dspace-xmlui/dspace-xmlui-api/src/main/resources/aspects/ArtifactBrowser/sitemap.xmap index b0ad556715..50c708109c 100644 --- a/dspace-xmlui/dspace-xmlui-api/src/main/resources/aspects/ArtifactBrowser/sitemap.xmap +++ b/dspace-xmlui/dspace-xmlui-api/src/main/resources/aspects/ArtifactBrowser/sitemap.xmap @@ -64,15 +64,15 @@ and searching the repository. - - + + - - + + @@ -81,49 +81,49 @@ and searching the repository. - + - - + + - - - - + + - - - - + + + - - + + @@ -131,33 +131,33 @@ and searching the repository. - - + + - + - + - - - + + + - + - + @@ -244,6 +244,12 @@ and searching the repository. + + + + + + diff --git a/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/i18n/messages.xml b/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/i18n/messages.xml index ed7279d99a..654d0d8746 100644 --- a/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/i18n/messages.xml +++ b/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/i18n/messages.xml @@ -158,14 +158,14 @@ AND OR NOT - - + + Or enter first few letters: Browse for items that begin with these letters - Jump to a point in the index: - (Choose month) - (Choose year) + Jump to a point in the index: + (Choose month) + (Choose year) Or type in a year: Browse for items that are from the given year. Sort by: @@ -190,17 +190,17 @@ Browsing {0} by Subject {1} Browsing {0} by Subject - + Browsing {0} by Title {1} Browsing {0} by Title - + Browsing {0} by Issue Date {1} Browsing {0} by Issue Date Browsing {0} by Submit Date {1} Browsing {0} by Submit Date - + Search Scope All of DSpace @@ -211,7 +211,7 @@ Dates Advanced Search Recent Submissions - + Community List Community List @@ -230,8 +230,8 @@ Sub-communities within this community Collections in this community Recent Submissions - - + + Contact us Contact us @@ -240,7 +240,7 @@ On-line form Feedback Email - + Give feedback Give feedback @@ -250,23 +250,23 @@ This address will be used to follow up on your feedback. Comments Send Feedback - + Give feedback Give feedback Feedback sent Your comments have been received. - + Search DSpace Enter some text in the box below to search DSpace. - + View Item This item appears in the following Collection(s) Show simple item record Show full item record - + Browse All of DSpace @@ -278,14 +278,14 @@ By Submit Date This Collection This Community - + Search Search Search Search Scope Full Text Search - + This resource is restricted Restricted @@ -305,9 +305,9 @@ item resource unknown - + - The item is restricted + The item is restricted The item you are attempting to access is a restricted item and requires credentials to view. Please login below to access the item. Monthly Reports @@ -317,34 +317,34 @@ There are currently no reports available for this service. Please check back later. - The file is restricted + The file is restricted The file you are attempting to access is a restricted file and requires credentials to view. Please login below to access the file. - + This export archive is restricted. The export archive you are attempting to access is a restricted resource and requires credentials to view. Please login below to access the export archive. - - - - - - - + + + + + + + New user registration Forgot Password - + Registration unavailable Registration unavailable The configuration of this DSpace repository does not allow registration in this context. Please contact the site administrator with questions or comments. - + Update Profile Create Profile @@ -369,7 +369,7 @@ Choose a password at least 6 characters long Please retype your password to confirm Authorization groups you belong to - + Verify Email Create Profile @@ -382,14 +382,14 @@ Password reset Password reset Your password has been reset. - + Invalid token Invalid token Invalid token The URL you provided is invalid. The URL may have been broken across multiple lines in your email client, like this: If so, copy and paste the entire URL directly into your browser's address bar, rather than simply clicking on the link. The address bar should then contain something like: - + My Account Profile @@ -410,7 +410,7 @@ Register new user Register an account to subscribe to collections for email updates, and submit new items to DSpace. Click here to register. - + Sign in Sign in @@ -420,7 +420,7 @@ The user name and/or password supplied were not valid. Password Sign in - + Choose a Login Method Choose Login @@ -438,24 +438,24 @@ Authentication Failed Failed Authentication Authentication Failed - + No Authentication Method Found No authentication method was found. Please contact the site administrator. Shibboleth Login Failed Shibboleth authentication not available at this time. Please contact the site administrator. - + Profile updated Update profile Profile updated Your profile information has been updated. - + Registration finished Registration Finished You're now registered to use the DSpace system. You can subscribe to collections to receive email updates about new items. - + Reset password Reset password @@ -466,7 +466,7 @@ Reset password Choose a password at least 6 characters long Please retype your password to confirm - + Forgot Password Forgot Password @@ -475,7 +475,7 @@ Enter the same address used when registering. Email address not found. Send info - + New user registration Email already in use @@ -488,7 +488,7 @@ This address will be verified and used as your login name. Unable to send email to this address. Register - + Verification email sent Verification email sent @@ -500,12 +500,12 @@ - - + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!--> + + @@ -516,7 +516,7 @@ Go to MyDSpace Home Item submission Item submission - Item submission + Item submission < Previous Save & Exit Next > @@ -528,14 +528,14 @@ Show simple item record Submission Submission - + Submit a new item to this collection - + Submissions - + Submissions & Workflow Submissions @@ -598,7 +598,7 @@ Creative Commons License Complete - + Resume @@ -615,7 +615,7 @@ Save it, I'll work on it later Remove the submission - + Initial Questions Important Note: @@ -632,7 +632,7 @@ Date Issued Citation Publisher - + Describe Item Error: Unknown field type @@ -669,7 +669,7 @@ Edit File checksum: Remove selected files - + Edit File File @@ -698,7 +698,7 @@ unknown known supported - + Creative Commons License If you wish, you may add a Creative Commons License to your item. Creative Commons licenses govern what people who read your work may then do with it. If you wish to select a Creative Commons license click the button below. You will be taken to the Creative Commons website, where you will be presented with several licensing options. After selecting a license, you will be returned to this page. @@ -719,13 +719,13 @@ Submission removed Your submission has been cancelled, and the incomplete item removed from the system. - Go to the Submissions page + Go to the Submissions page - Submission complete - Your submission will now go through the review process for this collection. You will receive e-mail notification as soon as your submission has joined the collection, or if there is a problem with your submission. You may also check on the status of your submission by visiting your submissions page. - Go to the Submissions page - Submit another item + Submission complete + Your submission will now go through the review process for this collection. You will receive e-mail notification as soon as your submission has joined the collection, or if there is a problem with your submission. You may also check on the status of your submission by visiting your submissions page. + Go to the Submissions page + Submit another item Actions you may perform on this task: @@ -750,17 +750,17 @@ The reason field is required Reject item - - - + + + - - - + + + The user was added successfully. The user was edited successfully. An email message has been sent to the user containing a token that may be used to choose a new password. @@ -786,10 +786,10 @@ The selected bitstreams have been deleted. The items were successfully mapped. The items have been unmapped. - + Manage E-people - + Context @@ -803,7 +803,7 @@ Export Item Export Collection Export Community - + Administrative Access Control @@ -820,8 +820,8 @@ My Exports - - + + Export Archive The item id provided is invalid. @@ -834,7 +834,7 @@ The collection was exported successfully. You should receive an e-mail when the archive is ready for download. You can also use the 'My Exports' link to view a list of your available archives. The community was exported successfully. You should receive an e-mail when the archive is ready for download. You can also use the 'My Exports' link to view a list of your available archives. Available export archives for download: - + @@ -855,8 +855,8 @@ Email Delete E-People Your search found no results... - - + + Add E-Person Add E-Person @@ -870,7 +870,7 @@ Require Certificate Can Log In Create E-Person - + Edit E-Person Edit E-Person @@ -887,19 +887,19 @@ You may also permanently remove this E-Person from the system or reset his/her password. When resetting a password the user will be sent an email containing a special link they can follow to choose a new password. Delete E-Person Login as E-Person - + This eperson is unable to be deleted because of the following constraint(s): and eperson has submitted one or more items eperson has an active submission workflow eperson has a workflow task awaiting their attention eperson has an unidentified constraint within the system - + Groups this E-Person is a member of: (via group: {0}) none - - + + Confirm E-Person Deletion Confirm Delete @@ -966,12 +966,12 @@ Name Email - group: {0} + group: {0} This group has no members. [pending] Remove members Remove members - + Confirm Group Deletion Confirm Delete @@ -981,11 +981,11 @@ Name E-People Groups - + Format registry - Metadata registry - + Metadata registry + Delete Bitstream Formats Delete bitstream formats @@ -1112,13 +1112,13 @@ Move to schema: Move fields - - - + + + Authorization Policy List - + Administer Authorization Policies Administer Authorization Policies @@ -1131,9 +1131,9 @@ Click here to go to the item wildcard policy admin tool Community/collection authorizations Click on a community or collection to edit its policies. - - - + + + Edit Policies Policies List @@ -1146,9 +1146,9 @@ Group Delete Selected Edit - - + + Edit Item's Policies Policies List @@ -1161,10 +1161,10 @@ Add a new Item policy Add a new Bundle policy Add a new Bitstream policy - + No policies found for this object. - - + + Edit Policy Edit Policy @@ -1176,7 +1176,7 @@ ID Name Actions for this resource - + Set current Search Results @@ -1184,8 +1184,8 @@ Search for a group Search Select the action - - + + Advanced Policy Manager Advanced Authorizations @@ -1201,7 +1201,7 @@ Collection Add policies Clear policies - + Confirm Policy Deletion Confirm Delete @@ -1210,7 +1210,7 @@ ID Action Group - + Items Edit Item @@ -1218,7 +1218,7 @@ Item Bitstreams Item Metadata View Item - + Upload Bitstream Upload bitstream @@ -1235,7 +1235,7 @@ Optionally, provide a brief description of the file, for example "Main article", or "Experiment data readings". Upload You need the ADD & WRITE privilege on at least one bundle to be able to upload new bitstreams. - + Confirm Deletion Confirm deletion @@ -1244,7 +1244,7 @@ Name Description Format - + Confirm Confirm @@ -1274,7 +1274,7 @@ If the format is not in the above list, select "format not in list" above and describe it in the field below. Other Format The application you used to create the file, and the version number (for example, "ACMESoft SuperApp version 1.5"). - + Item Bitstreams Item bitstreams @@ -1291,7 +1291,7 @@ Delete bitstreams You need the ADD & WRITE privilege on the item and bundles to be able to upload new bitstreams. You need the REMOVE privilege on the item and bundles to be able to delete bitstreams. - + Item Metadata Item metadata @@ -1326,21 +1326,21 @@ Permanently delete n/a (system administrators only) - + View Item View Item - + Find Item Find Item Internal Item ID/Item Handle Unable to resolve identifier. Find - + Item mapper - + Item Mapper Item Mapper - Map Items from Other Collections @@ -1352,7 +1352,7 @@ Search Items Browse mapped items (Requires the collection ADD privilege) - + Search Items Search items @@ -1362,7 +1362,7 @@ Collection Author Title - + Browse Mapped Items Browse mapped items @@ -1373,13 +1373,13 @@ Author Title You need the REMOVE privilege on this collection to be able to unmap items from this collection. - + - Collections - Edit Metadata - Assign Roles - - + Collections + Edit Metadata + Assign Roles + + Edit Collection Roles Roles @@ -1415,14 +1415,14 @@ Any items and incomplete submissions in this collection that aren't contained in other collections The contents of those items All associated authorization policies - + Confirm role deletion Confirm Confirm deletion for role {0} Are you sure you want to delete this role? Deleting this group will give READ access to all users for all items submitted to this collection from now on. Please note that this change is not retroactive. Existing items in the system will still be restricted to the members defined by the role you are about to delete. Are you sure you want to delete this role? All changes and customizations made to the {0} group will be lost and wouuld have to be created anew. - + Edit Collection Metadata Metadata @@ -1459,7 +1459,7 @@ Any items and incomplete submissions in this community that aren't contained in other communities The contents of those items All associated authorization policies - + Edit Community Metadata Metadata @@ -1474,7 +1474,7 @@ Current logo Remove logo Delete community - + Create Community Create Community @@ -1539,11 +1539,11 @@ 30 minutes 1 hour Keep the current count down timer - Manage session - Continue to allow authenticated sessions - Restrict authentication but maintain current sessions - Restrict authentication and kill current sessions - Note: Site administrators are exempt from session management. + Manage session + Continue to allow authenticated sessions + Restrict authentication but maintain current sessions + Restrict authentication and kill current sessions + Note: Site administrators are exempt from session management. Activate Deactivate Current Activity ({0} pages maximum) diff --git a/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/themes/Reference/lib/style.css b/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/themes/Reference/lib/style.css index 745d5d7f05..1380c5be0d 100644 --- a/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/themes/Reference/lib/style.css +++ b/dspace-xmlui/dspace-xmlui-webapp/src/main/webapp/themes/Reference/lib/style.css @@ -490,6 +490,10 @@ td { color: inherit; } +td.ds-table-cell.right { + text-align: right; +} + *.first-cell { vertical-align: middle; text-align: center;