From c1f1e44b05783ad7a744123bade4ef2899bde1a4 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:27:57 +0000 Subject: [PATCH 01/30] Update .gitignore --- .gitignore | 155 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 22b9f8cdc..20d55e5f2 100755 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,150 @@ -# ignore tracking usage data config - we want a different one for each user -usage-data-config.json -# ignore extension SCSS because it's dynamically generated +# Ignore prototype files +app/assets/data/search-index.json lib/extensions/_extensions.scss +package-lock.json +public + +# Prototype ignores - per-user +.tmp/ .env -.sass-cache +migrate.log +usage-data-config.json + +# General ignores .DS_Store -.start.pid +.idea .port.tmp -public -node_modules/* +.sass-cache +.start.pid .tmuxp.* -package-lock.json -app/assets/data/search-index.json -.idea + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* From bde75b293fbf1a33d914cfdf9bd22eac82429d51 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:30:22 +0000 Subject: [PATCH 02/30] Install govuk-prototype-kit dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index d550bf6e5..596955e11 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "express-session": "^1.17.2", "fancy-log": "^2.0.0", "govuk-frontend": "^5.5.0", + "govuk-prototype-kit": "^13.16.2", "gulp": "^5.0.0", "gulp-nodemon": "^2.5.0", "gulp-sass": "^5.0.0", From 93c2025c9896ebc234b53872206368a26b3ee41b Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:33:54 +0000 Subject: [PATCH 03/30] Remove packages --- package.json | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/package.json b/package.json index 596955e11..92c688e4e 100644 --- a/package.json +++ b/package.json @@ -20,20 +20,9 @@ "dependencies": { "@faker-js/faker": "^9.0.0", "@ministryofjustice/frontend": "^3.2.0", - "ansi-colors": "^4.1.1", - "basic-auth": "^2.0.0", - "basic-auth-connect": "^1.0.0", - "body-parser": "^1.14.1", - "browser-sync": "^3.0.2", - "client-sessions": "^0.8.0", "compression": "^1.7.4", "connect-flash": "^0.1.1", - "cookie-parser": "^1.4.5", - "cross-spawn": "^7.0.2", "csv-string": "^4.0.1", - "dotenv": "^16.4.5", - "express": "^4.21.2", - "express-session": "^1.17.2", "fancy-log": "^2.0.0", "govuk-frontend": "^5.5.0", "govuk-prototype-kit": "^13.16.2", @@ -41,7 +30,6 @@ "gulp-nodemon": "^2.5.0", "gulp-sass": "^5.0.0", "gulp-sourcemaps": "^3.0.0", - "inquirer": "^8.2.6", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "lunr": "^2.3.9", @@ -49,15 +37,12 @@ "marked-gfm-heading-id": "^4.1.1", "moment": "^2.27.0", "npm-run-all": "^4.1.5", - "nunjucks": "^3.2.3", "pluralize": "^8.0.0", - "portscanner": "^2.1.1", "require-dir": "^1.0.0", "rimraf": "^6.0.1", "sass": "^1.77.8", "string": "^3.3.3", "sync-request": "^6.0.0", - "universal-analytics": "^0.5.3", "uuid": "^11.0.3", "weighted": "^1.0.0" }, From dae42cb11afddac331c4c89904ff7ab9ce539f33 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:34:27 +0000 Subject: [PATCH 04/30] Delete start.js --- start.js | 53 ----------------------------------------------------- 1 file changed, 53 deletions(-) delete mode 100755 start.js diff --git a/start.js b/start.js deleted file mode 100755 index cf9fb0e14..000000000 --- a/start.js +++ /dev/null @@ -1,53 +0,0 @@ -// Core dependencies -const path = require('path') -const fs = require('fs') - -checkFiles() - -runGulp() - -// Warn if node_modules folder doesn't exist -function checkFiles () { - const nodeModulesExists = fs.existsSync(path.join(__dirname, '/node_modules')) - if (!nodeModulesExists) { - console.error('ERROR: Node module folder missing. Try running `npm install`') - process.exit(0) - } - - // Create template .env file if it doesn't exist - const envExists = fs.existsSync(path.join(__dirname, '/.env')) - if (!envExists) { - fs.createReadStream(path.join(__dirname, '/lib/template.env')) - .pipe(fs.createWriteStream(path.join(__dirname, '/.env'))) - } -} - -// Create template session data defaults file if it doesn't exist -const dataDirectory = path.join(__dirname, '/app/data') -const sessionDataDefaultsFile = path.join(dataDirectory, '/session-data-defaults.js') -const sessionDataDefaultsFileExists = fs.existsSync(sessionDataDefaultsFile) - -if (!sessionDataDefaultsFileExists) { - console.log('Creating session data defaults file') - if (!fs.existsSync(dataDirectory)) { - fs.mkdirSync(dataDirectory) - } - - fs.createReadStream(path.join(__dirname, '/lib/template.session-data-defaults.js')) - .pipe(fs.createWriteStream(sessionDataDefaultsFile)) -} - -// Run gulp -function runGulp () { - const spawn = require('cross-spawn') - - process.env.FORCE_COLOR = 1 - const gulp = spawn('./node_modules/.bin/gulp') - gulp.stdout.pipe(process.stdout) - gulp.stderr.pipe(process.stderr) - process.stdin.pipe(gulp.stdin) - - gulp.on('exit', function (code) { - console.log('gulp exited with code ' + code.toString()) - }) -} From a301995673a1286211194b0059fb55c99b932222 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:36:07 +0000 Subject: [PATCH 05/30] Delete server.js --- server.js | 259 ------------------------------------------------------ 1 file changed, 259 deletions(-) delete mode 100755 server.js diff --git a/server.js b/server.js deleted file mode 100755 index b88c1df8e..000000000 --- a/server.js +++ /dev/null @@ -1,259 +0,0 @@ -// Core dependencies -const path = require('path') -const url = require('url') - -// NPM dependencies -const bodyParser = require('body-parser') -const dotenv = require('dotenv') -const express = require('express') -const flash = require('connect-flash') -const nunjucks = require('nunjucks') -const sessionInCookie = require('client-sessions') -const sessionInMemory = require('express-session') -const cookieParser = require('cookie-parser') -const compression = require('compression') - -// Run before other code to make sure variables from .env are available -dotenv.config() - -// Local dependencies -const middleware = [ - require('./lib/middleware/authentication/authentication.js'), - require('./lib/middleware/extensions/extensions.js') -] -const config = require('./app/config.js') -const packageJson = require('./package.json') -const routes = require('./app/routes.js') -const utils = require('./lib/utils.js') -const extensions = require('./lib/extensions/extensions.js') - -const app = express() - -// Compression -// https://expressjs.com/en/resources/middleware/compression.html -function shouldCompress (req, res) { - if (req.headers['x-no-compression']) { - // don't compress responses with this request header - return false - } - - // fallback to standard filter function - return compression.filter(req, res) -} - -app.use(compression({ filter: shouldCompress })) - -// Set cookies for use in cookie banner. -app.use(cookieParser()) -app.use(utils.handleCookies(app)) - -// Set up configuration variables -const releaseVersion = packageJson.version -const env = (process.env.NODE_ENV || 'development').toLowerCase() -const useAutoStoreData = process.env.USE_AUTO_STORE_DATA || config.useAutoStoreData -const useCookieSessionStore = process.env.USE_COOKIE_SESSION_STORE || config.useCookieSessionStore -let useHttps = process.env.USE_HTTPS || config.useHttps - -useHttps = useHttps.toLowerCase() - -const useDocumentation = (config.useDocumentation === 'true') - -// Promo mode redirects the root to /docs - so our landing page is docs when published on heroku -let promoMode = process.env.PROMO_MODE || 'false' -promoMode = promoMode.toLowerCase() - -// Disable promo mode if docs aren't enabled -if (!useDocumentation) promoMode = 'false' - -// Force HTTPS on production. Do this before using basicAuth to avoid -// asking for username/password twice (for `http`, then `https`). -const isSecure = (env === 'production' && useHttps === 'true') -if (isSecure) { - app.use(utils.forceHttps) - app.set('trust proxy', 1) // needed for secure cookies on heroku -} - -middleware.forEach(func => app.use(func)) - -// Set up App -const appViews = extensions.getAppViews([ - path.join(__dirname, '/app/views/'), - path.join(__dirname, '/lib/') -]) - -const nunjucksConfig = { - autoescape: true, - noCache: true, - watch: false // We are now setting this to `false` (it's by default false anyway) as having it set to `true` for production was making the tests hang -} - -if (env === 'development') { - nunjucksConfig.watch = true -} - -nunjucksConfig.express = app - -const nunjucksAppEnv = nunjucks.configure(appViews, nunjucksConfig) - -// Add Nunjucks filters -utils.addNunjucksFilters(nunjucksAppEnv) - -// Add Nunjucks functions (**Register trainee teachers addition**) -utils.addNunjucksFunctions(nunjucksAppEnv) -// End Register added - -// Set views engine -app.set('view engine', 'njk') - -// Middleware to serve static assets -app.use('/public', express.static(path.join(__dirname, '/public'))) - -app.use('/assets', express.static(path.join(__dirname, '/node_modules/@ministryofjustice/frontend/moj/assets'))) - -// Serve govuk-frontend in from node_modules (so not to break pre-extenstions prototype kits) -app.use('/node_modules/govuk-frontend', express.static(path.join(__dirname, '/node_modules/govuk-frontend'))) - -app.use('/node_modules/moj-frontend', express.static(path.join(__dirname, '/node_modules/@ministryofjustice/frontend'))) - -// Support for parsing data in POSTs -app.use(bodyParser.json()) -app.use(bodyParser.urlencoded({ - extended: true -})) - -// Add variables that are available in all views -app.locals.asset_path = '/public/' -app.locals.useAutoStoreData = (useAutoStoreData === 'true') -app.locals.useCookieSessionStore = (useCookieSessionStore === 'true') -app.locals.cookieText = config.cookieText -app.locals.promoMode = promoMode -app.locals.releaseVersion = 'v' + releaseVersion -app.locals.serviceName = config.serviceName -app.locals.pageTitle = config.pageTitle - -// extensionConfig sets up variables used to add the scripts and stylesheets to each page. -app.locals.extensionConfig = extensions.getAppConfig() - -// Session uses service name to avoid clashes with other prototypes -const sessionName = 'govuk-prototype-kit-' + (Buffer.from(config.serviceName, 'utf8')).toString('hex') -const sessionOptions = { - secret: sessionName, - cookie: { - maxAge: 1000 * 60 * 60 * 4, // 4 hours - secure: isSecure - } -} - -// Support session data in cookie or memory -if (useCookieSessionStore === 'true') { - app.use(sessionInCookie(Object.assign(sessionOptions, { - cookieName: sessionName, - proxy: true, - requestKey: 'session' - }))) -} else { - app.use(sessionInMemory(Object.assign(sessionOptions, { - name: sessionName, - resave: false, - saveUninitialized: false - }))) -} - -app.use(flash()) - -// Automatically store all data users enter -if (useAutoStoreData === 'true') { - app.use(utils.autoStoreData) - utils.addCheckedFunction(nunjucksAppEnv) -} - -// Clear all data in session if you open /prototype-admin/clear-data -app.post('/admin/clear-data', (req, res) => { - console.log('Clearing session data') - req.session.data = {} - res.redirect('/') -}) - -// Redirect root to /docs when in promo mode. -if (promoMode === 'true') { - console.log('Prototype Kit running in promo mode') - - app.locals.cookieText = 'GOV.UK uses cookies to make the site simpler. Find out more about cookies' - - app.get('/', (req, res) => { - res.redirect('/docs') - }) - - // Allow search engines to index the Prototype Kit promo site - app.get('/robots.txt', (req, res) => { - res.type('text/plain') - res.send('User-agent: *\nAllow: /') - }) -} else { - // Prevent search indexing - app.use((req, res, next) => { - // Setting headers stops pages being indexed even if indexed pages link to them. - res.setHeader('X-Robots-Tag', 'noindex') - next() - }) - - app.get('/robots.txt', (req, res) => { - res.type('text/plain') - res.send('User-agent: *\nDisallow: /') - }) -} - -// Load routes (found in app/routes.js) -if (typeof (routes) !== 'function') { - console.log(routes.bind) - console.log('Warning: the use of bind in routes is deprecated - please check the Prototype Kit documentation for writing routes.') - routes.bind(app) -} else { - app.use('/', routes) -} - -// Strip .html and .htm if provided -app.get(/\.html?$/i, (req, res) => { - let path = req.path - const parts = path.split('.') - parts.pop() - path = parts.join('.') - res.redirect(path) -}) - -// Auto render any view that exists - -// App folder routes get priority -app.get(/^([^.]+)$/, (req, res, next) => { - utils.matchRoutes(req, res, next) -}) - -// Redirect all POSTs to GETs - this allows users to use POST for autoStoreData -// EXTRA added by Ed Horsford: preserve query params -app.post(/^\/([^.]+)$/, (req, res) => { - // res.redirect('/' + req.params[0]) - res.redirect(url.format({ - pathname: '/' + req.params[0], - query: req.query - }) - ) -}) - -// Catch 404 and forward to error handler -app.use((req, res, next) => { - const err = new Error(`Page not found: ${req.path}`) - err.status = 404 - next(err) -}) - -// Display error -app.use(function (err, req, res, next) { - console.error(err.message) - res.status(err.status || 500) - res.send(err.message) -}) - -console.log('\nGOV.UK Prototype Kit v' + releaseVersion) -console.log('\nNOTICE: the kit is for building prototypes, do not use it for production services.') - -module.exports = app From efc955395c024291ea9b31de96fcf436b2938011 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:38:00 +0000 Subject: [PATCH 06/30] Update filters.js --- app/filters.js | 106 +++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/app/filters.js b/app/filters.js index 521a8208a..fbe3065e1 100755 --- a/app/filters.js +++ b/app/filters.js @@ -1,70 +1,74 @@ +const govukPrototypeKit = require('govuk-prototype-kit') +const addFilter = govukPrototypeKit.views.addFilter + const fs = require('fs') const path = require('path') const individualFiltersFolder = path.join(__dirname, './filters') -module.exports = function (env) { - const filters = {} +const filters = {} - // Import filters from filters folder - if (fs.existsSync(individualFiltersFolder)) { - const files = fs.readdirSync(individualFiltersFolder) - files.forEach(file => { - const fileData = require(path.join(individualFiltersFolder, file)) - // Loop through each exported function in file (likely just one) - Object.keys(fileData).forEach((filterGroup) => { - // Get each method from the file - Object.keys(fileData[filterGroup]).forEach(filterName => { - filters[filterName] = fileData[filterGroup][filterName] - }) +// Import filters from filters folder +if (fs.existsSync(individualFiltersFolder)) { + const files = fs.readdirSync(individualFiltersFolder) + files.forEach(file => { + const fileData = require(path.join(individualFiltersFolder, file)) + // Loop through each exported function in file (likely just one) + Object.keys(fileData).forEach((filterGroup) => { + // Get each method from the file + Object.keys(fileData[filterGroup]).forEach(filterName => { + filters[filterName] = fileData[filterGroup][filterName] }) }) - } - - filters.selected = function (dataKey, selectWhenMatchesValue) { - if (this.ctx.data === undefined) { - return '' - } + }) +} - const dataValue = this.ctx.data[dataKey] +filters.selected = function (dataKey, selectWhenMatchesValue) { + if (this.ctx.data === undefined) { + return '' + } - if (dataValue === undefined && selectWhenMatchesValue === '') { - return 'selected' - } + const dataValue = this.ctx.data[dataKey] - if (dataValue === selectWhenMatchesValue) { - return 'selected' - } + if (dataValue === undefined && selectWhenMatchesValue === '') { + return 'selected' } - filters.dateStringFromData = function (dataKey) { - if (this.ctx.data === undefined) { - return '' - } - const day = this.ctx.data[dataKey + '-day'] - const month = this.ctx.data[dataKey + '-month'] - const year = this.ctx.data[dataKey + '-year'] - const filtered = [day, month, year].filter(function (element) { - return element && element !== '' - }) - return filtered.join('/') + if (dataValue === selectWhenMatchesValue) { + return 'selected' } +} - filters.compact = function (arr) { - return arr.filter(function (element) { - return element && element !== '' - }) +filters.dateStringFromData = function (dataKey) { + if (this.ctx.data === undefined) { + return '' } + const day = this.ctx.data[dataKey + '-day'] + const month = this.ctx.data[dataKey + '-month'] + const year = this.ctx.data[dataKey + '-year'] + const filtered = [day, month, year].filter(function (element) { + return element && element !== '' + }) + return filtered.join('/') +} + +filters.compact = function (arr) { + return arr.filter(function (element) { + return element && element !== '' + }) +} - filters.tick = function (value) { - if (value && value !== '') { - return '' - } else { - return '' - } +filters.tick = function (value) { + if (value && value !== '') { + return '' + } else { + return '' } +} + +/* ------------------------------------------------------------------ + keep the following line to return your filters to the app +------------------------------------------------------------------ */ - /* ------------------------------------------------------------------ - keep the following line to return your filters to the app - ------------------------------------------------------------------ */ - return filters +for (let filterName of Object.keys(filters)) { + addFilter(filterName, filters[filterName]) } From f291bae7759d468bfc2cc34157656d251f0bd8af Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:39:16 +0000 Subject: [PATCH 07/30] Update routes.js --- app/routes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/routes.js b/app/routes.js index 206b46b46..268d6610b 100755 --- a/app/routes.js +++ b/app/routes.js @@ -1,6 +1,7 @@ +const govukPrototypeKit = require('govuk-prototype-kit') +const router = govukPrototypeKit.requests.setupRouter() + const _ = require('lodash') -const express = require('express') -const router = express.Router() const url = require('url') const utils = require('./lib/utils') const permissions = require('./filters/permissions.js').filters From 11ef5bc829cc1f2d2fae60aa192a6f2bb3c5b3fc Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:40:37 +0000 Subject: [PATCH 08/30] Remove packages --- package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/package.json b/package.json index 92c688e4e..17d79b666 100644 --- a/package.json +++ b/package.json @@ -20,10 +20,8 @@ "dependencies": { "@faker-js/faker": "^9.0.0", "@ministryofjustice/frontend": "^3.2.0", - "compression": "^1.7.4", "connect-flash": "^0.1.1", "csv-string": "^4.0.1", - "fancy-log": "^2.0.0", "govuk-frontend": "^5.5.0", "govuk-prototype-kit": "^13.16.2", "gulp": "^5.0.0", From 3b37c24fb7eb4a7022f293196368f959d35fe828 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:41:27 +0000 Subject: [PATCH 09/30] Update package scripts --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 17d79b666..90fad8407 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "node": ">=20.0.0" }, "scripts": { - "start": "node start.js", + "dev": "govuk-prototype-kit dev", + "serve": "govuk-prototype-kit serve", + "start": "govuk-prototype-kit start", "lint:markdown": "markdownlint 'app/**/*.md' --disable MD013", "lint:markdown:fix": "markdownlint 'app/**/*.md' --disable MD013 --fix", "lint:javascripts": "standard", From 44bb3a807847124cf395aaffb9c313a5583c2efe Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:51:41 +0000 Subject: [PATCH 10/30] Update routes --- app/routes/bulk-action-routes.js | 2 +- app/routes/bulk-update-routes.js | 2 +- app/routes/existing-record-routes.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/routes/bulk-action-routes.js b/app/routes/bulk-action-routes.js index 27e0bac7c..f275aa7e9 100644 --- a/app/routes/bulk-action-routes.js +++ b/app/routes/bulk-action-routes.js @@ -1,4 +1,4 @@ -const filters = require('./../filters.js')() +const filters = require('./../filters.js') const moment = require('moment') const utils = require('./../lib/utils') diff --git a/app/routes/bulk-update-routes.js b/app/routes/bulk-update-routes.js index bdd3f497b..c9b3daa5a 100644 --- a/app/routes/bulk-update-routes.js +++ b/app/routes/bulk-update-routes.js @@ -1,4 +1,4 @@ -const filters = require('./../filters.js')() +const filters = require('./../filters.js') const utils = require('./../lib/utils') const weighted = require('weighted') const { fakerEN_GB: faker } = require('@faker-js/faker') diff --git a/app/routes/existing-record-routes.js b/app/routes/existing-record-routes.js index 97cb31d2c..66937aa8b 100644 --- a/app/routes/existing-record-routes.js +++ b/app/routes/existing-record-routes.js @@ -1,7 +1,7 @@ const _ = require('lodash') const { fakerEN_GB: faker } = require('@faker-js/faker') const dates = require('./../filters/dates.js').filters -const filters = require('./../filters.js')() +const filters = require('./../filters.js') const moment = require('moment') const path = require('path') const utils = require('./../lib/utils') From cdf290ae679fc799f4644ae75cdddfa17a37090b Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 12 Dec 2024 16:51:53 +0000 Subject: [PATCH 11/30] Update application.scss --- app/assets/sass/application.scss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/assets/sass/application.scss b/app/assets/sass/application.scss index aa6e61834..4b4631180 100755 --- a/app/assets/sass/application.scss +++ b/app/assets/sass/application.scss @@ -1,5 +1,11 @@ @use "sass:string"; +// +// For guidance on how to add CSS and SCSS see: +// https://prototype-kit.service.gov.uk/docs/adding-css-javascript-and-images +// +$govuk-new-typography-scale: true; + // global styles for and

tags $govuk-global-styles: true; @@ -7,11 +13,6 @@ $govuk-global-styles: true; $app-assets-path: "/public/"; $govuk-assets-path: "/dist/govuk/assets/"; -// Import GOV.UK Frontend and any extension styles if extensions have been configured -@import "lib/extensions/extensions"; - -@import "node_modules/@ministryofjustice/frontend/moj/all"; - // Patterns that aren't in Frontend @import "components/autocomplete"; @import "components/footer"; From 8dc1805684d10498194914cf40d5a2348ac98a6f Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Fri, 13 Dec 2024 15:58:43 +0000 Subject: [PATCH 12/30] Update styles --- app/assets/sass/application.scss | 2 +- app/assets/sass/unbranded.scss | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100755 app/assets/sass/unbranded.scss diff --git a/app/assets/sass/application.scss b/app/assets/sass/application.scss index 5399d8007..44ddc40c1 100755 --- a/app/assets/sass/application.scss +++ b/app/assets/sass/application.scss @@ -11,7 +11,7 @@ $govuk-global-styles: true; // We can't mount GOV.UK Frontend's assets at root as it's done automatically by the extensions framework. $app-assets-path: "/public/"; -$govuk-assets-path: "/dist/govuk/assets/"; +// $govuk-assets-path: "/dist/govuk/assets/"; // Patterns that aren't in Frontend @import "patterns/related-items"; diff --git a/app/assets/sass/unbranded.scss b/app/assets/sass/unbranded.scss deleted file mode 100755 index aa6b21768..000000000 --- a/app/assets/sass/unbranded.scss +++ /dev/null @@ -1,15 +0,0 @@ -// The unbranded stylesheet is used if you need to create pages in your -// prototype without the GOV.UK branding. -// See localhost:3000/docs/templates/blank-unbranded - -// Import settings first so we can override them before importing all of GOV.UK Frontend -// If you need to enable compatibility mode or the legacy palette, do that *before* this import. -@import "node_modules/govuk-frontend/dist/govuk/settings/index"; - -// Override the default GOV.UK Frontend font stack -$govuk-font-family: "HelveticaNeue", "Helvetica Neue", "Arial", "Helvetica", sans-serif; - -// Override the canvas background colour, which is normally grey to blend with the GOV.UK footer. -$govuk-canvas-background-colour: $govuk-body-background-colour; - -@import "application"; From 2ef52a77fc3dd2f5e1d3d8c68809f5b43329dad6 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Fri, 13 Dec 2024 16:00:30 +0000 Subject: [PATCH 13/30] Add flash messaging to routes --- app/routes.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/routes.js b/app/routes.js index d5e80486e..0deb5891b 100755 --- a/app/routes.js +++ b/app/routes.js @@ -6,6 +6,12 @@ const url = require('url') const utils = require('./lib/utils') const permissions = require('./filters/permissions.js').filters +/// ------------------------------------------------------------------------ /// +/// Flash messaging +/// ------------------------------------------------------------------------ /// +const flash = require('connect-flash') +router.use(flash()) + // ============================================================================= // Catch all // Used to pass common data to views From 1752b2879ce8fd1fd69a3a1608dbbb13ee415978 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 09:28:55 +0000 Subject: [PATCH 14/30] Update prototype config to use JSON file --- app/config.js | 36 ------------------------------------ app/config.json | 4 ++++ 2 files changed, 4 insertions(+), 36 deletions(-) delete mode 100755 app/config.js create mode 100644 app/config.json diff --git a/app/config.js b/app/config.js deleted file mode 100755 index 8b97ffe58..000000000 --- a/app/config.js +++ /dev/null @@ -1,36 +0,0 @@ -// Use this file to change prototype configuration. - -// Note: prototype config can be overridden using environment variables (eg on heroku) - -module.exports = { - // Service name used in header. Eg: 'Renew your passport' - serviceName: 'Register trainee teachers', - - // Default port that prototype runs on - port: '3000', - - // Enable or disable password protection on production - useAuth: 'true', - - // Automatically stores form data, and send to all views - useAutoStoreData: 'true', - - // Enable cookie-based session store (persists on restart) - // Please note 4KB cookie limit per domain, cookies too large will silently be ignored - useCookieSessionStore: 'false', - - // Enable or disable built-in docs and examples. - useDocumentation: 'true', - - // Force HTTP to redirect to HTTPS on production - useHttps: 'true', - - // Cookie warning - update link to service's cookie page. - cookieText: 'GOV.UK uses cookies to make the site simpler. Find out more about cookies', - - // Enable or disable Browser Sync - useBrowserSync: 'true', - - pageTitle: 'Register for teacher training - GOV.UK' - -} diff --git a/app/config.json b/app/config.json new file mode 100644 index 000000000..54a0eaffd --- /dev/null +++ b/app/config.json @@ -0,0 +1,4 @@ +{ + "serviceName": "Register trainee teachers", + "useNjkExtensions": true +} From 16c3cc5e5820ccf69da359e8c033197052abb8f6 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 10:28:17 +0000 Subject: [PATCH 15/30] Update page layouts --- app/views/{layout.njk => _layouts/main.njk} | 61 ++++--------------- app/views/_templates/_form.njk | 2 +- app/views/_templates/_guidance-page.njk | 2 +- app/views/_templates/_new-record.njk | 2 +- app/views/_templates/_page.njk | 2 +- app/views/_templates/_record-form.njk | 2 +- app/views/_templates/_record.njk | 2 +- app/views/errors/403.njk | 2 +- app/views/errors/404.njk | 2 +- app/views/errors/500.njk | 2 +- app/views/errors/service-unavailable.njk | 2 +- app/views/guidance/bulk-add-new-by-csv.njk | 2 +- app/views/home.njk | 2 +- app/views/index.njk | 2 +- app/views/layout_unbranded.njk | 17 ------ app/views/start-page.njk | 2 +- .../static-designs/funding-upload/index.njk | 2 +- .../funding-upload/upload-1.njk | 2 +- .../funding-upload/upload-2.njk | 2 +- .../funding-upload/upload-3.njk | 2 +- .../guidance-layout-variations.njk | 2 +- app/views/support/index.njk | 2 +- app/views/test-page.njk | 2 +- 23 files changed, 34 insertions(+), 86 deletions(-) rename app/views/{layout.njk => _layouts/main.njk} (70%) delete mode 100755 app/views/layout_unbranded.njk diff --git a/app/views/layout.njk b/app/views/_layouts/main.njk similarity index 70% rename from app/views/layout.njk rename to app/views/_layouts/main.njk index 6f12a82eb..04e131694 100755 --- a/app/views/layout.njk +++ b/app/views/_layouts/main.njk @@ -1,57 +1,24 @@ -{#- We can't mount GOV.UK Frontend's assets at root as it's done automatically by the extensions framework. -#} -{%- set assetPath = '/dist/govuk/assets' -%} - -{% extends "govuk/template.njk" %} - -{% from "govuk/components/accordion/macro.njk" import govukAccordion %} -{% from "govuk/components/back-link/macro.njk" import govukBackLink %} -{% from "govuk/components/breadcrumbs/macro.njk" import govukBreadcrumbs %} -{% from "govuk/components/button/macro.njk" import govukButton %} -{% from "govuk/components/character-count/macro.njk" import govukCharacterCount %} -{% from "govuk/components/checkboxes/macro.njk" import govukCheckboxes %} -{% from "govuk/components/date-input/macro.njk" import govukDateInput %} -{% from "govuk/components/details/macro.njk" import govukDetails %} -{% from "govuk/components/error-message/macro.njk" import govukErrorMessage %} -{% from "govuk/components/error-summary/macro.njk" import govukErrorSummary %} -{% from "govuk/components/fieldset/macro.njk" import govukFieldset %} -{% from "govuk/components/file-upload/macro.njk" import govukFileUpload %} -{% from "govuk/components/input/macro.njk" import govukInput %} -{% from "govuk/components/inset-text/macro.njk" import govukInsetText %} -{% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %} -{% from "govuk/components/panel/macro.njk" import govukPanel %} -{% from "govuk/components/pagination/macro.njk" import govukPagination %} -{% from "govuk/components/phase-banner/macro.njk" import govukPhaseBanner %} -{% from "govuk/components/radios/macro.njk" import govukRadios %} -{% from "govuk/components/select/macro.njk" import govukSelect %} -{% from "govuk/components/skip-link/macro.njk" import govukSkipLink %} -{% from "govuk/components/summary-list/macro.njk" import govukSummaryList %} -{% from "govuk/components/table/macro.njk" import govukTable %} -{% from "govuk/components/tabs/macro.njk" import govukTabs %} -{% from "govuk/components/tag/macro.njk" import govukTag %} -{% from "govuk/components/task-list/macro.njk" import govukTaskList %} -{% from "govuk/components/textarea/macro.njk" import govukTextarea %} -{% from "govuk/components/warning-text/macro.njk" import govukWarningText %} +{% extends "govuk-prototype-kit/layouts/govuk-branded.njk" %} {# custom components #} -{# Most of these copied from the Apply for teacher training prototype #} -{% from "_components/admin-feature/macro.njk" import appAdminFeature %} -{% from "_components/admin-banner/macro.njk" import appAdminBanner %} -{% from "_components/autocomplete/macro.njk" import appAutocomplete %} -{% from "_components/autocomplete-new/macro.njk" import appAutocompleteFromSelect %} -{% from "_components/autocomplete-new/macro.njk" import appAutocompleteFromInput %} -{% from "_components/banner/macro.njk" import appBanner %} -{% from "_components/footer/macro.njk" import appFooter %} -{% from "_components/school-autocomplete/macro.njk" import appSchoolAutocomplete with context %} -{% from "_components/sub-navigation/macro.njk" import appSubNavigation %} -{% from "_components/timeline/macro.njk" import appTimeline %} +{% from "_components/admin-banner/macro.njk" import appAdminBanner %} +{% from "_components/admin-feature/macro.njk" import appAdminFeature %} +{% from "_components/autocomplete-new/macro.njk" import appAutocompleteFromInput %} +{% from "_components/autocomplete-new/macro.njk" import appAutocompleteFromSelect %} +{% from "_components/autocomplete/macro.njk" import appAutocomplete %} +{% from "_components/banner/macro.njk" import appBanner %} {% from "_components/download-link-with-filename/macro.njk" import appDownloadLink %} +{% from "_components/footer/macro.njk" import appFooter %} +{% from "_components/school-autocomplete/macro.njk" import appSchoolAutocomplete with context %} +{% from "_components/sub-navigation/macro.njk" import appSubNavigation %} +{% from "_components/timeline/macro.njk" import appTimeline %} {# Wrapper of moj’s filter stuff #} -{% from "_components/filter-page/macro.njk" import appFilterPage %} +{% from "_components/filter-page/macro.njk" import appFilterPage %} {# moj components #} -{%- from "moj/components/filter/macro.njk" import mojFilter -%} {%- from "moj/components/button-menu/macro.njk" import mojButtonMenu -%} +{%- from "moj/components/filter/macro.njk" import mojFilter -%} {%- from "moj/components/primary-navigation/macro.njk" import mojPrimaryNavigation -%} {% block head %} @@ -241,8 +208,6 @@ {% endif %} - - {% endblock %} {% block beforeContent %} diff --git a/app/views/_templates/_form.njk b/app/views/_templates/_form.njk index 046119cf6..ab33d6022 100644 --- a/app/views/_templates/_form.njk +++ b/app/views/_templates/_form.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% if not backLink %} {% set backLink = 'javascript:history.back();' %} diff --git a/app/views/_templates/_guidance-page.njk b/app/views/_templates/_guidance-page.njk index 6e717003c..ba114d506 100644 --- a/app/views/_templates/_guidance-page.njk +++ b/app/views/_templates/_guidance-page.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% if not backLink %} {% set backLink = 'javascript:history.back();' %} diff --git a/app/views/_templates/_new-record.njk b/app/views/_templates/_new-record.njk index 0c2b706e4..080cc4c21 100644 --- a/app/views/_templates/_new-record.njk +++ b/app/views/_templates/_new-record.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "drafts" %} diff --git a/app/views/_templates/_page.njk b/app/views/_templates/_page.njk index d18c34ad8..ed377328e 100644 --- a/app/views/_templates/_page.njk +++ b/app/views/_templates/_page.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% if not backLink %} {% set backLink = 'javascript:history.back();' %} diff --git a/app/views/_templates/_record-form.njk b/app/views/_templates/_record-form.njk index 6ce80c797..e76df7dbe 100644 --- a/app/views/_templates/_record-form.njk +++ b/app/views/_templates/_record-form.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = navActive or "records" %} diff --git a/app/views/_templates/_record.njk b/app/views/_templates/_record.njk index e36b5108c..e2ce425d3 100644 --- a/app/views/_templates/_record.njk +++ b/app/views/_templates/_record.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "records" %} diff --git a/app/views/errors/403.njk b/app/views/errors/403.njk index 04ef0ee6f..b657ad23d 100755 --- a/app/views/errors/403.njk +++ b/app/views/errors/403.njk @@ -1,5 +1,5 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set pageHeading = "You do not have permission to perform this action" %} diff --git a/app/views/errors/404.njk b/app/views/errors/404.njk index 8ebf0bfc3..78da50cbd 100755 --- a/app/views/errors/404.njk +++ b/app/views/errors/404.njk @@ -1,5 +1,5 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set pageHeading = "Page not found" %} diff --git a/app/views/errors/500.njk b/app/views/errors/500.njk index 4a015607c..d99442358 100755 --- a/app/views/errors/500.njk +++ b/app/views/errors/500.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set hideNav = true %} {% set hidePrimaryNav = true %} diff --git a/app/views/errors/service-unavailable.njk b/app/views/errors/service-unavailable.njk index 678adfa00..520254a50 100755 --- a/app/views/errors/service-unavailable.njk +++ b/app/views/errors/service-unavailable.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set hideNav = true %} {% set hidePrimaryNav = true %} diff --git a/app/views/guidance/bulk-add-new-by-csv.njk b/app/views/guidance/bulk-add-new-by-csv.njk index 2921310cb..2420d4b8e 100755 --- a/app/views/guidance/bulk-add-new-by-csv.njk +++ b/app/views/guidance/bulk-add-new-by-csv.njk @@ -1,5 +1,5 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% if not backLink %} {% set backLink = 'javascript:history.back();' %} diff --git a/app/views/home.njk b/app/views/home.njk index 2e8ae318f..376777f2c 100755 --- a/app/views/home.njk +++ b/app/views/home.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} diff --git a/app/views/index.njk b/app/views/index.njk index 27afd0a6d..f7fb655a7 100755 --- a/app/views/index.njk +++ b/app/views/index.njk @@ -1,5 +1,5 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% block content %} diff --git a/app/views/layout_unbranded.njk b/app/views/layout_unbranded.njk deleted file mode 100755 index 034170e14..000000000 --- a/app/views/layout_unbranded.njk +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layout.njk" %} - -{% block headIcons %} - -{% endblock %} - -{% block head %} - - - - {% for stylesheetUrl in extensionConfig.stylesheets %} - - {% endfor %} -{% endblock %} - -{% block header %}{% endblock %} -{% block footer %}{% endblock %} diff --git a/app/views/start-page.njk b/app/views/start-page.njk index 0f24b6a16..b6f6dc4e4 100755 --- a/app/views/start-page.njk +++ b/app/views/start-page.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set bodyClasses = "start-page" %} {% set hideNav = true %} {% set hidePrimaryNav = true %} diff --git a/app/views/static-designs/funding-upload/index.njk b/app/views/static-designs/funding-upload/index.njk index e9e4ef2d6..d72a416bb 100755 --- a/app/views/static-designs/funding-upload/index.njk +++ b/app/views/static-designs/funding-upload/index.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} diff --git a/app/views/static-designs/funding-upload/upload-1.njk b/app/views/static-designs/funding-upload/upload-1.njk index cd6eafa00..235860dda 100755 --- a/app/views/static-designs/funding-upload/upload-1.njk +++ b/app/views/static-designs/funding-upload/upload-1.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} diff --git a/app/views/static-designs/funding-upload/upload-2.njk b/app/views/static-designs/funding-upload/upload-2.njk index bdd5537d8..30c94d629 100755 --- a/app/views/static-designs/funding-upload/upload-2.njk +++ b/app/views/static-designs/funding-upload/upload-2.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} diff --git a/app/views/static-designs/funding-upload/upload-3.njk b/app/views/static-designs/funding-upload/upload-3.njk index 5ecd2d2e8..6a319c629 100755 --- a/app/views/static-designs/funding-upload/upload-3.njk +++ b/app/views/static-designs/funding-upload/upload-3.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} diff --git a/app/views/static-designs/guidance-layout-variations.njk b/app/views/static-designs/guidance-layout-variations.njk index 4dff2dea6..82fb02c3a 100644 --- a/app/views/static-designs/guidance-layout-variations.njk +++ b/app/views/static-designs/guidance-layout-variations.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% if not backLink %} {% set backLink = 'javascript:history.back();' %} diff --git a/app/views/support/index.njk b/app/views/support/index.njk index afbb76f11..8fd4b2034 100755 --- a/app/views/support/index.njk +++ b/app/views/support/index.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} diff --git a/app/views/test-page.njk b/app/views/test-page.njk index 3824b3ba5..c4de5b69e 100755 --- a/app/views/test-page.njk +++ b/app/views/test-page.njk @@ -1,4 +1,4 @@ -{% extends "layout.njk" %} +{% extends "_layouts/main.njk" %} {% set navActive = "home" %} From 4b4a28aa0ffa59e11e3bd1e12b70f54b83cc6c4e Mon Sep 17 00:00:00 2001 From: lint-bot Date: Mon, 16 Dec 2024 10:36:14 +0000 Subject: [PATCH 16/30] Code style fixes --- app/filters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/filters.js b/app/filters.js index fbe3065e1..31d81b392 100755 --- a/app/filters.js +++ b/app/filters.js @@ -69,6 +69,6 @@ filters.tick = function (value) { keep the following line to return your filters to the app ------------------------------------------------------------------ */ -for (let filterName of Object.keys(filters)) { +for (const filterName of Object.keys(filters)) { addFilter(filterName, filters[filterName]) } From 6cba6c13aadaef54ece6cfdbd2131e2db4f6c66b Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 10:44:19 +0000 Subject: [PATCH 17/30] Update functions --- app/functions.js | 168 +++++++++++++++++++++++------------------------ app/routes.js | 3 + 2 files changed, 87 insertions(+), 84 deletions(-) diff --git a/app/functions.js b/app/functions.js index 8116aba6d..1d18fe457 100755 --- a/app/functions.js +++ b/app/functions.js @@ -1,108 +1,108 @@ -const fs = require('fs') -const path = require('path') -const individualFunctionsFolder = path.join(__dirname, './functions') +// const fs = require('fs') +// const path = require('path') +// const individualFunctionsFolder = path.join(__dirname, './functions') const moment = require('moment') const _ = require('lodash') const permissions = require('./filters/permissions.js').filters const { fakerEN_GB: faker } = require('@faker-js/faker') faker.seed(123) -module.exports = function (env) { - const functions = {} - - // Import functions from functions folder - if (fs.existsSync(individualFunctionsFolder)) { - const files = fs.readdirSync(individualFunctionsFolder) - files.forEach(file => { - const fileData = require(path.join(individualFunctionsFolder, file)) - // Loop through each exported function in file (likely just one) - Object.keys(fileData).forEach((functionGroup) => { - // Get each method from the file - Object.keys(fileData[functionGroup]).forEach(functionName => { - functions[functionName] = fileData[functionGroup][functionName] - }) - }) - }) - } - - // Return today’s date - functions.today = function () { - return moment().format('YYYY-MM-DD') - } - - functions.nowUTC = function () { - return Date.now() - } - /* - --------------------------------------- - Get current month - --------------------------------------- - For example, if it’s January: - - currentMonth() = 01 - - currentMonth(nameOfMonth) = ‘January’ - */ - functions.currentMonth = (type) => { - if (type === 'nameOfMonth') { - return moment().format('MMMM') - } else { - return parseInt(moment().format('MM'), 10) - } - } +// const functions = {} + +// Import functions from functions folder +// if (fs.existsSync(individualFunctionsFolder)) { +// const files = fs.readdirSync(individualFunctionsFolder) +// files.forEach(file => { +// const fileData = require(path.join(individualFunctionsFolder, file)) +// // Loop through each exported function in file (likely just one) +// Object.keys(fileData).forEach((functionGroup) => { +// // Get each method from the file +// Object.keys(fileData[functionGroup]).forEach(functionName => { +// functions[functionName] = fileData[functionGroup][functionName] +// }) +// }) +// }) +// } + +// Return today’s date +exports.today = () => { + return moment().format('YYYY-MM-DD') +} - // Get current year - functions.currentYear = (type) => { - return moment().add(type, 'years').format('YYYY') - } +exports.nowUTC = () => { + return Date.now() +} - // Get the context - useful for logging - functions.getContext = function () { - return this.ctx +/* + --------------------------------------- + Get current month + --------------------------------------- + For example, if it’s January: + - currentMonth() = 01 + - currentMonth(nameOfMonth) = ‘January’ +*/ +exports.currentMonth = (type) => { + if (type === 'nameOfMonth') { + return moment().format('MMMM') + } else { + return parseInt(moment().format('MM'), 10) } +} - // Expose all of lodash - functions.lodash = _ - - // Expose all of moment - functions.moment = moment +// Get current year +exports.currentYear = (type) => { + return moment().add(type, 'years').format('YYYY') +} - // Expose all of faker - functions.faker = faker +// Get the context - useful for logging +exports.getContext = () => { + return this.ctx +} - // Pass through to utility function. Done like this so we don't need to use filter syntax - as nothing really needs to get sent anyway - functions.isAuthorised = function (action) { - const data = this.ctx?.data - return permissions.providerIsAuthorised.apply(this, [data.signedInProviders, action]) - } +// Expose all of lodash +// functions.lodash = _ - // Config - set upstream variables - // Function adapted from: - // https://github.com/LotusTM/Kotsu/blob/4558ca79c58eadb14554e92deb68b276855d4502/modules/nunjucks-extensions.js#L66 - // see also https://github.com/mozilla/nunjucks/issues/406#issuecomment-227579804 +// Expose all of moment +// functions.moment = moment - // This allows child templates to override values set by parent templates - functions.config = function (prop, value, merge = true) { - const ctxValue = _.get(this.ctx, prop) +// Expose all of faker +// functions.faker = faker - // Get current context value if no `value` provided - if (value === undefined) return ctxValue +// Pass through to utility function. Done like this so we don't need to use filter syntax - as nothing really needs to get sent anyway +exports.isAuthorised = (data, action) => { + // const data = this.ctx?.data + // return permissions.providerIsAuthorised.apply(this, [data.signedInProviders, action]) + return true +} - if (!merge || !ctxValue) { - _.set(this.ctx, prop, value) - return - } +// Config - set upstream variables +// Function adapted from: +// https://github.com/LotusTM/Kotsu/blob/4558ca79c58eadb14554e92deb68b276855d4502/modules/nunjucks-extensions.js#L66 +// see also https://github.com/mozilla/nunjucks/issues/406#issuecomment-227579804 - // If this isn't Object, nothing we can do here, exit without changes to context - if (typeof value !== 'object') return +// This allows child templates to override values set by parent templates +exports.config = (prop, value, merge = true) => { + const ctxValue = _.get(this.ctx, prop) - // Shallow cloning prevents leaking when merging - value = (_.isArray(value) && _.union([], value, ctxValue)) || _.merge({}, value, ctxValue) + // Get current context value if no `value` provided + if (value === undefined) return ctxValue + if (!merge || !ctxValue) { _.set(this.ctx, prop, value) + return } - /* ------------------------------------------------------------------ - keep the following line to return your functions to the app - ------------------------------------------------------------------ */ - return functions + // If this isn't Object, nothing we can do here, exit without changes to context + if (typeof value !== 'object') return + + // Shallow cloning prevents leaking when merging + value = (_.isArray(value) && _.union([], value, ctxValue)) || _.merge({}, value, ctxValue) + + _.set(this.ctx, prop, value) } + +/* ------------------------------------------------------------------ + keep the following line to return your functions to the app +------------------------------------------------------------------ */ +// return functions diff --git a/app/routes.js b/app/routes.js index 0deb5891b..fc756b308 100755 --- a/app/routes.js +++ b/app/routes.js @@ -4,6 +4,7 @@ const router = govukPrototypeKit.requests.setupRouter() const _ = require('lodash') const url = require('url') const utils = require('./lib/utils') +const functions = require('./functions') const permissions = require('./filters/permissions.js').filters /// ------------------------------------------------------------------------ /// @@ -31,6 +32,8 @@ router.all('*', (req, res, next) => { res.locals.prevPage = parseInt(req.query.page) - 1 } + res.locals.isAuthorised = functions.isAuthorised + // Only search by the query if there is one // (and get "undefined" instead of "{}" if there is no query) const hasQuery = !_.isEmpty(req.query) From f4629dfad42c876583995985aef48fe8e7df0e1d Mon Sep 17 00:00:00 2001 From: lint-bot Date: Mon, 16 Dec 2024 10:45:54 +0000 Subject: [PATCH 18/30] Code style fixes --- app/functions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/functions.js b/app/functions.js index 1d18fe457..77c446177 100755 --- a/app/functions.js +++ b/app/functions.js @@ -7,7 +7,6 @@ const permissions = require('./filters/permissions.js').filters const { fakerEN_GB: faker } = require('@faker-js/faker') faker.seed(123) - // const functions = {} // Import functions from functions folder From 8b9fb64efabb21b0f928b18da6fe9766d60f4c0e Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 10:49:15 +0000 Subject: [PATCH 19/30] Delete Procfile --- Procfile | 1 - 1 file changed, 1 deletion(-) delete mode 100755 Procfile diff --git a/Procfile b/Procfile deleted file mode 100755 index 08938d90e..000000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: node ./node_modules/gulp/bin/gulp generate-assets && node listen-on-port.js From 89a95eb82820c046b6a4c42202a3f4bbef3e7952 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 10:49:21 +0000 Subject: [PATCH 20/30] Delete listen-on-port.js --- listen-on-port.js | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100755 listen-on-port.js diff --git a/listen-on-port.js b/listen-on-port.js deleted file mode 100755 index 31eb29408..000000000 --- a/listen-on-port.js +++ /dev/null @@ -1,31 +0,0 @@ -// NPM dependencies -const browserSync = require('browser-sync') - -// Local dependencies -const server = require('./server.js') -const config = require('./app/config.js') -const utils = require('./lib/utils.js') - -// Set up configuration variables -const useBrowserSync = config.useBrowserSync.toLowerCase() -const env = (process.env.NODE_ENV || 'development').toLowerCase() - -utils.findAvailablePort(server, function (port) { - console.log('Listening on port ' + port + ' url: http://localhost:' + port) - if (env === 'production' || useBrowserSync === 'false') { - server.listen(port) - } else { - server.listen(port - 50, function () { - browserSync({ - proxy: 'localhost:' + (port - 50), - port, - ui: false, - files: ['public/**/*.*', 'app/views/**/*.*'], - ghostMode: false, - open: false, - notify: false, - logLevel: 'error' - }) - }) - } -}) From 53026d40a2e4ed7bca0aa44c9836a54e3e2338f8 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 10:49:31 +0000 Subject: [PATCH 21/30] Delete lib folder and contents --- lib/admin/clear-data-success.html | 23 - lib/admin/clear-data.html | 36 -- lib/core_filters.js | 27 - lib/extensions/extensions.js | 193 ------- lib/extensions/extensions.test.js | 471 ------------------ .../authentication/authentication.js | 42 -- .../authentication/authentication.test.js | 162 ------ lib/middleware/extensions/extensions.js | 17 - lib/template.env | 20 - lib/template.session-data-defaults.js | 24 - lib/usage-data-prompt.txt | 9 - lib/usage_data.js | 79 --- lib/utils.js | 281 ----------- lib/v6/govuk_template_unbranded.html | 74 --- 14 files changed, 1458 deletions(-) delete mode 100755 lib/admin/clear-data-success.html delete mode 100755 lib/admin/clear-data.html delete mode 100755 lib/core_filters.js delete mode 100755 lib/extensions/extensions.js delete mode 100755 lib/extensions/extensions.test.js delete mode 100755 lib/middleware/authentication/authentication.js delete mode 100755 lib/middleware/authentication/authentication.test.js delete mode 100755 lib/middleware/extensions/extensions.js delete mode 100755 lib/template.env delete mode 100755 lib/template.session-data-defaults.js delete mode 100755 lib/usage-data-prompt.txt delete mode 100755 lib/usage_data.js delete mode 100755 lib/utils.js delete mode 100755 lib/v6/govuk_template_unbranded.html diff --git a/lib/admin/clear-data-success.html b/lib/admin/clear-data-success.html deleted file mode 100755 index cc80f5036..000000000 --- a/lib/admin/clear-data-success.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "layout.html" %} - -{% block pageTitle %} - Data cleared - GOV.UK Prototype Kit -{% endblock %} - -{% block content %} - -

- Data cleared -

- -

- The session data has been cleared. -

- -

- - Prototype home page - -

- -{% endblock %} diff --git a/lib/admin/clear-data.html b/lib/admin/clear-data.html deleted file mode 100755 index d4a2f4501..000000000 --- a/lib/admin/clear-data.html +++ /dev/null @@ -1,36 +0,0 @@ - -{% extends "layout.html" %} - -{% block pageTitle %} - Clear data? - GOV.UK Prototype Kit -{% endblock %} - -{% block beforeContent %} - {{ govukBackLink({ - "text": "Back", - "href": "javascript: window.history.go(-1)" - }) - }} -{% endblock %} - -{% block content %} - -
-
-
-

- Clear data? -

- {{ govukWarningText({ - text: "This will clear all of the data entered in this session", - iconFallbackText: "Warning" - }) }} - - {{ govukButton({ - text: "Clear the data" - }) }} -
-
-
- -{% endblock %} diff --git a/lib/core_filters.js b/lib/core_filters.js deleted file mode 100755 index 79bb69fbf..000000000 --- a/lib/core_filters.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = function (env) { - // If you need access to an internal nunjucks filter you can use env - // see the example below for 'safe' which is used in 'filters.log' - const nunjucksSafe = env.getFilter('safe') - - /** - * Object used to store the filters - * filters.foo("input") here, becomes {{ "input" | foo }} in templates - * @type {Object} - */ - const filters = {} - - /** - * Logs an object in the template to the console in the browser. - * @param {Any} a any type - * @return {String} a script tag with a console.log call. - * @example {{ "hello world" | log }} - * @example {{ "hello world" | log | safe }} [for environments with autoescaping turned on] - */ - filters.log = function log (a, string) { - if (string) { - return nunjucksSafe('') - } else return nunjucksSafe('') - } - - return filters -} diff --git a/lib/extensions/extensions.js b/lib/extensions/extensions.js deleted file mode 100755 index 1e06a70f0..000000000 --- a/lib/extensions/extensions.js +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Extensions.js (Use with caution) - * - * Experimental feature which is likely to change. - * This file returns helper methods to enable services to include - * their own departmental frontend(Styles, Scripts, nunjucks etc) - * - * Module.exports - * getPublicUrls: - * Params: (type | string ) eg. 'scripts', 'stylesheets' - * Description: - * returns array of urls for a type (script, stylesheet, nunjucks etc). - * getFileSystemPaths: - * Params: (type | string ) eg. 'scripts', 'stylesheets' - * Description: - * returns array paths to the file in the filesystem for a type (script, stylesheet, nunjucks etc) - * getPublicUrlAndFileSystemPaths: - * Params: (type | string ) eg. 'scripts', 'stylesheets' - * Description: - * returns Array of objects, each object is an extension and each obj has the filesystem & public url for the given type - * getAppConfig: - * Params: (type | string ) eg. 'scripts', 'stylesheets' - * Description: - * Returns an object containing two keys(scripts & stylesheets), each item contains an array of full paths to specific files. - * This is used in the views to output links and scripts each file. - * getAppViews: - * Params: (additionalViews | Array ) eg.extensions.getAppViews([path.join(__dirname, '/app/views/'),path.join(__dirname, '/lib/')]) - * Description: - * Returns an array of paths to nunjucks templates which is used to configure nunjucks in server.js - * setExtensionsByType - * Params: N/A - * Description: only used for test purposes to reset mocked extensions items to ensure they are up-to-date when the tests run - * - * * - */ - -/* eslint-disable no-unused-vars */ - -// Core dependencies -const fs = require('fs') -const path = require('path') - -// Local dependencies -const appConfig = require('../../app/config') - -// Generic utilities -const removeDuplicates = arr => [...new Set(arr)] -const filterOutParentAndEmpty = part => part && part !== '..' -const objectMap = (object, mapFn) => Object.keys(object).reduce((result, key) => { - result[key] = mapFn(object[key], key) - return result -}, {}) - -// File utilities -const getPathFromProjectRoot = (...all) => { - return path.join.apply(null, [__dirname, '..', '..'].concat(all)) -} -const pathToPackageConfigFile = packageName => getPathFromProjectRoot('node_modules', packageName, 'govuk-prototype-kit.config.json') - -const readJsonFile = (filePath) => { - return JSON.parse(fs.readFileSync(filePath, 'utf8')) -} -const getPackageConfig = packageName => { - if (fs.existsSync(pathToPackageConfigFile(packageName))) { - return readJsonFile(pathToPackageConfigFile(packageName)) - } else { - return {} - } -} - -// Handle errors to do with extension paths -// Example of `subject`: { packageName: 'govuk-frontend', item: '/all.js' } -const throwIfBadFilepath = subject => { - if (('' + subject.item).indexOf('\\') > -1) { - throw new Error(`Can't use backslashes in extension paths - "${subject.packageName}" used "${subject.item}".`) - } - if (!('' + subject.item).startsWith('/')) { - throw new Error(`All extension paths must start with a forward slash - "${subject.packageName}" used "${subject.item}".`) - } -} - -// Check for `baseExtensions` in config.js. If it's not there, default to `govuk-frontend` -const getBaseExtensions = () => appConfig.baseExtensions || ['govuk-frontend'] - -// Get all npm dependencies -// Get baseExtensions in the order defined in `baseExtensions` in config.js -// Then place baseExtensions before npm dependencies (and remove duplicates) -const getPackageNamesInOrder = () => { - const dependencies = readJsonFile(getPathFromProjectRoot('package.json')).dependencies || {} - const allNpmDependenciesInAlphabeticalOrder = Object.keys(dependencies).sort() - const installedBaseExtensions = getBaseExtensions() - .filter(packageName => allNpmDependenciesInAlphabeticalOrder.includes(packageName)) - - return removeDuplicates(installedBaseExtensions.concat(allNpmDependenciesInAlphabeticalOrder)) -} - -// Extensions provide items such as sass scripts, asset paths etc. -// This function groups them by type in a format which can used by getList -// Example of return -// { -// nunjucksPaths: [ -// { packageName: 'govuk-frontend', item: '/' }, -// { packageName: 'govuk-frontend', item: '/components'} -// ], -// scripts: [ -// { packageName: 'govuk-frontend', item: '/all.js' } -// ] -// assets: [ -// { packageName: 'govuk-frontend', item: '/assets' } -// ], -// sass: [ -// { packageName: 'govuk-frontend', item: '/all.scss' } -// ]} -const getExtensionsByType = () => { - return getPackageNamesInOrder() - .reduce((accum, packageName) => Object.assign({}, accum, objectMap( - getPackageConfig(packageName), - (listOfItemsForType, type) => (accum[type] || []) - .concat([].concat(listOfItemsForType).map(item => ({ - packageName, - item - }))) - )), {}) -} - -let extensionsByType - -const setExtensionsByType = () => { - extensionsByType = getExtensionsByType() -} - -setExtensionsByType() - -// The hard-coded reference to govuk-frontend allows us to soft launch without a breaking change. After a hard launch -// govuk-frontend assets will be served on /extension-assets/govuk-frontend -const getPublicUrl = config => { - let configItem - if (typeof config.item === 'object') { - configItem = config.item.path - } else { - configItem = config.item - } - - if (configItem.endsWith('assets') && config.packageName === 'govuk-frontend') { - return '/dist/govuk/assets' - } else { - return ['', 'extension-assets', config.packageName] - .concat(configItem.split('/').filter(filterOutParentAndEmpty)) - .map(encodeURIComponent) - .join('/') - } -} - -const getFileSystemPath = config => { - // throwIfBadFilepath(config) - - let configItem - if (typeof config.item === 'object') { - configItem = config.item.path.split('/').filter(filterOutParentAndEmpty).join(path.sep) - } else { - configItem = config.item.split('/').filter(filterOutParentAndEmpty).join(path.sep) - } - - return getPathFromProjectRoot('node_modules', - config.packageName, - configItem) -} - -const getPublicUrlAndFileSystemPath = config => ({ - fileSystemPath: getFileSystemPath(config), - publicUrl: getPublicUrl(config) -}) - -const getList = type => extensionsByType[type] || [] - -// Exports -const self = module.exports = { - getPublicUrls: type => getList(type).map(getPublicUrl), - getFileSystemPaths: type => getList(type).map(getFileSystemPath), - getPublicUrlAndFileSystemPaths: type => getList(type).map(getPublicUrlAndFileSystemPath), - getAppConfig: _ => ({ - scripts: self.getPublicUrls('scripts'), - stylesheets: self.getPublicUrls('stylesheets') - }), - getAppViews: additionalViews => self - .getFileSystemPaths('nunjucksPaths') - .reverse() - .concat(additionalViews || []), - - setExtensionsByType // exposed only for testing purposes -} - -/* eslint-enable no-unused-vars */ diff --git a/lib/extensions/extensions.test.js b/lib/extensions/extensions.test.js deleted file mode 100755 index 8dccdf9f3..000000000 --- a/lib/extensions/extensions.test.js +++ /dev/null @@ -1,471 +0,0 @@ -/* eslint-env jest */ -/* eslint-disable no-prototype-builtins */ -/* global spyOn */ -// NPM dependencies -const path = require('path') -const fs = require('fs') -const appConfig = require('../../app/config') - -// Local dependencies -const extensions = require('./extensions.js') - -// Local variables -const rootPath = path.join(__dirname, '..', '..') -let testScope - -// helpers -const joinPaths = arr => arr.map(x => path.join.apply(null, [rootPath].concat(x))) - -describe('extensions', () => { - beforeEach(() => { - testScope = { - originalValues: { - appConfigBaseExtensions: appConfig.baseExtensions - }, - fileSystem: {} - } - addFileToMockFileSystem(['package.json'], fs.readFileSync('package.json', 'utf8')) - addFileToMockFileSystem(['node_modules', 'govuk-frontend', 'govuk-prototype-kit.config.json'], '{"nunjucksPaths": ["/"],"scripts": ["/dist/govuk/all.js"],"assets": ["/dist/govuk/assets"],"sass": ["/dist/govuk/all.scss"]}') - setupFakeFilesystem() - extensions.setExtensionsByType() - }) - afterEach(() => { - jest.clearAllMocks() - appConfig.baseExtensions = testScope.originalValues.appConfigBaseExtensions - }) - - describe('Lookup file system paths', () => { - it('should lookup asset paths as file system paths', () => { - expect(extensions.getFileSystemPaths('assets')).toEqual(joinPaths([ - ['node_modules', 'govuk-frontend', 'govuk', 'assets'] - ])) - }) - it('should not allow traversing the file system', () => { - mockExtensionConfig('govuk-frontend', { assets: ['/abc/../../../../../def'] }) - expect(extensions.getFileSystemPaths('assets')).toEqual(joinPaths([ - ['node_modules', 'govuk-frontend', 'abc', 'def'] - ])) - }) - it('should show installed extensions asset paths as file system paths', () => { - delete appConfig.baseExtensions - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getFileSystemPaths('assets')).toEqual(joinPaths([ - ['node_modules', 'govuk-frontend', 'govuk', 'assets'], - ['node_modules', 'another-frontend', 'abc'], - ['node_modules', 'another-frontend', 'def'], - ['node_modules', 'hmrc-frontend', 'ghi'], - ['node_modules', 'hmrc-frontend', 'jkl'] - ])) - }) - it('should follow strict alphabetical order when no base extensions used', () => { - appConfig.baseExtensions = [] - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getFileSystemPaths('assets')).toEqual(joinPaths([ - ['node_modules', 'another-frontend', 'abc'], - ['node_modules', 'another-frontend', 'def'], - ['node_modules', 'govuk-frontend', 'govuk', 'assets'], - ['node_modules', 'hmrc-frontend', 'ghi'], - ['node_modules', 'hmrc-frontend', 'jkl'] - ])) - }) - it('should put specified baseExtensions at the top', () => { - appConfig.baseExtensions = ['hmrc-frontend', 'govuk-frontend'] - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getFileSystemPaths('assets')).toEqual(joinPaths([ - ['node_modules', 'hmrc-frontend', 'ghi'], - ['node_modules', 'hmrc-frontend', 'jkl'], - ['node_modules', 'govuk-frontend', 'govuk', 'assets'], - ['node_modules', 'another-frontend', 'abc'], - ['node_modules', 'another-frontend', 'def'] - ])) - }) - it('should show installed extensions asset paths as file system paths', () => { - mockExtensionConfig('hmrc-frontend', { - assets: ['/abc', '/def'] - }) - expect(extensions.getFileSystemPaths('assets')).toEqual(joinPaths([ - ['node_modules', 'govuk-frontend', 'govuk', 'assets'], - ['node_modules', 'hmrc-frontend', 'abc'], - ['node_modules', 'hmrc-frontend', 'def'] - ])) - }) - it('should lookup scripts paths as file system paths', () => { - expect(extensions.getFileSystemPaths('scripts')).toEqual(joinPaths([ - 'node_modules/govuk-frontend/dist/govuk/all.js' - ])) - }) - it('should not break when asking for an extension key which isn\'t used', function () { - expect(extensions.getFileSystemPaths('thisListDoesNotExist')).toEqual([]) - }) - }) - - describe('Lookup public URLs', () => { - it('should show installed extensions asset paths as file system paths', () => { - delete appConfig.baseExtensions - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getPublicUrls('assets')).toEqual([ - '/dist/govuk/assets', - '/extension-assets/another-frontend/abc', - '/extension-assets/another-frontend/def', - '/extension-assets/hmrc-frontend/ghi', - '/extension-assets/hmrc-frontend/jkl' - ]) - }) - it('should follow strict alphabetical order when no base extensions used', () => { - appConfig.baseExtensions = [] - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getPublicUrls('assets')).toEqual([ - '/extension-assets/another-frontend/abc', - '/extension-assets/another-frontend/def', - '/dist/govuk/assets', - '/extension-assets/hmrc-frontend/ghi', - '/extension-assets/hmrc-frontend/jkl' - ]) - }) - it('should put specified baseExtensions at the top', () => { - appConfig.baseExtensions = ['hmrc-frontend', 'govuk-frontend'] - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getPublicUrls('assets')).toEqual([ - '/extension-assets/hmrc-frontend/ghi', - '/extension-assets/hmrc-frontend/jkl', - '/dist/govuk/assets', - '/extension-assets/another-frontend/abc', - '/extension-assets/another-frontend/def' - ]) - }) - it('should url encode each part', () => { - mockExtensionConfig('mine', { assets: ['/abc:def'] }) - mockUninstallExtension('govuk-frontend') - - expect(extensions.getPublicUrls('assets')).toEqual(['/extension-assets/mine/abc%3Adef']) - }) - }) - - describe('Lookup public URLs with file system paths', () => { - it('should show installed extensions asset paths as file system paths', () => { - delete appConfig.baseExtensions - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getPublicUrlAndFileSystemPaths('assets')).toEqual([ - { - publicUrl: '/dist/govuk/assets', - fileSystemPath: path.join(rootPath, 'node_modules', 'govuk-frontend', 'govuk', 'assets') - }, - { - publicUrl: '/extension-assets/another-frontend/abc', - fileSystemPath: path.join(rootPath, 'node_modules', 'another-frontend', 'abc') - }, - { - publicUrl: '/extension-assets/another-frontend/def', - fileSystemPath: path.join(rootPath, 'node_modules', 'another-frontend', 'def') - }, - { - publicUrl: '/extension-assets/hmrc-frontend/ghi', - fileSystemPath: path.join(rootPath, 'node_modules', 'hmrc-frontend', 'ghi') - }, - { - publicUrl: '/extension-assets/hmrc-frontend/jkl', - fileSystemPath: path.join(rootPath, 'node_modules', 'hmrc-frontend', 'jkl') - } - ]) - }) - it('should follow strict alphabetical order when no base extensions used', () => { - appConfig.baseExtensions = [] - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getPublicUrlAndFileSystemPaths('assets')).toEqual([ - { - publicUrl: '/extension-assets/another-frontend/abc', - fileSystemPath: path.join(rootPath, 'node_modules', 'another-frontend', 'abc') - }, - { - publicUrl: '/extension-assets/another-frontend/def', - fileSystemPath: path.join(rootPath, 'node_modules', 'another-frontend', 'def') - }, - { - publicUrl: '/dist/govuk/assets', - fileSystemPath: path.join(rootPath, 'node_modules', 'govuk-frontend', 'govuk', 'assets') - }, - { - publicUrl: '/extension-assets/hmrc-frontend/ghi', - fileSystemPath: path.join(rootPath, 'node_modules', 'hmrc-frontend', 'ghi') - }, - { - publicUrl: '/extension-assets/hmrc-frontend/jkl', - fileSystemPath: path.join(rootPath, 'node_modules', 'hmrc-frontend', 'jkl') - } - ]) - }) - it('should put specified baseExtensions at the top', () => { - appConfig.baseExtensions = ['hmrc-frontend', 'govuk-frontend'] - mockExtensionConfig('another-frontend', { - assets: ['/abc', '/def'] - }) - mockExtensionConfig('hmrc-frontend', { - assets: ['/ghi', '/jkl'] - }) - expect(extensions.getPublicUrlAndFileSystemPaths('assets')).toEqual([ - { - publicUrl: '/extension-assets/hmrc-frontend/ghi', - fileSystemPath: path.join(rootPath, 'node_modules', 'hmrc-frontend', 'ghi') - }, - { - publicUrl: '/extension-assets/hmrc-frontend/jkl', - fileSystemPath: path.join(rootPath, 'node_modules', 'hmrc-frontend', 'jkl') - }, - { - publicUrl: '/dist/govuk/assets', - fileSystemPath: path.join(rootPath, 'node_modules', 'govuk-frontend', 'govuk', 'assets') - }, - { - publicUrl: '/extension-assets/another-frontend/abc', - fileSystemPath: path.join(rootPath, 'node_modules', 'another-frontend', 'abc') - }, - { - publicUrl: '/extension-assets/another-frontend/def', - fileSystemPath: path.join(rootPath, 'node_modules', 'another-frontend', 'def') - } - ]) - }) - it('should url encode each part', () => { - mockExtensionConfig('mine', { assets: ['/abc:def'] }) - mockUninstallExtension('govuk-frontend') - - expect(extensions.getPublicUrls('assets')).toEqual(['/extension-assets/mine/abc%3Adef']) - }) - it('should not break when asking for an extension key which isn\'t used', function () { - expect(extensions.getPublicUrls('anotherListThatDoesntExist')).toEqual([]) - }) - }) - - describe('getAppViews', () => { - it('should be a function', () => { - expect(extensions.getAppViews).toBeInstanceOf(Function) - }) - - it('should output govuk-frontend nunjucks paths as an array', () => { - expect(extensions.getAppViews()).toEqual(joinPaths([ - 'node_modules/govuk-frontend' - ])) - }) - - it('should also output hmcts-frontend nunjucks paths after it is installed', () => { - mockExtensionConfig('hmcts-frontend', { - nunjucksPaths: [ - '/my-components', - '/my-layouts' - ] - }) - - expect(extensions.getAppViews()).toEqual(joinPaths([ - 'node_modules/hmcts-frontend/my-layouts', - 'node_modules/hmcts-frontend/my-components', - 'node_modules/govuk-frontend' - ])) - }) - - it('should not output any nunjucks paths when frontends are uninstalled', () => { - mockUninstallExtension('govuk-frontend') - - expect(extensions.getAppViews()).toEqual([]) - }) - - it('should also output provided paths in the array', () => { - expect(extensions.getAppViews(joinPaths([ - '/app/views', - '/lib' - ]))).toEqual(joinPaths([ - 'node_modules/govuk-frontend', - '/app/views', - '/lib' - ])) - }) - - it('should output any provided paths in the array', () => { - expect(extensions.getAppViews([ - '/my-new-views-directory' - ])).toEqual([ - path.join(rootPath, 'node_modules/govuk-frontend'), - '/my-new-views-directory' - ]) - }) - }) - - describe('getAppConfig', () => { - it('returns an object', () => { - expect(extensions.getAppConfig()).toBeInstanceOf(Object) - }) - - it('should have script and stylesheet keys', () => { - expect(Object.keys(extensions.getAppConfig())).toEqual(['scripts', 'stylesheets']) - }) - - it('should return a list of public urls for the scripts', () => { - expect(extensions.getAppConfig().scripts).toEqual([ - '/extension-assets/govuk-frontend/dist/govuk/all.js' - ]) - }) - - it('should return a list of public urls for the stylesheets', () => { - expect(extensions.getAppConfig().stylesheets).toEqual([]) - }) - - it('should include installed extensions', () => { - mockExtensionConfig('my-extension', { scripts: ['/abc/def/ghi.js'] }) - expect(extensions.getAppConfig().scripts).toEqual([ - '/extension-assets/govuk-frontend/dist/govuk/all.js', - '/extension-assets/my-extension/abc/def/ghi.js' - ]) - }) - - it('should return a list of public urls for the stylesheets', () => { - expect(extensions.getAppConfig().stylesheets).toEqual([]) - }) - - it('should include installed extensions', () => { - mockExtensionConfig('my-extension', { stylesheets: ['/abc/def/ghi.css'] }) - expect(extensions.getAppConfig().stylesheets).toEqual([ - '/extension-assets/my-extension/abc/def/ghi.css' - ]) - }) - }) - - describe('error handling', () => { - it('should cope with keys which aren\'t arrays', () => { - mockExtensionConfig('my-fixable-extension', { stylesheets: '/abc.css' }) - mockExtensionConfig('another-fixable-extension', { stylesheets: '/abc.css' }) - - expect(extensions.getAppConfig().stylesheets).toEqual([ - '/extension-assets/another-fixable-extension/abc.css', - '/extension-assets/my-fixable-extension/abc.css' - ]) - }) - it('should throw if paths use backslashes', () => { - mockExtensionConfig('my-unfixable-extension', { stylesheets: '\\abc\\def.css' }) - mockExtensionConfig('another-fixable-extension', { stylesheets: ['/abc.css'] }) - - const expectedError = new Error('Can\'t use backslashes in extension paths - "my-unfixable-extension" used "\\abc\\def.css".') - - expect(() => { - extensions.getFileSystemPaths('stylesheets') - }).toThrow(expectedError) - - expect(() => { - extensions.getPublicUrlAndFileSystemPaths('stylesheets') - }).toThrow(expectedError) - }) - it('should throw if paths use backslashes further into the path', () => { - mockExtensionConfig('my-other-unfixable-extension', { stylesheets: ['/abc\\def.css'] }) - const expectedError2 = new Error('Can\'t use backslashes in extension paths - "my-other-unfixable-extension" used "/abc\\def.css".') - - expect(() => { - extensions.getFileSystemPaths('stylesheets') - }).toThrow(expectedError2) - - expect(() => { - extensions.getPublicUrlAndFileSystemPaths('stylesheets') - }).toThrow(expectedError2) - }) - it('should throw if it doesn\'t start with a forward slash', () => { - mockExtensionConfig('yet-another-unfixable-extension', { stylesheets: ['abc.css'] }) - - const noLeadingForwardSlashError = new Error('All extension paths must start with a forward slash - "yet-another-unfixable-extension" used "abc.css".') - - expect(() => { - extensions.getFileSystemPaths('stylesheets') - }).toThrow(noLeadingForwardSlashError) - - expect(() => { - extensions.getPublicUrlAndFileSystemPaths('stylesheets') - }).toThrow(noLeadingForwardSlashError) - }) - }) - - const setupFakeFilesystem = () => { - const prepFilePath = filePath => { - return (filePath).replace(rootPath + path.sep, '').split(path.sec) - } - - spyOn(fs, 'readFileSync').and.callFake(function (filePath) { - const trimmedPath = prepFilePath(filePath) - if (doesFileExitInMockFileSystem(trimmedPath)) { - return readFileFromMockFileSystem(trimmedPath) - } else { - const err = new Error(`ENOENT: no such file or directory, open '${filePath}'`) - err.code = 'ENOENT' - throw err - } - }) - spyOn(fs, 'existsSync').and.callFake(filePath => doesFileExitInMockFileSystem(prepFilePath(filePath))) - } - - const addFileToMockFileSystem = (pathParts, content) => { - testScope.fileSystem[path.join(...pathParts)] = content - } - - const readFileFromMockFileSystem = (pathParts) => testScope.fileSystem[path.join(...pathParts)] - - const doesFileExitInMockFileSystem = (pathParts) => { - return testScope.fileSystem.hasOwnProperty(path.join(...pathParts)) - } - - const mockInstallExtension = (packageName, version = '^0.0.1') => { - const existingPackageJson = JSON.parse(readFileFromMockFileSystem(['package.json'])) - existingPackageJson.dependencies[packageName] = version - addFileToMockFileSystem(['package.json'], JSON.stringify(existingPackageJson)) - extensions.setExtensionsByType() - } - - const mockUninstallExtension = (packageName) => { - const existingPackageJson = JSON.parse(readFileFromMockFileSystem(['package.json'])) - if (!existingPackageJson.dependencies.hasOwnProperty(packageName)) { - throw new Error(`Could not uninstall '${packageName}' as it is not installed`) - } - delete existingPackageJson.dependencies[packageName] - addFileToMockFileSystem(['package.json'], JSON.stringify(existingPackageJson)) - extensions.setExtensionsByType() - } - - const mockExtensionConfig = (packageName, config = {}, version) => { - addFileToMockFileSystem(['node_modules', packageName, 'govuk-prototype-kit.config.json'], JSON.stringify(config, null, 2)) - mockInstallExtension(packageName, version) - } -}) diff --git a/lib/middleware/authentication/authentication.js b/lib/middleware/authentication/authentication.js deleted file mode 100755 index 0d2e57266..000000000 --- a/lib/middleware/authentication/authentication.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Simple basic auth middleware for use with Express 4.x. - * - * Based on template found at: http://www.danielstjules.com/2014/08/03/basic-auth-with-express-4/ - * - * @example - * const authentication = required('authentication.js') - * app.use(authentication) - * - * @param {string} req Express Request object - * @param {string} res Express Response object - * @returns {function} Express 4 middleware requiring the given credentials - */ - -module.exports = (req, res, next) => { - // NPM Dependencies - const basicAuth = require('basic-auth') - - // Local dependencies - const config = require('../../../app/config.js') - - // Local Variables - const env = (process.env.NODE_ENV || 'development').toLowerCase() - const useAuth = (process.env.USE_AUTH || config.useAuth).toLowerCase() - const username = process.env.USERNAME - const password = process.env.PASSWORD - - if (env === 'production' && useAuth === 'true') { - if (!username || !password) { - console.error('Username or password is not set.') - return res.send('

Error:

Username or password not set. See guidance for setting these.

') - } - - const user = basicAuth(req) - - if (!user || user.name !== username || user.pass !== password) { - res.set('WWW-Authenticate', 'Basic realm=Authorization Required') - return res.sendStatus(401) - } - } - next() -} diff --git a/lib/middleware/authentication/authentication.test.js b/lib/middleware/authentication/authentication.test.js deleted file mode 100755 index 3152c1438..000000000 --- a/lib/middleware/authentication/authentication.test.js +++ /dev/null @@ -1,162 +0,0 @@ -/* eslint-env jest */ -// NPM dependencies -jest.mock('basic-auth') -jest.mock('express/lib/response') -const basicAuth = require('basic-auth') - -// Local dependencies -const authentication = require('./authentication.js') - -// Local variables -const userDetails = { - name: 'secret-username', - pass: 'secure-password' -} -const next = jest.fn() - -const res = require('express/lib/response') - -// Mock console.log so we can check any output -console.error = jest.fn() - -describe('authentication', () => { - it('should be a function', () => { - expect(authentication).toBeInstanceOf(Function) - }) - - describe('when it runs in production', () => { - beforeAll(() => { - process.env.NODE_ENV = 'production' - process.env.USE_AUTH = 'true' - }) - - describe('server with no username/password set', () => { - beforeAll(() => { - delete process.env.USERNAME - delete process.env.PASSWORD - }) - - beforeEach(() => { - // Jest mocks stores each call to the mocked function - // so we want to clear them before running the authentication again. - console.error.mockClear() - authentication({}, res, next) - }) - - it('should return a console error', () => { - const consoleErrorMessage = console.error.mock.calls[0][0] - expect(consoleErrorMessage).toBe('Username or password is not set.') - }) - - it('should send marked up error message', () => { - const errorDisplayedToUser = res.send.mock.calls[0][0] - expect(errorDisplayedToUser).toBe('

Error:

Username or password not set. See guidance for setting these.

') - }) - }) - - describe('server with username/password set', () => { - beforeAll(() => { - process.env.USERNAME = 'secret-username' - process.env.PASSWORD = 'secure-password' - }) - - describe('when a user supplies correct username/password', () => { - beforeEach(() => { - jest.clearAllMocks() - basicAuth.mockReturnValue(userDetails) - authentication({}, res, next) - }) - - it('should not return a console error', () => { - expect(console.error).not.toBeCalled() - }) - - it('should call basic-auth and return username/password', () => { - expect(basicAuth).toReturnWith(userDetails) - }) - - it('should not set authentication header', () => { - expect(res.set).not.toBeCalled() - }) - - it('should not return a status', () => { - expect(res.sendStatus).not.toBeCalled() - }) - - it('should progress to the next middleware', () => { - expect(next).toBeCalled() - }) - }) - - describe('when a user supplies incorrect username/password', () => { - beforeEach(() => { - jest.clearAllMocks() - basicAuth.mockReturnValue(undefined) - authentication({}, res, next) - }) - - it('should not return a console error', () => { - expect(console.error).not.toBeCalled() - }) - - it('should not send error message to the browser', () => { - expect(res.send).not.toBeCalled() - }) - - it('should call basic-auth and return username/password', () => { - expect(basicAuth).toReturnWith(undefined) - }) - - it('should set authentication header', () => { - expect(res.set).toHaveBeenCalledWith('WWW-Authenticate', 'Basic realm=Authorization Required') - }) - - it('should return 401/UnAuthorized', () => { - expect(res.sendStatus).toHaveBeenCalledWith(401) - }) - - it('should not progress to the next middleware', () => { - expect(next).not.toBeCalled() - }) - }) - }) - }) - - describe('when it runs in non-production enviroment (dev by default)', () => { - beforeAll(() => { - // Jest automatically sets NODE_ENV to 'test' - // but we want to test when there is no NODE_ENV and it defaults - // to development - delete process.env.NODE_ENV - }) - - beforeEach(() => { - jest.clearAllMocks() - authentication({}, res, next) - }) - - it('should not call console error', () => { - expect(console.error).not.toBeCalled() - }) - - it('should not send marked up error message', () => { - expect(res.send).not.toBeCalled() - }) - - it('should not call basic-auth', () => { - expect(basicAuth).not.toBeCalled() - }) - - it('should not set authentication header', () => { - expect(res.set).not.toBeCalled() - }) - - it('should not return 401/UnAuthorized', () => { - expect(res.sendStatus).not.toBeCalled() - }) - - it('should progress to the next middleware', () => { - expect(next).toBeCalled() - }) - }) -}) diff --git a/lib/middleware/extensions/extensions.js b/lib/middleware/extensions/extensions.js deleted file mode 100755 index 9475ae9cd..000000000 --- a/lib/middleware/extensions/extensions.js +++ /dev/null @@ -1,17 +0,0 @@ -const express = require('express') -const extensions = require('../../extensions/extensions') -const router = express.Router() - -// Serve assets from extensions -function setupPathsFor (item) { - extensions.getPublicUrlAndFileSystemPaths(item) - .forEach(paths => { - router.use(paths.publicUrl, express.static(paths.fileSystemPath)) - }) -} - -setupPathsFor('scripts') -setupPathsFor('stylesheets') -setupPathsFor('assets') - -module.exports = router diff --git a/lib/template.env b/lib/template.env deleted file mode 100755 index 473aba53a..000000000 --- a/lib/template.env +++ /dev/null @@ -1,20 +0,0 @@ -# ========================================= -# WARNING: Don't commit this file to git -# ========================================= -# -# Use this file for storing private data - things like API keys and admin credentials -# -# Storing data: -# -# EXAMPLE_API_KEY=123456789 -# JSON={"foo": "bar"} -# -# More examples here: https://www.npmjs.com/package/dotenv -# -# Use the data in your app: -# -# var key = process.env.EXAMPLE_API_KEY - -# ========================================= -# INSERT YOUR DATA HERE: - diff --git a/lib/template.session-data-defaults.js b/lib/template.session-data-defaults.js deleted file mode 100755 index 9e4921b0a..000000000 --- a/lib/template.session-data-defaults.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - -Provide default values for user session data. These are automatically added -via the `autoStoreData` middleware. Values will only be added to the -session if a value doesn't already exist. This may be useful for testing -journeys where users are returning or logging in to an existing application. - -============================================================================ - -Example usage: - -"full-name": "Sarah Philips", - -"options-chosen": [ "foo", "bar" ] - -============================================================================ - -*/ - -module.exports = { - - // Insert values here - -} diff --git a/lib/usage-data-prompt.txt b/lib/usage-data-prompt.txt deleted file mode 100755 index e3e6b9237..000000000 --- a/lib/usage-data-prompt.txt +++ /dev/null @@ -1,9 +0,0 @@ - -Help us improve the GOV.UK Prototype Kit -──────────────────────────────────────── - -With your permission, the kit can send useful anonymous usage data -for analysis to help the team improve the service. Read more here: -https://govuk-prototype-kit.herokuapp.com/docs/usage-data - -Do you give permission for the kit to send anonymous usage data? diff --git a/lib/usage_data.js b/lib/usage_data.js deleted file mode 100755 index f4f1cb89a..000000000 --- a/lib/usage_data.js +++ /dev/null @@ -1,79 +0,0 @@ -// Core dependencies -const path = require('path') -const fs = require('fs') -const os = require('os') - -// NPM dependencies -const inquirer = require('inquirer') -const universalAnalytics = require('universal-analytics') -const { v4: uuidv4 } = require('uuid') - -// Local dependencies -const packageJson = require('../package.json') - -exports.getUsageDataConfig = function () { - // Try to read config file to see if usage data is opted in - let usageDataConfig = {} - try { - usageDataConfig = require(path.join(__dirname, '../usage-data-config.json')) - } catch (e) { - // do nothing - we will make a config - } - return usageDataConfig -} - -exports.setUsageDataConfig = function (usageDataConfig) { - const usageDataConfigJSON = JSON.stringify(usageDataConfig, null, ' ') - try { - fs.writeFileSync(path.join(__dirname, '../usage-data-config.json'), usageDataConfigJSON) - return true - } catch (error) { - console.error(error) - } - return false -} - -// Ask for permission to track data -// returns a Promise with the user's answer -exports.askForUsageDataPermission = function () { - return new Promise(function (resolve, reject) { - const description = fs.readFileSync(path.join(__dirname, 'usage-data-prompt.txt'), 'utf8').trim() - - inquirer.prompt([{ - name: 'usageData', - message: description, - type: 'confirm', - when: () => process.stdout.isTTY, - default: false - }]).then(answers => resolve(answers.usageData)) - }) -} - -exports.startTracking = function (usageDataConfig) { - // Get client ID for tracking - // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#cid - - if (usageDataConfig.clientId === undefined) { - usageDataConfig.clientId = uuidv4() - exports.setUsageDataConfig(usageDataConfig) - } - - // Track kit start event, with kit version, operating system and Node.js version - const trackingId = 'UA-26179049-21' - const trackingUser = universalAnalytics(trackingId, usageDataConfig.clientId) - - const kitVersion = packageJson.version - const operatingSystem = os.platform() + ' ' + os.release() - const nodeVersion = process.versions.node - - // Anonymise the IP - trackingUser.set('anonymizeIp', 1) - - // Set custom dimensions - trackingUser.set('cd1', operatingSystem) - trackingUser.set('cd2', kitVersion) - trackingUser.set('cd3', nodeVersion) - - // Trigger start event - trackingUser.event('State', 'Start').send() -} diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100755 index ae0a814de..000000000 --- a/lib/utils.js +++ /dev/null @@ -1,281 +0,0 @@ -// Core dependencies -const fs = require('fs') - -// NPM dependencies -const path = require('path') -const portScanner = require('portscanner') -const inquirer = require('inquirer') -const request = require('sync-request') - -// Local dependencies -const config = require('../app/config.js') - -// Variables -let releaseUrl = null - -// Require core and custom filters, merges to one object -// and then add the methods to Nunjucks environment -exports.addNunjucksFilters = function (env) { - const coreFilters = require('./core_filters.js')(env) - const customFilters = require('../app/filters.js')(env) - const filters = Object.assign(coreFilters, customFilters) - Object.keys(filters).forEach(function (filterName) { - env.addFilter(filterName, filters[filterName]) - }) -} - -// ***Register trainee teachers - modified from default kit -// Require core and custom filters, merges to one object -// and then add the methods to Nunjucks environment -exports.addNunjucksFunctions = function (env) { - const functions = require('../app/functions.js')(env) - Object.keys(functions).forEach(function (functionName) { - env.addGlobal(functionName, functions[functionName]) - }) -} - -// ***Register trainee teachers - modified from default kit -// Add Nunjucks function called 'checked' to populate radios and checkboxes -exports.addCheckedFunction = function (env) { - env.addGlobal('checked', function (data, value) { - let checked = '' - // Check the requested data exists - if (!data) { - return checked - } - - // Check if value exists in data - if (data.includes(value)) { - checked = 'checked' - } - return checked - }) -} - -// Find an available port to run the server on -exports.findAvailablePort = function (app, callback) { - let port = null - - // When the server starts, we store the port in .port.tmp so it tries to restart - // on the same port - try { - port = Number(fs.readFileSync(path.join(__dirname, '/../.port.tmp'))) - } catch (e) { - port = Number(process.env.PORT || config.port) - } - - console.log('') - - // Check port is free, else offer to change - portScanner.findAPortNotInUse(port, port + 50, '127.0.0.1', function (error, availablePort) { - if (error) { throw error } - if (port === availablePort) { - // Port is free, return it via the callback - callback(port) - } else { - // Port in use - offer to change to available port - console.error('ERROR: Port ' + port + ' in use - you may have another prototype running.\n') - - // Ask user if they want to change port - inquirer.prompt([{ - name: 'changePort', - message: 'Change to an available port?', - type: 'confirm' - }]).then(answers => { - if (answers.changePort) { - // User answers yes - port = availablePort - fs.writeFileSync(path.join(__dirname, '/../.port.tmp'), port.toString()) - console.log('Changed to port ' + port) - - callback(port) - } else { - // User answers no - exit - console.log('\nYou can set a new default port in server.js, or by running the server with PORT=XXXX') - console.log("\nExit by pressing 'ctrl + c'") - process.exit(0) - } - }) - } - }) -} - -// Redirect HTTP requests to HTTPS -exports.forceHttps = (req, res, next) => { - if (req.headers['x-forwarded-proto'] !== 'https') { - console.log('Redirecting request to https') - // 302 temporary - this is a feature that can be disabled - return res.redirect(302, 'https://' + req.get('Host') + req.url) - } - - // Mark proxy as secure (allows secure cookies) - req.connection.proxySecure = true - next() -} - -// Synchronously get the URL for the latest release on GitHub and cache it -exports.getLatestRelease = function () { - if (releaseUrl !== null) { - // Release URL already exists - console.log('Release url cached:', releaseUrl) - return releaseUrl - } else { - // Release URL doesn't exist - try { - console.log('Getting latest release from GitHub') - - const res = request( - 'GET', - 'https://api.github.com/repos/alphagov/govuk-prototype-kit/releases/latest', - { - headers: { 'user-agent': 'node.js' } - } - ) - const data = JSON.parse(res.getBody('utf8')) - - // Cache releaseUrl before we return it - releaseUrl = `https://github.com/alphagov/govuk-prototype-kit/archive/${data.name}.zip` - - console.log('Release URL is', releaseUrl) - return releaseUrl - } catch (err) { - console.log("Couldn't retrieve release URL") - return 'https://github.com/alphagov/govuk-prototype-kit/releases/latest' - } - } -} - -// Try to match a request to a template, for example a request for /test -// would look for /app/views/test.html -// and /app/views/test/index.html - -function renderPath (path, res, next) { - // Try to render the path - res.render(path, function (error, html) { - if (!error) { - // Success - send the response - res.set({ 'Content-type': 'text/html; charset=utf-8' }) - res.end(html) - return - } - if (!error.message.startsWith('template not found')) { - // We got an error other than template not found - call next with the error - next(error) - return - } - if (!path.endsWith('/index')) { - // Maybe it's a folder - try to render [path]/index.html - renderPath(path + '/index', res, next) - return - } - // We got template not found both times - call next to trigger the 404 page - next() - }) -} - -exports.matchRoutes = (req, res, next) => { - let path = req.path - - // Remove the first slash, render won't work with it - path = path.substr(1) - - // If it's blank, render the root index - if (path === '') { - path = 'index' - } - - renderPath(path, res, next) -} - -// Store data from POST body or GET query in session -const storeData = function (input, data) { - for (const i in input) { - // any input where the name starts with _ is ignored - if (i.indexOf('_') === 0) { - continue - } - - const val = input[i] - - // Delete values when users unselect checkboxes - if (val === '_unchecked' || val === ['_unchecked']) { - delete data[i] - continue - } - - // Remove _unchecked from arrays of checkboxes - if (Array.isArray(val)) { - const index = val.indexOf('_unchecked') - if (index !== -1) { - val.splice(index, 1) - } - } else if (typeof val === 'object') { - // Store nested objects that aren't arrays - if (typeof data[i] !== 'object') { - data[i] = {} - } - - // Add nested values - storeData(val, data[i]) - continue - } - - data[i] = val - } -} - -// Get session default data from file -let sessionDataDefaults = {} - -const sessionDataDefaultsFile = path.join(__dirname, '/../app/data/session-data-defaults.js') - -try { - sessionDataDefaults = require(sessionDataDefaultsFile) -} catch (e) { - console.error('Could not load the session data defaults from app/data/session-data-defaults.js. Might be a syntax error?') - console.error(e) -} - -// Middleware - store any data sent in session, and pass it to all views -exports.autoStoreData = (req, res, next) => { - if (!req.session.data) { - req.session.data = {} - } - - req.session.data = Object.assign({}, sessionDataDefaults, req.session.data) - - storeData(req.body, req.session.data) - storeData(req.query, req.session.data) - - // Send session data to all views - - res.locals.data = {} - - for (const j in req.session.data) { - res.locals.data[j] = req.session.data[j] - } - - next() -} - -exports.handleCookies = function (app) { - return function handleCookies (req, res, next) { - const COOKIE_NAME = 'seen_cookie_message' - const cookie = req.cookies[COOKIE_NAME] - - if (cookie === 'yes') { - app.locals.shouldShowCookieMessage = false - return next() - } - - const maxAgeInDays = 28 - res.cookie(COOKIE_NAME, 'yes', { - maxAge: maxAgeInDays * 24 * 60 * 60 * 1000, - httpOnly: true - }) - - app.locals.shouldShowCookieMessage = true - - next() - } -} diff --git a/lib/v6/govuk_template_unbranded.html b/lib/v6/govuk_template_unbranded.html deleted file mode 100755 index 905f24104..000000000 --- a/lib/v6/govuk_template_unbranded.html +++ /dev/null @@ -1,74 +0,0 @@ -{% block top_of_page %}{% endblock %} - - - - - - {% block page_title %}{% endblock %} - - - - - - - - - - - - - - - - - - - - - - - {% block head %}{% endblock %} - - - - - - {% block body_start %}{% endblock %} - - - - - - - - - - - - {% block after_header %}{% endblock %} - - {% block content %}{% endblock %} - -
-
- - - - - - - - {% block body_end %}{% endblock %} - - - - From 3200eebbf93b1e1af6dae244472901a26a7a2ff0 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 11:25:38 +0000 Subject: [PATCH 22/30] Update filter checkboxes --- .../_includes/filter-panel/completeStatus.njk | 11 +++++---- .../_includes/filter-panel/course-level.njk | 9 ++++---- app/views/_includes/filter-panel/phase.njk | 11 +++++---- .../filter-panel/signed-in-providers.njk | 12 ++++------ app/views/_includes/filter-panel/source.njk | 17 ++++++-------- app/views/_includes/filter-panel/statuses.njk | 23 ++++++++++--------- .../_includes/filter-panel/studyMode.njk | 4 ++-- .../filter-panel/training-routes.njk | 10 ++++---- .../filter-panel/training-status.njk | 20 ++++++++-------- app/views/_includes/filter-panel/years.njk | 18 +++++---------- 10 files changed, 62 insertions(+), 73 deletions(-) diff --git a/app/views/_includes/filter-panel/completeStatus.njk b/app/views/_includes/filter-panel/completeStatus.njk index edb487681..c21f62001 100644 --- a/app/views/_includes/filter-panel/completeStatus.njk +++ b/app/views/_includes/filter-panel/completeStatus.njk @@ -1,17 +1,17 @@ {% set complete = { text: "Complete", - checked: checked(query.filterCompleteStatus, "Complete") + checked: true if query.filterCompleteStatus == "Complete" } %} {% set hasProblems = { text: "Has problems", value: "Has problems", - checked: checked(query.filterCompleteStatus, "Has problems") + checked: true if query.filterCompleteStatus == "Has problems" } %} {% set incomplete = { text: "Incomplete", - checked: checked(query.filterCompleteStatus, "Incomplete") + checked: true if query.filterCompleteStatus == "Incomplete" } %} {% set draftItems = [complete, incomplete, hasProblems] %} @@ -31,5 +31,6 @@ classes: "govuk-fieldset__legend--s" } }, - items: draftItems if navActive == "drafts" else recordItems -} | decorateAttributes(data, "data.filterCompleteStatus"))}} + items: draftItems if navActive == "drafts" else recordItems, + values: data.filterCompleteStatus +}) }} diff --git a/app/views/_includes/filter-panel/course-level.njk b/app/views/_includes/filter-panel/course-level.njk index 019ae1013..9952c9a9a 100644 --- a/app/views/_includes/filter-panel/course-level.njk +++ b/app/views/_includes/filter-panel/course-level.njk @@ -10,11 +10,12 @@ items: [ { text: "Undergraduate", - checked: checked(query.filterCourseLevel, "Undergraduate") + value: "Undergraduate" }, { text: "Postgraduate", - checked: checked(query.filterCourseLevel, "Postgraduate") + value: "Postgraduate" } - ] -} | decorateAttributes(data, "data.filterCourseLevel"))}} + ], + values: data.filterCourseLevel +}) }} diff --git a/app/views/_includes/filter-panel/phase.njk b/app/views/_includes/filter-panel/phase.njk index 3fc883ed3..fc427b963 100644 --- a/app/views/_includes/filter-panel/phase.njk +++ b/app/views/_includes/filter-panel/phase.njk @@ -10,15 +10,16 @@ items: [ { text: "Early years", - checked: checked(query.filterPhase, "Early years") + value: "Early years" }, { text: "Primary", - checked: checked(query.filterPhase, "Primary") + value: "Primary" }, { text: "Secondary", - checked: checked(query.filterPhase, "Secondary") + value: "Secondary" } - ] -} | decorateAttributes(data, "data.filterPhase"))}} + ], + values: data.filterPhase +}) }} diff --git a/app/views/_includes/filter-panel/signed-in-providers.njk b/app/views/_includes/filter-panel/signed-in-providers.njk index db452d169..4e1f8f091 100644 --- a/app/views/_includes/filter-panel/signed-in-providers.njk +++ b/app/views/_includes/filter-panel/signed-in-providers.njk @@ -7,8 +7,8 @@ {% set providerItems = [] %} {% for provider in userProviders %} {% set providerItems = providerItems | push({ - text: provider, - checked: checked(query.filterUserProviders, provider) + text: provider + value: provider }) %} {% endfor %} {{ govukCheckboxes({ @@ -19,10 +19,8 @@ classes: "govuk-fieldset__legend--s" } }, - hint: { - text: "" - }, - items: providerItems - } | decorateAttributes(data, "data.filterUserProviders")) }} + items: providerItems, + values: data.filterUserProviders + }) }} {% endif %} diff --git a/app/views/_includes/filter-panel/source.njk b/app/views/_includes/filter-panel/source.njk index a2537082b..dea5a3b9c 100644 --- a/app/views/_includes/filter-panel/source.njk +++ b/app/views/_includes/filter-panel/source.njk @@ -10,23 +10,20 @@ items: [ { text: "Added manually", - value: "Manual", - checked: checked(query.filterSource, "Manual") + value: "Manual" }, { text: "Imported from Apply", - value: "Apply", - checked: checked(query.filterSource, "Apply") + value: "Apply" }, { text: "Imported from DTTP", - value: "DTTP", - checked: checked(query.filterSource, "DTTP") + value: "DTTP" } if navActive !== "drafts", { text: "Imported from HESA", - value: "HESA", - checked: checked(query.filterSource, "HESA") + value: "HESA" } if navActive !== "drafts" - ] -} | decorateAttributes(data, "data.filterSource"))}} + ], + values: data.filterSource +}) }} diff --git a/app/views/_includes/filter-panel/statuses.njk b/app/views/_includes/filter-panel/statuses.njk index 9383038d3..29b0f6eb9 100644 --- a/app/views/_includes/filter-panel/statuses.njk +++ b/app/views/_includes/filter-panel/statuses.njk @@ -14,39 +14,40 @@ items: [ { text: "Draft", - checked: checked(query.filterStatus, "Draft") + value: "Draft" } if false, { text: "Pending TRN", - checked: checked(query.filterStatus, "Pending TRN") + value: "Pending TRN" }, { text: "TRN received", - checked: checked(query.filterStatus, "TRN received") + value: "TRN received" }, { text: "EYTS recommended", - checked: checked(query.filterStatus, "EYTS recommended") + value: "EYTS recommended" }, { text: "EYTS awarded", - checked: checked(query.filterStatus, "EYTS awarded") + value: "EYTS awarded" }, { text: "QTS recommended", - checked: checked(query.filterStatus, "QTS recommended") + value: "QTS recommended" }, { text: "QTS awarded", - checked: checked(query.filterStatus, "QTS awarded") + value: "QTS awarded" }, { text: "Deferred", - checked: checked(query.filterStatus, "Deferred") + value: "Deferred" }, { text: "Withdrawn", - checked: checked(query.filterStatus, "Withdrawn") + value: "Withdrawn" } - ] -} | decorateAttributes(data, "data.filterStatus"))}} + ], + values: data.filterStatus +}) }} diff --git a/app/views/_includes/filter-panel/studyMode.njk b/app/views/_includes/filter-panel/studyMode.njk index 95264193d..729b1e044 100644 --- a/app/views/_includes/filter-panel/studyMode.njk +++ b/app/views/_includes/filter-panel/studyMode.njk @@ -10,11 +10,11 @@ items: [ { text: "Full time", - checked: checked(query.filterStudyMode, "Full time") + checked: true if query.filterStudyMode == "Full time" }, { text: "Part time", - checked: checked(query.filterStudyMode, "Part time") + checked: true if query.filterStudyMode == "Part time" } ] } | decorateAttributes(data, "data.filterStudyMode"))}} diff --git a/app/views/_includes/filter-panel/training-routes.njk b/app/views/_includes/filter-panel/training-routes.njk index cbc22ce89..1efe5b011 100644 --- a/app/views/_includes/filter-panel/training-routes.njk +++ b/app/views/_includes/filter-panel/training-routes.njk @@ -7,7 +7,7 @@ {% for route in enabledTrainingRoutes %} {% set routeItems = routeItems | push({ text: route, - checked: checked(query.filterTrainingRoutes, route) + checked: true if query.filterTrainingRoutes == route }) %} {% endfor %} {{ govukCheckboxes({ @@ -18,10 +18,8 @@ classes: "govuk-fieldset__legend--s" } }, - hint: { - text: "" - }, - items: routeItems - } | decorateAttributes(data, "data.filterTrainingRoutes")) }} + items: routeItems, + values: data.filterTrainingRoutes + }) }} {% endif %} diff --git a/app/views/_includes/filter-panel/training-status.njk b/app/views/_includes/filter-panel/training-status.njk index bcfe79a65..a3a237371 100644 --- a/app/views/_includes/filter-panel/training-status.njk +++ b/app/views/_includes/filter-panel/training-status.njk @@ -10,13 +10,11 @@ {% set coursenNotYetStarted = registeredTrainees | filterByFunction('ittInTheFuture') %} - {{ govukCheckboxes({ classes: "govuk-checkboxes--small js-auto-submit", attributes: { "id": 'filterTrainingStatusCheckboxes' - } - , + }, fieldset: { legend: { text: "Training status", @@ -27,24 +25,24 @@ items: [ { text: "Course not started yet", - checked: checked(query.filterTrainingStatus, "Course not started yet") + value: "Course not started yet" } if coursenNotYetStarted | length, { text: "Actively training", - _checked: checkedStatus, - checked: checked(query.filterTrainingStatus, "Actively training") + value: "Actively training" }, { text: "Deferred", - checked: checked(query.filterTrainingStatus, "Deferred") + value: "Deferred" }, { text: "Awarded", - checked: checked(query.filterTrainingStatus, "Awarded") + value: "Awarded" }, { text: "Withdrawn", - checked: checked(query.filterTrainingStatus, "Withdrawn") + value: "Withdrawn" } - ] -} | decorateAttributes(data, "data.filterTrainingStatus"))}} + ], + values: data.filterTrainingStatus +}) }} diff --git a/app/views/_includes/filter-panel/years.njk b/app/views/_includes/filter-panel/years.njk index c8905053b..bf117470a 100644 --- a/app/views/_includes/filter-panel/years.njk +++ b/app/views/_includes/filter-panel/years.njk @@ -155,25 +155,20 @@ }, items: [ { - text: "2023 to 2024 academic year only", text: "2023 to 2024", - value: "2023 to 2024", - checked: checked(query.filterAcademicYears, "2023 to 2024") + value: "2023 to 2024" }, { - text: "2022 to 2023 academic year only", text: "2022 to 2023", - value: "2022 to 2023", - checked: checked(query.filterAcademicYears, "2022 to 2023") + value: "2022 to 2023" }, { - text: "2021 to 2022 academic year only", text: "2021 to 2022", - value: "2021 to 2022", - checked: checked(query.filterAcademicYears, "2021 to 2022") + value: "2021 to 2022" } - ] - } | decorateAttributes(data, "data.filterAcademicYears"))}} + ], + values: data.filterAcademicYears + }) }} {% endset %} @@ -210,4 +205,3 @@ }) }} {% endif %} - From a421be4586f74024bbbbbbe1f83208684ae23a13 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 11:43:22 +0000 Subject: [PATCH 23/30] Temporary fix to prevent lint errors --- app/filters/permissions.js | 8 ++++---- app/functions.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/filters/permissions.js b/app/filters/permissions.js index 6f508daf5..17a778592 100644 --- a/app/filters/permissions.js +++ b/app/filters/permissions.js @@ -19,7 +19,7 @@ filters.getHighestLevel = array => { // Returns an array of the access levels of each signed-in providers // This is mostly an internal utility function // eg ['accreditingProvider', 'leadPartner', 'admin'] -filters.getAccessLevels = function (providers, data) { +filters.getAccessLevels = (providers, data) => { data = data || this?.ctx?.data || false // Loop through each signed-in provider and get their type @@ -29,7 +29,7 @@ filters.getAccessLevels = function (providers, data) { } // Get the highest level of access the signed-in providers have -filters.getAccessLevel = function (providers, data) { +filters.getAccessLevel = (providers, data) => { data = data || this?.ctx?.data || false // Get all access levels const accessLevels = filters.getAccessLevels.apply(this, [providers, data]) @@ -39,7 +39,7 @@ filters.getAccessLevel = function (providers, data) { // Check if a provider (or providers) have auth to do an action // Usually this will be called via the `isAuthorised(action)` function -filters.providerIsAuthorised = function (providers, action) { +filters.providerIsAuthorised = (providers, action) => { const data = this?.ctx?.data || false const record = data?.record || false @@ -96,7 +96,7 @@ filters.providerIsAuthorised = function (providers, action) { // Returns an array of the access levels of each signed-in provider // eg ['accreditingProvider', 'leadPartner', 'admin'] -filters.getRecordAccessLevels = function (record, data = false) { +filters.getRecordAccessLevels = (record, data = false) => { data = data || this?.ctx?.data || false const signedInProviders = data.signedInProviders diff --git a/app/functions.js b/app/functions.js index 77c446177..815a829c0 100755 --- a/app/functions.js +++ b/app/functions.js @@ -3,7 +3,7 @@ // const individualFunctionsFolder = path.join(__dirname, './functions') const moment = require('moment') const _ = require('lodash') -const permissions = require('./filters/permissions.js').filters +// const permissions = require('./filters/permissions.js').filters const { fakerEN_GB: faker } = require('@faker-js/faker') faker.seed(123) @@ -69,7 +69,7 @@ exports.getContext = () => { // functions.faker = faker // Pass through to utility function. Done like this so we don't need to use filter syntax - as nothing really needs to get sent anyway -exports.isAuthorised = (data, action) => { +exports.isAuthorised = (action) => { // const data = this.ctx?.data // return permissions.providerIsAuthorised.apply(this, [data.signedInProviders, action]) return true From 47fa310e0339b3585d80e2baa29f6e8bed12f870 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 12:41:52 +0000 Subject: [PATCH 24/30] Fix data error --- app/views/_includes/filter-panel/signed-in-providers.njk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/_includes/filter-panel/signed-in-providers.njk b/app/views/_includes/filter-panel/signed-in-providers.njk index 4e1f8f091..2bc36f372 100644 --- a/app/views/_includes/filter-panel/signed-in-providers.njk +++ b/app/views/_includes/filter-panel/signed-in-providers.njk @@ -7,7 +7,7 @@ {% set providerItems = [] %} {% for provider in userProviders %} {% set providerItems = providerItems | push({ - text: provider + text: provider, value: provider }) %} {% endfor %} From c868fd9105f5ed3da62745c56a5263cd35d1fe80 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 12:42:12 +0000 Subject: [PATCH 25/30] Remove unnecessary MOJ component calls --- app/views/_layouts/main.njk | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/views/_layouts/main.njk b/app/views/_layouts/main.njk index 04e131694..35bef627f 100755 --- a/app/views/_layouts/main.njk +++ b/app/views/_layouts/main.njk @@ -8,19 +8,12 @@ {% from "_components/autocomplete/macro.njk" import appAutocomplete %} {% from "_components/banner/macro.njk" import appBanner %} {% from "_components/download-link-with-filename/macro.njk" import appDownloadLink %} +{% from "_components/filter-page/macro.njk" import appFilterPage %} {% from "_components/footer/macro.njk" import appFooter %} {% from "_components/school-autocomplete/macro.njk" import appSchoolAutocomplete with context %} {% from "_components/sub-navigation/macro.njk" import appSubNavigation %} {% from "_components/timeline/macro.njk" import appTimeline %} -{# Wrapper of moj’s filter stuff #} -{% from "_components/filter-page/macro.njk" import appFilterPage %} - -{# moj components #} -{%- from "moj/components/button-menu/macro.njk" import mojButtonMenu -%} -{%- from "moj/components/filter/macro.njk" import mojFilter -%} -{%- from "moj/components/primary-navigation/macro.njk" import mojPrimaryNavigation -%} - {% block head %} {% include "_includes/head.njk" %} {% endblock %} From cad846d8fc8e2fbc02bb756e6d79b4a0de319a81 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Mon, 16 Dec 2024 12:44:16 +0000 Subject: [PATCH 26/30] Fix sass lint errors --- app/assets/sass/application.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/sass/application.scss b/app/assets/sass/application.scss index 44ddc40c1..022336adb 100755 --- a/app/assets/sass/application.scss +++ b/app/assets/sass/application.scss @@ -1,9 +1,7 @@ @use "sass:string"; -// // For guidance on how to add CSS and SCSS see: // https://prototype-kit.service.gov.uk/docs/adding-css-javascript-and-images -// $govuk-new-typography-scale: true; // global styles for and

tags From 1cb15867473279b4e3e9a64d901836bed20074fb Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Wed, 18 Dec 2024 14:03:27 +0000 Subject: [PATCH 27/30] Delete gulp scripts --- gulp/clean.js | 16 -------------- gulp/config.json | 8 ------- gulp/copy-assets.js | 32 --------------------------- gulp/nodemon.js | 43 ------------------------------------- gulp/sass.js | 39 --------------------------------- gulp/school-search-index.js | 9 -------- gulp/watch.js | 20 ----------------- gulpfile.js | 41 ----------------------------------- 8 files changed, 208 deletions(-) delete mode 100755 gulp/clean.js delete mode 100755 gulp/config.json delete mode 100755 gulp/copy-assets.js delete mode 100755 gulp/nodemon.js delete mode 100755 gulp/sass.js delete mode 100644 gulp/school-search-index.js delete mode 100755 gulp/watch.js delete mode 100755 gulpfile.js diff --git a/gulp/clean.js b/gulp/clean.js deleted file mode 100755 index ba3e7cdf9..000000000 --- a/gulp/clean.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - clean.js - =========== - removes folders: - - public -*/ - -const { rimrafSync } = require('rimraf') -const gulp = require('gulp') - -const config = require('./config.json') - -gulp.task('clean', (done) => { - rimrafSync([config.paths.public, '.port.tmp']) - return done() -}) diff --git a/gulp/config.json b/gulp/config.json deleted file mode 100755 index d528fc4b5..000000000 --- a/gulp/config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "paths": { - "public": "public/", - "assets" : "app/assets/", - "nodeModules": "node_modules/", - "lib": "lib/" - } -} diff --git a/gulp/copy-assets.js b/gulp/copy-assets.js deleted file mode 100755 index f17028285..000000000 --- a/gulp/copy-assets.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - copy.js - =========== - copies images and javascript folders to public -*/ - -const gulp = require('gulp') - -const config = require('./config.json') - -gulp.task('copy-images', () => { - return gulp.src( - config.paths.assets + 'images/*.+(png|jpg|jpeg|gif|svg|ico)', - { encoding: false } - ) - .pipe(gulp.dest(config.paths.public + 'images/')) -}) - -gulp.task('copy-javascripts', () => { - return gulp.src(config.paths.assets + 'javascripts/*') - .pipe(gulp.dest(config.paths.public + 'javascripts/')) -}) - -gulp.task('copy-data', () => { - return gulp.src(config.paths.assets + 'data/**') - .pipe(gulp.dest(config.paths.public + 'data/')) -}) - -gulp.task('copy-downloads', () => { - return gulp.src(config.paths.assets + 'downloads/**') - .pipe(gulp.dest(config.paths.public + 'downloads/')) -}) diff --git a/gulp/nodemon.js b/gulp/nodemon.js deleted file mode 100755 index 956ba93ab..000000000 --- a/gulp/nodemon.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - nodemon.js - =========== - uses nodemon to run a server, watches for javascript and json changes -*/ - -const fs = require('fs') -const path = require('path') - -const gulp = require('gulp') -const colour = require('ansi-colors') -const log = require('fancy-log') -const nodemon = require('gulp-nodemon') - -const config = require('./config.json') - -// Warn about npm install on crash -const onCrash = () => { - log(colour.cyan('[nodemon] For missing modules try running `npm install`')) -} - -// Remove .port.tmp if it exists -const onQuit = () => { - try { - fs.unlinkSync(path.join(__dirname, '/../.port.tmp')) - } catch (e) {} - - process.exit(0) -} - -gulp.task('server', () => { - nodemon({ - watch: ['.env', '**/*.js', '**/*.json'], - script: 'listen-on-port.js', - ignore: [ - config.paths.public + '*', - config.paths.assets + '*', - config.paths.nodeModules + '*' - ] - }) - .on('crash', onCrash) - .on('quit', onQuit) -}) diff --git a/gulp/sass.js b/gulp/sass.js deleted file mode 100755 index 60166fdea..000000000 --- a/gulp/sass.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - sass.js - =========== - compiles sass from assets folder - also includes sourcemaps -*/ - -const gulp = require('gulp') -const sass = require('gulp-sass')(require('sass')) -const sourcemaps = require('gulp-sourcemaps') -const path = require('path') -const fs = require('fs') - -const extensions = require('../lib/extensions/extensions') -const config = require('./config.json') -const stylesheetDirectory = config.paths.public + 'stylesheets' - -gulp.task('sass-extensions', (done) => { - const fileContents = '$govuk-extensions-url-context: "/extension-assets"; ' + extensions.getFileSystemPaths('sass') - .map(filePath => `@import "${filePath.split(path.sep).join('/')}";`) - .join('\n') - fs.writeFile(path.join(config.paths.lib + 'extensions', '_extensions.scss'), fileContents, done) -}) - -gulp.task('sass', () => { - return gulp.src(config.paths.assets + '/sass/*.scss') - .pipe(sourcemaps.init()) - .pipe(sass({ outputStyle: 'expanded' }).on('error', (error) => { - // write a blank application.css to force browser refresh on error - if (!fs.existsSync(stylesheetDirectory)) { - fs.mkdirSync(stylesheetDirectory) - } - fs.writeFileSync(path.join(stylesheetDirectory, 'application.css'), '') - console.error('\n', error.messageFormatted, '\n') - this.emit('end') - })) - .pipe(sourcemaps.write()) - .pipe(gulp.dest(stylesheetDirectory)) -}) diff --git a/gulp/school-search-index.js b/gulp/school-search-index.js deleted file mode 100644 index 40bf5eb78..000000000 --- a/gulp/school-search-index.js +++ /dev/null @@ -1,9 +0,0 @@ -const gulp = require('gulp') -const lunr = require('./../scripts/generate-search-index') // generate - -gulp.task('school-search-index', (done) => { - // Generate search index - lunr() - - done() -}) diff --git a/gulp/watch.js b/gulp/watch.js deleted file mode 100755 index 727448a4b..000000000 --- a/gulp/watch.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - watch.js - =========== - watches sass/js/images -*/ - -const gulp = require('gulp') - -const config = require('./config.json') - -gulp.task('watch-sass', () => { - return gulp.watch(config.paths.assets + 'sass/**', { cwd: './' }, gulp.task('sass')) -}) - -gulp.task('watch-assets', () => { - return gulp.watch([ - config.paths.assets + 'images/**', - config.paths.assets + 'javascripts/**' - ], { cwd: './' }, gulp.task('copy-assets')) -}) diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100755 index b2aceb70c..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - gulpfile.js - =========== - Rather than manage one giant configuration file responsible - for creating multiple tasks, each task has been broken out into - its own file in `/gulp`. Any files in that directory - get automatically required below. - To add a new task, simply add a new task file that directory. -*/ - -const gulp = require('gulp') -const requireDir = require('require-dir') - -// Require all tasks in gulp/tasks, including subfolders -requireDir('./gulp', { recurse: true }) - -// gulp 4 requires dependency tasks to be defined before they are called. -// We'll keep our top-level tasks in this file so that they are defined at the end of the chain, after their dependencies. -gulp.task('generate-assets', gulp.series( - 'clean', - 'sass-extensions', - 'school-search-index', - gulp.parallel( - 'sass', - 'copy-images', - 'copy-javascripts', - 'copy-data', - 'copy-downloads' - ) -)) -gulp.task('watch', gulp.parallel( - 'watch-sass', - 'watch-assets' -)) -gulp.task('default', gulp.series( - 'generate-assets', - gulp.parallel( - 'watch', - 'server' - ) -)) From 0bc7b86bd6d897ea317fd5f4c87ecbd17f723e8a Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Wed, 18 Dec 2024 14:14:30 +0000 Subject: [PATCH 28/30] Remove gulp packages --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index 90fad8407..e65c9c2a2 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,6 @@ "csv-string": "^4.0.1", "govuk-frontend": "^5.5.0", "govuk-prototype-kit": "^13.16.2", - "gulp": "^5.0.0", - "gulp-nodemon": "^2.5.0", - "gulp-sass": "^5.0.0", - "gulp-sourcemaps": "^3.0.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", "lunr": "^2.3.9", From 2c1a924940b3bad31f24701a1c39dc7b69951706 Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 19 Dec 2024 14:24:39 +0000 Subject: [PATCH 29/30] Merge service navigation --- app/assets/sass/application.scss | 1 - .../overrides/_moj-primary-navigation.scss | 23 --------- app/views/_layouts/main.njk | 51 +++++-------------- app/views/organisations/organisation.njk | 2 +- app/views/organisations/token-details.njk | 2 +- app/views/organisations/token-generated.njk | 2 +- app/views/organisations/token-manage.njk | 2 +- app/views/support/organisations/view.njk | 2 +- 8 files changed, 17 insertions(+), 68 deletions(-) delete mode 100644 app/assets/sass/overrides/_moj-primary-navigation.scss diff --git a/app/assets/sass/application.scss b/app/assets/sass/application.scss index 022336adb..27f639b31 100755 --- a/app/assets/sass/application.scss +++ b/app/assets/sass/application.scss @@ -57,7 +57,6 @@ $app-assets-path: "/public/"; @import "overrides/govuk-tag"; @import "overrides/moj-button-menu"; @import "overrides/moj-filter"; -@import "overrides/moj-primary-navigation"; @import "overrides/status-grid"; @import "overrides/tabs"; diff --git a/app/assets/sass/overrides/_moj-primary-navigation.scss b/app/assets/sass/overrides/_moj-primary-navigation.scss deleted file mode 100644 index b5f045007..000000000 --- a/app/assets/sass/overrides/_moj-primary-navigation.scss +++ /dev/null @@ -1,23 +0,0 @@ -//// -/// @group helpers/moj-primary-navigation -//// - -.moj-primary-navigation.app-primary-navigation--support-ui { - background: govuk-tint(govuk-colour("purple"), 90); - - & *:link, *:visited { - color: govuk-colour("purple"); - } - - & *:hover { - color: govuk-tint(govuk-colour("purple"), 15); - } - - .moj-primary-navigation__link[aria-current]:before { - background-color: govuk-colour("purple"); - } - - .moj-primary-navigation { - background: govuk-tint(govuk-colour("purple"), 90); - } -} diff --git a/app/views/_layouts/main.njk b/app/views/_layouts/main.njk index 35bef627f..106571c92 100755 --- a/app/views/_layouts/main.njk +++ b/app/views/_layouts/main.njk @@ -43,13 +43,9 @@ containerClasses: "govuk-width-container", navigation: [ { - href: "#1", + href: "#", text: "Your account" - } if false, - { - href: "/select-organisation", - text: "Change organisation" - } if shouldShowOrganisationSwitchLink and false, + }, { href: "/start-page", text: "Sign out" @@ -61,27 +57,6 @@ {% include "_includes/organisation-switcher.njk" %} {% endif %} - {% set rightHandSideNavHtml %} - - {% if not data.isAdmin and not data.isImpersonating | falsify and not isSupportUi %} - {% set providerName = data.settings.userActiveProvider %} - -

- {% endif %} - - {% endset %} - {% set navItemsUser = [ { text: 'Home', @@ -127,7 +102,12 @@ text: 'Funding', href: '/funding/' + data.years.currentAcademicYearSimple + '/payment-schedule', active: true if navActive == 'Funding' - } if data.settings.showFundingInPrimaryNav + } if data.settings.showFundingInPrimaryNav, + { + text: 'Organisation details', + href: '/organisations/' + activeProvider.id, + active: true if navActive == 'provider' + } if not data.isAdmin and not data.isImpersonating | falsify and not isSupportUi ] | removeEmpty %} {% set navItemsSupport = [ @@ -192,11 +172,9 @@ {% if not hidePrimaryNav %} - {{ mojPrimaryNavigation({ - label: 'Primary navigation', - items: navItems, - containerClasses: "app-primary-navigation--support-ui" if isSupportUi, - searchHtml: rightHandSideNavHtml + {{ govukServiceNavigation({ + ariaLabel: "Primary navigation", + navigation: navItems }) }} {% endif %} @@ -210,7 +188,7 @@ "role": "status" }, tag: { - text: "prototype" + text: "Prototype" }, classes: 'govuk-!-margin-top-1', html: 'This is a prototype of a new service – your feedback will help us improve it' @@ -232,20 +210,16 @@

Get support

- - {% endset %} @@ -278,7 +252,6 @@ {% endblock %} {% endif %} - {% block bodyEnd %} {% block scripts %} {% include "_includes/scripts.njk" %} diff --git a/app/views/organisations/organisation.njk b/app/views/organisations/organisation.njk index 97100a772..d2892b086 100644 --- a/app/views/organisations/organisation.njk +++ b/app/views/organisations/organisation.njk @@ -4,7 +4,7 @@ Organisation – {{ provider.name }} {% endset %} -{# {% set navActive = "provider" %} #} +{% set navActive = "provider" %} {% set backText = "Home" %} {% set backLink = '/home' %} diff --git a/app/views/organisations/token-details.njk b/app/views/organisations/token-details.njk index b1c16d9a5..45ad376b9 100644 --- a/app/views/organisations/token-details.njk +++ b/app/views/organisations/token-details.njk @@ -4,7 +4,7 @@ Organisation – {{ provider.name }} {% endset %} -{# {% set navActive = "provider" %} #} +{% set navActive = "provider" %} {% set backText = "Back" %} {% set backLink = 'token-manage' %} diff --git a/app/views/organisations/token-generated.njk b/app/views/organisations/token-generated.njk index f6b4c9843..607f9e9d2 100644 --- a/app/views/organisations/token-generated.njk +++ b/app/views/organisations/token-generated.njk @@ -4,7 +4,7 @@ Organisation – {{ provider.name }} {% endset %} -{# {% set navActive = "provider" %} #} +{% set navActive = "provider" %} {% set backText = "Back" %} {% set backLink = 'token-details' %} diff --git a/app/views/organisations/token-manage.njk b/app/views/organisations/token-manage.njk index b2fabd2e0..b4f285293 100644 --- a/app/views/organisations/token-manage.njk +++ b/app/views/organisations/token-manage.njk @@ -4,7 +4,7 @@ Organisation – {{ provider.name }} {% endset %} -{# {% set navActive = "provider" %} #} +{% set navActive = "provider" %} {% set backText = "Back" %} {% set backLink = 'organisation' %} diff --git a/app/views/support/organisations/view.njk b/app/views/support/organisations/view.njk index 6b01c35b8..2e5c4040c 100644 --- a/app/views/support/organisations/view.njk +++ b/app/views/support/organisations/view.njk @@ -4,7 +4,7 @@ Organisation – {{ provider.name }} {% endset %} -{# {% set navActive = "provider" %} #} +{% set navActive = "provider" %} {% set backText = "Back" %} {# {% set backLink = '/home' %} #} From b6650874e59002426b4725329a73996c5da8c9ad Mon Sep 17 00:00:00 2001 From: Simon Whatley Date: Thu, 19 Dec 2024 14:26:31 +0000 Subject: [PATCH 30/30] Update govuk-frontend --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e65c9c2a2..83e6c3042 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@ministryofjustice/frontend": "^3.2.0", "connect-flash": "^0.1.1", "csv-string": "^4.0.1", - "govuk-frontend": "^5.5.0", + "govuk-frontend": "^5.7.1", "govuk-prototype-kit": "^13.16.2", "js-yaml": "^4.1.0", "lodash": "^4.17.21",