diff --git a/docs/apis.md b/docs/apis.md index bd36a9fa20..a4e9789b54 100644 --- a/docs/apis.md +++ b/docs/apis.md @@ -250,5 +250,5 @@ The [[Question API]] (which can be divided into the Question bank API and the Qu - [[Plugins]] - plugin types also have their own APIs - [[Callbacks]] - list of all callbacks in Moodle -- [[Coding style]] - general information about writing PHP code for Moodle +- [Coding style](/general/development/policies/codingstyle) - general information about writing PHP code for Moodle - [[Session locks]] diff --git a/docs/guides/moodleapp/coding-style.md b/docs/guides/moodleapp/coding-style.md index 34698c4451..2f8b4e9f4c 100644 --- a/docs/guides/moodleapp/coding-style.md +++ b/docs/guides/moodleapp/coding-style.md @@ -3,7 +3,7 @@ title: Moodle App Coding Style sidebar_position: 2 --- -This document outlines the exceptions to the [[Coding style]] and [[JavaScript Coding Style]] which apply to the Moodle App and also includes rules for other technologies that are used in the app, like Typescript and Angular. +This document outlines the exceptions to the [Coding style](/general/development/policies/codingstyle) and [[JavaScript Coding Style]] which apply to the Moodle App and also includes rules for other technologies that are used in the app, like Typescript and Angular. Unless otherwise specified, developers should follow the indications included on those documents. diff --git a/general/development/process.md b/general/development/process.md index ca6cf97130..b0d18dadc1 100644 --- a/general/development/process.md +++ b/general/development/process.md @@ -199,7 +199,7 @@ Bug fixes, and minor features or enhancements should go through the following pr ### Make sure there is a tracker issue -Every change must have an issue in the tracker. If you are fixing a bug, there is probably one there already, but if not, create one. [[Tracker tips|Tips for searching tracker]]. +Every change must have an issue in the tracker. If you are fixing a bug, there is probably one there already, but if not, create one. [Tips for searching tracker](./tracker/tips/index.md). ### Decide which branches the fix is required on @@ -253,7 +253,7 @@ Issues identified as [[Security|security issues]] are resolved in a slightly dif - If a developer has shared a solution as Git branches via Github, they should be asked to provide the solutions as [[How_to_create_a_patch|stand-alone patches]] attached to the issue and to [[#How to remove a branch from Github|remove the solution from Github]]. - contain details about the security problem in the commit message. - Instead use generic terms like, "improve", "better handling of" -- The solution will not be integrated until the week before a [[Process#Stable_maintenance_cycles|minor release]] following the normal [[Release process|Release process]]. In short, the issue will be incorporated into the integration version, rebased, tested and made ready for release as a normal issue would, but not until as late as possible. +- The solution will not be integrated until the week before a [minor release](#Stable-maintenance-cycles) following the normal [[Release process|Release process]]. In short, the issue will be incorporated into the integration version, rebased, tested and made ready for release as a normal issue would, but not until as late as possible. - Details of security issues will be shared with registered admins with the minor release. - Details of security issues will not be publicly announced until one week after a minor release to allow admins to update. Note that not all the labelled (minor) security issues are always handled following the procedure above. It's possible that, after discussion, it's decided a given issue is not a real Moodle security problem (say external disclosures/potential attacks using Moodle as vector, not as target, discussions revealing some private details). Those issues will be processed as normal issues, generating the needed user documentation if necessary and will be part of the habitual weekly releases. diff --git a/migratedPages.js b/migratedPages.js new file mode 100644 index 0000000000..b5f1792539 --- /dev/null +++ b/migratedPages.js @@ -0,0 +1,98 @@ +const path = require('path'); + +const obsoleteDocs = [ + "Setting_up_Eclipse", + "Setting_up_Netbeans", +]; + +/** + * A list of documents which have been migrated with their source and destination paths shown. + */ +const migratedDocs = { + "Access_API": "/docs/apis/access.md", + "Coding_style": "/general/development/policies/codingstyle/index.md", + "Communication_between_components": "/general/development/policies/component-communication/index.md", + "Developer_meeting_February_2022": "/general/community/meetings/202202.md", + "Developer_meetings": "/general/community/meetings.md", + "Integration_review": "/general/development/process/integration-review.md", + "Mission": "/general/community/mission.md", + "Moodle_research": "/general/community/research.md", + "Overview": "/general/community/intro.md", + "Peer_reviewing": "/general/development/process/peer-review.md", + "Process": "/general/development/process.md", + "Roadmap": "/general/community/roadmap.md", + "Tracker_intro": "/general/development/tracker.md", + "Tracker_tips": "/general/development/tracker/tips.md", +}; + +const isObsolete = (legacyPath) => obsoleteDocs.indexOf(legacyPath) !== -1; + +/** + * Whether the specified path has been migrated. + * + * @returns {bool} + */ +const isMigrated = (legacyPath) => (typeof migratedDocs[legacyPath] !== 'undefined'); + +/** + * Get the path to the new doc from a legacy doc path. + * + * @param legacyPath {string} + * @returns {string} + */ +const getMigratedDoc = legacyPath => { + if (!isMigrated) { + return null; + } + + const filename = migratedDocs[legacyPath]; + + if (filename.startsWith('/')) { + return filename.substr(1); + } + + return filename; +}; + +/** + * Get the path to the new doc relative to the file it was in. + * + * This has to consider whether the file is in the same docs instance or not due to versioning. + * + * @param {strin} legacyPath + * @param {string} usedIn + * @returns {string} + */ +const getMigrationLink = (legacyPath, usedIn) => { + const replacementFile = getMigratedDoc(legacyPath); + if (!replacementFile) { + return null; + } + + const relativeUsedIn = path.relative(process.env.PWD, usedIn); + + const replacementIsGeneral = replacementFile.startsWith('general/'); + const usedInIsGeneral = relativeUsedIn.startsWith('general/'); + const bothGeneral = replacementIsGeneral && usedInIsGeneral; + const neitherGeneral = !replacementIsGeneral && !usedInIsGeneral; + + if (bothGeneral || neitherGeneral) { + return path.relative(replacementFile, relativeUsedIn); + } + + if (replacementFile.endsWith('index.md')) { + return `/${replacementFile.replace(/\/index\.md$/, '')}`; + } + + if (replacementFile.endsWith('.md')) { + return `/${replacementFile.replace(/\.md$/, '')}`; + } + + return `/${replacementFile}`; +}; + +module.exports = { + isMigrated, + isObsolete, + getMigrationLink, +}; diff --git a/src/remark/legacyDocLinks.js b/src/remark/legacyDocLinks.js index 1067963872..63238870f8 100644 --- a/src/remark/legacyDocLinks.js +++ b/src/remark/legacyDocLinks.js @@ -1,18 +1,51 @@ const visit = require('unist-util-visit'); +const {isObsolete, isMigrated, getMigrationLink} = require('../../migratedPages'); const plugin = (options) => { - const transformer = async (ast) => { - visit(ast, 'linkReference', updateLink); + const transformer = async (ast, vfile) => { + visit(ast, 'linkReference', updateLink(vfile)); }; return transformer; }; -const getLinkFromString = string => { +const getLinkFromString = (vfile, string) => { let [linkComponent] = string.split('|'); // Links never have spaces in them. linkComponent = linkComponent.replaceAll(' ', '_'); + // Split on the bookmark (if present). + let [pageComponent, bookmarkComponent] = linkComponent.split('#'); + + if (pageComponent) { + if (isMigrated(pageComponent)) { + if (bookmarkComponent) { + bookmarkComponent = `#${bookmarkComponent.replaceAll('_', '-')}`; + } else { + bookmarkComponent = ''; + } + const migrationLink = getMigrationLink(pageComponent, vfile.path); + const replacement = `[${getDescriptionFromString(string)}](${migrationLink}${bookmarkComponent})`; + + let message = `---\n`; + message += `- Use of legacy docs link found for migrated doc\n`; + message += `- File: \t ${vfile.path}\n`; + message += `- Found: \t [[${string}]]\n`; + message += `- Replacement: \t ${replacement}\n`; + message += `---\n`; + console.warn(message); + + return migrationLink + bookmarkComponent; + } else if (isObsolete(pageComponent)) { + let message = `---\n`; + message += `- Use of obsoleted legacy doc link found for migrated doc\n`; + message += `- File: \t ${vfile.path}\n`; + message += `- Found: \t [[${string}]]\n`; + message += `---\n`; + console.warn(message); + } + } + if (linkComponent.substring(0, 1) === '#') { // This is a relative link in the same page. // Update it to meet the correct format. @@ -53,7 +86,7 @@ const getDescriptionFromString = string => { * @param {Number} index * @param {Tree} parent */ -const updateLink = (node, index, parent) => { +const updateLink = (vfile) => (node, index, parent) => { if (parent.children[index - 1]?.type !== 'text') { return null; } @@ -72,7 +105,7 @@ const updateLink = (node, index, parent) => { const linkNode = { type: 'link', - url: getLinkFromString(node.label), + url: getLinkFromString(vfile, node.label), children: [{ type: 'text', value: getDescriptionFromString(node.label),