├── gradle.properties ├── .gitignore ├── .editorconfig ├── src └── main │ ├── webapp │ └── WEB-INF │ │ └── web.xml │ └── java │ └── com │ └── robertlancer │ └── supershare │ ├── util │ ├── Mime.java │ ├── DriveBackoff.java │ ├── Mail.java │ └── ServiceFactory.java │ └── servlets │ └── DocumentViewer.java ├── LICENSE └── README.md /gradle.properties: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RobertLancer' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Users 2 | *.iml 3 | out 4 | build 5 | .idea 6 | .gradle 7 | settings.gradle 8 | src/main/webapp/WEB-INF/appengine-web.xml 9 | src/main/webapp/WEB-INF/privatekey 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | DocumentViewer 7 | com.robertlancer.supershare.servlets.DocumentViewer 8 | 9 | 10 | DocumentViewer 11 | /* 12 | 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Robert Lancer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/com/robertlancer/supershare/util/Mime.java: -------------------------------------------------------------------------------- 1 | package com.robertlancer.supershare.util; 2 | 3 | 4 | public class Mime { 5 | public static final String AUDIO = "application/vnd.google-apps.audio"; 6 | public static final String DOCUMENT = "application/vnd.google-apps.document"; 7 | public static final String DRAWING = "application/vnd.google-apps.drawing"; 8 | public static final String FILE = "application/vnd.google-apps.file"; 9 | public static final String FOLDER = "application/vnd.google-apps.folder"; 10 | public static final String FORM = "application/vnd.google-apps.form"; 11 | public static final String FUSIONTABLE = "application/vnd.google-apps.fusiontable"; 12 | public static final String PHOTO = "application/vnd.google-apps.photo"; 13 | public static final String PRESENTATION = "application/vnd.google-apps.presentation"; 14 | public static final String SCRIPT = "application/vnd.google-apps.script"; 15 | public static final String SITES = "application/vnd.google-apps.sites"; 16 | public static final String SPREADSHEET = "application/vnd.google-apps.spreadsheet"; 17 | public static final String UNKNOWN = " application/vnd.google-apps.unknown"; 18 | public static final String VIDEO = "application/vnd.google-apps.video"; 19 | public static final String PDF = "application/pdf"; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/robertlancer/supershare/util/DriveBackoff.java: -------------------------------------------------------------------------------- 1 | package com.robertlancer.supershare.util; 2 | 3 | 4 | import com.google.api.client.googleapis.services.AbstractGoogleClientRequest; 5 | import com.google.api.client.http.HttpResponseException; 6 | 7 | import java.io.IOException; 8 | import java.util.Random; 9 | import java.util.concurrent.ExecutorService; 10 | import java.util.concurrent.Executors; 11 | 12 | public class DriveBackoff { 13 | 14 | private static final int RETRY_COUNT = 5; 15 | private static final Random randomGenerator = new Random(); 16 | 17 | public T execute(AbstractGoogleClientRequest request, boolean failSilently) throws IOException { 18 | 19 | for (int i = 0; i < RETRY_COUNT; i++) { 20 | try { 21 | return request.execute(); 22 | } catch (HttpResponseException gre) { 23 | switch (gre.getStatusCode()) { 24 | case 500: 25 | case 403: 26 | System.out.println("Retrying request " + i + ", " + gre.getMessage()); 27 | try { 28 | Thread.sleep((1 << i) * 1000 + randomGenerator.nextInt(1001)); 29 | } catch (InterruptedException e) { 30 | e.printStackTrace(); 31 | } 32 | continue; 33 | default: 34 | System.out.println("Unknown status code so throwing error " + gre.getStatusCode()); 35 | if (failSilently) 36 | System.out.println("Fail silently status code $gre.statusCode - " + gre.getMessage()); 37 | else 38 | throw gre; 39 | } 40 | } catch (IOException ioe) { 41 | if (failSilently) 42 | System.out.println("Fail silently " + ioe.getMessage()); 43 | else 44 | throw ioe; 45 | } 46 | } 47 | return null; 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/java/com/robertlancer/supershare/util/Mail.java: -------------------------------------------------------------------------------- 1 | package com.robertlancer.supershare.util; 2 | 3 | import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64; 4 | import com.google.api.services.gmail.Gmail; 5 | import com.google.api.services.gmail.model.Message; 6 | 7 | import javax.mail.MessagingException; 8 | import javax.mail.Session; 9 | import javax.mail.internet.InternetAddress; 10 | import javax.mail.internet.MimeMessage; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.util.Properties; 14 | 15 | public class Mail { 16 | 17 | /** 18 | * Send an email from the user's mailbox to its recipient. 19 | * 20 | * @param service Authorized Gmail API instance. 21 | * @param userId User's email address. The special value "me" 22 | * can be used to indicate the authenticated user. 23 | * @param email Email to be sent. 24 | * @throws javax.mail.MessagingException 25 | * @throws java.io.IOException 26 | */ 27 | public static void sendMessage(Gmail service, String userId, MimeMessage email) 28 | throws MessagingException, IOException { 29 | Message message = createMessageWithEmail(email); 30 | message = service.users().messages().send(userId, message).execute(); 31 | 32 | System.out.println("Message id: " + message.getId()); 33 | System.out.println(message.toPrettyString()); 34 | } 35 | 36 | /** 37 | * Create a Message from an email 38 | * 39 | * @param email Email to be set to raw of message 40 | * @return Message containing base64 encoded email. 41 | * @throws java.io.IOException 42 | * @throws javax.mail.MessagingException 43 | */ 44 | public static Message createMessageWithEmail(MimeMessage email) throws MessagingException, IOException { 45 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 46 | email.writeTo(baos); 47 | String encodedEmail = Base64.encodeBase64URLSafeString(baos.toByteArray()); 48 | Message message = new Message(); 49 | message.setRaw(encodedEmail); 50 | return message; 51 | } 52 | 53 | /** 54 | * Create a MimeMessage using the parameters provided. 55 | * 56 | * @param to Email address of the receiver. 57 | * @param from Email address of the sender, the mailbox account. 58 | * @param subject Subject of the email. 59 | * @param bodyText Body text of the email. 60 | * @return MimeMessage to be used to send email. 61 | * @throws javax.mail.MessagingException 62 | */ 63 | public static MimeMessage createEmail(String to, String from, String subject, 64 | String bodyText) throws MessagingException { 65 | Properties props = new Properties(); 66 | Session session = Session.getDefaultInstance(props, null); 67 | 68 | MimeMessage email = new MimeMessage(session); 69 | InternetAddress tAddress = new InternetAddress(to); 70 | InternetAddress fAddress = new InternetAddress(from); 71 | 72 | email.setFrom(new InternetAddress(from)); 73 | email.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(to)); 74 | email.setSubject(subject); 75 | email.setText(bodyText); 76 | return email; 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Super Share 2 | =========== 3 | 4 | [Super Share Presentation](http://share.robertlancer.com/Super-Share) 5 | 6 | Super Share is a more elegant way to share documents from Google Drive for Google Apps users. 7 | 8 | It deploys to Google App Engine and is designed to be efficient enough to run within App Engine's free daily quota. 9 | 10 | **This system automatically changes certain file permissions to include Anyone with the link can view** 11 | 12 | + share.example.com/file-title URL pattern 13 | + Receive alerts for when files are viewed, enabled per a file 14 | + Supports partial matches, ie: "Road" will match "RoadTrip.mp4" 15 | + No Google Drive branding 16 | + Runs on Google App Engine 17 | + Will easily run within the free daily App Engine quota 18 | + Open Source with the MIT License. Feel free to fork it and customize it 19 | 20 | Demos 21 | + [Picture](http://share.robertlancer.com/KoreanBBQ.jpg) 22 | + [Spreadsheet](http://share.robertlancer.com/Spreadsheet) 23 | + [Video](http://share.robertlancer.com/Roadtrip.mp4) 24 | + [PDF](http://share.robertlancer.com/Comic-Book.pdf) 25 | + [Google Doc](http://share.robertlancer.com/Press) 26 | + [Google Slides](http://share.robertlancer.com/Super-Share) 27 | 28 | ## Prerequisites 29 | 30 | + [Perform Google Apps Domain-Wide Delegation of Authority](https://developers.google.com/drive/v2/web/delegation) 31 | + At a minimum you will need to have the *https://www.googleapis.com/auth/drive* and *https://www.googleapis.com/auth/gmail.compose* scopes enabled 32 | + Create a project in the [Google Cloud Console](http://console.developers.google.com) 33 | 34 | ## Get the code 35 | 36 | git clone https://github.com/rlancer/super-share.git 37 | cd super-share 38 | 39 | ## Arange your Content 40 | 41 | + Create a folder in Drive 42 | + Add in the files you would like to share underneath that folder 43 | + Files will automatically have their permission updated to *Anyone with the link* can view 44 | 45 | ## Add in an appengine-web.xml 46 | 47 | Create the appengine-web.xml underneath the *webapp/WEB-INF* directory. Adjust the properties to match your environment's settings. 48 | 49 | ```xml 50 | 51 | cloud-console-project-id 52 | 1 53 | true 54 | false 55 | true 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ``` 65 | 66 | ## Add your Private Key 67 | 68 | The private key comes from performing domain wide delegation of authority 69 | 70 | Copy the .p12 file to *src/main/webapp/WEB-INF/privatekey* directory 71 | 72 | ## Enable View Alert Emails for a File 73 | 74 | Simply edit the description field of the file in Drive to include the text *#SSALERT* and 75 | the owner will receive an email from themselves when the file is viewed. 76 | Emails include the IP address and User Agent of the viewer 77 | 78 | 79 | ## Run the App Locally 80 | 81 | gradle appengineRun 82 | 83 | A local server should be spawned. Visit http://localhost:SERVER_PORT/file-name in your browser. If you see your file displayed then everything works! 84 | 85 | ## Deploy to App Engine 86 | 87 | gradle appengineUpdate 88 | 89 | ## Set up your Sub-domain 90 | 91 | Generally we will want our users to visit *shares.our-domain.com/file-name* 92 | 93 | To set this up we will need to go to our Google Apps admin panel and wire up the Super Share app to our domain 94 | 95 | Login to [admin.google.com](https://admin.google.com) 96 | 97 | Go to the App Engine section - If you cant see it try clicking on More 98 | 99 | Next add your App Engine Id and map it to point to your sub-domain 100 | 101 | Follow the setup instructions to wire up your sub-domain in your DNS settings 102 | 103 | ## Roadmap 104 | 105 | + Support for static HTML websites 106 | + Support for downloading files 107 | 108 | -------------------------------------------------------------------------------- /src/main/java/com/robertlancer/supershare/util/ServiceFactory.java: -------------------------------------------------------------------------------- 1 | package com.robertlancer.supershare.util; 2 | 3 | import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; 4 | import com.google.api.client.googleapis.services.json.AbstractGoogleJsonClient; 5 | import com.google.api.client.http.HttpRequest; 6 | import com.google.api.client.http.HttpRequestInitializer; 7 | import com.google.api.client.http.HttpTransport; 8 | import com.google.api.client.http.javanet.NetHttpTransport; 9 | import com.google.api.client.json.jackson.JacksonFactory; 10 | import com.google.api.client.util.Maps; 11 | import com.google.api.services.drive.Drive; 12 | import com.google.api.services.drive.DriveScopes; 13 | import com.google.api.services.gmail.Gmail; 14 | import com.google.api.services.gmail.GmailScopes; 15 | 16 | import java.io.File; 17 | import java.io.FileInputStream; 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import java.lang.reflect.InvocationTargetException; 21 | import java.security.*; 22 | import java.security.cert.CertificateException; 23 | import java.util.Collection; 24 | import java.util.Map; 25 | 26 | public class ServiceFactory { 27 | 28 | public static String emailAddress; 29 | public static PrivateKey privateKey; 30 | 31 | static { 32 | 33 | File privateKeysDirectory = new File("WEB-INF/privatekey/"); 34 | 35 | if (!privateKeysDirectory.exists()) { 36 | throw new RuntimeException("You must have a privatekey directory underneath the WEB-INF directory."); 37 | } 38 | 39 | File[] filesUnderPrivateKey = privateKeysDirectory.listFiles(); 40 | 41 | if (filesUnderPrivateKey.length == 0) 42 | throw new RuntimeException("You must have a private key underneath the WEB-INF/privatekey/ directory."); 43 | 44 | try { 45 | 46 | emailAddress = System.getProperty("serviceAccountEmailAddress"); 47 | 48 | InputStream iss = new FileInputStream(filesUnderPrivateKey[0]); 49 | KeyStore keystore = KeyStore.getInstance("PKCS12"); 50 | keystore.load(iss, "notasecret".toCharArray()); 51 | privateKey = (PrivateKey) keystore.getKey("privatekey", "notasecret".toCharArray()); 52 | 53 | } catch (UnrecoverableKeyException e) { 54 | e.printStackTrace(System.err); 55 | } catch (CertificateException e) { 56 | e.printStackTrace(System.err); 57 | } catch (NoSuchAlgorithmException e) { 58 | e.printStackTrace(System.err); 59 | } catch (KeyStoreException e) { 60 | e.printStackTrace(System.err); 61 | } catch (IOException e) { 62 | e.printStackTrace(System.err); 63 | } 64 | } 65 | 66 | 67 | public static Gmail getGmailService(String email) { 68 | return (Gmail) getService_Singleton(email, Gmail.class); 69 | } 70 | 71 | public static Drive getDriveService(String email) { 72 | return (Drive) getService_Singleton(email, Drive.class); 73 | } 74 | 75 | 76 | public static String getAccessToken(AbstractGoogleJsonClient jsonClient) { 77 | 78 | GoogleCredential googleCredential = ((Intializer) jsonClient.getRequestFactory().getInitializer()).credential; 79 | if (googleCredential.getAccessToken() == null || googleCredential.getExpirationTimeMilliseconds() <= System.currentTimeMillis()) { 80 | try { 81 | System.out.println("Access token is null so refreshing token"); 82 | googleCredential.refreshToken(); 83 | } catch (IOException e) { 84 | System.err.println("Error refreshing token for " + googleCredential.getServiceAccountUser()); 85 | throw new ClientException(e); 86 | } 87 | } 88 | return googleCredential.getAccessToken(); 89 | } 90 | 91 | 92 | private static Map SERVICES_SINGLETONS = Maps.newHashMap(); 93 | 94 | private static AbstractGoogleJsonClient getService_Singleton(String email, Class classOf) { 95 | 96 | String key = email + classOf.getName().toLowerCase(); 97 | 98 | AbstractGoogleJsonClient clients = SERVICES_SINGLETONS.get(key); 99 | 100 | if (clients == null) { 101 | clients = getService(email, classOf); 102 | SERVICES_SINGLETONS.put(key, clients); 103 | } 104 | 105 | return clients; 106 | } 107 | 108 | private static AbstractGoogleJsonClient getService(String email, Class classOf) { 109 | 110 | Collection sets = null; 111 | 112 | String classSimpleName = classOf.getSimpleName(); 113 | 114 | java.util.Set set = new java.util.HashSet(); 115 | set.add(DriveScopes.DRIVE); 116 | set.add(GmailScopes.GMAIL_COMPOSE); 117 | sets = java.util.Collections.unmodifiableSet(set); 118 | 119 | HttpTransport httpTransport = new NetHttpTransport(); 120 | JacksonFactory jsonFactory = new JacksonFactory(); 121 | 122 | final GoogleCredential credential = new GoogleCredential.Builder() 123 | .setTransport(httpTransport) 124 | .setJsonFactory(jsonFactory) 125 | .setServiceAccountId(emailAddress) 126 | .setServiceAccountScopes(sets) 127 | .setServiceAccountUser(email) 128 | .setServiceAccountPrivateKey(privateKey) 129 | .build(); 130 | 131 | try { 132 | return ((AbstractGoogleJsonClient.Builder) getInnerClass(classOf, "Builder").getConstructors()[0].newInstance(httpTransport, jsonFactory, null)) 133 | .setApplicationName("Super Share") 134 | .setHttpRequestInitializer(new Intializer(credential)) 135 | .build(); 136 | } catch (InstantiationException e) { 137 | e.printStackTrace(); 138 | } catch (IllegalAccessException e) { 139 | e.printStackTrace(); 140 | } catch (InvocationTargetException e) { 141 | e.printStackTrace(); 142 | } 143 | 144 | return null; 145 | } 146 | 147 | public static Class getInnerClass(Class classOf, String className) { 148 | 149 | for (Class inner : classOf.getDeclaredClasses()) { 150 | if (inner.getName().toLowerCase().endsWith(className.toLowerCase())) ; 151 | return inner; 152 | } 153 | 154 | return null; 155 | } 156 | 157 | public static class Intializer implements HttpRequestInitializer { 158 | 159 | public GoogleCredential credential; 160 | 161 | public Intializer(GoogleCredential credential) { 162 | this.credential = credential; 163 | } 164 | 165 | @Override 166 | public void initialize(HttpRequest httpRequest) throws IOException { 167 | credential.initialize(httpRequest); 168 | httpRequest.setConnectTimeout(3 * 60000); 169 | httpRequest.setReadTimeout(3 * 60000); 170 | } 171 | } 172 | 173 | public static class ClientException extends RuntimeException { 174 | 175 | public Exception e; 176 | 177 | public ClientException(Exception e) { 178 | this.e = e; 179 | } 180 | } 181 | } 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /src/main/java/com/robertlancer/supershare/servlets/DocumentViewer.java: -------------------------------------------------------------------------------- 1 | package com.robertlancer.supershare.servlets; 2 | 3 | import com.google.api.services.drive.Drive; 4 | import com.google.api.services.drive.model.File; 5 | import com.google.api.services.drive.model.FileList; 6 | import com.google.api.services.drive.model.Permission; 7 | import com.google.api.services.gmail.Gmail; 8 | import com.robertlancer.supershare.util.DriveBackoff; 9 | import com.robertlancer.supershare.util.Mail; 10 | import com.robertlancer.supershare.util.Mime; 11 | import com.robertlancer.supershare.util.ServiceFactory; 12 | 13 | import javax.mail.MessagingException; 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServlet; 16 | import javax.servlet.http.HttpServletRequest; 17 | import javax.servlet.http.HttpServletResponse; 18 | import java.io.IOException; 19 | import java.util.Date; 20 | import java.util.List; 21 | 22 | public class DocumentViewer extends HttpServlet { 23 | 24 | @Override 25 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 26 | 27 | String pathInfo = req.getPathInfo(); 28 | 29 | if (pathInfo == null || pathInfo.length() <= 1) { 30 | sendError(404, resp); 31 | return; 32 | } 33 | 34 | String fileTitle = pathInfo.substring(1).replace("-", " ").replace("'", ""); 35 | 36 | if (fileTitle.equalsIgnoreCase("favicon.ico")) 37 | return; 38 | 39 | String folderId = System.getProperty("folder"); 40 | String email = System.getProperty("email"); 41 | 42 | File fileToOutput = getFile(fileTitle, folderId, email); 43 | 44 | if (fileToOutput == null) { 45 | sendError(404, resp); 46 | return; 47 | } else { 48 | boolean anyoneHasAccess = false; 49 | for (Permission permission : fileToOutput.getPermissions()) { 50 | if (permission.getType().equalsIgnoreCase("anyone")) { 51 | anyoneHasAccess = true; 52 | break; 53 | } 54 | } 55 | 56 | if (!anyoneHasAccess) { 57 | Permission permission = new Permission(); 58 | permission.setType("anyone"); 59 | permission.setRole("reader"); 60 | permission.setWithLink(true); 61 | 62 | Drive.Permissions.Insert updatePermissionReq = ServiceFactory.getDriveService(email).permissions().insert(fileToOutput.getId(), permission); 63 | Permission inserted = new DriveBackoff().execute(updatePermissionReq, false); 64 | } 65 | 66 | resp.getWriter().write(outputFile(fileToOutput)); 67 | sendViewAlert(fileToOutput, req); 68 | } 69 | } 70 | 71 | public static void sendViewAlert(File fileToOutput, HttpServletRequest req) { 72 | String description = fileToOutput.getDescription(); 73 | 74 | if (description == null) 75 | return; 76 | 77 | boolean sendAlert = description.contains("#SSALERT"); 78 | 79 | if (!sendAlert) 80 | return; 81 | 82 | String email = fileToOutput.getOwners().get(0).getEmailAddress(); 83 | Gmail gmail = ServiceFactory.getGmailService(email); 84 | 85 | String ipAddress = req.getRemoteAddr(); 86 | 87 | String body = fileToOutput.getTitle() + " was viewed.\n\n" + 88 | "IP: " + ipAddress + "\n" + 89 | "User Agent: " + req.getHeader("User-Agent") + "\n" + new Date().toLocaleString(); 90 | 91 | try { 92 | 93 | Mail.sendMessage(gmail, "me", Mail.createEmail(email, email, fileToOutput.getTitle(), body)); 94 | } catch (MessagingException e) { 95 | e.printStackTrace(); 96 | } catch (IOException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | public static String outputFile(File fileToOutput) { 102 | 103 | String url = outputFileAsIFrameGetURL(fileToOutput); 104 | 105 | String iframe = ""; 106 | 107 | StringBuilder output = new StringBuilder(); 108 | output.append(""); 109 | output.append("" + fileToOutput.getTitle() + ""); 110 | output.append(""); 112 | output.append(""); 113 | output.append(""); 114 | output.append(""); 117 | output.append(iframe); 118 | output.append(""); 119 | 120 | return output.toString(); 121 | } 122 | 123 | public static String outputFileAsIFrameGetURL(File fileToOutput) { 124 | 125 | String domain = fileToOutput.getOwners().get(0).getEmailAddress().split("@")[1]; 126 | String id = fileToOutput.getId(); 127 | 128 | switch (fileToOutput.getMimeType()) { 129 | case Mime.DOCUMENT: 130 | return "https://docs.google.com/a/" + domain + "/document/d/" + id + "/preview"; 131 | case Mime.SPREADSHEET: 132 | return "https://docs.google.com/a/" + domain + "/spreadsheet/ccc?key=" + id + "&output=html&widget=true&chrome=false"; 133 | case Mime.PRESENTATION: 134 | return "https://docs.google.com/a/" + domain + "/presentation/d/" + id + "/preview"; 135 | case Mime.DRAWING: 136 | return "https://docs.google.com/a/" + domain + "/drawings/d/" + id + "/preview"; 137 | default: 138 | return "https://docs.google.com/a/" + domain + "/file/d/" + id + "/preview"; 139 | } 140 | } 141 | 142 | public static File getFile(String title, String folderId, String email) throws IOException { 143 | String operator; 144 | if (System.getProperty("allowPartialMatches").equals("false")) { 145 | operator = "="; 146 | } else { 147 | operator = "contains"; 148 | } 149 | 150 | Drive drive = ServiceFactory.getDriveService(email); 151 | Drive.Files.List request = drive.files().list().setQ("'" + folderId + "' in parents and trashed = false and title " + operator + " '" + title + "'").setFields("items(description,owners,id,downloadUrl,iconLink,mimeType,permissions,title)").setMaxResults(1000); 152 | List items = new DriveBackoff().execute(request, false).getItems(); 153 | 154 | if (items.isEmpty()) 155 | return null; 156 | 157 | if (items.size() == 1) 158 | return items.get(0); 159 | else { 160 | for (File file : items) { 161 | if (file.getTitle().equalsIgnoreCase(title)) 162 | return file; 163 | } 164 | } 165 | 166 | return items.get(0); 167 | } 168 | 169 | public void sendError(int code, HttpServletResponse resp) { 170 | try { 171 | resp.setStatus(code); 172 | if (code == 404) 173 | resp.getWriter().write(""+ 174 | "404 Not Found"+ 175 | "

Not Found

The requested resource was not found.

"+ 176 | "
Google Frontend
"); 177 | else 178 | resp.getWriter().write(""+ 179 | "500 Internal Server Error"+ 180 | "

Not Found

The server encountered an "+ 181 | "internal error and was unable to complete your request.

"+ 182 | "
Google Frontend
"); 183 | } catch (Exception e) { 184 | e.printStackTrace(System.err); 185 | } 186 | } 187 | 188 | @Override 189 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 190 | doGet(req, resp); 191 | } 192 | } 193 | --------------------------------------------------------------------------------