adapt-authoring-adaptframework 1.6.0 → 1.7.0
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/lib/AdaptFrameworkBuild.js +1 -0
- package/lib/AdaptFrameworkImport.js +133 -131
- package/lib/AdaptFrameworkModule.js +0 -6
- package/lib/AdaptFrameworkUtils.js +3 -3
- package/lib/apidefs.js +1 -0
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ class AdaptFrameworkBuild {
|
|
|
40
40
|
* @property {String} courseId The course to build
|
|
41
41
|
* @property {String} userId The user executing the build
|
|
42
42
|
* @property {String} expiresAt When the build expires
|
|
43
|
+
* @property {Boolean} compress Whether output files should be compressed into an archive file
|
|
43
44
|
*
|
|
44
45
|
* @constructor
|
|
45
46
|
* @param {AdaptFrameworkBuildOptions} options
|
|
@@ -4,6 +4,7 @@ import { glob } from 'glob'
|
|
|
4
4
|
import octopus from 'adapt-octopus'
|
|
5
5
|
import path from 'upath'
|
|
6
6
|
import semver from 'semver'
|
|
7
|
+
import { unzip } from 'zipper'
|
|
7
8
|
|
|
8
9
|
import FWUtils from './AdaptFrameworkUtils.js'
|
|
9
10
|
|
|
@@ -61,7 +62,7 @@ class AdaptFrameworkImport {
|
|
|
61
62
|
/**
|
|
62
63
|
* Options to be passed to AdaptFrameworkBuild
|
|
63
64
|
* @typedef {Object} AdaptFrameworkImportOptions
|
|
64
|
-
* @property {String}
|
|
65
|
+
* @property {String} importPath Path to the import package
|
|
65
66
|
* @property {String} userId Owner of the imported course
|
|
66
67
|
* @property {String} language Language of course files to be imported (if not specified, _defaultLanguage will be used)
|
|
67
68
|
* @property {Array<String>} assetFolders List of non-standard asset directories
|
|
@@ -75,124 +76,122 @@ class AdaptFrameworkImport {
|
|
|
75
76
|
* @constructor
|
|
76
77
|
* @param {AdaptFrameworkImportOptions} options
|
|
77
78
|
*/
|
|
78
|
-
constructor ({
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* User-defined settings related to what is included with the import
|
|
169
|
-
* @type {Object}
|
|
170
|
-
*/
|
|
171
|
-
this.settings = {
|
|
172
|
-
isDryRun,
|
|
173
|
-
importContent,
|
|
174
|
-
importPlugins,
|
|
175
|
-
migrateContent,
|
|
176
|
-
updatePlugins,
|
|
177
|
-
removeSource
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Invoked before the import process has started
|
|
181
|
-
*/
|
|
182
|
-
this.preImportHook = new Hook()
|
|
183
|
-
/**
|
|
184
|
-
* Invoked once the import process has completed
|
|
185
|
-
*/
|
|
186
|
-
this.postImportHook = new Hook()
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* plugins on import that are of a lower version than the installed version
|
|
190
|
-
* @type {Array}
|
|
191
|
-
*/
|
|
192
|
-
this.pluginsToMigrate = ['core']
|
|
193
|
-
} catch (e) {
|
|
194
|
-
throw App.instance.errors.FW_IMPORT_INVALID_COURSE
|
|
79
|
+
constructor ({ importPath, userId, language, assetFolders, tags, isDryRun, importContent = true, importPlugins = true, migrateContent = true, updatePlugins = false, removeSource = true }) {
|
|
80
|
+
const e = App.instance.errors.INVALID_PARAMS
|
|
81
|
+
if (!importPath) throw e.setData({ params: ['importPath'] })
|
|
82
|
+
if (!userId) throw e.INVALID_PARAMS.setData({ params: ['userId'] })
|
|
83
|
+
/**
|
|
84
|
+
* Reference to the package.json data
|
|
85
|
+
* @type {Object}
|
|
86
|
+
*/
|
|
87
|
+
this.pkg = undefined
|
|
88
|
+
/**
|
|
89
|
+
* Path of the import package
|
|
90
|
+
* @type {String}
|
|
91
|
+
*/
|
|
92
|
+
this.path = importPath.replaceAll('\\', '/')
|
|
93
|
+
/**
|
|
94
|
+
* Language code for the langauge to import. Used for locating course content.
|
|
95
|
+
* @type {String}
|
|
96
|
+
*/
|
|
97
|
+
this.language = language
|
|
98
|
+
/**
|
|
99
|
+
* List of asset folders to check
|
|
100
|
+
* @type {Array<String>}
|
|
101
|
+
*/
|
|
102
|
+
this.assetFolders = assetFolders ?? ['assets']
|
|
103
|
+
/**
|
|
104
|
+
* List of asset metadata
|
|
105
|
+
* @type {Array}
|
|
106
|
+
*/
|
|
107
|
+
this.assetData = []
|
|
108
|
+
/**
|
|
109
|
+
* List of tags to apply to the course
|
|
110
|
+
* @type {Array<String>}
|
|
111
|
+
*/
|
|
112
|
+
this.tags = tags
|
|
113
|
+
/**
|
|
114
|
+
* Path to the import course folder
|
|
115
|
+
* @type {String}
|
|
116
|
+
*/
|
|
117
|
+
this.coursePath = undefined
|
|
118
|
+
/**
|
|
119
|
+
* Path to the import course language folder
|
|
120
|
+
* @type {String}
|
|
121
|
+
*/
|
|
122
|
+
this.langPath = undefined
|
|
123
|
+
/**
|
|
124
|
+
* A cache of the import's content JSON file data (note this is not the DB data used by the application)
|
|
125
|
+
* @type {Object}
|
|
126
|
+
*/
|
|
127
|
+
this.contentJson = {
|
|
128
|
+
course: {},
|
|
129
|
+
contentObjects: []
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Key/value store of the installed content plugins
|
|
133
|
+
* @type {Object}
|
|
134
|
+
*/
|
|
135
|
+
this.usedContentPlugins = {}
|
|
136
|
+
/**
|
|
137
|
+
* All plugins installed during the import as a name -> metadata map
|
|
138
|
+
* @type {Object}
|
|
139
|
+
*/
|
|
140
|
+
this.newContentPlugins = {}
|
|
141
|
+
/**
|
|
142
|
+
* Key/value store mapping old component keys to component names
|
|
143
|
+
* @type {Object}
|
|
144
|
+
*/
|
|
145
|
+
this.componentNameMap = {}
|
|
146
|
+
/**
|
|
147
|
+
* A key/value map of asset file names to new asset ids
|
|
148
|
+
* @type {Object}
|
|
149
|
+
*/
|
|
150
|
+
this.assetMap = {}
|
|
151
|
+
/**
|
|
152
|
+
* A key/value map of old ids to new ids
|
|
153
|
+
* @type {Object}
|
|
154
|
+
*/
|
|
155
|
+
this.idMap = {}
|
|
156
|
+
/**
|
|
157
|
+
* The _id of the user initiating the import
|
|
158
|
+
* @type {String}
|
|
159
|
+
*/
|
|
160
|
+
this.userId = userId
|
|
161
|
+
/**
|
|
162
|
+
* Contains non-fatal infomation messages regarding import status which can be return as response data. Fatal errors are thrown in the usual way.
|
|
163
|
+
* @type {Object}
|
|
164
|
+
*/
|
|
165
|
+
this.statusReport = {
|
|
166
|
+
info: [],
|
|
167
|
+
warn: []
|
|
195
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* User-defined settings related to what is included with the import
|
|
171
|
+
* @type {Object}
|
|
172
|
+
*/
|
|
173
|
+
this.settings = {
|
|
174
|
+
isDryRun,
|
|
175
|
+
importContent,
|
|
176
|
+
importPlugins,
|
|
177
|
+
migrateContent,
|
|
178
|
+
updatePlugins,
|
|
179
|
+
removeSource
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Invoked before the import process has started
|
|
183
|
+
*/
|
|
184
|
+
this.preImportHook = new Hook()
|
|
185
|
+
/**
|
|
186
|
+
* Invoked once the import process has completed
|
|
187
|
+
*/
|
|
188
|
+
this.postImportHook = new Hook()
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* plugins on import that are of a lower version than the installed version
|
|
192
|
+
* @type {Array}
|
|
193
|
+
*/
|
|
194
|
+
this.pluginsToMigrate = ['core']
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
/**
|
|
@@ -277,21 +276,24 @@ class AdaptFrameworkImport {
|
|
|
277
276
|
* @return {Promise}
|
|
278
277
|
*/
|
|
279
278
|
async prepare () {
|
|
279
|
+
if (this.path.endsWith('.zip')) {
|
|
280
|
+
this.path = await unzip(this.path, `${this.path}_unzip`, { removeSource: true })
|
|
281
|
+
}
|
|
280
282
|
try { // if it's a nested zip, move everything up a level
|
|
281
|
-
const files = await fs.readdir(this.
|
|
283
|
+
const files = await fs.readdir(this.path)
|
|
282
284
|
if (files.length === 1) {
|
|
283
|
-
const nestDir = `${this.
|
|
285
|
+
const nestDir = `${this.path}/${files[0]}`
|
|
284
286
|
await fs.stat(`${nestDir}/package.json`)
|
|
285
|
-
const newDir = path.join(`${this.
|
|
287
|
+
const newDir = path.join(`${this.path}_2`)
|
|
286
288
|
await fs.rename(nestDir, newDir)
|
|
287
|
-
await fs.rm(this.
|
|
288
|
-
this.
|
|
289
|
+
await fs.rm(this.path, { recursive: true })
|
|
290
|
+
this.path = newDir
|
|
289
291
|
}
|
|
290
292
|
} catch (e) {
|
|
291
293
|
// nothing to do
|
|
292
294
|
}
|
|
293
295
|
// find and store the course data path
|
|
294
|
-
const courseDirs = await glob(`${this.
|
|
296
|
+
const courseDirs = await glob(`${this.path}/*/course`)
|
|
295
297
|
if (courseDirs.length > 1) {
|
|
296
298
|
this.framework.log('error', 'MULTIPLE_COURSE_DIRS', courseDirs)
|
|
297
299
|
throw App.instance.errors.FW_IMPORT_INVALID_COURSE
|
|
@@ -305,16 +307,16 @@ class AdaptFrameworkImport {
|
|
|
305
307
|
this.framework.log('error', e)
|
|
306
308
|
throw App.instance.errors.FW_IMPORT_INVALID_COURSE
|
|
307
309
|
}
|
|
308
|
-
FWUtils.logDir('unzipPath', this.
|
|
310
|
+
FWUtils.logDir('unzipPath', this.path)
|
|
309
311
|
FWUtils.logDir('coursePath', this.coursePath)
|
|
310
312
|
|
|
311
313
|
try {
|
|
312
|
-
/** @ignore */this.pkg = await FWUtils.readJson(`${this.
|
|
314
|
+
/** @ignore */this.pkg = await FWUtils.readJson(`${this.path}/package.json`)
|
|
313
315
|
} catch (e) {
|
|
314
316
|
throw App.instance.errors.FW_IMPORT_INVALID
|
|
315
317
|
}
|
|
316
318
|
try {
|
|
317
|
-
await fs.rm(`${this.
|
|
319
|
+
await fs.rm(`${this.path}/package-lock.json`)
|
|
318
320
|
} catch (e) {}
|
|
319
321
|
|
|
320
322
|
if (!semver.satisfies(this.pkg.version, semver.major(this.framework.version).toString())) {
|
|
@@ -335,7 +337,7 @@ class AdaptFrameworkImport {
|
|
|
335
337
|
*/
|
|
336
338
|
async convertSchemas () {
|
|
337
339
|
return octopus.runRecursive({
|
|
338
|
-
cwd: this.
|
|
340
|
+
cwd: this.path,
|
|
339
341
|
logger: { log: (...args) => FWUtils.log('debug', ...args) }
|
|
340
342
|
})
|
|
341
343
|
}
|
|
@@ -344,7 +346,7 @@ class AdaptFrameworkImport {
|
|
|
344
346
|
* Writes the contents of 2-customStyles.less to course.json file. Unfortunately it's necessary to do it this way to ensure it's included in migrations.
|
|
345
347
|
*/
|
|
346
348
|
async patchCustomStyle () {
|
|
347
|
-
const [customStylePath] = await glob('**/2-customStyles.less', { cwd: this.
|
|
349
|
+
const [customStylePath] = await glob('**/2-customStyles.less', { cwd: this.path, absolute: true })
|
|
348
350
|
const courseJsonPath = `${this.langPath}/course.json`
|
|
349
351
|
if (!customStylePath) {
|
|
350
352
|
return
|
|
@@ -406,7 +408,7 @@ class AdaptFrameworkImport {
|
|
|
406
408
|
* @return {Promise}
|
|
407
409
|
*/
|
|
408
410
|
async loadPluginData () {
|
|
409
|
-
const usedPluginPaths = await glob(`${this.
|
|
411
|
+
const usedPluginPaths = await glob(`${this.path}/src/+(components|extensions|menu|theme)/*`, { absolute: true })
|
|
410
412
|
const getPluginType = pluginData => {
|
|
411
413
|
for (const type of ['component', 'extension', 'menu', 'theme']) {
|
|
412
414
|
if (pluginData[type] !== undefined) return type
|
|
@@ -802,7 +804,7 @@ class AdaptFrameworkImport {
|
|
|
802
804
|
}
|
|
803
805
|
try {
|
|
804
806
|
const tasks = [
|
|
805
|
-
fs.rm(this.
|
|
807
|
+
fs.rm(this.path, { recursive: true })
|
|
806
808
|
]
|
|
807
809
|
if (error) {
|
|
808
810
|
tasks.push(
|
|
@@ -6,7 +6,6 @@ import fs from 'fs/promises'
|
|
|
6
6
|
import FWUtils from './AdaptFrameworkUtils.js'
|
|
7
7
|
import path from 'path'
|
|
8
8
|
import semver from 'semver'
|
|
9
|
-
import { unzip } from 'zipper'
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Module to handle the interface with the Adapt framework
|
|
@@ -320,11 +319,6 @@ class AdaptFrameworkModule extends AbstractModule {
|
|
|
320
319
|
* @return {AdaptFrameworkImportSummary}
|
|
321
320
|
*/
|
|
322
321
|
async importCourse (options) {
|
|
323
|
-
let unzipPath = options.importPath
|
|
324
|
-
if (options.importPath.endsWith('.zip')) {
|
|
325
|
-
unzipPath = `${options.importPath}_unzip`
|
|
326
|
-
await unzip(options.importPath, unzipPath, { removeSource: true })
|
|
327
|
-
}
|
|
328
322
|
const importer = await AdaptFrameworkImport.run(options)
|
|
329
323
|
importer.preImportHook.tap(() => this.preImportHook.invoke(importer))
|
|
330
324
|
importer.postImportHook.tap(() => this.postImportHook.invoke(importer))
|
|
@@ -270,14 +270,14 @@ class AdaptFrameworkUtils {
|
|
|
270
270
|
*/
|
|
271
271
|
static async importHandler (req, res, next) {
|
|
272
272
|
try {
|
|
273
|
-
let
|
|
273
|
+
let importPath = req.body.importPath
|
|
274
274
|
if (req.get('Content-Type').indexOf('multipart/form-data') === 0) {
|
|
275
275
|
await AdaptFrameworkUtils.handleImportFile(req, res)
|
|
276
276
|
const [course] = req.fileUpload.files.course
|
|
277
|
-
|
|
277
|
+
importPath = course.filepath
|
|
278
278
|
}
|
|
279
279
|
const importer = await FrameworkImport.run({
|
|
280
|
-
|
|
280
|
+
importPath,
|
|
281
281
|
userId: req.auth.user._id.toString(),
|
|
282
282
|
isDryRun: AdaptFrameworkUtils.toBoolean(req.body.dryRun),
|
|
283
283
|
assetFolders: req.body.formAssetFolders,
|
package/lib/apidefs.js
CHANGED
|
@@ -54,6 +54,7 @@ export default {
|
|
|
54
54
|
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
55
55
|
type: 'object',
|
|
56
56
|
properties: {
|
|
57
|
+
path: { type: 'String' },
|
|
57
58
|
isDryRun: { type: 'Boolean', default: false },
|
|
58
59
|
importContent: { type: 'Boolean', default: true },
|
|
59
60
|
importPlugins: { type: 'Boolean', default: true },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adapt-authoring-adaptframework",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Adapt framework integration for the Adapt authoring tool",
|
|
5
5
|
"homepage": "https://github.com/adapt-security/adapt-authoring-adaptframework",
|
|
6
6
|
"license": "GPL-3.0",
|