Basic prototype

This commit is contained in:
Connor Yoh 2025-03-26 18:00:14 +00:00
parent 9951695eb1
commit 9362bee73b
8 changed files with 204 additions and 0 deletions

View File

@ -195,4 +195,25 @@ public class AppConfig {
public String uuid() {
return applicationProperties.getAutomaticallyGenerated().getUUID();
}
@Bean(name = "GoogleDriveEnabled")
public boolean googleDriveEnabled() {
return applicationProperties.getPremium().isEnabled()
&& applicationProperties.getPremium().getProFeatures().getGoogleDrive().isEnabled();
}
@Bean(name = "GoogleDriveClientId")
public String googleDriveClientId() {
return applicationProperties.getPremium().getProFeatures().getGoogleDrive().getClientId();
}
@Bean(name = "GoogleDriveApiKey")
public String googleDriveApiKey() {
return applicationProperties.getPremium().getProFeatures().getGoogleDrive().getApiKey();
}
@Bean(name = "GoogleDriveAppId")
public String googleDriveAppId() {
return applicationProperties.getPremium().getProFeatures().getGoogleDrive().getAppId();
}
}

View File

@ -431,6 +431,7 @@ public class ApplicationProperties {
public static class ProFeatures {
private boolean ssoAutoLogin;
private CustomMetadata customMetadata = new CustomMetadata();
private GoogleDrive googleDrive = new GoogleDrive();
@Data
public static class CustomMetadata {
@ -449,6 +450,26 @@ public class ApplicationProperties {
: producer;
}
}
@Data
public static class GoogleDrive {
private boolean enabled;
private String clientId;
private String apiKey;
private String appId;
public String getClientId() {
return clientId == null || clientId.trim().isEmpty() ? "" : clientId;
}
public String getApiKey() {
return apiKey == null || apiKey.trim().isEmpty() ? "" : apiKey;
}
public String getAppId() {
return appId == null || appId.trim().isEmpty() ? "" : appId;
}
}
}
@Data

View File

@ -72,6 +72,11 @@ premium:
author: username
creator: Stirling-PDF
producer: Stirling-PDF
googleDrive:
enabled: false
clientId: ''
apiKey: ''
appId: ''
legal:
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder

View File

@ -271,3 +271,14 @@
align-items: center;
z-index: 9999;
}
.googleDriveButton {
position: absolute;
bottom: 15px;
right: 5px;
width: 5%;
}
.googleDriveButton img {
width:100%
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><path fill="#1e88e5" d="M38.59,39c-0.535,0.93-0.298,1.68-1.195,2.197C36.498,41.715,35.465,42,34.39,42H13.61 c-1.074,0-2.106-0.285-3.004-0.802C9.708,40.681,9.945,39.93,9.41,39l7.67-9h13.84L38.59,39z"/><path fill="#fbc02d" d="M27.463,6.999c1.073-0.002,2.104-0.716,3.001-0.198c0.897,0.519,1.66,1.27,2.197,2.201l10.39,17.996 c0.537,0.93,0.807,1.967,0.808,3.002c0.001,1.037-1.267,2.073-1.806,3.001l-11.127-3.005l-6.924-11.993L27.463,6.999z"/><path fill="#e53935" d="M43.86,30c0,1.04-0.27,2.07-0.81,3l-3.67,6.35c-0.53,0.78-1.21,1.4-1.99,1.85L30.92,30H43.86z"/><path fill="#4caf50" d="M5.947,33.001c-0.538-0.928-1.806-1.964-1.806-3c0.001-1.036,0.27-2.073,0.808-3.004l10.39-17.996 c0.537-0.93,1.3-1.682,2.196-2.2c0.897-0.519,1.929,0.195,3.002,0.197l3.459,11.009l-6.922,11.989L5.947,33.001z"/><path fill="#1565c0" d="M17.08,30l-6.47,11.2c-0.78-0.45-1.46-1.07-1.99-1.85L4.95,33c-0.54-0.93-0.81-1.96-0.81-3H17.08z"/><path fill="#2e7d32" d="M30.46,6.8L24,18L17.53,6.8c0.78-0.45,1.66-0.73,2.6-0.79L27.46,6C28.54,6,29.57,6.28,30.46,6.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -80,6 +80,23 @@ function setupFileInput(chooser) {
overlay = false;
}
const googleDriveFileListener = function (e) {
const googleDriveFile = e.detail;
const fileInput = document.getElementById(elementId);
if (fileInput?.hasAttribute('multiple')) {
allFiles.push(googleDriveFile);
} else if (fileInput) {
allFiles = [googleDriveFile];
}
const dataTransfer = new DataTransfer();
allFiles.forEach((file) => dataTransfer.items.add(file));
fileInput.files = dataTransfer.files;
fileInput.dispatchEvent(new CustomEvent('change', { bubbles: true, detail: { source: 'drag-drop' } }));
}
const dropListener = function (e) {
e.preventDefault();
@ -130,6 +147,7 @@ function setupFileInput(chooser) {
document.body.addEventListener('dragenter', dragenterListener);
document.body.addEventListener('dragleave', dragleaveListener);
document.body.addEventListener('drop', dropListener);
document.body.addEventListener('googleDriveFilePicked', googleDriveFileListener);
$('#' + elementId).on('change', async function (e) {
let element = e.target;

View File

@ -0,0 +1,111 @@
const SCOPES = 'https://www.googleapis.com/auth/drive.readonly';
const SESSION_STORAGE_ID = "googleDrivePickerAccessToken"
let tokenClient;
let accessToken = sessionStorage.getItem(SESSION_STORAGE_ID);
/**
* Callback after api.js is loaded.
*/
function gapiLoaded() {
gapi.load('client:picker', initializePicker);
}
/**
* Callback after the API client is loaded. Loads the
* discovery doc to initialize the API.
*/
async function initializePicker() {
await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest');
}
/**
* Callback after Google Identity Services are loaded.
*/
function gisLoaded() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: CLIENT_ID,
scope: SCOPES,
callback: '', // defined later
});
}
/**
* Sign in the user upon button click.
*/
function handleAuthClick() {
tokenClient.callback = (response) => {
if (response.error !== undefined) {
throw (response);
}
accessToken = response.access_token;
sessionStorage.setItem(SESSION_STORAGE_ID, accessToken);
createPicker();
};
tokenClient.requestAccessToken({prompt: accessToken === null? 'consent' : ''});
}
/**
* Sign out the user upon button click.
*/
function signOut() {
if (accessToken) {
sessionStorage.removeItem(SESSION_STORAGE_ID);
google.accounts.oauth2.revoke(accessToken);
accessToken = null;
}
}
/**
* Create and render a Picker object for searching images.
*/
function createPicker() {
const picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setDeveloperKey(API_KEY)
.setAppId(APP_ID)
.setOAuthToken(accessToken)
.addView(
new google.picker.DocsView()
.setIncludeFolders(true)
.setMimeTypes('application/pdf,image/png,image/jpg,image/jpeg,image/svg')
)
.addView(
new google.picker.DocsView()
.setIncludeFolders(true)
.setMimeTypes('application/pdf,image/*')
.setEnableDrives(true)
)
.setCallback(pickerCallback)
.setTitle('Stirling PDF - Google Drive')
.build();
picker.setVisible(true);
}
/**
* Displays the file details of the user's selection.
* @param {object} data - Containers the user selection from the picker
*/
async function pickerCallback(data) {
if (data.action === google.picker.Action.PICKED) {
data[google.picker.Response.DOCUMENTS].forEach(async pickedFile => {
const fileId = pickedFile[google.picker.Document.ID];
console.log(fileId);
const res = await gapi.client.drive.files.get({
'fileId': fileId,
'alt': 'media',
});
var file = new File([new Uint8Array(res.body.length).map((_, i) => res.body.charCodeAt(i))],
pickedFile.name, {
type: pickedFile.mimeType,
lastModified: pickedFile.lastModified,
endings: pickedFile.endings
} )
document.body.dispatchEvent(new CustomEvent("googleDriveFilePicked", {detail: file}));
});
}
}

View File

@ -245,6 +245,9 @@
<div th:text="#{fileChooser.or}" style="margin-right: 5px"></div>
<div th:text="#{fileChooser.dragAndDrop}" id="dragAndDrop"></div>
</div>
</div>
<div th:if="${@GoogleDriveEnabled == true}" id="google-drive-button" class="googleDriveButton" onclick="handleAuthClick()">
<img th:src="@{'/images/google-drive.svg'}" alt="google drive">
</div>
<div class="selected-files flex-wrap"></div>
</div>
@ -258,4 +261,17 @@
</div>
</div>
<script th:src="@{'/js/fileInput.js'}" type="module"></script>
<div th:if="${@GoogleDriveEnabled == true}" >
<script type="text/javascript" th:src="@{'/js/googleFilePicker.js'}"></script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
<script th:inline="javascript">
const CLIENT_ID = /*[[${@GoogleDriveClientId}]]*/ null;
const API_KEY = /*[[${@GoogleDriveApiKey}]]*/ null;
const APP_ID = /*[[${@GoogleDriveAppId}]]*/ null;
</script>
</div>
</th:block>