@windward/core 0.0.6 → 0.0.7
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/Image.vue +1 -6
- package/components/Content/Blocks/OpenResponse.vue +137 -0
- package/components/Content/Blocks/OpenResponseCollate.vue +158 -0
- package/components/Settings/OpenResponseCollateSettings.vue +170 -0
- package/components/Settings/OpenResponseSettings.vue +81 -0
- package/components/utils/TinyMCEWrapper.vue +13 -3
- package/i18n/en-US/components/content/blocks/index.ts +4 -0
- package/i18n/en-US/components/content/blocks/open_response.ts +5 -0
- package/i18n/en-US/components/content/blocks/open_response_collate.ts +6 -0
- package/i18n/en-US/components/settings/index.ts +5 -1
- package/i18n/en-US/components/settings/open_response.ts +5 -0
- package/i18n/en-US/components/settings/open_response_collate.ts +6 -0
- package/i18n/en-US/shared/content_blocks.ts +2 -0
- package/i18n/en-US/shared/settings.ts +3 -1
- package/i18n/es-ES/components/content/blocks/index.ts +4 -0
- package/i18n/es-ES/components/content/blocks/open_response.ts +6 -0
- package/i18n/es-ES/components/content/blocks/open_response_collate.ts +6 -0
- package/i18n/es-ES/components/settings/index.ts +4 -0
- package/i18n/es-ES/components/settings/open_response.ts +5 -0
- package/i18n/es-ES/components/settings/open_response_collate.ts +7 -0
- package/i18n/es-ES/shared/content_blocks.ts +2 -0
- package/i18n/es-ES/shared/settings.ts +3 -0
- package/i18n/sv-SE/components/content/blocks/index.ts +4 -0
- package/i18n/sv-SE/components/content/blocks/open_response.ts +6 -0
- package/i18n/sv-SE/components/content/blocks/open_response_collate.ts +6 -0
- package/i18n/sv-SE/components/settings/index.ts +5 -1
- package/i18n/sv-SE/components/settings/open_response.ts +5 -0
- package/i18n/sv-SE/components/settings/open_response_collate.ts +6 -0
- package/i18n/sv-SE/shared/content_blocks.ts +2 -0
- package/i18n/sv-SE/shared/settings.ts +2 -0
- package/package.json +1 -1
- package/plugin.js +43 -0
- package/test/Components/Content/Blocks/OpenResponse.spec.js +31 -0
- package/test/Components/Content/Blocks/OpenResponseCollate.spec.js +36 -0
- package/test/Components/Settings/OpenResponseCollateSettings.spec.js +20 -0
- package/test/Components/Settings/OpenResponseSettings.spec.js +20 -0
|
@@ -48,10 +48,10 @@ export default {
|
|
|
48
48
|
},
|
|
49
49
|
data() {
|
|
50
50
|
return {
|
|
51
|
+
id: 'image_' + Crypto.id(),
|
|
51
52
|
aspectRatio: undefined,
|
|
52
53
|
}
|
|
53
54
|
},
|
|
54
|
-
extends: BaseContentBlock,
|
|
55
55
|
beforeMount() {
|
|
56
56
|
if (_.isEmpty(this.block.metadata.config)) {
|
|
57
57
|
this.block.metadata.config = {}
|
|
@@ -63,11 +63,6 @@ export default {
|
|
|
63
63
|
this.block.metadata.config.aria_describedby = ''
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
-
data() {
|
|
67
|
-
return {
|
|
68
|
-
id: 'image_' + Crypto.id(),
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
66
|
computed: {
|
|
72
67
|
describedById() {
|
|
73
68
|
// If there's a described by
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="stateLoaded">
|
|
4
|
+
<div v-if="block.body && !submitted">
|
|
5
|
+
<TextViewer v-model="block.body" :height="200"></TextViewer>
|
|
6
|
+
<TextEditor
|
|
7
|
+
v-model="response"
|
|
8
|
+
:height="200"
|
|
9
|
+
menubar=""
|
|
10
|
+
></TextEditor>
|
|
11
|
+
<p class="pa-3 text-center blue-grey lighten-5">
|
|
12
|
+
<v-btn
|
|
13
|
+
color="primary"
|
|
14
|
+
:disabled="!canSubmit"
|
|
15
|
+
@click="onSubmit"
|
|
16
|
+
>
|
|
17
|
+
{{ $t('shared.forms.submit') }}
|
|
18
|
+
</v-btn>
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div v-else-if="block.body && submitted">
|
|
23
|
+
<TextViewer v-model="block.body" :height="200"></TextViewer>
|
|
24
|
+
<p>
|
|
25
|
+
{{
|
|
26
|
+
$t(
|
|
27
|
+
'windward.core.components.content.blocks.open_response.your_response'
|
|
28
|
+
)
|
|
29
|
+
}}
|
|
30
|
+
</p>
|
|
31
|
+
<v-alert color="light-blue lighten-4">
|
|
32
|
+
<TextViewer v-model="response" :height="200"></TextViewer>
|
|
33
|
+
</v-alert>
|
|
34
|
+
<div v-if="block.metadata.config.sample_response">
|
|
35
|
+
<p>
|
|
36
|
+
{{
|
|
37
|
+
$t(
|
|
38
|
+
'windward.core.components.content.blocks.open_response.sample_response'
|
|
39
|
+
)
|
|
40
|
+
}}
|
|
41
|
+
</p>
|
|
42
|
+
<v-alert color="light-blue lighten-4">
|
|
43
|
+
<TextViewer
|
|
44
|
+
v-model="block.metadata.config.sample_response"
|
|
45
|
+
:height="200"
|
|
46
|
+
></TextViewer>
|
|
47
|
+
</v-alert>
|
|
48
|
+
</div>
|
|
49
|
+
<p class="pa-3 text-center blue-grey lighten-5">
|
|
50
|
+
<v-btn color="primary" @click="submitted = false">{{
|
|
51
|
+
$t('shared.forms.edit')
|
|
52
|
+
}}</v-btn>
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<v-alert v-else type="warning">
|
|
57
|
+
{{
|
|
58
|
+
$t(
|
|
59
|
+
'windward.core.components.content.blocks.open_response.initial_setup'
|
|
60
|
+
)
|
|
61
|
+
}}
|
|
62
|
+
</v-alert>
|
|
63
|
+
</div>
|
|
64
|
+
<div v-if="!stateLoaded">
|
|
65
|
+
<v-progress-circular
|
|
66
|
+
indeterminate
|
|
67
|
+
:size="64"
|
|
68
|
+
:width="4"
|
|
69
|
+
color="white"
|
|
70
|
+
>
|
|
71
|
+
</v-progress-circular>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
import _ from 'lodash'
|
|
78
|
+
import TextViewer from '~/components/Text/TextViewer.vue'
|
|
79
|
+
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
80
|
+
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
81
|
+
|
|
82
|
+
export default {
|
|
83
|
+
name: 'ContentBlockOpenResponse',
|
|
84
|
+
extends: BaseContentBlock,
|
|
85
|
+
components: {
|
|
86
|
+
TextViewer,
|
|
87
|
+
TextEditor,
|
|
88
|
+
},
|
|
89
|
+
data() {
|
|
90
|
+
return {
|
|
91
|
+
stateLoaded: false,
|
|
92
|
+
response: '',
|
|
93
|
+
submitted: false,
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
beforeMount() {
|
|
97
|
+
if (_.isEmpty(this.block.body)) {
|
|
98
|
+
this.block.body = ''
|
|
99
|
+
}
|
|
100
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
101
|
+
this.block.metadata.config = {}
|
|
102
|
+
}
|
|
103
|
+
if (_.isEmpty(this.block.metadata.config.sample_response)) {
|
|
104
|
+
this.block.metadata.config.sample_response = ''
|
|
105
|
+
}
|
|
106
|
+
if (_.isEmpty(this.block.metadata.config.starting_text)) {
|
|
107
|
+
this.block.metadata.config.starting_text = ''
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
computed: {
|
|
111
|
+
canSubmit() {
|
|
112
|
+
// Make sure the response is not empty and not equal to the starting text
|
|
113
|
+
return (
|
|
114
|
+
this.response &&
|
|
115
|
+
this.response !== this.block.metadata.config.starting_text
|
|
116
|
+
)
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
watch: {},
|
|
120
|
+
mounted() {},
|
|
121
|
+
methods: {
|
|
122
|
+
onAfterSetContentBlockState() {
|
|
123
|
+
// If after the state is applied the response is still empty then apply the default response
|
|
124
|
+
if (this.response === '') {
|
|
125
|
+
this.response = this.block.metadata.config.starting_text
|
|
126
|
+
}
|
|
127
|
+
this.stateLoaded = true
|
|
128
|
+
},
|
|
129
|
+
onSubmit() {
|
|
130
|
+
this.submitted = true
|
|
131
|
+
|
|
132
|
+
// Force the state to save on submit and not wait
|
|
133
|
+
this.$Tracking.flushState()
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
</script>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<div v-if="block.metadata.config.linked.length">
|
|
4
|
+
<v-btn color="primary" block @click="onCollate">
|
|
5
|
+
<v-icon class="mr-3">mdi-file-word</v-icon>
|
|
6
|
+
{{
|
|
7
|
+
$t(
|
|
8
|
+
'windward.core.components.content.blocks.open_response_collate.download_document'
|
|
9
|
+
)
|
|
10
|
+
}}
|
|
11
|
+
</v-btn>
|
|
12
|
+
</div>
|
|
13
|
+
<v-alert v-else type="warning">
|
|
14
|
+
{{
|
|
15
|
+
$t(
|
|
16
|
+
'windward.core.components.content.blocks.open_response_collate.initial_setup'
|
|
17
|
+
)
|
|
18
|
+
}}
|
|
19
|
+
</v-alert>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
import _ from 'lodash'
|
|
25
|
+
import { mapGetters } from 'vuex'
|
|
26
|
+
import BaseContentBlock from '~/components/Content/Blocks/BaseContentBlock'
|
|
27
|
+
import UserContentBlockState from '~/models/UserContentBlockState'
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
name: 'ContentBlockOpenResponseCollate',
|
|
31
|
+
extends: BaseContentBlock,
|
|
32
|
+
components: {},
|
|
33
|
+
data() {
|
|
34
|
+
return {}
|
|
35
|
+
},
|
|
36
|
+
beforeMount() {
|
|
37
|
+
if (_.isEmpty(this.block.body)) {
|
|
38
|
+
this.block.body = ''
|
|
39
|
+
}
|
|
40
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
41
|
+
this.block.metadata.config = {}
|
|
42
|
+
}
|
|
43
|
+
if (_.isEmpty(this.block.metadata.config.linked)) {
|
|
44
|
+
this.block.metadata.config.linked = []
|
|
45
|
+
}
|
|
46
|
+
if (_.isEmpty(this.block.metadata.config.filename)) {
|
|
47
|
+
this.block.metadata.config.filename = ''
|
|
48
|
+
}
|
|
49
|
+
if (_.isEmpty(this.block.metadata.config.include_prompts)) {
|
|
50
|
+
this.block.metadata.config.include_prompts = false
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
computed: {
|
|
54
|
+
...mapGetters({
|
|
55
|
+
organization: 'organization/get',
|
|
56
|
+
course: 'course/get',
|
|
57
|
+
enrollment: 'enrollment/get',
|
|
58
|
+
content: 'content/get',
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
watch: {},
|
|
62
|
+
mounted() {},
|
|
63
|
+
methods: {
|
|
64
|
+
async onCollate() {
|
|
65
|
+
let userState = await UserContentBlockState.where({
|
|
66
|
+
'metadata->block->tag': 'plugin-core-open-response',
|
|
67
|
+
course_user_id: this.enrollment.id,
|
|
68
|
+
})
|
|
69
|
+
.whereIn('content_block_id', this.block.metadata.config.linked)
|
|
70
|
+
.get()
|
|
71
|
+
let collated = ''
|
|
72
|
+
|
|
73
|
+
userState.forEach((state) => {
|
|
74
|
+
// Prepend the prompt from the state if include prompts is enabled
|
|
75
|
+
if (this.block.metadata.config.include_prompts) {
|
|
76
|
+
collated +=
|
|
77
|
+
'<strong>' +
|
|
78
|
+
state.metadata.block.body +
|
|
79
|
+
'</strong><hr />'
|
|
80
|
+
}
|
|
81
|
+
if (!_.isEmpty(state.metadata.response)) {
|
|
82
|
+
collated += '\n' + state.metadata.response
|
|
83
|
+
} else {
|
|
84
|
+
collated +=
|
|
85
|
+
'\n<p>' +
|
|
86
|
+
this.$t(
|
|
87
|
+
'windward.core.components.content.blocks.open_response_collate.no_response'
|
|
88
|
+
) +
|
|
89
|
+
'</p>'
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
let filename = this.block.metadata.config.filename
|
|
93
|
+
if (_.isEmpty(this.block.metadata.config.filename)) {
|
|
94
|
+
// Default filename is the users name + the current page
|
|
95
|
+
filename =
|
|
96
|
+
this.$auth.user.last_name +
|
|
97
|
+
'_' +
|
|
98
|
+
this.$auth.user.first_name +
|
|
99
|
+
'_' +
|
|
100
|
+
_.get(this.content, 'content.name_prefix') +
|
|
101
|
+
_.get(this.content, 'content.name')
|
|
102
|
+
|
|
103
|
+
// Change spaces to underscores and remove special characters
|
|
104
|
+
filename = filename.replaceAll(/\s+/gi, '_')
|
|
105
|
+
filename = filename.replaceAll(/[^a-z0-9\-\_]/gi, '')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.generateDocument(collated, filename)
|
|
109
|
+
},
|
|
110
|
+
generateDocument(htmlBody, filename = '') {
|
|
111
|
+
// Specify file name. If one isn't supplied then a default name of `exported_document_YYYY-MM-DD.doc` is used
|
|
112
|
+
filename = filename
|
|
113
|
+
? filename + '.doc'
|
|
114
|
+
: 'exported_document_' +
|
|
115
|
+
new Date().toISOString().split('T')[0] +
|
|
116
|
+
'.doc'
|
|
117
|
+
|
|
118
|
+
var preHtml =
|
|
119
|
+
"<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>" +
|
|
120
|
+
"<head><meta charset='utf-8'><title>" +
|
|
121
|
+
filename +
|
|
122
|
+
'</title></head>' +
|
|
123
|
+
'<body>'
|
|
124
|
+
var postHtml = '</body></html>'
|
|
125
|
+
var html = preHtml + htmlBody + postHtml
|
|
126
|
+
|
|
127
|
+
var blob = new Blob(['\ufeff', html], {
|
|
128
|
+
type: 'application/msword',
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Specify link url
|
|
132
|
+
var url =
|
|
133
|
+
'data:application/vnd.ms-word;charset=utf-8,' +
|
|
134
|
+
encodeURIComponent(html)
|
|
135
|
+
|
|
136
|
+
// Create download link element
|
|
137
|
+
var downloadLink = document.createElement('a')
|
|
138
|
+
|
|
139
|
+
document.body.appendChild(downloadLink)
|
|
140
|
+
|
|
141
|
+
if (navigator.msSaveOrOpenBlob) {
|
|
142
|
+
navigator.msSaveOrOpenBlob(blob, filename)
|
|
143
|
+
} else {
|
|
144
|
+
// Create a link to the file
|
|
145
|
+
downloadLink.href = url
|
|
146
|
+
|
|
147
|
+
// Setting the file name
|
|
148
|
+
downloadLink.download = filename
|
|
149
|
+
|
|
150
|
+
//triggering the function
|
|
151
|
+
downloadLink.click()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
document.body.removeChild(downloadLink)
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
</script>
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-container>
|
|
3
|
+
<v-form>
|
|
4
|
+
<p>
|
|
5
|
+
{{
|
|
6
|
+
$t(
|
|
7
|
+
'windward.core.components.settings.open_response_collate.instructions'
|
|
8
|
+
)
|
|
9
|
+
}}
|
|
10
|
+
</p>
|
|
11
|
+
<v-switch
|
|
12
|
+
v-model="block.metadata.config.include_prompts"
|
|
13
|
+
:label="
|
|
14
|
+
$t(
|
|
15
|
+
'windward.core.components.settings.open_response_collate.include_prompts'
|
|
16
|
+
)
|
|
17
|
+
"
|
|
18
|
+
></v-switch>
|
|
19
|
+
<v-text-field
|
|
20
|
+
v-model="block.metadata.config.filename"
|
|
21
|
+
:label="
|
|
22
|
+
$t(
|
|
23
|
+
'windward.core.components.settings.open_response_collate.filename'
|
|
24
|
+
)
|
|
25
|
+
"
|
|
26
|
+
></v-text-field>
|
|
27
|
+
<v-list>
|
|
28
|
+
<v-list-item-group
|
|
29
|
+
v-model="linked"
|
|
30
|
+
multiple
|
|
31
|
+
color="primary"
|
|
32
|
+
@change="onLinkedChange"
|
|
33
|
+
>
|
|
34
|
+
<draggable
|
|
35
|
+
v-bind="dragOptions"
|
|
36
|
+
:list="contentBlocks"
|
|
37
|
+
@change="onDragChange"
|
|
38
|
+
>
|
|
39
|
+
<v-list-item
|
|
40
|
+
v-for="contentBlock in contentBlocks"
|
|
41
|
+
:key="contentBlock.id"
|
|
42
|
+
:value="contentBlock.id"
|
|
43
|
+
>
|
|
44
|
+
<template #default="{ active }">
|
|
45
|
+
<v-list-item-icon>
|
|
46
|
+
<v-icon>mdi-drag</v-icon>
|
|
47
|
+
</v-list-item-icon>
|
|
48
|
+
|
|
49
|
+
<v-list-item-content>
|
|
50
|
+
<v-list-item-title>
|
|
51
|
+
{{
|
|
52
|
+
contentBlock.block.body.replace(
|
|
53
|
+
/<[^>]*>?/gm,
|
|
54
|
+
''
|
|
55
|
+
)
|
|
56
|
+
}}
|
|
57
|
+
</v-list-item-title>
|
|
58
|
+
</v-list-item-content>
|
|
59
|
+
<v-list-item-action>
|
|
60
|
+
<v-checkbox
|
|
61
|
+
:input-value="active"
|
|
62
|
+
color="primary"
|
|
63
|
+
></v-checkbox>
|
|
64
|
+
</v-list-item-action>
|
|
65
|
+
</template>
|
|
66
|
+
</v-list-item>
|
|
67
|
+
</draggable>
|
|
68
|
+
</v-list-item-group>
|
|
69
|
+
</v-list>
|
|
70
|
+
</v-form>
|
|
71
|
+
</v-container>
|
|
72
|
+
</template>
|
|
73
|
+
|
|
74
|
+
<script>
|
|
75
|
+
import _ from 'lodash'
|
|
76
|
+
import { mapGetters } from 'vuex'
|
|
77
|
+
import draggable from 'vuedraggable'
|
|
78
|
+
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
79
|
+
import BaseContentSettings from '~/components/Content/Tool/BaseContentSettings.js'
|
|
80
|
+
import ContentBlock from '~/models/ContentBlock'
|
|
81
|
+
import Course from '~/models/Course'
|
|
82
|
+
|
|
83
|
+
export default {
|
|
84
|
+
name: 'ImageSettings',
|
|
85
|
+
extends: BaseContentSettings,
|
|
86
|
+
components: { draggable, TextEditor },
|
|
87
|
+
props: {
|
|
88
|
+
settings: { type: Object, required: false, default: null },
|
|
89
|
+
context: { type: String, required: false, default: 'block' },
|
|
90
|
+
},
|
|
91
|
+
data() {
|
|
92
|
+
return {
|
|
93
|
+
contentBlocks: [],
|
|
94
|
+
linked: [],
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
computed: {
|
|
98
|
+
...mapGetters({
|
|
99
|
+
organization: 'organization/get',
|
|
100
|
+
course: 'course/get',
|
|
101
|
+
enrollment: 'enrollment/get',
|
|
102
|
+
}),
|
|
103
|
+
dragOptions() {
|
|
104
|
+
return {
|
|
105
|
+
animation: 200,
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
async fetch() {
|
|
110
|
+
this.contentBlocks = await ContentBlock.where(
|
|
111
|
+
'tag',
|
|
112
|
+
'plugin-core-open-response'
|
|
113
|
+
)
|
|
114
|
+
.for(new Course({ id: this.course.id }))
|
|
115
|
+
.get()
|
|
116
|
+
},
|
|
117
|
+
beforeMount() {
|
|
118
|
+
if (_.isEmpty(this.block)) {
|
|
119
|
+
this.block = {}
|
|
120
|
+
}
|
|
121
|
+
if (_.isEmpty(this.block.body)) {
|
|
122
|
+
this.block.body = ''
|
|
123
|
+
}
|
|
124
|
+
if (_.isEmpty(this.block.metadata)) {
|
|
125
|
+
this.block.metadata = {}
|
|
126
|
+
}
|
|
127
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
128
|
+
this.block.metadata.config = {}
|
|
129
|
+
}
|
|
130
|
+
if (_.isEmpty(this.block.metadata.config.linked)) {
|
|
131
|
+
this.block.metadata.config.linked = []
|
|
132
|
+
}
|
|
133
|
+
if (_.isEmpty(this.block.metadata.config.filename)) {
|
|
134
|
+
this.block.metadata.config.filename = ''
|
|
135
|
+
}
|
|
136
|
+
if (_.isEmpty(this.block.metadata.config.include_prompts)) {
|
|
137
|
+
this.block.metadata.config.include_prompts = false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.linked = this.block.metadata.config.linked
|
|
141
|
+
},
|
|
142
|
+
watch: {},
|
|
143
|
+
mounted() {},
|
|
144
|
+
methods: {
|
|
145
|
+
// Called when the order changes
|
|
146
|
+
onDragChange(e) {
|
|
147
|
+
this.setLinkedBlocks()
|
|
148
|
+
},
|
|
149
|
+
// Called when blocks are added / removed
|
|
150
|
+
onLinkedChange() {
|
|
151
|
+
this.setLinkedBlocks()
|
|
152
|
+
},
|
|
153
|
+
setLinkedBlocks() {
|
|
154
|
+
const linkedContentIds = []
|
|
155
|
+
|
|
156
|
+
// Loop over content blocks since they have the correct order we want to collate
|
|
157
|
+
this.contentBlocks.forEach((contentBlock) => {
|
|
158
|
+
// If the unsorted this.linked includes the block id, add it in the right order
|
|
159
|
+
if (this.linked.includes(contentBlock.id)) {
|
|
160
|
+
linkedContentIds.push(contentBlock.id)
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// Set the linked and also body for convenience
|
|
165
|
+
this.block.metadata.config.linked = linkedContentIds
|
|
166
|
+
this.block.body = linkedContentIds.join()
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
</script>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-container>
|
|
3
|
+
<v-form>
|
|
4
|
+
<h3 class="pb-2">
|
|
5
|
+
{{
|
|
6
|
+
$t(
|
|
7
|
+
'windward.core.components.settings.open_response.question'
|
|
8
|
+
)
|
|
9
|
+
}}
|
|
10
|
+
</h3>
|
|
11
|
+
<TextEditor v-model="block.body" :height="200"></TextEditor>
|
|
12
|
+
|
|
13
|
+
<h3 class="pb-2">
|
|
14
|
+
{{
|
|
15
|
+
$t(
|
|
16
|
+
'windward.core.components.settings.open_response.sample_response'
|
|
17
|
+
)
|
|
18
|
+
}}
|
|
19
|
+
</h3>
|
|
20
|
+
<TextEditor
|
|
21
|
+
v-model="block.metadata.config.sample_response"
|
|
22
|
+
:height="200"
|
|
23
|
+
></TextEditor>
|
|
24
|
+
|
|
25
|
+
<h3 class="pb-2">
|
|
26
|
+
{{
|
|
27
|
+
$t(
|
|
28
|
+
'windward.core.components.settings.open_response.starting_text'
|
|
29
|
+
)
|
|
30
|
+
}}
|
|
31
|
+
</h3>
|
|
32
|
+
<TextEditor
|
|
33
|
+
v-model="block.metadata.config.starting_text"
|
|
34
|
+
:height="200"
|
|
35
|
+
></TextEditor>
|
|
36
|
+
</v-form>
|
|
37
|
+
</v-container>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script>
|
|
41
|
+
import _ from 'lodash'
|
|
42
|
+
import TextEditor from '~/components/Text/TextEditor.vue'
|
|
43
|
+
import BaseContentSettings from '~/components/Content/Tool/BaseContentSettings.js'
|
|
44
|
+
import ContentBlockAsset from '~/components/Content/ContentBlockAsset.vue'
|
|
45
|
+
|
|
46
|
+
export default {
|
|
47
|
+
name: 'ImageSettings',
|
|
48
|
+
extends: BaseContentSettings,
|
|
49
|
+
components: { ContentBlockAsset, TextEditor },
|
|
50
|
+
props: {
|
|
51
|
+
settings: { type: Object, required: false, default: null },
|
|
52
|
+
context: { type: String, required: false, default: 'block' },
|
|
53
|
+
},
|
|
54
|
+
beforeMount() {
|
|
55
|
+
if (_.isEmpty(this.block)) {
|
|
56
|
+
this.block = {}
|
|
57
|
+
}
|
|
58
|
+
if (_.isEmpty(this.block.body)) {
|
|
59
|
+
this.block.body = ''
|
|
60
|
+
}
|
|
61
|
+
if (_.isEmpty(this.block.metadata)) {
|
|
62
|
+
this.block.metadata = {}
|
|
63
|
+
}
|
|
64
|
+
if (_.isEmpty(this.block.metadata.config)) {
|
|
65
|
+
this.block.metadata.config = {}
|
|
66
|
+
}
|
|
67
|
+
if (_.isEmpty(this.block.metadata.config.sample_response)) {
|
|
68
|
+
this.block.metadata.config.sample_response = ''
|
|
69
|
+
}
|
|
70
|
+
if (_.isEmpty(this.block.metadata.config.starting_text)) {
|
|
71
|
+
this.block.metadata.config.starting_text = ''
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
data() {
|
|
75
|
+
return {}
|
|
76
|
+
},
|
|
77
|
+
watch: {},
|
|
78
|
+
mounted() {},
|
|
79
|
+
methods: {},
|
|
80
|
+
}
|
|
81
|
+
</script>
|
|
@@ -26,6 +26,17 @@ export default {
|
|
|
26
26
|
value: { type: String, required: true, default: '' },
|
|
27
27
|
api_key: { type: String, required: true, default: '' },
|
|
28
28
|
height: { type: Number, required: false, default: null },
|
|
29
|
+
menubar: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: false,
|
|
32
|
+
default: 'file edit insert format view table windward help',
|
|
33
|
+
},
|
|
34
|
+
toolbar: {
|
|
35
|
+
type: String,
|
|
36
|
+
required: false,
|
|
37
|
+
default:
|
|
38
|
+
'undo redo | formatselect | bold italic | alignleft aligncenter alignright | table bullist numlist outdent indent | mathButton ',
|
|
39
|
+
},
|
|
29
40
|
},
|
|
30
41
|
beforeMount() {
|
|
31
42
|
this.text = this.value
|
|
@@ -50,7 +61,7 @@ export default {
|
|
|
50
61
|
return {
|
|
51
62
|
height: 500,
|
|
52
63
|
visual: false,
|
|
53
|
-
menubar:
|
|
64
|
+
menubar: this.menubar,
|
|
54
65
|
menu: {
|
|
55
66
|
insert: {
|
|
56
67
|
title: 'Insert',
|
|
@@ -67,8 +78,7 @@ export default {
|
|
|
67
78
|
'anchor insertdatetime ',
|
|
68
79
|
'paste code wordcount table WindwardToolKit ',
|
|
69
80
|
],
|
|
70
|
-
toolbar:
|
|
71
|
-
'undo redo | formatselect | bold italic | alignleft aligncenter alignright | table bullist numlist outdent indent | mathButton ',
|
|
81
|
+
toolbar: this.toolbar,
|
|
72
82
|
table_advtab: false,
|
|
73
83
|
table_cell_advtab: false,
|
|
74
84
|
table_row_advtab: false,
|
|
@@ -4,6 +4,8 @@ import video from './video'
|
|
|
4
4
|
import table from './table'
|
|
5
5
|
import tab from './tab'
|
|
6
6
|
import feedback from './feedback'
|
|
7
|
+
import open_response from './open_response'
|
|
8
|
+
import open_response_collate from './open_response_collate'
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
11
|
user_upload,
|
|
@@ -12,4 +14,6 @@ export default {
|
|
|
12
14
|
table,
|
|
13
15
|
tab,
|
|
14
16
|
feedback,
|
|
17
|
+
open_response,
|
|
18
|
+
open_response_collate,
|
|
15
19
|
}
|
|
@@ -3,11 +3,15 @@ import user_upload from './user_upload'
|
|
|
3
3
|
import text_editor from './text_editor'
|
|
4
4
|
import video from './video'
|
|
5
5
|
import clickable_icon from './clickable_icon'
|
|
6
|
+
import open_response from './open_response'
|
|
7
|
+
import open_response_collate from './open_response_collate'
|
|
6
8
|
|
|
7
9
|
export default {
|
|
8
10
|
image,
|
|
9
11
|
user_upload,
|
|
10
12
|
text_editor,
|
|
11
13
|
video,
|
|
12
|
-
clickable_icon
|
|
14
|
+
clickable_icon,
|
|
15
|
+
open_response,
|
|
16
|
+
open_response_collate,
|
|
13
17
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
title: {
|
|
3
3
|
assessment: 'Assessment Settings',
|
|
4
|
+
open_response: 'Open Response Settings',
|
|
5
|
+
open_response_collate: 'Open Response Collate Settings',
|
|
4
6
|
image: 'Image Settings',
|
|
5
7
|
user_upload: 'User Upload Settings',
|
|
6
8
|
tab_settings: 'Tab Settings',
|
|
@@ -10,6 +12,6 @@ export default {
|
|
|
10
12
|
video: 'Video Settings',
|
|
11
13
|
table: 'Table Settings',
|
|
12
14
|
math: 'Math Settings',
|
|
13
|
-
feedback: 'Feedback Settings'
|
|
15
|
+
feedback: 'Feedback Settings',
|
|
14
16
|
},
|
|
15
17
|
}
|
|
@@ -4,6 +4,8 @@ import video from './video'
|
|
|
4
4
|
import table from './table'
|
|
5
5
|
import tab from './tab'
|
|
6
6
|
import feedback from './feedback'
|
|
7
|
+
import open_response from './open_response'
|
|
8
|
+
import open_response_collate from './open_response_collate'
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
11
|
user_upload,
|
|
@@ -12,4 +14,6 @@ export default {
|
|
|
12
14
|
table,
|
|
13
15
|
tab,
|
|
14
16
|
feedback,
|
|
17
|
+
open_response,
|
|
18
|
+
open_response_collate,
|
|
15
19
|
}
|
|
@@ -3,6 +3,8 @@ import user_upload from './user_upload'
|
|
|
3
3
|
import text_editor from './text_editor'
|
|
4
4
|
import video from './video'
|
|
5
5
|
import clickable_icon from './clickable_icon'
|
|
6
|
+
import open_response from './open_response'
|
|
7
|
+
import open_response_collate from './open_response_collate'
|
|
6
8
|
|
|
7
9
|
export default {
|
|
8
10
|
image,
|
|
@@ -10,4 +12,6 @@ export default {
|
|
|
10
12
|
text_editor,
|
|
11
13
|
video,
|
|
12
14
|
clickable_icon,
|
|
15
|
+
open_response,
|
|
16
|
+
open_response_collate,
|
|
13
17
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
instructions:
|
|
3
|
+
'Marque las respuestas abiertas para cotejar. Arrastra las indicaciones para establecer el orden en el que deseas que se cotejen las respuestas',
|
|
4
|
+
include_prompts: 'Incluir mensajes',
|
|
5
|
+
filename:
|
|
6
|
+
'Nombre de archivo (en blanco para el nombre de la página actual)',
|
|
7
|
+
}
|
|
@@ -7,6 +7,8 @@ export default {
|
|
|
7
7
|
rich_text: 'Texto enriquecido',
|
|
8
8
|
math: 'Matemáticas',
|
|
9
9
|
accordion: 'Acordeón',
|
|
10
|
+
open_response: 'Respuesta abierta',
|
|
11
|
+
open_response_collate: 'Intercalación de respuesta abierta',
|
|
10
12
|
image: 'Imagen',
|
|
11
13
|
user_upload: 'Carga de usuario',
|
|
12
14
|
clickable_icons: 'Iconos en los que se puede hacer clic',
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
title: {
|
|
3
3
|
assessment: 'Configuración de evaluación',
|
|
4
|
+
open_response: 'Abrir configuración de respuesta',
|
|
5
|
+
open_response_collate:
|
|
6
|
+
'Abrir configuración de clasificación de respuestas',
|
|
4
7
|
image: 'Configuración de imagen',
|
|
5
8
|
user_upload: 'Configuración de carga del usuario',
|
|
6
9
|
tab_settings: 'Configuración de pestaña',
|
|
@@ -4,6 +4,8 @@ import video from './video'
|
|
|
4
4
|
import table from './table'
|
|
5
5
|
import tab from './tab'
|
|
6
6
|
import feedback from './feedback'
|
|
7
|
+
import open_response from './open_response'
|
|
8
|
+
import open_response_collate from './open_response_collate'
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
11
|
user_upload,
|
|
@@ -12,4 +14,6 @@ export default {
|
|
|
12
14
|
table,
|
|
13
15
|
tab,
|
|
14
16
|
feedback,
|
|
17
|
+
open_response,
|
|
18
|
+
open_response_collate,
|
|
15
19
|
}
|
|
@@ -3,11 +3,15 @@ import user_upload from './user_upload'
|
|
|
3
3
|
import text_editor from './text_editor'
|
|
4
4
|
import video from './video'
|
|
5
5
|
import clickable_icon from './clickable_icon'
|
|
6
|
+
import open_response from './open_response'
|
|
7
|
+
import open_response_collate from './open_response_collate'
|
|
6
8
|
|
|
7
9
|
export default {
|
|
8
10
|
image,
|
|
9
11
|
user_upload,
|
|
10
12
|
text_editor,
|
|
11
13
|
video,
|
|
12
|
-
clickable_icon
|
|
14
|
+
clickable_icon,
|
|
15
|
+
open_response,
|
|
16
|
+
open_response_collate,
|
|
13
17
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
title: {
|
|
3
3
|
assessment: 'Bedömningsinställningar',
|
|
4
|
+
open_response: 'Öppna svarsinställningar',
|
|
5
|
+
open_response_collate: 'Öppna inställningar för svarssortering',
|
|
4
6
|
image: 'Bildinställningar',
|
|
5
7
|
user_upload: 'User Upload Settings',
|
|
6
8
|
tab_settings: 'Flikinställningar',
|
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -11,6 +11,8 @@ import ClickableIcons from './components/Content/Blocks/ClickableIcons'
|
|
|
11
11
|
|
|
12
12
|
import UserUploadNav from './components/Navigation/Items/UserUploadNav.vue'
|
|
13
13
|
|
|
14
|
+
import OpenResponse from './components/Content/Blocks/OpenResponse'
|
|
15
|
+
import OpenResponseCollate from './components/Content/Blocks/OpenResponseCollate'
|
|
14
16
|
import Image from './components/Content/Blocks/Image'
|
|
15
17
|
import UserUpload from './components/Content/Blocks/UserUpload'
|
|
16
18
|
|
|
@@ -18,7 +20,10 @@ import GlossaryPage from './pages/glossary.vue'
|
|
|
18
20
|
import CourseGlossaryToolNav from './components/Navigation/Items/CourseGlossaryToolNav.vue'
|
|
19
21
|
import GlossaryNav from './components/Navigation/Items/GlossaryNav.vue'
|
|
20
22
|
import AskTheExpert from './components/Navigation/Items/AskTheExpert.vue'
|
|
23
|
+
|
|
21
24
|
// Entrypoint for npm
|
|
25
|
+
import OpenResponseSettings from './components/Settings/OpenResponseSettings.vue'
|
|
26
|
+
import OpenResponseCollateSettings from './components/Settings/OpenResponseCollateSettings.vue'
|
|
22
27
|
import ImageSettings from './components/Settings/ImageSettings.vue'
|
|
23
28
|
import UserUploadSettings from './components/Settings/UserUploadSettings.vue'
|
|
24
29
|
import ClickableIconsSettings from './components/Settings/ClickableIconsSettings.vue'
|
|
@@ -176,6 +181,26 @@ export default {
|
|
|
176
181
|
'windward.core.shared.content_blocks.grouping.basic',
|
|
177
182
|
},
|
|
178
183
|
},
|
|
184
|
+
{
|
|
185
|
+
tag: 'core-open-response',
|
|
186
|
+
template: OpenResponse,
|
|
187
|
+
metadata: {
|
|
188
|
+
icon: 'mdi-typewriter',
|
|
189
|
+
name: 'windward.core.shared.content_blocks.title.open_response',
|
|
190
|
+
grouping:
|
|
191
|
+
'windward.core.shared.content_blocks.grouping.basic',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
tag: 'core-open-response-collate',
|
|
196
|
+
template: OpenResponseCollate,
|
|
197
|
+
metadata: {
|
|
198
|
+
icon: 'mdi-typewriter',
|
|
199
|
+
name: 'windward.core.shared.content_blocks.title.open_response_collate',
|
|
200
|
+
grouping:
|
|
201
|
+
'windward.core.shared.content_blocks.grouping.basic',
|
|
202
|
+
},
|
|
203
|
+
},
|
|
179
204
|
{
|
|
180
205
|
tag: 'core-image',
|
|
181
206
|
template: Image,
|
|
@@ -218,6 +243,24 @@ export default {
|
|
|
218
243
|
},
|
|
219
244
|
],
|
|
220
245
|
settings: [
|
|
246
|
+
{
|
|
247
|
+
tag: 'core-open-response-settings',
|
|
248
|
+
template: OpenResponseSettings,
|
|
249
|
+
context: ['block.core-open-response'],
|
|
250
|
+
metadata: {
|
|
251
|
+
icon: 'mdi-cog',
|
|
252
|
+
name: 'windward.core.shared.settings.title.open_response',
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
tag: 'core-open-response-collate-settings',
|
|
257
|
+
template: OpenResponseCollateSettings,
|
|
258
|
+
context: ['block.core-open-response-collate'],
|
|
259
|
+
metadata: {
|
|
260
|
+
icon: 'mdi-cog',
|
|
261
|
+
name: 'windward.core.shared.settings.title.open_response_collate',
|
|
262
|
+
},
|
|
263
|
+
},
|
|
221
264
|
{
|
|
222
265
|
tag: 'core-image-settings',
|
|
223
266
|
template: ImageSettings,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils'
|
|
2
|
+
import Vue from 'vue'
|
|
3
|
+
import Vuetify from 'vuetify'
|
|
4
|
+
import { defaultMocks } from '@/test/mocks'
|
|
5
|
+
import OpenResponse from '@/components/Content/Blocks/OpenResponse.vue'
|
|
6
|
+
|
|
7
|
+
Vue.use(Vuetify)
|
|
8
|
+
|
|
9
|
+
describe('OpenResponse content block component', () => {
|
|
10
|
+
test('is a Vue instance', () => {
|
|
11
|
+
const wrapper = shallowMount(OpenResponse, {
|
|
12
|
+
vuetify: new Vuetify(),
|
|
13
|
+
mocks: defaultMocks,
|
|
14
|
+
propsData: {
|
|
15
|
+
value: {
|
|
16
|
+
id: '00000000-0000-0000-0000-000000000000',
|
|
17
|
+
tag: 'plugin-core-open-response',
|
|
18
|
+
body: '<p>Test Block</p>',
|
|
19
|
+
metadata: {
|
|
20
|
+
config: {
|
|
21
|
+
sample_response: 'sample response example',
|
|
22
|
+
starting_text: 'starting text example',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
expect(wrapper.vm).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils'
|
|
2
|
+
import Vue from 'vue'
|
|
3
|
+
import Vuetify from 'vuetify'
|
|
4
|
+
import { defaultMocks } from '@/test/mocks'
|
|
5
|
+
import Image from '@/components/Content/Blocks/Image.vue'
|
|
6
|
+
|
|
7
|
+
Vue.use(Vuetify)
|
|
8
|
+
|
|
9
|
+
describe('Image content block component', () => {
|
|
10
|
+
test('is a Vue instance', () => {
|
|
11
|
+
const wrapper = shallowMount(Image, {
|
|
12
|
+
vuetify: new Vuetify(),
|
|
13
|
+
mocks: defaultMocks,
|
|
14
|
+
propsData: {
|
|
15
|
+
value: {
|
|
16
|
+
id: '00000000-0000-0000-0000-000000000000',
|
|
17
|
+
tag: 'plugin-core-open-response-collate',
|
|
18
|
+
body: '00000000-0000-0000-0000-000000000001,00000000-0000-0000-0000-000000000002,00000000-0000-0000-0000-000000000003',
|
|
19
|
+
metadata: {
|
|
20
|
+
config: {
|
|
21
|
+
linked: [
|
|
22
|
+
'00000000-0000-0000-0000-000000000001',
|
|
23
|
+
'00000000-0000-0000-0000-000000000002',
|
|
24
|
+
'00000000-0000-0000-0000-000000000003',
|
|
25
|
+
],
|
|
26
|
+
filename: 'example_filename_override',
|
|
27
|
+
include_prompts: true,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expect(wrapper.vm).toBeTruthy()
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils'
|
|
2
|
+
import Vuetify from 'vuetify'
|
|
3
|
+
import Vue from 'vue'
|
|
4
|
+
|
|
5
|
+
import { defaultMocks } from '@/test/mocks'
|
|
6
|
+
import OpenResponseCollateSettings from '@/components/Settings/OpenResponseCollateSettings'
|
|
7
|
+
|
|
8
|
+
Vue.use(Vuetify)
|
|
9
|
+
|
|
10
|
+
describe('OpenResponseCollateSettings', () => {
|
|
11
|
+
test('is a Vue instance', () => {
|
|
12
|
+
const wrapper = shallowMount(OpenResponseCollateSettings, {
|
|
13
|
+
propsData: {
|
|
14
|
+
tag: 'core-open-response-collate-settings',
|
|
15
|
+
},
|
|
16
|
+
mocks: defaultMocks,
|
|
17
|
+
})
|
|
18
|
+
expect(wrapper.vm).toBeTruthy()
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { shallowMount } from '@vue/test-utils'
|
|
2
|
+
import Vuetify from 'vuetify'
|
|
3
|
+
import Vue from 'vue'
|
|
4
|
+
|
|
5
|
+
import { defaultMocks } from '@/test/mocks'
|
|
6
|
+
import OpenResponseSettings from '@/components/Settings/OpenResponseSettings'
|
|
7
|
+
|
|
8
|
+
Vue.use(Vuetify)
|
|
9
|
+
|
|
10
|
+
describe('OpenResponseSettings', () => {
|
|
11
|
+
test('is a Vue instance', () => {
|
|
12
|
+
const wrapper = shallowMount(OpenResponseSettings, {
|
|
13
|
+
propsData: {
|
|
14
|
+
tag: 'core-open-response-settings',
|
|
15
|
+
},
|
|
16
|
+
mocks: defaultMocks,
|
|
17
|
+
})
|
|
18
|
+
expect(wrapper.vm).toBeTruthy()
|
|
19
|
+
})
|
|
20
|
+
})
|