diff --git a/.travis.yml b/.travis.yml index c9fb8efb4..2f2cde56d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,11 @@ env: - PROJECT_HOST_VAR=PROJECT_HOST_$TRAVIS_BRANCH - PROJECT_HOST=${!PROJECT_HOST_VAR} - PROJECT_HOST=${PROJECT_HOST:-$PROJECT_HOST_STAGING} + - STATIC_HOST_master=https://cdn2.scratch.mit.edu + - STATIC_HOST_STAGING=https://cdn.scratch.ly + - STATIC_HOST_VAR=STATIC_HOST_$TRAVIS_BRANCH + - STATIC_HOST=${!STATIC_HOST_VAR} + - STATIC_HOST=${STATIC_HOST:-$STATIC_HOST_STAGING} - PATH=$PATH:$PWD/test/integration/node_modules/chromedriver/bin - AWS_ACCESS_KEY_ID=$EB_AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY=$EB_AWS_SECRET_ACCESS_KEY @@ -57,6 +62,7 @@ env: - FASTLY_SERVICE_ID=${!FASTLY_SERVICE_ID_VAR} - FASTLY_SERVICE_ID=${FASTLY_SERVICE_ID:-$FASTLY_SERVICE_ID_STAGING} - GA_TRACKER_master=UA-30688952-1 + - GA_TRACKER_STAGING=UA-30688952-7 - GA_TRACKER_VAR=GA_TRACKER_$TRAVIS_BRANCH - GA_TRACKER=${!GA_TRACKER_VAR} - GA_TRACKER=${GA_TRACKER:-$GA_TRACKER_STAGING} @@ -71,11 +77,17 @@ env: - S3_BUCKET_NAME=${!S3_BUCKET_NAME_VAR} - S3_BUCKET_NAME=${S3_BUCKET_NAME:-$S3_BUCKET_NAME_STAGING} - S3_LOCAL_DIR=build - - SENTRY_DSN_master=https://6cf7e15e06b24ba48b727910bd9e6d9e@app.getsentry.com/54913 - - SENTRY_DSN_STAGING=https://7e69dd3d620e434490f07ef0e60613f9@app.getsentry.com/58289 + - SENTRY_DSN_master=https://ebc2f8a6bc7b44ca8fd902fd4f16b3d7@sentry.io/1357122 + - SENTRY_DSN_STAGING=https://c01014988b0a4f44bbefdf235623c456@sentry.io/1357982 - SENTRY_DSN_VAR=SENTRY_DSN_$TRAVIS_BRANCH - SENTRY_DSN=${!SENTRY_DSN_VAR} - SENTRY_DSN=${SENTRY_DSN:-$SENTRY_DSN_STAGING} + - SENTRY_ORG=scratch-foundation + - SENTRY_PROJECT_master=scratch-30-production + - SENTRY_PROJECT_STAGING=scratch-30-staging + - SENTRY_PROJECT_VAR=SENTRY_PROJECT_$TRAVIS_BRANCH + - SENTRY_PROJECT=${!SENTRY_PROJECT_VAR} + - SENTRY_PROJECT=${SENTRY_PROJECT:-$SENTRY_PROJECT_STAGING} - SKIP_CLEANUP=true - NODE_ENV=production - WWW_VERSION=${TRAVIS_COMMIT:0:5} diff --git a/.tx/config b/.tx/config index b61791f04..fe2fc4709 100644 --- a/.tx/config +++ b/.tx/config @@ -20,12 +20,6 @@ source_file = src/views/wedo2/l10n.json source_lang = en type = KEYVALUEJSON -[scratch-website.cards-l10njson] -file_filter = localizations/cards/.json -source_file = src/views/cards/l10n.json -source_lang = en -type = KEYVALUEJSON - [scratch-website.teacherregistration-l10njson] file_filter = localizations/teacherregistration/.json source_file = src/views/teacherregistration/l10n.json @@ -86,12 +80,6 @@ source_file = src/views/splash/l10n.json source_lang = en type = KEYVALUEJSON -[scratch-website.microworlds-homepage-l10njson] -file_filter = localizations/microworlds-homepage/.json -source_file = src/views/microworldshomepage/l10n.json -source_lang = en -type = KEYVALUEJSON - [scratch-website.conference-index-2017-l10njson] file_filter = localizations/conference-index-2017/.json source_file = src/views/conference/2017/index/l10n.json @@ -172,3 +160,18 @@ type = KEYVALUEJSON source_file = src/views/wedo2-legacy/l10n.json source_lang = en type = KEYVALUEJSON + +[scratch-website.parents-l10njson] +source_file = src/views/parents/l10n.json +source_lang = en +type = KEYVALUEJSON + +[scratch-website.scratch_14-l10njson] +source_file = src/views/scratch_1.4/l10n.json +source_lang = en +type = KEYVALUEJSON + +[scratch-website.ideas-l10njson] +source_file = src/views/ideas/l10n.json +source_lang = en +type = KEYVALUEJSON diff --git a/Makefile b/Makefile index f68ed91c9..c033cba7f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ ESLINT=./node_modules/.bin/eslint NODE= NODE_OPTIONS=--max_old_space_size=8000 node -SASSLINT=./node_modules/.bin/sass-lint -v SCRATCH_DOCKER_CONFIG=./node_modules/.bin/docker_config.sh S3CMD=s3cmd sync -P --delete-removed --add-header=Cache-Control:no-cache,public,max-age=3600 TAP=./node_modules/.bin/tap @@ -70,8 +69,6 @@ test: lint: $(ESLINT) . --ext .js,.jsx,.json - $(SASSLINT) ./src/*.scss - $(SASSLINT) ./src/**/*.scss unit: $(TAP) ./test/unit/*.js diff --git a/README.md b/README.md index 57ab0aa11..aaaaf7b91 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Build Status](https://travis-ci.org/LLK/scratch-www.svg)](https://travis-ci.org/LLK/scratch-www) [![Coverage Status](https://coveralls.io/repos/github/LLK/scratch-www/badge.svg?branch=develop)](https://coveralls.io/github/LLK/scratch-www?branch=develop) [![dependencies Status](https://david-dm.org/llk/scratch-www/status.svg)](https://david-dm.org/llk/scratch-www) -[![devDependencies Status](https://david-dm.org/llk/scratch-www/dev-status.svg)](https://david-dm.org/llk/scratch-www?type=dev) +[![devDependencies Status](https://david-dm.org/llk/scratch-www/dev-status.svg)](https://david-dm.org/llk/scratch-www?type=dev) [![Greenkeeper badge](https://badges.greenkeeper.io/LLK/scratch-www.svg)](https://greenkeeper.io/) ### Where am I? Physically? No idea. diff --git a/bin/build-locales b/bin/build-locales index 336c5c8fa..7924a2951 100755 --- a/bin/build-locales +++ b/bin/build-locales @@ -34,7 +34,7 @@ */ var async = require('async'); var fs = require('fs'); -var languages = require('../languages.json'); +const languages = require('scratch-l10n').default; var localizedUrls = require('./lib/localized-urls'); var merge = require('lodash.merge'); var defaults = require('lodash.defaults'); diff --git a/bin/configure-fastly.js b/bin/configure-fastly.js index 74399a0d3..631c79a93 100644 --- a/bin/configure-fastly.js +++ b/bin/configure-fastly.js @@ -1,7 +1,7 @@ var async = require('async'); var defaults = require('lodash.defaults'); var fastlyConfig = require('./lib/fastly-config-methods'); -const languages = require('../languages.json'); +const languages = require('scratch-l10n').default; var routeJson = require('../src/routes.json'); @@ -171,6 +171,98 @@ async.auto({ if (err) return cb(err); cb(null, headers); }); + }], + tipbarRedirectHeaders: ['version', function (cb, results) { + async.auto({ + requestCondition: function (cb2) { + var condition = { + name: 'routes/?tip_bar= (request)', + statement: 'req.url ~ "\\?tip_bar="', + type: 'REQUEST', + priority: 10 + }; + fastly.setCondition(results.version, condition, cb2); + }, + responseCondition: function (cb2) { + var condition = { + name: 'routes/?tip_bar= (response)', + statement: 'req.url ~ "\\?tip_bar="', + type: 'RESPONSE', + priority: 10 + }; + fastly.setCondition(results.version, condition, cb2); + }, + responseObject: ['requestCondition', function (cb2, redirectResults) { + var responseObject = { + name: 'redirects/?tip_bar=', + status: 301, + response: 'Moved Permanently', + request_condition: redirectResults.requestCondition.name + }; + fastly.setResponseObject(results.version, responseObject, cb2); + }], + redirectHeader: ['responseCondition', function (cb2, redirectResults) { + var header = { + name: 'redirects/?tip_bar=', + action: 'set', + ignore_if_set: 0, + type: 'RESPONSE', + dst: 'http.Location', + src: 'regsub(req.url, "tip_bar=", "tutorial=")', + response_condition: redirectResults.responseCondition.name + }; + fastly.setFastlyHeader(results.version, header, cb2); + }] + }, function (err, redirectResults) { + if (err) return cb(err); + cb(null, redirectResults); + }); + }], + embedRedirectHeaders: ['version', function (cb, results) { + async.auto({ + requestCondition: function (cb2) { + var condition = { + name: 'routes/projects/embed (request)', + statement: 'req.url ~ "^/projects/embed/(\\d+)"', + type: 'REQUEST', + priority: 10 + }; + fastly.setCondition(results.version, condition, cb2); + }, + responseCondition: function (cb2) { + var condition = { + name: 'routes/projects/embed (response)', + statement: 'req.url ~ "^/projects/embed/(\\d+)"', + type: 'RESPONSE', + priority: 10 + }; + fastly.setCondition(results.version, condition, cb2); + }, + responseObject: ['requestCondition', function (cb2, redirectResults) { + var responseObject = { + name: 'redirects/projects/embed', + status: 301, + response: 'Moved Permanently', + request_condition: redirectResults.requestCondition.name + }; + fastly.setResponseObject(results.version, responseObject, cb2); + }], + redirectHeader: ['responseCondition', function (cb2, redirectResults) { + var header = { + name: 'redirects/projects/embed', + action: 'set', + ignore_if_set: 0, + type: 'RESPONSE', + dst: 'http.Location', + src: '"/projects/" + re.group.1 + "/embed"', + response_condition: redirectResults.responseCondition.name + }; + fastly.setFastlyHeader(results.version, header, cb2); + }] + }, function (err, redirectResults) { + if (err) return cb(err); + cb(null, redirectResults); + }); }] }, function (err, results) { if (err) throw new Error(err); diff --git a/bin/import-pootle b/bin/import-pootle deleted file mode 100755 index 9951ce0c1..000000000 --- a/bin/import-pootle +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env node - /* - Generate language json files corresponding to l10n files to import from - pootle into transifex. - - For example, extract the strings corresponding to splash ids from pootle. - For each language, in localizations/splash create - fr.json => - { - 'splash.welcome' : 'Bienvenue', - ... - } - etc. - */ - -var fs = require('fs'); -var path = require('path'); -var routes = require('../src/routes.json'); -var languages = require('../languages.json'); -var localeCompare = require('./lib/locale-compare'); - -var outputDir = path.resolve(__dirname, '../localizations'); -try { - fs.accessSync(outputDir, fs.F_OK); -} catch (err) { - // Doesn't exist - create it. - fs.mkdirSync(outputDir); -} - -// general is a special case, do it first -var l10n = path.resolve(__dirname, '../src/l10n.json'); -localeCompare.writeTranslations('general', l10n, languages); - -for (var v in routes) { - if (typeof routes[v].redirect !== 'undefined') { - continue; - } - var subdir = routes[v].view.split('/'); - subdir.pop(); - l10n = path.resolve(__dirname, '../src/views/' + subdir.join('/') + '/l10n.json'); - var name = routes[v].name; - try { - // only import if there is an l10n file - fs.accessSync(l10n); - } catch (err) { - // skip views without l10n files - process.stdout.write(`Skipping ${name}, no l10n\n`); - continue; - } - localeCompare.writeTranslations(name, l10n, languages); -} diff --git a/languages.json b/languages.json deleted file mode 100644 index 510c29c69..000000000 --- a/languages.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "ab": "Аҧсшәа", - "ar": "العربية", - "an": "Aragonés", - "ast": "Asturianu", - "id": "Bahasa Indonesia", - "ms": "Bahasa Melayu", - "be": "Беларуская", - "bg": "Български", - "ca": "Català", - "cs": "Česky", - "cy": "Cymraeg", - "da": "Dansk", - "de": "Deutsch", - "yum": "Edible Scratch", - "et": "Eesti", - "el": "Ελληνικά", - "en": "English", - "eo": "Esperanto", - "es": "Español", - "eu": "Euskara", - "fa": "فارسی", - "fr": "Français", - "fur": "Furlan", - "ga": "Gaeilge", - "gd": "Gàidhlig", - "gl": "Galego", - "ko": "한국어", - "hy": "Հայերեն", - "he": "עִבְרִית", - "hi": "हिन्दी", - "hr": "Hrvatski", - "zu": "isiZulu", - "is": "Íslenska", - "it": "Italiano", - "kn": "ಭಾಷೆ-ಹೆಸರು", - "kk": "Қазақша", - "rw": "Kinyarwanda", - "ht": "Kreyòl", - "ku": "Kurdî", - "la": "Latina", - "lv": "Latviešu", - "lt": "Lietuvių", - "mk": "Македонски", - "hu": "Magyar", - "ml": "മലയാളം", - "mt": "Malti", - "cat": "Meow", - "mr": "मराठी", - "mn": "Монгол хэл", - "my": "မြန်မာဘာသာ", - "nl": "Nederlands", - "ja": "日本語", - "nb": "Norsk Bokmål", - "nn": "Norsk Nynorsk", - "uz": "Oʻzbekcha", - "th": "ไทย", - "pl": "Polski", - "pt": "Português", - "pt-br": "Português Brasileiro", - "ro": "Română", - "ru": "Русский", - "sc": "Sardu", - "sq": "Shqip", - "sk": "Slovenčina", - "sl": "Slovenščina", - "sr": "Српски", - "fi": "Suomi", - "sv": "Svenska", - "te": "తెలుగు", - "vi": "Tiếng Việt", - "tr": "Türkçe", - "uk": "Українська", - "zh-cn": "简体中文", - "zh-tw": "繁體中文" -} diff --git a/package.json b/package.json index 3f52c53fc..47430a197 100644 --- a/package.json +++ b/package.json @@ -24,20 +24,21 @@ }, "homepage": "https://github.com/llk/scratch-www#readme", "dependencies": { + "@sentry/browser": "4.4.2", "bunyan": "1.7.1", + "clipboard-copy": "2.0.1", "compression": "1.6.1", "express": "4.16.1", "express-http-proxy": "1.1.0", "lodash.defaults": "4.0.1", "newrelic": "1.25.4", - "raven": "0.10.0", + "react-helmet": "5.2.0", "scratch-docker": "^1.0.2", "scratch-parser": "^4.2.0", "scratch-storage": "^0.5.1" }, "devDependencies": { "ajv": "6.4.0", - "approximate-number": "2.0.0", "async": "1.5.2", "autoprefixer": "6.3.6", "babel-cli": "6.26.0", @@ -47,6 +48,7 @@ "babel-plugin-transform-object-rest-spread": "6.26.0", "babel-preset-es2015": "6.22.0", "babel-preset-react": "6.22.0", + "bowser": "1.9.4", "cheerio": "1.0.0-rc.2", "classnames": "2.2.5", "cookie": "0.2.2", @@ -85,7 +87,6 @@ "po2icu": "0.0.2", "postcss-loader": "2.0.10", "prop-types": "15.6.0", - "raven-js": "3.0.4", "react": "16.2.0", "react-dom": "16.2.0", "react-intl": "2.4.0", @@ -98,9 +99,9 @@ "react-telephone-input": "4.3.4", "redux": "3.5.2", "redux-thunk": "2.0.1", - "sass-lint": "1.5.1", "sass-loader": "6.0.6", - "scratch-gui": "latest", + "scratch-gui": "0.1.0-prerelease.20181227160059", + "scratch-l10n": "latest", "scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master", "slick-carousel": "1.6.0", "source-map-support": "0.3.2", diff --git a/src/_colors.scss b/src/_colors.scss index 33c5560c3..a963d81d2 100644 --- a/src/_colors.scss +++ b/src/_colors.scss @@ -5,9 +5,16 @@ $ui-blue-10percent: hsla(215, 100, 65, .1); $ui-blue-25percent: hsla(215, 100, 65, .25); $ui-orange: hsla(38, 100, 55, 1); // #FFAB19 Control Primary +$ui-orange-high-contrast: hsla(30, 100, 55, 1); // #FFAB19 Control Primary $ui-orange-10percent: hsla(35, 90, 55, .1); $ui-orange-25percent: hsla(35, 90, 55, .25); +$ui-dark-orange: hsla(30, 100, 55, 1); // ##FF8C1A Variables Primary + +$ui-red: hsla(20, 100%, 55%, 1); /* #FF661A */ +$ui-red-25percent: hsla(20, 100%, 55%, .25); +$ui-green-35percent: rgba(126, 225, 195, .35); + $ui-light-gray: hsla(0, 0, 98, 1); //#FAFAFA $ui-gray: hsla(0, 0, 95, 1); //#F2F2F2 $ui-dark-gray: hsla(0, 0, 70, 1); //#B3B3B3 @@ -40,6 +47,7 @@ $ui-light-mint: hsl(163, 53, 67); $active-gray: hsla(0, 0, 0, .1); $active-dark-gray: hsla(0, 0, 0, .2); $box-shadow-gray: hsla(0, 0, 0, .25); +$box-shadow-light-gray: hsla(0, 0, 0, .15); $overlay-gray: hsla(0, 0, 0, .75); $transparent-light-blue: rgba(229, 240, 254, 0); diff --git a/src/components/adminpanel/adminpanel.jsx b/src/components/adminpanel/adminpanel.jsx index 4d9ac9e79..926a7a4f5 100644 --- a/src/components/adminpanel/adminpanel.jsx +++ b/src/components/adminpanel/adminpanel.jsx @@ -1,100 +1,49 @@ -const bindAll = require('lodash.bindall'); -const connect = require('react-redux').connect; +const classNames = require('classnames'); const PropTypes = require('prop-types'); const React = require('react'); -const Button = require('../forms/button.jsx'); - require('./adminpanel.scss'); -class AdminPanel extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleToggleVisibility' - ]); - this.state = { - showPanel: false - }; - } - handleToggleVisibility (e) { - e.preventDefault(); - this.setState({showPanel: !this.state.showPanel}); - } - render () { - if (!this.props.isAdmin) return false; - - if (this.state.showPanel) { - return ( -
- - x - -
-

Admin Panel

-
-
-
- {this.props.children} -
Page Cache
-
-
    -
  • -
    - -
    - For anonymous users: - -
    -
    -
  • -
-
-
-
-
- ); - } - return ( -
+const AdminPanel = ({ + className, + children, + isOpen, + onOpen, + onClose +}) => ( +
+ {isOpen ? ( + - > + x -
- ); - } -} +
+

Admin Panel

+
+
+ {children} +
+ + ) : ( + + > + + )} +
+); AdminPanel.propTypes = { children: PropTypes.node, - isAdmin: PropTypes.bool + className: PropTypes.string, + isOpen: PropTypes.bool, + onClose: PropTypes.func, + onOpen: PropTypes.func }; -const mapStateToProps = state => ({ - isAdmin: state.permissions.admin -}); - -const ConnectedAdminPanel = connect(mapStateToProps)(AdminPanel); - -module.exports = ConnectedAdminPanel; +module.exports = AdminPanel; diff --git a/src/components/adminpanel/adminpanel.scss b/src/components/adminpanel/adminpanel.scss index 6b8c36907..6ca7361bb 100644 --- a/src/components/adminpanel/adminpanel.scss +++ b/src/components/adminpanel/adminpanel.scss @@ -1,6 +1,6 @@ @import "../../colors"; -#admin-panel { +.admin-panel { position: fixed; top: 0; left: 0; @@ -9,16 +9,11 @@ box-shadow: 0 2px 5px $box-shadow-gray; background-color: $ui-gray; padding: 1rem; + width: 230px; height: 100%; overflow: scroll; text-shadow: none; - &.visible { - width: 20%; - min-width: 180px; - max-width: 230px; - } - &.hidden { width: 10px; } @@ -28,33 +23,6 @@ cursor: pointer; } - .admin-content { - dl { - list-style: none; - - dt { - margin: 2rem 0 1rem 0; - border-bottom: 1px solid $ui-dark-gray; - font-size: large; - font-weight: 700; - } - - dd { - margin-left: 0; - } - } - - ul { - padding: 0; - - li { - margin: 0; - list-style: none; - - } - } - } - .button-row { display: flex; font-size: small; diff --git a/src/components/crashmessage/crashmessage.jsx b/src/components/crashmessage/crashmessage.jsx index d66a3c610..50fc181fb 100644 --- a/src/components/crashmessage/crashmessage.jsx +++ b/src/components/crashmessage/crashmessage.jsx @@ -20,6 +20,16 @@ const CrashMessage = props => (

+ {props.eventId && ( +

+ +

+ )}