@windward/integrations 0.0.12 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Content/Blocks/ExternalIntegration/LtiConsumer.vue +136 -20
- package/components/ExternalIntegration/Driver/Lti1p1/ManageConsumer.vue +7 -5
- package/components/ExternalIntegration/Driver/Lti1p1/ManageConsumers.vue +3 -2
- package/components/ExternalIntegration/Driver/Lti1p3/ManageConsumer.vue +284 -0
- package/components/ExternalIntegration/Driver/Lti1p3/ManageConsumers.vue +229 -0
- package/components/ExternalIntegration/Driver/Lti1p3/ManageProvider.vue +14 -0
- package/components/ExternalIntegration/Driver/Lti1p3/ViewConsumer.vue +224 -0
- package/components/ExternalIntegration/Driver/ManageLti1p3.vue +3 -3
- package/components/Integration/Driver/ManageAtutor.vue +7 -13
- package/components/Integration/Driver/ManageBase.vue +0 -7
- package/components/Integration/Driver/ManageResourcespace.vue +6 -0
- package/components/Integration/JobLog.vue +4 -3
- package/components/SecretField.vue +16 -1
- package/components/Settings/ExternalIntegration/LtiConsumerSettings.vue +72 -30
- package/i18n/en-US/components/content/blocks/external_integration/lti_consumer.ts +7 -0
- package/i18n/en-US/components/external_integration/driver/lti1p3.ts +4 -0
- package/i18n/en-US/components/integration/driver.ts +2 -2
- package/i18n/en-US/components/settings/external_integration/lti_consumer.ts +3 -0
- package/i18n/es-ES/components/content/blocks/external_integration/index.ts +5 -0
- package/i18n/es-ES/components/content/blocks/external_integration/lti_consumer.ts +17 -0
- package/i18n/es-ES/components/content/blocks/index.ts +5 -0
- package/i18n/es-ES/components/content/index.ts +5 -0
- package/i18n/es-ES/components/external_integration/driver/lti1p1.ts +14 -0
- package/i18n/es-ES/components/external_integration/driver/lti1p3.ts +23 -0
- package/i18n/es-ES/components/external_integration/index.ts +31 -0
- package/i18n/es-ES/components/external_integration/provider_target.ts +9 -0
- package/i18n/es-ES/components/file_import/index.ts +5 -0
- package/i18n/es-ES/components/file_import/resourcespace.ts +4 -0
- package/i18n/es-ES/components/index.ts +15 -0
- package/i18n/es-ES/components/integration/driver.ts +45 -0
- package/i18n/es-ES/components/integration/index.ts +9 -0
- package/i18n/es-ES/components/integration/job.ts +23 -0
- package/i18n/es-ES/components/integration/job_log.ts +24 -0
- package/i18n/es-ES/components/navigation/index.ts +5 -0
- package/i18n/es-ES/components/navigation/integrations.ts +8 -0
- package/i18n/es-ES/components/settings/external_integration/index.ts +5 -0
- package/i18n/es-ES/components/settings/external_integration/lti_consumer.ts +10 -0
- package/i18n/es-ES/components/settings/index.ts +5 -0
- package/i18n/es-ES/index.ts +16 -0
- package/i18n/es-ES/modules/index.ts +5 -0
- package/i18n/es-ES/pages/course/external_integration/index.ts +6 -0
- package/i18n/es-ES/pages/course/index.ts +5 -0
- package/i18n/es-ES/pages/importContent.ts +3 -0
- package/i18n/es-ES/pages/importCourse.ts +13 -0
- package/i18n/es-ES/pages/index.ts +13 -0
- package/i18n/es-ES/pages/login/index.ts +5 -0
- package/i18n/es-ES/pages/login/lti.ts +21 -0
- package/i18n/es-ES/pages/vendor.ts +11 -0
- package/i18n/es-ES/shared/content_blocks.ts +8 -0
- package/i18n/es-ES/shared/error.ts +10 -0
- package/i18n/es-ES/shared/file.ts +5 -0
- package/i18n/es-ES/shared/index.ts +15 -0
- package/i18n/es-ES/shared/menu.ts +3 -0
- package/i18n/es-ES/shared/permission.ts +31 -0
- package/i18n/es-ES/shared/settings.ts +5 -0
- package/i18n/index.ts +11 -0
- package/i18n/sv-SE/components/content/blocks/external_integration/index.ts +5 -0
- package/i18n/sv-SE/components/content/blocks/external_integration/lti_consumer.ts +17 -0
- package/i18n/sv-SE/components/content/blocks/index.ts +5 -0
- package/i18n/sv-SE/components/content/index.ts +5 -0
- package/i18n/sv-SE/components/external_integration/driver/lti1p1.ts +14 -0
- package/i18n/sv-SE/components/external_integration/driver/lti1p3.ts +22 -0
- package/i18n/sv-SE/components/external_integration/index.ts +31 -0
- package/i18n/sv-SE/components/external_integration/provider_target.ts +9 -0
- package/i18n/sv-SE/components/file_import/index.ts +5 -0
- package/i18n/sv-SE/components/file_import/resourcespace.ts +4 -0
- package/i18n/sv-SE/components/index.ts +15 -0
- package/i18n/sv-SE/components/integration/driver.ts +44 -0
- package/i18n/sv-SE/components/integration/index.ts +9 -0
- package/i18n/sv-SE/components/integration/job.ts +23 -0
- package/i18n/sv-SE/components/integration/job_log.ts +24 -0
- package/i18n/sv-SE/components/navigation/index.ts +5 -0
- package/i18n/sv-SE/components/navigation/integrations.ts +8 -0
- package/i18n/sv-SE/components/settings/external_integration/index.ts +5 -0
- package/i18n/sv-SE/components/settings/external_integration/lti_consumer.ts +10 -0
- package/i18n/sv-SE/components/settings/index.ts +5 -0
- package/i18n/sv-SE/index.ts +16 -0
- package/i18n/sv-SE/modules/index.ts +5 -0
- package/i18n/sv-SE/pages/course/external_integration/index.ts +6 -0
- package/i18n/sv-SE/pages/course/index.ts +5 -0
- package/i18n/sv-SE/pages/importContent.ts +3 -0
- package/i18n/sv-SE/pages/importCourse.ts +13 -0
- package/i18n/sv-SE/pages/index.ts +13 -0
- package/i18n/sv-SE/pages/login/index.ts +5 -0
- package/i18n/sv-SE/pages/login/lti.ts +21 -0
- package/i18n/sv-SE/pages/vendor.ts +11 -0
- package/i18n/sv-SE/shared/content_blocks.ts +8 -0
- package/i18n/sv-SE/shared/error.ts +9 -0
- package/i18n/sv-SE/shared/file.ts +5 -0
- package/i18n/sv-SE/shared/index.ts +15 -0
- package/i18n/sv-SE/shared/menu.ts +3 -0
- package/i18n/sv-SE/shared/permission.ts +31 -0
- package/i18n/sv-SE/shared/settings.ts +5 -0
- package/models/ExternalIntegration/{Lti1p1Consumer.ts → LtiConsumer.ts} +1 -1
- package/package.json +1 -1
- package/plugin.js +3 -4
- package/test/Feature/LocaleKeys.spec.js +9 -0
- package/test/__mocks__/componentsMock.js +12 -0
|
@@ -1,23 +1,52 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<div v-if="!missing && consumer">
|
|
4
|
-
<
|
|
5
|
-
<TextViewer v-model="consumer.description"></TextViewer>
|
|
4
|
+
<h3>{{ block.metadata.config.title }}</h3>
|
|
6
5
|
|
|
7
|
-
<
|
|
8
|
-
v-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
<TextViewer
|
|
7
|
+
v-model="block.metadata.config.instructions"
|
|
8
|
+
></TextViewer>
|
|
9
|
+
|
|
10
|
+
<v-row
|
|
11
|
+
v-if="
|
|
12
|
+
consumer.enabled &&
|
|
13
|
+
block.metadata.config.launch_type !== 'inline'
|
|
14
|
+
"
|
|
15
|
+
class="d-flex justify-center"
|
|
12
16
|
>
|
|
13
|
-
<v-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
<v-col class="d-flex justify-center col-md-4">
|
|
18
|
+
<v-btn
|
|
19
|
+
v-if="
|
|
20
|
+
block.metadata.config.launch_type === 'new_window'
|
|
21
|
+
"
|
|
22
|
+
color="primary"
|
|
23
|
+
block
|
|
24
|
+
@click="onLaunch"
|
|
25
|
+
>
|
|
26
|
+
{{
|
|
27
|
+
$t(
|
|
28
|
+
'windward.integrations.components.content.blocks.external_integration.lti_consumer.launch_in_' +
|
|
29
|
+
block.metadata.config.launch_type,
|
|
30
|
+
[consumer.name]
|
|
31
|
+
)
|
|
32
|
+
}}
|
|
33
|
+
</v-btn>
|
|
34
|
+
<v-btn
|
|
35
|
+
v-else
|
|
36
|
+
color="primary"
|
|
37
|
+
block
|
|
38
|
+
@click="setLaunchTimer(100)"
|
|
39
|
+
>
|
|
40
|
+
{{
|
|
41
|
+
$t(
|
|
42
|
+
'windward.integrations.components.content.blocks.external_integration.lti_consumer.launch_in_' +
|
|
43
|
+
block.metadata.config.launch_type,
|
|
44
|
+
[consumer.name]
|
|
45
|
+
)
|
|
46
|
+
}}
|
|
47
|
+
</v-btn>
|
|
48
|
+
</v-col>
|
|
49
|
+
</v-row>
|
|
21
50
|
|
|
22
51
|
<v-alert v-if="!consumer.enabled" type="error">
|
|
23
52
|
{{
|
|
@@ -33,6 +62,7 @@
|
|
|
33
62
|
:target="target"
|
|
34
63
|
:action="launchData.target || ''"
|
|
35
64
|
:method="launchData.method || 'POST'"
|
|
65
|
+
@submit.prevent
|
|
36
66
|
>
|
|
37
67
|
<input
|
|
38
68
|
v-for="(value, key) of launchData.payload"
|
|
@@ -50,6 +80,35 @@
|
|
|
50
80
|
:name="frameId"
|
|
51
81
|
class="launch-frame"
|
|
52
82
|
></iframe>
|
|
83
|
+
<Dialog
|
|
84
|
+
v-if="block.metadata.config.launch_type === 'modal'"
|
|
85
|
+
v-model="openModal"
|
|
86
|
+
:trigger="false"
|
|
87
|
+
persistent
|
|
88
|
+
max-width="1350px"
|
|
89
|
+
@click:close="closeModal"
|
|
90
|
+
>
|
|
91
|
+
<template #title>{{ block.metadata.config.title }}</template>
|
|
92
|
+
<template #trigger>
|
|
93
|
+
<v-icon small>mdi-pencil</v-icon>
|
|
94
|
+
<span class="sr-only">{{
|
|
95
|
+
$t(
|
|
96
|
+
'windward.integrations.components.external_integration.driver.lti1p1.edit'
|
|
97
|
+
)
|
|
98
|
+
}}</span>
|
|
99
|
+
</template>
|
|
100
|
+
<template #form="{ on, attrs }">
|
|
101
|
+
<iframe
|
|
102
|
+
v-if="launched"
|
|
103
|
+
v-bind="attrs"
|
|
104
|
+
:name="frameId"
|
|
105
|
+
class="launch-frame"
|
|
106
|
+
v-on="on"
|
|
107
|
+
></iframe>
|
|
108
|
+
|
|
109
|
+
<div v-else></div>
|
|
110
|
+
</template>
|
|
111
|
+
</Dialog>
|
|
53
112
|
</div>
|
|
54
113
|
|
|
55
114
|
<v-alert v-if="missing" type="error">
|
|
@@ -88,17 +147,18 @@
|
|
|
88
147
|
<script>
|
|
89
148
|
import _ from 'lodash'
|
|
90
149
|
import { mapGetters } from 'vuex'
|
|
91
|
-
import
|
|
150
|
+
import LtiConsumer from '../../../../models/ExternalIntegration/LtiConsumer'
|
|
92
151
|
import Crypto from '~/helpers/Crypto'
|
|
93
152
|
import TextViewer from '~/components/Text/TextViewer.vue'
|
|
94
153
|
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
95
154
|
import Course from '~/models/Course'
|
|
96
155
|
import Organization from '~/models/Organization'
|
|
97
156
|
import Enrollment from '~/models/Enrollment'
|
|
157
|
+
import Dialog from '~/components/Dialog.vue'
|
|
98
158
|
|
|
99
159
|
export default {
|
|
100
160
|
name: 'ContentBlockExternalIntegrationLti1p1Consumer',
|
|
101
|
-
components: { TextViewer },
|
|
161
|
+
components: { TextViewer, Dialog },
|
|
102
162
|
extends: BaseContentBlock,
|
|
103
163
|
data() {
|
|
104
164
|
return {
|
|
@@ -109,6 +169,8 @@ export default {
|
|
|
109
169
|
frameId: Crypto.id() + '_frame',
|
|
110
170
|
missing: false,
|
|
111
171
|
launched: false,
|
|
172
|
+
launchTimeout: {},
|
|
173
|
+
openModal: false,
|
|
112
174
|
}
|
|
113
175
|
},
|
|
114
176
|
async fetch() {
|
|
@@ -123,12 +185,27 @@ export default {
|
|
|
123
185
|
}),
|
|
124
186
|
},
|
|
125
187
|
watch: {
|
|
188
|
+
'block.metadata.config.launch_type': {
|
|
189
|
+
deep: true,
|
|
190
|
+
handler(newVal, oldVal) {
|
|
191
|
+
// We changed the consumer tool id, reload
|
|
192
|
+
if (
|
|
193
|
+
newVal === 'inline' &&
|
|
194
|
+
this.block.metadata.config.tool_id !== null
|
|
195
|
+
) {
|
|
196
|
+
this.setLaunchTimer()
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
},
|
|
126
200
|
'block.metadata.config.tool_id': {
|
|
127
201
|
deep: true,
|
|
128
202
|
handler(newVal, oldVal) {
|
|
129
203
|
// We changed the consumer tool id, reload
|
|
130
204
|
if (newVal !== oldVal) {
|
|
131
205
|
this.loadConsumer()
|
|
206
|
+
if (this.block.metadata.config.launch_type === 'inline') {
|
|
207
|
+
this.setLaunchTimer()
|
|
208
|
+
}
|
|
132
209
|
}
|
|
133
210
|
},
|
|
134
211
|
},
|
|
@@ -140,13 +217,45 @@ export default {
|
|
|
140
217
|
if (_.isEmpty(this.block.metadata.config.tool_id)) {
|
|
141
218
|
this.block.metadata.config.tool_id = null
|
|
142
219
|
}
|
|
220
|
+
if (_.isEmpty(this.block.metadata.config.title)) {
|
|
221
|
+
this.block.metadata.config.title = this.$t(
|
|
222
|
+
'windward.integrations.components.content.blocks.external_integration.lti_consumer.title'
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
if (_.isEmpty(this.block.metadata.config.instructions)) {
|
|
226
|
+
this.block.metadata.config.instructions = this.$t(
|
|
227
|
+
'windward.integrations.components.content.blocks.external_integration.lti_consumer.instructions'
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
mounted() {
|
|
232
|
+
if (
|
|
233
|
+
this.block.metadata.config.tool_id !== null &&
|
|
234
|
+
this.block.metadata.config.launch_type === 'inline'
|
|
235
|
+
) {
|
|
236
|
+
this.setLaunchTimer()
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
destroyed() {
|
|
241
|
+
clearTimeout(this.launchTimeout)
|
|
143
242
|
},
|
|
144
|
-
mounted() {},
|
|
145
243
|
methods: {
|
|
244
|
+
setLaunchTimer(timeout = 1000) {
|
|
245
|
+
clearTimeout(this.launchTimeout)
|
|
246
|
+
this.launchTimeout = setTimeout(this.onLaunch, timeout)
|
|
247
|
+
this.launched = true
|
|
248
|
+
},
|
|
249
|
+
closeModal() {
|
|
250
|
+
this.launchData = {}
|
|
251
|
+
this.target = ''
|
|
252
|
+
this.openModal = false
|
|
253
|
+
this.launched = false
|
|
254
|
+
},
|
|
146
255
|
async loadConsumer() {
|
|
147
256
|
try {
|
|
148
257
|
if (!_.isEmpty(this.block.metadata.config.tool_id)) {
|
|
149
|
-
this.consumer = await new
|
|
258
|
+
this.consumer = await new LtiConsumer()
|
|
150
259
|
.for(
|
|
151
260
|
new Organization({ id: this.organization.id }),
|
|
152
261
|
new Course({ id: this.course.id })
|
|
@@ -168,8 +277,11 @@ export default {
|
|
|
168
277
|
// Clear the launch data in-case we're in inline mode
|
|
169
278
|
// If the iframe isn't destroyed subsequent launches will be new windows instead of the target iframe
|
|
170
279
|
this.launchData = {}
|
|
280
|
+
if (this.block.metadata.config.launch_type === 'modal') {
|
|
281
|
+
this.openModal = true
|
|
282
|
+
}
|
|
171
283
|
|
|
172
|
-
this.launchData = await
|
|
284
|
+
this.launchData = await LtiConsumer.custom(
|
|
173
285
|
new Enrollment({ id: this.enrollment.id }),
|
|
174
286
|
this.consumer,
|
|
175
287
|
'/launch'
|
|
@@ -182,6 +294,9 @@ export default {
|
|
|
182
294
|
case 'inline':
|
|
183
295
|
this.target = this.frameId
|
|
184
296
|
break
|
|
297
|
+
case 'modal':
|
|
298
|
+
this.target = this.frameId
|
|
299
|
+
break
|
|
185
300
|
default:
|
|
186
301
|
this.target = '_blank'
|
|
187
302
|
}
|
|
@@ -216,5 +331,6 @@ export default {
|
|
|
216
331
|
.launch-frame {
|
|
217
332
|
aspect-ratio: 16/9;
|
|
218
333
|
width: 100%;
|
|
334
|
+
border-style: none;
|
|
219
335
|
}
|
|
220
336
|
</style>
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
<TextEditor
|
|
34
34
|
id="description"
|
|
35
35
|
v-model="consumer.description"
|
|
36
|
+
menubar="edit"
|
|
37
|
+
toolbar="undo redo | bold italic underline strikethrough "
|
|
36
38
|
:height="200"
|
|
37
39
|
></TextEditor>
|
|
38
40
|
|
|
@@ -148,7 +150,7 @@
|
|
|
148
150
|
<script>
|
|
149
151
|
import _ from 'lodash'
|
|
150
152
|
import { mapGetters } from 'vuex'
|
|
151
|
-
import
|
|
153
|
+
import LtiConsumer from '../../../../models/ExternalIntegration/LtiConsumer'
|
|
152
154
|
import Organization from '~/models/Organization'
|
|
153
155
|
import Course from '~/models/Course'
|
|
154
156
|
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
@@ -160,7 +162,7 @@ export default {
|
|
|
160
162
|
extends: FormVue,
|
|
161
163
|
props: {
|
|
162
164
|
value: {
|
|
163
|
-
type: [
|
|
165
|
+
type: [LtiConsumer, null],
|
|
164
166
|
required: false,
|
|
165
167
|
default: null,
|
|
166
168
|
},
|
|
@@ -220,9 +222,9 @@ export default {
|
|
|
220
222
|
},
|
|
221
223
|
created() {
|
|
222
224
|
if (_.isEmpty(this.value)) {
|
|
223
|
-
this.consumer = new
|
|
225
|
+
this.consumer = new LtiConsumer(this.consumer)
|
|
224
226
|
} else {
|
|
225
|
-
this.consumer = new
|
|
227
|
+
this.consumer = new LtiConsumer(_.cloneDeep(this.value))
|
|
226
228
|
}
|
|
227
229
|
},
|
|
228
230
|
mounted() {
|
|
@@ -260,7 +262,7 @@ export default {
|
|
|
260
262
|
this.consumer.metadata.custom.splice(index, 1)
|
|
261
263
|
},
|
|
262
264
|
async save() {
|
|
263
|
-
let consumer = new
|
|
265
|
+
let consumer = new LtiConsumer(this.consumer).for(
|
|
264
266
|
new Organization({ id: this.organization.id }),
|
|
265
267
|
new Course({ id: this.course.id })
|
|
266
268
|
)
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
<script>
|
|
80
80
|
import _ from 'lodash'
|
|
81
81
|
import { mapGetters } from 'vuex'
|
|
82
|
-
import
|
|
82
|
+
import LtiConsumer from '../../../../models/ExternalIntegration/LtiConsumer'
|
|
83
83
|
import ManageConsumer from './ManageConsumer.vue'
|
|
84
84
|
import Course from '~/models/Course'
|
|
85
85
|
import Organization from '~/models/Organization'
|
|
@@ -168,11 +168,12 @@ export default {
|
|
|
168
168
|
this.loadConsumers()
|
|
169
169
|
},
|
|
170
170
|
async loadConsumers() {
|
|
171
|
-
this.consumers = await new
|
|
171
|
+
this.consumers = await new LtiConsumer()
|
|
172
172
|
.for(
|
|
173
173
|
new Organization({ id: this.organization.id }),
|
|
174
174
|
new Course({ id: this.course.id })
|
|
175
175
|
)
|
|
176
|
+
.where('version', '1.1')
|
|
176
177
|
.get()
|
|
177
178
|
},
|
|
178
179
|
onConfirmDelete(consumer) {
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="!render" class="integration-loading">
|
|
4
|
+
<v-progress-circular size="128" indeterminate />
|
|
5
|
+
</div>
|
|
6
|
+
<div v-if="render">
|
|
7
|
+
<v-form v-model="formValid" @submit.prevent>
|
|
8
|
+
<v-row justify="center" align="center" class="mt-5">
|
|
9
|
+
<v-col cols="12">
|
|
10
|
+
<v-text-field
|
|
11
|
+
v-model="consumer.target"
|
|
12
|
+
:label="
|
|
13
|
+
$t(
|
|
14
|
+
'windward.integrations.components.external_integration.target_url'
|
|
15
|
+
)
|
|
16
|
+
"
|
|
17
|
+
:hint="
|
|
18
|
+
$t(
|
|
19
|
+
'windward.integrations.components.external_integration.target_url'
|
|
20
|
+
)
|
|
21
|
+
"
|
|
22
|
+
></v-text-field>
|
|
23
|
+
<v-text-field
|
|
24
|
+
v-model="consumer.name"
|
|
25
|
+
:placeholder="$t('shared.forms.name')"
|
|
26
|
+
:label="$t('shared.forms.name')"
|
|
27
|
+
:hint="$t('shared.forms.name')"
|
|
28
|
+
></v-text-field>
|
|
29
|
+
|
|
30
|
+
<label for="description">{{
|
|
31
|
+
$t('shared.forms.description')
|
|
32
|
+
}}</label>
|
|
33
|
+
<TextEditor
|
|
34
|
+
id="description"
|
|
35
|
+
v-model="consumer.description"
|
|
36
|
+
menubar="edit"
|
|
37
|
+
toolbar="undo redo | bold italic underline strikethrough "
|
|
38
|
+
:height="200"
|
|
39
|
+
></TextEditor>
|
|
40
|
+
|
|
41
|
+
<v-switch
|
|
42
|
+
v-model="consumer.enabled"
|
|
43
|
+
:label="$t('shared.forms.enabled')"
|
|
44
|
+
/>
|
|
45
|
+
<v-text-field
|
|
46
|
+
v-model="consumer.metadata.tool_oidc_auth_endpoint"
|
|
47
|
+
:placeholder="
|
|
48
|
+
$t(
|
|
49
|
+
'windward.integrations.components.external_integration.driver.lti1p3.tool_oidc_auth_endpoint'
|
|
50
|
+
)
|
|
51
|
+
"
|
|
52
|
+
:label="
|
|
53
|
+
$t(
|
|
54
|
+
'windward.integrations.components.external_integration.driver.lti1p3.tool_oidc_auth_endpoint'
|
|
55
|
+
)
|
|
56
|
+
"
|
|
57
|
+
></v-text-field>
|
|
58
|
+
<v-text-field
|
|
59
|
+
v-model="consumer.metadata.tool_public_keyset_url"
|
|
60
|
+
:placeholder="
|
|
61
|
+
$t(
|
|
62
|
+
'windward.integrations.components.external_integration.driver.lti1p3.tool_public_keyset_url'
|
|
63
|
+
)
|
|
64
|
+
"
|
|
65
|
+
:label="
|
|
66
|
+
$t(
|
|
67
|
+
'windward.integrations.components.external_integration.driver.lti1p3.tool_public_keyset_url'
|
|
68
|
+
)
|
|
69
|
+
"
|
|
70
|
+
></v-text-field>
|
|
71
|
+
|
|
72
|
+
<v-data-table
|
|
73
|
+
:headers="customParameterHeaders"
|
|
74
|
+
:items="consumer.metadata.custom"
|
|
75
|
+
hide-default-footer
|
|
76
|
+
class="elevation-1"
|
|
77
|
+
>
|
|
78
|
+
<template #[`item.key`]="{ index }">
|
|
79
|
+
<v-text-field
|
|
80
|
+
v-model="
|
|
81
|
+
consumer.metadata.custom[index].key
|
|
82
|
+
"
|
|
83
|
+
:label="
|
|
84
|
+
$t(
|
|
85
|
+
'windward.integrations.components.external_integration.driver.lti1p1.parameter_name'
|
|
86
|
+
)
|
|
87
|
+
"
|
|
88
|
+
/>
|
|
89
|
+
</template>
|
|
90
|
+
<template #[`item.value`]="{ index }">
|
|
91
|
+
<v-text-field
|
|
92
|
+
v-model="
|
|
93
|
+
consumer.metadata.custom[index].value
|
|
94
|
+
"
|
|
95
|
+
:label="
|
|
96
|
+
$t(
|
|
97
|
+
'windward.integrations.components.external_integration.driver.lti1p1.value'
|
|
98
|
+
)
|
|
99
|
+
"
|
|
100
|
+
/>
|
|
101
|
+
</template>
|
|
102
|
+
<template #[`item.actions`]="{ index }">
|
|
103
|
+
<v-btn
|
|
104
|
+
text
|
|
105
|
+
color="primary"
|
|
106
|
+
@click="deleteCustomParameter(index)"
|
|
107
|
+
>
|
|
108
|
+
<v-icon small> mdi-delete </v-icon>
|
|
109
|
+
<span class="sr-only">{{
|
|
110
|
+
$t('shared.forms.delete')
|
|
111
|
+
}}</span>
|
|
112
|
+
</v-btn>
|
|
113
|
+
</template>
|
|
114
|
+
<template #footer>
|
|
115
|
+
<div class="text-center">
|
|
116
|
+
<v-btn
|
|
117
|
+
color="primary"
|
|
118
|
+
class="mb-3"
|
|
119
|
+
@click="addCustomParameter"
|
|
120
|
+
>{{ $t('shared.forms.add') }}</v-btn
|
|
121
|
+
>
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
</v-data-table>
|
|
125
|
+
<br />
|
|
126
|
+
<v-select
|
|
127
|
+
v-model="consumer.metadata.security_level"
|
|
128
|
+
:items="securityLevels"
|
|
129
|
+
item-text="name"
|
|
130
|
+
item-value="value"
|
|
131
|
+
label="Security Level"
|
|
132
|
+
outlined
|
|
133
|
+
></v-select>
|
|
134
|
+
</v-col>
|
|
135
|
+
</v-row>
|
|
136
|
+
</v-form>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
|
+
|
|
141
|
+
<script>
|
|
142
|
+
import _ from 'lodash'
|
|
143
|
+
import { mapGetters } from 'vuex'
|
|
144
|
+
import LtiConsumer from '../../../../models/ExternalIntegration/LtiConsumer'
|
|
145
|
+
import Organization from '~/models/Organization'
|
|
146
|
+
import Course from '~/models/Course'
|
|
147
|
+
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
148
|
+
import FormVue from '~/components/Form'
|
|
149
|
+
|
|
150
|
+
export default {
|
|
151
|
+
name: 'ManageLti1p3ConsumerDriver',
|
|
152
|
+
components: { TextEditor },
|
|
153
|
+
extends: FormVue,
|
|
154
|
+
props: {
|
|
155
|
+
value: {
|
|
156
|
+
type: [LtiConsumer, null],
|
|
157
|
+
required: false,
|
|
158
|
+
default: null,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
emits: ['update:consumer'],
|
|
162
|
+
meta: {
|
|
163
|
+
privilege: {
|
|
164
|
+
'': {
|
|
165
|
+
writable: true,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
data() {
|
|
170
|
+
return {
|
|
171
|
+
render: false,
|
|
172
|
+
consumer: {
|
|
173
|
+
version: '1.3',
|
|
174
|
+
metadata: {
|
|
175
|
+
custom: [],
|
|
176
|
+
security_level: '',
|
|
177
|
+
security: [],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
customParameterHeaders: [
|
|
181
|
+
{
|
|
182
|
+
text: this.$t(
|
|
183
|
+
'windward.integrations.components.external_integration.driver.lti1p1.parameter_name'
|
|
184
|
+
),
|
|
185
|
+
value: 'key',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
text: this.$t(
|
|
189
|
+
'windward.integrations.components.external_integration.driver.lti1p1.value'
|
|
190
|
+
),
|
|
191
|
+
value: 'value',
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
text: this.$t('shared.forms.actions'),
|
|
195
|
+
value: 'actions',
|
|
196
|
+
sortable: false,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
securityLevels: [
|
|
200
|
+
{ name: 'Full Access', value: 'full' },
|
|
201
|
+
{ name: 'Email Only', value: 'email' },
|
|
202
|
+
{ name: 'Name Only', value: 'name' },
|
|
203
|
+
{ name: 'Anonymous', value: 'anonymous' },
|
|
204
|
+
// { name: 'Custom', value: 'custom' }, // TODO: When this is selected provide direct access to check off LTI fields
|
|
205
|
+
],
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
computed: {
|
|
209
|
+
...mapGetters({
|
|
210
|
+
organization: 'organization/get',
|
|
211
|
+
course: 'course/get',
|
|
212
|
+
}),
|
|
213
|
+
},
|
|
214
|
+
created() {
|
|
215
|
+
if (_.isEmpty(this.value)) {
|
|
216
|
+
this.consumer = new LtiConsumer(this.consumer)
|
|
217
|
+
} else {
|
|
218
|
+
this.consumer = new LtiConsumer(_.cloneDeep(this.value))
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
mounted() {
|
|
222
|
+
if (
|
|
223
|
+
!this.$PermissionService.userHasAccessTo(
|
|
224
|
+
'plugin.windward.integrations.course.externalIntegration',
|
|
225
|
+
'writable'
|
|
226
|
+
)
|
|
227
|
+
) {
|
|
228
|
+
// Display an angry error that they can't view this driver
|
|
229
|
+
this.$dialog.error(this.$t('shared.error.description_401'), {
|
|
230
|
+
duration: null,
|
|
231
|
+
action: {
|
|
232
|
+
text: this.$t('shared.forms.close'),
|
|
233
|
+
onClick: (_e, toastObject) => {
|
|
234
|
+
toastObject.goAway(0)
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// eslint-disable-next-line no-console
|
|
240
|
+
console.error('You do not have access to this consumer!')
|
|
241
|
+
|
|
242
|
+
// Return so we don't even attempt loading
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
this.render = true
|
|
247
|
+
},
|
|
248
|
+
methods: {
|
|
249
|
+
addCustomParameter() {
|
|
250
|
+
this.consumer.metadata.custom.push({ key: '', value: '' })
|
|
251
|
+
},
|
|
252
|
+
deleteCustomParameter(index) {
|
|
253
|
+
this.consumer.metadata.custom.splice(index, 1)
|
|
254
|
+
},
|
|
255
|
+
async save() {
|
|
256
|
+
let consumer = new LtiConsumer(this.consumer).for(
|
|
257
|
+
new Organization({ id: this.organization.id }),
|
|
258
|
+
new Course({ id: this.course.id })
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
consumer = await consumer.save()
|
|
263
|
+
this.consumer = consumer
|
|
264
|
+
|
|
265
|
+
// Clone and delete the metadata since we don't need to share that around
|
|
266
|
+
// Also include the vendor going back for easier mapping
|
|
267
|
+
const consumerEvent = _.cloneDeep(consumer)
|
|
268
|
+
|
|
269
|
+
this.$dialog.success(this.$t('shared.forms.saved'))
|
|
270
|
+
this.$emit('update:consumer', consumerEvent)
|
|
271
|
+
} catch (e) {
|
|
272
|
+
this.$dialog.error(
|
|
273
|
+
this.$t('windward.integrations.shared.error.save_failed')
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
async onSave() {
|
|
278
|
+
if (this.formValid) {
|
|
279
|
+
await this.save()
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
</script>
|