001/* 002 * Copyright 2023 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.nextcloud; 017 018import java.io.IOException; 019import java.util.Iterator; 020import java.util.List; 021import java.util.Optional; 022 023import org.apache.avalon.framework.activity.Initializable; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.cocoon.ProcessingException; 028import org.apache.cocoon.generation.AbstractGenerator; 029import org.apache.cocoon.xml.AttributesImpl; 030import org.apache.cocoon.xml.XMLUtils; 031import org.apache.commons.lang3.StringUtils; 032import org.xml.sax.SAXException; 033 034import org.ametys.core.util.DateUtils; 035import org.ametys.plugins.explorer.resources.generators.ResourcesExplorerGenerator; 036import org.ametys.plugins.extrausermgt.oauth.OAuthProvider; 037import org.ametys.plugins.extrausermgt.oauth.OauthProviderExtensionPoint; 038import org.ametys.runtime.config.Config; 039 040import com.github.sardine.DavResource; 041import com.github.sardine.Sardine; 042import com.github.sardine.impl.SardineImpl; 043import com.nimbusds.oauth2.sdk.token.AccessToken; 044 045/** 046 * Generate SAX event to represent the content of a remote Nextcloud repository. 047 * 048 * This generator is intended to produced an XML equivalent to one produced by {@link ResourcesExplorerGenerator}. 049 * The main difference will be in the attribute available. No id or icon cls can be computed. 050 */ 051public class NextcloudExplorerGenerator extends AbstractGenerator implements Serviceable, Initializable 052{ 053 private String _remoteUrl; 054 private OauthProviderExtensionPoint _oauthEP; 055 056 public void service(ServiceManager manager) throws ServiceException 057 { 058 _oauthEP = (OauthProviderExtensionPoint) manager.lookup(OauthProviderExtensionPoint.ROLE); 059 } 060 061 public void initialize() throws Exception 062 { 063 _remoteUrl = Config.getInstance().getValue("nextcloud.remote.url"); 064 } 065 066 public void generate() throws IOException, SAXException, ProcessingException 067 { 068 OAuthProvider nextcloudProvider = _oauthEP.getExtension("nextcloud"); 069 Optional<AccessToken> accessToken = nextcloudProvider.getStoredAccessToken(); 070 Optional<String> userId = nextcloudProvider.getStoredCustomParameter("user_id"); 071 if (accessToken.isEmpty() || userId.isEmpty()) 072 { 073 contentHandler.startDocument(); 074 XMLUtils.createElement(contentHandler, "not-connected"); 075 contentHandler.endDocument(); 076 return; 077 } 078 079 Sardine webDavClient = new SardineImpl(accessToken.get().getValue()); 080 081 contentHandler.startDocument(); 082 083 saxRemoteFolder("/remote.php/dav/files/" + userId.get() + "/", webDavClient); 084 085 contentHandler.endDocument(); 086 } 087 088 private void saxRemoteFolder(String path, Sardine davClient) throws ProcessingException, SAXException 089 { 090 try 091 { 092 // Retrieve all resources 093 List<DavResource> resources = davClient.list(_remoteUrl + path); 094 DavResource target = null; 095 for (Iterator iterator = resources.iterator(); iterator.hasNext() && target == null;) 096 { 097 DavResource resource = (DavResource) iterator.next(); 098 if (StringUtils.equals(resource.getPath(), path)) 099 { 100 resources.remove(resource); 101 target = resource; 102 } 103 } 104 if (target != null) 105 { 106 saxDirectory(target, resources, davClient); 107 } 108 else 109 { 110 throw new ProcessingException("Failed to retrieve information for remote folder at path " + path); 111 } 112 } 113 catch (IOException e) 114 { 115 throw new ProcessingException("Failed to list content of remote folder '" + path + "'", e); 116 } 117 118 } 119 120 private void saxDirectory(DavResource directory, List<DavResource> children, Sardine davClient) throws SAXException, ProcessingException 121 { 122 AttributesImpl childAtts = new AttributesImpl(); 123 124 childAtts.addCDATAAttribute("name", directory.getName()); 125 126 childAtts.addCDATAAttribute("path", directory.getPath()); 127 childAtts.addCDATAAttribute("type", ResourcesExplorerGenerator.RESOURCE_COLLECTION); 128 129 boolean hasResources = false; 130 boolean hasChildNodes = false; 131 for (Iterator iterator = children.iterator(); iterator.hasNext() && !(hasChildNodes && hasResources);) 132 { 133 DavResource davResource = (DavResource) iterator.next(); 134 if (davResource.isDirectory()) 135 { 136 hasChildNodes = true; 137 } 138 else 139 { 140 hasResources = true; 141 } 142 } 143 if (hasChildNodes) 144 { 145 childAtts.addCDATAAttribute("hasChildNodes", "true"); 146 } 147 148 if (hasResources) 149 { 150 childAtts.addCDATAAttribute("hasResources", "true"); 151 } 152 XMLUtils.startElement(contentHandler, "Node", childAtts); 153 154 for (DavResource child : children) 155 { 156 if (child.isDirectory()) 157 { 158 saxRemoteFolder(child.getPath(), davClient); 159 } 160 else 161 { 162 saxResource(child); 163 } 164 } 165 166 XMLUtils.endElement(contentHandler, "Node"); 167 168 } 169 170 private void saxResource(DavResource resource) throws SAXException 171 { 172 AttributesImpl childAtts = new AttributesImpl(); 173 childAtts.addCDATAAttribute("href", _remoteUrl + resource.getHref().toString()); 174 childAtts.addCDATAAttribute("name", resource.getName()); 175 childAtts.addCDATAAttribute("mimetype", resource.getContentType()); 176 childAtts.addCDATAAttribute("lastModified", DateUtils.dateToString(resource.getModified())); 177 childAtts.addCDATAAttribute("size", String.valueOf(resource.getContentLength())); 178 childAtts.addCDATAAttribute("type", ResourcesExplorerGenerator.RESOURCE); 179 childAtts.addCDATAAttribute("path", resource.getPath()); 180 181 XMLUtils.createElement(contentHandler, "Node", childAtts); 182 } 183}