Friday, July 02, 2010

Alfresco: Exposing Functionality via Webscripts

As I have mentioned before, I don't plan on my (hypothetical) bloggers and editors using the Alfresco web UI to manage the content in the CMS. I want to provide them with an interface similar to Drupal's rather than the file based interface Alfresco provides by default. To modify Alfresco's web UI to support this would require quite a lot of work, including having to learn Java Server Faces, upon which Alfresco's web UI is based, and which I know nothing about, and given that I don't use it at the moment for anything else, don't feel particularly compelled to learn.

Instead, I plan on building a Java based webapp from the ground up, and have it proxy through to Alfresco over HTTP via REST scripts using Alfreso's Webscript framework. This approach appears to be quite popular, Alfresco Developer's Guide has an entire chapter devoted to this, and there is also a Alfresco's Web Scripts page also has quite a bit of documentation about this.

Essentially, on the Alfresco side, one writes a "controller", consisting of an XML descriptor, a Webscript and one or more views to expose a given function. Each view has a URL associated with it. Webscripts can be written using Java or Javascript. Views are written using Freemarker Template Language (FTL). There is a strict naming convention that needs to be followed for everything to work, since the controller is run dynamically using the Webscript framework.

I made a conscious decision to keep things in Java as far as possible, so even though I liked the Javascript API (it is as comprehensive as the Java API, results in more concise code and you can write and debug code in the running server, which is a great timesaver), I decided to check out the Java way first. I do want to use the Javascript API (at least for Webscripts) at some point in the future though.

So anyway, I built two Webscripts this week, which I describe below.

Search Webscript

The Search Webscript does a fulltext search for a given term. It is intended to be used from a in-page search widget, like the ones you find on the top right corner of some websites. The user enters a term or phrase and gets back a page of search results.

Obviously, the Search Webscript uses the Alfresco Search Service to retrieve the results using a Lucene query. The difference is that the results are filtered by user role, ie, a blogger only gets to search in documents that are either in his inbox, or have been submitted by him for review, or have been published already. An editor on the other hand, gets to search in all documents in review, and all published documents, but not documents that are in Draft state in blogger's inboxes.

Descriptor

Here is the XML descriptor for the Search WebScript. It needs to be stored in the directory shown in the Source comment, along with the FTL files. It defines the URLs that are exposed by the controller, along with some transaction and authentication requirements.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!-- Source: config/alfresco/extension/templates/webscripts/com/mycompany/search/search.get.desc.xml -->
<webscript>
  <shortname>Document Search [mycompany]</shortname>
  <description>Document Search [mycompany]</description>
  <url>/mycompany/search</url>
  <url>/mycompany/search.json</url>
  <url>/mycompany/search.html</url>
  <format default="json">extension</format>
  <authentication>none</authentication>
  <transaction>none</transaction>
</webscript>

Java Code

A design strategy I have followed consistently in all my Alfresco work so far is to delegate the work involving the Alfresco Foundation API to a helper class. This is to make it easier to test functionality via unit tests on the command line (although it does take a while for the unit tests to run, since it has to spin up the Alfresco application context each time). Consequently, the Java code that follows isn't particularly interesting, but serves to show the general structure of a Webscript Java controller. I show the WebscriptHelper class later in the post.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Source: src/java/com/mycompany/alfresco/extension/webscripts/GetSearch.java
package com.mycompany.alfresco.extension.webscripts;

import java.util.HashMap;
import java.util.Map;

import org.alfresco.web.scripts.Cache;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.Status;
import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest;
import org.apache.commons.lang.StringUtils;

public class GetSearch extends DeclarativeWebScript {

  private WebscriptHelper helper;
  
  public void setHelper(WebscriptHelper helper) {
    this.helper = helper;
  }

  @Override
  public Map<String,Object> executeImpl(WebScriptRequest request, 
      Status status, Cache cache) {
    String query = request.getParameter("query");
    // TODO: replace with alf_ticket?
    String user = request.getParameter("user");
    if (StringUtils.isEmpty(user) || StringUtils.isEmpty(query)) {
      throw new WebScriptException(
        "Can't find mandatory parameters query term and/or user");
    }
    Map<String,Object> model = new HashMap<String,Object>();
    helper.search(model, query, user);
    model.put("query", query);
    model.put("user", user);
    return model;
  }
}

As you can see, my Webscript controller extends DeclarativeWebScript and overrides its executeImpl(WebRequest, Status, Cache)::Map method. The call to helper.search() populates a List<Post> into the model (the Map that is returned from the executeImpl).

HTML Template

The HTML template is quite simple, we don't really need this for my setup to work (I plan on using the JSON data on the client side), but it helps for debugging the service. Besides I wanted to check out FTL (I've used Velocity but not Freemarker before this). Here is the HTML template.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<#-- Source: config/alfresco/extension/templates/webscripts/com/mycompany/search/search.get.html.ftl -->
<html>
  <head>
    <title>Search results for "${query}"</title>
  </head>
  <body>
    <h2>Search Results for "${query}"</h2>
    <#list posts as post>
      <p>
        <#if post.pubState == "Draft" || post.pubState == "Review">
          <b><a href="/preview/${post.id}">${post.title}</a></b>
        </#if>
        <#if post.pubState == "Published">
          <b><a href="${post.furl}">${post.title}</a></b>
        </#if>
        <br/>${post.description?html}
        <br/><br/><font size="-1">
          <b>Status: </b>${post.pubState}&nbsp;
          <b>Created By: </b>${post.creator}&nbsp;
          <b>Dated: </b>${post.created?datetime}
        </font>
      </p>
    </#list>
  </body>
</html>

JSON Template

The corresponding JSON template looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<#-- Source: config/alfresco/extension/templates/webscripts/com/mycompany/search/search.get.json.ftl -->
{"model": 
  {"query" : "${query}"},
  {"user" : "${user}"},
  {"posts" : [
    <#list posts as post>
      {"id" : "${post.id}",
        "title" : "${post.title}",
      <#if post.pubState == "Draft" || post.pubState == "Review">
        "furl" : "/preview/${post.id}",
      <#else>
        "furl" : "${post.furl}",
      </#if>
        "description" : ${post.description?html}",
        "pubState" : "${post.pubState}",
        "creator" : "${post.creator}",
        "created" : "${post.created?datetime}"
      },
    </#list>
  ]}
}

Example HTML Output

An example output for a search with user=happy and query=drupal is shown below. The JSON output is less spectacular (relatively speaking), but contains the same data. This is in response to the URL:

1
http://localhost:8080/alfresco/service/mycompany/search.html?query=drupal&user=happy


Dashboard Webscript

The Dashboard Webscript returns the documents that a user needs to work on at the current time. Like the Search Webscript, the contents of the dashboard is dependent on the user. For example, a blogger only sees the contents of his home directory, while an editor will see separate buckets containing lists of documents for Review, Published documents, etc.

Descriptor

The descriptor for this webscript is similar to the one for search. Here it is.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!-- Source: config/alfresco/extension/templates/webscripts/com/mycompany/dashboard/dashboard.get.desc.xml -->
<webscript>
  <shortname>Dashboard [mycompany]</shortname>
  <description>Dashboard [mycompany]</description>
  <url>/mycompany/dashboard</url>
  <url>/mycompany/dashboard.json</url>
  <url>/mycompany/dashboard.html</url>
  <format default="json">extension</format>
  <authentication>none</authentication>
  <transaction>none</transaction>
</webscript>

Java Code

Again, not much to say about this, its similar to the one for Search.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Source: src/java/com/mycompany/alfresco/extension/webscripts/GetDashboard.java
package com.mycompany.alfresco.extension.webscripts;

import java.util.HashMap;
import java.util.Map;

import org.alfresco.web.scripts.Cache;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.Status;
import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest;
import org.apache.cxf.common.util.StringUtils;

public class GetDashboard extends DeclarativeWebScript {

  private WebscriptHelper helper;
  
  public void setHelper(WebscriptHelper helper) {
    this.helper = helper;
  }
  
  @Override
  public Map<String,Object> executeImpl(WebScriptRequest request, 
      Status status, Cache cache) {
    String user = request.getParameter("user");
    if (StringUtils.isEmpty(user)) {
      throw new WebScriptException("Can't find mandatory parameter user");
    }
    Map<String,Object> model = new HashMap<String,Object>();
    helper.getDashboard(model, user);
    model.put("user", user);
    return model;
  }
}

HTML Template

The HTML Template for the Dashboard is shown below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
<#-- Source: config/alfresco/extension/templates/webscripts/com/mycompany/dashboard/dashboard.get.html.ftl -->
<html>
  <head>
    <title>Dashboard for ${role?cap_first} ${user}</title>
  </head>
  <body>
    <h2>Welcome ${role?cap_first} ${user}</h2>
    <#if inbox??>
      <h2>Inbox</h2>
      <table cellspacing="3" cellpadding="3" border="0">
        <tr>
          <td><b>Post Title</b></td>
          <td><b>Status</b></td>
          <td><b>Created By</b></td>
          <td><b>Date</b></td>
        </tr>
      <#list inbox as post>
        <tr>
          <td><a href="/preview/${post.id}">${post.title}</a></td>
          <td>${post.pubState}</td>
          <td>${post.creator}</td>
          <td>${post.created?datetime}</td>
        </tr>  
      </#list>
      </table>
    </#if>
    <#if review??>
      <h2>Posts Pending Review</h2>
      <table cellspacing="3" cellpadding="3" border="0">
        <tr>
          <td><b>Post Title</b></td>
          <td><b>Status</b></td>
          <td><b>Created By</b></td>
          <td><b>Date</b></td>
        </tr>
      <#list review as post>
        <tr>
          <td><a href="/preview/${post.id}">${post.title}</a></td>
          <td>${post.pubState}</td>
          <td>${post.creator}</td>
          <td>${post.created?datetime}</td>
        </tr>  
      </#list>
      </table>
    </#if>
    <#if published??>
      <h2>Published Posts</h2>
      <table cellspacing="3" cellpadding="3" border="0">
        <tr>
          <td><b>Post Title</b></td>
          <td><b>Status</b></td>
          <td><b>Created By</b></td>
          <td><b>Date</b></td>
        </tr>
      <#list published as post>
        <tr>
          <td><a href="${post.furl}">${post.title}</a></td>
          <td>${post.pubState}</td>
          <td>${post.creator}</td>
          <td>${post.created?datetime}</td>
        </tr>  
      </#list>
      </table>
    </#if>
    <#if scheduled??>
      <h2>Posts Scheduled for Publish</h2>
      <table cellspacing="3" cellpadding="3" border="0">
        <tr>
          <td><b>Post Title</b></td>
          <td><b>Status</b></td>
          <td><b>Created By</b></td>
          <td><b>Date</b></td>
        </tr>
      <#list scheduled as post>
        <tr>
          <td><a href="/preview/${post.id}">${post.title}</a></td>
          <td>${post.pubState}</td>
          <td>${post.creator}</td>
          <td>${post.created?datetime}</td>
        </tr>  
      </#list>
      </table>
    </#if>
    <#if archived??>
      <h2>Archived Posts</h2>
      <table cellspacing="3" cellpadding="3" border="0">
        <tr>
          <td><b>Post Title</b></td>
          <td><b>Status</b></td>
          <td><b>Created By</b></td>
          <td><b>Date</b></td>
        </tr>
      <#list archived as post>
        <tr>
          <td><a href="/preview/${post.id}">${post.title}</a></td>
          <td>${post.pubState}</td>
          <td>${post.creator}</td>
          <td>${post.created?datetime}</td>
        </tr>  
      </#list>
      </table>
    </#if>
  </body>
</html>

JSON Template

And the JSON Template for the Dashboard.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<#-- Source: config/alfresco/extension/templates/webscripts/com/mycompany/dashboard/dashboard.get.json.ftl -->
{"model": 
  {"user" : "${user}"},
  {"role" : "${role?cap_first}"},
  <#if inbox??>
    {"inbox" : [
      <#list posts as post>
        {"id" : "${post.id}",
         "title" : "${post.title}",
         "furl" : "/preview/${post.id}",
         "pubState" : "${post.pubState}",
         "creator" : "${post.creator}",
         "created" : "${post.created?datetime}"
        },
      </#list>
    ]},
  </#if>
  <#if review??>
    {"review" : [
      <#list review as post>
        {"id" : "${post.id}",
         "title" : "${post.title}",
         "furl" : "/preview/${post.id}",
         "pubState" : "${post.pubState}",
         "creator" : "${post.creator}",
         "created" : "${post.created?datetime}"
        },
      </#list>
    ]},
  </#if>
  <#if published??>
    {"published" : [
      <#list published as post>
        {"id" : "${post.id}",
         "title" : "${post.title}",
         "furl" : "${post.furl}",
         "pubState" : "${post.pubState}",
         "creator" : "${post.creator}",
         "created" : "${post.created?datetime}"
        },
      </#list>
    ]},
  </#if>
  <#if scheduled??>
    {"scheduled" : [
      <#list scheduled as post>
        {"id" : "${post.id}",
         "title" : "${post.title}",
         "furl" : "/preview/${post.id}",
         "pubState" : "${post.pubState}",
         "creator" : "${post.creator}",
         "created" : "${post.created?datetime}"
        },
      </#list>
    ]},
  </#if>
  <#if archived??>
    {"archived" : [
      <#list archived as post>
        {"id" : "${post.id}",
         "title" : "${post.title}",
         "furl" : "/preview/${post.id}",
         "pubState" : "${post.pubState}",
         "creator" : "${post.creator}",
         "created" : "${post.created?datetime}"
        },
      </#list>
    ]},
  </#if>
}

Example HTML Output

The XML output representing the dashboard for user "happy" is shown below. This is in response to a URL of the form:

1
http://localhost:8080/alfresco/service/mycompany/dashboard.html?user=doc


Webscript Helper

The Webscript helper does most of the work, as I mentioned above. Not much explanation is required if you are familiar with the Alfresco Java API. It has two methods search() and getDashboard() which are called from the corresponding Webscripts.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Source: src/java/com/mycompany/alfresco/extension/webscripts/WebscriptHelper.java
package com.mycompany.alfresco.extension.webscripts;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.transaction.UserTransaction;

import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.scripts.WebScriptException;
import org.apache.commons.lang.WordUtils;
import org.apache.log4j.Logger;
import org.springframework.util.CollectionUtils;

import com.mycompany.alfresco.extension.model.MyContentModel;
import com.mycompany.alfresco.extension.model.Post;

public class WebscriptHelper {

  private final Logger logger = Logger.getLogger(getClass());
  
  private AuthorityService authorityService;
  private TransactionService transactionService;
  private SearchService searchService;
  private NodeService nodeService;

  public void setAuthorityService(AuthorityService authorityService) {
    this.authorityService = authorityService;
  }

  public void setTransactionService(TransactionService transactionService) {
    this.transactionService = transactionService;
  }

  public void setSearchService(SearchService searchService) {
    this.searchService = searchService;
  }

  public void setNodeService(NodeService nodeService) {
    this.nodeService = nodeService;
  }

  public void search(Map<String,Object> model, 
      String term, String userName) throws WebScriptException {
    List<Post> posts = new ArrayList<Post>();
    ResultSet resultSet = null;
    UserTransaction tx = transactionService.getUserTransaction();
    try {
      tx.begin();
      try {
        // tailor the query depending on who is calling it
        StringBuilder queryBuilder = new StringBuilder();
        String role = getRole(userName);
        if ("blogger".equals(role)) {
          // Blogger can see own posts in Draft and Review, and all
          // published posts
          queryBuilder.
            // all docs with term in user's home directory
            append("(+PATH:\"/app:company_home/app:user_homes/sys:").
            append(WordUtils.capitalize(userName)).
            append("/*\" +").
            append(term).
            append(") ").
            // OR all docs in review containing term and created by user
            append("(+PATH:\"/app:company_home/cm:Public/cm:Review/*").
            append("\" +cm\\:creator:\"").
            append(userName).
            append("\" +").
            append(term).
            append(") ").
            // OR all docs in published containing term
            append("(+PATH:\"/app:company_home/cm:Public/cm:Published/cm:Live/*").
            append("\" +").
            append(term).
            append(")");
        } else if ("editor".equals(role)) {
          // Editor can see all posts in Review and Published
          queryBuilder.
            // all docs with term in Review
            append("(+PATH:\"/app:company_home/cm:Public/cm:Review/*").
            append("\" +").
            append(term).
            append(") ").
            // all docs with term in Published
            append("(+PATH:\"/app:company_home/cm:Public/cm:Published/cm:Live/*").
            append("\" +").
            append(term).
            append(")");
        } else {
          throw new WebScriptException("Invalid user: " + userName + 
          ". Must have permissions of GROUP_BLOGGER or GROUP_EDITOR");
        }
        logger.debug("query=" + queryBuilder.toString());
        resultSet = searchService.query(
          StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 
          SearchService.LANGUAGE_LUCENE, queryBuilder.toString());
        for (ChildAssociationRef caref : resultSet.getChildAssocRefs()) {
          NodeRef nodeRef = caref.getChildRef();
          posts.add(getPost(nodeRef));
        }
        tx.commit();
      } catch (Exception e) {
        tx.rollback();
        throw e;
      } finally {
        if (resultSet != null) { resultSet.close(); }
      }
      model.put("posts", posts);
    } catch (Exception e) {
      throw new WebScriptException(e.getMessage(), e);
    }
  }
  
  public void getDashboard(Map<String,Object> model, String userName) 
      throws WebScriptException {
    UserTransaction tx = transactionService.getUserTransaction();
    try {
    tx.begin();
    try {
      String role = getRole(userName);
      if ("blogger".equals(role)) {
        model.put("role", role);
        // contains posts in the blogger's home directory
        List<Post> inboxPosts = getPosts(
          "/app:company_home/app:user_homes/sys:" + 
          WordUtils.capitalize(userName));
        if (! CollectionUtils.isEmpty(inboxPosts)) {
          model.put("inbox", inboxPosts);
        }
      } else if ("editor".equals(role)) {
        model.put("role", role);
        // contains posts under review, and...
        List<Post> reviewPosts = getPosts(
          "/app:company_home/cm:Public/cm:Review");
        if (! CollectionUtils.isEmpty(reviewPosts)) {
          model.put("review", reviewPosts);
        }
        // published posts, and...
        List<Post> publishedPosts = getPosts(
          "/app:company_home/cm:Public/cm:Published/cm:Live");
        if (! CollectionUtils.isEmpty(publishedPosts)) {
          model.put("published", publishedPosts);
        }
        // scheduled posts, and...
        List<Post> scheduledPosts = getPosts(
          "/app:company_home/cm:Public/cm:Published/cm:Scheduled");
        if (! CollectionUtils.isEmpty(scheduledPosts)) {
          model.put("scheduled", scheduledPosts);
        }
        // archived posts
        List<Post> archivedPosts = getPosts(
          "/app:company_home/cm:Public/cm:Published/cm:Archived");
        if (! CollectionUtils.isEmpty(archivedPosts)) {
          model.put("archived", archivedPosts);
        }
      }
      tx.commit();
    } catch (Exception e) {
      tx.rollback();
      throw e;
    }
    } catch (Exception e) {
      throw new WebScriptException(e.getMessage(), e);
    }
  }

  private List<Post> getPosts(String path) throws Exception {
    List<Post> posts = new ArrayList<Post>();
    ResultSet resultSet = null;
    try {
      String query = "+PATH:\"" + path + "/*\" +ASPECT:\"my:publishable\"";
      resultSet = searchService.query(
        StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 
        SearchService.LANGUAGE_LUCENE, query);
      for (ChildAssociationRef caref : resultSet.getChildAssocRefs()) {
        NodeRef nodeRef = caref.getChildRef();
        posts.add(getPost(nodeRef));
      }
    } catch (Exception e) {
      throw e;
    } finally {
      if (resultSet != null) { resultSet.close(); }
    }
    return posts;
  }

  public String getRole(String userName) {
    Set<String> authorities = authorityService.getAuthoritiesForUser(userName);
    if (authorities.contains("GROUP_GROUP_BLOGGER")) {
      return "blogger";
    } else if (authorities.contains("GROUP_GROUP_EDITOR")) {
      return "editor";
    } else {
      return null;
    }
  }

  private Post getPost(NodeRef nodeRef) {
    Map<QName,Serializable> props = nodeService.getProperties(nodeRef);
    Post post = new Post();
    post.setId((String) props.get(ContentModel.PROP_NODE_UUID));
    post.setTitle((String) props.get(ContentModel.PROP_TITLE));
    post.setFurl((String) props.get(MyContentModel.PROP_FURL));
    post.setDescription((String) props.get(ContentModel.PROP_DESCRIPTION));
    post.setPubState(props.get(MyContentModel.PROP_PUBSTATE));
    post.setCreated((Date) props.get(ContentModel.PROP_CREATED));
    post.setCreator((String) props.get(ContentModel.PROP_CREATOR));
    return post;
  }
}


Spring Configuration

Finally, we configure these two webscripts in Spring. We create a mycompamy-scripts-context.xml file in the config/alfresco/extension directory, which is loaded via Alfresco's extension mechanism. It defines the two Webscript beans and the helper.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<!-- Source: config/alfresco/extension/mycompany-scripts-context.xml -->
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 
    'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
  <bean id="webscript.com.mycompany.search.search.get"
      class="com.mycompany.alfresco.extension.webscripts.GetSearch"
      parent="webscript">
    <property name="helper" ref="webscriptHelper"/>
  </bean>

  <bean id="webscript.com.mycompany.dashboard.dashboard.get"
      class="com.mycompany.alfresco.extension.webscripts.GetDashboard"
      parent="webscript">
    <property name="helper" ref="webscriptHelper"/>
  </bean>
  
  <bean id="webscriptHelper" 
      class="com.mycompany.alfresco.extension.webscripts.WebscriptHelper">
    <property name="authorityService" ref="authorityService"/>
    <property name="transactionService" ref="transactionService"/>
    <property name="searchService" ref="searchService"/>
    <property name="nodeService" ref="nodeService"/>
  </bean>
</beans>


What's Next?

I am nearing the end of my Alfresco customization project. I still have some more Webscripts to write, but they are likely to be repetitive, so I don't plan on writing about them unless there is something to write about. I do have the CMS client webapp to build, but I plan to take a short break in order to learn Spring-Roo, with which I plan to build the client.

Be the first to comment. Comments are moderated to prevent spam.