├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .travis.yml ├── LICENSE ├── Original-Readme.md ├── Procfile ├── README.md ├── app.json ├── application.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src └── main ├── java └── com │ └── github │ └── dhaval_mehta │ └── savetogoogledrive │ ├── SaveToGoogleDriveApplication.java │ ├── advice │ └── WebRestControllerAdvice.java │ ├── config │ └── SwaggerConfig.java │ ├── controller │ ├── mvc │ │ ├── IndexController.java │ │ └── UploadController.java │ └── rest │ │ ├── StatusController.java │ │ ├── drive │ │ └── GoogleDriveController.java │ │ └── oauth │ │ ├── GoogleOauthController.java │ │ └── SessionController.java │ ├── exception │ └── ApiException.java │ ├── listener │ ├── ApplicationContextListener.java │ └── SessionListener.java │ ├── model │ ├── ApiError.java │ ├── DownloadFileInfo.java │ ├── Token.java │ ├── UploadInformation.java │ ├── UploadStatus.java │ └── User.java │ ├── uploader │ ├── UploadManager.java │ ├── UploadTask.java │ ├── Uploader.java │ └── drive │ │ ├── DriveUploader.java │ │ ├── DriveUploaderBuilder.java │ │ ├── NonResumableDriveUploader.java │ │ └── ResumableDriveUploader.java │ └── utility │ ├── HttpUtilities.java │ └── IOUtilities.java ├── resources ├── application.properties └── static │ ├── css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootstrap.min.css.map │ ├── font-awesome.css │ ├── font-awesome.min.css │ └── style.default.css │ ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 │ ├── img │ ├── guest.png │ └── savetodrive.png │ └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── front.js │ ├── jquery.cookie.js │ ├── jquery.min.js │ ├── jquery.validate.min.js │ ├── sticky-footer.js │ ├── submit_upload.js │ ├── tether.min.js │ └── uploads.js └── webapp ├── META-INF └── context.xml └── WEB-INF ├── jsp ├── index.jsp ├── new_upload.jsp └── uploads.jsp ├── tags └── templete.tag └── web.xml /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_install: 3 | - chmod +x mvnw 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Dhaval Mehta 2 | Save to Google Drive 3 |
4 | 5 | 6 |

A web application to download file directly to google drive from URL.

7 | 8 |
9 | 10 | 11 | 12 | Build Status 14 | 15 | 16 | 17 | 18 | Contributions welcome 20 | 21 | 22 | 23 | 24 | Issues 26 | 27 | 28 | 29 | 30 | License MIT 32 | 33 |
34 | 35 | ## Table of Content 36 | - [Features](#features) 37 | - [Live Demo](#live-demo) 38 | - [Build Process](#build-process) 39 | - [Built With](#built-with) 40 | - [Bug Report](#bug-report) 41 | - [Feature Request](#feature-request) 42 | - [Team](#team) 43 | - [Links](#links) 44 | - [Contributing](#contributing) 45 | - [License](#license) 46 | 47 | ## Features 48 | 49 | * OAuth - Say good bye to registration and login 50 | - Use Google account to login 51 | * No upload size limit 52 | * No access to your personal files 53 | * No Concurrent Upload limit 54 | * Automatically extraction of filename from URL 55 | * Small API: with only 6 methods there's not much to learn 56 | 57 | ## Live Demo 58 | 59 | Here is a working live demo: [https://savetogoogledrive.herokuapp.com](https://savetogoogledrive.herokuapp.com) 60 | 61 | ## Build Process 62 | 63 | ```bash 64 | # Clone this repository 65 | $ git clone https://github.com/dhaval-mehta/url-to-google-drive 66 | 67 | # Go into the repository 68 | $ cd url-to-google-drive 69 | 70 | # Build the app 71 | $ ./mvnw package 72 | 73 | # Run the app 74 | $ ./mvnw spring-boot:run 75 | ``` 76 | 77 | ## Built With 78 | 79 | This application uses several open source packages to run. 80 | 81 | - [Spring](https://spring.io/) 82 | - [Gson](https://github.com/google/gson) 83 | - [Swagger](http://springfox.github.io/springfox/) 84 | - [Apache commons email](https://commons.apache.org/proper/commons-email/) 85 | - [Apache commons io](https://commons.apache.org/proper/commons-io/) 86 | - [Apache Tomcat](http://tomcat.apache.org/) 87 | 88 | ## Bug Report 89 | 90 | If you find a bug (the website couldn't handle the upload request and / or gave undesired results), kindly open an issue [here](https://github.com/dhaval-mehta/url-to-google-drive/issues/new) by including your upload URL. 91 | 92 | ## Feature Request 93 | 94 | Feature requests are always welcome. If you'd like to request a new function, feel free to do so by opening an issue [here](https://github.com/dhaval-mehta/url-to-google-drive/issues/new). Please include sample queries and their corresponding results. 95 | 96 | ## Team 97 | 98 | - [Dhaval Mehta](https://github.com/dhaval-mehta) 99 | - [Aditya Krishnakumar](https://github.com/beingadityak) 100 | 101 | ## Links 102 | 103 | * [Web site](https://savetogoogledrive.herokuapp.com) 104 | * [Documentation](https://savetogoogledrive.herokuapp.com/swagger-ui.html) 105 | * [Bug Report](https://savetogoogledrive.herokuapp.com/bug_report.jsp) 106 | * [Issue tracker](https://github.com/dhaval-mehta/url-to-google-drive/issues) 107 | * [Source code](https://github.com/dhaval-mehta/url-to-google-drive/) 108 | 109 | ## Contributing 110 | 111 | We always welcome new contributors. If you wish to contribute, please take a quick look at the [guidelines](./CONTRIBUTING.md)! 112 | 113 | ## License 114 | 115 | MIT © Dhaval Mehta 116 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java $JAVA_OPTS -Dserver.port=$PORT -jar target/*.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bhadoo Cloud Drive 2 | 3 | Save Files from URL to Google Drive 4 | 5 | ## Required 6 | 7 | * Open [Google Dev Credentials Site](https://console.developers.google.com/apis/credentials). 8 | * Create a Project, name as you like. 9 | * Enable [Drive API](https://console.developers.google.com/apis/library/drive.googleapis.com) 10 | * In [Credentials Page](https://console.developers.google.com/apis/credentials) Click `Create Credentials` and then Click `OAuth Client ID`. 11 | * Select Web Application. 12 | * In `Authorized JavaScript origins` enter your domain name or IP whichever you are using for this app. 13 | * In `Authorized redirect URIs` enter your domain name or IP with `/api/oauth/google/callback` at last. 14 | * eg. for `https://bhadooclouduploader.herokuapp.com` it's `https://bhadooclouduploader.herokuapp.com/api/oauth/google/callback` 15 | * Copy your details. 16 | * You'll need these 3 when deploying to Heroku using below button. 17 | 18 | ## Deploy to Ubuntu 19 | 20 | * Use your own Client Credentials if you're deploying it, or it won't work. 21 | * `git clone https://github.com/PBhadoo/gdupload app` 22 | * `cd app` 23 | * `chmod +x mvnw` 24 | * `export client_id=58094879805-jdsomen2duv9ilj81fcu1qkag5todele.apps.googleusercontent.com` 25 | * `export client_secret=EG0OYdjcllAGJ81eEMpQ3vW6` 26 | * `export redirect_uri=https://gdupload.hashhackers.com/api/oauth/google/callback` 27 | * `tmux new -s 1` 28 | * `./mvnw package` 29 | * `./mvnw spring-boot:run` 30 | 31 | Source: https://github.com/cloud-transfer/cloud-transfer-backend 32 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SaveToGoogleDrive - Bhadoo Cloud", 3 | "description": "App to Upload files directly to Google Drive", 4 | "repository": "https://github.com/ParveenBhadooOfficial/SaveToGoogleDrive", 5 | "logo": "https://raw.githubusercontent.com/ParveenBhadooOfficial/SaveToGoogleDrive/master/src/main/resources/static/img/savetodrive.png", 6 | "keywords": ["google","drive","bhadoo","bhadoo cloud"], 7 | "env": { 8 | "client_id": "29163138686-6neioodnr2m04i57l3rfu6vuvs44bcmq.apps.googleusercontent.com" { 9 | "description": "Client ID from Google Console (http://bit.ly/37ED7dv)", 10 | "value": "58094879805-jdsomen2duv9ilj81fcu1qkag5todele.apps.googleusercontent.com" 11 | }, 12 | "client_secret": "fd8ofG8abMDsoj_DsPDJLr0p" { 13 | "description": "Client Secret from Google Console (http://bit.ly/37ED7dv)", 14 | "value": "EG0OYdjcllAGJ81eEMpQ3vW6" 15 | }, 16 | "redirect_uri":"https://cloud-website256.herokuapp.com/api/oauth/google/callback"{ 17 | "description": "Your App URL eg. https://app.example.com/api/oauth/google/callback (Replace this with your own)", 18 | "value": "https://bhadooclouduploader.herokuapp.com/api/oauth/google/callback" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /application.properties: -------------------------------------------------------------------------------- 1 | spring.mvc.view.prefix: /WEB-INF/jsp/ 2 | spring.mvc.view.suffix: .jsp 3 | server.error.whitelabel.enabled=false -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.dhavalmehta1997 7 | savetogoogledrive 8 | 1.0 9 | jar 10 | 11 | savetogoogledrive 12 | A Restful API and web application to upload file directly to google drive from URL. 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.8.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.apache.tomcat.embed 34 | tomcat-embed-jasper 35 | provided 36 | 37 | 38 | javax.servlet 39 | jstl 40 | 41 | 42 | commons-io 43 | commons-io 44 | 2.7 45 | 46 | 47 | com.google.code.gson 48 | gson 49 | jar 50 | 51 | 52 | javax.validation 53 | validation-api 54 | 55 | 56 | org.apache.httpcomponents 57 | httpclient 58 | jar 59 | 60 | 61 | org.apache.commons 62 | commons-email 63 | 1.5 64 | 65 | 66 | io.springfox 67 | springfox-swagger-ui 68 | 2.6.1 69 | 70 | 71 | io.springfox 72 | springfox-swagger2 73 | 2.6.1 74 | 75 | 76 | 77 | org.postgresql 78 | postgresql 79 | runtime 80 | 81 | 82 | org.springframework.boot 83 | spring-boot-starter-test 84 | test 85 | 86 | 87 | org.springframework.security 88 | spring-security-test 89 | test 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-maven-plugin 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/SaveToGoogleDriveApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.ServletComponentScan; 6 | import org.springframework.boot.web.support.SpringBootServletInitializer; 7 | 8 | @SpringBootApplication 9 | @ServletComponentScan 10 | public class SaveToGoogleDriveApplication extends SpringBootServletInitializer { 11 | 12 | public static void main(String[] args) throws Exception { 13 | SpringApplication.run(SaveToGoogleDriveApplication.class, args); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/advice/WebRestControllerAdvice.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.advice; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; 7 | import com.github.dhaval_mehta.savetogoogledrive.model.ApiError; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.HttpRequestMethodNotSupportedException; 11 | import org.springframework.web.bind.annotation.ControllerAdvice; 12 | import org.springframework.web.bind.annotation.ExceptionHandler; 13 | 14 | @ControllerAdvice 15 | public class WebRestControllerAdvice { 16 | 17 | @ExceptionHandler(ApiException.class) 18 | public ResponseEntity handleApiException(ApiException ex) { 19 | ApiError apiError = new ApiError(ex.getStatus(), ex.getMessage(), null); 20 | return new ResponseEntity<>(apiError, apiError.getStatus()); 21 | } 22 | 23 | @ExceptionHandler(HttpRequestMethodNotSupportedException.class) 24 | public ResponseEntity handleMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) { 25 | ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, ex.getMessage(), null); 26 | return new ResponseEntity<>(apiError, apiError.getStatus()); 27 | } 28 | 29 | @ExceptionHandler(Exception.class) 30 | public ResponseEntity handleGenericException(Exception ex) { 31 | Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); 32 | ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage(), null); 33 | return new ResponseEntity<>(apiError, HttpStatus.INTERNAL_SERVER_ERROR); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.service.Contact; 11 | import springfox.documentation.spi.DocumentationType; 12 | import springfox.documentation.spring.web.plugins.Docket; 13 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 14 | 15 | @Configuration 16 | @EnableSwagger2 17 | public class SwaggerConfig { 18 | @Bean 19 | public Docket api() { 20 | return new Docket(DocumentationType.SWAGGER_2).useDefaultResponseMessages(false).select() 21 | .apis(RequestHandlerSelectors 22 | .basePackage("com.github.dhavalmehta1997.savetogoogledrive.controller.rest")) 23 | .paths(PathSelectors.any()).build().apiInfo(apiInfo()); 24 | } 25 | 26 | private ApiInfo apiInfo() { 27 | return new ApiInfoBuilder().title("Save to Drive Rest API") 28 | .description("It is a free open source api to upload file from url directly to Google Drive.") 29 | .contact(new Contact("Dhaval Mehta", null, "dhaval.mehta197@gmail.com")).version("1.0").build(); 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/controller/mvc/IndexController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.controller.mvc; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | @Controller 7 | public class IndexController { 8 | 9 | @RequestMapping("") 10 | public String index() { 11 | return "index"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/controller/mvc/UploadController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.controller.mvc; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | @Controller 7 | public class UploadController { 8 | 9 | @RequestMapping("/new_upload") 10 | public String newUplaod() { 11 | return "new_upload"; 12 | } 13 | 14 | @RequestMapping("/uploads") 15 | public String uploads() { 16 | return "uploads"; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/controller/rest/StatusController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.controller.rest; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import javax.servlet.http.HttpSession; 8 | 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.PathVariable; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.ResponseStatus; 16 | import org.springframework.web.bind.annotation.RestController; 17 | 18 | import com.github.dhaval_mehta.savetogoogledrive.model.ApiError; 19 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadInformation; 20 | import com.github.dhaval_mehta.savetogoogledrive.uploader.UploadManager; 21 | 22 | import io.swagger.annotations.Api; 23 | import io.swagger.annotations.ApiOperation; 24 | import io.swagger.annotations.ApiResponse; 25 | import io.swagger.annotations.ApiResponses; 26 | 27 | @RestController 28 | @RequestMapping("api/status") 29 | @Api(description = "handle requests for upload status.") 30 | public class StatusController { 31 | 32 | private final HttpSession session; 33 | 34 | @Autowired 35 | public StatusController(HttpSession session) { 36 | this.session = session; 37 | } 38 | 39 | @GetMapping("/{id}") 40 | @ResponseStatus(HttpStatus.OK) 41 | @ApiOperation(value = "gives upload information for given id", response = UploadInformation.class) 42 | @ApiResponses({ @ApiResponse(code = 404, message = "server does not find any upload with given ID"), 43 | @ApiResponse(code = 500, message = "There is something wrong at server side. Please contact developers.", response = ApiError.class) }) 44 | public ResponseEntity getStatus(@PathVariable("id") String id) { 45 | UploadInformation uploadInformation = UploadManager.getUploadManager().getUploadInformation(id); 46 | 47 | if (uploadInformation == null) 48 | return new ResponseEntity<>(HttpStatus.NOT_FOUND); 49 | 50 | return new ResponseEntity<>(uploadInformation, HttpStatus.OK); 51 | } 52 | 53 | @GetMapping 54 | @ApiOperation(value = "gives array of upload information of current user.", response = UploadInformation[].class) 55 | @ApiResponses({ 56 | @ApiResponse(code = 500, message = "There is something wrong at server side. Please contact developers.", response = ApiError.class) }) 57 | public List handleStatusRequest() { 58 | 59 | @SuppressWarnings("unchecked") 60 | List uploads = (List) session.getAttribute("uploads"); 61 | List uploadInformations = new ArrayList<>(); 62 | 63 | if (uploads != null) 64 | uploads.forEach((id) -> uploadInformations.add(UploadManager.getUploadManager().getUploadInformation(id))); 65 | 66 | Collections.reverse(uploadInformations); 67 | return uploadInformations; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/controller/rest/drive/GoogleDriveController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.controller.rest.drive; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.net.MalformedURLException; 6 | import java.net.URI; 7 | import java.net.URL; 8 | import java.net.UnknownHostException; 9 | import java.util.List; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | 13 | import javax.servlet.http.HttpSession; 14 | 15 | import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; 16 | import com.github.dhaval_mehta.savetogoogledrive.model.ApiError; 17 | import com.github.dhaval_mehta.savetogoogledrive.model.User; 18 | import com.github.dhaval_mehta.savetogoogledrive.uploader.UploadTask; 19 | import com.github.dhaval_mehta.savetogoogledrive.uploader.Uploader; 20 | import com.github.dhaval_mehta.savetogoogledrive.uploader.drive.DriveUploaderBuilder; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.http.HttpStatus; 23 | import org.springframework.http.ResponseEntity; 24 | import org.springframework.web.bind.annotation.PostMapping; 25 | import org.springframework.web.bind.annotation.RequestMapping; 26 | import org.springframework.web.bind.annotation.RequestParam; 27 | import org.springframework.web.bind.annotation.ResponseStatus; 28 | import org.springframework.web.bind.annotation.RestController; 29 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 30 | 31 | import com.github.dhaval_mehta.savetogoogledrive.uploader.UploadManager; 32 | 33 | import io.swagger.annotations.Api; 34 | import io.swagger.annotations.ApiOperation; 35 | import io.swagger.annotations.ApiResponse; 36 | import io.swagger.annotations.ApiResponses; 37 | import io.swagger.annotations.ResponseHeader; 38 | 39 | @RestController 40 | @RequestMapping("api/drive") 41 | @Api(description = "handles requests related to Google Drive", produces = "application/json", consumes = "application/json") 42 | public class GoogleDriveController { 43 | 44 | private final HttpSession session; 45 | 46 | @Autowired 47 | public GoogleDriveController(HttpSession session) { 48 | this.session = session; 49 | } 50 | 51 | @PostMapping("/upload") 52 | @ResponseStatus(HttpStatus.ACCEPTED) 53 | @ApiOperation(value = "takes URL and add it into upload queue") 54 | @ApiResponses({ 55 | @ApiResponse(code = 202, message = "Server has successfully added URL into upload queue", responseHeaders = { 56 | @ResponseHeader(name = "Location", response = java.net.URL.class) }), 57 | @ApiResponse(code = 422, message = "Either URL is invalid or Server did't find file from submitted URL. For more details regrading error, read response json.", response = ApiError.class), 58 | @ApiResponse(code = 500, message = "There is something wrong at server side. Please contact developers.", response = ApiError.class) }) 59 | public ResponseEntity handleUploadRequest(@RequestParam("url") String urlString, 60 | @RequestParam(value = "filename", required = false) String filename) { 61 | User user = (User) session.getAttribute("user"); 62 | URL url; 63 | 64 | try { 65 | url = new URL(urlString); 66 | } catch (MalformedURLException ex) { 67 | throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, "URL is Malformed", ex); 68 | } 69 | 70 | try { 71 | Uploader uploader = new DriveUploaderBuilder().setUploadUrl(url).setFileName(filename).setUser(user) 72 | .build(); 73 | 74 | UploadTask uploadTask = new UploadTask(uploader); 75 | UploadManager.getUploadManager().add(uploadTask); 76 | @SuppressWarnings("unchecked") 77 | List uploads = (List) session.getAttribute("uploads"); 78 | uploads.add(uploadTask.getId()); 79 | 80 | URI location = ServletUriComponentsBuilder.fromCurrentRequest().replacePath("api/status/") 81 | .replaceQueryParams(null).path(uploadTask.getId()).build().toUri(); 82 | 83 | return ResponseEntity.accepted().header("Location", location.toString()).body(null); 84 | 85 | } catch (FileNotFoundException ex) { 86 | throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, "No file found at given URL", ex); 87 | } catch (UnknownHostException ex) { 88 | throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, url.getHost() + ": host not found.", ex); 89 | } catch (IOException ex) { 90 | Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex); 91 | throw new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage(), ex); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/controller/rest/oauth/GoogleOauthController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.controller.rest.oauth; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.net.URISyntaxException; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import javax.servlet.http.HttpServletResponse; 11 | import javax.servlet.http.HttpSession; 12 | import javax.validation.constraints.NotNull; 13 | 14 | import org.apache.http.NameValuePair; 15 | import org.apache.http.client.HttpClient; 16 | import org.apache.http.client.entity.UrlEncodedFormEntity; 17 | import org.apache.http.client.methods.HttpGet; 18 | import org.apache.http.client.methods.HttpPost; 19 | import org.apache.http.client.utils.URIBuilder; 20 | import org.apache.http.impl.client.HttpClientBuilder; 21 | import org.apache.http.message.BasicNameValuePair; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.http.HttpStatus; 24 | import org.springframework.web.bind.annotation.GetMapping; 25 | import org.springframework.web.bind.annotation.RequestMapping; 26 | import org.springframework.web.bind.annotation.RequestParam; 27 | import org.springframework.web.bind.annotation.ResponseStatus; 28 | import org.springframework.web.bind.annotation.RestController; 29 | 30 | import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; 31 | import com.github.dhaval_mehta.savetogoogledrive.model.Token; 32 | import com.github.dhaval_mehta.savetogoogledrive.model.User; 33 | import com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities; 34 | import com.google.gson.FieldNamingPolicy; 35 | import com.google.gson.Gson; 36 | import com.google.gson.GsonBuilder; 37 | 38 | import io.swagger.annotations.Api; 39 | import io.swagger.annotations.ApiOperation; 40 | import io.swagger.annotations.ApiResponse; 41 | import io.swagger.annotations.ApiResponses; 42 | import io.swagger.annotations.ResponseHeader; 43 | 44 | /** 45 | * @author Dhaval 46 | */ 47 | @RestController 48 | @RequestMapping("/api/oauth/google") 49 | @Api(description = "handles Google OAuth.", produces = "application/json", consumes = "application/json") 50 | public class GoogleOauthController { 51 | 52 | private final static String PROFILE_URL = "https://www.googleapis.com/oauth2/v1/userinfo"; 53 | private final static String TOKEN_URL = "https://www.googleapis.com/oauth2/v4/token"; 54 | private final static String OAUTH_URL = "https://accounts.google.com/o/oauth2/auth"; 55 | private final static String ACCESS_TYPE = "offline"; 56 | private final static String RESPONSE_TYPE = "code"; 57 | private final static String SCOPE = "https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"; 58 | private final static String GRANT_TYPE = "authorization_code"; 59 | private final static String CLIENT_ID = System.getenv("client_id"); 60 | private final static String CLIENT_SECRET = System.getenv("client_secret"); 61 | private final static String REDIRECT_URI = System.getenv("redirect_uri"); 62 | private final static Gson gson = new GsonBuilder() 63 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); 64 | private final HttpSession session; 65 | 66 | @Autowired 67 | public GoogleOauthController(HttpSession session) { 68 | this.session = session; 69 | } 70 | 71 | @GetMapping(value = "/redirect") 72 | @ResponseStatus(HttpStatus.FOUND) 73 | @ApiOperation(value = "redirect client for oauth") 74 | @ApiResponses(@ApiResponse(code = 302, message = "server successfully redirected client for oauth", responseHeaders = @ResponseHeader(name = "Location", response = java.net.URL.class))) 75 | public void redirectForOauth2(HttpServletResponse response) throws URISyntaxException, IOException { 76 | String redirectUrl = new URIBuilder(OAUTH_URL).addParameter("scope", SCOPE) 77 | .addParameter("access_type", ACCESS_TYPE).addParameter("redirect_uri", REDIRECT_URI) 78 | .addParameter("response_type", RESPONSE_TYPE).addParameter("client_id", CLIENT_ID).build().toString(); 79 | response.sendRedirect(redirectUrl); 80 | } 81 | 82 | @GetMapping("/callback") 83 | @ResponseStatus(HttpStatus.FOUND) 84 | @ApiOperation(value = "handles oauth callback from google", hidden = true) 85 | @ApiResponses(@ApiResponse(code = 302, message = "redirect client for session check", responseHeaders = @ResponseHeader(name = "Location", response = java.net.URL.class))) 86 | public void handleCallback(@RequestParam("code") String code, HttpServletResponse response) 87 | throws IOException, URISyntaxException { 88 | Token token = getAccessToken(code); 89 | User user = getUser(token); 90 | 91 | if (user != null) { 92 | session.setAttribute("user", user); 93 | response.sendRedirect("/"); 94 | } else { 95 | response.setHeader("Location", ""); 96 | response.sendRedirect("/"); 97 | } 98 | } 99 | 100 | private Token getAccessToken(@NotNull String code) throws IOException { 101 | // Initialize client 102 | HttpClient httpClient = HttpClientBuilder.create().build(); 103 | HttpPost httpPost = new HttpPost(TOKEN_URL); 104 | 105 | // add request parameters 106 | List parameters = new ArrayList<>(); 107 | parameters.add(new BasicNameValuePair("code", code)); 108 | parameters.add(new BasicNameValuePair("client_id", CLIENT_ID)); 109 | parameters.add(new BasicNameValuePair("client_secret", CLIENT_SECRET)); 110 | parameters.add(new BasicNameValuePair("redirect_uri", REDIRECT_URI)); 111 | parameters.add(new BasicNameValuePair("grant_type", GRANT_TYPE)); 112 | httpPost.setEntity(new UrlEncodedFormEntity(parameters)); 113 | 114 | // send request 115 | org.apache.http.HttpResponse response = httpClient.execute(httpPost); 116 | int statusCode = response.getStatusLine().getStatusCode(); 117 | InputStream inputStream = response.getEntity().getContent(); 118 | 119 | if (HttpUtilities.success(statusCode)) 120 | return gson.fromJson(new InputStreamReader(inputStream), Token.class); 121 | 122 | throw new ApiException(HttpStatus.valueOf(statusCode)); 123 | } 124 | 125 | private User getUser(@NotNull Token token) throws IOException, URISyntaxException { 126 | 127 | URIBuilder builder = new URIBuilder(PROFILE_URL); 128 | builder.addParameter("access_token", token.getAccessToken()); 129 | 130 | HttpClient httpClient = HttpClientBuilder.create().build(); 131 | HttpGet httpGet = new HttpGet(builder.build()); 132 | org.apache.http.HttpResponse response = httpClient.execute(httpGet); 133 | int statusCode = response.getStatusLine().getStatusCode(); 134 | InputStream inputStream = response.getEntity().getContent(); 135 | 136 | if (HttpUtilities.success(statusCode)) { 137 | User user = gson.fromJson(new InputStreamReader(inputStream), User.class); 138 | user.setToken(token); 139 | return user; 140 | } 141 | 142 | throw new ApiException(HttpStatus.valueOf(statusCode)); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/controller/rest/oauth/SessionController.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.controller.rest.oauth; 2 | 3 | import io.swagger.annotations.Api; 4 | import io.swagger.annotations.ApiOperation; 5 | import io.swagger.annotations.ApiResponse; 6 | import io.swagger.annotations.ApiResponses; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.servlet.http.HttpServletResponse; 13 | import javax.servlet.http.HttpSession; 14 | import java.io.IOException; 15 | 16 | @RestController 17 | @RequestMapping("/api") 18 | @Api(description = "provides functionalities regarding sessions.", produces = "application/json", consumes = "application/json") 19 | public class SessionController { 20 | 21 | private final HttpSession session; 22 | 23 | @Autowired 24 | public SessionController(HttpSession session) { 25 | this.session = session; 26 | } 27 | 28 | @GetMapping("/checkSession") 29 | @ApiOperation(value = "utility to check session is alive or not") 30 | @ApiResponses({@ApiResponse(code = 200, message = "session is alive and you are authenticated"), 31 | @ApiResponse(code = 401, message = "your session might be expired. Please re-authenticate.")}) 32 | public void checkSession(HttpServletResponse response) throws IOException { 33 | if (session.getAttribute("user") != null) 34 | return; 35 | 36 | response.sendRedirect("/index.jsp"); 37 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/exception/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class ApiException extends RuntimeException { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | private HttpStatus status; 10 | 11 | public ApiException(HttpStatus status) { 12 | this(status, null, null); 13 | } 14 | 15 | public ApiException(HttpStatus status, String message) { 16 | this(status, message, null); 17 | } 18 | 19 | public ApiException(HttpStatus status, String message, Throwable cause) { 20 | super(message, cause); 21 | this.status = status; 22 | } 23 | 24 | public ApiException(HttpStatus status, Throwable cause) { 25 | this(status, null, cause); 26 | } 27 | 28 | public ApiException(HttpStatus status, String message, Throwable cause, boolean enableSuppression, 29 | boolean writableStackTrace) { 30 | super(message, cause, enableSuppression, writableStackTrace); 31 | this.status = status; 32 | } 33 | 34 | public HttpStatus getStatus() { 35 | return status; 36 | } 37 | 38 | public void setStatus(HttpStatus status) { 39 | this.status = status; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "ApiException{" + "status=" + status + '}'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/listener/ApplicationContextListener.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.listener; 2 | 3 | import com.github.dhaval_mehta.savetogoogledrive.uploader.UploadManager; 4 | 5 | import javax.servlet.ServletContextEvent; 6 | import javax.servlet.ServletContextListener; 7 | 8 | public class ApplicationContextListener implements ServletContextListener { 9 | @Override 10 | public void contextInitialized(ServletContextEvent servletContextEvent) { 11 | 12 | } 13 | 14 | @Override 15 | public void contextDestroyed(ServletContextEvent servletContextEvent) { 16 | UploadManager.getUploadManager().distroy(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/listener/SessionListener.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.listener; 2 | 3 | import javax.servlet.annotation.WebListener; 4 | import javax.servlet.http.HttpSessionEvent; 5 | import javax.servlet.http.HttpSessionListener; 6 | import java.util.ArrayList; 7 | 8 | @WebListener 9 | public class SessionListener implements HttpSessionListener { 10 | @Override 11 | public void sessionCreated(HttpSessionEvent httpSessionEvent) { 12 | httpSessionEvent.getSession().setAttribute("uploads", new ArrayList()); 13 | } 14 | 15 | @Override 16 | public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/model/ApiError.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.model; 2 | 3 | import org.springframework.http.HttpStatus; 4 | 5 | public class ApiError { 6 | 7 | int statusCode; 8 | String meaning; 9 | String message; 10 | String description; 11 | 12 | public ApiError(HttpStatus httpStatus, String message, String description) { 13 | statusCode = httpStatus.value(); 14 | meaning = httpStatus.getReasonPhrase(); 15 | this.message = message; 16 | this.description = description; 17 | } 18 | 19 | public ApiError(int statusCode, String meaning, String message, String description) { 20 | this.statusCode = statusCode; 21 | this.meaning = meaning; 22 | this.message = message; 23 | this.description = description; 24 | } 25 | 26 | public int getStatusCode() { 27 | return statusCode; 28 | } 29 | 30 | public String getMessage() { 31 | return message; 32 | } 33 | 34 | public String getDescription() { 35 | return description; 36 | } 37 | 38 | public HttpStatus getStatus() { 39 | return HttpStatus.valueOf(statusCode); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/model/DownloadFileInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.model; 2 | 3 | import java.net.URL; 4 | 5 | public class DownloadFileInfo { 6 | 7 | private URL uploadUrl; 8 | private String fileName; 9 | private String contentType; 10 | private long contentLength; 11 | private boolean resumeSupported; 12 | 13 | public URL getUploadUrl() { 14 | return uploadUrl; 15 | } 16 | 17 | public void setUploadUrl(URL uploadUrl) { 18 | this.uploadUrl = uploadUrl; 19 | } 20 | 21 | public String getFileName() { 22 | return fileName; 23 | } 24 | 25 | public void setFileName(String fileName) { 26 | this.fileName = fileName; 27 | } 28 | 29 | public String getContentType() { 30 | return contentType; 31 | } 32 | 33 | public void setContentType(String contentType) { 34 | this.contentType = contentType; 35 | } 36 | 37 | public long getContentLength() { 38 | return contentLength; 39 | } 40 | 41 | public void setContentLength(long contentLength) { 42 | this.contentLength = contentLength; 43 | } 44 | 45 | public boolean isResumeSupported() { 46 | return resumeSupported; 47 | } 48 | 49 | public void setResumeSupported(boolean resumeSupported) { 50 | this.resumeSupported = resumeSupported; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/model/Token.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.dhaval_mehta.savetogoogledrive.model; 7 | 8 | import java.time.Instant; 9 | 10 | /** 11 | * @author Dhaval 12 | */ 13 | public class Token { 14 | 15 | private String accessToken; 16 | private String tokenType; 17 | private Integer expiresIn; 18 | private String refreshToken; 19 | private String idToken; 20 | private long tokenUpdatedAt; 21 | 22 | public Token() { 23 | tokenUpdatedAt = Instant.EPOCH.getEpochSecond(); 24 | } 25 | 26 | public String getAccessToken() { 27 | return accessToken; 28 | } 29 | 30 | public void setAccessToken(String accessToken) { 31 | this.accessToken = accessToken; 32 | } 33 | 34 | public String getTokenType() { 35 | return tokenType; 36 | } 37 | 38 | public void setTokenType(String tokenType) { 39 | this.tokenType = tokenType; 40 | } 41 | 42 | public Integer getExpiresIn() { 43 | return expiresIn; 44 | } 45 | 46 | public void setExpiresIn(Integer expiresIn) { 47 | this.expiresIn = expiresIn; 48 | } 49 | 50 | public String getRefreshToken() { 51 | return refreshToken; 52 | } 53 | 54 | public void setRefreshToken(String refreshToken) { 55 | this.refreshToken = refreshToken; 56 | } 57 | 58 | public String getIdToken() { 59 | return idToken; 60 | } 61 | 62 | public void setIdToken(String idToken) { 63 | this.idToken = idToken; 64 | } 65 | 66 | public long getTokenUpdatedAt() { 67 | return tokenUpdatedAt; 68 | } 69 | 70 | public void setTokenUpdatedAt(long tokenUpdatedAt) { 71 | this.tokenUpdatedAt = tokenUpdatedAt; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/model/UploadInformation.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.model; 2 | 3 | public class UploadInformation { 4 | 5 | private String url; 6 | private String fileName; 7 | private long totalSize; 8 | private long uploadedSize; 9 | private String errorMessage; 10 | private UploadStatus uploadStatus; 11 | private double speed; 12 | 13 | public String getUrl() { 14 | return url; 15 | } 16 | 17 | public void setUrl(String url) { 18 | this.url = url; 19 | } 20 | 21 | public String getFileName() { 22 | return fileName; 23 | } 24 | 25 | public void setFileName(String fileName) { 26 | this.fileName = fileName; 27 | } 28 | 29 | public long getUploadedSize() { 30 | return uploadedSize; 31 | } 32 | 33 | public void setUploadedSize(long uploadedSize) { 34 | this.uploadedSize = uploadedSize; 35 | } 36 | 37 | public long getTotalSize() { 38 | return totalSize; 39 | } 40 | 41 | public void setTotalSize(long totalSize) { 42 | this.totalSize = totalSize; 43 | } 44 | 45 | public UploadStatus getUploadStatus() { 46 | return uploadStatus; 47 | } 48 | 49 | public void setUploadStatus(UploadStatus uploadStatus) { 50 | this.uploadStatus = uploadStatus; 51 | } 52 | 53 | public double getSpeed() { 54 | return speed; 55 | } 56 | 57 | public void setSpeed(double speed) { 58 | this.speed = speed; 59 | } 60 | 61 | public String getErrorMessage() { 62 | return errorMessage; 63 | } 64 | 65 | public void setErrorMessage(String errorMessage) { 66 | this.errorMessage = errorMessage; 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/model/UploadStatus.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.model; 2 | 3 | public enum UploadStatus { 4 | waiting, uploading, failed, completed 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/model/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package com.github.dhaval_mehta.savetogoogledrive.model; 7 | 8 | import com.google.gson.FieldNamingPolicy; 9 | import com.google.gson.Gson; 10 | import com.google.gson.GsonBuilder; 11 | import com.google.gson.annotations.SerializedName; 12 | import org.apache.http.NameValuePair; 13 | import org.apache.http.client.HttpClient; 14 | import org.apache.http.client.entity.UrlEncodedFormEntity; 15 | import org.apache.http.client.methods.HttpPost; 16 | import org.apache.http.impl.client.HttpClientBuilder; 17 | import org.apache.http.message.BasicNameValuePair; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStreamReader; 21 | import java.time.Instant; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.logging.Level; 25 | import java.util.logging.Logger; 26 | 27 | /** 28 | * @author Dhaval 29 | */ 30 | public class User { 31 | 32 | @SerializedName("given_name") 33 | private String name; 34 | 35 | @SerializedName("picture") 36 | private String profilePhotoUrl; 37 | 38 | private String email; 39 | private Token token; 40 | private long tokenUpdatedAt; 41 | 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | public void setName(String name) { 47 | this.name = name; 48 | } 49 | 50 | public String getProfilePhotoUrl() { 51 | return profilePhotoUrl; 52 | } 53 | 54 | public void setProfilePhotoUrl(String profilePhotoUrl) { 55 | this.profilePhotoUrl = profilePhotoUrl; 56 | } 57 | 58 | public String getEmail() { 59 | return email; 60 | } 61 | 62 | public void setEmail(String email) { 63 | this.email = email; 64 | } 65 | 66 | public Token getToken() { 67 | return token; 68 | } 69 | 70 | public void setToken(Token token) { 71 | this.token = token; 72 | } 73 | 74 | public long getTokenUpdatedAt() { 75 | return tokenUpdatedAt; 76 | } 77 | 78 | public void setTokenUpdatedAt(long tokenUpdatedAt) { 79 | this.tokenUpdatedAt = tokenUpdatedAt; 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | return "User{" + "name=" + name + ", email=" + email + ", profilePhotoUrl=" + profilePhotoUrl + ", token=" + token + ", tokenUpdatedAt=" + tokenUpdatedAt + '}'; 85 | } 86 | 87 | public synchronized void refreshTokenIfNecessary() { 88 | long elapsedTime = Instant.EPOCH.getEpochSecond() - token.getTokenUpdatedAt(); 89 | 90 | if (token.getExpiresIn() - elapsedTime < 100) 91 | refreshToken(); 92 | } 93 | 94 | private void refreshToken() { 95 | try { 96 | String refreshToken = token.getRefreshToken(); 97 | 98 | String userUrl = "https://www.googleapis.com/oauth2/v4/token"; 99 | HttpClient httpClient = HttpClientBuilder.create().build(); 100 | HttpPost httpPost = new HttpPost(userUrl); 101 | 102 | List parameters = new ArrayList<>(); 103 | parameters.add(new BasicNameValuePair("client_id", System.getenv("client_id"))); 104 | parameters.add(new BasicNameValuePair("client_secret", System.getenv("client_secret"))); 105 | parameters.add(new BasicNameValuePair("grant_type", "refresh_token")); 106 | parameters.add(new BasicNameValuePair("refresh_token", refreshToken)); 107 | httpPost.setEntity(new UrlEncodedFormEntity(parameters)); 108 | 109 | org.apache.http.HttpResponse response = httpClient.execute(httpPost); 110 | int httpStatusCode = response.getStatusLine().getStatusCode(); 111 | InputStreamReader reader = new InputStreamReader(response.getEntity().getContent()); 112 | 113 | if (httpStatusCode / 100 == 2) { 114 | Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); 115 | token = gson.fromJson(reader, Token.class); 116 | token.setRefreshToken(refreshToken); 117 | tokenUpdatedAt = Instant.EPOCH.getEpochSecond(); 118 | } 119 | } catch (IOException ex) { 120 | Logger.getLogger(User.class.getName()).log(Level.SEVERE, null, ex); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/UploadManager.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader; 2 | 3 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadInformation; 4 | 5 | import javax.validation.constraints.NotNull; 6 | import java.util.HashMap; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | /** 11 | * @author Dhaval 12 | */ 13 | public class UploadManager { 14 | 15 | private static final UploadManager UPLOAD_MANAGER; 16 | 17 | static { 18 | UPLOAD_MANAGER = new UploadManager(); 19 | } 20 | 21 | private final ExecutorService executorService; 22 | private final HashMap idToTaskMap; 23 | 24 | private UploadManager() { 25 | executorService = Executors.newFixedThreadPool(10); 26 | idToTaskMap = new HashMap<>(); 27 | } 28 | 29 | public static UploadManager getUploadManager() { 30 | return UPLOAD_MANAGER; 31 | } 32 | 33 | public UploadInformation getUploadInformation(@NotNull String id) { 34 | UploadTask uploadTask = idToTaskMap.get(id); 35 | 36 | if (uploadTask == null) 37 | return null; 38 | 39 | return uploadTask.getUploader().getUploadInformation(); 40 | } 41 | 42 | public void add(@NotNull UploadTask uploadTask) { 43 | idToTaskMap.put(uploadTask.getId(), uploadTask); 44 | executorService.execute(uploadTask); 45 | } 46 | 47 | public void distroy() { 48 | executorService.shutdown(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/UploadTask.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader; 2 | 3 | import java.math.BigInteger; 4 | import java.security.SecureRandom; 5 | 6 | public class UploadTask implements Runnable { 7 | 8 | private static final SecureRandom random = new SecureRandom(); 9 | private String id; 10 | private Uploader uploader; 11 | 12 | public UploadTask(Uploader uploader) { 13 | id = generateRandomId() + Integer.toString(hashCode(), 32); 14 | this.uploader = uploader; 15 | } 16 | 17 | private static synchronized String generateRandomId() { 18 | return new BigInteger(130, random).toString(32) + new BigInteger(String.valueOf(System.currentTimeMillis())).toString(32); 19 | } 20 | 21 | @Override 22 | public void run() { 23 | uploader.upload(); 24 | } 25 | 26 | public String getId() { 27 | return id; 28 | } 29 | 30 | public Uploader getUploader() { 31 | return uploader; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/Uploader.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader; 2 | 3 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadInformation; 4 | 5 | public interface Uploader { 6 | void upload(); 7 | 8 | UploadInformation getUploadInformation(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/drive/DriveUploader.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader.drive; 2 | 3 | import static com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities.USER_AGENT; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.io.IOException; 7 | import java.io.PrintStream; 8 | import java.net.HttpURLConnection; 9 | import java.net.MalformedURLException; 10 | import java.net.URL; 11 | 12 | import javax.validation.constraints.NotNull; 13 | 14 | import org.apache.commons.io.IOUtils; 15 | import org.springframework.http.HttpStatus; 16 | 17 | import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; 18 | import com.github.dhaval_mehta.savetogoogledrive.model.DownloadFileInfo; 19 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadInformation; 20 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadStatus; 21 | import com.github.dhaval_mehta.savetogoogledrive.model.User; 22 | import com.github.dhaval_mehta.savetogoogledrive.uploader.Uploader; 23 | import com.google.gson.JsonObject; 24 | 25 | abstract class DriveUploader implements Uploader { 26 | private static final URL CREATE_FILE_URL; 27 | 28 | static { 29 | try { 30 | CREATE_FILE_URL = new URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable"); 31 | } catch (MalformedURLException ex) { 32 | throw new ExceptionInInitializerError(ex); 33 | } 34 | } 35 | 36 | final int chunkSize; 37 | protected User user; 38 | DownloadFileInfo downloadFileInfo; 39 | UploadInformation uploadInformation; 40 | private URL createdFileUrl; 41 | 42 | DriveUploader(DownloadFileInfo downloadFileInfo, User user) { 43 | this(); 44 | this.user = user; 45 | this.downloadFileInfo = downloadFileInfo; 46 | uploadInformation.setFileName(downloadFileInfo.getFileName()); 47 | uploadInformation.setUrl(downloadFileInfo.getUploadUrl().toString()); 48 | uploadInformation.setTotalSize(downloadFileInfo.getContentLength()); 49 | 50 | try { 51 | obtainUploadUrl(); 52 | } catch (Exception e) { 53 | uploadInformation.setUploadStatus(UploadStatus.failed); 54 | uploadInformation.setErrorMessage(e.getMessage()); 55 | } 56 | } 57 | 58 | private DriveUploader() { 59 | chunkSize = 1024 * 1024 * 50; // 50 MB 60 | uploadInformation = new UploadInformation(); 61 | uploadInformation.setUploadStatus(UploadStatus.waiting); 62 | 63 | } 64 | 65 | /** 66 | * It will upload bytes in range [start,end] to Google drive. 67 | * 68 | * @param start 69 | * starting byte of range 70 | * @param end 71 | * ending byte of range 72 | */ 73 | void uploadPartially(@NotNull byte[] buffer, long start, long end) { 74 | String contentRange = "bytes " + start + "-" + end + "/" + downloadFileInfo.getContentLength(); 75 | 76 | int statusCode; 77 | try { 78 | HttpURLConnection uploadConnection = (HttpURLConnection) createdFileUrl.openConnection(); 79 | uploadConnection.setDoOutput(true); 80 | uploadConnection.setRequestProperty("User-Agent", USER_AGENT); 81 | uploadConnection.setRequestProperty("Content-Range", contentRange); 82 | IOUtils.copy(new ByteArrayInputStream(buffer), uploadConnection.getOutputStream()); 83 | uploadConnection.connect(); 84 | statusCode = uploadConnection.getResponseCode(); 85 | } catch (IOException e) { 86 | throw new RuntimeException("Error While uploading file.", e); 87 | } 88 | 89 | // In case of successful upload, status code will be 3** or 2** 90 | if (statusCode < 400) 91 | uploadInformation.setUploadedSize(end + 1); 92 | else if (statusCode == 403) { 93 | throw new RuntimeException( 94 | "Google didn't allow us to create file. Your drive might not have enough space."); 95 | } 96 | } 97 | 98 | private void obtainUploadUrl() throws IOException { 99 | user.refreshTokenIfNecessary(); 100 | HttpURLConnection connection = (HttpURLConnection) CREATE_FILE_URL.openConnection(); 101 | connection.setRequestMethod("POST"); 102 | connection.setRequestProperty("User-Agent", USER_AGENT); 103 | JsonObject jsonObject = new JsonObject(); 104 | jsonObject.addProperty("name", downloadFileInfo.getFileName()); 105 | String postBody = jsonObject.toString(); 106 | 107 | connection.setDoOutput(true); 108 | connection.setRequestProperty("Content-Type", "application/json"); 109 | connection.setRequestProperty("Authorization", 110 | user.getToken().getTokenType() + " " + user.getToken().getAccessToken()); 111 | connection.setRequestProperty("X-Upload-Content-Type", downloadFileInfo.getContentType()); 112 | connection.setRequestProperty("X-Upload-Content-Length", String.valueOf(downloadFileInfo.getContentLength())); 113 | 114 | try (PrintStream writer = new PrintStream(connection.getOutputStream())) { 115 | writer.print(postBody); 116 | } 117 | 118 | connection.connect(); 119 | 120 | int statusCode = connection.getResponseCode(); 121 | 122 | if (statusCode == HttpStatus.OK.value()) 123 | createdFileUrl = new URL(connection.getHeaderField("Location")); 124 | else if (statusCode == HttpStatus.UNAUTHORIZED.value()) 125 | throw new ApiException(HttpStatus.UNAUTHORIZED, "Your session is expired"); 126 | else 127 | throw new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, "Cannot create new file in google dirve."); 128 | 129 | } 130 | 131 | @Override 132 | public UploadInformation getUploadInformation() { 133 | return uploadInformation; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/drive/DriveUploaderBuilder.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader.drive; 2 | 3 | import static com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities.USER_AGENT; 4 | 5 | import java.io.IOException; 6 | import java.io.UnsupportedEncodingException; 7 | import java.net.HttpURLConnection; 8 | import java.net.URL; 9 | import java.net.URLDecoder; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | import org.apache.commons.io.FilenameUtils; 14 | import org.springframework.http.HttpStatus; 15 | 16 | import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; 17 | import com.github.dhaval_mehta.savetogoogledrive.model.DownloadFileInfo; 18 | import com.github.dhaval_mehta.savetogoogledrive.model.User; 19 | import com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities; 20 | 21 | public class DriveUploaderBuilder { 22 | 23 | private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern 24 | .compile("(?i)filename[^;\\n=]*=(?:utf-8'')?[\"]?([^;\"]*)"); 25 | 26 | private User user; 27 | private DownloadFileInfo downloadFileInfo; 28 | 29 | public DriveUploaderBuilder() { 30 | downloadFileInfo = new DownloadFileInfo(); 31 | } 32 | 33 | public DriveUploaderBuilder setFileName(String fileName) { 34 | downloadFileInfo.setFileName(fileName); 35 | return this; 36 | } 37 | 38 | public DriveUploaderBuilder setUser(User user) { 39 | this.user = user; 40 | return this; 41 | } 42 | 43 | public DriveUploaderBuilder setUploadUrl(URL uploadUrl) { 44 | downloadFileInfo.setUploadUrl(uploadUrl); 45 | return this; 46 | } 47 | 48 | public DriveUploader build() throws IOException { 49 | assert user != null; 50 | assert downloadFileInfo.getUploadUrl() != null; 51 | fetchFileInformation(); 52 | 53 | if (downloadFileInfo.isResumeSupported()) 54 | return new ResumableDriveUploader(downloadFileInfo, user); 55 | else 56 | return new NonResumableDriveUploader(downloadFileInfo, user); 57 | } 58 | 59 | private void fetchFileInformation() throws IOException { 60 | assert downloadFileInfo.getUploadUrl() != null; 61 | HttpURLConnection connection = (HttpURLConnection) downloadFileInfo.getUploadUrl().openConnection(); 62 | connection.setRequestProperty("User-Agent", USER_AGENT); 63 | connection.setRequestMethod("HEAD"); 64 | 65 | connection.connect(); 66 | 67 | int statusCode = connection.getResponseCode(); 68 | 69 | if (!HttpUtilities.success(statusCode)) 70 | throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, "Server can not download file"); 71 | 72 | String mimeType = connection.getContentType().toLowerCase(); 73 | 74 | if (mimeType.contains("application/xml") || mimeType.contains("text/xml") 75 | || mimeType.contains("application/json") || mimeType.contains("text/html")) 76 | throw new ApiException(HttpStatus.UNPROCESSABLE_ENTITY, "Given URL does not point to a file."); 77 | 78 | downloadFileInfo.setContentLength(connection.getContentLengthLong()); 79 | downloadFileInfo.setContentType(connection.getContentType()); 80 | 81 | String acceptRangeHeader = connection.getHeaderField("Accept-Ranges"); 82 | 83 | if (acceptRangeHeader != null) 84 | downloadFileInfo.setResumeSupported(acceptRangeHeader.startsWith("bytes")); 85 | else 86 | downloadFileInfo.setResumeSupported(checkResumeSupportUsingGetMethod()); 87 | 88 | if (downloadFileInfo.getFileName() == null || downloadFileInfo.getFileName().equals("")) { 89 | String contentDisposition = connection.getHeaderField("Content-Disposition"); 90 | downloadFileInfo.setFileName(findFileName(contentDisposition)); 91 | } 92 | } 93 | 94 | private String findFileName(String contentDisposition) { 95 | 96 | if (contentDisposition != null) 97 | try { 98 | Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition); 99 | 100 | if (matcher.find()) { 101 | String match = matcher.group(1); 102 | 103 | if (match != null && !match.equals("")) { 104 | match = URLDecoder.decode(match, "UTF-8"); 105 | return match; 106 | } 107 | } 108 | 109 | } catch (IllegalStateException ex) { 110 | // cannot find filename using content disposition http header. 111 | // Use url to find filename; 112 | } catch (UnsupportedEncodingException e) { 113 | e.printStackTrace(); 114 | } 115 | return FilenameUtils.getName(downloadFileInfo.getUploadUrl().getPath()); 116 | } 117 | 118 | private boolean checkResumeSupportUsingGetMethod() throws IOException { 119 | HttpURLConnection connection = (HttpURLConnection) downloadFileInfo.getUploadUrl().openConnection(); 120 | connection.setRequestMethod("GET"); 121 | String rangeHeaderValue = "bytes=" + 0 + "-" + 0; 122 | connection.setRequestProperty("Range", rangeHeaderValue); 123 | connection.setRequestProperty("User-Agent", USER_AGENT); 124 | return HttpStatus.PARTIAL_CONTENT.value() == connection.getResponseCode(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/drive/NonResumableDriveUploader.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader.drive; 2 | 3 | import com.github.dhaval_mehta.savetogoogledrive.exception.ApiException; 4 | import com.github.dhaval_mehta.savetogoogledrive.model.DownloadFileInfo; 5 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadStatus; 6 | import com.github.dhaval_mehta.savetogoogledrive.model.User; 7 | import org.springframework.http.HttpStatus; 8 | 9 | import java.io.*; 10 | import java.math.BigInteger; 11 | import java.nio.channels.Channels; 12 | import java.nio.channels.ReadableByteChannel; 13 | import java.security.SecureRandom; 14 | import java.util.logging.Level; 15 | import java.util.logging.Logger; 16 | 17 | public class NonResumableDriveUploader extends DriveUploader { 18 | private static final SecureRandom random = new SecureRandom(); 19 | private File downloadedFile; 20 | 21 | NonResumableDriveUploader(DownloadFileInfo downloadFileInfo, User user) { 22 | super(downloadFileInfo, user); 23 | } 24 | 25 | private static synchronized String generateRandomString() { 26 | return new BigInteger(130, random).toString(32) + new BigInteger(String.valueOf(System.currentTimeMillis())).toString(32); 27 | } 28 | 29 | @Override 30 | public void upload() { 31 | try { 32 | downloadFile(); 33 | } catch (IOException e) { 34 | Logger.getLogger(NonResumableDriveUploader.class.getName()).log(Level.SEVERE, null, e); 35 | uploadInformation.setUploadStatus(UploadStatus.failed); 36 | uploadInformation.setErrorMessage("File not found."); 37 | return; 38 | } 39 | try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(downloadedFile))) { 40 | byte buffer[] = new byte[chunkSize]; 41 | int n; 42 | 43 | uploadInformation.setUploadStatus(UploadStatus.uploading); 44 | 45 | while ((n = bufferedInputStream.read(buffer)) > 0) { 46 | long start = uploadInformation.getUploadedSize(); 47 | long startTime = System.currentTimeMillis(); 48 | uploadPartially(buffer, start, start + n - 1); 49 | long endTime = System.currentTimeMillis(); 50 | double timeElapsed = (endTime - startTime) / 1000.0; 51 | uploadInformation.setSpeed(n / timeElapsed); 52 | uploadInformation.setUploadedSize(start + n); 53 | } 54 | 55 | uploadInformation.setUploadStatus(UploadStatus.completed); 56 | } catch (IOException e) { 57 | uploadInformation.setUploadStatus(UploadStatus.failed); 58 | uploadInformation.setErrorMessage(e.getMessage()); 59 | e.printStackTrace(); 60 | } 61 | downloadedFile.delete(); 62 | downloadedFile.getParentFile().delete(); 63 | } 64 | 65 | private void downloadFile() throws IOException { 66 | String random = generateRandomString(); 67 | File downloadDirectory = new File(System.getenv("TEMP_STROAGE"), random); 68 | boolean created = downloadDirectory.mkdir(); 69 | 70 | if (!created) { 71 | uploadInformation.setUploadStatus(UploadStatus.failed); 72 | uploadInformation.setErrorMessage("Can not create download directory."); 73 | throw new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, "Can not create download directory."); 74 | } 75 | 76 | downloadedFile = new File(downloadDirectory, downloadFileInfo.getFileName()); 77 | ReadableByteChannel rbc = Channels.newChannel(downloadFileInfo.getUploadUrl().openStream()); 78 | FileOutputStream fos = new FileOutputStream(downloadedFile); 79 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 80 | fos.close(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/uploader/drive/ResumableDriveUploader.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.uploader.drive; 2 | 3 | import static com.github.dhaval_mehta.savetogoogledrive.utility.HttpUtilities.USER_AGENT; 4 | 5 | import java.io.InputStream; 6 | import java.net.HttpURLConnection; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | import org.apache.commons.io.IOUtils; 11 | 12 | import com.github.dhaval_mehta.savetogoogledrive.model.DownloadFileInfo; 13 | import com.github.dhaval_mehta.savetogoogledrive.model.UploadStatus; 14 | import com.github.dhaval_mehta.savetogoogledrive.model.User; 15 | 16 | public class ResumableDriveUploader extends DriveUploader { 17 | 18 | private static final Logger LOGGER = Logger.getLogger(ResumableDriveUploader.class.getName()); 19 | 20 | ResumableDriveUploader(DownloadFileInfo downloadFileInfo, User user) { 21 | super(downloadFileInfo, user); 22 | } 23 | 24 | private byte[] downloadPartially(long start, long end) { 25 | try { 26 | HttpURLConnection downloadConnection = (HttpURLConnection) downloadFileInfo.getUploadUrl().openConnection(); 27 | 28 | downloadConnection.setRequestMethod("GET"); 29 | String rangeHeaderValue = "bytes=" + start + "-" + end; 30 | downloadConnection.setRequestProperty("Range", rangeHeaderValue); 31 | downloadConnection.setRequestProperty("User-Agent", USER_AGENT); 32 | InputStream in = downloadConnection.getInputStream(); 33 | return IOUtils.toByteArray(in); 34 | } catch (Exception e) { 35 | throw new RuntimeException("Can not download file", e); 36 | } 37 | } 38 | 39 | /** 40 | * This function will upload file to Google drive using resumable upload method. 41 | */ 42 | @Override 43 | public void upload() { 44 | try { 45 | uploadInformation.setUploadStatus(UploadStatus.uploading); 46 | 47 | while (uploadInformation.getUploadedSize() < downloadFileInfo.getContentLength()) { 48 | long start = uploadInformation.getUploadedSize(); 49 | long end = uploadInformation.getUploadedSize() + chunkSize - 1; 50 | 51 | if (end >= downloadFileInfo.getContentLength()) 52 | end = downloadFileInfo.getContentLength() - 1; 53 | 54 | long startTime = System.currentTimeMillis(); 55 | byte[] buffer = downloadPartially(start, end); 56 | uploadPartially(buffer, start, end); 57 | long endTime = System.currentTimeMillis(); 58 | 59 | uploadInformation.setUploadedSize(end + 1); 60 | double dataFetched = end - start + 1; 61 | double timeElapsed = (endTime - startTime) / 1000.0; 62 | uploadInformation.setSpeed(dataFetched / timeElapsed); 63 | } 64 | 65 | uploadInformation.setUploadStatus(UploadStatus.completed); 66 | 67 | } catch (Throwable e) { 68 | handleError(e); 69 | } 70 | } 71 | 72 | private void handleError(Throwable e) { 73 | uploadInformation.setUploadStatus(UploadStatus.failed); 74 | uploadInformation.setErrorMessage(e.getMessage()); 75 | LOGGER.log(Level.SEVERE, null, e); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/utility/HttpUtilities.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.utility; 2 | 3 | public class HttpUtilities { 4 | 5 | public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"; 6 | 7 | public static boolean success(int httpStatusCode) { 8 | return httpStatusCode % 2 == 0; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/dhaval_mehta/savetogoogledrive/utility/IOUtilities.java: -------------------------------------------------------------------------------- 1 | package com.github.dhaval_mehta.savetogoogledrive.utility; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | 8 | public class IOUtilities { 9 | public static String inputStreamToString(InputStream inputStream) throws IOException { 10 | assert inputStream != null; 11 | 12 | BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); 13 | StringBuilder sb = new StringBuilder(); 14 | String line; 15 | 16 | while ((line = br.readLine()) != null) 17 | sb.append(line); 18 | 19 | return sb.toString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=80 2 | -------------------------------------------------------------------------------- /src/main/resources/static/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} 5 | -------------------------------------------------------------------------------- /src/main/resources/static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/main/resources/static/img/guest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/img/guest.png -------------------------------------------------------------------------------- /src/main/resources/static/img/savetodrive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PBhadoo/gdupload/6ca0b9f9444da88ace1f385f1188d258dbc795b0/src/main/resources/static/img/savetodrive.png -------------------------------------------------------------------------------- /src/main/resources/static/js/front.js: -------------------------------------------------------------------------------- 1 | /*global $, document, Chart, LINECHART, data, options, window*/ 2 | $(document).ready(function () { 3 | 4 | 'use strict'; 5 | 6 | // ------------------------------------------------------- // 7 | // For demo purposes only 8 | // ------------------------------------------------------ // 9 | 10 | if ($.cookie("theme_csspath")) { 11 | $('link#theme-stylesheet').attr("href", $.cookie("theme_csspath")); 12 | } 13 | 14 | $("#colour").change(function () { 15 | 16 | if ($(this).val() !== '') { 17 | 18 | var theme_csspath = 'css/style.' + $(this).val() + '.css'; 19 | 20 | $('link#theme-stylesheet').attr("href", theme_csspath); 21 | 22 | $.cookie("theme_csspath", theme_csspath, { expires: 365, path: document.URL.substr(0, document.URL.lastIndexOf('/')) }); 23 | } 24 | 25 | return false; 26 | }); 27 | 28 | // ------------------------------------------------------- // 29 | // Search Box 30 | // ------------------------------------------------------ // 31 | $('#search').on('click', function (e) { 32 | e.preventDefault(); 33 | $('.search-box').fadeIn(); 34 | }); 35 | $('.dismiss').on('click', function () { 36 | $('.search-box').fadeOut(); 37 | }); 38 | 39 | // ------------------------------------------------------- // 40 | // Card Close 41 | // ------------------------------------------------------ // 42 | $('.card-close a.remove').on('click', function (e) { 43 | e.preventDefault(); 44 | $(this).parents('.card').fadeOut(); 45 | }); 46 | 47 | 48 | // ------------------------------------------------------- // 49 | // Adding fade effect to dropdowns 50 | // ------------------------------------------------------ // 51 | $('.dropdown').on('show.bs.dropdown', function () { 52 | $(this).find('.dropdown-menu').first().stop(true, true).fadeIn(); 53 | }); 54 | $('.dropdown').on('hide.bs.dropdown', function () { 55 | $(this).find('.dropdown-menu').first().stop(true, true).fadeOut(); 56 | }); 57 | 58 | 59 | // ------------------------------------------------------- // 60 | // Login form validation 61 | // ------------------------------------------------------ // 62 | $('#login-form').validate({ 63 | messages: { 64 | loginUsername: 'please enter your username', 65 | loginPassword: 'please enter your password' 66 | } 67 | }); 68 | 69 | // ------------------------------------------------------- // 70 | // Register form validation 71 | // ------------------------------------------------------ // 72 | $('#register-form').validate({ 73 | messages: { 74 | registerUsername: 'please enter your first name', 75 | registerEmail: 'please enter a vaild Email Address', 76 | registerPassword: 'please enter your password' 77 | } 78 | }); 79 | 80 | // ------------------------------------------------------- // 81 | // Sidebar Functionality 82 | // ------------------------------------------------------ // 83 | $('#toggle-btn').on('click', function (e) { 84 | e.preventDefault(); 85 | $(this).toggleClass('active'); 86 | 87 | $('.side-navbar').toggleClass('shrinked'); 88 | $('.content-inner').toggleClass('active'); 89 | 90 | if ($(window).outerWidth() > 1183) { 91 | if ($('#toggle-btn').hasClass('active')) { 92 | $('.navbar-header .brand-small').hide(); 93 | $('.navbar-header .brand-big').show(); 94 | } else { 95 | $('.navbar-header .brand-small').show(); 96 | $('.navbar-header .brand-big').hide(); 97 | } 98 | } 99 | 100 | if ($(window).outerWidth() < 1183) { 101 | $('.navbar-header .brand-small').show(); 102 | } 103 | 104 | adjust_footer(); 105 | }); 106 | 107 | // ------------------------------------------------------- // 108 | // Transition Placeholders 109 | // ------------------------------------------------------ // 110 | $('input.input-material').on('focus', function () { 111 | $(this).siblings('.label-material').addClass('active'); 112 | }); 113 | 114 | $('input.input-material').on('blur', function () { 115 | $(this).siblings('.label-material').removeClass('active'); 116 | 117 | if ($(this).val() !== '') { 118 | $(this).siblings('.label-material').addClass('active'); 119 | } else { 120 | $(this).siblings('.label-material').removeClass('active'); 121 | } 122 | }); 123 | 124 | // ------------------------------------------------------- // 125 | // External links to new window 126 | // ------------------------------------------------------ // 127 | $('.external').on('click', function (e) { 128 | 129 | e.preventDefault(); 130 | window.open($(this).attr("href")); 131 | }); 132 | 133 | }); 134 | -------------------------------------------------------------------------------- /src/main/resources/static/js/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.4.1 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2013 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | (function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD 11 | define(['jquery'], factory); 12 | } else if (typeof exports === 'object') { 13 | // CommonJS 14 | factory(require('jquery')); 15 | } else { 16 | // Browser globals 17 | factory(jQuery); 18 | } 19 | }(function ($) { 20 | 21 | var pluses = /\+/g; 22 | 23 | function encode(s) { 24 | return config.raw ? s : encodeURIComponent(s); 25 | } 26 | 27 | function decode(s) { 28 | return config.raw ? s : decodeURIComponent(s); 29 | } 30 | 31 | function stringifyCookieValue(value) { 32 | return encode(config.json ? JSON.stringify(value) : String(value)); 33 | } 34 | 35 | function parseCookieValue(s) { 36 | if (s.indexOf('"') === 0) { 37 | // This is a quoted cookie as according to RFC2068, unescape... 38 | s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); 39 | } 40 | 41 | try { 42 | // Replace server-side written pluses with spaces. 43 | // If we can't decode the cookie, ignore it, it's unusable. 44 | // If we can't parse the cookie, ignore it, it's unusable. 45 | s = decodeURIComponent(s.replace(pluses, ' ')); 46 | return config.json ? JSON.parse(s) : s; 47 | } catch(e) {} 48 | } 49 | 50 | function read(s, converter) { 51 | var value = config.raw ? s : parseCookieValue(s); 52 | return $.isFunction(converter) ? converter(value) : value; 53 | } 54 | 55 | var config = $.cookie = function (key, value, options) { 56 | 57 | // Write 58 | 59 | if (value !== undefined && !$.isFunction(value)) { 60 | options = $.extend({}, config.defaults, options); 61 | 62 | if (typeof options.expires === 'number') { 63 | var days = options.expires, t = options.expires = new Date(); 64 | t.setTime(+t + days * 864e+5); 65 | } 66 | 67 | return (document.cookie = [ 68 | encode(key), '=', stringifyCookieValue(value), 69 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 70 | options.path ? '; path=' + options.path : '', 71 | options.domain ? '; domain=' + options.domain : '', 72 | options.secure ? '; secure' : '' 73 | ].join('')); 74 | } 75 | 76 | // Read 77 | 78 | var result = key ? undefined : {}; 79 | 80 | // To prevent the for loop in the first place assign an empty array 81 | // in case there are no cookies at all. Also prevents odd result when 82 | // calling $.cookie(). 83 | var cookies = document.cookie ? document.cookie.split('; ') : []; 84 | 85 | for (var i = 0, l = cookies.length; i < l; i++) { 86 | var parts = cookies[i].split('='); 87 | var name = decode(parts.shift()); 88 | var cookie = parts.join('='); 89 | 90 | if (key && key === name) { 91 | // If second argument (value) is a function it's a converter... 92 | result = read(cookie, value); 93 | break; 94 | } 95 | 96 | // Prevent storing a cookie that we couldn't decode. 97 | if (!key && (cookie = read(cookie)) !== undefined) { 98 | result[name] = cookie; 99 | } 100 | } 101 | 102 | return result; 103 | }; 104 | 105 | config.defaults = {}; 106 | 107 | $.removeCookie = function (key, options) { 108 | if ($.cookie(key) === undefined) { 109 | return false; 110 | } 111 | 112 | // Must not alter options, thus extending a fresh object... 113 | $.cookie(key, '', $.extend({}, options, { expires: -1 })); 114 | return !$.cookie(key); 115 | }; 116 | 117 | })); 118 | -------------------------------------------------------------------------------- /src/main/resources/static/js/jquery.validate.min.js: -------------------------------------------------------------------------------- 1 | !function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(jQuery)}(function(t){t.extend(t.fn,{validate:function(e){if(!this.length)return void(e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var i=t.data(this[0],"validator");return i?i:(this.attr("novalidate","novalidate"),i=new t.validator(e,this[0]),t.data(this[0],"validator",i),i.settings.onsubmit&&(this.on("click.validate",":submit",function(e){i.settings.submitHandler&&(i.submitButton=e.target),t(this).hasClass("cancel")&&(i.cancelSubmit=!0),void 0!==t(this).attr("formnovalidate")&&(i.cancelSubmit=!0)}),this.on("submit.validate",function(e){function s(){var s,r;return i.settings.submitHandler?(i.submitButton&&(s=t("").attr("name",i.submitButton.name).val(t(i.submitButton).val()).appendTo(i.currentForm)),r=i.settings.submitHandler.call(i,i.currentForm,e),i.submitButton&&s.remove(),void 0!==r?r:!1):!0}return i.settings.debug&&e.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,s()):i.form()?i.pendingRequest?(i.formSubmitted=!0,!1):s():(i.focusInvalid(),!1)})),i)},valid:function(){var e,i,s;return t(this[0]).is("form")?e=this.validate().form():(s=[],e=!0,i=t(this[0].form).validate(),this.each(function(){e=i.element(this)&&e,e||(s=s.concat(i.errorList))}),i.errorList=s),e},rules:function(e,i){var s,r,n,a,o,l,h=this[0];if(null!=h&&null!=h.form){if(e)switch(s=t.data(h.form,"validator").settings,r=s.rules,n=t.validator.staticRules(h),e){case"add":t.extend(n,t.validator.normalizeRule(i)),delete n.messages,r[h.name]=n,i.messages&&(s.messages[h.name]=t.extend(s.messages[h.name],i.messages));break;case"remove":return i?(l={},t.each(i.split(/\s/),function(e,i){l[i]=n[i],delete n[i],"required"===i&&t(h).removeAttr("aria-required")}),l):(delete r[h.name],n)}return a=t.validator.normalizeRules(t.extend({},t.validator.classRules(h),t.validator.attributeRules(h),t.validator.dataRules(h),t.validator.staticRules(h)),h),a.required&&(o=a.required,delete a.required,a=t.extend({required:o},a),t(h).attr("aria-required","true")),a.remote&&(o=a.remote,delete a.remote,a=t.extend(a,{remote:o})),a}}}),t.extend(t.expr[":"],{blank:function(e){return!t.trim(""+t(e).val())},filled:function(e){var i=t(e).val();return null!==i&&!!t.trim(""+i)},unchecked:function(e){return!t(e).prop("checked")}}),t.validator=function(e,i){this.settings=t.extend(!0,{},t.validator.defaults,e),this.currentForm=i,this.init()},t.validator.format=function(e,i){return 1===arguments.length?function(){var i=t.makeArray(arguments);return i.unshift(e),t.validator.format.apply(this,i)}:void 0===i?e:(arguments.length>2&&i.constructor!==Array&&(i=t.makeArray(arguments).slice(1)),i.constructor!==Array&&(i=[i]),t.each(i,function(t,i){e=e.replace(new RegExp("\\{"+t+"\\}","g"),function(){return i})}),e)},t.extend(t.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:t([]),errorLabelContainer:t([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(t){this.lastActive=t,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,t,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(t)))},onfocusout:function(t){this.checkable(t)||!(t.name in this.submitted)&&this.optional(t)||this.element(t)},onkeyup:function(e,i){var s=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===i.which&&""===this.elementValue(e)||-1!==t.inArray(i.keyCode,s)||(e.name in this.submitted||e.name in this.invalid)&&this.element(e)},onclick:function(t){t.name in this.submitted?this.element(t):t.parentNode.name in this.submitted&&this.element(t.parentNode)},highlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).addClass(i).removeClass(s):t(e).addClass(i).removeClass(s)},unhighlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).removeClass(i).addClass(s):t(e).removeClass(i).addClass(s)}},setDefaults:function(e){t.extend(t.validator.defaults,e)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:t.validator.format("Please enter no more than {0} characters."),minlength:t.validator.format("Please enter at least {0} characters."),rangelength:t.validator.format("Please enter a value between {0} and {1} characters long."),range:t.validator.format("Please enter a value between {0} and {1}."),max:t.validator.format("Please enter a value less than or equal to {0}."),min:t.validator.format("Please enter a value greater than or equal to {0}."),step:t.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function e(e){!this.form&&this.hasAttribute("contenteditable")&&(this.form=t(this).closest("form")[0]);var i=t.data(this.form,"validator"),s="on"+e.type.replace(/^validate/,""),r=i.settings;r[s]&&!t(this).is(r.ignore)&&r[s].call(i,this,e)}this.labelContainer=t(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||t(this.currentForm),this.containers=t(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var i,s=this.groups={};t.each(this.settings.groups,function(e,i){"string"==typeof i&&(i=i.split(/\s/)),t.each(i,function(t,i){s[i]=e})}),i=this.settings.rules,t.each(i,function(e,s){i[e]=t.validator.normalizeRule(s)}),t(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable]",e).on("click.validate","select, option, [type='radio'], [type='checkbox']",e),this.settings.invalidHandler&&t(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler),t(this.currentForm).find("[required], [data-rule-required], .required").attr("aria-required","true")},form:function(){return this.checkForm(),t.extend(this.submitted,this.errorMap),this.invalid=t.extend({},this.errorMap),this.valid()||t(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var t=0,e=this.currentElements=this.elements();e[t];t++)this.check(e[t]);return this.valid()},element:function(e){var i,s,r=this.clean(e),n=this.validationTargetFor(r),a=this,o=!0;return void 0===n?delete this.invalid[r.name]:(this.prepareElement(n),this.currentElements=t(n),s=this.groups[n.name],s&&t.each(this.groups,function(t,e){e===s&&t!==n.name&&(r=a.validationTargetFor(a.clean(a.findByName(t))),r&&r.name in a.invalid&&(a.currentElements.push(r),o=a.check(r)&&o))}),i=this.check(n)!==!1,o=o&&i,this.invalid[n.name]=i?!1:!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),t(e).attr("aria-invalid",!i)),o},showErrors:function(e){if(e){var i=this;t.extend(this.errorMap,e),this.errorList=t.map(this.errorMap,function(t,e){return{message:t,element:i.findByName(e)[0]}}),this.successList=t.grep(this.successList,function(t){return!(t.name in e)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){t.fn.resetForm&&t(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var e=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(e)},resetElements:function(t){var e;if(this.settings.unhighlight)for(e=0;t[e];e++)this.settings.unhighlight.call(this,t[e],this.settings.errorClass,""),this.findByName(t[e].name).removeClass(this.settings.validClass);else t.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(t){var e,i=0;for(e in t)t[e]&&i++;return i},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(t){t.not(this.containers).text(""),this.addWrapper(t).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{t(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(e){}},findLastActive:function(){var e=this.lastActive;return e&&1===t.grep(this.errorList,function(t){return t.element.name===e.name}).length&&e},elements:function(){var e=this,i={};return t(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var s=this.name||t(this).attr("name");return!s&&e.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.hasAttribute("contenteditable")&&(this.form=t(this).closest("form")[0]),s in i||!e.objectLength(t(this).rules())?!1:(i[s]=!0,!0)})},clean:function(e){return t(e)[0]},errors:function(){var e=this.settings.errorClass.split(" ").join(".");return t(this.settings.errorElement+"."+e,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=t([]),this.toHide=t([])},reset:function(){this.resetInternals(),this.currentElements=t([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(t){this.reset(),this.toHide=this.errorsFor(t)},elementValue:function(e){var i,s,r=t(e),n=e.type;return"radio"===n||"checkbox"===n?this.findByName(e.name).filter(":checked").val():"number"===n&&"undefined"!=typeof e.validity?e.validity.badInput?"NaN":r.val():(i=e.hasAttribute("contenteditable")?r.text():r.val(),"file"===n?"C:\\fakepath\\"===i.substr(0,12)?i.substr(12):(s=i.lastIndexOf("/"),s>=0?i.substr(s+1):(s=i.lastIndexOf("\\"),s>=0?i.substr(s+1):i)):"string"==typeof i?i.replace(/\r/g,""):i)},check:function(e){e=this.validationTargetFor(this.clean(e));var i,s,r,n=t(e).rules(),a=t.map(n,function(t,e){return e}).length,o=!1,l=this.elementValue(e);if("function"==typeof n.normalizer){if(l=n.normalizer.call(e,l),"string"!=typeof l)throw new TypeError("The normalizer should return a string value.");delete n.normalizer}for(s in n){r={method:s,parameters:n[s]};try{if(i=t.validator.methods[s].call(this,l,e,r.parameters),"dependency-mismatch"===i&&1===a){o=!0;continue}if(o=!1,"pending"===i)return void(this.toHide=this.toHide.not(this.errorsFor(e)));if(!i)return this.formatAndAdd(e,r),!1}catch(h){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+e.id+", check the '"+r.method+"' method.",h),h instanceof TypeError&&(h.message+=". Exception occurred when checking element "+e.id+", check the '"+r.method+"' method."),h}}if(!o)return this.objectLength(n)&&this.successList.push(e),!0},customDataMessage:function(e,i){return t(e).data("msg"+i.charAt(0).toUpperCase()+i.substring(1).toLowerCase())||t(e).data("msg")},customMessage:function(t,e){var i=this.settings.messages[t];return i&&(i.constructor===String?i:i[e])},findDefined:function(){for(var t=0;tWarning: No message defined for "+e.name+""),r=/\$?\{(\d+)\}/g;return"function"==typeof s?s=s.call(this,i.parameters,e):r.test(s)&&(s=t.validator.format(s.replace(r,"{$1}"),i.parameters)),s},formatAndAdd:function(t,e){var i=this.defaultMessage(t,e);this.errorList.push({message:i,element:t,method:e.method}),this.errorMap[t.name]=i,this.submitted[t.name]=i},addWrapper:function(t){return this.settings.wrapper&&(t=t.add(t.parent(this.settings.wrapper))),t},defaultShowErrors:function(){var t,e,i;for(t=0;this.errorList[t];t++)i=this.errorList[t],this.settings.highlight&&this.settings.highlight.call(this,i.element,this.settings.errorClass,this.settings.validClass),this.showLabel(i.element,i.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(t=0;this.successList[t];t++)this.showLabel(this.successList[t]);if(this.settings.unhighlight)for(t=0,e=this.validElements();e[t];t++)this.settings.unhighlight.call(this,e[t],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return t(this.errorList).map(function(){return this.element})},showLabel:function(e,i){var s,r,n,a,o=this.errorsFor(e),l=this.idOrName(e),h=t(e).attr("aria-describedby");o.length?(o.removeClass(this.settings.validClass).addClass(this.settings.errorClass),o.html(i)):(o=t("<"+this.settings.errorElement+">").attr("id",l+"-error").addClass(this.settings.errorClass).html(i||""),s=o,this.settings.wrapper&&(s=o.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(s):this.settings.errorPlacement?this.settings.errorPlacement.call(this,s,t(e)):s.insertAfter(e),o.is("label")?o.attr("for",l):0===o.parents("label[for='"+this.escapeCssMeta(l)+"']").length&&(n=o.attr("id"),h?h.match(new RegExp("\\b"+this.escapeCssMeta(n)+"\\b"))||(h+=" "+n):h=n,t(e).attr("aria-describedby",h),r=this.groups[e.name],r&&(a=this,t.each(a.groups,function(e,i){i===r&&t("[name='"+a.escapeCssMeta(e)+"']",a.currentForm).attr("aria-describedby",o.attr("id"))})))),!i&&this.settings.success&&(o.text(""),"string"==typeof this.settings.success?o.addClass(this.settings.success):this.settings.success(o,e)),this.toShow=this.toShow.add(o)},errorsFor:function(e){var i=this.escapeCssMeta(this.idOrName(e)),s=t(e).attr("aria-describedby"),r="label[for='"+i+"'], label[for='"+i+"'] *";return s&&(r=r+", #"+this.escapeCssMeta(s).replace(/\s+/g,", #")),this.errors().filter(r)},escapeCssMeta:function(t){return t.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(t){return this.groups[t.name]||(this.checkable(t)?t.name:t.id||t.name)},validationTargetFor:function(e){return this.checkable(e)&&(e=this.findByName(e.name)),t(e).not(this.settings.ignore)[0]},checkable:function(t){return/radio|checkbox/i.test(t.type)},findByName:function(e){return t(this.currentForm).find("[name='"+this.escapeCssMeta(e)+"']")},getLength:function(e,i){switch(i.nodeName.toLowerCase()){case"select":return t("option:selected",i).length;case"input":if(this.checkable(i))return this.findByName(i.name).filter(":checked").length}return e.length},depend:function(t,e){return this.dependTypes[typeof t]?this.dependTypes[typeof t](t,e):!0},dependTypes:{"boolean":function(t){return t},string:function(e,i){return!!t(e,i.form).length},"function":function(t,e){return t(e)}},optional:function(e){var i=this.elementValue(e);return!t.validator.methods.required.call(this,i,e)&&"dependency-mismatch"},startRequest:function(e){this.pending[e.name]||(this.pendingRequest++,t(e).addClass(this.settings.pendingClass),this.pending[e.name]=!0)},stopRequest:function(e,i){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],t(e).removeClass(this.settings.pendingClass),i&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(t(this.currentForm).submit(),this.formSubmitted=!1):!i&&0===this.pendingRequest&&this.formSubmitted&&(t(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e,i){return i="string"==typeof i&&i||"remote",t.data(e,"previousValue")||t.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,{method:i})})},destroy:function(){this.resetForm(),t(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,i){e.constructor===String?this.classRuleSettings[e]=i:t.extend(this.classRuleSettings,e)},classRules:function(e){var i={},s=t(e).attr("class");return s&&t.each(s.split(" "),function(){this in t.validator.classRuleSettings&&t.extend(i,t.validator.classRuleSettings[this])}),i},normalizeAttributeRule:function(t,e,i,s){/min|max|step/.test(i)&&(null===e||/number|range|text/.test(e))&&(s=Number(s),isNaN(s)&&(s=void 0)),s||0===s?t[i]=s:e===i&&"range"!==e&&(t[i]=!0)},attributeRules:function(e){var i,s,r={},n=t(e),a=e.getAttribute("type");for(i in t.validator.methods)"required"===i?(s=e.getAttribute(i),""===s&&(s=!0),s=!!s):s=n.attr(i),this.normalizeAttributeRule(r,a,i,s);return r.maxlength&&/-1|2147483647|524288/.test(r.maxlength)&&delete r.maxlength,r},dataRules:function(e){var i,s,r={},n=t(e),a=e.getAttribute("type");for(i in t.validator.methods)s=n.data("rule"+i.charAt(0).toUpperCase()+i.substring(1).toLowerCase()),this.normalizeAttributeRule(r,a,i,s);return r},staticRules:function(e){var i={},s=t.data(e.form,"validator");return s.settings.rules&&(i=t.validator.normalizeRule(s.settings.rules[e.name])||{}),i},normalizeRules:function(e,i){return t.each(e,function(s,r){if(r===!1)return void delete e[s];if(r.param||r.depends){var n=!0;switch(typeof r.depends){case"string":n=!!t(r.depends,i.form).length;break;case"function":n=r.depends.call(i,i)}n?e[s]=void 0!==r.param?r.param:!0:(t.data(i.form,"validator").resetElements(t(i)),delete e[s])}}),t.each(e,function(s,r){e[s]=t.isFunction(r)&&"normalizer"!==s?r(i):r}),t.each(["minlength","maxlength"],function(){e[this]&&(e[this]=Number(e[this]))}),t.each(["rangelength","range"],function(){var i;e[this]&&(t.isArray(e[this])?e[this]=[Number(e[this][0]),Number(e[this][1])]:"string"==typeof e[this]&&(i=e[this].replace(/[\[\]]/g,"").split(/[\s,]+/),e[this]=[Number(i[0]),Number(i[1])]))}),t.validator.autoCreateRanges&&(null!=e.min&&null!=e.max&&(e.range=[e.min,e.max],delete e.min,delete e.max),null!=e.minlength&&null!=e.maxlength&&(e.rangelength=[e.minlength,e.maxlength],delete e.minlength,delete e.maxlength)),e},normalizeRule:function(e){if("string"==typeof e){var i={};t.each(e.split(/\s/),function(){i[this]=!0}),e=i}return e},addMethod:function(e,i,s){t.validator.methods[e]=i,t.validator.messages[e]=void 0!==s?s:t.validator.messages[e],i.length<3&&t.validator.addClassRules(e,t.validator.normalizeRule(e))},methods:{required:function(e,i,s){if(!this.depend(s,i))return"dependency-mismatch";if("select"===i.nodeName.toLowerCase()){var r=t(i).val();return r&&r.length>0}return this.checkable(i)?this.getLength(e,i)>0:e.length>0},email:function(t,e){return this.optional(e)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(t)},url:function(t,e){return this.optional(e)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(t)},date:function(t,e){return this.optional(e)||!/Invalid|NaN/.test(new Date(t).toString())},dateISO:function(t,e){return this.optional(e)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(t)},number:function(t,e){return this.optional(e)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(t)},digits:function(t,e){return this.optional(e)||/^\d+$/.test(t)},minlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(e,i);return this.optional(i)||r>=s},maxlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(e,i);return this.optional(i)||s>=r},rangelength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(e,i);return this.optional(i)||r>=s[0]&&r<=s[1]},min:function(t,e,i){return this.optional(e)||t>=i},max:function(t,e,i){return this.optional(e)||i>=t},range:function(t,e,i){return this.optional(e)||t>=i[0]&&t<=i[1]},step:function(e,i,s){var r,n=t(i).attr("type"),a="Step attribute on input type "+n+" is not supported.",o=["text","number","range"],l=new RegExp("\\b"+n+"\\b"),h=n&&!l.test(o.join()),u=function(t){var e=(""+t).match(/(?:\.(\d+))?$/);return e&&e[1]?e[1].length:0},d=function(t){return Math.round(t*Math.pow(10,r))},c=!0;if(h)throw new Error(a);return r=u(s),(u(e)>r||d(e)%d(s)!==0)&&(c=!1),this.optional(i)||c},equalTo:function(e,i,s){var r=t(s);return this.settings.onfocusout&&r.not(".validate-equalTo-blur").length&&r.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){t(i).valid()}),e===r.val()},remote:function(e,i,s,r){if(this.optional(i))return"dependency-mismatch";r="string"==typeof r&&r||"remote";var n,a,o,l=this.previousValue(i,r);return this.settings.messages[i.name]||(this.settings.messages[i.name]={}),l.originalMessage=l.originalMessage||this.settings.messages[i.name][r],this.settings.messages[i.name][r]=l.message,s="string"==typeof s&&{url:s}||s,o=t.param(t.extend({data:e},s.data)),l.old===o?l.valid:(l.old=o,n=this,this.startRequest(i),a={},a[i.name]=e,t.ajax(t.extend(!0,{mode:"abort",port:"validate"+i.name,dataType:"json",data:a,context:n.currentForm,success:function(t){var s,a,o,h=t===!0||"true"===t;n.settings.messages[i.name][r]=l.originalMessage,h?(o=n.formSubmitted,n.resetInternals(),n.toHide=n.errorsFor(i),n.formSubmitted=o,n.successList.push(i),n.invalid[i.name]=!1,n.showErrors()):(s={},a=t||n.defaultMessage(i,{method:r,parameters:e}),s[i.name]=l.message=a,n.invalid[i.name]=!0,n.showErrors(s)),l.valid=h,n.stopRequest(i,h)}},s)),"pending")}}});var e,i={};t.ajaxPrefilter?t.ajaxPrefilter(function(t,e,s){var r=t.port;"abort"===t.mode&&(i[r]&&i[r].abort(),i[r]=s)}):(e=t.ajax,t.ajax=function(s){var r=("mode"in s?s:t.ajaxSettings).mode,n=("port"in s?s:t.ajaxSettings).port;return"abort"===r?(i[n]&&i[n].abort(),i[n]=e.apply(this,arguments),i[n]):e.apply(this,arguments)})}); -------------------------------------------------------------------------------- /src/main/resources/static/js/sticky-footer.js: -------------------------------------------------------------------------------- 1 | function adjust_footer(){ 2 | $('.push').height(0); 3 | $('.content-inner').css('height', 'auto'); 4 | $('.side-navbar').css('height', 'auto'); 5 | $('.content-inner').height($(document).height() - $('.header').height()); 6 | var footerHeight = $('.content-inner').height() - $('.push').position().top; 7 | if(footerHeight < 0) 8 | { 9 | $('.content-inner').height($('.push').position().top); 10 | footerHeight = 0; 11 | } 12 | $('.push').height(footerHeight); 13 | } 14 | 15 | 16 | $(document).ready(function(){ 17 | adjust_footer(); 18 | }); 19 | 20 | $(window).resize(function() { 21 | adjust_footer(); 22 | }); -------------------------------------------------------------------------------- /src/main/resources/static/js/submit_upload.js: -------------------------------------------------------------------------------- 1 | var frm = $('#uploadform'); 2 | 3 | frm.submit(function (e) { 4 | e.preventDefault(); 5 | var url = $("#url").val(); 6 | var filename = $("#filename").val(); 7 | var dataString = 'url=' + encodeURIComponent(url) + '&filename=' + encodeURIComponent(filename); 8 | if (url == '') { 9 | alert("Please Enter Url"); 10 | } 11 | else { 12 | $.ajax({ 13 | type: frm.attr('method'), 14 | url: frm.attr('action'), 15 | data: frm.serialize(), 16 | cache: false, 17 | success: function () { 18 | frm.trigger("reset"); 19 | $("#submit_message").remove(); 20 | frm.append('

Successfully Submitted!

') 21 | }, 22 | error: function (data) { 23 | $("#submit_message").remove(); 24 | console.log(data.responseText); 25 | var json = $.parseJSON(data.responseText); 26 | var message = json["message"]; 27 | 28 | if (json["statusCode"] / 100 == 5) 29 | message = (message == null) ? "There is some problem at server side. Please contact developers." : message; 30 | 31 | frm.append('

Error: ' + message + '

') 32 | console.log(json); 33 | } 34 | }); 35 | } 36 | return false; 37 | }); 38 | -------------------------------------------------------------------------------- /src/main/resources/static/js/tether.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e(require,exports,module):t.Tether=e()}(this,function(t,e,o){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t){var e=t.getBoundingClientRect(),o={};for(var n in e)o[n]=e[n];if(t.ownerDocument!==document){var r=t.ownerDocument.defaultView.frameElement;if(r){var s=i(r);o.top+=s.top,o.bottom+=s.top,o.left+=s.left,o.right+=s.left}}return o}function r(t){var e=getComputedStyle(t)||{},o=e.position,n=[];if("fixed"===o)return[t];for(var i=t;(i=i.parentNode)&&i&&1===i.nodeType;){var r=void 0;try{r=getComputedStyle(i)}catch(s){}if("undefined"==typeof r||null===r)return n.push(i),n;var a=r,f=a.overflow,l=a.overflowX,h=a.overflowY;/(auto|scroll)/.test(f+h+l)&&("absolute"!==o||["relative","absolute","fixed"].indexOf(r.position)>=0)&&n.push(i)}return n.push(t.ownerDocument.body),t.ownerDocument!==document&&n.push(t.ownerDocument.defaultView),n}function s(){A&&document.body.removeChild(A),A=null}function a(t){var e=void 0;t===document?(e=document,t=document.documentElement):e=t.ownerDocument;var o=e.documentElement,n=i(t),r=P();return n.top-=r.top,n.left-=r.left,"undefined"==typeof n.width&&(n.width=document.body.scrollWidth-n.left-n.right),"undefined"==typeof n.height&&(n.height=document.body.scrollHeight-n.top-n.bottom),n.top=n.top-o.clientTop,n.left=n.left-o.clientLeft,n.right=e.body.clientWidth-n.width-n.left,n.bottom=e.body.clientHeight-n.height-n.top,n}function f(t){return t.offsetParent||document.documentElement}function l(){var t=document.createElement("div");t.style.width="100%",t.style.height="200px";var e=document.createElement("div");h(e.style,{position:"absolute",top:0,left:0,pointerEvents:"none",visibility:"hidden",width:"200px",height:"150px",overflow:"hidden"}),e.appendChild(t),document.body.appendChild(e);var o=t.offsetWidth;e.style.overflow="scroll";var n=t.offsetWidth;o===n&&(n=e.clientWidth),document.body.removeChild(e);var i=o-n;return{width:i,height:i}}function h(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],e=[];return Array.prototype.push.apply(e,arguments),e.slice(1).forEach(function(e){if(e)for(var o in e)({}).hasOwnProperty.call(e,o)&&(t[o]=e[o])}),t}function u(t,e){if("undefined"!=typeof t.classList)e.split(" ").forEach(function(e){e.trim()&&t.classList.remove(e)});else{var o=new RegExp("(^| )"+e.split(" ").join("|")+"( |$)","gi"),n=c(t).replace(o," ");g(t,n)}}function d(t,e){if("undefined"!=typeof t.classList)e.split(" ").forEach(function(e){e.trim()&&t.classList.add(e)});else{u(t,e);var o=c(t)+(" "+e);g(t,o)}}function p(t,e){if("undefined"!=typeof t.classList)return t.classList.contains(e);var o=c(t);return new RegExp("(^| )"+e+"( |$)","gi").test(o)}function c(t){return t.className instanceof t.ownerDocument.defaultView.SVGAnimatedString?t.className.baseVal:t.className}function g(t,e){t.setAttribute("class",e)}function m(t,e,o){o.forEach(function(o){-1===e.indexOf(o)&&p(t,o)&&u(t,o)}),e.forEach(function(e){p(t,e)||d(t,e)})}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function v(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function y(t,e){var o=arguments.length<=2||void 0===arguments[2]?1:arguments[2];return t+o>=e&&e>=t-o}function b(){return"undefined"!=typeof performance&&"undefined"!=typeof performance.now?performance.now():+new Date}function w(){for(var t={top:0,left:0},e=arguments.length,o=Array(e),n=0;e>n;n++)o[n]=arguments[n];return o.forEach(function(e){var o=e.top,n=e.left;"string"==typeof o&&(o=parseFloat(o,10)),"string"==typeof n&&(n=parseFloat(n,10)),t.top+=o,t.left+=n}),t}function C(t,e){return"string"==typeof t.left&&-1!==t.left.indexOf("%")&&(t.left=parseFloat(t.left,10)/100*e.width),"string"==typeof t.top&&-1!==t.top.indexOf("%")&&(t.top=parseFloat(t.top,10)/100*e.height),t}function O(t,e){return"scrollParent"===e?e=t.scrollParents[0]:"window"===e&&(e=[pageXOffset,pageYOffset,innerWidth+pageXOffset,innerHeight+pageYOffset]),e===document&&(e=e.documentElement),"undefined"!=typeof e.nodeType&&!function(){var t=e,o=a(e),n=o,i=getComputedStyle(e);if(e=[n.left,n.top,o.width+n.left,o.height+n.top],t.ownerDocument!==document){var r=t.ownerDocument.defaultView;e[0]+=r.pageXOffset,e[1]+=r.pageYOffset,e[2]+=r.pageXOffset,e[3]+=r.pageYOffset}$.forEach(function(t,o){t=t[0].toUpperCase()+t.substr(1),"Top"===t||"Left"===t?e[o]+=parseFloat(i["border"+t+"Width"]):e[o]-=parseFloat(i["border"+t+"Width"])})}(),e}var E=function(){function t(t,e){for(var o=0;o1?o-1:0),i=1;o>i;i++)n[i-1]=arguments[i];for(;e16?(e=Math.min(e-16,250),void(o=setTimeout(i,250))):void("undefined"!=typeof t&&b()-t<10||(null!=o&&(clearTimeout(o),o=null),t=b(),D(),e=b()-t))};"undefined"!=typeof window&&"undefined"!=typeof window.addEventListener&&["resize","scroll","touchmove"].forEach(function(t){window.addEventListener(t,n)})}();var X={center:"center",left:"right",right:"left"},F={middle:"middle",top:"bottom",bottom:"top"},H={top:0,left:0,middle:"50%",center:"50%",bottom:"100%",right:"100%"},N=function(t,e){var o=t.left,n=t.top;return"auto"===o&&(o=X[e.left]),"auto"===n&&(n=F[e.top]),{left:o,top:n}},U=function(t){var e=t.left,o=t.top;return"undefined"!=typeof H[t.left]&&(e=H[t.left]),"undefined"!=typeof H[t.top]&&(o=H[t.top]),{left:e,top:o}},V=function(t){var e=t.split(" "),o=B(e,2),n=o[0],i=o[1];return{top:n,left:i}},R=V,q=function(t){function e(t){var o=this;n(this,e),z(Object.getPrototypeOf(e.prototype),"constructor",this).call(this),this.position=this.position.bind(this),L.push(this),this.history=[],this.setOptions(t,!1),x.modules.forEach(function(t){"undefined"!=typeof t.initialize&&t.initialize.call(o)}),this.position()}return v(e,t),E(e,[{key:"getClass",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"":arguments[0],e=this.options.classes;return"undefined"!=typeof e&&e[t]?this.options.classes[t]:this.options.classPrefix?this.options.classPrefix+"-"+t:t}},{key:"setOptions",value:function(t){var e=this,o=arguments.length<=1||void 0===arguments[1]?!0:arguments[1],n={offset:"0 0",targetOffset:"0 0",targetAttachment:"auto auto",classPrefix:"tether"};this.options=h(n,t);var i=this.options,s=i.element,a=i.target,f=i.targetModifier;if(this.element=s,this.target=a,this.targetModifier=f,"viewport"===this.target?(this.target=document.body,this.targetModifier="visible"):"scroll-handle"===this.target&&(this.target=document.body,this.targetModifier="scroll-handle"),["element","target"].forEach(function(t){if("undefined"==typeof e[t])throw new Error("Tether Error: Both element and target must be defined");"undefined"!=typeof e[t].jquery?e[t]=e[t][0]:"string"==typeof e[t]&&(e[t]=document.querySelector(e[t]))}),d(this.element,this.getClass("element")),this.options.addTargetClasses!==!1&&d(this.target,this.getClass("target")),!this.options.attachment)throw new Error("Tether Error: You must provide an attachment");this.targetAttachment=R(this.options.targetAttachment),this.attachment=R(this.options.attachment),this.offset=V(this.options.offset),this.targetOffset=V(this.options.targetOffset),"undefined"!=typeof this.scrollParents&&this.disable(),"scroll-handle"===this.targetModifier?this.scrollParents=[this.target]:this.scrollParents=r(this.target),this.options.enabled!==!1&&this.enable(o)}},{key:"getTargetBounds",value:function(){if("undefined"==typeof this.targetModifier)return a(this.target);if("visible"===this.targetModifier){if(this.target===document.body)return{top:pageYOffset,left:pageXOffset,height:innerHeight,width:innerWidth};var t=a(this.target),e={height:t.height,width:t.width,top:t.top,left:t.left};return e.height=Math.min(e.height,t.height-(pageYOffset-t.top)),e.height=Math.min(e.height,t.height-(t.top+t.height-(pageYOffset+innerHeight))),e.height=Math.min(innerHeight,e.height),e.height-=2,e.width=Math.min(e.width,t.width-(pageXOffset-t.left)),e.width=Math.min(e.width,t.width-(t.left+t.width-(pageXOffset+innerWidth))),e.width=Math.min(innerWidth,e.width),e.width-=2,e.topo.clientWidth||[n.overflow,n.overflowX].indexOf("scroll")>=0||this.target!==document.body,r=0;i&&(r=15);var s=t.height-parseFloat(n.borderTopWidth)-parseFloat(n.borderBottomWidth)-r,e={width:15,height:.975*s*(s/o.scrollHeight),left:t.left+t.width-parseFloat(n.borderLeftWidth)-15},f=0;408>s&&this.target===document.body&&(f=-11e-5*Math.pow(s,2)-.00727*s+22.58),this.target!==document.body&&(e.height=Math.max(e.height,24));var l=this.target.scrollTop/(o.scrollHeight-s);return e.top=l*(s-e.height-f)+t.top+parseFloat(n.borderTopWidth),this.target===document.body&&(e.height=Math.max(e.height,24)),e}}},{key:"clearCache",value:function(){this._cache={}}},{key:"cache",value:function(t,e){return"undefined"==typeof this._cache&&(this._cache={}),"undefined"==typeof this._cache[t]&&(this._cache[t]=e.call(this)),this._cache[t]}},{key:"enable",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?!0:arguments[0];this.options.addTargetClasses!==!1&&d(this.target,this.getClass("enabled")),d(this.element,this.getClass("enabled")),this.enabled=!0,this.scrollParents.forEach(function(e){e!==t.target.ownerDocument&&e.addEventListener("scroll",t.position)}),e&&this.position()}},{key:"disable",value:function(){var t=this;u(this.target,this.getClass("enabled")),u(this.element,this.getClass("enabled")),this.enabled=!1,"undefined"!=typeof this.scrollParents&&this.scrollParents.forEach(function(e){e.removeEventListener("scroll",t.position)})}},{key:"destroy",value:function(){var t=this;this.disable(),L.forEach(function(e,o){e===t&&L.splice(o,1)}),0===L.length&&s()}},{key:"updateAttachClasses",value:function(t,e){var o=this;t=t||this.attachment,e=e||this.targetAttachment;var n=["left","top","bottom","right","middle","center"];"undefined"!=typeof this._addAttachClasses&&this._addAttachClasses.length&&this._addAttachClasses.splice(0,this._addAttachClasses.length),"undefined"==typeof this._addAttachClasses&&(this._addAttachClasses=[]);var i=this._addAttachClasses;t.top&&i.push(this.getClass("element-attached")+"-"+t.top),t.left&&i.push(this.getClass("element-attached")+"-"+t.left),e.top&&i.push(this.getClass("target-attached")+"-"+e.top),e.left&&i.push(this.getClass("target-attached")+"-"+e.left);var r=[];n.forEach(function(t){r.push(o.getClass("element-attached")+"-"+t),r.push(o.getClass("target-attached")+"-"+t)}),M(function(){"undefined"!=typeof o._addAttachClasses&&(m(o.element,o._addAttachClasses,r),o.options.addTargetClasses!==!1&&m(o.target,o._addAttachClasses,r),delete o._addAttachClasses)})}},{key:"position",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?!0:arguments[0];if(this.enabled){this.clearCache();var o=N(this.targetAttachment,this.attachment);this.updateAttachClasses(this.attachment,o);var n=this.cache("element-bounds",function(){return a(t.element)}),i=n.width,r=n.height;if(0===i&&0===r&&"undefined"!=typeof this.lastSize){var s=this.lastSize;i=s.width,r=s.height}else this.lastSize={width:i,height:r};var h=this.cache("target-bounds",function(){return t.getTargetBounds()}),u=h,d=C(U(this.attachment),{width:i,height:r}),p=C(U(o),u),c=C(this.offset,{width:i,height:r}),g=C(this.targetOffset,u);d=w(d,c),p=w(p,g);for(var m=h.left+p.left-d.left,v=h.top+p.top-d.top,y=0;yT.innerWidth&&(S=this.cache("scrollbar-size",l),E.viewport.bottom-=S.height),A.body.scrollHeight>T.innerHeight&&(S=this.cache("scrollbar-size",l),E.viewport.right-=S.width),(-1===["","static"].indexOf(A.body.style.position)||-1===["","static"].indexOf(A.body.parentElement.style.position))&&(E.page.bottom=A.body.scrollHeight-v-r,E.page.right=A.body.scrollWidth-m-i),"undefined"!=typeof this.options.optimizations&&this.options.optimizations.moveElement!==!1&&"undefined"==typeof this.targetModifier&&!function(){var e=t.cache("target-offsetparent",function(){return f(t.target)}),o=t.cache("target-offsetparent-bounds",function(){return a(e)}),n=getComputedStyle(e),i=o,r={};if(["Top","Left","Bottom","Right"].forEach(function(t){r[t.toLowerCase()]=parseFloat(n["border"+t+"Width"])}),o.right=A.body.scrollWidth-o.left-i.width+r.right,o.bottom=A.body.scrollHeight-o.top-i.height+r.bottom,E.page.top>=o.top+r.top&&E.page.bottom>=o.bottom&&E.page.left>=o.left+r.left&&E.page.right>=o.right){var s=e.scrollTop,l=e.scrollLeft;E.offset={top:E.page.top-o.top+s-r.top,left:E.page.left-o.left+l-r.left}}}(),this.move(E),this.history.unshift(E),this.history.length>3&&this.history.pop(),e&&_(),!0}}},{key:"move",value:function(t){var e=this;if("undefined"!=typeof this.element.parentNode){var o={};for(var n in t){o[n]={};for(var i in t[n]){for(var r=!1,s=0;s=0){var c=a.split(" "),m=B(c,2);u=m[0],h=m[1]}else h=u=a;var b=O(e,r);("target"===u||"both"===u)&&(ob[3]&&"bottom"===v.top&&(o-=d,v.top="top")),"together"===u&&("top"===v.top&&("bottom"===y.top&&ob[3]&&o-(s-d)>=b[1]&&(o-=s-d,v.top="bottom",y.top="bottom")),"bottom"===v.top&&("top"===y.top&&o+s>b[3]?(o-=d,v.top="top",o-=s,y.top="bottom"):"bottom"===y.top&&ob[3]&&"top"===y.top?(o-=s,y.top="bottom"):ob[2]&&"right"===v.left&&(n-=p,v.left="left")),"together"===h&&(nb[2]&&"right"===v.left?"left"===y.left?(n-=p,v.left="left",n-=f,y.left="right"):"right"===y.left&&(n-=p,v.left="left",n+=f,y.left="left"):"center"===v.left&&(n+f>b[2]&&"left"===y.left?(n-=f,y.left="right"):nb[3]&&"top"===y.top&&(o-=s,y.top="bottom")),("element"===h||"both"===h)&&(nb[2]&&("left"===y.left?(n-=f,y.left="right"):"center"===y.left&&(n-=f/2,y.left="right"))),"string"==typeof l?l=l.split(",").map(function(t){return t.trim()}):l===!0&&(l=["top","left","right","bottom"]),l=l||[];var w=[],C=[];o=0?(o=b[1],w.push("top")):C.push("top")),o+s>b[3]&&(l.indexOf("bottom")>=0?(o=b[3]-s,w.push("bottom")):C.push("bottom")),n=0?(n=b[0],w.push("left")):C.push("left")),n+f>b[2]&&(l.indexOf("right")>=0?(n=b[2]-f,w.push("right")):C.push("right")),w.length&&!function(){var t=void 0;t="undefined"!=typeof e.options.pinnedClass?e.options.pinnedClass:e.getClass("pinned"),g.push(t),w.forEach(function(e){g.push(t+"-"+e)})}(),C.length&&!function(){var t=void 0;t="undefined"!=typeof e.options.outOfBoundsClass?e.options.outOfBoundsClass:e.getClass("out-of-bounds"),g.push(t),C.forEach(function(e){g.push(t+"-"+e)})}(),(w.indexOf("left")>=0||w.indexOf("right")>=0)&&(y.left=v.left=!1),(w.indexOf("top")>=0||w.indexOf("bottom")>=0)&&(y.top=v.top=!1),(v.top!==i.top||v.left!==i.left||y.top!==e.attachment.top||y.left!==e.attachment.left)&&(e.updateAttachClasses(y,v),e.trigger("update",{attachment:y,targetAttachment:v}))}),M(function(){e.options.addTargetClasses!==!1&&m(e.target,g,c),m(e.element,g,c)}),{top:o,left:n}}});var j=x.Utils,a=j.getBounds,m=j.updateClasses,M=j.defer;x.modules.push({position:function(t){var e=this,o=t.top,n=t.left,i=this.cache("element-bounds",function(){return a(e.element)}),r=i.height,s=i.width,f=this.getTargetBounds(),l=o+r,h=n+s,u=[];o<=f.bottom&&l>=f.top&&["left","right"].forEach(function(t){var e=f[t];(e===n||e===h)&&u.push(t)}),n<=f.right&&h>=f.left&&["top","bottom"].forEach(function(t){var e=f[t];(e===o||e===l)&&u.push(t)});var d=[],p=[],c=["left","top","right","bottom"];return d.push(this.getClass("abutted")),c.forEach(function(t){d.push(e.getClass("abutted")+"-"+t)}),u.length&&p.push(this.getClass("abutted")),u.forEach(function(t){p.push(e.getClass("abutted")+"-"+t)}),M(function(){e.options.addTargetClasses!==!1&&m(e.target,p,d),m(e.element,p,d)}),!0}});var B=function(){function t(t,e){var o=[],n=!0,i=!1,r=void 0;try{for(var s,a=t[Symbol.iterator]();!(n=(s=a.next()).done)&&(o.push(s.value),!e||o.length!==e);n=!0);}catch(f){i=!0,r=f}finally{try{!n&&a["return"]&&a["return"]()}finally{if(i)throw r}}return o}return function(e,o){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return t(e,o);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();return x.modules.push({position:function(t){var e=t.top,o=t.left;if(this.options.shift){var n=this.options.shift;"function"==typeof this.options.shift&&(n=this.options.shift.call(this,{top:e,left:o}));var i=void 0,r=void 0;if("string"==typeof n){n=n.split(" "),n[1]=n[1]||n[0];var s=n,a=B(s,2);i=a[0],r=a[1],i=parseFloat(i,10),r=parseFloat(r,10)}else i=n.top,r=n.left;return e+=i,o+=r,{top:e,left:o}}}}),I}); -------------------------------------------------------------------------------- /src/main/resources/static/js/uploads.js: -------------------------------------------------------------------------------- 1 | function fetchUploads() { 2 | 3 | (function ($) { 4 | var tempScrollTop = $(window).scrollTop(); 5 | $(".uploads").remove(); 6 | var url = '/api/status'; 7 | 8 | $.ajax( 9 | { 10 | url: url, 11 | async: false, 12 | 13 | success: function (json) { 14 | $.each(json, function () { 15 | var oneGB = 1024 * 1024 * 1024; 16 | var oneMB = 1024 * 1024; 17 | var oneKB = 1024; 18 | 19 | var speed; 20 | var completed; 21 | var remaining = this.totalSize - this.uploadedSize; 22 | 23 | if (this.speed > oneGB) 24 | speed = (this.speed / oneGB).toFixed(1) + ' GBps'; 25 | else if (this.speed > oneMB) 26 | speed = (this.speed / oneMB).toFixed(1) + ' MBps'; 27 | else if (this.speed > oneKB) 28 | speed = (this.speed / oneKB).toFixed(1) + ' KBps'; 29 | else 30 | speed = this.speed + " Bps"; 31 | 32 | if (this.uploadedSize > oneGB) 33 | completed = (this.uploadedSize / oneGB).toFixed(1) + ' GB'; 34 | else if (this.uploadedSize > oneMB) 35 | completed = (this.uploadedSize / oneMB).toFixed(1) + ' MB'; 36 | else if (this.uploadedSize > oneKB) 37 | completed = (this.uploadedSize / oneKB).toFixed(1) + ' KB'; 38 | else 39 | completed = this.uploadedSize + " B"; 40 | 41 | if (remaining > oneGB) 42 | remaining = (remaining / oneGB).toFixed(1) + ' GB'; 43 | else if (remaining > oneMB) 44 | remaining = (remaining / oneMB).toFixed(1) + ' MB'; 45 | else if (remaining > oneKB) 46 | remaining = (remaining / oneKB).toFixed(1) + ' KB'; 47 | else 48 | remaining = remaining + " B"; 49 | 50 | var completedPercentage = (this.uploadedSize / this.totalSize * 100).toFixed(1); 51 | 52 | var background = ''; 53 | var upload; 54 | 55 | if(this.uploadStatus == 'failed') 56 | { 57 | background = 'bg-danger'; 58 | upload = '
' + 59 | '
' + 60 | '
' + 61 | '
Name: ' + this.fileName + '
' + 62 | '
Status: ' + this.uploadStatus + '
' + 63 | '
Message: ' + this.errorMessage + '
' + 64 | '
' + 65 | '
' + 66 | '
' + completedPercentage + '%
' + 67 | '
' + 68 | '
' + 69 | '
' + 70 | '
' + 71 | '
'; 72 | } 73 | else 74 | { 75 | if (completedPercentage == 100) 76 | background = 'bg-success'; 77 | 78 | upload = '
' + 79 | '
' + 80 | '
' + 81 | '
Name: ' + this.fileName + '
' + 82 | '
Status: ' + this.uploadStatus + '
' + 83 | '
Speed: ' + speed + '
' + 84 | '
Completed: ' + completed + '
' + 85 | '
Remaining: ' + remaining + '
' + 86 | '
' + 87 | '
' + 88 | '
' + completedPercentage + '%
' + 89 | '
' + 90 | '
' + 91 | '
' + 92 | '
' + 93 | '
'; 94 | 95 | 96 | } 97 | 98 | $('.push').before(upload); 99 | }); 100 | }, 101 | error: function (xhr) { 102 | alert("An error occured: " + xhr.status + " " + xhr.statusText); 103 | } 104 | }); 105 | $(window).scrollTop(tempScrollTop); 106 | })(jQuery); 107 | 108 | } 109 | 110 | 111 | jQuery(document).ready(function ($) { 112 | $(function () { 113 | setInterval(function () { 114 | fetchUploads(); 115 | }, 1000); 116 | }); 117 | }); -------------------------------------------------------------------------------- /src/main/webapp/META-INF/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/index.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | <%@page contentType="text/html" pageEncoding="UTF-8" %> 3 | <%@taglib prefix="ui" tagdir="/WEB-INF/tags" %> 4 | 5 | 6 | 7 | 8 |
9 |
10 |
11 |
12 | 13 | 14 |

Welcome to Bhadoo Cloud Drive Uploader

15 |

This site has no registration service or user limits.

16 |

Once you login your Google Drive account you can upload any file from it's URL to your Google Drive Account.

17 |

Please Note: This site only supports direct links.

18 |

We do not save any of your data and once server restarts auth codes are cleared from our database too.


19 | 20 | 21 | 22 |
23 | 24 |

Hi, .

25 |

Congratulations! You are successfully authenticated using your Google account: .

26 | 29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/new_upload.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Created by IntelliJ IDEA. 3 | User: Dhaval 4 | Date: 27-09-2017 5 | Time: 04:22 PM 6 | To change this template use File | Settings | File Templates. 7 | --%> 8 | 9 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 10 | <%@page contentType="text/html" pageEncoding="UTF-8" %> 11 | <%@taglib prefix="ui" tagdir="/WEB-INF/tags" %> 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 |

You need to Login First

23 |


24 |
25 |
26 |
27 |
28 |
29 | 30 | 36 | 37 |
38 |
39 |
40 |
41 |
42 |
43 |

Enter File Information

44 |
45 |
46 |
47 |
48 | 49 | 51 |
52 |
53 | 54 | 57 | File name is optional. 58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 | 76 | 77 |
78 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jsp/uploads.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Document : uploads 3 | Created on : 13 Sep, 2017, 8:15:34 PM 4 | Author : Dhaval 5 | --%> 6 | 7 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 8 | <%@page contentType="text/html" pageEncoding="UTF-8" %> 9 | <%@taglib prefix="ui" tagdir="/WEB-INF/tags" %> 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 |
24 |

You need to Login First

25 |


26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 | 38 | 39 | 45 |
46 |
47 |
48 |
49 | 50 | 51 | 52 |
53 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/tags/templete.tag: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 3 | <%@tag description="The template for the front-end" pageEncoding="UTF-8" %> 4 | <%-- The list of normal or fragment attributes can be specified here: --%> 5 | <%@attribute name="head_area" fragment="true" %> 6 | <%@attribute name="body_area" fragment="true" required="true" %> 7 | <%@attribute name="javascript" fragment="true" %> 8 | 9 | 10 | 11 | 12 | 13 | Bhadoo Cloud - Upload to Google Drive 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 58 | 59 | 60 |
61 | 62 |
63 | 93 |
94 |
95 | 96 | 129 |
130 | 131 | 132 | 133 |
134 |
135 |
136 |
137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------