Skip to content

Commit

Permalink
pkp/pkp-lib#9661 Allow Journal Managers to invite users to adopt a ro…
Browse files Browse the repository at this point in the history
…le - ORCiD
  • Loading branch information
ewhanson committed Dec 20, 2024
1 parent ccd51cd commit 91931b8
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/components/Modal/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<div class="flex min-h-12 items-center">
<DialogTitle
v-if="title"
class="m-0 inline-flex min-w-[1px] items-center overflow-x-hidden overflow-ellipsis whitespace-nowrap px-8 pt-12 text-4xl-bold"
class="m-0 inline-flex min-w-[1px] items-center overflow-x-hidden overflow-ellipsis px-8 pt-12 text-4xl-bold"
:class="icon ? 'pb-5' : 'pb-8'"
>
<div v-if="icon" :class="iconStyles">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<p>{{ message }}</p>
<div>
<ul role="list">
<li>{{ t('user.email') }}:{{ email }}</li>
<li>{{ t('userInvitation.roleTable.role') }}:{{ roles }}</li>
<li>{{ t('common.status') }}:{{ status }}</li>
<li>{{ t('user.affiliation') }}:{{ affiliation }}</li>
<li>{{ t('user.email') }}: {{ email }}</li>
<li>{{ t('userInvitation.roleTable.role') }}: {{ roles }}</li>
<li>{{ t('common.status') }}: {{ status }}</li>
<li>{{ t('user.affiliation') }}: {{ affiliation }}</li>
</ul>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export const useUserInvitationManagerStore = defineComponentStore(
}),
affiliation: invitationObj.existingUser
? localize(invitationObj.existingUser.affiliation)
: invitationObj.affiliation,
: localize(invitationObj.affiliation),
},
actions: [
{
Expand Down
2 changes: 1 addition & 1 deletion src/pages/acceptInvitation/AcceptInvitationPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
{{ t('common.back') }}
</PkpButton>
<PkpButton
v-if="!store.isOnFirstStep"
v-if="store.currentStep.id !== 'verifyOrcid'"
:is-primary="true"
@click="store.nextStep"
>
Expand Down
101 changes: 98 additions & 3 deletions src/pages/acceptInvitation/AcceptInvitationPageStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const useAcceptInvitationPageStore = defineComponentStore(

const email = ref(null);
const userId = ref(null);
const existingUser = ref(null);

/** All Errors */
const errors = ref({});
Expand Down Expand Up @@ -82,8 +83,37 @@ export const useAcceptInvitationPageStore = defineComponentStore(
if (data.value.givenName) {
updateAcceptInvitationPayload('givenName', data.value.givenName); //if not check this override the multilingual structure
}
if (data.value.affiliation) {
updateAcceptInvitationPayload('affiliation', data.value.affiliation);
}
updateAcceptInvitationPayload('userCountry', data.value.country);
updateAcceptInvitationPayload('userOrcid', data.value.orcid);

updateAcceptInvitationPayload('orcid', data.value.orcid);
updateAcceptInvitationPayload(
'orcidIsVerified',
data.value.orcidIsVerified,
);
updateAcceptInvitationPayload(
'orcidAccessDenied',
data.value.orcidAccessDenied,
);
updateAcceptInvitationPayload(
'orcidAccessToken',
data.value.orcidAccessToken,
);
updateAcceptInvitationPayload(
'orcidAccessScope',
data.value.orcidAccessScope,
);
updateAcceptInvitationPayload(
'orcidRefreshToken',
data.value.orcidRefreshToken,
);
updateAcceptInvitationPayload(
'orcidAccessExpiresOn',
data.value.orcidAccessExpiresOn,
);

updateAcceptInvitationPayload(
'userGroupsToAdd',
data.value.userGroupsToAdd,
Expand All @@ -93,9 +123,11 @@ export const useAcceptInvitationPageStore = defineComponentStore(
userId.value ? true : false,
);
// add username to invitation payload for validations
updateAcceptInvitationPayload('username', null);
updateAcceptInvitationPayload('username', data.value.username);
// add password to invitation payload for validations
updateAcceptInvitationPayload('password', null);

existingUser.value = data.existingUser;
errors.value = [];
if (steps.value.length === 0) {
await submit();
Expand All @@ -107,6 +139,61 @@ export const useAcceptInvitationPageStore = defineComponentStore(
acceptInvitationPayload.value[fieldName] = value;
}

/**
* Sets ORCID data in invitation payload. If data is null, all ORCID related fields will be set to null/zero value.
*
* @param {Object|null} data - The ORCID OAuth data object.
* @param {string} data.orcid - The ORCID URL of the user.
* @param {boolean} data.orcidIsVerified - Indicates if the user's ORCID is verified.
* @param {null|string} data.orcidAccessDenied - Indicates if access to ORCID is denied (null if not denied).
* @param {string} data.orcidAccessToken - The access token for ORCID API.
* @param {string} data.orcidAccessScope - The scope of access for the ORCID API.
* @param {string} data.orcidRefreshToken - The refresh token for obtaining new access tokens.
* @param {string} data.orcidAccessExpiresOn - The expiration date and time of the access token in ISO format.
* @param data
*/
function setOrcidData(data) {
const fields = [
'orcid',
'orcidIsVerified',
'orcidAccessDenied',
'orcidAccessToken',
'orcidAccessScope',
'orcidRefreshToken',
'orcidAccessExpiresOn',
];
const isDataNull = data === null;
fields.forEach((fieldName) => {
acceptInvitationPayload.value[fieldName] = isDataNull
? null
: data[fieldName];
});
}

const hasValidOrcid = computed(() => {
if (acceptInvitationPayload.value['orcidIsVerified']) {
return true;
} else if (existingUser.value.orcidIsVerified) {
return true;
}

return false;
});

const orcidUri = computed(() => {
const invitationOrcid = acceptInvitationPayload.value['orcid'];
if (invitationOrcid) {
return invitationOrcid;
}

const userOrcid = existingUser.value['orcid'];
if (userOrcid) {
return userOrcid;
}

return null;
});

/** Steps */
const currentStepId = ref(
pageInitConfig.steps[0] ? pageInitConfig.steps[0].id : '',
Expand Down Expand Up @@ -311,7 +398,12 @@ export const useAcceptInvitationPageStore = defineComponentStore(
method: 'PUT',
body: {invitationData: invitationRequestPayload.value},
});
if (!acceptInvitationPayload.value.privacyStatement) {
// FIXME: Privacy statement check blocks ORCID from saving before hand.
// Can this check be moved outside of update payload check?
if (
!acceptInvitationPayload.value.privacyStatement &&
currentStep.value.id !== 'verifyOrcid'
) {
errors.value = {
privacyStatement: [t('acceptInvitation.privacyStatement.validation')],
};
Expand Down Expand Up @@ -412,13 +504,15 @@ export const useAcceptInvitationPageStore = defineComponentStore(
//computed
currentStep,
currentStepIndex,
hasValidOrcid,
isOnFirstStep,
isOnLastStep,
isValid,
pageTitle,
startedSteps,
stepTitle,
openStep,
orcidUri,
steps,
pageTitleDescription,
errors,
Expand All @@ -428,6 +522,7 @@ export const useAcceptInvitationPageStore = defineComponentStore(
//methods
nextStep,
previousStep,
setOrcidData,
updateAcceptInvitationPayload,
cancel,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div class="my-4">
<FieldText
:label="t('user.username')"
:value="fields.username"
:value="store.acceptInvitationPayload.username"
:description="t('acceptInvitation.usernameField.description')"
:is-required="true"
name="username"
Expand Down Expand Up @@ -61,7 +61,7 @@ const props = defineProps({
validateFields: {type: Array, required: true},
});
const {t} = useLocalize();
const fields = ref({username: '', password: '', privacyStatement: false});
const fields = ref({password: '', privacyStatement: false});
const {pageUrl} = useUrl('about/privacy');
const options = [
{
Expand Down
42 changes: 38 additions & 4 deletions src/pages/acceptInvitation/AcceptInvitationVerifyOrcid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,57 @@

<script setup>
import PkpButton from '@/components/Button/Button.vue';
import {defineProps} from 'vue';
import {defineProps, onMounted} from 'vue';
import {useLocalize} from '@/composables/useLocalize';
import {useAcceptInvitationPageStore} from './AcceptInvitationPageStore';
defineProps({});
const props = defineProps({
orcidUrl: {type: String, required: true},
orcidOAuthUrl: {type: String, required: true},
});
const store = useAcceptInvitationPageStore();
const {t} = useLocalize();
onMounted(() => {
pkp.eventBus.$on('addOrcidInvitationData', (data) => setOrcidData(data));
});
/**
* Processes ORCID data for an invitation.
*
* @param {Object|null} data - The ORCID OAuth data object.
* @param {string} data.orcid - The ORCID URL of the user.
* @param {boolean} data.orcidIsVerified - Indicates if the user's ORCID is verified.
* @param {null|string} data.orcidAccessDenied - Indicates if access to ORCID is denied (null if not denied).
* @param {string} data.orcidAccessToken - The access token for ORCID API.
* @param {string} data.orcidAccessScope - The scope of access for the ORCID API.
* @param {string} data.orcidRefreshToken - The refresh token for obtaining new access tokens.
* @param {string} data.orcidAccessExpiresOn - The expiration date and time of the access token in ISO format.
*
* @returns {void}
*/
async function setOrcidData(data) {
store.setOrcidData(data);
await store.nextStep();
}
/**
* Go to the next step
*/
function skipOrcid() {
delete store.acceptInvitationPayload.userOrcid;
store.openStep(store.steps[1 + store.currentStepIndex].id);
}
/**
* Initiates ORCID OAuth granting flow
*/
function verifyOrcid() {
store.openStep(store.steps[1 + store.currentStepIndex].id);
const oauthWindow = window.open(
props.orcidOAuthUrl,
'_blank',
'toolbar=no, scrollbars=yes, width=540, height=700, top=500, left=500',
);
oauthWindow.opener = self;
}
</script>

0 comments on commit 91931b8

Please sign in to comment.