Skip to content

Commit 66da708

Browse files
Enhance, debug, and clean up scripts for CSS migration (BL-12857)
1 parent d20da5c commit 66da708

File tree

5 files changed

+250
-96
lines changed

5 files changed

+250
-96
lines changed
+14-12
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
11
In Bloom 5.7, we introduce a modernized, parameterized page layout CSS system, known as "Appearance". This new system conflicts with the arbitrary custom css that was used in prior Bloom versions, mostly in the area of margins.
22

3-
As of this writing, we are only looking at `customBookStyles.css`, but `customCollectionsStyles.css` have the same problem.
3+
As of this writing, we are looking at both `customBookStyles.css` and `customCollectionsStyles.css`, but not handling having both files with content, and not really dealing with `customCollectionStyles.css` being a collection wide settings file.
44

55
When Bloom encounters a pre-5.7 book with a `customBookStyles.css` that would conflict with the new system, it goes into a kind of "safe mode" that keeps things working by using a legacy `basePage.css`. Better, though, is to migrate the old css to the new system. Conceivably, a reliable program could be written to do this automatically. However at the moment what we do is to write the migrations using the utilities here, and then have a dev evaluate individual migrations and then copy them over to the shipping Bloom. So when Bloom encounters one of these books, we may already have a tested migration for it.
66

77
# How to use this system
88

99
⚠️ Be careful what you commit to an open source repo. We do not want to expose emails or other private data.
1010

11-
1. As of this writing, bun only works on linux. If you are on windows, just install Windows Subsystem for Linux (not as big of a deal as it sounds), then run in a WSL terminal in VSCODE.
11+
1. As of this writing, bun works only on linux. If you are on windows, just install Windows Subsystem for Linux (not as big of a deal as it sounds), then run in a WSL terminal in VSCODE.
1212

13-
1. Get [bun](https://bun.sh/) installed
13+
2. Get [bun](https://bun.sh/) installed
1414

15-
1. Install dependencies: `bun install`
16-
17-
1. Download all the `customBookStyles.css` from blorg
15+
3. Choose (or create) a folder for working with CSS files and download all the `customBookStyles.css` and `customCollectionStyles.css` files from bloomlibrary.org. This will take some time.
1816

1917
`./some-path/BloomBulkDownloader.exe --include "*/*/custom*Styles.css" --syncfolder ./output/downloads --bucket production customBookStyles-files`
2018

19+
4. Download all the `meta.json` files from bloomlibrary.org. Again, this will take some time.
20+
21+
`./some-path/BloomBulkDownloader.exe --include "*/*/meta.json" --syncfolder ./output/downloads --bucket production customBookStyles-files`
22+
2123
Each of the following take an optional argument that will limit the number of records processed.
2224

23-
5. Process those, grouping them into bins of duplicate stylesheets
25+
5. Process those, grouping them into bins of duplicate stylesheets
2426

25-
`bun run group-stylesheets.ts 13`
27+
`bun run /path-to/group-stylesheets.ts 13`
2628

27-
1. Process those groups, discarding ones that don't need migration
29+
6. Process those groups, discarding ones that don't need migration
2830

29-
`bun run filter-stylesheets.ts 7`
31+
`bun run /path-to/filter-stylesheets.ts 7`
3032

31-
1. Create draft migration files for each one
33+
7. Create draft migration files for each one
3234

33-
`bun run create-migrations.ts 3`
35+
`bun run /path-to/create-migrations.ts 3`

custom-css-analysis-and-migration/create-migrations.ts

+52-14
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,47 @@ for (const record of records) {
3636
if (record.css === undefined) continue;
3737

3838
++count;
39-
if (Bun.argv.length > 2 && count >= Number.parseInt(Bun.argv[2])) break;
39+
if (Bun.argv.length > 2 && count > Number.parseInt(Bun.argv[2])) break;
4040
console.log(`css = ${record.css}`);
4141
const checksum = crypto.createHash("md5").update(record.css).digest("hex");
4242
const folder = `./output/migration/${count} ${checksum}`;
43+
console.log(`output folder=${folder}`);
4344
fs.mkdirSync(folder);
4445

46+
let cssPath = `${folder}/customBookStyles.css`;
47+
if (fs.existsSync(cssPath)) {
48+
cssPath = `${folder}/customBookStyles2.css`;
49+
console.log(`WARNING: multiple migrations in ${folder}`);
50+
}
51+
52+
const migration = migrateCssToAppearance(record.css);
53+
4554
fs.writeFileSync(
46-
`${folder}/customBookStyles.css`,
47-
migrateCssToAppearance(record.css) +
48-
`\n\n /* ----- ORIGINAL ---- */\n/*${record.css.replaceAll(
49-
"*/",
50-
"#/"
51-
)}*/`
55+
cssPath,
56+
migration.modifiedCss +
57+
`
58+
59+
/* ----- ORIGINAL ---- */
60+
/*${record.css.replaceAll("*/", "#/")}*/
61+
`
5262
);
5363

64+
const brandingSet = new Set();
65+
for (let i = 0; i < record.paths.length; ++i) {
66+
const metaPath = record.paths[i] + "/meta.json";
67+
try {
68+
const metaString: string = fs.readFileSync(metaPath, "utf8");
69+
const metadata = JSON.parse(metaString);
70+
const brandingProject = metadata.brandingProjectName as string;
71+
if (brandingProject &&
72+
brandingProject.toLowerCase() !== "default" &&
73+
brandingProject.toLowerCase() !== "local community")
74+
brandingSet.add(brandingProject);
75+
} catch (e) {
76+
console.log("Could not extract brandingProject from " + metaPath);
77+
}
78+
}
79+
5480
const uniqueUploaders = [
5581
...new Set(
5682
record.paths.map((path: string) => {
@@ -63,15 +89,27 @@ for (const record of records) {
6389
),
6490
].slice(0, 3); // first 3 are enough to give a sense of who uploaded these books
6591

66-
fs.writeFileSync(
67-
`${folder}/appearance.json`,
92+
const brandings = [...brandingSet];
93+
94+
const outputString: string =
6895
`// Matches customBookStyles.css with checksum ${checksum}
69-
// This was used by ${record.book_count} books (${record.unique_named_books} unique).` +
70-
// enhance: +`// Affected branding projects include "Mali-ACR-2020-Soninke".`
71-
`// Uploaders included ${JSON.stringify(uniqueUploaders)}.
96+
// This was used by ${record.book_count} books (${record.unique_named_books} unique).
97+
` +
98+
(brandings && brandings.length > 0
99+
? `// Affected branding projects included ${JSON.stringify(brandings)}.
100+
`
101+
: ``) +
102+
`// Uploaders included ${JSON.stringify(uniqueUploaders)}.
72103
// Example Book: ${record.first_book}
73104
// A replacement customBookStyles.css has been generated.
74105
{
75-
}`
76-
);
106+
"cssThemeName": "default",
107+
"coverShowTitleL2": ${migration.hideL2TitleOnCover ? "false" : "true"},
108+
"coverShowTitleL3": false,
109+
}
110+
`;
111+
let appearancePath = `${folder}/appearance.json`;
112+
if (fs.existsSync(appearancePath))
113+
appearancePath = `${folder}/appearance2.json`;
114+
fs.writeFileSync(appearancePath, outputString);
77115
}

custom-css-analysis-and-migration/filter-stylesheets.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const recordsWithUniqueifiedPaths = filteredRecords.map((record) => {
3434
const uniquePaths = uniqueFilenames.map((filename) => {
3535
return paths.find((path) => path.endsWith(filename));
3636
});
37-
const instanceId = paths[0].split("/")[2];
37+
const instanceId = paths[0].split("/")[3];
3838
return {
3939
book_count: paths.length,
4040
unique_named_books: uniquePaths.length,
@@ -75,12 +75,13 @@ console.write(
7575
`, covering ${filteredRecords.reduce(
7676
(acc, record) => acc + record.paths.length,
7777
0
78-
)} books, `
78+
)} books`
7979
);
80-
8180
// console.write(
82-
// `${recordsWithUniqueifiedPaths.reduce(
81+
// `, ${recordsWithUniqueifiedPaths.reduce(
8382
// (acc, record) => acc + record.uniqueified_paths.length,
8483
// 0
8584
// )} of which have unique names (should remove most rebrands).\r\n`
8685
// );
86+
console.write(`
87+
`);

custom-css-analysis-and-migration/group-stylesheets.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,38 @@ interface FileData {
1111
}
1212

1313
const boilerplate =
14-
'/* Some books may need control over aspects of layout that cannot yet be adjusted\r\n from the Bloom interface. In those cases, Bloom provides this "under the hood" method\r\n of creating style rules using the underlying "Cascading Stylesheets" system.\r\n These rules are then applied to all books in this collection. EDIT THIS FILE ONLY\r\n IN THE COLLECTION FOLDER: changes made to a copy found in the book folder will be\r\n lost the next time the book is edited with Bloom!\r\n\r\n Note: you can also add a file named "customBookStyles.css" in the book folder,\r\n to limit the effects of the rules to just that one book.\r\n\r\n You can learn about CSS from hundreds of books, or online. However chances are, if\r\n you need this customization, you will need an expert to create a version of this file\r\n for you, or give you rules that you can paste in below this line. */';
14+
/\/\* Some books may need control over aspects of layout that cannot yet be adjusted(.|[\r\n])*?\*\//;
1515

16-
const boilerplate2 =
17-
'/* Some books may need control over aspects of layout that cannot yet be adjusted\r\n from the Bloom interface. In those cases, Bloom provides this "under the hood" method\r\n of creating style rules using the underlying "Cascading Stylesheets" system. \r\n These rules are then applied to all books in this collection.\r\n\r\n Note: you can also add a file named "customBookStyles.css" in the book folder,\r\n to limit the effects of the rules to just that one book.\r\n \r\n You can learn about CSS from hundreds of books, or online. However chances are, if\r\n you need this customization, you will need an expert to create a version of this file\r\n for you, or give you rules that you can paste in below this line. */';
1816
let count = 0;
1917
function readFilesRecursively(
2018
dir: string,
2119
fileMap: Map<string, FileData>
2220
): void {
2321
const files = fs.readdirSync(dir);
22+
let cssCount = 0;
2423
for (const file of files) {
2524
const filePath = path.join(dir, file);
2625
if (Bun.argv.length > 2 && count >= Number.parseInt(Bun.argv[2])) return;
2726
if (fs.statSync(filePath).isDirectory()) {
2827
readFilesRecursively(filePath, fileMap);
2928
} else {
29+
if (file === "meta.json") continue;
3030
const content = fs
3131
.readFileSync(filePath, "utf8")
3232
.replace(boilerplate, "")
33-
.replace(boilerplate2, "")
3433
.trim();
3534
if (content === "") continue;
35+
++cssCount;
3636

3737
const fileData: FileData = fileMap.get(content) || { content, paths: [] };
3838
fileData.paths.push(dir.replace("./output/downloads", ""));
3939
fileMap.set(content, fileData);
40-
console.log(++count + " " + dir);
40+
console.log(++count + " " + filePath);
4141
}
4242
}
43+
if (cssCount > 1)
44+
console.log(`WARNING: multiple CSS files with content in ${dir}`);
45+
cssCount = 0;
4346
}
4447

4548
const sourceDir = "./output/downloads";

0 commit comments

Comments
 (0)