page-path-utils.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import nodePath from 'path';
  2. import escapeStringRegexp from 'escape-string-regexp';
  3. /**
  4. * Whether path is the top page
  5. * @param path
  6. */
  7. export const isTopPage = (path: string): boolean => {
  8. return path === '/';
  9. };
  10. /**
  11. * Whether path belongs to the trash page
  12. * @param path
  13. */
  14. export const isTrashPage = (path: string): boolean => {
  15. // https://regex101.com/r/BSDdRr/1
  16. if (path.match(/^\/trash(\/.*)?$/)) {
  17. return true;
  18. }
  19. return false;
  20. };
  21. /**
  22. * Whether path belongs to the user page
  23. * @param path
  24. */
  25. export const isUserPage = (path: string): boolean => {
  26. // https://regex101.com/r/SxPejV/1
  27. if (path.match(/^\/user(\/.*)?$/)) {
  28. return true;
  29. }
  30. return false;
  31. };
  32. /**
  33. * Whether path belongs to the shared page
  34. * @param path
  35. */
  36. export const isSharedPage = (path: string): boolean => {
  37. // https://regex101.com/r/ZjdOiB/1
  38. if (path.match(/^\/share(\/.*)?$/)) {
  39. return true;
  40. }
  41. return false;
  42. };
  43. const restrictedPatternsToDelete: Array<RegExp> = [
  44. /^\/user\/[^/]+$/, // user page
  45. ];
  46. export const isDeletablePage = (path: string): boolean => {
  47. return !restrictedPatternsToDelete.some(pattern => path.match(pattern));
  48. };
  49. const restrictedPatternsToCreate: Array<RegExp> = [
  50. /\^|\$|\*|\+|#|%|\?/,
  51. /^\/-\/.*/,
  52. /^\/_r\/.*/,
  53. /^\/_apix?(\/.*)?/,
  54. /^\/?https?:\/\/.+$/, // avoid miss in renaming
  55. /\/{2,}/, // avoid miss in renaming
  56. /\s+\/\s+/, // avoid miss in renaming
  57. /.+\/edit$/,
  58. /.+\.md$/,
  59. /^(\.\.)$/, // see: https://github.com/weseek/growi/issues/3582
  60. /(\/\.\.)\/?/, // see: https://github.com/weseek/growi/issues/3582
  61. /^\/(installer|register|login|logout|admin|me|files|trash|paste|comments|tags|share)(\/.*|$)/,
  62. ];
  63. export const isCreatablePage = (path: string): boolean => {
  64. return !restrictedPatternsToCreate.some(pattern => path.match(pattern));
  65. };
  66. /**
  67. * return user path
  68. * @param user
  69. */
  70. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  71. export const userPageRoot = (user: any): string => {
  72. if (!user || !user.username) {
  73. return '';
  74. }
  75. return `/user/${user.username}`;
  76. };
  77. /**
  78. * return user path
  79. * @param parentPath
  80. * @param childPath
  81. * @param newPath
  82. */
  83. export const convertToNewAffiliationPath = (oldPath: string, newPath: string, childPath: string): string => {
  84. if (newPath === null) {
  85. throw new Error('Please input the new page path');
  86. }
  87. const pathRegExp = new RegExp(`^${escapeStringRegexp(oldPath)}`, 'i');
  88. return childPath.replace(pathRegExp, newPath);
  89. };
  90. /**
  91. * Encode SPACE and IDEOGRAPHIC SPACE
  92. * @param {string} path
  93. * @returns {string}
  94. */
  95. export const encodeSpaces = (path?:string): string | undefined => {
  96. if (path == null) {
  97. return undefined;
  98. }
  99. // Encode SPACE and IDEOGRAPHIC SPACE
  100. return path.replace(/ /g, '%20').replace(/\u3000/g, '%E3%80%80');
  101. };
  102. /**
  103. * Generate editor path
  104. * @param {string} paths
  105. * @returns {string}
  106. */
  107. export const generateEditorPath = (...paths: string[]): string => {
  108. const joinedPath = [...paths].join('/');
  109. if (!isCreatablePage(joinedPath)) {
  110. throw new Error('Invalid characters on path');
  111. }
  112. try {
  113. const url = new URL(joinedPath, 'https://dummy');
  114. return `${url.pathname}#edit`;
  115. }
  116. catch (err) {
  117. throw new Error('Invalid path format');
  118. }
  119. };
  120. /**
  121. * returns ancestors paths
  122. * @param {string} path
  123. * @param {string[]} ancestorPaths
  124. * @returns {string[]}
  125. */
  126. export const collectAncestorPaths = (path: string, ancestorPaths: string[] = []): string[] => {
  127. if (isTopPage(path)) return ancestorPaths;
  128. const parentPath = nodePath.dirname(path);
  129. ancestorPaths.push(parentPath);
  130. return collectAncestorPaths(parentPath, ancestorPaths);
  131. };