diff --git a/__tests__/shared/components/Contentful/SearchBar/SearchBar.jsx b/__tests__/shared/components/Contentful/SearchBar/SearchBar.jsx
deleted file mode 100644
index f4c032931f..0000000000
--- a/__tests__/shared/components/Contentful/SearchBar/SearchBar.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { SearchBarInner } from 'components/Contentful/SearchBar/SearchBar';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- selected: '',
- options: [],
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchBar/__snapshots__/SearchBar.jsx.snap b/__tests__/shared/components/Contentful/SearchBar/__snapshots__/SearchBar.jsx.snap
deleted file mode 100644
index 1772d9ce45..0000000000
--- a/__tests__/shared/components/Contentful/SearchBar/__snapshots__/SearchBar.jsx.snap
+++ /dev/null
@@ -1,53 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
-
-
-
-
-
-
-
-`;
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterAuthor/FilterAuthor.jsx b/__tests__/shared/components/Contentful/SearchPageFilter/FilterAuthor/FilterAuthor.jsx
deleted file mode 100644
index 5c40cfd351..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterAuthor/FilterAuthor.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { FilterAuthorInner } from 'components/Contentful/SearchPageFilter/FilterAuthor';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- selected: 'All authors',
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterAuthor/__snapshots__/FilterAuthor.jsx.snap b/__tests__/shared/components/Contentful/SearchPageFilter/FilterAuthor/__snapshots__/FilterAuthor.jsx.snap
deleted file mode 100644
index e8c4b0a310..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterAuthor/__snapshots__/FilterAuthor.jsx.snap
+++ /dev/null
@@ -1,25 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
- Author
-
-
-
-`;
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterDate/FilterDate.jsx b/__tests__/shared/components/Contentful/SearchPageFilter/FilterDate/FilterDate.jsx
deleted file mode 100644
index 37c02c2333..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterDate/FilterDate.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { FilterDateInner } from 'components/Contentful/SearchPageFilter/FilterDate';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterDate/__snapshots__/FilterDate.jsx.snap b/__tests__/shared/components/Contentful/SearchPageFilter/FilterDate/__snapshots__/FilterDate.jsx.snap
deleted file mode 100644
index caf83b4709..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterDate/__snapshots__/FilterDate.jsx.snap
+++ /dev/null
@@ -1,38 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
- From
-
-
-
-
- -
-
-
-
-`;
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterRadio/FilterRadio.jsx b/__tests__/shared/components/Contentful/SearchPageFilter/FilterRadio/FilterRadio.jsx
deleted file mode 100644
index 9985ed1b5d..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterRadio/FilterRadio.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { FilterRadioInner } from 'components/Contentful/SearchPageFilter/FilterRadio';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterRadio/__snapshots__/FilterRadio.jsx.snap b/__tests__/shared/components/Contentful/SearchPageFilter/FilterRadio/__snapshots__/FilterRadio.jsx.snap
deleted file mode 100644
index 2f9f770d7b..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterRadio/__snapshots__/FilterRadio.jsx.snap
+++ /dev/null
@@ -1,7 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-`;
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterSelection/FilterSelection.jsx b/__tests__/shared/components/Contentful/SearchPageFilter/FilterSelection/FilterSelection.jsx
deleted file mode 100644
index 9fa14b0fc4..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterSelection/FilterSelection.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { FilterSelectionInner } from 'components/Contentful/SearchPageFilter/FilterSelection';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterSelection/__snapshots__/FilterSelection.jsx.snap b/__tests__/shared/components/Contentful/SearchPageFilter/FilterSelection/__snapshots__/FilterSelection.jsx.snap
deleted file mode 100644
index 2f9f770d7b..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterSelection/__snapshots__/FilterSelection.jsx.snap
+++ /dev/null
@@ -1,7 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-`;
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterTags/FilterTags.jsx b/__tests__/shared/components/Contentful/SearchPageFilter/FilterTags/FilterTags.jsx
deleted file mode 100644
index a6984ff231..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterTags/FilterTags.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { FilterTagsInner } from 'components/Contentful/SearchPageFilter/FilterTags';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/FilterTags/__snapshots__/FilterTags.jsx.snap b/__tests__/shared/components/Contentful/SearchPageFilter/FilterTags/__snapshots__/FilterTags.jsx.snap
deleted file mode 100644
index 511ea5807c..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/FilterTags/__snapshots__/FilterTags.jsx.snap
+++ /dev/null
@@ -1,15 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-`;
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/SearchPageFilter.jsx b/__tests__/shared/components/Contentful/SearchPageFilter/SearchPageFilter.jsx
deleted file mode 100644
index 54b8282bac..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/SearchPageFilter.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { SearchPageFilterInner } from 'components/Contentful/SearchPageFilter/SearchPageFilter';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- startDate: '2019-08-01T07:21:20.249Z',
- endDate: '2019-09-01T07:21:20.249Z',
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/SearchPageFilter/__snapshots__/SearchPageFilter.jsx.snap b/__tests__/shared/components/Contentful/SearchPageFilter/__snapshots__/SearchPageFilter.jsx.snap
deleted file mode 100644
index e72483934a..0000000000
--- a/__tests__/shared/components/Contentful/SearchPageFilter/__snapshots__/SearchPageFilter.jsx.snap
+++ /dev/null
@@ -1,94 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
-
- filter
-
-
-
-
-
-
-
-
-
-`;
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksAuthor/TracksAuthor.jsx b/__tests__/shared/components/Contentful/TracksFilter/TracksAuthor/TracksAuthor.jsx
deleted file mode 100644
index 8a3183493d..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksAuthor/TracksAuthor.jsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { TracksAuthorInner } from 'components/Contentful/TracksFilter/TracksAuthor';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- selected: 'All authors',
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksAuthor/__snapshots__/TracksAuthor.jsx.snap b/__tests__/shared/components/Contentful/TracksFilter/TracksAuthor/__snapshots__/TracksAuthor.jsx.snap
deleted file mode 100644
index e8c4b0a310..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksAuthor/__snapshots__/TracksAuthor.jsx.snap
+++ /dev/null
@@ -1,25 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
- Author
-
-
-
-`;
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksDate/TracksDate.jsx b/__tests__/shared/components/Contentful/TracksFilter/TracksDate/TracksDate.jsx
deleted file mode 100644
index 08f91606aa..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksDate/TracksDate.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { TracksDateInner } from 'components/Contentful/TracksFilter/TracksDate';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksDate/__snapshots__/TracksDate.jsx.snap b/__tests__/shared/components/Contentful/TracksFilter/TracksDate/__snapshots__/TracksDate.jsx.snap
deleted file mode 100644
index 0978c0bdd8..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksDate/__snapshots__/TracksDate.jsx.snap
+++ /dev/null
@@ -1,44 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
- Date
-
-
-
-
- -
-
-
-
-
-`;
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksFilter.jsx b/__tests__/shared/components/Contentful/TracksFilter/TracksFilter.jsx
deleted file mode 100644
index 6e7b09b0ae..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksFilter.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { TracksFilterInner } from 'components/Contentful/TracksFilter/TracksFilter';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- startDate: '2019-08-01T07:21:20.249Z',
- endDate: '2019-09-01T07:21:20.249Z',
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksTags/TracksTags.jsx b/__tests__/shared/components/Contentful/TracksFilter/TracksTags/TracksTags.jsx
deleted file mode 100644
index 328e5ade34..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksTags/TracksTags.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import Renderer from 'react-test-renderer/shallow';
-import { TracksTagsInner } from 'components/Contentful/TracksFilter/TracksTags';
-
-test('Matches shallow shapshot', () => {
- const renderer = new Renderer();
- const MOCK_PROPS = {
- theme: {},
- };
- renderer.render();
- expect(renderer.getRenderOutput()).toMatchSnapshot();
-});
diff --git a/__tests__/shared/components/Contentful/TracksFilter/TracksTags/__snapshots__/TracksTags.jsx.snap b/__tests__/shared/components/Contentful/TracksFilter/TracksTags/__snapshots__/TracksTags.jsx.snap
deleted file mode 100644
index 62182b0ff3..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/TracksTags/__snapshots__/TracksTags.jsx.snap
+++ /dev/null
@@ -1,19 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-`;
diff --git a/__tests__/shared/components/Contentful/TracksFilter/__snapshots__/TracksFilter.jsx.snap b/__tests__/shared/components/Contentful/TracksFilter/__snapshots__/TracksFilter.jsx.snap
deleted file mode 100644
index a16029d305..0000000000
--- a/__tests__/shared/components/Contentful/TracksFilter/__snapshots__/TracksFilter.jsx.snap
+++ /dev/null
@@ -1,74 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Matches shallow shapshot 1`] = `
-
-
-
- filter
-
-
-
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap b/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap
index 6b3d2bb144..10310b5e75 100644
--- a/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap
+++ b/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap
@@ -303,6 +303,15 @@ exports[`Matches shallow shapshot 1`] = `
About Community
+
+
+ Changelog
+
+
diff --git a/config/default.js b/config/default.js
index 65aa340f6a..270f233b68 100644
--- a/config/default.js
+++ b/config/default.js
@@ -182,6 +182,8 @@ module.exports = {
* parameters that should never be send to the client side. */
SECRET: {
CONTENTFUL: {
+ DEFAULT_SPACE_NAME: 'default',
+ DEFAULT_ENVIRONMENT: 'master',
MANAGEMENT_TOKEN: '', // Personal Access Token to use the Content Management API
default: { // Human-readable name of space
SPACE_ID: '',
diff --git a/package-lock.json b/package-lock.json
index f0a3a85c73..286c66f0f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1554,9 +1554,9 @@
}
},
"@types/express-serve-static-core": {
- "version": "4.17.4",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.4.tgz",
- "integrity": "sha512-dPs6CaRWxsfHbYDVU51VjEJaUJEcli4UI0fFMT4oWmgCvHj+j7oIxz5MLHVL0Rv++N004c21ylJNdWQvPkkb5w==",
+ "version": "4.17.5",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.5.tgz",
+ "integrity": "sha512-578YH5Lt88AKoADy0b2jQGwJtrBxezXtVe/MBqWXKZpqx91SnC0pVkVCcxcytz3lWW+cHBYDi3Ysh0WXc+rAYw==",
"requires": {
"@types/node": "*",
"@types/range-parser": "*"
@@ -2121,13 +2121,6 @@
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
- },
- "dependencies": {
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
- }
}
},
"aria-query": {
@@ -4156,15 +4149,15 @@
}
},
"caniuse-db": {
- "version": "1.0.30001040",
- "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001040.tgz",
- "integrity": "sha512-rrwOYbMn4sTFNdqT4sD1gtrfOKr8CPyUEw3FMhlEYb4b+KHhBJY+a5NROsNTBDoABVHyW6v1EKG1iuJx+gosAg==",
+ "version": "1.0.30001042",
+ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001042.tgz",
+ "integrity": "sha512-2RKrB2hkLCW/8Uj32oaXj0O+N9ROo0/BF0EueWHwgs6AeeSiL+rCSsbICR3ayBJOZavgcFx65ZCw7QiafsoUFQ==",
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30001040",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001040.tgz",
- "integrity": "sha512-Ep0tEPeI5wCvmJNrXjE3etgfI+lkl1fTDU6Y3ZH1mhrjkPlVI9W4pcKbMo+BQLpEWKVYYp2EmYaRsqpPC3k7lQ==",
+ "version": "1.0.30001042",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz",
+ "integrity": "sha512-igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw==",
"dev": true
},
"capture-exit": {
@@ -4381,9 +4374,9 @@
}
},
"cli-width": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
- "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
+ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
"dev": true
},
"clipboard": {
@@ -4949,16 +4942,36 @@
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
+ "contentful": {
+ "version": "7.14.2",
+ "resolved": "https://registry.npmjs.org/contentful/-/contentful-7.14.2.tgz",
+ "integrity": "sha512-g4CQuQUMBlvx7igaAYVZ6jyMl4C1pcKNa2gu86XapaJSI0eG+0V6YfkMPc/gaTxfIj4055YAs+kB0sPRNZ/hrQ==",
+ "requires": {
+ "axios": "^0.19.1",
+ "contentful-resolve-response": "^1.1.4",
+ "contentful-sdk-core": "^6.4.0",
+ "json-stringify-safe": "^5.0.1",
+ "lodash": "^4.17.11"
+ }
+ },
"contentful-management": {
- "version": "5.21.1",
- "resolved": "https://registry.npmjs.org/contentful-management/-/contentful-management-5.21.1.tgz",
- "integrity": "sha512-82Ly4a6lT9k8+JBl+g9BcEYH9Krsnw3FzKayR2hhurERLdq8Gs2nQ1MoartIeVE2M5bXHNu+R97bx70emWAf+w==",
+ "version": "5.23.0",
+ "resolved": "https://registry.npmjs.org/contentful-management/-/contentful-management-5.23.0.tgz",
+ "integrity": "sha512-N0RoJYLauJoo6g3ThygN8l/OVHR0Np7a30+nkNPtkPew/tjFbNqhe9RP+ZLz91veU6OWZXG51ourvsfcsxUPxA==",
"requires": {
"axios": "^0.19.0",
"contentful-sdk-core": "^6.4.0",
"lodash": "^4.17.11"
}
},
+ "contentful-resolve-response": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/contentful-resolve-response/-/contentful-resolve-response-1.1.4.tgz",
+ "integrity": "sha512-oFq6n6zjbiwD9/7mBa8YHPwvPM0B0D4uOgg1n/rVzpQPhCrzeIixNj6fbJAbDiJt05rZqxiY3K1Db7pPRhRaZw==",
+ "requires": {
+ "lodash": "^4.17.4"
+ }
+ },
"contentful-sdk-core": {
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-6.4.4.tgz",
@@ -5342,6 +5355,12 @@
"requires": {
"find-up": "^2.1.0"
}
+ },
+ "serialize-javascript": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+ "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
+ "dev": true
}
}
},
@@ -5351,9 +5370,9 @@
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
},
"core-js-pure": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz",
- "integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==",
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
+ "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==",
"dev": true
},
"core-util-is": {
@@ -5361,6 +5380,15 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
"cosmiconfig": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
@@ -6599,9 +6627,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"electron-to-chromium": {
- "version": "1.3.402",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.402.tgz",
- "integrity": "sha512-gaCDfX7IUH0s3JmBiHCDPrvVcdnTTP1r4WLJc2dHkYYbLmXZ2XHiJCcGQ9Balf91aKTvuCKCyu2JjJYRykoI1w==",
+ "version": "1.3.412",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.412.tgz",
+ "integrity": "sha512-4bVdSeJScR8fT7ERveLWbxemY5uXEHVseqMRyORosiKcTUSGtVwBkV8uLjXCqoFLeImA57Z9hbz3TOid01U4Hw==",
"dev": true
},
"elliptic": {
@@ -7232,18 +7260,18 @@
"integrity": "sha1-MKlHMDxrjV6VW+4rmbHSMyBqaQE="
},
"esquery": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz",
- "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+ "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
"dev": true,
"requires": {
- "estraverse": "^5.0.0"
+ "estraverse": "^5.1.0"
},
"dependencies": {
"estraverse": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz",
- "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz",
+ "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==",
"dev": true
}
}
@@ -9788,12 +9816,6 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
- },
- "wordwrap": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
- "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
- "dev": true
}
}
},
@@ -10138,9 +10160,9 @@
}
},
"html-entities": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
- "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
+ "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
"dev": true
},
"html-tags": {
@@ -10150,15 +10172,14 @@
"dev": true
},
"html-to-text": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-3.3.0.tgz",
- "integrity": "sha1-aptjxpm4hbt7qEsURr/mh2u/z7c=",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-5.1.1.tgz",
+ "integrity": "sha512-Bci6bD/JIfZSvG4s0gW/9mMKwBRoe/1RWLxUME/d6WUSZCdY7T60bssf/jFf7EYXRyqU4P5xdClVqiYU0/ypdA==",
"requires": {
- "he": "^1.0.0",
- "htmlparser2": "^3.9.2",
- "optimist": "^0.6.1",
- "underscore": "^1.8.3",
- "underscore.string": "^3.2.3"
+ "he": "^1.2.0",
+ "htmlparser2": "^3.10.1",
+ "lodash": "^4.17.11",
+ "minimist": "^1.2.0"
}
},
"htmlparser2": {
@@ -12614,12 +12635,6 @@
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
"dev": true
- },
- "underscore": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
- "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
- "dev": true
}
}
},
@@ -12933,17 +12948,17 @@
}
},
"jwks-rsa": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.7.0.tgz",
- "integrity": "sha512-tq7DVJt9J6wTvl9+AQfwZIiPSuY2Vf0F+MovfRTFuBqLB1xgDVhegD33ChEAQ6yBv9zFvUIyj4aiwrSA5VehUw==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.8.0.tgz",
+ "integrity": "sha512-+HYROHD5fsYQCNrJ37RSr2NjbN2/V9YT+yVF3oJxLmPIZWrmp1SOl1hMw2RcuNh+LGA2bGZIhRKGiMjhQa/b7Q==",
"requires": {
"@types/express-jwt": "0.0.42",
+ "axios": "^0.19.2",
"debug": "^4.1.0",
"jsonwebtoken": "^8.5.1",
"limiter": "^1.1.4",
"lru-memoizer": "^2.0.1",
- "ms": "^2.1.2",
- "request": "^2.88.0"
+ "ms": "^2.1.2"
},
"dependencies": {
"debug": {
@@ -14226,7 +14241,7 @@
"dev": true
},
"navigation-component": {
- "version": "git+https://github.com/topcoder-platform/navigation-component.git#832e036d30460453aaf6241acfcd94a640d142aa",
+ "version": "git+https://github.com/topcoder-platform/navigation-component.git#24bd50897313b8236a2f0193de0ab5b7a26ab31b",
"from": "git+https://github.com/topcoder-platform/navigation-component.git#develop",
"requires": {
"classnames": "^2.2.6",
@@ -14283,6 +14298,11 @@
"symbol-observable": "^1.2.0"
}
},
+ "serialize-javascript": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+ "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A=="
+ },
"topcoder-react-utils": {
"version": "0.7.9",
"resolved": "https://registry.npmjs.org/topcoder-react-utils/-/topcoder-react-utils-0.7.9.tgz",
@@ -14333,9 +14353,9 @@
"optional": true
},
"nearley": {
- "version": "2.19.1",
- "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.19.1.tgz",
- "integrity": "sha512-xq47GIUGXxU9vQg7g/y1o1xuKnkO7ev4nRWqftmQrLkfnE/FjRqDaGOUakM8XHPn/6pW3bGjU2wgoJyId90rqg==",
+ "version": "2.19.2",
+ "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.19.2.tgz",
+ "integrity": "sha512-h6lygT0BWAGErDvoE2LfI+tDeY2+UUrqG5dcBPdCmjnjud9z1wE0P7ljb85iNbE93YA+xJLpoSYGMuUqhnSSSA==",
"dev": true,
"requires": {
"commander": "^2.19.0",
@@ -14919,9 +14939,13 @@
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
},
"object-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
- "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
+ "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
},
"object-keys": {
"version": "1.1.1",
@@ -15063,22 +15087,6 @@
"resolved": "https://registry.npmjs.org/opentracing/-/opentracing-0.14.1.tgz",
"integrity": "sha1-QNJ4vupBdmCjXdnT7nZRH/qRHc0="
},
- "optimist": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
- "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
- "requires": {
- "minimist": "~0.0.1",
- "wordwrap": "~0.0.2"
- },
- "dependencies": {
- "minimist": {
- "version": "0.0.10",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
- "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
- }
- }
- },
"optimize-css-assets-webpack-plugin": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-4.0.3.tgz",
@@ -17379,9 +17387,9 @@
"integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw=="
},
"query-string": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.12.0.tgz",
- "integrity": "sha512-aoiFW9ZU7jP8Itjqfpw80Qe7RoyCIhFrW522sdsp9LG92pat6CCG3d8qNZBaUi71FsEjIfLjx9Ky347FtVoqXA==",
+ "version": "6.12.1",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.12.1.tgz",
+ "integrity": "sha512-OHj+zzfRMyj3rmo/6G8a5Ifvw3AleL/EbcHMD27YA31Q+cO5lfmQxECkImuNVjcskLcvBRVHNAB3w6udMs1eAA==",
"requires": {
"decode-uri-component": "^0.2.0",
"split-on-first": "^1.0.0",
@@ -17944,9 +17952,9 @@
}
},
"react-resize-detector": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-4.2.1.tgz",
- "integrity": "sha512-ZfPMBPxXi0o3xox42MIEtz84tPSVMW9GgwLHYvjVXlFM+OkNzbeEtpVSV+mSTJmk4Znwomolzt35zHN9LNBQMQ==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-4.2.3.tgz",
+ "integrity": "sha512-4AeS6lxdz2KOgDZaOVt1duoDHrbYwSrUX32KeM9j6t9ISyRphoJbTRCMS1aPFxZHFqcCGLT1gMl3lEcSWZNW0A==",
"requires": {
"lodash": "^4.17.15",
"lodash-es": "^4.17.15",
@@ -19162,9 +19170,9 @@
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz",
+ "integrity": "sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==",
"requires": {
"path-parse": "^1.0.6"
}
@@ -20045,9 +20053,9 @@
"dev": true
},
"serialize-javascript": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
- "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A=="
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
+ "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ=="
},
"serve-favicon": {
"version": "2.5.0",
@@ -20560,9 +20568,9 @@
}
},
"sprintf-js": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
- "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sshpk": {
"version": "1.16.1",
@@ -21979,7 +21987,7 @@
}
},
"tc-accounts": {
- "version": "git+https://github.com/appirio-tech/accounts-app.git#41529eab164d442cff0d43eeed07998c097a2afe",
+ "version": "git+https://github.com/appirio-tech/accounts-app.git#c30db73cddbf329ea502a42cf42a5fee2dfe5406",
"from": "git+https://github.com/appirio-tech/accounts-app.git#dev",
"requires": {
"@uirouter/angularjs": "^1.0.0",
@@ -32307,12 +32315,6 @@
"ajv-keywords": "^3.1.0"
}
},
- "serialize-javascript": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
- "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
- "dev": true
- },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -32756,6 +32758,11 @@
"react-is": "^16.8.2"
}
},
+ "serialize-javascript": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+ "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A=="
+ },
"tc-core-library-js": {
"version": "github:appirio-tech/tc-core-library-js#d16413db30b1eed21c0cf426e185bedb2329ddab",
"from": "github:appirio-tech/tc-core-library-js#v2.6",
@@ -32927,6 +32934,13 @@
"serve-favicon": "^2.5.0",
"shortid": "^2.2.8",
"url-parse": "^1.4.1"
+ },
+ "dependencies": {
+ "serialize-javascript": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+ "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A=="
+ }
}
},
"topo": {
@@ -33075,38 +33089,20 @@
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"uglify-js": {
- "version": "3.8.1",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz",
- "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==",
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz",
+ "integrity": "sha512-JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA==",
"dev": true,
"optional": true,
"requires": {
- "commander": "~2.20.3",
- "source-map": "~0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "optional": true
- }
+ "commander": "~2.20.3"
}
},
"underscore": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
- "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
- },
- "underscore.string": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
- "integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
- "requires": {
- "sprintf-js": "^1.0.3",
- "util-deprecate": "^1.0.2"
- }
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
+ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
+ "dev": true
},
"unfetch": {
"version": "4.1.0",
@@ -34645,9 +34641,10 @@
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
},
"wordwrap": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
- "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
},
"wordwrapjs": {
"version": "3.0.0",
diff --git a/package.json b/package.json
index 7a67a068a6..1269e5aa62 100644
--- a/package.json
+++ b/package.json
@@ -45,8 +45,10 @@
"browser-cookies": "^1.2.0",
"classnames": "^2.2.6",
"config": "^1.30.0",
+ "contentful": "^7.14.2",
"contentful-management": "^5.10.0",
"cookie-parser": "^1.4.3",
+ "cors": "^2.8.5",
"country-list": "^2.1.1",
"d3": "3.5.17",
"dd-trace": "^0.7.1",
diff --git a/src/server/index.js b/src/server/index.js
index 25e4c3bd80..062cb1449c 100644
--- a/src/server/index.js
+++ b/src/server/index.js
@@ -26,7 +26,6 @@ import { toJson as xmlToJson } from 'utils/xml2json';
import cdnRouter from './routes/cdn';
import mailChimpRouter from './routes/mailchimp';
import mockDocuSignFactory from './__mocks__/docu-sign-mock';
-// import { pollArticlesForThrive } from 'server/services/contentful';
/* Dome API for topcoder communities */
import tcCommunitiesDemoApi from './tc-communities';
@@ -34,10 +33,6 @@ import tcCommunitiesDemoApi from './tc-communities';
import webpackConfigFactory from '../../webpack.config';
/* eslint-enable */
-// DISABLED
-// Init TC Blog -> THRIVE poll bridge
-// pollArticlesForThrive();
-
global.atob = atob;
const CMS_BASE_URL = `https://app.contentful.com/spaces/${config.SECRET.CONTENTFUL.SPACE_ID}`;
diff --git a/src/server/routes/contentful.js b/src/server/routes/contentful.js
index 463310255d..638ff7bd02 100644
--- a/src/server/routes/contentful.js
+++ b/src/server/routes/contentful.js
@@ -2,7 +2,6 @@
* The routes that expose assets and content from Contentful CMS to the CDN.
*/
-import config from 'config';
import express from 'express';
import {
@@ -13,16 +12,14 @@ import {
articleVote,
} from '../services/contentful';
-const routes = express.Router();
+const cors = require('cors');
-const LOCAL_MODE = Boolean(config.CONTENTFUL.LOCAL_MODE);
+const routes = express.Router();
-/* Sets Access-Control-Allow-Origin header to avoid CORS error.
- * TODO: Replace the wildcard value by an appropriate origin filtering. */
-routes.use((req, res, next) => {
- res.set('Access-Control-Allow-Origin', '*');
- next();
-});
+// Enables CORS on those routes according config above
+// ToDo configure CORS for set of our trusted domains
+routes.use(cors());
+routes.options('*', cors());
/* Gets non-image asset file. */
routes.use(
@@ -60,7 +57,7 @@ routes.use(
routes.use('/:spaceName/:environment/preview/assets/:id', (req, res, next) => {
const { environment, id, spaceName } = req.params;
getService(spaceName, environment, true)
- .getAsset(id, !LOCAL_MODE)
+ .getAsset(id)
.then(res.send.bind(res), next);
});
@@ -68,7 +65,7 @@ routes.use('/:spaceName/:environment/preview/assets/:id', (req, res, next) => {
routes.use('/:spaceName/:environment/preview/assets', (req, res, next) => {
const { environment, spaceName } = req.params;
getService(spaceName, environment, true)
- .queryAssets(req.query, !LOCAL_MODE)
+ .queryAssets(req.query)
.then(res.send.bind(res), next);
});
@@ -94,7 +91,7 @@ routes.use(
(req, res, next) => {
const { environment, id, spaceName } = req.params;
getService(spaceName, environment, false)
- .getAsset(id, !LOCAL_MODE)
+ .getAsset(id)
.then(res.send.bind(res), next);
},
);
@@ -103,7 +100,7 @@ routes.use(
routes.use(':spaceName/:environment/published/assets', (req, res, next) => {
const { environment, spaceName } = req.params;
getService(spaceName, environment, false)
- .queryAssets(req.query, !LOCAL_MODE)
+ .queryAssets(req.query)
.then(res.send.bind(res), next);
});
@@ -132,25 +129,4 @@ routes.use('/:spaceName/:environment/votes', (req, res, next) => {
.then(res.send.bind(res), next);
});
-/* Returns index of assets and content. */
-/*
-routes.use('/index', async (req, res, next) => {
- try {
- res.set('Cache-Control', `max-age=${1000}`);
- res.send(await getIndex());
- } catch (err) { next(err); }
-});
-*/
-
-/* Returns URL for the next sync of assets and content index with Contentful
- * API. */
-/*
-routes.use('/next-sync-url', async (req, res, next) => {
- try {
- res.set('Cache-Control', `max-age=${1000}`);
- res.send(await getNextSyncUrl());
- } catch (err) { next(err); }
-});
-*/
-
export default routes;
diff --git a/src/server/services/contentful.js b/src/server/services/contentful.js
index 8dba5cf7e2..431ceda155 100644
--- a/src/server/services/contentful.js
+++ b/src/server/services/contentful.js
@@ -1,76 +1,35 @@
-/* eslint-disable no-param-reassign */
/**
- * Server-side functions necessary for effective integration with Contentful
- * CMS.
+ * Server-side functions necessary for effective integration
+ * with Contentful CMS
*/
import _ from 'lodash';
import config from 'config';
-import fetch from 'isomorphic-fetch';
-// import logger from 'utils/logger';
-// import moment from 'moment';
-import qs from 'qs';
+import { createClient } from 'contentful';
import { logger } from 'topcoder-react-lib';
+import { isomorphy } from 'topcoder-react-utils';
const contentful = require('contentful-management');
-const xml2json = require('xml2json');
/* Holds Contentful CDN URL. */
const CDN_URL = 'https://cdn.contentful.com/spaces';
-/* Holds the maximal index age [ms].
- *
- * Set to 1 minute, which means ~100k API requests to Contentful from our dev
- * and prod environments (preview API calls apart, but there should be not that
- * many of them, as the circle of potential editors is edit, compared to that of
- * the regular website visitors).
- */
-// const INDEX_MAXAGE = 60 * 1000;
-
/* Holds Contentful Preview URL. */
const PREVIEW_URL = 'https://preview.contentful.com/spaces';
-/* Holds base URL of Community App CDN. */
-// const TC_CDN_URL = `${config.CDN.PUBLIC}/contentful`;
-
export const ASSETS_DOMAIN = 'assets.ctfassets.net';
export const IMAGES_DOMAIN = 'images.ctfassets.net';
-const MAX_FETCH_RETRIES = 5;
-
-/* GENERAL-PURPOSE CONTETNFUL API SERVICE. */
-
/**
- * Given an asset object, replaces its original file URL by URL leading to our
- * own CloudFront CDN.
- *
- * BEWARE:
- * - It mutates the argument asset.
- *
- * @param {Object} asset
+ * Generic logger for errors and warnings
+ * from Contentful API calls
+ * @param {String} level
+ * @param {String} data
*/
-/*
-function mapAssetFileUrlToCdn(asset) {
- let x = asset.fields.file.url.split('/');
- switch (x[2]) {
- case ASSETS_DOMAIN:
- x = `${TC_CDN_URL}/assets/${x[4]}/${x[5]}/${x[6]}`;
- break;
- case IMAGES_DOMAIN:
- x = `${TC_CDN_URL}/images/${x[4]}/${x[5]}/${x[6]}`;
- break;
- default: throw new Error('Unexpected asset location');
+function logHandler(level, data) {
+ if (isomorphy.isDev) {
+ logger.log('Contentful logHandler', level, data);
}
- asset.fields.file.url = x; // eslint-disable-line no-param-reassign
-}
-*/
-
-/**
- * Creates a promise that resolves two second after its creation.
- * @return {Promise}
- */
-function oneSecondDelay() {
- return new Promise(resolve => setTimeout(resolve, 1000));
}
/**
@@ -82,54 +41,32 @@ class ApiService {
* Creates a new service instance.
* @param {String} baseUrl The base API endpoint.
* @param {String} key API key.
+ * @param {String} spaceId The space id.
+ * @param {Boolean} preview Use the preview API?
*/
- constructor(baseUrl, key) {
- this.private = { baseUrl, key };
- }
-
- /**
- * Gets data from the specified endpoing.
- * @param {String} endpoint
- * @param {Object} query Optional. URL query to append to the request.
- * @return {Promise}
- */
- async fetch(endpoint, query) {
- // Contentful API rate limits, which are 78 requests within 1 second
- // we await 14ms before each request to make sure we don't break this limitation
- await new Promise(resolve => setTimeout(resolve, 14));
- // fire calls to Contentful API
- let url = `${this.private.baseUrl}${endpoint}`;
- if (query) url += `?${qs.stringify(query)}`;
- let res;
- for (let i = 0; i < MAX_FETCH_RETRIES; i += 1) {
- /* The loop is here to retry async operation multiple times in case of
- * failures due to violation of Contentful API rate limits, which are
- * 78 requests within 1 second. Thus, it is a valid use of await inside
- * loop. */
- /* eslint-disable no-await-in-loop */
- res = await fetch(url, {
- headers: { Authorization: `Bearer ${this.private.key}` },
- });
- /* 429 = "Too Many Requests" */
- if (res.status !== 429) break;
- await oneSecondDelay();
- /* eslint-enable no-await-in-loop */
- }
- if (!res.ok) throw new Error(res.statusText);
- return res.json();
+ constructor(baseUrl, key, spaceId, preview) {
+ this.private = {
+ baseUrl, key, spaceId, preview,
+ };
+ // client config
+ const clientConf = {
+ accessToken: key,
+ space: spaceId,
+ logHandler,
+ };
+ if (preview) clientConf.host = 'preview.contentful.com';
+ // create the client to work with
+ this.client = createClient(clientConf);
}
/**
* Gets the specified asset.
* @param {String} id Asset ID.
- * @param {Boolean} mapFileUrlToCdn Optional. Pass in `true` to replace the
- * actual file path by Community App CDN path.
* @return {Promise}
*/
- async getAsset(id /* , mapFileUrlToCdn */) {
- const res = await this.fetch(`/assets/${id}`);
- // if (mapFileUrlToCdn) mapAssetFileUrlToCdn(res);
- return res;
+ async getAsset(id) {
+ const res = await this.client.getAsset(id);
+ return res.stringifySafe ? JSON.parse(res.stringifySafe()) : res;
}
/**
@@ -137,21 +74,19 @@ class ApiService {
* @param {String} id Entry ID.
* @return {Promise}
*/
- getEntry(id) {
- return this.fetch(`/entries/${id}`);
+ async getEntry(id) {
+ const res = await this.client.getEntry(id);
+ return res.stringifySafe ? JSON.parse(res.stringifySafe()) : res;
}
/**
* Queries assets.
* @param {Object} query Optional. Query.
- * @param {Boolean} mapFileUrlToCdn Optional. Pass in `true` to replace the
- * actual file path by Community App CDN path.
* @return {Promise}
*/
- async queryAssets(query /* , mapFileUrlToCdn */) {
- const res = await this.fetch('/assets', query);
- // if (mapFileUrlToCdn) res.items.forEach(x => mapAssetFileUrlToCdn(x));
- return res;
+ async queryAssets(query) {
+ const res = await this.client.getAssets(query);
+ return res.stringifySafe ? JSON.parse(res.stringifySafe()) : res;
}
/**
@@ -159,8 +94,9 @@ class ApiService {
* @param {Object} query Optional. Query for filtering / sorting of entries.
* @return {Promise}
*/
- queryEntries(query) {
- return this.fetch('/entries', query);
+ async queryEntries(query) {
+ const res = await this.client.getEntries(query);
+ return res.stringifySafe ? JSON.parse(res.stringifySafe()) : res;
}
}
@@ -179,17 +115,21 @@ export function articleVote(body) {
.then(environment => environment.getEntry(body.id))
.then((entry) => {
if (!entry.fields.upvotes) {
+ // eslint-disable-next-line no-param-reassign
entry.fields.upvotes = {
'en-US': body.votes.upvotes,
};
} else {
+ // eslint-disable-next-line no-param-reassign
entry.fields.upvotes['en-US'] = body.votes.upvotes;
}
if (!entry.fields.downvotes) {
+ // eslint-disable-next-line no-param-reassign
entry.fields.downvotes = {
'en-US': body.votes.downvotes,
};
} else {
+ // eslint-disable-next-line no-param-reassign
entry.fields.downvotes['en-US'] = body.votes.downvotes;
}
return entry.update();
@@ -197,136 +137,12 @@ export function articleVote(body) {
.then(entry => entry.publish());
}
-/**
- * This function fetches TC RSS feed to create draft articles in THRIVE for the new posted blogs.
- * It calls itself by interval to poll for new blogs.
- * Runs on server side only.
- */
-export async function pollArticlesForThrive() {
- // make this function execute only when in production
- if (process.env.BABEL_ENV !== 'production') return;
- logger.log('polling blog articles for THRIVE -> INIT');
- // Create Contentful client to work with
- const client = contentful.createClient({
- accessToken: config.SECRET.CONTENTFUL.MANAGEMENT_TOKEN,
- });
- // connect to Thrive space
- const env = await client.getSpace(config.SECRET.CONTENTFUL.EDU.SPACE_ID)
- .then(space => space.getEnvironment('master'));
- // fetch the poll interval
- const pollConf = await env.getEntry('7n1HT3MhUhgjvCzd36Nymd');
- const pollInt = pollConf ? pollConf.fields.timeInSeconds : 86400; // default to day if not found
- // fetch the RSS feed and parse it
- const feedXML = await fetch(config.URL.THRIVE_POLL_FEED);
- if (feedXML.ok) {
- let feed = await feedXML.text();
- feed = await Promise.resolve(xml2json.toJson(feed, { object: true }));
- if (feed) {
- // when feed loaded and parsed ok
- // apply filter first based on tags work only with 'Community Stories'
- feed.rss.channel.item = _.filter(
- feed.rss.channel.item,
- blogItem => blogItem.category.indexOf('Community Stories') !== -1,
- );
- // loop all feed items and check if those exists in space
- // if not existing then create
- Promise.all(
- _.map(feed.rss.channel.item,
- blogPost => env.getEntries({
- content_type: 'article',
- 'fields.title[match]': blogPost.title,
- })
- .then((queryData) => {
- if (queryData.total === 0) {
- // article not found in Contentful space
- // will create it with payload...
- const article = {
- fields: {
- title: { 'en-US': blogPost.title },
- type: { 'en-US': 'Article' },
- tags: { 'en-US': blogPost.category },
- creationDate: { 'en-US': new Date(blogPost.pubDate) },
- content: { 'en-US': blogPost.description },
- externalArticle: { 'en-US': true },
- contentUrl: { 'en-US': blogPost.link },
- },
- };
- // check if author exists
- // if yes link it if no create it?
- return env.getEntries({
- content_type: 'person',
- query: blogPost['dc:creator'],
- })
- .then((author) => {
- // found an author that matches link it
- if (author.total) {
- article.fields.contentAuthor = {
- 'en-US': [{
- sys: {
- type: 'Link', linkType: 'Entry', id: author.items[0].sys.id,
- },
- }],
- };
- }
- // try get the page to extract its featured image
- return fetch(blogPost.link)
- .then(rsp => rsp.text())
- .then((HTMLsrc) => {
- const match = /]*href="([^"]+)"/gm.exec(HTMLsrc);
- if (match && match[1]) {
- // create the asset in Contentful
- return env.createAsset({
- fields: {
- title: { 'en-US': blogPost.title },
- file: {
- 'en-US': { fileName: blogPost.title, contentType: 'image', upload: match[1] },
- },
- },
- })
- .then(asset => asset.processForAllLocales())
- .then((asset) => {
- article.fields.featuredImage = {
- 'en-US': {
- sys: {
- type: 'Link', linkType: 'Asset', id: asset.sys.id,
- },
- },
- };
- return env.createEntry('article', article);
- });
- }
- // could not find image
- // just create the article without it...
- return env.createEntry('article', article);
- });
- });
- }
- // Article exists in Contentful space
- // do nothing...
- return 1;
- })),
- )
- .then(() => logger.log('polling blog articles for THRIVE -> DONE'));
- }
- }
- // schedule a repeat each TC_EDU_POLL_TIME
- setTimeout(
- pollArticlesForThrive,
- pollInt * 1000 + (Math.floor(Math.random() * Math.floor(5)) * 60 * 1000),
- ).unref();
-}
-
-// /* Contentful CDN service. */
-// export const cdnService = new ApiService(CDN_URL, CDN_KEY);
-
-// /* Contentful Preview service. */
-// export const previewService = new ApiService(PREVIEW_URL, PREVIEW_KEY);
-
-
let services;
function initServiceInstances() {
- const contentfulConfig = config.SECRET.CONTENTFUL;
+ const contentfulConfig = _.omit(config.SECRET.CONTENTFUL, [
+ 'DEFAULT_SPACE_NAME', 'DEFAULT_ENVIRONMENT', 'MANAGEMENT_TOKEN',
+ ]);
services = {};
_.map(contentfulConfig, (spaceConfig, spaceName) => {
services[spaceName] = {};
@@ -338,8 +154,10 @@ function initServiceInstances() {
const cdnBaseUrl = `${CDN_URL}/${spaceId}/environments/${environment}`;
const svcs = {};
- svcs.previewService = new ApiService(previewBaseUrl.toString(), env.PREVIEW_API_KEY);
- svcs.cdnService = new ApiService(cdnBaseUrl.toString(), env.CDN_API_KEY);
+ svcs.previewService = new ApiService(
+ previewBaseUrl.toString(), env.PREVIEW_API_KEY, spaceId, true,
+ );
+ svcs.cdnService = new ApiService(cdnBaseUrl.toString(), env.CDN_API_KEY, spaceId);
services[spaceName][environment] = svcs;
}
});
@@ -378,302 +196,3 @@ export function getService(spaceName, environment, preview) {
const service = services[name][env];
return preview ? service.previewService : service.cdnService;
}
-
-/**
- * Generates the last version for content index, and other similar data that
- * have to be refreshed regularly to keep us in sync with content edits in CMS.
- * @return {Number}
- */
-/*
-function getLastVersion() {
- const now = Date.now();
- return now - (now % INDEX_MAXAGE);
-}
-*/
-
-/**
- * Gets the index of assets and entries via Community App CDN.
- * @param {Number} version Optional. The version of index to fetch. Defaults to
- * the latest index version.
- * @return {Promise}
- */
-/*
-async function getIndexViaCdn(version = getLastVersion()) {
- const res = await fetch(`${TC_CDN_URL}/index?version=${version}`);
- if (!res.ok) {
- const MSG = 'Failed to get the index';
- logger.error(MSG, res);
- throw new Error(MSG);
- }
- return res.json();
-}
-*/
-
-/**
- * Gets the next sync URL via CDN.
- * @param {Number} version Optional. The version of index to fetch. Defaults to
- * the latest version.
- * @return {Promise}
- */
-/*
-async function getNextSyncUrlViaCdn(version = getLastVersion()) {
- const res =
- await fetch(`${TC_CDN_URL}/next-sync-url?version=${version}`);
- if (!res.ok) {
- const MSG = 'Failed to get the next sync URL';
- logger.error(MSG, res.statusText);
- throw new Error(MSG);
- }
- return res.text();
-}
-*/
-
-/* THE INDEX OF CMS ASSETS AND ENTRIES.
- *
- * A tricky logic is involved to keep it all working properly, beware to modify
- * and in all cases prefer to use exported functions that provide access to the
- * index and take care about its correct updating. */
-
-/* The barrier for syncronization of parallel calls to async functions exported
- * by this module. If not null, then it is a promise that signals that the index
- * update is in progress; in this case any function that needs to access the
- * index should wait until the promise is resolved. */
-// let barrier = null;
-
-/* Holds the next sync URL provided by the CMS. */
-// let nextSyncUrl;
-
-/* The public index of CMS assets and entries. It is the map between CMS IDs of
- * these assets/entries and the timestamps of their last updates. Note that this
- * index is accessible by the frontend via CDN, thus anybody can access it and
- * thus all assets and entries mentioned in this index. That's why announcements
- * with future startDate are not included into this index. */
-// let publicIndex;
-
-/**
- * Adds a new asset to the index, or updates the existing one.
- * @param {Object} asset
- */
-/*
-function indexAsset(asset) {
- const { id, createdAt, updatedAt } = asset.sys;
- publicIndex.assets[id] = moment(updatedAt || createdAt).valueOf();
-}
-*/
-
-/**
- * Adds a new entry to the index, or updates the existing one.
- * @param {Object} entry
- */
-/*
-function indexEntry(entry) {
- let isPublic = true;
- const { id, createdAt, updatedAt } = entry.sys;
- const timestamp = moment(updatedAt || createdAt).valueOf();
-
- const type = entry.sys.contentType.sys.id;
- switch (type) {
- /* We use an additional index of dashboard announcement to be able to find
- * out which announcement should be show at any moment, without a call to
- * CMS. We also do not include future announcements into the public index
- * to avoid any exposure to general public before the time. */
-/*
- case 'dashboardAnnouncement': {
- const now = Date.now();
- const endDate = moment(entry.fields.endDate['en-US']).valueOf();
- const startDate = moment(entry.fields.startDate['en-US']).valueOf();
- isPublic = now > startDate;
- if (now < endDate) {
- currentDashboardAnnouncementsMap[id] = {
- id,
- endDate,
- startDate,
- timestamp,
- };
- }
- break;
- }
- default:
- }
-
- if (isPublic) publicIndex.entries[id] = timestamp;
-}
-*/
-
-/**
- * Adds a new asset or entry to the index, or updates / removes the existing
- * one.
- * @param {Object} item
- */
-/*
-function indexItem(item) {
- const { id, type } = item.sys;
- switch (type) {
- case 'Asset': indexAsset(item); break;
- case 'DeletedAsset': delete publicIndex.assets[id]; break;
- case 'DeletedEntry':
- delete currentDashboardAnnouncementsMap[id];
- delete publicIndex.entries[id];
- break;
- case 'Entry': indexEntry(item); break;
- default: throw new Error('Invariant violation');
- }
-}
-*/
-
-/**
- * Updates the current announcement ID.
- */
-/*
-function updateCurrentDashboardAnnouncementId() {
- const list = [];
- const now = Date.now();
- Object.values(currentDashboardAnnouncementsMap).forEach((item) => {
- if (item.endDate < now) delete currentDashboardAnnouncementsMap[item.id];
- else if (item.startDate < now) list.push(item);
- });
- if (list.length) {
- list.sort((a, b) => b.startDate - a.startDate);
- currentDashboardAnnouncementId = list[0].id;
- } else currentDashboardAnnouncementId = '';
- if (currentDashboardAnnouncementId
- && (!publicIndex.entries[currentDashboardAnnouncementId])) {
- publicIndex.entries[currentDashboardAnnouncementId] = list[0].timestamp;
- }
-}
-*/
-
-/**
- * Updates the index.
- * @return {Promise}
- */
-/*
-async function updateIndex() {
- let nextPageUrl = nextSyncUrl;
- while (nextPageUrl) {
- /* Disabled, as we really need to keep these iterations sequential, thus
- * await inside the loop is not an error. */
-/* eslint-disable no-await-in-loop */
-/*
- let d = await fetch(nextPageUrl, {
- headers: { Authorization: `Bearer ${CDN_KEY}` },
- });
- if (!d.ok) {
- const MSG = 'Failed to update the index';
- logger.error(MSG, d.statusText);
- throw new Error(MSG);
- }
- d = await d.json();
- /* eslint-anable no-await-in-loop */
-/*
- d.items.forEach(indexItem);
- ({ nextPageUrl, nextSyncUrl } = d);
- }
- publicIndex.timestamp = Date.now();
- updateCurrentDashboardAnnouncementId();
-}
-*/
-
-/**
- * Inits the index with data from CMS.
- * @return {Promise}
- */
-/*
-async function initIndex() {
- /* Gets necessary data from CMS. */
-/*
- let d = await fetch(`${CDN_URL}/sync?initial=true`, {
- headers: { Authorization: `Bearer ${CDN_KEY}` },
- });
- if (!d.ok) {
- const MSG = 'Failed to initialize the index';
- logger.error(MSG, d.statusText);
- throw new Error(MSG);
- }
- d = await d.json();
-
- /* Generates the index. */
-/*
- publicIndex = {
- assets: {},
- entries: {},
- };
- currentDashboardAnnouncementsMap = {};
- d.items.forEach(indexItem);
- publicIndex.timestamp = Date.now();
- updateCurrentDashboardAnnouncementId();
-
- /* In case the initial update is too large to fit into a single response.
- * TODO: This updateIndex(..) function can be combined with initIndex(..)
- * into a single function. The URL query is the only real difference between
- * them. */
-/*
- if (d.nextPageUrl) {
- nextSyncUrl = d.nextPageUrl;
- await updateIndex();
- } else ({ nextSyncUrl } = d);
-}
-*/
-
-/**
- * Returns the index of CMS assets and content, along with the timestamps of
- * their last updates. This function also takes care about initialization and
- * automatic updates of the index, as necessary.
- * @return {Promise}
- */
-/*
-export async function getIndex() {
- while (barrier) await barrier;
- if (!publicIndex) barrier = initIndex();
- else if (Date.now() - publicIndex.timestamp > INDEX_MAXAGE) {
- barrier = updateIndex();
- }
- if (barrier) {
- await barrier;
- barrier = null;
-
- /* These two calls are necessary to cache the updated index by CDN. */
-/*
- getIndexViaCdn();
- getCurrentDashboardAnnouncementsIndexViaCdn();
- getNextSyncUrlViaCdn();
- }
-
- return publicIndex;
-}
-*/
-
-/**
- * Returns the next sync URL.
- * @return {Promise}
- */
-/*
-export async function getNextSyncUrl() {
- while (barrier) await barrier;
- if (!publicIndex || Date.now() - publicIndex.timestamp > INDEX_MAXAGE) {
- await getIndex();
- }
- return nextSyncUrl;
-}
-*/
-
-/* Module initialization.
- * This code tries to pull the current index from CDN, where it is supposed to
- * be cached, to keep it persistent across re-deployments of the app, and also
- * to prevent unnecessary calls to Contentful APIs from a locally deployed
- * server. In case of failure, it initializes the new index using getIndex()
- * function directly. */
-/*
-let version = Date.now() - INDEX_MAXAGE;
-version -= version % INDEX_MAXAGE;
-Promise.all([
- getIndexViaCdn(version),
- getCurrentDashboardAnnouncementsIndexViaCdn(version),
- getNextSyncUrl(version),
-]).then(([index, dashIndex, next]) => {
- publicIndex = index;
- currentDashboardAnnouncementsMap = dashIndex;
- nextSyncUrl = next;
- updateCurrentDashboardAnnouncementId();
-}).catch(() => getIndex());
-*/
diff --git a/src/shared/components/Contentful/Article/Article.jsx b/src/shared/components/Contentful/Article/Article.jsx
index 2beb028f37..c0ef0eae71 100644
--- a/src/shared/components/Contentful/Article/Article.jsx
+++ b/src/shared/components/Contentful/Article/Article.jsx
@@ -8,6 +8,8 @@ import PT from 'prop-types';
import { fixStyle } from 'utils/contentful';
import { getService } from 'services/contentful';
import MarkdownRenderer from 'components/MarkdownRenderer';
+import ReactDOMServer from 'react-dom/server';
+import markdown from 'utils/markdown';
import ContentfulLoader from 'containers/ContentfulLoader';
import LoadingIndicator from 'components/LoadingIndicator';
import YouTubeVideo from 'components/YouTubeVideo';
@@ -21,6 +23,8 @@ import UserDefault from 'assets/images/ico-user-default.svg';
import ReadMoreArrow from 'assets/images/read-more-arrow.svg';
import qs from 'qs';
+const htmlToText = require('html-to-text');
+
// character length for the content preview
const CONTENT_PREVIEW_LENGTH = 110;
// Votes local storage key
@@ -254,7 +258,17 @@ export default class Article extends React.Component {
{
- `${subData.entries.items[rec.sys.id].fields.content.substring(0, CONTENT_PREVIEW_LENGTH)}..`
+ `${htmlToText.fromString(
+ ReactDOMServer.renderToString(markdown(
+ subData.entries.items[rec.sys.id].fields.content,
+ )),
+ {
+ ignoreHref: true,
+ ignoreImage: true,
+ singleNewLineParagraphs: true,
+ uppercaseHeadings: false,
+ },
+ ).substring(0, CONTENT_PREVIEW_LENGTH)}...`
}
{
diff --git a/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx b/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx
index e153e91d5d..59a6e794bd 100644
--- a/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx
+++ b/src/shared/components/Contentful/ArticleCard/ArticleCard.jsx
@@ -12,6 +12,7 @@ import React from 'react';
import { themr } from 'react-css-super-themr';
import { config } from 'topcoder-react-utils';
import markdown from 'utils/markdown';
+import ReactDOMServer from 'react-dom/server';
// SVG assets
import ThumbUpIcon from 'assets/images/ico-thumb-up.svg';
import CommentIcon from 'assets/images/ico-comment.svg';
@@ -21,17 +22,18 @@ import PlayIcon from 'assets/images/ico-play.svg';
import ReadMoreArrow from 'assets/images/read-more-arrow.svg';
import qs from 'qs';
+const htmlToText = require('html-to-text');
+
// date/time format to use for the creation date
const FORMAT = 'MMM DD, YYYY';
// date/time format for 'Forum post' cards is different from others
const FORUM_POST_FORMAT = 'MMM DD, YYYY [at] h:mm A';
// max length for the title of the 'Article small' cards
const ART_SMALL_TITLE_MAX_LENGTH = 29;
-// character length for the content preview
-const CONTENT_PREVIEW_LENGTH = 110;
// Article large card 'breakpoint'
const ARTICLE_LARGE_BREAKPOINT = 473;
-
+// character length for the content preview
+const CONTENT_PREVIEW_LENGTH = 110;
class ArticleCard extends React.Component {
constructor(props) {
@@ -132,10 +134,16 @@ class ArticleCard extends React.Component {
: article.title;
// truncate content for 'Article large' cards
- const content = (
- (themeName === 'Article large' || themeName === 'Recommended')
- && article.content.length > CONTENT_PREVIEW_LENGTH)
- ? markdown(`${article.content.substring(0, CONTENT_PREVIEW_LENGTH)}...`)
+ const content = themeName === 'Article large' || themeName === 'Recommended'
+ ? `${htmlToText.fromString(
+ ReactDOMServer.renderToString(markdown(article.content)),
+ {
+ ignoreHref: true,
+ ignoreImage: true,
+ singleNewLineParagraphs: true,
+ uppercaseHeadings: false,
+ },
+ ).substring(0, CONTENT_PREVIEW_LENGTH)}...`
: undefined;
// set the correct format to apply to the `article.creationDate`
diff --git a/src/shared/components/SubmissionPage/Uploading/styles.scss b/src/shared/components/SubmissionPage/Uploading/styles.scss
index 7dbf48d28d..d39f367a05 100644
--- a/src/shared/components/SubmissionPage/Uploading/styles.scss
+++ b/src/shared/components/SubmissionPage/Uploading/styles.scss
@@ -4,15 +4,14 @@
@include roboto-regular;
align-items: center;
- position: absolute;
width: 100%;
- height: calc(100vh);
background-color: white;
- top: -54px;
left: 0;
z-index: 100000;
display: flex;
justify-content: center;
+ position: relative;
+ padding: 30px;
}
.link,
diff --git a/src/shared/components/SubmissionPage/styles.scss b/src/shared/components/SubmissionPage/styles.scss
index 4fabe9d902..7837315c20 100644
--- a/src/shared/components/SubmissionPage/styles.scss
+++ b/src/shared/components/SubmissionPage/styles.scss
@@ -4,8 +4,8 @@
display: flex;
flex: 1;
justify-content: center;
- padding: 30px 0 40px;
- background: #f6f6f6;
+ padding: 80px 0 40px;
+ background: #fff;
@include md-to-lg {
padding-bottom: 0;
diff --git a/src/shared/components/TopcoderFooter/index.jsx b/src/shared/components/TopcoderFooter/index.jsx
index 70acbf4905..6f97a5c674 100644
--- a/src/shared/components/TopcoderFooter/index.jsx
+++ b/src/shared/components/TopcoderFooter/index.jsx
@@ -89,6 +89,7 @@ export default function TopcoderFooter() {
Contact Us
Join Community
About Community
+ Changelog
Talk to Sales
diff --git a/src/shared/containers/EDU/styles/tabs.scss b/src/shared/containers/EDU/styles/tabs.scss
index 2345eae506..eee81a60b5 100644
--- a/src/shared/containers/EDU/styles/tabs.scss
+++ b/src/shared/containers/EDU/styles/tabs.scss
@@ -41,6 +41,7 @@
border-radius: 9px;
margin-left: 9px;
padding: 0 7px;
+ padding-top: 2px;
}
&.selected {