Skip to content

Commit

Permalink
Merge pull request #22972 from code-dot-org/dtp_candidate_2c9db32f
Browse files Browse the repository at this point in the history
DTP (Test > Production: 2c9db32)
  • Loading branch information
davidsbailey committed Jun 7, 2018
2 parents 5bc5c52 + c512218 commit cc29071
Show file tree
Hide file tree
Showing 724 changed files with 4,743 additions and 1,125 deletions.
1 change: 1 addition & 0 deletions apps/Gruntfile.js
Expand Up @@ -437,6 +437,7 @@ describe('entry tests', () => {
'levelbuilder_studio': './src/sites/studio/pages/levelbuilder_studio.js',
'levelbuilder_pixelation': './src/sites/studio/pages/levelbuilder_pixelation.js',
'blocks/edit': './src/sites/studio/pages/blocks/edit.js',
'shared_blockly_functions/edit':'./src/sites/studio/pages/shared_blockly_functions/edit.js',
'levels/contract_match': './src/sites/studio/pages/levels/contract_match.jsx',
'levels/_curriculum_reference': './src/sites/studio/pages/levels/_curriculum_reference.js',
'levels/_dialog': './src/sites/studio/pages/levels/_dialog.js',
Expand Down
6 changes: 4 additions & 2 deletions apps/i18n/common/en_us.json
Expand Up @@ -1008,8 +1008,10 @@
"shareSettingDisableDialogNote": "Note that students will still be able to share projects created using the programming tools designed for younger students like Play Lab and Artist. These tools limit what students can create and do not allow for uploading any of their own assets. To protect students’ privacy, shared creations in the project gallery are labeled only with the first letter of a student’s name and an age range.",
"shareSettingMoreDetailsTooltip": "App Lab / Game Lab / Web Lab sharing enabled?",
"shareWarningsAge": "Please provide your age below and click OK to continue.",
"shareWarningsMoreInfo": "Our Privacy Policy",
"shareWarningsStoreData": "This app built on Code Studio stores data that could be viewed by anyone with this sharing link, so avoid providing personal information about yourself or others.",
"shareWarningsMoreInfo": "Code.org Privacy Policy",
"shareWarningsStoreDataBeforeHighlight": "This app built on Code Studio stores data that can be viewed by anyone with access to this project, so ",
"shareWarningsStoreDataHighlight": "avoid providing personal information about yourself or others",
"shareWarningsStoreDataAfterHighlight": ".",
"sharingAgePrompt": "Please select an age.",
"sharingBlockedByTeacher": "Sorry, you do not have permissions to share this project. If you want to be able to share your project, please ask your teacher to enable sharing of App Lab / Game Lab / Web Lab projects for your section from the 'Manage students' tab in their dashboard. They can do this by adding the project sharing column from the Actions settings menu.",
"show": "Show",
Expand Down
1 change: 1 addition & 0 deletions apps/package.json
Expand Up @@ -89,6 +89,7 @@
"css-loader": "^0.23.1",
"csv-parse": "^1.1.7",
"data-collection": "^1.1.6",
"dedent": "^0.7.0",
"ejs": "0.8.4",
"ejs-compiled-loader": "2.1.1",
"enzyme": "2.8.0",
Expand Down
74 changes: 61 additions & 13 deletions apps/src/applab/Exporter.js
Expand Up @@ -14,10 +14,12 @@ import exportFontAwesomeCssEjs from '../templates/export/fontAwesome.css.ejs';
import exportExpoIndexEjs from '../templates/export/expo/index.html.ejs';
import exportExpoPackageJson from '../templates/export/expo/package.exported_json';
import exportExpoAppJsonEjs from '../templates/export/expo/app.json.ejs';
import exportExpoAppJs from '../templates/export/expo/App.exported_js';
import exportExpoAppEjs from '../templates/export/expo/App.js.ejs';
import exportExpoCustomAssetJs from '../templates/export/expo/CustomAsset.exported_js';
import exportExpoDataWarningJs from '../templates/export/expo/DataWarning.exported_js';
import exportExpoPackagedFilesEjs from '../templates/export/expo/packagedFiles.js.ejs';
import exportExpoPackagedFilesEntryEjs from '../templates/export/expo/packagedFilesEntry.js.ejs';
import exportExpoWarningPng from '../templates/export/expo/warning.png';
import exportExpoIconPng from '../templates/export/expo/icon.png';
import exportExpoSplashPng from '../templates/export/expo/splash.png';
import logToCloud from '../logToCloud';
Expand Down Expand Up @@ -139,7 +141,7 @@ const APP_OPTIONS_OVERRIDES = {
readonlyWorkspace: true,
};

export function getAppOptionsFile() {
export function getAppOptionsFile(expoMode) {
function getAppOptionsAtPath(whitelist, sourceOptions) {
if (!whitelist || !sourceOptions) {
return null;
Expand All @@ -155,6 +157,14 @@ export function getAppOptionsFile() {
}
const options = getAppOptionsAtPath(APP_OPTIONS_WHITELIST, getAppOptions());
_.merge(options, APP_OPTIONS_OVERRIDES);
options.nativeExport = expoMode;
if (!expoMode) {
// call non-whitelisted hasDataAPIs() function and persist as a bool in
// the exported options:
const { shareWarningInfo = {} } = getAppOptions();
const { hasDataAPIs } = shareWarningInfo;
options.exportUsesDataAPIs = hasDataAPIs && hasDataAPIs();
}
return `window.APP_OPTIONS = ${JSON.stringify(options)};`;
}

Expand Down Expand Up @@ -230,14 +240,21 @@ function extractCSSFromHTML(el) {
const fontAwesomeWOFFRelativeSourcePath = '/fonts/fontawesome-webfont.woff2';
const fontAwesomeWOFFPath = 'applab/fontawesome-webfont.woff2';

function getExportConfigPath() {
const baseHref = project.getShareUrl();
return `${baseHref}/export_config?script_call=setExportConfig`;
}

export default {
exportAppToZip(appName, code, levelHtml, expoMode) {
const { css, outerHTML } = transformLevelHtml(levelHtml);

const exportConfigPath = getExportConfigPath();
const jQueryBaseName = 'jquery-1.12.1.min';
var html;
if (expoMode) {
html = exportExpoIndexEjs({
exportConfigPath,
htmlBody: outerHTML,
applabApiPath: "applab-api.j",
jQueryPath: jQueryBaseName + ".j",
Expand All @@ -250,6 +267,7 @@ export default {
});
} else {
html = exportProjectEjs({
exportConfigPath,
htmlBody: outerHTML,
fontPath: fontAwesomeWOFFPath,
});
Expand Down Expand Up @@ -287,6 +305,11 @@ export default {
});

if (expoMode) {
appAssets.push({
url: exportExpoWarningPng,
zipPath: appName + '/appassets/warning.png',
dataType: 'binary',
});
appAssets.push({
url: exportExpoIconPng,
zipPath: appName + '/appassets/icon.png',
Expand All @@ -307,11 +330,17 @@ export default {
appName: appName,
projectId: project.getCurrentId()
});
const { shareWarningInfo = {} } = getAppOptions();
const { hasDataAPIs } = shareWarningInfo;
const appJs = exportExpoAppEjs({
hasDataAPIs: hasDataAPIs && hasDataAPIs(),
});

zip.file(appName + "/package.json", exportExpoPackageJson);
zip.file(appName + "/app.json", appJson);
zip.file(appName + "/App.js", exportExpoAppJs);
zip.file(appName + "/App.js", appJs);
zip.file(appName + "/CustomAsset.js", exportExpoCustomAssetJs);
zip.file(appName + "/DataWarning.js", exportExpoDataWarningJs);
}
// NOTE: for expoMode, it is important that index.html comes first...
zip.file(mainProjectFilesPrefix + "index.html", rewriteAssetUrls(appAssets, html));
Expand Down Expand Up @@ -339,7 +368,7 @@ export default {
))).then(
([applabApi], [commonLocale], [applabLocale], [applabCSS], [commonCSS], [fontAwesomeWOFF], ...rest) => {
zip.file(appName + "/" + (expoMode ? "assets/applab-api.j" : rootApplabPrefix + "applab-api.js"),
[getAppOptionsFile(), commonLocale, applabLocale, applabApi].join('\n'));
[getAppOptionsFile(expoMode), commonLocale, applabLocale, applabApi].join('\n'));
zip.file(mainProjectFilesPrefix + fontAwesomeWOFFPath, fontAwesomeWOFF);
if (expoMode) {
const [data] = rest[0];
Expand Down Expand Up @@ -453,26 +482,38 @@ export default {
},

async publishToExpo(appName, code, levelHtml) {
const appOptionsJs = getAppOptionsFile();
const appOptionsJs = getAppOptionsFile(true);
const { css, outerHTML } = transformLevelHtml(levelHtml);
const fontAwesomeCSS = exportFontAwesomeCssEjs({fontPath: fontAwesomeWOFFPath});
const exportConfigPath = getExportConfigPath();
const { origin } = window.location;
const applabApiPath = getEnvironmentPrefix() === 'cdo-development' ?
`${origin}/blockly/js/applab-api.js` :
`${origin}/blockly/js/applab-api.min.js`;
const html = exportExpoIndexEjs({
exportConfigPath,
htmlBody: outerHTML,
commonLocalePath: "https://studio.code.org/blockly/js/en_us/common_locale.js",
applabLocalePath: "https://studio.code.org/blockly/js/en_us/applab_locale.js",
commonLocalePath: `${origin}/blockly/js/en_us/common_locale.js`,
applabLocalePath: `${origin}/blockly/js/en_us/applab_locale.js`,
appOptionsPath: "appOptions.j",
fontPath: fontAwesomeWOFFPath,
applabApiPath: "https://studio.code.org/blockly/js/applab-api.min.js",
applabApiPath,
jQueryPath: "https://code.jquery.com/jquery-1.12.1.min.js",
commonCssPath: "https://studio.code.org/blockly/css/common.css",
applabCssPath: "https://studio.code.org/blockly/css/applab.css",
commonCssPath: `${origin}/blockly/css/common.css`,
applabCssPath: `${origin}/blockly/css/applab.css`,
});
const { shareWarningInfo = {} } = getAppOptions();
const { hasDataAPIs } = shareWarningInfo;
const appJs = exportExpoAppEjs({
hasDataAPIs: hasDataAPIs && hasDataAPIs(),
});

const appAssets = generateAppAssets({ html, code });

const files = {
'App.js': { contents: exportExpoAppJs, type: 'CODE'},
'App.js': { contents: appJs, type: 'CODE'},
'CustomAsset.js': { contents: exportExpoCustomAssetJs, type: 'CODE'},
'DataWarning.js': { contents: exportExpoDataWarningJs, type: 'CODE'},
};

const session = new SnackSession({
Expand Down Expand Up @@ -508,6 +549,13 @@ export default {
filename: fontAwesomeWOFFPath,
});

appAssets.push({
url: exportExpoWarningPng,
dataType: 'binary',
filename: 'warning.png',
assetLocation: 'appassets/',
});

const assetDownloads = appAssets.map(asset =>
download(asset.url, asset.dataType || 'text')
);
Expand All @@ -519,7 +567,7 @@ export default {
const snackAssetUrls = await Promise.all(assetUploads);

snackAssetUrls.forEach((url, index) => {
files['assets/' + appAssets[index].filename] = {
files[(appAssets[index].assetLocation || 'assets/') + appAssets[index].filename] = {
contents: url,
type: 'ASSET',
};
Expand Down Expand Up @@ -604,7 +652,7 @@ function transformLevelHtml(levelHtml) {
}

function getEnvironmentPrefix() {
const hostname = window.location.hostname;
const { hostname } = window.location;
if (hostname.includes("adhoc")) {
// As adhoc hostnames may include other keywords, check it first.
return "cdo-adhoc";
Expand Down
49 changes: 28 additions & 21 deletions apps/src/applab/api-entry.js
Expand Up @@ -15,6 +15,8 @@ import {getAppOptions, setAppOptions, setupApp} from '@cdo/apps/code-studio/init
import {getStore} from '@cdo/apps/redux';
import {setIsRunning} from '@cdo/apps/redux/runState';
import {getExportedGlobals} from './export';
import * as shareWarnings from '../shareWarnings';
import {navigateToHref} from '../utils';
window.CDOSounds = Sounds.getSingleton();

const noop = function () {};
Expand All @@ -28,15 +30,16 @@ studioApp().highlight = noop;
Applab.render = noop;

// window.APP_OPTIONS gets generated on the fly by the exporter and appended to this file.
setAppOptions(Object.assign(window.APP_OPTIONS, {isExported: true}));
const exportOptions = Object.assign({isExported: true}, window.EXPORT_OPTIONS);
setAppOptions(Object.assign(window.APP_OPTIONS, exportOptions));
setupApp(window.APP_OPTIONS);
loadApplab(getAppOptions());
const config = getAppOptions();
loadApplab(config);
// reset applab turtle manually (normally called when execution begins)
// before the student's code is run.
Applab.resetTurtle();
getStore().dispatch(setIsRunning(true));


// Expose api functions globally, unless they already exist
// in which case they are probably browser apis that we should
// not overwrite... unless they are in a whitelist of browser
Expand All @@ -50,26 +53,30 @@ for (let key in globalApi) {
}
}

const STORAGE_COMMANDS = [
'createRecord',
'readRecords',
'updateRecord',
'deleteRecord',
'onRecordEvent',
'getKeyValue',
'setKeyValue',
'getKeyValueSync',
'setKeyValueSync',
];

for (let key in STORAGE_COMMANDS) {
STORAGE_COMMANDS[key] = function () {
console.error("Data APIs are not available outside of code studio.");
};
}

// Set up an error handler for student errors and warnings.
injectErrorHandler(new JavaScriptModeErrorHandler(
() => Applab.JSInterpreter,
Applab
));

function __start() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'code.js';
document.getElementsByTagName("head")[0].appendChild(script);
}

if (!config.nativeExport) {
document.addEventListener('DOMContentLoaded', () => {
if (config.exportUsesDataAPIs) {
shareWarnings.checkSharedAppWarnings({
channelId: config.channel,
hasDataAPIs: () => ( true ),
onWarningsComplete: __start,
onTooYoung: () => ( navigateToHref('https://studio.code.org/too_young') ),
});
} else {
__start();
}
});
}
19 changes: 12 additions & 7 deletions apps/src/block_utils.js
Expand Up @@ -416,6 +416,9 @@ exports.cleanBlocks = function (blocksDom) {
* non-inlined blocks.
* @property {boolean} assignment Indicates that this block should generate
* an assignment statement, with this input yielding the variable name.
* @property {boolean} defer Indicates that this input should be wrapped in a
* function before being passed into func, so that evaluation can be deferred
* until later.
*/

/**
Expand Down Expand Up @@ -508,6 +511,7 @@ const determineInputs = function (text, args, strictTypes=[]) {
type: arg.type,
options: arg.options,
assignment: arg.assignment,
defer: arg.defer,
};
Object.keys(labeledInput).forEach(key => {
if (labeledInput[key] === undefined) {
Expand Down Expand Up @@ -825,18 +829,19 @@ exports.createJsWrapperBlockCreator = function (
let prefix = '';
const values = args.map(arg => {
const inputConfig = inputConfigs.find(input => input.name === arg.name);
const inputCode = inputTypes[inputConfig.mode].generateCode(this, inputConfig);
let inputCode = inputTypes[inputConfig.mode].generateCode(this, inputConfig);
if (inputConfig.assignment) {
prefix += `${inputCode} = `;
}
return inputCode;
}).filter(value => value !== null).map(value => {
if (value === "") {
if (inputCode === "") {
// Missing inputs should be passed into func as undefined
return "undefined";
inputCode = "undefined";
}
return value;
});
if (inputConfig.defer) {
inputCode = `function () {\n return ${inputCode};\n}`;
}
return inputCode;
}).filter(value => value !== null);

if (simpleValue) {
return [
Expand Down
2 changes: 0 additions & 2 deletions apps/src/code-studio/components/AdvancedShareOptions.jsx
Expand Up @@ -193,7 +193,6 @@ class AdvancedShareOptions extends React.Component {
<p style={style.p}>
Export your project as a zipped file, which will contain the
HTML/CSS/JS files, as well as any assets, for your project.
Note that data APIs will not work outside of Code Studio.
</p>
<button onClick={this.downloadExport} style={{marginLeft: 0}}>
{spinner}
Expand Down Expand Up @@ -227,7 +226,6 @@ class AdvancedShareOptions extends React.Component {
<div>
<p style={style.p}>
Try running your project in the Expo app on iOS or Android.
Note that data APIs will not work outside of Code Studio.
You can also export for submission to the Apple App Store or the
Google Play Store (both require following our step-by-step guide).
</p>
Expand Down
15 changes: 10 additions & 5 deletions apps/src/gamelab/GameLabJr.interpreted.js
Expand Up @@ -186,7 +186,7 @@ function whenPressedAndReleased(direction, pressedHandler, releasedHandler) {
}

function clickedOn(sprite, event) {
touchEvents.push({type: mousePressedOver, event: event, param: sprite});
touchEvents.push({type: mousePressedOver, event: event, sprite: sprite});
}

function spriteDestroyed(sprite, event) {
Expand Down Expand Up @@ -353,17 +353,22 @@ function draw() {
for (let i = 0; i < touchEvents.length; i++) {
const eventType = touchEvents[i].type;
const event = touchEvents[i].event;
const param = touchEvents[i].param;
if (eventType(param)) {
const param = touchEvents[i].sprite ?
touchEvents[i].sprite() :
touchEvents[i].param;
if (param && eventType(param)) {
event();
}
}

// Run collision events
for (let i = 0; i<collisionEvents.length; i++) {
const collisionEvent = collisionEvents[i];
const a = collisionEvent.a;
const b = collisionEvent.b;
const a = collisionEvent.a && collisionEvent.a();
const b = collisionEvent.b && collisionEvent.b();
if (!a || !b) {
continue;
}
if (a.overlap(b)) {
if (!collisionEvent.touching || collisionEvent.keepFiring) {
collisionEvent.event();
Expand Down

0 comments on commit cc29071

Please sign in to comment.