Build a Content-Rich Electron App Using the OCE Starter Site CLI

OCE blogger
Oracle Developers
Published in
9 min readJun 27, 2019

Overview

This blog demonstrates how you can use a custom ReactJS Site Template with the OCE Starter Site CLI to generate a native Electron app to browse content in Oracle Content and Experience Cloud.

The ReactJS Site Template is an important part of pulling contents into the ReactJS Site. The template serves as the skeleton for generating the site. In this case, the generated site is basically the Electron app. The contents are seeded into the app based on the content model. In this blog, we will present how the Starter Site CLI generates the Electron app with content.

The Electron framework is used to build a desktop app with JavaScript. The framework takes care of the hard parts so that you can focus on the core of the application. For this blog, we will also cover the Electron-specific changes in the custom template.

Install and set up Oracle Content and Experience Toolkit

The Oracle Content and Experience Toolkit, which includes Starter Site CLI, can be obtained directly from GitHub. Go to https://github.com/oracle/content-and-experience-toolkit and click Clone or download. Choose Download ZIP, and content-and-experience-toolkit-master.zip will be downloaded. Unzip the file content-and-experience-toolkit-master.zip into the user home directory, ~/. Enter cd content-and-experience-toolkit-master/react-starter-site to go to the directory.

To set up the Starter Site CLI, continue at step 4 of Install the Starter Site CLI. After installing the Starter Site CLI, you should be able to invoke the help for cecss.

~/content-and-experience-toolkit-master/react-starter-sites$ cecss -h
Usage: cecss <command> [options]
Run 'cecss <command> -h' to get the detailed help for the command.Commands:
cecss create-site <name> Creates the Site <name> for the content from local or from CEC server.
cecss export-server-content <channel> Create content template based on the channel <channel>, then export and download the archive from CEC server.
cecss list-server-content-types List all content types from server.
cecss list-server-channels List all channels from server.
cecss develop Start development server. Watches files, rebuilds, and hot reloads if something changes.
cecss build Build a CEC starter site.
cecss serve Serve previously build CEC starter site.
Options:
--help, -h Show help [boolean]
--version, -v Show version number

Then Do a One-Time Gradle Setup to set up gradle.properties.

Finally, create a new file under the user’s home directory called .cec_properties (~/.cec_properties) and input the values for your Oracle Content and Experience Cloud server. For example:

cec_url=<server-name.example.com>
cec_username=<username>
cec_password=<password>
cec_env=pod_ec

Use of a custom ReactJS Site Template to generate an Electron app

An Electron app will be generated from a ReactJS Site Template; namely, ElectronTemplate.zip. This template has all the files required for integrating contents into an Electron app. First download the template ElectronTemplate.zip and put it into the ~/content-and-experience-toolkit-master/react-starter-sites/data directory. Also, in the ~/content-and-experience-toolkit-master/react-starter-sites directory, there is already an out-of-the-box content template, StarterBlog_export.zip, which contains the contents for a sample blog.

Now from the ~/content-and-experience-toolkit-master/react-starter-sites directory, we will execute the CLI cecss create-site to generate the Electron app Blog1, passing in the site template ElectronTemplate.zip and content template StarterBlog_export,zip.

~/content-and-experience-toolkit-master/react-starter-sites$ cecss create-site Blog1 -f ~/content-and-experience-toolkit-master/react-starter-sites/data/ElectronTemplate.zip -c StarterBlog_export.zip
- unzip content template file
- unzip site template
- add content to site
- set up files for site runtime
- create component file ./Blog1/src/Starter_Blog_Author/Starter_Blog_Author.js
- create component file ./Blog1/src/Starter_Blog_Post/Starter_Blog_Post.js
- finish processing js files
*** site created, please run npm install to build

This will create a directory called Blog1 that contains the Electron app. Next we are going edit the package.json file under the Blog1 directory. This adds entries necessary to run the Electron app.

~/content-and-experience-toolkit-master/react-starter-sites$ cd Blog1
~/content-and-experience-toolkit-master/react-starter-sites/Blog1$ vi package.json

We are going to add a productName name-value:

productName”: “Blog1”,

We will change the main entry’s value:

main”: “src/main.js”,

We will change the start entry’s value under the scripts:

“start” : “electron src/main.js”,

Lastly we will add two dependencies:

"electron": "^5.0.1",
"electron-packager": "^13.1.1",

Here is what the snippet of package.json looks like, with the modifications in bold:

{
"name": "cec-startsite",
"productName": "Blog1",
"version": "1.0.0",
"description": "Content and Experience Cloud starter site",
"main": "src/main.js",
"scripts": {
"start": "electron src/main.js",
"build": "webpack --mode production --config ./webpack.config-prd.js",
"dev": "concurrently \"node ./server.js dev\" \"node ./webpack-server.js\""
},
"repository": {
"type": "git",
"url": ""
},
"keywords": [
"CEC"
],
"author": "Hui Zeng",
"engines": {
"node": "8.9.4"
},
"license": "ISC",
"dependencies": {
"electron": "^5.0.1",
"electron-packager": "^13.1.1",

"babel-core": "6.26.3",

Next we will run npm install to install all the dependencies.

~/content-and-experience-toolkit-master/react-starter-sites/Blog1$ npm install> fsevents@1.2.9 install /Users/kyoung/content-and-experience-toolkit-master/react-starter-sites/Blog1/node_modules/fsevents
> node install
node-pre-gyp WARN Using request for node-pre-gyp https download
[fsevents] Success: "/Users/kyoung/content-and-experience-toolkit-master/react-starter-sites/Blog1/node_modules/fsevents/lib/binding/Release/node-v64-darwin-x64/fse.node" is installed via remote
> puppeteer@1.7.0 install /Users/kyoung/content-and-experience-toolkit-master/react-starter-sites/Blog1/node_modules/puppeteer
> node install.js
Downloading Chromium r579032 - 80.8 Mb [====================] 100% 0.0s
Chromium downloaded to /Users/kyoung/content-and-experience-toolkit-master/react-starter-sites/Blog1/node_modules/puppeteer/.local-chromium/mac-579032
> core-js@2.6.9 postinstall /Users/kyoung/content-and-experience-toolkit-master/react-starter-sites/Blog1/node_modules/core-js
> node scripts/postinstall || echo "ignore"
Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!The project needs your help! Please consider supporting of core-js on Open Collective or Patreon:
> https://opencollective.com/core-js
> https://www.patreon.com/zloirock
Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)> electron@5.0.3 postinstall /Users/kyoung/content-and-experience-toolkit-master/react-starter-sites/Blog1/node_modules/electron
> node install.js
Downloading tmp-652-1-SHASUMS256.txt-5.0.3
[============================================>] 100.0% of 4.74 kB (4.74 kB/s)
npm notice created a lockfile as package-lock.json. You should commit this file.
added 1018 packages from 662 contributors and audited 8014 packages in 156.366s
found 1 high severity vulnerability
run `npm audit fix` to fix them, or `npm audit` for details

To run the app in development mode, do npm start develop. The Electron app Blog1 shows the sample blog. The contents come from the content template. In the development mode, any changes to the app are immediately reflected. For now, the app name is called Electron and the icon is temporary; we will change them later.

~/content-and-experience-toolkit-master/react-starter-sites/Blog1$ npm start develop

Quit the Electron app by using the menu, Electron > Quit Electron. Now let’s run the Starter Site CLI command cecss build, which will build the production version of the app. When building the production version of the app, you could specify that the contents come from Oracle Content and Experience Cloud by updating the app’s .cec_properties. For simplicity, we will not do that.

~/content-and-experience-toolkit-master/react-starter-sites/Blog1$ cecss build

Next we are going use the utility electron-packager to include the desire icon and specify the app name Blog1. For example, on a Mac you would do the following command. On Win32, you would change the icon parameter to src/icons/oce.ico. On Linux, you would change the icon parameter to src/icons/oce.png.

~/content-and-experience-toolkit-master/react-starter-sites/Blog1$ node_modules/.bin/electron-packager . --icon src/icons/oce.icns

After you invoke electron-packager, you will see the subdirectory Blog1-darwin-x64 on a Mac, for example. Inside of that you would find an executable called Blog1.

When you invoke the executable, it will render the production Blog1 app and use the Oracle Content and Experience Cloud icon.

How the Starter Site CLI uses template expansion to generate the Electron app with content

Structure of the React JS Site Template and Build a Starter Site Template are documented in Developing for Oracle Content and Experience Cloud. It is important to read those before this section.

A major part of generating site or Electron app is template expansion using Mustache. Mustache is a template syntax. It can be used for HTML, config files, and source code. It works by expanding macros in a template using values provided in a hash or object. When cecss create-site is used, Mustache is called to expand a set of macros for all the files in the template. The set of macros are documented in Build a Starter Site Template.

For example, in the Blog1/src/index.html file in the custom template ElectronTemplate.zip, the title of the page is coded as:

<title>{{sitename}}</title>

The {{sitename}} is a known macros. When cecss create-site is called, the macros will be substituted with the actual site name in the resulting Electron app. For the app Blog1, the title would be:

<title>Blog1</title>

Another form of template expansion has to do with directory/file name in the template. In the custom template, there is directory called {{types}} and file name {{name}}.js.

{{types}}
{{types}}/{{name}}.js

{{types}} and {{name}} are known macros. {{types}} is an array of the names of all content types in the app. {{name}} is the field name. In the resulting app Blog1, the following are the template expansion:

Blog1/src/Starter_Blog_Post
Blog1/src/Starter_Blog_Post/Starter_Blog_Post.js
Blog1/src/Starter_Blog_Author
Blog1/src/Starter_Blog_Author/Starter_Blog_Author.js

As you can see, template expansion using Mustache is very useful and powerful.

Electron-specific changes in the custom template

ElectronTemplate.zip is customized from the out-of-the-box template StarterSite.zip. In this section we will cover the Electron-specific changes in ElectronTemplate.zip.

Blog1/src/main.js is the bootstrap of the Electron app. It has a createWindow() function that is used to create the main window of the Electron app.

function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1200,
height: 900,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
var mode;
if (process.argv[2] == 'develop') {
mode = 'develop';
} else {
mode = 'production';
}
console.log('file://' + __dirname + '/index.html?mode=' + mode);
mainWindow.loadURL('file://' + __dirname + '/index.html?mode=' + mode);
// Open the DevTools.
//mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

The createWindow() function has a call to loadURL(), which will load index.html. Blog1/src/index.html is the main page of the app. Depending on whether production or development is used, the base href is set to the corresponding port. See the bold section of index.html.

<html lang="en">
<script>
const urlParams = new URLSearchParams(window.location.search);
const mode = urlParams.get('mode');
if (mode == 'develop') {
document.write('<base href="
http://localhost:9090"/>');
} else if (mode == 'production') {
document.write('<base href="
http://localhost:8080"/>');
}
</script>

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Blog1</title>
<link rel="shortcut icon" href="/favicon.ico">
</head>
<body class="app">
<div class="app" id="root"></div>
<script type="text/javascript" src="/bundle.js"></script>
</body>
<script src="/bundle.js"></script>
</html>

In Blog1/src/app/App.js, a ReactJS route directs any request to the Electron app’s */src/index.html.


<Route exact path={'*/src/index.html'} render={props =><ContentSwitcher {...props} ContentComp={ {{component}} } site={SITE_NAME} contentType='{{name}}'/>} />

That is all it takes to run an Electron app with contents! You can work iteratively in development mode and then switch to use Oracle Content and Experience Cloud to host the contents in production.

--

--