diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +node_modules diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..b56d919 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,14 @@ +{ + "env": { + "browser": false, + "node": true, + "commonjs": false, + "es2020": true + }, + "extends": [ + "standard" + ], + "parserOptions": { + "ecmaVersion": 2020 + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..567e772 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,55 @@ +name: Bug Report +description: File a bug report +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: description + attributes: + label: What happened? + description: Please describe the issue + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behaviour + description: Tell us what should have happened + - type: textarea + id: repro-steps + attributes: + label: Steps to reproduce + description: Tell us how to reproduce the issue + validations: + required: true + - type: input + id: aat-version + attributes: + label: Authoring tool version + description: What version of the Adapt authoring tool are you running? + validations: + required: true + - type: input + id: fw-version + attributes: + label: Framework version + description: What version of the Adapt framework are you running? + - type: dropdown + id: browsers + attributes: + label: What browsers are you seeing the problem on? + multiple: true + options: + - Firefox + - Chrome + - Safari + - Microsoft Edge + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: sh diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ec4bb38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..2a344aa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,22 @@ +name: Feature request +description: Request a new feature +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to request a new feature in the Adapt authoring tool! The Adapt team will consider all new feature requests, but unfortunately cannot commit to every one. + - type: textarea + id: description + attributes: + label: Feature description + description: Please describe your feature request + validations: + required: true + - type: checkboxes + id: contribute + attributes: + label: Can you work on this feature? + description: If you are able to commit your own time to work on this feature, it will greatly increase the liklihood of it being implemented by the core dev team. Otherwise, it will be triaged and prioritised alongside the core team's current priorities. + options: + - label: I can contribute diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..33fe6d3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,25 @@ +[//]: # (Please title your PR according to eslint commit conventions) +[//]: # (See https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-eslint#eslint-convention for details) + +[//]: # (Add a link to the original issue) + +[//]: # (Delete as appropriate) +### Fix +* A sentence describing each fix + +### Update +* A sentence describing each udpate + +### New +* A sentence describing each new feature + +### Breaking +* A sentence describing each breaking change + +[//]: # (List appropriate steps for testing if needed) +### Testing +1. Steps for testing + +[//]: # (Mention any other dependencies) + + diff --git a/.github/workflows/new.yml b/.github/workflows/new.yml new file mode 100644 index 0000000..2651527 --- /dev/null +++ b/.github/workflows/new.yml @@ -0,0 +1,19 @@ +name: Add to main project + +on: + issues: + types: + - opened + pull_request: + types: + - opened + +jobs: + add-to-project: + name: Add to main project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v0.1.0 + with: + project-url: https://github.com/orgs/adapt-security/projects/5 + github-token: ${{ secrets.PROJECTS_SECRET }} diff --git a/index.js b/index.js index a44dc2c..e2e4988 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ -/** +/** * Application of custom course theme settings * @namespace coursetheme */ -export { default } from './lib/CourseThemeModule.js'; +export { default } from './lib/CourseThemeModule.js' diff --git a/lib/CourseThemeModule.js b/lib/CourseThemeModule.js index b7f7bf2..ed52c92 100644 --- a/lib/CourseThemeModule.js +++ b/lib/CourseThemeModule.js @@ -1,6 +1,6 @@ -import _ from 'lodash'; -import AbstractApiModule from 'adapt-authoring-api'; -import fs from 'fs/promises'; +import _ from 'lodash' +import AbstractApiModule from 'adapt-authoring-api' +import fs from 'fs/promises' /** * Module which handles course theming * @memberof coursetheme @@ -8,17 +8,17 @@ import fs from 'fs/promises'; */ class CourseThemeModule extends AbstractApiModule { /** @override */ - async setValues() { - /** @ignore */ this.root = 'coursethemepresets'; - /** @ignore */ this.permissionsScope = 'content'; - /** @ignore */ this.schemaName = 'coursethemepreset'; - /** @ignore */ this.schemaExtensionName = 'coursethemepreset'; - /** @ignore */ this.collectionName = 'coursethemepresets'; - /** @ignore */ this.attributeKey = 'themeVariables'; + async setValues () { + /** @ignore */ this.root = 'coursethemepresets' + /** @ignore */ this.permissionsScope = 'content' + /** @ignore */ this.schemaName = 'coursethemepreset' + /** @ignore */ this.schemaExtensionName = 'coursethemepreset' + /** @ignore */ this.collectionName = 'coursethemepresets' + /** @ignore */ this.attributeKey = 'themeVariables' - const perms = [`write:${this.permissionsScope}`]; + const perms = [`write:${this.permissionsScope}`] - this.useDefaultRouteConfig(); + this.useDefaultRouteConfig() this.routes.push({ route: '/:_id/apply/:courseId', validate: false, @@ -29,19 +29,21 @@ class CourseThemeModule extends AbstractApiModule { validate: false, handlers: { post: this.removeHandler() }, permissions: { post: perms } - }); + }) } + /** @override */ - async init() { - await super.init(); - const [framework, content] = await this.app.waitForModule('adaptframework', 'content'); + async init () { + await super.init() + const [framework, content] = await this.app.waitForModule('adaptframework', 'content') /** * Cached module instance for easy access * @type {ContentModule} */ - this.content = content; - framework.preBuildHook.tap(this.writeCustomLess.bind(this)); + this.content = content + framework.preBuildHook.tap(this.writeCustomLess.bind(this)) } + /** * Writes the customStyle and themeVariables attributes to LESS files. themeVariables are reduced into a string of variables, in the format `@key: value;` * @param {AdaptFrameworkBuild} fwBuild Reference to the current build @@ -57,18 +59,20 @@ class CourseThemeModule extends AbstractApiModule { this.writeFile(fwBuild, '3-themeCustomStyles.less', themeCustomStyle) ]); } + /** * Generates a LESS variables string * @param {object} data The data to process * @param {string} variablesStr String memo to allow recursion * @return {string} The processed LESS varaibles string */ - getVariablesString(data = {}, variablesStr = '') { + getVariablesString (data = {}, variablesStr = '') { return Object.entries(data).reduce((s, [k, v]) => { - if(_.isObject(v)) return this.getVariablesString(v, s); - return v ? `${s}@${k}: ${v};\n` : s; - }, variablesStr); + if (_.isObject(v)) return this.getVariablesString(v, s) + return v ? `${s}@${k}: ${v};\n` : s + }, variablesStr) } + /** * Writes a file to the theme folder * @param {Object} fwBuild Build data @@ -76,50 +80,52 @@ class CourseThemeModule extends AbstractApiModule { * @param {String} fileContents Contents to be written * @return {Promise} */ - async writeFile(fwBuild, filename, fileContents) { - if(_.isEmpty(fileContents)) { - return; + async writeFile (fwBuild, filename, fileContents) { + if (_.isEmpty(fileContents)) { + return } try { - const outputDir = `${fwBuild.dir}/src/theme/${fwBuild.courseData.config.data._theme}/less/zzzzz`; - await fs.mkdir(outputDir, { recursive: true }); - await fs.writeFile(`${outputDir}/${filename}`, fileContents); - } catch(e) { - this.log('error', `failed to write ${filename}, ${e.message}`); + const outputDir = `${fwBuild.dir}/src/theme/${fwBuild.courseData.config.data._theme}/less/zzzzz` + await fs.mkdir(outputDir, { recursive: true }) + await fs.writeFile(`${outputDir}/${filename}`, fileContents) + } catch (e) { + this.log('error', `failed to write ${filename}, ${e.message}`) } } + /** * Handles applying theme settings * @return {Function} Handler function */ - applyHandler() { + applyHandler () { return async (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 }); - res.sendStatus(204); - } catch(e) { - return next(e); + const { _id, courseId } = req.apiData.query + const [{ properties: presetProps }] = await this.find({ _id }) + await this.content.update({ _id: courseId }, { [this.attributeKey]: presetProps }) + res.sendStatus(204) + } catch (e) { + return next(e) } - }; + } } + /** * Handles removing theme settings * @return {Function} Handler function */ - removeHandler() { + removeHandler () { return async (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]) }); - res.sendStatus(204); - } catch(e) { - return next(e); + 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]) }) + res.sendStatus(204) + } catch (e) { + return next(e) } - }; + } } /** @@ -203,4 +209,4 @@ class CourseThemeModule extends AbstractApiModule { } } -export default CourseThemeModule; \ No newline at end of file +export default CourseThemeModule; diff --git a/package.json b/package.json index 1a11e9c..935beaa 100644 --- a/package.json +++ b/package.json @@ -10,5 +10,9 @@ "dependencies": { "adapt-authoring-api": "github:adapt-security/adapt-authoring-api", "lodash": "^4.17.21" + }, + "devDependencies": { + "eslint": "^8.36.0", + "standard": "^17.0.0" } }