001package org.apache.archiva.webdav.util; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.repository.storage.StorageAsset; 023import org.apache.commons.lang3.StringUtils; 024import org.apache.jackrabbit.webdav.io.OutputContext; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028import java.io.IOException; 029import java.io.PrintWriter; 030import java.nio.file.Path; 031import java.nio.file.Paths; 032import java.text.DateFormat; 033import java.util.ArrayList; 034import java.util.Comparator; 035import java.util.Date; 036import java.util.List; 037import java.util.Locale; 038import java.util.Map; 039import java.util.SortedMap; 040import java.util.TreeMap; 041 042/** 043 */ 044public class IndexWriter 045{ 046 047 private static final Logger log = LoggerFactory.getLogger( IndexWriter.class ); 048 049 private final String logicalResource; 050 051 private final List<StorageAsset> repositoryAssets; 052 053 054 private final boolean isVirtual; 055 056 public IndexWriter( StorageAsset reference, String logicalResource ) 057 { 058 this.repositoryAssets = new ArrayList<>( ); 059 this.repositoryAssets.add(reference); 060 this.logicalResource = logicalResource; 061 this.isVirtual = false; 062 } 063 064 public IndexWriter( List<StorageAsset> localResources, String logicalResource ) 065 { 066 this.logicalResource = logicalResource; 067 this.repositoryAssets = localResources; 068 this.isVirtual = true; 069 } 070 071 public void write( OutputContext outputContext ) 072 { 073 outputContext.setModificationTime( new Date().getTime() ); 074 outputContext.setContentType( "text/html" ); 075 outputContext.setETag( "" ); // skygo ETag MRM-1127 seems to be fixed 076 if ( outputContext.hasStream() ) 077 { 078 PrintWriter writer = new PrintWriter( outputContext.getOutputStream() ); 079 writeDocumentStart( writer ); 080 try 081 { 082 writeHyperlinks( writer ); 083 } 084 catch ( IOException e ) 085 { 086 log.error("Could not write hyperlinks {}", e.getMessage(), e); 087 } 088 writeDocumentEnd( writer ); 089 writer.flush(); 090 writer.close(); 091 } 092 } 093 094 private void writeDocumentStart( PrintWriter writer ) 095 { 096 writer.println("<!DOCTYPE html>"); 097 writer.println( "<html>" ); 098 writer.println( "<head>" ); 099 writer.println( "<title>Collection: /" + logicalResource + "</title>" ); 100 writer.println( "<style type=\"text/css\">" ); 101 writer.println( "ul{list-style:none;}" ); 102 103 StringBuilder relative = new StringBuilder("../../"); 104 if ( logicalResource != null && logicalResource.length() > 0 ) 105 { 106 String tmpRelative = StringUtils.replace( logicalResource, "\\", "/" ); 107 for (int i=0;i<tmpRelative.split("/").length;i++) 108 { 109 relative.append("../"); 110 } 111 } 112 writer.println( ".file{background:url(" + relative.toString() + "images/package-x-generic.png) no-repeat scroll 0 0 transparent;}" ); 113 writer.println( ".folder{background:url(" + relative.toString() + "images/folder.png) no-repeat scroll 0 0 transparent;}" ); 114 writer.println( "a{color:#0088CC;text-decoration: none;padding-left:20px;}" ); 115 writer.println( ".collection tr:nth-child(odd){background-color:#fafafa;}" ); 116 writer.println( "tr td:nth-child(2){width:150px;color:#cc8800;text-align:right;}" ); 117 writer.println( "tr td:nth-child(3){width:150px;color:#0000cc;text-align:center;}" ); 118 writer.println( "th td:nth-child(2){width:150px;}" ); 119 writer.println( "th td:nth-child(3){width:150px;}" ); 120 writer.println( "</style>" ); 121 writer.println( "<link rel=\"shortcut icon\" href=\"../../favicon.ico\"/>" ); 122 writer.println( "</head>" ); 123 writer.println( "<body>" ); 124 writer.println( "<h3>Collection: /" + logicalResource + "</h3>" ); 125 126 //Check if not root 127 if ( logicalResource != null && logicalResource.length() > 0 ) 128 { 129 Path file = Paths.get( logicalResource ); 130 String parentName = file.getParent() == null ? "/" : file.getParent().toString(); 131 132 //convert to unix path in case archiva is hosted on windows 133 parentName = StringUtils.replace( parentName, "\\", "/" ); 134 135 writer.println( "<ul>" ); 136 writer.println( "<li><a class=\"folder\" href=\"../\">" + parentName + "</a> <i><small>(Parent)</small></i></li>" ); 137 writer.println( "</ul>" ); 138 } 139 140 writer.println( "<table class=\"collection\">" ); 141 writer.println( "<tr><th>Name</th><th>Size (Bytes)</th><th>Last Modified</th></tr>" ); 142 } 143 144 private void writeDocumentEnd( PrintWriter writer ) 145 { 146 writer.println( "</table>" ); 147 writer.println( "</body>" ); 148 writer.println( "</html>" ); 149 } 150 151 private void writeHyperlinks( PrintWriter writer ) throws IOException 152 { 153 if ( !isVirtual ) 154 { 155 for ( StorageAsset localResource : repositoryAssets ) 156 { 157 localResource.list().stream().sorted( 158 Comparator.comparing( StorageAsset::getName ) 159 ).forEach( asset -> { 160 writeHyperlink( writer, asset.getName(), asset.getModificationTime().toEpochMilli(), asset.getSize(), 161 asset.isContainer() ); 162 } ); 163 } 164 } 165 else 166 { 167 // virtual repository - filter unique directories 168 SortedMap<String, StorageAsset> uniqueChildFiles = new TreeMap<>(); 169 for ( StorageAsset resource : repositoryAssets ) 170 { 171 List<StorageAsset> files = resource.list(); 172 for ( StorageAsset file : files ) 173 { 174 // the first entry wins 175 if (!uniqueChildFiles.containsKey( file.getName() )) { 176 uniqueChildFiles.put(file.getName(), file); 177 } 178 } 179 } 180 for ( Map.Entry<String, StorageAsset> entry : uniqueChildFiles.entrySet()) 181 { 182 final StorageAsset asset = entry.getValue(); 183 writeHyperlink( writer, asset.getName(), asset.getModificationTime().toEpochMilli(), 184 asset.getSize(), asset.isContainer()); 185 } 186 } 187 } 188 189 private static String fileDateFormat( long date ) 190 { 191 DateFormat dateFormatter = DateFormat.getDateTimeInstance( DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault() ); 192 Date aDate = new Date( date ); 193 return dateFormatter.format( aDate ); 194 } 195 196 private void writeHyperlink( PrintWriter writer, String resourceName, long lastModified, long fileSize, boolean directory ) 197 { 198 if ( directory ) 199 { 200 writer.println( "<tr><td><a class=\"folder\" href=\"" + resourceName + "/\">" + resourceName + "</a></td><td> </td><td> </td></tr>" ); 201 } 202 else 203 { 204 writer.println( "<tr><td><a class=\"file\" href=\"" + resourceName + "\">" + resourceName + "</a></td><td class=\"size\">" + fileSize + " </td><td class=\"date\">" + fileDateFormat( lastModified ) + "</td></tr>" ); 205 } 206 } 207}