Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ import ExcelConnectionProperties from "./cdata/ExcelConnectionProperties.vue";
import GithubConnectionProperties from "./cdata/GithubConnectionProperties.vue";
import DocusignConnectionProperties from "./cdata/DocusignConnectionProperties.vue";
import GmailConnectionProperties from "./cdata/GmailConnectionProperties.vue";
import BamboohrConnectionProperties from "./cdata/BamboohrConnectionProperties.vue";
import SapHanaConnectionProperties from "./cdata/SapHanaConnectionProperties.vue";

export default {
components: {
ExcelConnectionProperties,
GithubConnectionProperties,
DocusignConnectionProperties,
GmailConnectionProperties,
BamboohrConnectionProperties,
SapHanaConnectionProperties,
},
props: {
formData: {
Expand All @@ -38,6 +42,8 @@ export default {
"cdata.github": "github-connection-properties",
"cdata.docusign": "docusign-connection-properties",
"cdata.gmail": "gmail-connection-properties",
"cdata.BambooHR": "bamboohr-connection-properties",
"cdata.saphana": "sap-hana-connection-properties",
},
};
},
Expand Down
242 changes: 80 additions & 162 deletions resources/js/admin/settings/components/SettingDriverAuthorization.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<template>
<div>
<!-- Authorize badge -->
<div v-if="hasAuthorizedBadge">
<b-badge
pill
:variant="isAuthorized ? 'success' : 'warning'"
>
<span v-if="isAuthorized">{{ $t('Authorized') }}</span>
<span v-else>{{ $t('Not Authorized') }}</span>
<span v-if="isAuthorized">{{ $t("Authorized") }}</span>
<span v-else>{{ $t("Not Authorized") }}</span>
</b-badge>
</div>
<div v-else>
Empty
{{ $t("Empty") }}
</div>

<!-- Connection properties modal -->
<b-modal
v-model="showModal"
class="setting-object-modal"
Expand All @@ -24,110 +27,30 @@
class="d-block"
>
<div>
<h5
v-if="setting.name"
class="mb-0"
>
{{ $t(setting.name) }}
</h5>
<h5
v-else
class="mb-0"
>
{{ setting.key }}
<h5 class="mb-0">
<span v-if="setting.name">{{ $t(setting.name) }}</span>
<span v-else>{{ $t(setting.key) }}</span>
</h5>
<small class="form-text text-muted">{{ $t('Configure the driver connection properties.') }}</small>
<small class="form-text text-muted">
{{ $t("Configure the driver connection properties.") }}
</small>
</div>
<button
type="button"
:aria-label="$t('Close')"
class="close"
:aria-label="$t('Close')"
@click="onCancel"
>
×
&times;
</button>
</template>
<div>
<b-form-group
required
:label="$t('Client ID')"
:description="formDescription('The client ID assigned when you register your application.', 'client_id', errors)"
:invalid-feedback="errorMessage('client_id', errors)"
:state="errorState('client_id', errors)"
>
<b-form-input
v-model="formData.client_id"
required
autofocus
autocomplete="off"
:state="errorState('client_id', errors)"
name="client_id"
data-cy="client_id"
/>
</b-form-group>

<b-form-group
required
:label="$t('Client Secret')"
:description="formDescription('The client secret assigned when you register your application.', 'client_secret', errors)"
:invalid-feedback="errorMessage('client_secret', errors)"
:state="errorState('client_secret', errors)"
>
<b-input-group>
<b-form-input
v-model="formData.client_secret"
required
autofocus
autocomplete="off"
trim
:type="type"
:state="errorState('client_secret', errors)"
name="client_secret"
data-cy="client_secret"
/>
<b-input-group-append>
<b-button
:aria-label="$t('Toggle Show Password')"
variant="secondary"
@click="togglePassword"
>
<i
class="fas"
:class="icon"
/>
</b-button>
</b-input-group-append>
</b-input-group>
</b-form-group>

<b-form-group
required
:label="$t('Redirect URL')"
:description="formDescription('This value must match the callback URL you specify in your app settings.', 'callback_url', errors)"
:invalid-feedback="errorMessage('callback_url', errors)"
:state="errorState('callback_url', errors)"
>
<b-input-group>
<b-form-input
v-model="formData.callback_url"
autofocus
readonly
autocomplete="off"
:state="errorState('callback_url', errors)"
name="callback_url"
data-cy="callback_url"
/>
<b-input-group-append>
<b-button
:aria-label="$t('Copy')"
variant="secondary"
@click="onCopy"
>
<i class="fas fa-copy" />
</b-button>
</b-input-group-append>
</b-input-group>
</b-form-group>
<component
:is="authSchemeToComponent(setting.config?.AuthScheme)"
:form-data="formData"
:auth-scheme="setting.config?.AuthScheme"
@updateFormData="updateFormData"
/>

<additional-driver-connection-properties
:driver-key="setting?.key"
Expand All @@ -145,20 +68,20 @@
data-cy="cancel-button"
@click="onCancel"
>
{{ $t('Cancel') }}
{{ $t("Cancel") }}
</button>
<button
type="button"
class="btn btn-secondary ml-3"
data-cy="authorize-button"
:disabled="isButtonDisabled"
@click="onSave"
>
{{ $t('Authorize') }}
{{ $t("Authorize") }}
</button>
</div>
</b-modal>

<!-- Authorizing modal -->
<b-modal
v-model="showAuthorizingModal"
class="setting-object-modal"
Expand All @@ -169,26 +92,33 @@
no-fade
>
<div class="text-center">
<h3>{{ $t('Connecting Driver') }}</h3>
<h3>{{ $t("Connecting Driver") }}</h3>
<i class="fas fa-circle-notch fa-spin fa-3x p-0 text-primary" />
</div>
</b-modal>
</div>
</template>

<script>
// eslint-disable-next-line import/no-unresolved
import { FormErrorsMixin, Required } from "SharedComponents";
import settingMixin from "../mixins/setting";
import AdditionalDriverConnectionProperties from "./AdditionalDriverConnectionProperties.vue";
import OauthConnectionProperties from "./cdata/OauthConnectionProperties.vue";
import NoneConnectionProperties from "./cdata/NoneConnectionProperties.vue";
import PasswordConnectionProperties from "./cdata/PasswordConnectionProperties.vue";

export default {
components: { AdditionalDriverConnectionProperties },
components: {
AdditionalDriverConnectionProperties,
OauthConnectionProperties,
NoneConnectionProperties,
PasswordConnectionProperties,
},
mixins: [settingMixin, FormErrorsMixin, Required],
props: {
setting: {
type: [Object, null],
default: null,
default: () => ({}),
},
value: {
type: Object,
Expand All @@ -198,19 +128,19 @@ export default {
data() {
return {
input: "",
formData: {
client_id: "",
client_secret: "",
callback_url: "",
},
formData: {},
selected: null,
showModal: false,
showAuthorizingModal: false,
transformed: null,
errors: {},
isInvalid: true,
type: "password",
resetData: true,
componentsMap: {
OAuth: "oauth-connection-properties",
None: "none-connection-properties",
Password: "password-connection-properties",
Basic: "password-connection-properties",
},
};
},
computed: {
Expand All @@ -227,57 +157,18 @@ export default {
}
return false;
},
changed() {
return JSON.stringify(this.formData) !== JSON.stringify(this.transformed);
},
icon() {
if (this.type === "password") {
return "fa-eye";
}
return "fa-eye-slash";
},
isButtonDisabled() {
return this.isInvalid || (this.isAuthorized && !this.changed);
},
},
watch: {
formData: {
handler() {
this.isInvalid = this.validateData();
},
deep: true,
},
},
mounted() {
if (this.value === null) {
this.resetFormData();
} else {
this.formData = this.value;
}
this.isInvalid = this.validateData();
this.transformed = this.copy(this.formData);
},
methods: {
onCopy() {
navigator.clipboard.writeText(this.formData.callback_url).then(() => {
ProcessMaker.alert(this.$t("The setting was copied to your clipboard."), "success");
}, () => {
ProcessMaker.alert(this.$t("The setting was not copied to your clipboard."), "danger");
});
},
togglePassword() {
if (this.type === "text") {
this.type = "password";
} else {
this.type = "text";
}
},
validateData() {
// Check if client_id and client_secret are empty
const clientIdEmpty = _.isEmpty(this.formData.client_id);
const clientSecretEmpty = _.isEmpty(this.formData.client_secret);

return _.isEmpty(this.formData) || clientIdEmpty || clientSecretEmpty;
authSchemeToComponent(scheme) {
return this.componentsMap[scheme] || null;
},
onCancel() {
this.showModal = false;
Expand All @@ -294,14 +185,48 @@ export default {
onModalHidden() {
this.resetFormData();
},
generateCallbackUrl(item) {
if (item.config.AuthScheme === "OAuth") {
const name = item.key.split("cdata.")[1];
const appUrl = document.head.querySelector("meta[name=\"app-url\"]").content;

this.formData.callback_url = `${appUrl}/external-integrations/${name}`;
}
},
authorizeConnection() {
this.showAuthorizingModal = true;
this.showModal = false;
this.resetData = false;
ProcessMaker.apiClient.post(`settings/${this.setting.id}/get-oauth-url`, this.formData)

if (this.setting.config.AuthScheme === "OAuth") {
this.authorizeOAuthConnection();
} else {
this.authorizeNoneConnection();
}
},
authorizeOAuthConnection() {
ProcessMaker.apiClient
.post(`settings/${this.setting.id}/get-oauth-url`, this.formData)
.then((response) => {
window.location = response.data?.url;
})
.catch((error) => {
const errorMessage = error.response?.data?.message || error.message;
ProcessMaker.alert(errorMessage, "danger");
})
.finally(() => {
this.showModal = true;
this.showAuthorizingModal = false;
});
},
authorizeNoneConnection() {
ProcessMaker.apiClient
.post(`settings/${this.setting.id}/authorize-driver`, this.formData)
.then((response) => {
window.location = response.data.url;
this.showModal = false;
this.showAuthorizingModal = true;
})
.catch((error) => {
const errorMessage = error.response?.data?.message || error.message;
ProcessMaker.alert(errorMessage, "danger");
Expand All @@ -310,18 +235,11 @@ export default {
});
},
onSave() {
const driver = this.setting.key.split("cdata.")[1];

this.formData.driver = driver;
this.formData.name = this.setting.config?.name;
this.formData.driver = this.setting.config?.driver;
this.transformed = { ...this.formData };
this.authorizeConnection();
},
generateCallbackUrl(data) {
const name = data.key.split("cdata.")[1];
const appUrl = document.head.querySelector("meta[name=\"app-url\"]").content;

this.formData.callback_url = `${appUrl}/external-integrations/${name}`;
},
resetFormData() {
if (this.resetData) {
this.formData = {
Expand Down
Loading