Skip to content

Commit b1028d8

Browse files
authored
✨ Update Open Graph metadata in index.html (ITISFoundation#5520)
1 parent a9703a5 commit b1028d8

File tree

9 files changed

+186
-63
lines changed

9 files changed

+186
-63
lines changed

services/static-webserver/client/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ compile: ## qx compiles host' 'source' -> image's 'build-output'
5252
--build-arg VCS_STATUS_CLIENT=${VCS_STATUS_CLIENT} \
5353
--build-arg VCS_URL=${VCS_URL} \
5454
--target=build-client .
55+
python ./scripts/post-compile.py
5556

5657
touch: ## minimal image build with /project/output-build inside
5758
# touch /project/output-build such that multi-stage 'services/web/Dockerfile' can build development target (fixes #1097)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"applications": [
3+
{
4+
"application": "osparc",
5+
"replacements": {
6+
"replace_me_og_title": "oSPARC",
7+
"replace_me_og_description": "open online simulations for Stimulating Peripheral Activity to Relieve Conditions",
8+
"replace_me_og_image": "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/master/services/static-webserver/client/source/resource/osparc/favicon-osparc.png"
9+
}
10+
}, {
11+
"application": "s4l",
12+
"replacements": {
13+
"replace_me_og_title": "Sim4Life",
14+
"replace_me_og_description": "Computational life sciences platform that combines computable human phantoms, powerful physics solvers and advanced tissue models.",
15+
"replace_me_og_image": "https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/background-images/S4L/Sim4Life-head-default.png"
16+
}
17+
}, {
18+
"application": "s4lacad",
19+
"replacements": {
20+
"replace_me_og_title": "Sim4Life Science",
21+
"replace_me_og_description": "Sim4Life for Science - Computational life sciences platform that combines computable human phantoms, powerful physics solvers and advanced tissue models.",
22+
"replace_me_og_image": "https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/background-images/S4L/Sim4Life-head-academy.png"
23+
}
24+
}, {
25+
"application": "s4ldesktop",
26+
"replacements": {
27+
"replace_me_og_title": "Sim4Life (Desktop)",
28+
"replace_me_og_description": "Computational life sciences platform that combines computable human phantoms, powerful physics solvers and advanced tissue models.",
29+
"replace_me_og_image": "https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/background-images/S4L/Sim4Life-head-default.png"
30+
}
31+
}, {
32+
"application": "s4ldesktopacad",
33+
"replacements": {
34+
"replace_me_og_title": "Sim4Life Science (Desktop)",
35+
"replace_me_og_description": "Sim4Life for Science - Computational life sciences platform that combines computable human phantoms, powerful physics solvers and advanced tissue models.",
36+
"replace_me_og_image": "https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/background-images/S4L/Sim4Life-head-academy.png"
37+
}
38+
}, {
39+
"application": "s4llite",
40+
"replacements": {
41+
"replace_me_og_title": "S4L Lite",
42+
"replace_me_og_description": "Sim4Life for Students - Computational life sciences platform that combines computable human phantoms, powerful physics solvers and advanced tissue models.",
43+
"replace_me_og_image": "https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/background-images/S4L/Sim4Life-head-lite.png"
44+
}
45+
}, {
46+
"application": "tis",
47+
"replacements": {
48+
"replace_me_og_title": "TI Plan - IT'IS",
49+
"replace_me_og_description": "A tool powered by o²S²PARC technology that reduces optimization of targeted neurostimulation protocols.",
50+
"replace_me_og_image": "https://raw.githubusercontent.com/ITISFoundation/osparc-simcore/master/services/static-webserver/client/source/resource/osparc/tip_splitimage.png"
51+
}
52+
}
53+
]
54+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import os
2+
import json
3+
4+
5+
output_folders = [
6+
"source-output", # dev output
7+
"build-output", # default production output
8+
"build-client" # I believe we create the production outputs here
9+
]
10+
11+
12+
def read_json_file(filename):
13+
dirname = os.path.dirname(__file__)
14+
meta_filename = os.path.join(dirname, filename)
15+
with open(meta_filename, "r") as file:
16+
metadata = json.load(file)
17+
return metadata["applications"]
18+
19+
20+
def update_apps_metadata():
21+
dirname = os.path.dirname(__file__)
22+
applications = read_json_file("apps_metadata.json")
23+
for i in applications:
24+
application = i.get("application")
25+
for output_folder in output_folders:
26+
filename = os.path.join(dirname, '..', output_folder, application, "index.html")
27+
if not os.path.isfile(filename):
28+
continue
29+
with open(filename, "r") as file:
30+
data = file.read()
31+
replacements = i.get("replacements")
32+
for key in replacements:
33+
replace_text = replacements[key]
34+
data = data.replace(key, replace_text)
35+
with open(filename, "w") as file:
36+
print(f"Updating app metadata: {filename}")
37+
file.write(data)
38+
39+
40+
if __name__ == "__main__":
41+
update_apps_metadata()

services/static-webserver/client/source/boot/index.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
<link rel="mask-icon" href="${resourcePath}osparc/favicon-osparc.png"/>
2929

3030
<!-- Open Graph metadata -->
31-
<meta property="og:title" id="openGraphTitle" content="${appTitle}" />
32-
<!-- They need to be written by the compiler, modifying the page after BootJs doesn't make the trick -->
33-
<!-- <meta property="og:description" id="openGraphDescription" content="open online simulations for Stimulating Peripheral Activity to Relieve Conditions" /> -->
34-
<!-- <meta property="og:image" id="openGraphImage" content="../resource/osparc/favicon-osparc.png" /> -->
31+
<meta property="og:title" content="replace_me_og_title" />
32+
<meta property="og:description" content="replace_me_og_description" />
33+
<meta property="og:image" content="replace_me_og_image" />
34+
<meta property="og:type" content="website" />
3535

3636
<style>
3737
body {
@@ -46,7 +46,7 @@
4646
</style>
4747

4848
<title>${appTitle}</title>
49-
<meta name="description" content="open online simulations for Stimulating Peripheral Activity to Relieve Conditions" />
49+
<meta name="description" content="replace_me_og_description" />
5050

5151
<noscript>
5252
<meta http-equiv="refresh" content="0; url=nojs.html"/>

services/static-webserver/client/source/class/osparc/po/PreRegistration.js

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,54 +17,38 @@
1717
************************************************************************ */
1818

1919
qx.Class.define("osparc.po.PreRegistration", {
20-
extend: qx.ui.core.Widget,
21-
22-
construct: function() {
23-
this.base(arguments);
24-
25-
this._setLayout(new qx.ui.layout.VBox(10));
26-
27-
this.__buildLayout();
28-
},
29-
30-
statics: {
31-
createGroupBox: function(title) {
32-
const box = new qx.ui.groupbox.GroupBox(title).set({
33-
appearance: "settings-groupbox",
34-
layout: new qx.ui.layout.VBox(5),
35-
alignX: "center"
36-
});
37-
box.getChildControl("legend").set({
38-
font: "text-14"
39-
});
40-
box.getChildControl("frame").set({
41-
backgroundColor: "transparent"
42-
});
43-
return box;
44-
},
45-
46-
},
20+
extend: osparc.po.BaseView,
4721

4822
members: {
49-
__preRegistrationLayout: null,
50-
5123
_createChildControlImpl: function(id) {
5224
let control;
5325
switch (id) {
5426
case "search-preregistration":
5527
control = this.__searchPreRegistration();
5628
this._add(control);
5729
break;
30+
case "finding-status":
31+
control = new qx.ui.basic.Label();
32+
this._add(control);
33+
break;
34+
case "pre-registration-container":
35+
control = new qx.ui.container.Scroll();
36+
this._add(control, {
37+
flex: 1
38+
});
39+
break;
5840
}
5941
return control || this.base(arguments, id);
6042
},
6143

62-
__buildLayout: function() {
63-
this._createChildControlImpl("search-preregistration");
44+
_buildLayout: function() {
45+
this.getChildControl("search-preregistration");
46+
this.getChildControl("finding-status");
47+
this.getChildControl("pre-registration-container");
6448
},
6549

6650
__searchPreRegistration: function() {
67-
const groupBox = this.self().createGroupBox(this.tr("Pre-Registration"));
51+
const groupBox = osparc.po.BaseView.createGroupBox(this.tr("Pre-Registration"));
6852
const form = this.__preRegistrationForm();
6953
const formRenderer = new qx.ui.form.renderer.Single(form);
7054
groupBox.add(formRenderer);
@@ -91,20 +75,23 @@ qx.Class.define("osparc.po.PreRegistration", {
9175
return;
9276
}
9377
if (form.validate()) {
94-
if (this.__preRegistrationLayout) {
95-
this._remove(this.__preRegistrationLayout);
96-
}
97-
78+
submitBtn.setFetching(true);
79+
const findingStatus = this.getChildControl("finding-status");
80+
findingStatus.setValue(this.tr("Searching Pre-Registered users..."));
9881
const params = {
9982
data: JSON.parse(requestAccountData.getValue())
10083
};
101-
submitBtn.setFetching(true);
10284
osparc.data.Resources.fetch("users", "preRegister", params)
10385
.then(data => {
104-
const layout = this.__preRegistrationLayout = this.__createPreRegistrationLayout(data);
105-
this._add(layout);
86+
if (data.length) {
87+
findingStatus.setValue(this.tr("Pre-Registered as:"));
88+
} else {
89+
findingStatus.setValue(this.tr("No Pre-Registered user found"));
90+
}
91+
this.__populatePreRegistrationLayout(data);
10692
})
10793
.catch(err => {
94+
findingStatus.setValue(this.tr("Error searching Pre-Registered users"));
10895
console.error(err);
10996
osparc.FlashMessenger.logAs(err.message, "ERROR");
11097
})
@@ -116,23 +103,10 @@ qx.Class.define("osparc.po.PreRegistration", {
116103
return form;
117104
},
118105

119-
__createPreRegistrationLayout: function(respData) {
120-
const vBox = new qx.ui.container.Composite(new qx.ui.layout.VBox(2));
121-
122-
const hBox = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
123-
alignY: "middle"
124-
}));
125-
vBox.add(hBox);
126-
127-
const respLabel = new qx.ui.basic.Label(this.tr("Pre-Registered as:"));
128-
vBox.add(respLabel);
129-
106+
__populatePreRegistrationLayout: function(respData) {
107+
const preRegistrationContainer = this.getChildControl("pre-registration-container");
130108
const preregistrationRespViewer = new osparc.ui.basic.JsonTreeWidget(respData, "preregistration-data");
131-
const container = new qx.ui.container.Scroll();
132-
container.add(preregistrationRespViewer);
133-
vBox.add(container);
134-
135-
return vBox;
109+
preRegistrationContainer.add(preregistrationRespViewer);
136110
}
137111
}
138112
});

services/static-webserver/client/source/class/osparc/po/Users.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,12 @@ qx.Class.define("osparc.po.Users", {
3131
control = new qx.ui.basic.Label();
3232
this._add(control);
3333
break;
34-
case "found-users-container": {
34+
case "found-users-container":
3535
control = new qx.ui.container.Scroll();
3636
this._add(control, {
3737
flex: 1
3838
});
3939
break;
40-
}
4140
}
4241
return control || this.base(arguments, id);
4342
},

services/static-webserver/client/tools/qooxdoo-kit/builder/Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ RUN \
5151
ls -la build-output
5252
# -> /project/build-output
5353

54+
# Install python...
55+
RUN apk add --update python3 py3-pip
56+
# ...and run post compilation step
57+
RUN \
58+
--mount=type=bind,source=scripts,target=scripts \
59+
python3 ./scripts/post-compile.py
60+
5461

5562
FROM joseluisq/static-web-server:1.16.0-alpine as server-base
5663

services/web/server/tests/unit/isolated/test_statics.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ def source_boot_index_html(web_client_dir: Path) -> Path:
3131
return index_html
3232

3333

34+
@pytest.fixture(scope="module")
35+
def metadata_file(web_client_dir: Path) -> Path:
36+
metadata_filepath = web_client_dir / "scripts" / "apps_metadata.json"
37+
assert metadata_filepath.exists()
38+
return json.loads(metadata_filepath.read_text())
39+
40+
3441
def test_expected_frontend_apps_produced_by_webclient(client_compile_cfg: dict):
3542
"""
3643
tests that names in FRONTEND_APP_DEFAULT and FRONTEND_APPS_AVAILABLE
@@ -66,3 +73,21 @@ def test_expected_frontend_apps_produced_by_webclient(client_compile_cfg: dict):
6673
), "Sync with values in FRONTEND_APPS_AVAILABLE with {compile_filepath}"
6774

6875
assert FRONTEND_APP_DEFAULT in FRONTEND_APPS_AVAILABLE
76+
77+
78+
def test_expected_frontend_apps_metadata(client_compile_cfg: dict, metadata_file: dict):
79+
"""
80+
tests that names in FRONTEND_APP_DEFAULT and metadata provided in app_metadata.json
81+
corresponds to actual front-end apps produced by static-webserver/client
82+
"""
83+
frontend_apps_in_repo = {
84+
feapp["name"] for feapp in client_compile_cfg["applications"]
85+
}
86+
87+
frontend_apps_in_metadata = {
88+
feapp["application"] for feapp in metadata_file["applications"]
89+
}
90+
91+
assert (
92+
frontend_apps_in_repo == frontend_apps_in_metadata
93+
), "Sync with values in FRONTEND_APPS_AVAILABLE with {metadata_filepath}"

tests/e2e/tests/title.test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1+
const appMetadata = require('../../../services/static-webserver/client/scripts/apps_metadata.json')
2+
13
beforeAll(async () => {
24
await page.goto(url);
35
}, ourTimeout);
46

57
test('Check site title', async () => {
68
const title = await page.title();
7-
expect(title).toBe('oSPARC');
9+
expect(title).toBe("oSPARC");
10+
11+
// oSPARC ([0]) is the product served by default
12+
const replacements = appMetadata["applications"][0]["replacements"];
13+
14+
const description = await page.$$eval("head > meta[name='description']", descriptions => {
15+
return descriptions[0].content;
16+
});
17+
expect(description).toBe(replacements["replace_me_og_description"]);
18+
19+
// Open Graph metadata
20+
const ogTitle = await page.$$eval("head > meta[property='og:title']", ogTitles => {
21+
return ogTitles[0].content;
22+
});
23+
expect(ogTitle).toBe(replacements["replace_me_og_title"]);
24+
25+
const ogDescription = await page.$$eval("head > meta[property='og:description']", ogDescriptions => {
26+
return ogDescriptions[0].content;
27+
});
28+
expect(ogDescription).toBe(replacements["replace_me_og_description"]);
29+
830
}, 20000);

0 commit comments

Comments
 (0)