Deploying React pages to Share

cancel
Showing results for 
Search instead for 
Did you mean: 

Deploying React pages to Share

ddraper
Intermediate II
8 4 5,636

Introduction

I've previously experimented with developing Alfresco clients using a number of different application frameworks. In this blog post I'm going to step through the process of creating a page using React and deploying it into Share.

Although I'm using React in this example I could have just as easily used Vue.js, Aurelia, Angular, Ember or any framework that provides a CLI or project template for developing and packaging a Single Page Application.

Basic Setup

  1. Install the create-react-app CLI following the instructions on the linked page
  2. Create a new application:
    create-react-app MyApp‍‍‍‍‍‍‍‍‍
  3. Change to the project directory:
    cd MyApp‍‍‍‍‍‍‍‍‍
  4. Eject!
    npm run eject‍‍‍‍‍‍‍‍‍‍
  5. Edit the "scripts/start.js" file and add the following at the end of the "addMiddleware" function:
    let alfrescoProxy = httpProxyMiddleware('/share/proxy/alfresco-api', {
       target: 'http://localhost:8080/alfresco',
       changeOrigin: true,
       pathRewrite: {
          '^/share/proxy/alfresco-api': 'api'
       }
    });
    devServer.use(alfrescoProxy);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
  6. Install alfresco-js-utils (the version used at the time of writing is specified, newer versions maybe available at time of reading!):
    npm install alfresco-js-utils@0.0.7 --save-dev‍‍‍‍‍‍‍‍‍‍
  7. Start the application:
    npm start‍‍‍‍‍‍‍‍‍

Why Eject?

Strictly speaking it is not necessary to eject (and it would be preferable not to). However, in this example I'm re-using my own NPM package called alfresco-js-utils which provides some re-usable JavaScript functions. One of these functions is a service for retrieving Nodes from the Alfresco Repository using a V1 REST API and the URL that this function calls starts with /share/proxy/alfresco as it is intended to ultimately be run on Share. It is possible to configure an API proxy for the create-react-app CLI but it does not support URL rewriting. Therefore we need to eject in order to gain access to the start.js script that is edited to allow us to add custom HTTP proxy middleware.

Create Your Page

You should now have an application running locally with a great development experience with hot-reloading, etc provided by create-react-app. If you make an REST API call to (using an URL starting with /share/proxy/alfresco) then you should get a basic authentication challenge and once you've provided some valid credentials you'll be able to access data from the Alfresco Repository (don't worry, when you deploy into Share the authentication will be handled for you by Share/Surf).

I've built a very simple client for browsing company home. You can view/copy the code from this GitHub repository (check out this tag for the code at the time of writing this post). The client contains 4 components:

  • List
  • ListView
  • Breadcrumb
  • Toolbar

...that provide a simple interface for browsing.

Build and Deploy

Once you're happy with your page then run:

npm run build‍‍‍‍‍‍‍

This will populate the build folder with the resources that you want to deploy into Share. 

  1. Copy the build folder to a new location
  2. Rename the static folder to be "META-INF"
  3. Create the following files:
    1. alfresco/site-data/pages/react.xml
    2. alfresco/site-data/template-instances/react.xml
    3. alfresco/templates/react.ftl

Your folder should now look like this:

Now update the files as follows:

alfresco/site-data/pages/react.xml

<?xml version='1.0' encoding='UTF-8'?>
<page>
   <title>React</title>
   <description>React</description>
   <template-instance>react</template-instance>
   <authentication>user</authentication>
</page>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This defines the new Surf page that will contain our React code.

alfresco/site-data/template-instances/react.xml

<?xml version='1.0' encoding='UTF-8'?>
<template-instance>
   <template-type>react</template-type>
</template-instance>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This is referenced from the page and creates a mapping to a template.

Copy the contents of the index.html file into the alfresco/templates/react.ftl file but replace all occurrences of "/static" with "/share/res".

If you change the contents of any JS or CSS and rebuild it will be necessary to copy and update the contents of index.html again because the resources are named with a checksum (just like Surf/Aikau does!)

Now bundle up the contents of the copied build folder (not including the build folder itself) as a JAR file and copy it into the WEB-INF/lib directory of Share and restart the server that it is running on.

Once the server has restarted you'll be able to login to Share and then you will be able to access your page at /share/page/react.

Include the Header and Footer

If you want your users to access this new page with the context of Share then it makes sense to include the standard header and footer.

Update the react.ftl file initially so that it looks like this:

<#include "/org/alfresco/include/alfresco-template.ftl" />
<@templateHeader>
   <!-- Insert CSS link here -->
</@>
<@templateBody>
   <div id="alf-hd">
      <@region scope="global" id="share-header" chromeless="true"/>
   </div>
   <div id="bd">
      <!-- Insert contents of body element here -->
   </div>
</@>
<@templateFooter>
   <div id="alf-ft">
      <@region id="footer" scope="global" />
   </div>
</@>
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

...and then add the CSS <link> element and the contents of the <body> element from index.html into the commented sections.

Update your JAR file with the changes, redeploy and restart the server and when you access the same URL you'll see the header and footer.

Add a Link to the Header

Obviously it's not ideal for users to have to enter a URL into the browser to get to your new page so let's add a link into the header bar.

This Stack Overflow question and answer provides a good overview on doing this properly (go on, give it an up vote - you know you want to!) but the basic steps in this case are as follows:

  1. Create an alfresco/site-data/extensions/react-extension.xml file containing:
    <extension>
       <modules>
          <module>
             <id>React Extension</id>
             <auto-deploy>true</auto-deploy>
             <customizations>
                <customization>
                   <targetPackageRoot>org.alfresco</targetPackageRoot>
                   <sourcePackageRoot>org.alfresco.share.pages.customizations</sourcePackageRoot>
                </customization>

                <customization>
                   <targetPackageRoot>org.alfresco.share.pages</targetPackageRoot>
                   <sourcePackageRoot>org.alfresco.share.pages.customizations.share.header</sourcePackageRoot>
                   <alwaysApply>
                      <webscript>share-header</webscript>
                   </alwaysApply>
                </customization>
             </customizations>
          </module>
       </modules>
    </extension>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
  2. Create an alfresco/site-webscripts/org/alfresco/share/pages/customizations/share/header/share-header.get.js file containing:
    var headerMenuBar = widgetUtils.findObject(model.jsonModel.widgets, "id", "HEADER_APP_MENU_BAR");
    if (headerMenuBar && headerMenuBar.config && headerMenuBar.config.widgets)
    {
       headerMenuBar.config.widgets.push({
          name: "alfresco/menus/AlfMenuBarItem",
          config: {
             label: "React page",
             targetUrl: "react"
          }
       });
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
  3. Repack your JAR file, deploy it to /share/WEB-INF/lib and restart the server

Now on each page in Share you'll see a link to your new page:

Summary

It's very easy to take advantage of the development environments provided for modern web application frameworks and deploy the output into Share. Some of the techniques shown in this post may be old but are still extremely effective which shows the value in a properly architected framework.

4 Comments
neilecker
Active Member II

Hi Dave, thanks for this post.  I'm trying to do something similar but I'm not quite understanding how the javascript libraries are being included.

I wanted to include the Vue.js library on a standard surf page like in the example above and if I don't bring in the standard header and footer I can add <script src="https://unpkg.com/vue"></script> to the page and start using it without issue.  Once I include the standard header and footer and insert the vue.min.js is retrieved from the CDN but something is different as I get a "Vue is not defined".

Something like:

<#include "/org/alfresco/include/alfresco-template.ftl" />
<@templateHeader>
   <!-- Insert CSS link here -->
</@>
<@templateBody>
   <div id="alf-hd">
      <@region scope="global" id="share-header" chromeless="true"/>
   </div>
   <div id="bd">
      <div id="app">
          <!-- Some HTML here -->
      </div>

      <script src="https://unpkg.com/vue"></script>
      <script type="text/javascript">
          var app = new Vue({
              el: '#app',
              /* Make use of Vue library */
          });
      </script>

   </div>
</@>
<@templateFooter>
   <div id="alf-ft">
      <@region id="footer" scope="global" />
   </div>
</@>‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This question is more generally about including any additional javascript resource, how should they be added to a surf page when the alfresco-template.ftl is being included?

Thanks,

Neil

ddraper
Intermediate II

Are you seeing those script tags output in the final HTML page? ...and are you seeing XHR requests made for the JS resources? On the face of it I can't see much wrong with what you've got there though. You might want to consider trying the <@script> FreeMarker directive for the Vue.js import (as opposed to an HTML script element) but otherwise it seems fine.

Also, as an alternative approach have you seen this other post I did specifically about embedding Vue.js into Aikau? https://community.alfresco.com/community/ecm/blog/2017/01/16/deja-vue 

neilecker
Active Member II

Yes, the script tags are output as is in the final HTML.  I'm not seeing an XHR request for them if that's what is supposed to happen but they are loaded by the browser.  I tried using the FreeMarker <@script> directive which does add the script tag to the <head> of the HTML but it seems that the dojo loader gets involved and I start getting a "TypeError: this is undefined".

I did read your post on embedding Vue.js into Aikau but my knowledge of Aikau and creating widgets is rather minimal at the moment.  It's a really simple use case and I was looking for the simplest way to add in a page that "fits" into share.  If I don't include the alfresco-template.ftl I can include and use any javascript libraries I want and also get access to the surf root objects which is pretty nice, I'm just missing the share header and footer.

Another option would be to just use jQuery which is already being loaded in Share but it looks like I have the same problem if I try to use the alfresco-template.ftl, the difference being that I get a "$ is not defined" instead of "Vue is not defined".  

Anyways, sounds like I'm not doing things as intended and I should do some more learning about both the FreeMarker templates and Aikau.  Unless you have any other ideas about how to use a javascript library (even jQuery) inside the alfresco-template.ftl.

Thanks for your advice!

Neil

adelohb
Partner

hello I am a novice on alfresco I tried to follow this tutorial but with angular 2 but I did not succeed. could you describe me the follow step to integrate a simple page angular on alfresco share please !!!