@windward/integrations 0.0.6 → 0.0.8

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.
Files changed (26) hide show
  1. package/components/Content/Blocks/ExternalIntegration/LtiConsumer.vue +37 -23
  2. package/components/ExternalIntegration/Driver/Lti1p1/ManageConsumer.vue +12 -14
  3. package/components/ExternalIntegration/Driver/Lti1p1/ManageConsumers.vue +22 -25
  4. package/components/ExternalIntegration/Driver/Lti1p1/ManageProvider.vue +53 -19
  5. package/components/ExternalIntegration/Driver/Lti1p1/ManageProviders.vue +53 -21
  6. package/components/ExternalIntegration/Driver/ManageLti1p1.vue +2 -2
  7. package/components/ExternalIntegration/ProviderTargetPicker.vue +218 -0
  8. package/components/ExternalIntegration/ProviderTargetViewer.vue +50 -0
  9. package/components/Integration/Driver/ManageBase.vue +3 -1
  10. package/components/Integration/JobTable.vue +2 -1
  11. package/components/SecretField.vue +1 -1
  12. package/components/Settings/ExternalIntegration/LtiConsumerSettings.vue +16 -19
  13. package/i18n/en-US/components/content/blocks/external_integration/lti_consumer.ts +2 -0
  14. package/i18n/en-US/components/external_integration/driver/lti1p1.ts +0 -20
  15. package/i18n/en-US/components/external_integration/index.ts +26 -1
  16. package/i18n/en-US/components/external_integration/provider_target.ts +9 -0
  17. package/i18n/en-US/pages/login/lti.ts +1 -1
  18. package/package.json +1 -1
  19. package/pages/admin/importCourse.vue +3 -0
  20. package/pages/admin/vendors.vue +2 -1
  21. package/plugin.js +1 -1
  22. package/test/Components/ExternalIntegration/ProviderTargetPicker.spec.js +22 -0
  23. package/test/Components/ExternalIntegration/ProviderTargetViewer.spec.js +22 -0
  24. package/test/__mocks__/componentsMock.js +12 -0
  25. package/test/__mocks__/modelMock.js +1 -1
  26. package/test/mocks.js +1 -0
@@ -28,16 +28,16 @@
28
28
  </v-alert>
29
29
 
30
30
  <v-form
31
- name="ltiForm"
32
31
  ref="ltiForm"
32
+ name="ltiForm"
33
33
  :target="target"
34
34
  :action="launchData.target || ''"
35
35
  :method="launchData.method || 'POST'"
36
36
  >
37
37
  <input
38
- type="hidden"
39
38
  v-for="(value, key) of launchData.payload"
40
39
  :key="key"
40
+ type="hidden"
41
41
  :name="key"
42
42
  :value="value"
43
43
  />
@@ -60,6 +60,21 @@
60
60
  )
61
61
  }}
62
62
  </v-alert>
63
+ <v-alert
64
+ v-else-if="
65
+ !$PermissionService.userHasAccessTo(
66
+ 'plugin.windward.integrations.course.externalIntegration',
67
+ 'readable'
68
+ )
69
+ "
70
+ type="error"
71
+ >
72
+ {{
73
+ $t(
74
+ 'windward.integrations.components.content.blocks.external_integration.lti_consumer.no_access'
75
+ )
76
+ }}
77
+ </v-alert>
63
78
  <v-alert v-else-if="consumer === null" type="warning">
64
79
  {{
65
80
  $t(
@@ -83,17 +98,8 @@ import Lti1p1Consumer from '../../../../models/ExternalIntegration/Lti1p1Consume
83
98
 
84
99
  export default {
85
100
  name: 'ContentBlockExternalIntegrationLti1p1Consumer',
86
- extends: BaseContentBlock,
87
101
  components: { TextViewer },
88
102
  extends: BaseContentBlock,
89
- beforeMount() {
90
- if (_.isEmpty(this.block.metadata.config)) {
91
- this.block.metadata.config = {}
92
- }
93
- if (_.isEmpty(this.block.metadata.config.tool_id)) {
94
- this.block.metadata.config.tool_id = null
95
- }
96
- },
97
103
  data() {
98
104
  return {
99
105
  consumer: null,
@@ -105,6 +111,17 @@ export default {
105
111
  launched: false,
106
112
  }
107
113
  },
114
+ async fetch() {
115
+ this.target = this.frameId
116
+ await this.loadConsumer()
117
+ },
118
+ computed: {
119
+ ...mapGetters({
120
+ organization: 'organization/get',
121
+ course: 'course/get',
122
+ enrollment: 'enrollment/get',
123
+ }),
124
+ },
108
125
  watch: {
109
126
  'block.metadata.config.tool_id': {
110
127
  deep: true,
@@ -116,16 +133,13 @@ export default {
116
133
  },
117
134
  },
118
135
  },
119
- async fetch() {
120
- this.target = this.frameId
121
- this.loadConsumer()
122
- },
123
- computed: {
124
- ...mapGetters({
125
- organization: 'organization/get',
126
- course: 'course/get',
127
- enrollment: 'enrollment/get',
128
- }),
136
+ beforeMount() {
137
+ if (_.isEmpty(this.block.metadata.config)) {
138
+ this.block.metadata.config = {}
139
+ }
140
+ if (_.isEmpty(this.block.metadata.config.tool_id)) {
141
+ this.block.metadata.config.tool_id = null
142
+ }
129
143
  },
130
144
  mounted() {},
131
145
  methods: {
@@ -186,8 +200,8 @@ export default {
186
200
  )
187
201
  }
188
202
  } catch (e) {
189
- console.error('LTI Link Launch Fail')
190
- console.error(e)
203
+ // eslint-disable-next-line no-console
204
+ console.error('LTI Link Launch Fail', e)
191
205
  }
192
206
  },
193
207
  onBeforeSave() {
@@ -11,12 +11,12 @@
11
11
  v-model="consumer.target"
12
12
  :label="
13
13
  $t(
14
- 'windward.integrations.components.external_integration.driver.lti1p1.target_url'
14
+ 'windward.integrations.components.external_integration.target_url'
15
15
  )
16
16
  "
17
17
  :hint="
18
18
  $t(
19
- 'windward.integrations.components.external_integration.driver.lti1p1.target_url'
19
+ 'windward.integrations.components.external_integration.target_url'
20
20
  )
21
21
  "
22
22
  ></v-text-field>
@@ -31,18 +31,14 @@
31
31
  $t('shared.forms.description')
32
32
  }}</label>
33
33
  <TextEditor
34
- v-model="consumer.description"
35
34
  id="description"
35
+ v-model="consumer.description"
36
36
  :height="200"
37
37
  ></TextEditor>
38
38
 
39
39
  <v-switch
40
40
  v-model="consumer.enabled"
41
- :label="
42
- $t(
43
- 'windward.integrations.components.external_integration.driver.lti1p1.enabled'
44
- )
45
- "
41
+ :label="$t('shared.forms.enabled')"
46
42
  />
47
43
 
48
44
  <v-text-field
@@ -86,7 +82,7 @@
86
82
  hide-default-footer
87
83
  class="elevation-1"
88
84
  >
89
- <template #item.key="{ index }">
85
+ <template #[`item.key`]="{ index }">
90
86
  <v-text-field
91
87
  v-model="
92
88
  consumer.metadata.custom[index].key
@@ -98,7 +94,7 @@
98
94
  "
99
95
  />
100
96
  </template>
101
- <template #item.value="{ index }">
97
+ <template #[`item.value`]="{ index }">
102
98
  <v-text-field
103
99
  v-model="
104
100
  consumer.metadata.custom[index].value
@@ -110,7 +106,7 @@
110
106
  "
111
107
  />
112
108
  </template>
113
- <template #item.actions="{ index }">
109
+ <template #[`item.actions`]="{ index }">
114
110
  <v-btn
115
111
  text
116
112
  color="primary"
@@ -152,11 +148,11 @@
152
148
  <script>
153
149
  import _ from 'lodash'
154
150
  import { mapGetters } from 'vuex'
155
- import Lti1p1Consumer from '../../../../models/ExternalIntegration/Lti1p1Consumer'
156
- import FormVue from '~/components/Form'
157
151
  import Organization from '~/models/Organization'
158
152
  import Course from '~/models/Course'
159
153
  import TextEditor from '~/components/Text/TextEditor.vue'
154
+ import Lti1p1Consumer from '../../../../models/ExternalIntegration/Lti1p1Consumer'
155
+ import FormVue from '~/components/Form'
160
156
 
161
157
  export default {
162
158
  name: 'ManageLti1p1ConsumerDriver',
@@ -240,11 +236,13 @@ export default {
240
236
  duration: null,
241
237
  action: {
242
238
  text: this.$t('shared.forms.close'),
243
- onClick: (e, toastObject) => {
239
+ onClick: (_e, toastObject) => {
244
240
  toastObject.goAway(0)
245
241
  },
246
242
  },
247
243
  })
244
+
245
+ // eslint-disable-next-line no-console
248
246
  console.error('You do not have access to this consumer!')
249
247
 
250
248
  // Return so we don't even attempt loading
@@ -11,7 +11,11 @@
11
11
  'windward.integrations.components.external_integration.driver.lti1p1.new'
12
12
  )
13
13
  }}</template>
14
- <template #trigger>{{ $t('shared.forms.new') }}</template>
14
+ <template #trigger>{{
15
+ $t(
16
+ 'windward.integrations.components.external_integration.driver.lti1p1.new'
17
+ )
18
+ }}</template>
15
19
  <template #form="{ on, attrs }"
16
20
  ><ManageConsumer v-bind="attrs" v-on="on"></ManageConsumer
17
21
  ></template>
@@ -23,22 +27,20 @@
23
27
  :items-per-page="10"
24
28
  class="elevation-1"
25
29
  >
26
- <template #item.description="{ item }">
30
+ <template #[`item.description`]="{ item }">
27
31
  {{ item.description.replace(/(<([^>]+)>)/gi, '').trim() }}
28
32
  </template>
29
33
 
30
- <template #item.enabled="{ item }">
34
+ <template #[`item.enabled`]="{ item }">
31
35
  <v-icon :color="item.enabled ? 'success' : 'error'"
32
36
  >{{ item.enabled ? 'mdi-check' : 'mdi-close' }}
33
37
  </v-icon>
34
38
  <span v-if="!item.enabled" class="sr-only">{{
35
- $t(
36
- 'windward.integrations.components.external_integration.driver.lti1p1.enabled'
37
- )
39
+ $t('shared.forms.enabled')
38
40
  }}</span>
39
41
  </template>
40
42
 
41
- <template #item.created_at="{ item }">
43
+ <template #[`item.created_at`]="{ item }">
42
44
  {{ $d(new Date(item.created_at), 'long') }}
43
45
  </template>
44
46
  <template #[`item.actions`]="{ index, item }">
@@ -77,42 +79,35 @@
77
79
  <script>
78
80
  import _ from 'lodash'
79
81
  import { mapGetters } from 'vuex'
80
- import Lti1p1Consumer from '../../../../models/ExternalIntegration/Lti1p1Consumer'
81
- import SecretField from '../../../SecretField.vue'
82
- import ManageConsumer from './ManageConsumer.vue'
83
82
  import Course from '~/models/Course'
84
83
  import Organization from '~/models/Organization'
85
84
  import Dialog from '~/components/Dialog.vue'
85
+ import Lti1p1Consumer from '../../../../models/ExternalIntegration/Lti1p1Consumer'
86
+ import ManageConsumer from './ManageConsumer.vue'
86
87
 
87
88
  export default {
88
89
  name: 'ManageLti1p1ConsumersDriver',
89
- components: { SecretField, Dialog, ManageConsumer },
90
+ components: { Dialog, ManageConsumer },
90
91
  data() {
91
92
  return {
92
93
  consumers: [],
93
94
  headers: [
94
95
  {
95
96
  text: this.$t(
96
- 'windward.integrations.components.external_integration.driver.lti1p1.target_url'
97
+ 'windward.integrations.components.external_integration.target_url'
97
98
  ),
98
99
  value: 'target',
99
100
  },
100
101
  {
101
- text: this.$t(
102
- 'windward.integrations.components.external_integration.driver.lti1p1.name'
103
- ),
102
+ text: this.$t('shared.forms.name'),
104
103
  value: 'name',
105
104
  },
106
105
  {
107
- text: this.$t(
108
- 'windward.integrations.components.external_integration.driver.lti1p1.description'
109
- ),
106
+ text: this.$t('shared.forms.description'),
110
107
  value: 'description',
111
108
  },
112
109
  {
113
- text: this.$t(
114
- 'windward.integrations.components.external_integration.driver.lti1p1.enabled'
115
- ),
110
+ text: this.$t('shared.forms.enabled'),
116
111
  value: 'enabled',
117
112
  },
118
113
  { text: this.$t('shared.forms.created'), value: 'created_at' },
@@ -139,16 +134,18 @@ export default {
139
134
  duration: null,
140
135
  action: {
141
136
  text: this.$t('shared.forms.close'),
142
- onClick: (e, toastObject) => {
137
+ onClick: (_e, toastObject) => {
143
138
  toastObject.goAway(0)
144
139
  },
145
140
  },
146
141
  })
147
142
  if (_.isEmpty(this.organization.id) || _.isEmpty(this.course.id)) {
143
+ // eslint-disable-next-line no-console
148
144
  console.error(
149
145
  'Cannot load external integrations because organization or course is not set!'
150
146
  )
151
147
  } else {
148
+ // eslint-disable-next-line no-console
152
149
  console.error(
153
150
  'You do not have access to this external integration!'
154
151
  )
@@ -167,7 +164,7 @@ export default {
167
164
  }),
168
165
  },
169
166
  methods: {
170
- onSaved(e) {
167
+ onSaved() {
171
168
  this.loadConsumers()
172
169
  },
173
170
  async loadConsumers() {
@@ -185,14 +182,14 @@ export default {
185
182
  action: [
186
183
  {
187
184
  text: this.$t('shared.forms.cancel'),
188
- onClick: (e, toastObject) => {
185
+ onClick: (_e, toastObject) => {
189
186
  toastObject.goAway(0)
190
187
  },
191
188
  },
192
189
  {
193
190
  text: this.$t('shared.forms.confirm'),
194
191
  // router navigation
195
- onClick: (e, toastObject) => {
192
+ onClick: (_e, toastObject) => {
196
193
  this.deleteConsumer(consumer)
197
194
  toastObject.goAway(0)
198
195
  },
@@ -4,33 +4,46 @@
4
4
  <v-progress-circular size="128" indeterminate />
5
5
  </div>
6
6
  <div v-if="render">
7
- <v-form v-model="formValid" @submit.prevent>
8
- <v-row justify="center" align="center" class="mt-5">
7
+ <v-form ref="form" v-model="formValid" @submit.prevent>
8
+ <v-row justify="center" align="center">
9
9
  <v-col cols="12">
10
+ <ProviderTargetPicker
11
+ v-model="provider"
12
+ ></ProviderTargetPicker>
13
+
10
14
  <v-text-field
15
+ id="target-url"
11
16
  v-model="provider.target"
17
+ disabled
12
18
  :label="
13
19
  $t(
14
- 'windward.integrations.components.external_integration.driver.lti1p1.target_url'
20
+ 'windward.integrations.components.external_integration.target_url'
15
21
  )
16
22
  "
17
23
  :hint="
18
24
  $t(
19
- 'windward.integrations.components.external_integration.driver.lti1p1.target_url'
25
+ 'windward.integrations.components.external_integration.target_url'
20
26
  )
21
27
  "
28
+ :rules="validation.existsRules"
22
29
  ></v-text-field>
23
30
 
24
31
  <v-switch
25
32
  v-model="provider.enabled"
33
+ :label="$t('shared.forms.enabled')"
34
+ />
35
+
36
+ <v-switch
37
+ v-model="provider.grade_sync"
26
38
  :label="
27
39
  $t(
28
- 'windward.integrations.components.external_integration.driver.lti1p1.enabled'
40
+ 'windward.integrations.components.external_integration.send_grades'
29
41
  )
30
42
  "
31
43
  />
32
44
 
33
45
  <v-text-field
46
+ id="lti-key"
34
47
  v-model="provider.metadata.key"
35
48
  :placeholder="
36
49
  $t(
@@ -50,6 +63,7 @@
50
63
  ></v-text-field>
51
64
 
52
65
  <v-text-field
66
+ id="lti-secret"
53
67
  v-model="provider.metadata.secret"
54
68
  :placeholder="
55
69
  $t(
@@ -77,7 +91,7 @@
77
91
  <v-expansion-panel-header>
78
92
  {{
79
93
  $t(
80
- 'windward.integrations.components.external_integration.driver.lti1p1.role_map_panel'
94
+ 'windward.integrations.components.external_integration.role_map_panel'
81
95
  )
82
96
  }}
83
97
  </v-expansion-panel-header>
@@ -86,7 +100,7 @@
86
100
  <strong>
87
101
  {{
88
102
  $t(
89
- 'windward.integrations.components.external_integration.driver.lti1p1.role_map_instructions'
103
+ 'windward.integrations.components.external_integration.role_map_instructions'
90
104
  )
91
105
  }}
92
106
  </strong>
@@ -94,7 +108,7 @@
94
108
  <p>
95
109
  {{
96
110
  $t(
97
- 'windward.integrations.components.external_integration.driver.lti1p1.role_map_warning'
111
+ 'windward.integrations.components.external_integration.role_map_warning'
98
112
  )
99
113
  }}
100
114
  </p>
@@ -105,14 +119,14 @@
105
119
  <th>
106
120
  {{
107
121
  $t(
108
- 'windward.integrations.components.external_integration.driver.lti1p1.role_in_host'
122
+ 'windward.integrations.components.external_integration.role_in_host'
109
123
  )
110
124
  }}
111
125
  </th>
112
126
  <th>
113
127
  {{
114
128
  $t(
115
- 'windward.integrations.components.external_integration.driver.lti1p1.role_in_local'
129
+ 'windward.integrations.components.external_integration.role_in_local'
116
130
  )
117
131
  }}
118
132
  </th>
@@ -145,7 +159,7 @@
145
159
  item-text="name"
146
160
  :label="
147
161
  $t(
148
- 'windward.integrations.components.external_integration.driver.lti1p1.map_to_role'
162
+ 'windward.integrations.components.external_integration.map_to_role'
149
163
  )
150
164
  "
151
165
  ></v-select>
@@ -167,7 +181,7 @@
167
181
  >
168
182
  {{
169
183
  $t(
170
- 'windward.integrations.components.external_integration.driver.lti1p1.delete_role_map_item'
184
+ 'windward.integrations.components.external_integration.delete_role_map_item'
171
185
  )
172
186
  }}
173
187
  </span>
@@ -182,7 +196,7 @@
182
196
  "
183
197
  :label="
184
198
  $t(
185
- 'windward.integrations.components.external_integration.driver.lti1p1.remote_role'
199
+ 'windward.integrations.components.external_integration.remote_role'
186
200
  )
187
201
  "
188
202
  ></v-text-field>
@@ -193,7 +207,7 @@
193
207
  :items="roles"
194
208
  :label="
195
209
  $t(
196
- 'windward.integrations.components.external_integration.driver.lti1p1.map_to_role'
210
+ 'windward.integrations.components.external_integration.map_to_role'
197
211
  )
198
212
  "
199
213
  item-value="id"
@@ -217,7 +231,7 @@
217
231
  >
218
232
  {{
219
233
  $t(
220
- 'windward.integrations.components.external_integration.driver.lti1p1.add_role_map_item'
234
+ 'windward.integrations.components.external_integration.add_role_map_item'
221
235
  )
222
236
  }}
223
237
  </span>
@@ -240,15 +254,16 @@
240
254
  <script>
241
255
  import _ from 'lodash'
242
256
  import { mapGetters } from 'vuex'
243
- import Lti1p1Provider from '../../../../models/ExternalIntegration/Lti1p1Provider'
244
- import FormVue from '~/components/Form'
257
+ import FormVue from '~/components/Form.vue'
245
258
  import Role from '~/models/Role'
246
259
  import Organization from '~/models/Organization'
247
260
  import Course from '~/models/Course'
261
+ import ProviderTargetPicker from '../../ProviderTargetPicker.vue'
262
+ import Lti1p1Provider from '../../../../models/ExternalIntegration/Lti1p1Provider'
248
263
 
249
264
  export default {
250
265
  name: 'ManageLti1p1ProviderDriver',
251
- components: {},
266
+ components: { ProviderTargetPicker },
252
267
  extends: FormVue,
253
268
  props: {
254
269
  value: {
@@ -293,6 +308,7 @@ export default {
293
308
  ...mapGetters({
294
309
  organization: 'organization/get',
295
310
  course: 'course/get',
311
+ contentTree: 'content/getTree',
296
312
  }),
297
313
  },
298
314
  created() {
@@ -317,11 +333,12 @@ export default {
317
333
  duration: null,
318
334
  action: {
319
335
  text: this.$t('shared.forms.close'),
320
- onClick: (e, toastObject) => {
336
+ onClick: (_e, toastObject) => {
321
337
  toastObject.goAway(0)
322
338
  },
323
339
  },
324
340
  })
341
+ // eslint-disable-next-line no-console
325
342
  console.error('You do not have access to this provider!')
326
343
 
327
344
  // Return so we don't even attempt loading
@@ -358,13 +375,30 @@ export default {
358
375
 
359
376
  this.$dialog.success(this.$t('shared.forms.saved'))
360
377
  this.$emit('update:provider', providerEvent)
378
+ this.$emit('input', provider)
361
379
  } catch (e) {
362
380
  this.$dialog.error(
363
381
  this.$t('windward.integrations.shared.error.save_failed')
364
382
  )
365
383
  }
384
+
385
+ // Create a new provider to clear the form if the original value is empty aka new
386
+ if (_.isEmpty(this.value)) {
387
+ this.provider = new Lti1p1Provider({
388
+ role_metadata: {},
389
+ metadata: {},
390
+ })
391
+ }
366
392
  },
367
393
  async onSave() {
394
+ this.$refs.form.validate()
395
+ if (this.formValid) {
396
+ await this.save()
397
+ }
398
+ },
399
+
400
+ async onSaveAndNew() {
401
+ this.$refs.form.validate()
368
402
  if (this.formValid) {
369
403
  await this.save()
370
404
  }