diff --git a/lib/CourseThemeModule.js b/lib/CourseThemeModule.js index d943b3e..9261378 100644 --- a/lib/CourseThemeModule.js +++ b/lib/CourseThemeModule.js @@ -1,7 +1,11 @@ import _ from 'lodash' import AbstractApiModule from 'adapt-authoring-api' import fs from 'fs/promises' +import semver from 'semver' import { getVariablesString, processCustomStyling } from './utils.js' + +const CONFIG_THEME_VARS_MIN_VERSION = '6.61.0' // adjust to actual release version + /** * Module which handles course theming * @memberof coursetheme @@ -26,7 +30,22 @@ class CourseThemeModule extends AbstractApiModule { * @type {ContentModule} */ this.content = content + /** + * Cached module instance for easy access + * @type {AdaptFrameworkModule} + */ + this.framework = framework framework.preBuildHook.tap(this.writeCustomLess.bind(this)) + framework.registerImportContentMigration(async (data, importer) => { + if (data._type === 'course' && data.themeVariables) { + importer._themeVariablesMigration = data.themeVariables + delete data.themeVariables + } + if (data._type === 'config' && importer._themeVariablesMigration) { + data.themeVariables = importer._themeVariablesMigration + delete importer._themeVariablesMigration + } + }) } /** @@ -34,12 +53,20 @@ class CourseThemeModule extends AbstractApiModule { * @param {AdaptFrameworkBuild} fwBuild Reference to the current build */ async writeCustomLess (fwBuild) { - const fontImportString = await this.processFileVariables(fwBuild) - processCustomStyling(fwBuild.courseData) + const configData = fwBuild.courseData.config.data + const courseData = fwBuild.courseData.course.data + // prefer config, fall back to course for unmigrated data + const variables = configData[this.attributeKey] || courseData[this.attributeKey] || {} + + const fontImportString = await this.processFileVariables(fwBuild, variables) + processCustomStyling(fwBuild.courseData, variables) + + const { customStyle, themeCustomStyle } = courseData + const frameworkHandlesVars = semver.gte(this.framework.version, CONFIG_THEME_VARS_MIN_VERSION) - const { customStyle, [this.attributeKey]: variables, themeCustomStyle } = fwBuild.courseData.course.data return Promise.all([ - this.writeFile(fwBuild, '1-variables.less', fontImportString + getVariablesString(variables)), + this.writeFile(fwBuild, '1-variables.less', + frameworkHandlesVars ? fontImportString : fontImportString + getVariablesString(variables)), this.writeFile(fwBuild, '2-customStyles.less', customStyle), this.writeFile(fwBuild, '3-themeCustomStyles.less', themeCustomStyle) ]) @@ -74,8 +101,9 @@ class CourseThemeModule extends AbstractApiModule { async applyHandler (req, res, next) { try { const { _id, courseId } = req.apiData.query - const [{ properties: presetProps }] = await this.find({ _id }) - await this.content.update({ _id: courseId }, { [this.attributeKey]: presetProps }) + const { properties: presetProps } = await this.findOne({ _id }) + const config = await this.content.findOne({ _courseId: courseId, _type: 'config' }) + await this.content.update({ _id: config._id }, { [this.attributeKey]: presetProps }) res.sendStatus(204) } catch (e) { return next(e) @@ -91,9 +119,10 @@ class CourseThemeModule extends AbstractApiModule { async removeHandler (req, res, next) { try { const { _id, courseId } = req.apiData.query - const [{ properties: presetProps }] = await this.find({ _id }) - const [{ themeVariables: existingProps }] = await this.content.find({ _id: courseId }) - await this.content.update({ _id: courseId }, { [this.attributeKey]: _.pickBy(existingProps, (v, k) => v !== presetProps[k]) }) + const { properties: presetProps } = await this.findOne({ _id }) + const config = await this.content.findOne({ _courseId: courseId, _type: 'config' }) + const existingProps = config[this.attributeKey] || {} + await this.content.update({ _id: config._id }, { [this.attributeKey]: _.pickBy(existingProps, (v, k) => v !== presetProps[k]) }) res.sendStatus(204) } catch (e) { return next(e) @@ -102,11 +131,12 @@ class CourseThemeModule extends AbstractApiModule { /** * Copies uploaded font files into the build - * @param {object} data The data to process + * @param {AdaptFrameworkBuild} fwBuild Reference to the current build + * @param {Object} variables The theme variables object */ - async processFileVariables (fwBuild) { + async processFileVariables (fwBuild, variables) { const assets = await this.app.waitForModule('assets') - const fontData = fwBuild.courseData.course.data.themeVariables._font + const fontData = variables?._font if (!fontData) { return '' @@ -118,7 +148,7 @@ class CourseThemeModule extends AbstractApiModule { if (!f['font-family']) return // copy each uploaded font file for (const font in f._files) { - const [data] = await assets.find({ _id: f._files[font] }) + const data = await assets.findOne({ _id: f._files[font] }) const asset = assets.createFsWrapper(data) try { const relativeFontPath = `fonts/${f._files[font]}.${asset.data.subtype}` diff --git a/lib/utils/processCustomStyling.js b/lib/utils/processCustomStyling.js index 4509634..9ebd441 100644 --- a/lib/utils/processCustomStyling.js +++ b/lib/utils/processCustomStyling.js @@ -2,14 +2,15 @@ * Processes custom styling by moving the themeCustomStyle attribute from * themeVariables to the top-level course data * @param {Object} courseData The course data object (fwBuild.courseData) + * @param {Object} variables The theme variables object * @memberof coursetheme */ -export function processCustomStyling (courseData) { - const customStyling = courseData.course.data.themeVariables.themeCustomStyle +export function processCustomStyling (courseData, variables) { + const customStyling = variables?.themeCustomStyle if (!customStyling) return courseData.course.data.themeCustomStyle = customStyling - delete courseData.course.data.themeVariables.themeCustomStyle + delete variables.themeCustomStyle } diff --git a/package.json b/package.json index d82f076..dd9d4f0 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "repository": "github:adapt-security/adapt-authoring-coursetheme", "dependencies": { "adapt-authoring-api": "^3.0.0", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "semver": "^7.6.0" }, "peerDependencies": { "adapt-authoring-adaptframework": "^2.0.0",