Skip to content

Commit 2904f64

Browse files
Merge pull request #6380 from topcoder-platform/develop
Release v1.17.3
2 parents ed864e3 + a4123f9 commit 2904f64

File tree

9 files changed

+169
-53
lines changed

9 files changed

+169
-53
lines changed

.circleci/config.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,14 +349,14 @@ workflows:
349349
filters:
350350
branches:
351351
only:
352-
- reskin
352+
- develop
353353
# This is alternate dev env for parallel testing
354354
- "build-test":
355355
context : org-global
356356
filters:
357357
branches:
358358
only:
359-
- circleci-fix
359+
- debug-CMS-error
360360
# This is alternate dev env for parallel testing
361361
- "build-qa":
362362
context : org-global
@@ -370,7 +370,6 @@ workflows:
370370
filters:
371371
branches:
372372
only:
373-
- footer-update
374373
- reskin
375374
# This is stage env for production QA releases
376375
- "build-prod-staging":
@@ -379,7 +378,7 @@ workflows:
379378
branches:
380379
only:
381380
- develop
382-
- stat_marathon_match_link
381+
- fix-security-headers
383382
# Production builds are exectuted
384383
# when PR is merged to the master
385384
# Don't change anything in this configuration

src/server/index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ ts = moment(ts.timestamp).valueOf();
5050
const sw = `sw.js${process.env.NODE_ENV === 'production' ? '' : '?debug'}`;
5151
const swScope = '/challenges'; // we are currently only interested in improving challenges pages
5252

53+
const tcoPattern = new RegExp(/^tco\d{2}\.topcoder(?:-dev)?\.com$/i);
54+
5355
const EXTRA_SCRIPTS = [
5456
`<script type="application/javascript">
5557
if('serviceWorker' in navigator){
@@ -135,13 +137,14 @@ async function onExpressJsSetup(server) {
135137
res.header('Referrer-Policy', 'strict-origin-when-cross-origin');
136138
res.header('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
137139

138-
if (req.url.startsWith('/__community__/veterans') || req.hostname === 'veterans.topcoder.com') {
140+
if (req.url.startsWith('/__community__/veterans') || req.hostname === 'veterans.topcoder.com' || req.url.startsWith('/__community__/tco') || tcoPattern.test(req.hostname)) {
139141
res.header(
140142
'Content-Security-Policy',
141143
"default-src 'self';"
142144
+ " script-src 'report-sample' 'self' 'unsafe-inline' 'unsafe-eval'"
143145
+ ` ${config.CDN.PUBLIC}`
144146
+ ' http://www.google-analytics.com'
147+
+ ' https://www.google-analytics.com'
145148
+ ' https://43d132d5dbff47c59d9d53ad448f93c2.js.ubembed.com'
146149
+ ' https://assets.ubembed.com'
147150
+ ' https://assets.zendesk.com'
@@ -187,6 +190,7 @@ async function onExpressJsSetup(server) {
187190
+ ` ${config.URL.AUTH}`
188191
+ ' https://www.youtube.com;'
189192
+ " img-src 'self'"
193+
+ ' data:'
190194
+ ` ${config.CDN.PUBLIC}`
191195
+ ' https://cdn.segment.com'
192196
+ ' https://d1of0acg2orgco.cloudfront.net'
@@ -196,11 +200,14 @@ async function onExpressJsSetup(server) {
196200
+ ' https://heapanalytics.com'
197201
+ ' https://q.quora.com'
198202
+ ' https://topcoder-prod-media.s3.amazonaws.com'
203+
+ ' https://topcoder-dev-media.s3.amazonaws.com'
199204
+ ' https://www.facebook.com'
200205
+ ' https://www.google-analytics.com'
201206
+ ' https://www.google.com'
202207
+ ' https://www.googletagmanager.com'
203-
+ ' https://i.ytimg.com;'
208+
+ ' https://i.ytimg.com'
209+
+ ' https://images.contentful.com'
210+
+ ' https://d0.awsstatic.com/logos/;'
204211
+ " manifest-src 'self';"
205212
+ " media-src 'self';"
206213
+ ' report-uri https://623d4c23f90d055298b24042.endpoint.csper.io/?v=0;'

src/shared/actions/contentful.js

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getService } from 'services/contentful';
33
import { config, redux } from 'topcoder-react-utils';
44
import { removeTrailingSlash } from 'utils/url';
55
import { menuItemBuilder, target as urlTarget } from 'utils/contentful';
6-
import { services } from 'topcoder-react-lib';
6+
import { services, logger } from 'topcoder-react-lib';
77

88
const ERRMSG_UNKNOWN_TARGET = 'Unknown action target';
99

@@ -91,15 +91,19 @@ async function getContentDone(operationId, contentId, target, preview, spaceName
9191
environment,
9292
});
9393
let content;
94-
switch (target) {
95-
case TARGETS.ASSETS:
96-
content = await service.getAsset(contentId);
97-
break;
98-
case TARGETS.ENTRIES:
99-
content = await service.getEntry(contentId);
100-
break;
101-
default:
102-
throw new Error(ERRMSG_UNKNOWN_TARGET);
94+
try {
95+
switch (target) {
96+
case TARGETS.ASSETS:
97+
content = await service.getAsset(contentId);
98+
break;
99+
case TARGETS.ENTRIES:
100+
content = await service.getEntry(contentId);
101+
break;
102+
default:
103+
throw new Error(ERRMSG_UNKNOWN_TARGET);
104+
}
105+
} catch (e) {
106+
logger.error('getContentDone error', e);
103107
}
104108

105109
return {
@@ -141,15 +145,19 @@ async function queryContentDone(operationId, queryId, target,
141145
environment,
142146
});
143147
let data;
144-
switch (target) {
145-
case TARGETS.ASSETS:
146-
data = await service.queryAssets(query);
147-
break;
148-
case TARGETS.ENTRIES:
149-
data = await service.queryEntries(query);
150-
break;
151-
default:
152-
throw new Error(ERRMSG_UNKNOWN_TARGET);
148+
try {
149+
switch (target) {
150+
case TARGETS.ASSETS:
151+
data = await service.queryAssets(query);
152+
break;
153+
case TARGETS.ENTRIES:
154+
data = await service.queryEntries(query);
155+
break;
156+
default:
157+
throw new Error(ERRMSG_UNKNOWN_TARGET);
158+
}
159+
} catch (e) {
160+
logger.error('queryContentDone error', e);
153161
}
154162

155163
return {

src/shared/components/Contentful/Route.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ function ChildRoutesLoader(props) {
4747
<React.Fragment>
4848
<MetaTags
4949
description={fields.description}
50-
image={fields.thumbnail}
50+
image={fields.socialThumbnail}
5151
siteName={fields.socialSiteName}
5252
socialDescription={fields.socialDescription}
5353
socialTitle={fields.socialTitle}

src/shared/components/MetaTags.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ function MetaTags({
2424
feed,
2525
feedTitle,
2626
}) {
27-
const img = `${domain}${image}`;
27+
let img = `${domain}${image}`;
28+
if (image && (image.indexOf('http://') === 0 || image.indexOf('https://') === 0)) {
29+
img = `${image}`;
30+
}
2831
const socTitle = socialTitle || title;
2932
const socDesc = socialDescription || description;
3033
return (

src/shared/components/challenge-detail/MMDashboard/Graph/index.jsx

Lines changed: 73 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
/* eslint-disable react/no-this-in-sfc */
2-
import React from 'react';
2+
import React, { useState } from 'react';
33
import Highcharts from 'highcharts';
44
import HighchartsReact from 'highcharts-react-official';
55
import PT from 'prop-types';
66
import moment from 'moment';
7-
import { getRatingColor } from 'utils/tc';
7+
import { getHighlightedColor, getRatingColor, getUnSelectedColors } from 'utils/tc';
88
import DefaultUserImage from 'assets/images/ico-user-default.png';
99

10-
import './styles.scss';
1110
import _ from 'lodash';
11+
import styles from './styles.scss';
1212

1313
export default function Graph({ statisticsData, baseline, awardLine }) {
14+
const [point, setPoint] = useState(null);
15+
1416
const flatData = [];
1517
const dates = [];
1618
_.each(statisticsData, (entry) => {
@@ -24,28 +26,71 @@ export default function Graph({ statisticsData, baseline, awardLine }) {
2426
});
2527
});
2628

29+
const pointDatas = _.map(flatData, (data) => {
30+
let color;
31+
let isSelected = false;
32+
if (point) {
33+
isSelected = point.customData.handle === data.handle;
34+
if (isSelected) {
35+
color = getHighlightedColor(data.rating || 0);
36+
} else {
37+
color = getUnSelectedColors(data.rating || 0);
38+
}
39+
} else {
40+
color = data.ratingColor || getRatingColor(data.rating || 0);
41+
}
42+
return {
43+
x: moment(data.created).valueOf(),
44+
y: _.max([0, data.score ? (parseFloat(data.score)) : 0]),
45+
name: data.handle,
46+
color,
47+
customData: data,
48+
marker: {
49+
enabled: true,
50+
width: 'circle',
51+
radius: isSelected ? 6 : 4,
52+
},
53+
className: !isSelected && point ? styles.selectedPoint : '',
54+
};
55+
});
56+
2757
const options = {
58+
plotOptions: {
59+
line: {
60+
events: {
61+
click() {
62+
this.group.toFront();
63+
},
64+
},
65+
},
66+
},
2867
chart: {
2968
type: 'scatter',
3069
backgroundColor: '#fff',
70+
events: {
71+
click: () => {
72+
setPoint(null);
73+
},
74+
},
3175
},
3276
title: {
3377
text: '',
3478
},
3579
series: [
3680
{
37-
data: _.map(flatData, data => ({
38-
x: moment(data.created).valueOf(),
39-
y: _.max([0, data.score ? (parseFloat(data.score)) : 0]),
40-
name: data.handle,
41-
color: data.ratingColor
42-
|| getRatingColor(data.rating
43-
|| 0),
44-
customData: data,
45-
})),
81+
data: pointDatas,
4682
pointStart: moment(_.min(dates)).valueOf(),
4783
pointInterval: 24 * 3600 * 1000,
4884
backgroundColor: 'rgb(51,51,51)',
85+
point: {
86+
events: {
87+
click: (e) => {
88+
if (e && e.point) {
89+
setPoint(e.point);
90+
}
91+
},
92+
},
93+
},
4994
},
5095
],
5196
legend: {
@@ -106,23 +151,27 @@ export default function Graph({ statisticsData, baseline, awardLine }) {
106151
},
107152
tooltip: {
108153
formatter() {
109-
const str = `
110-
<div style="border-radius:4px; padding-top: 15px; padding-left: 10px;">
111-
<img height="30" width="30" src="${this.point.customData.photoUrl || DefaultUserImage}" style="position: absolute; border-radius: 50%;" />
112-
<p style="margin-left: 50px">${this.point.customData.handle}</p>
113-
<br />
114-
<p style="margin-left: 50px;">${this.point.customData.submissionCount} submissions</p>
115-
<p style="margin-left: 50px;">Score: ${this.y}</p>
116-
<p style="margin-left: 50px;">Submitted: ${moment(this.point.customData.created).format('MM/DD/YYYY')} </p>
117-
</div>
118-
`;
119-
return str;
154+
const currentPointer = this.point || point;
155+
if (currentPointer) {
156+
const str = `
157+
<div style="border-radius:4px; padding-top: 15px; padding-left: 10px;">
158+
<img height="30" width="30" src="${currentPointer.customData.photoUrl || DefaultUserImage}" style="position: absolute; border-radius: 50%;" />
159+
<p style="margin-left: 50px">${currentPointer.customData.handle}</p>
160+
<br />
161+
<p style="margin-left: 50px;">${currentPointer.customData.submissionCount} submissions</p>
162+
<p style="margin-left: 50px;">Score: ${this.y}</p>
163+
<p style="margin-left: 50px;">Submitted: ${moment(currentPointer.customData.created).format('MM/DD/YYYY')} </p>
164+
</div>
165+
`;
166+
return str;
167+
}
168+
return false;
120169
},
121170
useHTML: true,
122171
style: {
123172
color: '#fff',
124173
},
125-
backgroundColor: 'rgb(51,51,51)',
174+
backgroundColor: point ? '#2e2e2e' : 'rgb(51,51,51)',
126175
shared: true,
127176
},
128177
};

src/shared/components/challenge-detail/MMDashboard/Graph/styles.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@
2121
padding-right: 16px;
2222
}
2323
}
24+
25+
.selectedPoint {
26+
z-index: 1000 !important;
27+
opacity: 0.9;
28+
}

src/shared/reducers/contentful/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ function create(init) {
6060
const spaceName = _.get(payload, 'spaceName') || config.CONTENTFUL.DEFAULT_SPACE_NAME;
6161
const environment = _.get(payload, 'environment') || config.CONTENTFUL.DEFAULT_ENVIRONMENT;
6262
const res = _.get(newState, `${spaceName}.${environment}`);
63-
if (error || !res) {
64-
logger.log('CMS-related error');
63+
if ((error && payload && payload.sys && payload.sys.id !== 'NotFound') || !res) {
64+
logger.log('CMS-related error', JSON.stringify(action), config.CONTENTFUL.DEFAULT_SPACE_NAME, config.CONTENTFUL.DEFAULT_ENVIRONMENT, JSON.stringify(res), JSON.stringify(newState));
6565
return state;
6666
}
6767
const st = state[spaceName][environment];

src/shared/utils/tc.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,57 @@ export const RATING_COLORS = [{
122122
color: '#EF3A3A' /* Red */,
123123
limit: Infinity,
124124
}];
125+
125126
export function getRatingColor(rating) {
126127
let i = 0; const r = Number(rating);
127128
while (RATING_COLORS[i].limit <= r) i += 1;
128129
return RATING_COLORS[i].color || 'black';
129130
}
130131

132+
export const HIGHLIGHTED_RATING_COLORS = [{
133+
color: '#464646' /* Grey */,
134+
limit: 900,
135+
}, {
136+
color: '#00ab00' /* Green */,
137+
limit: 1200,
138+
}, {
139+
color: '#3748ff' /* Blue */,
140+
limit: 1500,
141+
}, {
142+
color: '#ffe879' /* Yellow */,
143+
limit: 2200,
144+
}, {
145+
color: '#ff2a2a' /* Red */,
146+
limit: Infinity,
147+
}];
148+
export function getHighlightedColor(rating) {
149+
let i = 0; const r = Number(rating);
150+
while (HIGHLIGHTED_RATING_COLORS[i].limit <= r) i += 1;
151+
return HIGHLIGHTED_RATING_COLORS[i].color || 'black';
152+
}
153+
154+
export const UNSELECTED_RATING_COLORS = [{
155+
color: '#f3f3f3' /* Grey */,
156+
limit: 900,
157+
}, {
158+
color: '#e6f6e6' /* Green */,
159+
limit: 1200,
160+
}, {
161+
color: '#c5c8ef' /* Blue */,
162+
limit: 1500,
163+
}, {
164+
color: '#fff5c4' /* Yellow */,
165+
limit: 2200,
166+
}, {
167+
color: '#f47d7d' /* Red */,
168+
limit: Infinity,
169+
}];
170+
export function getUnSelectedColors(rating) {
171+
let i = 0; const r = Number(rating);
172+
while (UNSELECTED_RATING_COLORS[i].limit <= r) i += 1;
173+
return UNSELECTED_RATING_COLORS[i].color || 'black';
174+
}
175+
131176
/**
132177
* Given ExpressJS HTTP request it extracts Topcoder auth tokens from cookies,
133178
* if they are present there and are not expired.

0 commit comments

Comments
 (0)