7 mistakes developers make when customizing Alfresco Share

I’ve seen more than my–ahem–fair share of Alfresco Share over the last several months. Many clients feel that their needs are so close to what Share provides out-of-the-box, that they can save time and money by starting with Share as the basis for their custom content-centric application. Whether or not that’s a good idea is the subject of another post. This post assumes that, for whatever reason, you find yourself customizing Alfresco’s Share client and wondering what are some of the common pitfalls to avoid. Here’s seven. Feel free to add to the list.

1. Ignoring client-side JavaScript minification

Here is a massive understatement for you: Alfresco Share has a lot of client-side JavaScript files. Most, if not all, of these are minified, or compressed, to reduce the size of a given page and increase client-side performance. If you’ve ever looked at the FreeMarker source for one of Alfresco’s pages, you may have seen something like this:

<@script type="text/javascript" src="${page.url.context}/components/blog/blog-common.js">

It looks like an everyday JavaScript reference but what’s up with that “@script” tag? It’s a FreeMarker macro. It switches out the JavaScript source file for the minified version when debug is turned off and uses the original uncompressed source when debug is turned on, which makes stepping through the client-side JavaScript much more pleasant.

There are two things you need to be aware of here. First, if you find yourself tweaking Alfresco’s client-side JavaScript, you’ll need to remember to deploy both the expanded and minified version of the file. Otherwise, when people turn debug on and off, they’ll see different results. Second, when you create your own client-side JavaScript, you need to minify your own code for the same reason.

You could turn debug on and leave it on (bad idea) or you could use a “normal” script tag and point to the non-minified versions of your JavaScript, but it is really easy to add minification as a part of your build, so you might as well set that up early in the project and you won’t have to worry about it later.

There are several JavaScript compressors out there. Here’s a link to the YUI Compressor. You can drop the JAR into your project and then invoke it from Ant quite easily. Ask Google for some examples.

2. Assuming Alfresco and Share are on the same host

When you install Alfresco it deploys a web application in the “/alfresco” context–that’s your repository and the old Alfresco Explorer client–and a second web application in the “/share” context. Depending on what you’re doing you might deploy numerous additional web apps based on Share or Surf.

Regardless of how you choose to deploy, you need to remember that there is no guarantee your app and Alfresco will be on the same machine, app server, or port number. One of the beauties of the Surf architecture is that you can scale it out across multiple app servers and they can all talk to the same (or multiple) Alfresco repository servers. The underlying Surf framework on which Share is based has configuration and helper variables you can leverage to deal with this. You should not be hardcoding “localhost” or any other hostname in your Share code.

3. Incomplete theme customization

Alfresco Share 3.3 has user-selectable themes. As part of your customization effort you can define your own theme and then configure that to be the default. An easy way to create your own theme is to copy one of the out-of-the-box themes and then modify it to suit your needs. The keys to cloning a theme successfully are:

  1. Copy one of the themes other than “Default”
  2. Search and replace references to the old theme name in the new CSS files (login.css, presentation.css, and yui/assets/skin.css)
  3. In the previous step, don’t forget yui/assets/skin.css!

4. Duplicating, rather than extending, Alfresco web scripts

Suppose you want to change something in one of Alfresco’s web scripts. You may be tempted to change the out-of-the-box controller JavaScript or FreeMarker views, but don’t do it. A nice thing about the web script framework is that you can override even just a single file that is part of a web script by placing your version of the file with the same name in the same folder structure under web-extension. This also works on the repository tier, but instead of web-extension you use the “extension” directory.

For example, maybe I want to extend the document-actions config XML in Share with my own settings. I will NOT copy my version over the top of Alfresco’s. Instead, I’ll put my copy in a file named “document-actions.get.config.xml” under WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details. When Alfresco loads the web script, it will use my version of the config.

5. Not using the web-extension directory

While we’re on the topic, all of your custom Share config files go in web-extension under the Share web application. Don’t put them in $TOMCAT_HOME/shared/classes and don’t put them in the Share web app’s classes/alfresco directory. Use the web-extension directory to keep your stuff separate from Alfresco’s. I also recommend doing the same with your client-side files–create a directory called “extension” for your client-side JavaScript, images, CSS, and so-on.

6. Using the same Tomcat server as the Alfresco repository during development

This one isn’t going to cause you problems, but it sure will slow you down. Even if your production Share web app will run on the same Tomcat as the Alfresco WAR, do yourself a favor: While you’re coding, use two Tomcats. On port 8080, you’ll run Alfresco and out-of-the-box Share. On some other port you’ll run a second Tomcat server with your custom Share- or Surf-based web app. That way, when you need to restart your custom Share app, you don’t have to wait for the repository to start back up. You’ll cut way down on the time you spend waiting for Tomcat to restart which, over time, can speed up your development cycle tremendously.

7. Failing to test on Alfresco’s supported browsers

Have I mentioned how much client-side JavaScript there is in Share? Every time you touch Alfresco’s JavaScript or create your own, you’ll need to test it on the browsers your client intends to use. So there are two recommendations here: First, make sure you are testing across Alfresco’s supported browsers. Second, make sure your clients only expect to use Alfresco’s supported browsers. Failure to do both of these can lead to some missed expectations on both sides. The browsers Alfresco supports for 3.3 are on the supported stacks page on the Alfresco web site.

What am I missing? Add a comment with your Alfresco Share street smarts.

32 comments

  1. Fred says:

    Thanks Jeff for the post.

    I was thinking about configuration you make to alfresco or share, like simply subsystems configurations or anything that touches an xml file that Alfresco could overwrite if you redeploy a war file (ie, you forgot the quickr module, you push it down, restart and realize that your authentication is not working, and that all your changes are gone) or the modification of an alfresco core conf file to point to your own custom config file.

    What are your advices/processes to make sure that your changes do not get lost when you redeploy the alfresco or share war?

    Thanks a lot 🙂
    Cheers
    fred

  2. jpotts says:

    Put subsystems and other environment- or server-specific files in $TOMCAT_HOME/shared/classes. Put everything else in the WAR. To avoid regressing the files you put in the WAR you could deploy your customizations as an AMP and then your “AMP’d” WAR is always ready to be redeployed, always has all of your customizations, and can easily be queried using the MMT to determine which AMPs have been applied to the WAR.

    You can also deploy Share customizations as an AMP, if you want.

  3. Steve says:

    Jeff —

    To avoid hard coding localhost I attempted to retrieve the host name from webscript-framework-config.xml using Freemarker. I find that the hostnameCfg variable below is always undefined. I’ve assumed this is a bug and reported it. Do you have another suggested approach to retrieve the host name?

    this is a test
    http
    8080

  4. Steve says:

    Looks like it stripped out my code in my previous post, so I’ve removed the LT/GT characters:

    var serverCfg = config.scoped[“Server”].server;
    var myhostname = serverCfg.hostname;
    var myscheme = serverCfg.scheme ;

    Here is the configuration:

    alfresco-config
    config

    config evaluator=”string-compare condition=”Server”
    server
    hostname is a test /hostname
    scheme http /scheme
    port 8080 /port
    /server
    /config
    /alfresco-config

  5. Steve says:

    I have a case where a Share page calls a repository web script that returns json containing a url of a user’s avatar image. That url needs the hostname so it can be embedded in a link.

  6. Naveen says:

    Jeff,

    Thanks for your post. Very helpful. Quick question. I have to modify the permissions.js file which is used to create a pop-up panel (in YUI) when editing the permissions of a file.

    I have set the debug to true and when I edit the original file in (\webapps\share\modules\documentlibrary) I see my file loaded. I am trying keep all my files in the “Shared” section but can’t seem to find the correct location for this file so that it is loaded. It continues to load the original file instead.

    Any idea of the location this should be stored?

  7. Naveen says:

    Additional note:

    I have tried to upload the file to:

    \webapps\share\WEB-INF\classes\alfresco\web-extension\modules\documentlibrary

    with no luck.

  8. jpotts says:

    The permissions.js file is a client-side, not a server-side, JavaScript file. That means it does not go in WEB-INF but rather in the webapp’s docroot. In this case, assuming what you want to do is overwrite Alfresco’s with your own, the file goes in $TOMCAT_HOME/webapps/share/modules/documentlibrary/permissions.js. Also remember to create a minified version of this file and name it permissions-min.js.

    Hope that helps,

    Jeff

  9. Steve says:

    Regarding the use of built-in freemarker url.context variable, it returns “alfresco”, but not the host/port.

  10. jpotts says:

    Yeah, I realized that after I posted. I guess I’ve never needed to refer to the host and port Share is running on. You don’t need it to refer to CSS, JavaScript, or other resources because you’re on the same server. And, you don’t need it to use AJAX to hit a web script running on the either the Repository app server or the Share app server.

    In your case, the repo tier is returning a URL to the user’s avatar. If you’re trying to display the avatar, you should be using the Share proxy URL to display that URL rather than using the repo’s URL directly. For example, I have a case where a repository web script returns the URL to a logo stored in the repo. The repository web script spits out JSON. The logic that computes the logo URL looks like this:

    < #if logo??>/proxy/alfresco/api/node/${logo.nodeRef?replace('://','/')}/content< #else>/components/images/no-user-photo-64.png

    What’s happening here is that the FTL is checking to see if a logo variable exists in the model. If it does, it builds a URL using the proxy syntax. An alternative would be to simply pass back the node ref and let the Share web script take that node ref and build the URL. If the logo variable does not exist, the web script returns the link to a placeholder.

    Then, on the Share web script, my image reference looks like this:

    img src=”${url.context}${obj.logo}”

    That obj variable is just the JSON coming back from the repository tier.

    Hope that helps,

    Jeff

  11. Balaji says:

    Jeff,
    Amazing blog! Just few questions to follow up: When you want ot extend and start your customization in share, I want to first set up a dev environemnt with right tools to start with. I get lot of options like STS,surf plugins, or use the eclipse with downloaded share or download the source code and then start developing. Which is the most safe and desired option.?

  12. jpotts says:

    Balaji,

    I typically do not import the entire Share web app into my source code so a plug-in like Roo, even if it did work with Share (and I haven’t tried that), wouldn’t be that helpful. Instead, I use good old Eclipse with Ant-based builds.

    The Ant build packages up a zip which then gets unzipped into the exploded Share WAR, which is the same approach you can use with your repository extensions. It is worth noting that both repository and Share extensions can be packaged as AMP files, and that works as well, but I like the zip/unzip approach in development because the deployment is so much faster.

    Just like in repository extensions, you need to use Share’s extension mechanism to deploy your customizations. The Alfresco folks (and the wiki) mostly show extensions being deployed to $TOMCAT_HOME/webapps/shared. I really don’t like that and I’m not sure why they do. Most of my customers don’t want to be messing with anything that’s outside of the WAR so why not unzip right into the exploded WAR directory? As long as you are using good extension directory practices (web-extension, in the case of Share) you aren’t going to overwrite any of Alfresco’s code. And, the nice thing is, you can WAR that up and move it around without having to worry about what did or did not make it into shared.

    As far as STS versus Eclipse goes, that’s largely a personal preference. There may be some tooling that comes down the line to make Alfresco developers more productive, but it isn’t here yet. And, because STS is Eclipse-based, whatever does come will likely work in Eclipse anyway. If you are building a Spring-based front-end on top of Alfresco, that might be a big plus for STS.

    Hope that helps,

    Jeff

  13. Alex says:

    Hi Jeff,

    I’ve got a tricky situation here regarding overriding webscripts and hoping to get some advice from you.

    We use amps to deploy our codes and starting to encounter a problem that multiple amps trying to override the same webscript. For example, we need to set custom permissions to document actions in share document library. However, there are two amp modules needing to do this. Only one of the modules will successfully override the original webscript.

    What would be the best way to manage this?

  14. jpotts says:

    Unfortunately, there is no good way to address this. You are at the mercy of the classloader and the fact that the Module Management Tool does not do intelligent merging of multiple AMPs.

    Jeff

  15. Terry says:

    Cool Article thanks.

    I have been asked to look at using Alfresco as a document repository for a new project. Basically I will have a number of web applications built using mainly jsf ajax enabled components maybe primefaces or richfaces. These web apps will not be deployed on the same host as alfresco. I would like to reuse if possible document related ui components that support:

    – Opening and Streaming documents from repo back to browser
    – Document Security
    – Document Actions – checkout etc
    – Document Previewing

    What are my options?

    Do I modify share and redeploy it as part of one of my apps?
    Can I reuse Docasu embed it in my app?
    Do I use standard jsf components and use a json based data model ?
    Do I write my own jsf components?

    Any help much appreciated – what’s the best approach at integrating what’s out there with a document centric jsf based web app?

  16. jpotts says:

    Terry,

    Share isn’t JSF-based. It is built on Surf. So if one of your requirements is that it be JSF, that’s not an option. DoCASU is not JSF either. I believe it is EXT-JS if memory serves correctly.

    There are no reusable JSF components for Alfresco for a custom app that I know of so you’ll have to write your own.

    I’d encourage you to use CMIS, if at all possible. The Apache Chemistry OpenCMIS Java API will be able to perform CRUD functions against the repository including modifying ACLs and performing checkouts. For preview, though, you might have to do something on your own or see if you can somehow snag what’s already been done in Share.

    Hope this helps,

    Jeff

  17. jpotts says:

    EZRef Admin,

    Not sure who submitted that but they appear to be editing Alfresco’s files directly instead of using the extension mechanism which is definitely not a best practice.

    Jeff

  18. Guy Roberts says:

    Regarding point 6, another way to restart share without needing to restart alfresco is to use the Tomcat Web Application Manager to just restart the share application.

  19. jpotts says:

    @yt,

    I don’t recall whether that is part of the Share source or the underlying Spring Surf framework. You’ll have to do some digging.

    Jeff

  20. Lanre says:

    Hi and thank you. As a starting one of the first things I was asked was to add additional properties to a user and your post on that helped. I now have to make that happen with the ‘new user’ creation process. Does it also make use of the same extended slingshotUserFactory class?

  21. Anil says:

    Thanks for your tips,

    I am using asp.net application and I need to implement “Document Management Systems” in my application. So for this I preferred Alfresco.
    So first I installed WSE 3.0 and now “WSE 3.0 Setting” option is coming , in this setting window what I need to choose, what is the next step, How I implement Alfresco in my project please guide me.

  22. Jeff Potts says:

    Anil,

    Thanks for reading. This is a pretty general question so it is hard for anyone to give you specific advice. You’ll probably also have better luck in the Alfresco forums where the entire community can take a look and potentially help out.

    If you create a post, please be sure to specify what “WSE” is. Most of the Alfresco community are Java and open source people who may not be familiar with Microsoft technology, so you might have to explain exactly what it is you are trying to do and where you are running into problems.

    Jeff

  23. himansu123 says:

    i created a custom login page in Alfresco share project .now i want to add my css to this login file .where i should put the css file in my project ..?

  24. Jeff Potts says:

    himansu123, In an AMP, client-side web assets go under ./src/main/amp/web. You might consider creating a CSS file under that directory, for example.

Comments are closed.