- License
- Introduction
- What is the Web Script Framework?
- Your First Web Script
- Setup
- Creating Your Own REST API for Ratings
- Invoking web scripts with ajax
- Conclusion
- Things to Explore on Your Own
- Where to Find More Information
License
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
Introduction
This tutorial is an introduction to the Alfresco Web Script Framework. It continues the “SomeCo Whitepapers” example started in previous tutorials. Here's what you have seen in the previous tutorials:
- The custom content types tutorial extended the out-of-the-box content model so that SomeCo could store custom metadata about one of their document types, whitepapers.
- The custom actions tutorial showed how to create rule actions and UI actions. The tutorial created a UI action called "Set Web Flag" that toggles a flag used to determine whether or not the whitepaper should be shown on the web site.
- The custom behavior tutorial showed how to create business logic associated with a type or aspect. In that tutorial, a custom content model defines a new "rateable" aspect and a custom behavior is bound to that aspect that knows how to calculate the average user rating for a given piece of content. The calculation gets triggered every time a rating is created or deleted. The tutorial used unit tests and a quick-and-dirty web script to create ratings.
SomeCo is now ready to move to the next step: Exposing the rating functionality on the Alfresco back-end as an API to the front-end. In their infinite wisdom, the team at SomeCo realizes that Alfresco's web script framework provides a nice way to expose a lightweight, RESTful API for working with whitepapers and ratings. The API can be used by SomeCo's front-end web site developers to retrieve whitepapers and let end-users rate those whitepapers.
In this tutorial, you'll learn how to roll your own REST API on top of Alfresco. The API will be used for:
- retrieving a list of whitepapers,
- retrieving the average rating for a given whitepaper,
- retrieving a specific rating,
- posting a new rating for a whitepaper, and
- deleting all ratings for a given whitepaper.
As you'll learn shortly, web scripts can be written in either server-side JavaScript or Java. This tutorial will use JavaScript for most of the controller logic but you'll see how to use Java as well.
The complete source code that accompanies this tutorial is available at GitHub.
Sound decent? Okay, let's get started.
What is the Web Script Framework?
In today's everything-as-a-service environment, the CMS is increasingly seen as a black-box component. Other systems, both producers and consumers of content, need to interact with the CMS and other components via REST.
Alfresco provides an industry-standard, RESTful API for working with the repository called CMIS. But there are times when this API doesn't do everything you need to do. In that case, a great alternative is to create your own API using the web script framework.
Think of a web script as a chunk of code that is mapped to a human-readable URL. So, for example, a URL that returns expense reports pending approval might look like:
/alfresco/service/expenses/pending
while a URL that returns expenses pending approval for a specific user might look like:
/alfresco/service/expenses/pending/jpotts
In the URL above, you could read the “jpotts” component of the URL as an implied argument. A more explicit way to provide an argument would be like:
/alfresco/service/expenses/pending?user=jpotts
Or maybe “pending” is an argument as well which tells the web script what status of expense reports to return. The point is that the structure of the URL and how (and if) your URL includes arguments is completely up to you.
The response the URL returns is also up to you. Your response might return HTML, XML, JSON, or even a JSR-168 Portlet.
The web script framework makes it easy to follow the Model-View-Controller (MVC) pattern, although it isn't required. The controller is server-side JavaScript, a Java class, or both. The controller handles the request, performs any business logic that is needed, populates the model with data, and then forwards the request to the view. The view is a FreeMarker template responsible for constructing a response in the appropriate format. The model is a data structure passed between the controller and the view.
The mapping of URL to controller is done through an XML descriptor which is responsible for declaring the URL pattern, whether the script requires a transaction or not, and the authentication requirements for the script. The descriptor optionally describes arguments that can be passed to the script as well as the response formats that are available.
The response formats are mapped to FreeMarker templates through naming convention. So, for example, the FreeMarker template that returns expenses as HTML would be named with an extension of “html” while the one that returns XML would be named with an extension of “xml”.
The descriptor, the server-side JavaScript controller, and the FreeMarker templates that collectively make up a web script can reside either in the Alfresco content repository or on the file system. If a web script controller is written in Java, the class must reside somewhere on the classpath.
With these very basic building blocks you can build a lot of cool stuff. For example, you can use web scripts to expose the Alfresco content repository through a RESTful API to:
- Enable a front-end web application written in any language that can talk HTTP to retrieve repository data in XML, JSON, or any other format or to persist data to the repository;
- Populate JSR-168 portlets;
- Capture user-contributed content/data;
- Create ATOM or RSS feeds for repository content or business process data; and
- Respond to requests for data from the Alfresco Share front-end.
The last thing to mention is that web scripts are executed in a “web script runtime”. There are three runtimes available out-of-the-box:
- The servlet runtime executes all web scripts requested via HTTP/S.
- The JSF runtime allows JSF components to execute scripts.
- The JSR-168 runtime allows portlets to invoke web scripts directly.
You can write your own runtime if these don't meet your needs. Alfresco may add more in the future. This tutorial focuses exclusively on the servlet runtime.
Your First Web Script
Let's walk through some examples. You're going to start with a very simple "Hello World" web script. After that, the examples will get progressively more complex until, at the end, you will have a REST-based interface for creating, reading, and deleting SomeCo whitepaper ratings.
Hello World Example
Let's implement the most basic web script possible: A "Hello World" script that echoes back an argument. You'll need one descriptor and one FreeMarker template. Do the following:
- Log in to Alfresco Share as an administrator.
- In the Repository, navigate to /Company Home/Data Dictionary/Web Scripts.
Create a file called helloworld.get.desc.xml with the following content:
<webscript> <shortname>Hello World</shortname> <description>Hello world web script</description> <url>/someco/helloworld?name={nameArgument}</url> </webscript>
Create a file called helloworld.get.html.ftl with the following content:

Hello, ${args.name}!

- Go to http://localhost:8080/alfresco/service/index and press the "Refresh Web Scripts" button. If you then click the “List Web Scripts” link you should be able to find the web script you just defined.
Now go to http://localhost:8080/alfresco/service/someco/helloworld?name=Jeff. You should see:
Hello, Jeff!
Notice that the URL starts with "/alfresco/service" but the descriptor you created in step 3 defines the URL to be "/someco/helloworld". When you are invoking web scripts running in Alfresco over HTTP/S, the URL will always start with "/[alfresco web context]/service". You may also see the "service" part shortened to "s".
A few things to note. First, notice the file names include “get”. That's the HTTP method used to call the URL. In later examples you'll see how to use POST and DELETE. By differentiating on the HTTP method, you can have multiple controllers for the same “service” depending on which method is used to call the service.
The second thing to notice is that there is only one argument being passed in. You can have as many as you want. Just remember that descriptors must be valid XML which means ampersands must be escaped. So the proper way to define a URL with multiple arguments is:
<url>/someco/helloworld?name={nameArgument}&secondArg={anotherArg}</url>
You can also signify optional parameters by adding a question mark, like this:
<url>/someco/helloworld?name={nameArgument}&secondArg={anotherArg?}</url>
Third, notice that this web script consists only of a descriptor and a FreeMarker template. It's got a view but not a controller. The web script still ran because controllers are optional.
Most web scripts are going to use a controller, though, so go ahead and add one.
Create a file called helloworld.get.js with the following content:
model.foo = "bar";
Update your helloworld.get.html.ftl file with the following content:

Hello, ${args.name}!

Foo: ${foo}

Web scripts with controllers that are being run out of the Data Dictionary require a security context. If you were to try to run this web script now you'd see an Error 500 to that effect. To fix this, modify your descriptor to add an authentication element, like this:
<webscript> <shortname>Hello World</shortname> <description>Hello world web script</description> <url>/someco/helloworld?name={nameArgument}</url> <authentication>guest</authentication> </webscript>
- Go to http://localhost:8080/alfresco/service/index and press the "Refresh Web Scripts" button. This is required because you added a controller that the web script run-time didn't know about.
Now go to your web browser and enter the same URL from the first example which was http://localhost:8080/alfresco/service/someco/helloworld?name=Jeff. You should see:
Hello, Jeff! Foo: bar
If you enter this URL in the same browser as you used to refresh the web scripts, it will run successfully because you already have a session established as an administrator. If you open a new browser window and enter the URL you will get challenged with an authentication dialog. Because you modified the descriptor to run as guest or higher, you can avoid the dialog by appending
&guest=true
to the end of the web script URL.
The web script framework executed the controller. The controller populated the model with a variable called "foo". The web script framework then passed the model on to the view, implemented by the FreeMarker template. The view can access any variables that are placed in the model. In this case, it output the value of foo
. It also has access to the arguments passed to the web script.
The controller can do anything the Alfresco JavaScript API can do. In this case, the controller didn't leverage the JavaScript API at all—it just put some data into the “model” object which was then read by the FreeMarker template. In subsequent examples the controller will have more work to do. Later, you'll see how to use Java instead of JavaScript for the controller.
Deploying web scripts to the repository versus deploying to the classpath
In this quick example you created the descriptor, controller, and view directly in the Alfresco repository through the Alfresco Share user interface. Most of the time, though, you'll be creating several web scripts that will be part of a larger project. Uploading all of those files to an Alfresco production environment leaves an opportunity for error. So, for the rest of the tutorial you'll be deploying your web scripts to the classpath on the file system.
The advantage of using the file system is that the web scripts that make up your solution can be deployed alongside your other extensions without requiring anyone to manually upload them to the repository. The disadvantage is that some changes may require a server restart.
If the same web scripts are defined in the repository as well as the classpath, the files in the repository take precedence over the files on the classpath. The Alfresco wiki documents the search order for web scripts (See “Where to find more information” at the end of this tutorial for a list of references).
Using the web scripts console
As you just saw when you refreshed the list of web scripts, Alfresco comes with a tool for listing and reloading web script definitions. To get to the tool, go to http://localhost:8080/alfresco/service/index. You'll see links that let you browse the list of deployed web scripts. This is a handy debugging tool and it also helps you figure out what web scripts are available out-of-the-box.
You have seen how to create a very simple web script using files created in a text editor or directly in the Alfresco Share user interface. Let's talk about the tools and the project organization next.
Setup
Before getting too far down the road, let me tell you about the tools you'll need and then give you a description of the project organization.
Tools
Here is what I am using on my machine:
- Mac OS X 10.12.6
- Java 1.8.0_77
- Apache Maven 3.5.3 (installed using Macports)
- Alfresco Maven SDK 3.0.1 (No download necessary)
By default, when you create an Alfresco project using the Alfresco Maven SDK the project will be configured to depend on the latest stable Alfresco Community Edition build.
An IDE is optional. Most people working with Alfresco use IntelliJ, Eclipse or something similar.
Project Organization
You just saw that web scripts can be created by creating or uploading files to the Alfresco repository. For the rest of these examples, though, you'll create a project that will package the web scripts as an AMP (Alfresco Module Package). This is the recommended way to distribute your Alfresco customizations. It's a lot less error-prone than handing a bunch of files to someone and asking them to upload them to the proper place in the repository.
I am not going to spend much time talking about how the Alfresco Maven SDK works. If you aren't already familiar with it, you may want to read the Getting Started with the Alfresco Maven SDK tutorial on ecmarchitect.com first and then come back to this one.
If you are planning on following along, go ahead and use the Alfresco Maven SDK to bootstrap your project using the "alfresco-allinone-archetype" archetype. Use a groupId
of "com.someco" and an artifactId
of "webscripts-tutorial".
I'll refer to the directory where you created the project directory as $TUTORIAL_HOME.
One small change is necessary before continuing. Starting with SDK 3.0.0, the default is to output a JAR. But we always want to output AMPs. To configure the project to generate AMPs, edit the pom.xml and uncomment the maven-assembly-plugin.
Next, there are a few dependencies we need to take care of. First, when running the project with the embedded Tomcat server, we want the webscripts-tutorial AMPs to be deployed, but we also want some of the AMPs from previous tutorials to be deployed. Specifically, the content tutorial, behaviors tutorial, and actions tutorial. To make this happen, edit the pom.xml in the "webscripts-tutorial" directory and add the following platform dependencies:
<platformModules>
...SNIP...

<moduleDependency>
<groupId>com.someco</groupId>
<artifactId>content-tutorial-platform-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<type>amp</type>
</moduleDependency>

<moduleDependency>
<groupId>com.someco</groupId>
<artifactId>behavior-tutorial-platform-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<type>amp</type>
</moduleDependency>

<moduleDependency>
<groupId>com.someco</groupId>
<artifactId>actions-tutorial-platform-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<type>amp</type>
</moduleDependency>
...SNIP...
</platform-modules>
We also want the Share AMPs from those same projects to be installed, so a little further down in the same file add the following Share dependencies:
<shareModules>
...SNIP...

<moduleDependency>
<groupId>com.someco</groupId>
<artifactId>content-tutorial-share-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<type>amp</type>
</moduleDependency>

<moduleDependency>
<groupId>com.someco</groupId>
<artifactId>behavior-tutorial-share-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<type>amp</type>
</moduleDependency>

<moduleDependency>
<groupId>com.someco</groupId>
<artifactId>actions-tutorial-share-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<type>amp</type>
</moduleDependency>
...SNIP...
</shareModules>
Finally, the tutorial will have a compile-time dependency on the behavior-tutorial. To address that, edit the pom.xml under "webscripts-tutorial-platform-jar" and add the following dependency:
<dependency>
<groupId>com.someco</groupId>
<artifactId>behavior-tutorial-platform-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
Now we'll be able to refer to Java classes in the behavior tutorial and when running the project the correct AMPs will get installed automatically.
If you have not already checked out the source code from the content, behavior, and actions tutorials, you need to do so, then run mvn install -DskipTests=true
for each so that the artifacts will be installed in your local Maven repo.
Now you're ready to write some web scripts.
Creating Your Own REST API for Ratings
SomeCo wants a REST API that front-end developers can use to find whitepapers and ratings as well as post new ratings. Before diving in, it probably makes sense to rough out the API.
| URL | Method | Description | Response Formats |
|---|---|---|---|
| /someco/whitepapers | GET | Returns a list of whitepapers. | HTML, JSON |
| /someco/rating?id={id} | GET | Gets the average rating for a given whitepaper by passing in the whitepaper's noderef. | HTML, JSON |
| /someco/rating?id={id}&rating={rating}&user={user} | POST | Creates a new rating for the specified whitepaper by passing in a rating value and the user who posted the rating. | HTML, JSON |
| /someco/rating?id={id} | DELETE | Deletes all ratings for a specified whitepaper. | HTML |
The URL pattern must be unique. In this tutorial all URLs will be prefixed with "someco". Alfresco reserves certain package names and URLs for their own use but by incorporating things like your domain name into your URLs and package names, you'll steer clear of those.
When this API is in place, front-end developers can incorporate whitepapers and user-contributed ratings into the SomeCo web site. The following mockups show how the front-end team plans to use the API you're going to build to query for whitepaper and ratings data.
You can't tell from the mockups, but the ratings widget is clickable. When clicked it sends an asynchronous post to the /someco/rating URL described in the table above. When the whitepaper link is clicked, the page below is displayed.
I reused the description from the index page for the Executive Summary. In the real world this would probably be a more lengthy description separate from the introduction on the index page. The “Download this white paper” link uses the standard “Download URL” to give the user direct access to the content.
Listing all whitepapers
As a quick review, recall that SomeCo writes whitepapers and manages those papers with Alfresco. Some whitepapers are published to the web site. A custom aspect called sc:webable
includes a boolean property called sc:isActive
. Whitepapers with the sc:isActive
flag set to true
should be shown on the web site.
Let's write a web script that returns all whitepapers. The list of whitepapers needs to be in two formats: HTML and JSON. HTML will allow you to easily test the service and JSON will make it easy for code on the front-end to process the list. This will require four files:
- one descriptor,
- one JavaScript controller,
- one FreeMarker template for the HTML response, and
- one FreeMarker template for the JSON response.
I'll walk you through the descriptor, controller, and the two views.
Step 1: Write the descriptor
The first step is to create the descriptor file. Web scripts that are deployed in an AMP go in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/extension/templates/webscripts
The Alfresco Maven SDK probably created that directory structure and added an "alfresco" directory with some demo web scripts. Go ahead and delete the example web scripts. We don't need those.
The web script framework allows web script assets to be organized in a hierarchical folder or package structure within that directory. This keeps your web scripts from colliding with other people's web scripts or Alfresco's. Use a reverse domain name pattern just like you would for a Java package. This tutorial will use "com/someco" for its package. This particular web script is about whitepapers so the files that make up the whitepapers web script will reside in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/extension/templates/webscripts/com/someco/whitepapers
To create the descriptor, create a new XML file called "whitepapers.get.desc.xml" in that directory.
Edit the file to have the following content:
<webscript>
<shortname>Get all whitepapers</shortname>
<description>Returns a list of active whitepapers</description>
<url>/someco/whitepapers</url>
<url>/someco/whitepapers.json</url>
<url>/someco/whitepapers.html</url>
<format default="json">extension</format>
<authentication>guest</authentication>
<transaction>none</transaction>
</webscript>
There are a few elements in this descriptor you didn't see in the Hello World example. First, notice that there are multiple url
elements. There is one url
element for each format plus one without a format. This shows how to request a different output format from the same base URL. Because the URLs differ only in format, it isn't strictly required that they be listed in the descriptor, but it is a good practice.
The format
element declares the which extension syntax the web script uses and defines a default output format. In this case, the web script uses the “extension” syntax—the extension on the URL specifies the format of the response. An alternative syntax is to use the “argument” syntax like this:
<url>/someco/whitepapers?format=json</url>
<url>/someco/whitepapers?format=html</url>
Which syntax you use is really up to you. If you want to accept either syntax, you can use “any” as the format.
Using the format
element set to "extension" and the default
attribute set to "json", if someone invokes the URL and passes in the format as an argument instead of as an extension, they'll get an error 500. If someone invokes the web script without specifying a format, they'll get a JSON response.
The authentication
element declares the minimum level of authentication required for this script. If your web script access the repository this must be set to "guest" or higher. Other options are "none", "user", and "admin".
The transaction
element specifies the level of transaction required by the script. Listing whitepapers doesn't need a transaction, so in this example it is set to "none". Other possible values are:
- required: the web script requires a transaction and can be part of an existing transaction if one has already been started
- requiresnew: the web script must be run in its own new transaction
The next step is to write the controller.
Step 2: Write the controller
The controller is where the logic that queries the whitepapers will live. Create a file called "whitepapers.get.js" in the same directory as the controller. Edit the file with the following content:
<import resource="classpath:alfresco/extension/scripts/rating.js">
var whitepapers = search.luceneSearch("PATH:\"/app:company_home/cm:Someco/cm:Whitepapers/*\" +TYPE:\"{http://www.someco.com/model/content/1.0}whitepaper\"");
if (whitepapers == null || whitepapers.length == 0) {
status.code = 404;
status.message = "No whitepapers found";
status.redirect = true;
} else {
var whitepaperInfo = new Array();
for (i = 0; i < whitepapers.length; i++) {
var whitepaper = new whitepaperEntry(whitepapers[i],
getRating(whitepapers[i]));
whitepaperInfo[i] = whitepaper;
}
model.whitepapers = whitepaperInfo;
}
function whitepaperEntry(whitepaper, rating) {
this.whitepaper = whitepaper;
this.rating = rating;
}
The first thing to notice about the script is that it imports another script. The rating.js script was created as part of the custom behaviors tutorial to contain logic related to ratings.
The next thing to notice is that the script queries the repository using Lucene to get a list of whitepapers. Look at what happens if there are no whitepapers found. The response code gets set to 404, which is the standard HTTP response code for “File not found”. Alfresco has standard response templates for error codes but you can override them with your own by creating FreeMarker templates that follow a specific naming convention. For example, you could have a custom 404 response template for whitepapers by creating a file called "whitepapers.get.html.404.ftl". See the official Alfresco documentation on response status code templates for more information.
The last thing that happens is that the web script builds a new Array for the results. The model.whitepapers
variable could just be set to the whitepapers
variable that contains the query results but I want to add some data to the result set, so I'm building a new Array and setting that to the model.
The getRating()
function that is getting called resides in rating.js, which is part of the AMP you created in the custom behaviors tutorial. Just for reference, that function looks like this:
function getRating(curNode, curUser) {
var rating = {};
rating.average = curNode.properties["{http://www.someco.com/model/ratings/1.0}averageRating"];
rating.count = curNode.properties["{http://www.someco.com/model/ratings/1.0}ratingCount"];
rating.user = getUserRating(curNode, curUser);
return rating;
}
The function simply retrieves the scr:averageRating
and scr:ratingCount
properties from the specified node as well as the current user's rating for this object, and returns everything in a single rating object.
Step 3: Write the views
Assuming there are items in the search results, the web script will need FreeMarker templates to return them in the appropriate format. Let's create the HTML response template first. Create a new file called "whitepapers.get.html.ftl" in the same directory as the controller you just created. Edit the file with the following content:
<#assign datetimeformat="EEE, dd MMM yyyy HH:mm:ss zzz">

Whitepapers

<#list whitepapers as child>

Name
${child.whitepaper.properties.name}

Title
${child.whitepaper.properties["cm:title"]!""}

Link
${url.context}${child.whitepaper.url}

Type
${child.whitepaper.mimetype}

Size
${child.whitepaper.size}

Id
${child.whitepaper.id}

Description

<#if child.whitepaper.properties["cm:description"]?exists
&& child.whitepaper.properties["cm:description"] !=
"">${child.whitepaper.properties["cm:description"]}</#if>

Pub Date
${child.whitepaper.properties["cm:modified"]?string(datetimeformat)}

Rating

Average
${child.rating.average!"0"}

Count
${child.rating.count!"0"}

<#if !(child.whitepaper == whitepapers?last.whitepaper)>

</#if>
</#list>

This template iterates through the query results passed in by the controller, and builds an HTML table with properties of each whitepaper. (Yes, the table is ugly. Yes, you could use CSS to spruce it up tremendously or even remove the table entirely. But for SomeCo, this response template is really for debugging purposes only and I didn't want to fool with the CSS so a table it is).
Now create the response template for the JSON response. In the same directory as the HTML response template, create a file called "whitepapers.get.json.ftl" with the following content:
<#assign datetimeformat="EEE, dd MMM yyyy HH:mm:ss zzz">
{"whitepapers" : [
<#list whitepapers as child>
{
"name" : "${child.whitepaper.properties.name}",
"title" : "${child.whitepaper.properties["cm:title"]}",
"link" : "${url.context}${child.whitepaper.url}",
"type" : "${child.whitepaper.mimetype}",
"size" : "${child.whitepaper.size}",
"id" : "${child.whitepaper.id}",
"description" : "<#if child.whitepaper.properties["cm:description"]?exists && child.whitepaper.properties["cm:description"] != "">${child.whitepaper.properties["cm:description"]}</#if>",
"pubDate" : "${child.whitepaper.properties["cm:modified"]?string(datetimeformat)}",
"rating" : {
"average" : "${child.rating.average!"0"}",
"count" : "${child.rating.count!"0"}"
}
}
<#if !(child.whitepaper == whitepapers?last.whitepaper)>,</#if>
</#list>
]
}
Again, just like the HTML response template, the script iterates through the result set, but this one outputs JSON instead of HTML. The JSON structure is completely arbitrary. In the real world, you would work this out with the front-end development team.
Deploying the whitepaper web script
Recall that after you bootstrapped this project from the "all-in-one" archetype, you edited the pom.xml file to add the content, behavior, and actions AMPs as dependencies. That means you can easily test your web script by firing up the embedded Tomcat server and your webscripts will have the dependencies they expect. Plus, you'll be able to log in to Share and create test content, run the custom actions from the actions tutorial, and so on.
The dependencies from the earlier tutorials do the following:
- content-tutorial-platform-jar. This is required because the web script looks for content with the
sc:isActive
flag set and that's defined in the SomeCo Content Model that is part of that project. - behavior-tutorial-platform-jar. This is required because it contains a behavior that calculates the average rating for a piece of content and because it has the rating.js server-side JavaScript file that the controllers in this tutorial import.
- actions-tutorial-platform-jar. This one is optional. It contains a UI action that is used to set the
sc:isActive
flag from within the Alfresco Share user interface.
Running the embedded Tomcat server with these dependencies installed automatically for you is really convenient. At some point, you'll want to deploy to an actual Alfresco installation.
You can deploy these AMPs to your Alfresco installation using the "apply_amps.sh" script. Alternatively, you can use the "alfresco:install" plugin that is part of the Alfresco Maven SDK to install each one.
For example, on my machine, I have all three of those projects checked out from source as well as the webscripts-tutorial-platform-jar project. And, I have Alfresco running in $TOMCAT_HOME with an exploded Alfresco WAR. So, for each project I can do this:
- Switch to $TUTORIAL_HOME/$PROJECT_DIRECTORY
- Run
mvn install
to build the AMP. Usemvn install -DskipTests=true
to skip the unit tests some of those projects may have. - Run
mvn alfresco:install -Dmaven.alfresco.warLocation=$TOMCAT_HOME/webapps/alfresco
to install the AMP to the exploded Alfresco web application. If you are running the alfresco.war unexploded you can specify the path to the WAR file instead.
After doing that for all four projects (the three dependencies and the webscripts-tutorial-platform-jar project) start Tomcat and test.
While you are developing, and for the rest of the tutorial, it's easiest to just use the embedded Tomcat server. You can do that by switching to the "webscripts-tutorial" directory and running run.sh
or run.bat
depending on your platform.
Now that your Alfresco server is running the webscripts-tutorial-platform-jar AMP and its dependencies, you are ready to test out the whitepapers web script.
Testing
You've deployed the project and its dependencies to your Alfresco installation and the server is running. You are now ready to test out the web script. Do this:
- Upload some files to your Alfresco repository. It doesn't matter what they are. The files should go in "Company Home/Someco/Whitepapers". Capitalization matters.
- Change the type on the files you uploaded to "SomeCo Whitepaper". You can save time if you set that up as a rule on your Whitepapers folder before you upload your test files in step 1.
- If you are going to run the web script as a guest user, make sure that "Guest" is given "Consumer" access to the Whitepapers folder.
The web script descriptor says that this web script requires Guest access or higher. That means you have three options for running the web script:
- Log in to Alfresco (/alfresco) before running the web script.
- Run the web script and then authenticate with a valid user and password when the basic authentication dialog is presented.
Run the web script with “?guest=true” appended to the URL, like this:
http://localhost:8080/alfresco/service/someco/whitepapers.html?guest=true
It doesn't matter which one you choose. The tutorial assumes you are going with option 3.
If you forget the ".html" extension in the URL, you'll get a JSON response because the descriptor set that format as the default.
If all goes well you should see something similar to the figure below:
Debugging
Did it work? If not, it's time to debug. Debug logging verbosity is controlled by log4j.properties. There are many log4j.properties files. For turning on JavaScript logging, edit the one in:
$TOMCAT_HOME/webapps/alfresco/WEB-INF/classes
Edit log4j.properties and change the ScriptLogger to DEBUG, like this:
log4j.logger.org.alfresco.repo.jscript.ScriptLogger=DEBUG
This will cause any logger.log statements in your controller to write to catalina.out. If that isn't enough logging for you, you can set:
log4j.logger.org.alfresco.repo.jscript=DEBUG
That will give you debug logging on all server-side JavaScript.
You may decide that you need to step through your server-side JavaScript. Assuming your server is running on a machine with a GUI, you can edit log4j.properties and set:
log4j.logger.org.alfresco.repo.web.scripts.AlfrescoRhinoScriptDebugger=on
Then, when you restart your application server, Alfresco will launch the Rhino JavaScript Debugger in a GUI. You can use it to set breakpoints and step through your JavaScript controller.
Another tool you'll want to leverage is the web script console. The web script console is at:
http://localhost:8080/alfresco/service/index
You can use it to see everything Alfresco knows about your script (and any other scripts it knows about). For example, you can go to:
http://localhost:8080/alfresco/service/script/com/someco/whitepapers/whitepapers.get
and Alfresco will dump everything it knows about that web script, including the descriptor, the controller, and all of the response templates.
The node browser in Alfresco Share Admin Tools can be helpful to debug problems as well. You can get to it by going to:
http://localhost:8080/share/page/console/admin-console/node-browser
In this case, for example, the JavaScript controller runs a Lucene query. If the controller isn't finding any whitepapers even though you've created test data, try executing the query in the node browser. If it doesn't return results, there's something wrong with your test data (or your query). You can use the node browser to navigate to the test data and inspect its type, aspects, and property values.
Remember that if you make changes to the web script without restarting the application server, you may need to use the web script console to refresh the web scripts.
Retrieving the ratings for a specific whitepaper
You have a working web script that fetches a list of whitepapers. Now let's create a web script that retrieves rating summary data for a specific object.
This is pretty easy because of the existing getRating()
function in rating.js. The controller is called "rating.get.js" and it resides in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/extension/templates/webscripts/com/someco/ratings
All the controller has to do is grab the ID of the rateable object that is being requested, locate the node, then call getRating()
as shown below:
<import resource="classpath:alfresco/module/behavior-tutorial-platform-jar/scripts/rating.js">
if (args.id == null || args.id.length == 0) {
status.code = 400;
status.message = "Node ID has not been provided";
status.redirect = true;
} else {
var curNode = search.findNode("workspace://SpacesStore/" + args.id);
if (curNode == null) {
status.code = 404;
status.message = "No node found for id:" + args.id;
status.redirect = true;
} else {
model.rating = getRating(curNode, args.user);
}
}
The descriptor and response templates are very similar to the whitepaper example so I won't include them here. If you want to grab them to copy them into your project, they are:
Now you can deploy and test the rating web script.
Deploying and testing the rating web script
If you are following along, build the AMP, install it, and start up the embedded Tomcat server by running run.sh
or run.bat
.
Now invoke the whitepaper web script you created earlier:
http://localhost:8080/alfresco/service/someco/whitepapers.html&guest=true
The HTML the whitepaper web script returns includes a link that invokes your new rating web script for a specific whitepaper. The link should look similar to:
http://localhost:8080/alfresco/s/someco/rating.html?id=43946f68-1fc6-4d54-b730-33a20633b432&guest=true
If you change the ".html" suffix to ".json", the result should look like this:
{"rating" :
{
"average" : "1.923",
"count" : "13"
}
}
Now you have a web script that can retrieve the rating information for a specific object. Next, you'll add a web script that creates new ratings.
Posting a rating with a Java-backed Web Script
You have seen how to retrieve whitepapers and rating data about specific whitepapers. Now let's create a web script that can create ratings. But first, a few words about authentication.
All of the web scripts created thus far require Guest access or higher. As you've seen, that means the URL has to include "&guest=true" or a session has to be established with a valid login. (A third option would be to pass in a ticket, but that's out-of-scope for this tutorial).
Remember that the goal here is for SomeCo to let users come to its web site, retrieve whitepapers, and rate whitepapers. How will SomeCo web site users be authorized to create whitepaper rating objects? There are three options:
- Set the permissions to allow write access for Guest users on the /Someco/Whitepapers folder. This isn't a good option because SomeCo doesn't want Guests to be able to upload arbitrary content to their repository.
- Set the web script descriptor to use "user" authentication and require Guests to authenticate as registered users before rating whitepapers. This isn't a terrible option but SomeCo doesn't want their web site users to have to register for an account and login before rating whitepapers.
- Let guest users call the web script but tell Alfresco to run the web script as a specific user that does have authorization to create new objects. In this case that user will be "admin" but a user account dedicated to the purpose of creating ratings is probably a better idea in the real world.
Option 3 is the one SomeCo chooses to go with.
Although it isn't required, this example will use a Java-based controller instead of server-side JavaScript so you can see how that works. Even though Java will be used, the steps are the same at a high-level:
- Write the descriptor
- Write the Java controller
- Write the views
The descriptor, together with the two views, reside in the same folder as the GET rating web script you worked on in the previous section, which is:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/extension/templates/webscripts/com/someco/ratings
The controller will be written in the Java source code folder for the project, compiled, and packaged in a JAR as part of the AMP.
Let's look at each of the steps needed to implement the post rating web script.
Step 1: Write the descriptor
The descriptor, rating.post.desc.xml, has two things you haven't seen yet. First, the authentication
element uses the runas
attribute to specify that the web script should be executed as "admin" even though it only requires Guest access or higher to execute:
<authentication runas="admin">guest</authentication>
Note that using runas
in this way only works for web scripts that are loaded from the classpath. For security reasons, it will not work for web scripts uploaded to the repository.
The second piece of the descriptor to look at is the transaction
element. Unlike the GET web scripts you've seen so far, this web script creates new content so the web script will need a transaction:
<transaction>required</transaction>
The descriptor will cause Alfresco to invoke the web script as if it were being run by "admin", regardless of who calls it, and it will run within a transaction. The next step is to write the Java controller.
Step 2: Write the Java controller
This step actually has three parts:
- Write the business logic for creating the rating. That's going to go into a class that doesn't know anything about web scripts so that it is easier to reuse.
- Write a Java class that functions as the web script controller. This class will do some basic error checking and then call the class created in the previous step to actually create a rating.
- Configure the web script controller via Spring so that Alfresco knows to invoke it when the web script is called.
Let's take a look.
Step 2a: Write the business logic for creating the rating
At some point there might be other Java classes that need to create ratings. So the logic that does that should be generic. This class has a dependency on the "behavior-tutorial-platform-jar" project created in an earlier tutorial, so the first thing to do is to edit this project's pom.xml file, which lives in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar
And add the dependency, which looks like this:
<dependency>
<groupId>com.someco</groupId>
<artifactId>behavior-tutorial-platform-jar</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
Create a new package called "com.someco.beans" in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/java
In that package, create a new class called "RatingBean". The class (without the imports) looks like this:
public class RatingBean {
// Dependencies
private NodeService nodeService;
public void create(final NodeRef nodeRef, final int rating, final String user) {
// add the aspect to this document if it needs it
if (nodeService.hasAspect(nodeRef, QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, SomeCoRatingsModel.ASPECT_SCR_RATEABLE))) {
} else {
nodeService.addAspect(nodeRef, QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, SomeCoRatingsModel.ASPECT_SCR_RATEABLE), null);
}
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, "rating"), rating);
props.put(QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, "rater"), user);
nodeService.createNode(nodeRef, QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, SomeCoRatingsModel.ASSN_SCR_RATINGS), QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, "rating" + new Date().getTime()), QName.createQName(SomeCoRatingsModel.NAMESPACE_SOMECO_RATINGS_CONTENT_MODEL, SomeCoRatingsModel.TYPE_SCR_RATING), props);
}
public NodeService getNodeService() {
return nodeService;
}
public void setNodeService(NodeService nodeService) {
this.nodeService = nodeService;
}
}
The create()
method expects a nodeRef
that is being rated, a rating
, and the user
creating the rating. The method checks the node to see if it already has the rateable aspect and adds it if it does not. Then, it sets up the rating and user properties to be set on the rating object. Finally, it calls createNode()
on the Alfresco node service to create the rating as a child of the object being rated.
As you can see, this class does not know that it will be invoked by a web script. It's generic enough to be used by anything that needs to create a rating.
With that in place, you can now create the Java web script controller.
Step 2b: Write the controller class
The create()
method in the RatingBean
class provides a handy method for creating new ratings on any object. Now all we have to do is write a Java controller for the rating POST web script that grabs the id of the object being rated, the rating, and the person creating the rating, and pass all of that to the create()
method.
To do that, create a new package called "com.someco.scripts" in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/java
Within that package, create a new class called "PostRating". The class name isn't significant but following a descriptive convention is helpful as the number of Java-backed web scripts grows.
The class needs to extend org.alfresco.webscripts.DeclarativeWebScript
. The logic goes in executeImpl
as shown below.
public class PostRating extends DeclarativeWebScript {
private RatingBean ratingBean;
private NodeService nodeService;
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req,
Status status) {
String id = req.getParameter("id");
String rating = req.getParameter("rating");
String user = req.getParameter("user");
if (id == null || rating == null || rating.equals("0") || user == null) {
status.setCode(400, "Required data has not been provided");
status.setRedirect(true);
} else {
NodeRef curNode = new NodeRef("workspace://SpacesStore/" + id);
if (!nodeService.exists(curNode)) {
status.setCode(404, "No node found for id:" + id);
status.setRedirect(true);
} else {
ratingBean.create(curNode, Integer.parseInt(rating), user);
}
}
Map<String, Object> model = new HashMap<String, Object>();
model.put("node", id);
model.put("rating", rating);
model.put("user", user);
return model;
}
public NodeService getNodeService() {
return nodeService;
}
public void setNodeService(NodeService nodeService) {
this.nodeService = nodeService;
}
public RatingBean getRatingBean() {
return ratingBean;
}
public void setRatingBean(RatingBean ratingBean) {
this.ratingBean = ratingBean;
}
}
This code should look strikingly similar to a JavaScript controller and in fact it does the same thing. It checks the arguments, sets an error code if the arguments are missing, and then writes some data to the model.
The controller gets the RatingBean
class through Spring dependency injection.
The next step is to configure both of these Java classes in Spring.
Step 2c: Create Spring configuration for the web script controller
The Java controller for the web script exists, but both the controller class, PostRating
, and the bean that contains the business logic, RatingBean
, have dependencies that need to be injected. And, the web script framework needs to know to invoke the PostRating
class when the web script is called. All of that happens through Spring configuration.
You learned in earlier tutorials that the Spring context file for a module is called service-context.xml and it lives in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/module/webscripts-tutorial-platform-jar/context
The Alfresco Maven SDK created that file for you when you used it to bootstrap the project.
Edit that file. You can delete any sample bean elements that may already be in the file. Make the beans
element look like this:
<beans>
<bean id="ratingBean" class="com.someco.beans.RatingBean">
<property name="nodeService">
<ref bean="NodeService" />
</property>
</bean>
<bean id="webscript.com.someco.ratings.rating.post" class="com.someco.scripts.PostRating" parent="webscript">
<property name="ratingBean">
<ref bean="ratingBean" />
</property>
<property name="nodeService">
<ref bean="NodeService" />
</property>
</bean>
</beans>
The first bean
element is simple--the RatingBean
class needs the Alfresco NodeService
so one is being injected.
The second bean
element is where the web script framework finds out about the PostRating
class. The magic is in the id
and parent
attributes. The id
attribute follows a naming convention. The convention is:
webscript.package.service-id.method
Pay close attention to the use of the singular "webscript" here versus the plural "webscripts" in other places. Accidentally using the plural form here is a potential multi-hour debugging session ending in a forehead slap and a “Doh!” if you aren't careful.
It is probably worth mentioning that a decision to use a Java-backed web script doesn't exclude the use of JavaScript for the same web script. If you have both a Java class and a JavaScript file for the same web script, the Java class gets executed first followed by the JavaScript. The script has access to everything the Java class put in the model and can update the model before passing it along to the view. Implementing both a Java-based controller and a JavaScript-based controller for the same web script is potentially confusing to you and the rest of your team so don't do it unless you have good reason, and if you do make sure to document it well.
Step 3: Write the views
The response templates for this web script look like the examples you've seen so far. Like previous examples, this web script can respond with either an HTML or JSON response:
These should look similar to the views you've already created.
Deploying the post web script
This web script is written in Java so you'll need to shutdown the server, build the AMP, and deploy it to your Alfresco server as you've done in prior steps, then start Tomcat back up.
Testing the post web script with curl
Curl, and other types of HTTP clients, are great for testing web scripts. For example, using curl you can post a rating like this:
curl -X POST "http://localhost:8080/alfresco/s/someco/rating?id=802d6f27-82ec-4c9c-8e29-9b6e4a3401ef&rating=5&user=jpotts&guest=true"
That id
is from the node reference of an existing whitepaper.
Running this returns:
{"rating" :
{
"node" : "802d6f27-82ec-4c9c-8e29-9b6e4a3401ef",
"rating" : "5",
"user" : "jpotts"
}
}
Assuming everything is okay, you now have web scripts that can retrieve as well as create content in the repository. Let's finish up the custom ratings API by creating a web script that deletes ratings.
Deleting ratings
This should be old hat by now. Creating a web script to delete the ratings associated with a specific whitepaper is similar to the other web scripts, it's just that this one is invoked with the DELETE HTTP method. Let's look at the descriptor and the controller.
Step 1: Configure the descriptor to restrict the web script to administrators
It seems rare that you would want to delete all ratings for a given node but highly likely that if you are going to expose it, it should be for admins only. So let's tell Alfresco that this web script can only by run by administrators. The descriptor is named rating.delete.desc.xml. It lives in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/extension/templates/webscripts/com/someco/ratings
To restrict the web script to administrators, set the authentication
element like this:
<authentication>admin</authentication>
Now only administrators can execute the web script.
Step 2: Write the controller
As in previous examples, the controller JavaScript, rating.delete.js, reads and checks the arguments then calls a function. In this case it is the deleteRatings()
function that already exists in the rating.js file packaged in the behavior-tutorial project in the behavior-tutorial-platform-jar module. The body of the function is:
function deleteRatings(curNode) {
// check the parent to make sure it has the right aspect
if (curNode.hasAspect("{http://www.someco.com/model/content/1.0}rateable")) {
// continue, this is what we want
} else {
logger.log("Node did not have rateable aspect.");
return;
}
// get the node's children
var children = curNode.children;
if (children != null && children.length \> 0) {
logger.log("Found children...iterating");
for (i in children) {
var child = children[i];
logger.log("Removing child: " + child.id);
child.remove();
}
}
}
The script bails if the node doesn't have the rateable aspect (because there wouldn't be any ratings). Otherwise, it grabs the children and deletes them. Note the important assumption that the only children that exist are ratings. If there's a possibility of other child associations, you'd obviously want to be more discriminating.
Step 3: Create the view
The delete web script has only a single view, rating.delete.html.ftl, and it is pretty boring. It echos back the ID of the object from which the ratings were deleted, then provides a link back to the GET rating web script.
Testing the delete web script with curl
There is no reason to shutdown Alfresco when you deploy this web script. For example, in IntelliJ, I can just do a "Build Project" then re-run the web script. If your change isn't there try using the web scripts console to refresh the list of web scripts and you are ready to test.
One way to test this web script is to use curl. For example, if you know the node reference of a piece of content with ratings, you can grab the ID part of the nodeRef and invoke the DELETE web script like this:
curl -uadmin:admin -X DELETE "http://localhost:8080/alfresco/s/someco/rating/delete.html?id=802d6f27-82ec-4c9c-8e29-9b6e4a3401ef"
Notice that the admin username and password are being passed in. The web script requires an administrator to execute it.
If everything goes as planned you will see something similar to:

Successfully deleted ratings for node: 802d6f27-82ec-4c9c-8e29-9b6e4a3401ef

Back to Ratings

If you then visit the GET rating web script, either in your browser or through curl, you'll see the object no longer has any ratings.
Example summary
You've seen how to implement two GET web scripts (one that retrieves a list of whitepapers and that retrieves a specific rating), a POST web script for creating new ratings, and a DELETE web script for clearing out ratings. At this point SomeCo has everything they need to build a front-end that talks to the Alfresco repository via REST.
One piece of functionality I didn't show, but I've included in the source, is the ability for an optional “user” argument to be passed in to the two GET scripts. When present, the web script will return the last rating for the specified user in addition to the overall rating data.
Invoking web scripts with ajax
SomeCo's custom ratings API is in place but it would be nice to have a user interface for creating ratings. In this section, you'll add a little five star ratings widget to the rating.get.html.ftl template that uses client-side JavaScript to make POSTs against the rating web script to create ratings.
If you have no interest in calling web scripts from client-side JavaScript you can safely skip this entire section. Otherwise, read on.
Here is what the HTML view of the rating GET web script should look like when this is working:
The purpose of the rating widget is two-fold. First, it graphically displays the average rating for a whitepaper. Second, each star in the widget is hot. When one of the stars is clicked, an asynchronous post is made to the rating POST web script which causes a new rating object to get created.
The name of the person submitting the rating would normally be passed in based on some sort of credential, maybe from a portal session or a cookie. In this test form, the rater's name is specified as a plain text field.
To implement this you'll need to:
- Copy the client-side assets the widget needs into the appropriate folder in the AMP.
- Modify the GET rating web script with client-side JavaScript that instantiates the widget and calls the POST web script when it is clicked.
Step 1: Copy the client-side assets into the project
I found a five star widget online written in client-side JavaScript and used that as a starting point (the original site has gone offline). The widget leverages the prototype JavaScript library.
The first step is to copy the JavaScript and images into the AMP project structure. The images reside in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/amp/web/images
And the client-side JavaScript resides in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/amp/web/scripts
Next you'll modify the rating web script to leverage these.
Step 2: Modify the GET rating web script to include client-side JavaScript
Recall that the FreeMarker view for the rating GET web script resides in:
$TUTORIAL_HOME/webscripts-tutorial-platform-jar/src/main/resources/alfresco/extension/templates/webscripts/com/someco/ratings
The file is called rating.get.html.ftl, but let's focus on the main body of the HTML first. Here it is:

Back to the list of whitepapers

Node: ${args.id}

Average: ${rating.average}

# of Ratings: ${rating.count}

<#if (rating.user > 0)>

User rating: ${rating.user}

</#if>
<form name="login">
Rater:<input name="userId"></input>
</form>
Rating:
${rating.average}

Delete ratings for this node

This is all basic HTML/FreeMarker stuff you've seen before. The last line sets up a div
for the ratings widget. The id
of the div
uses the node ref of the whitepaper. This allows multiple ratings widgets to be on the same page and makes it easy for the JavaScript to pass the node ref on to the rating POST web script.
Now take a look at the inline JavaScript. I'm going to omit some of the less interesting functions and just show the functions related to posting ratings.
function submitRating(evt) {
var tmp = Event.element(evt).getAttribute('id').substr(5);
var widgetId = tmp.substr(0, tmp.indexOf('_'));
var starNbr = tmp.substr(tmp.indexOf('_')+1);
if (document.login.userId.value != undefined && document.login.userId.value != "") {
curUser = document.login.userId.value;
} else {
curUser = "jpotts";
}
postRating(widgetId, starNbr, curUser);
}
function postRating(id, rating, user) {
if (receiveReq.readyState == 4 || receiveReq.readyState == 0) {
receiveReq.open("POST", "${url.serviceContext}/someco/rating?id=" + id + "&rating=" + rating + "&guest=true&user=" + user, true);
receiveReq.onreadystatechange = handleRatingPosted;
receiveReq.send(null);
}
}
function handleRatingPosted() {
if (receiveReq.readyState == 4) {
window.location.reload(true);
}
}
Those of you familiar with AJAX techniques may be wondering why I didn't use the prototype library to make the post--it is already already being used with the rating widget. I had trouble getting prototype to play nicely with the web script framework and this particular version of prototype is quite old, so rather than spend time upgrading the library and debugging the problem, I chose to use the lower-level XMLHttpRequest
.
Dealing with the cross-domain scripting limitation
In this example, the Alfresco server and the server serving up the page with the ratings widget are the same machine. In production, Alfresco and the front-end might be different machines. Browsers hitting the front-end web site will be able to make AJAX calls to the front-end servers but not the Alfresco server. This known as the “cross-domain scripting limitation”. Ways to deal with the limitation are widely documented on the Internet. The web script framework does provide a callback mechanism if you choose to go that route.
Conclusion
This tutorial provided an introduction to the Alfresco web script framework. It began with a very simple "Hello World" web script deployed to the content repository and then gradually moved to more complex examples.
By the end of the tutorial you created a REST API for retrieving whitepapers, getting the average rating for a specific whitepaper, posting new ratings for a given whitepaper, and deleting all ratings for a specific whitepaper.
You saw how to use both JavaScript and Java to implement web script controller logic. You used FreeMarker to render web script output in HTML as well as JSON.
Finally, you saw one example of how to invoke web scripts from client-side JavaScript by implementing a five star ratings widget that creates new ratings when clicked. That discussion included options for working around the cross-domain scripting limitation.
Things to Explore on Your Own
There are still topics left to explore. For now, that's up to you. Here are some suggestions for things you might want to investigate further...
- Web scripts are almost self-documenting. Help yourself and your team by giving web scripts a meaningful
description
in the descriptor XML. You can also set thefamily
element to group web scripts to make them easier to find in the web script console. - Web scripts can have an associated "config" file. This is useful when you would like administrators to be able to modify variables or settings for your web script without risking inadvertent changes to your code.
- The individual files that make up a web script can be overridden. For example, suppose you want to change how the HTML view of an out-of-the-box web script works but you don't want to change its descriptor, controllers, or other views. If you create a file in your project under the extension directory that has the same name and file path as Alfresco's, yours will take precedence.
- You can use web scripts to create portlets for portlet containers like like Liferay or JBoss Portal.
- This example showed web scripts running in the "repo" tier but the "share" tier also includes the web script framework. Web scripts are a fundamental building block of the Alfresco Share user interface. Any custom work you do in Share will most likely involve web scripts at some point.
- These examples showed the common case where the web script framework sends the response to the requester in its entirety. But you can also write web scripts that stream their responses.
- With a little bit of work you can write web script controllers in other languages besides JavaScript and Java.
The number of web scripts shipped with Alfresco has grown tremendously over the years. Before writing your own, browse the web script index to see what you might be able to leverage. Be careful, though. Not all out-of-the-box web scripts are meant for public use. The web script console can sort web scripts by "lifecycle". Avoid web scripts marked "deprecated" or "internal".
Where to Find More Information
- The complete source code for these examples is available on GitHub.
- Official documentation for both Enterprise Edition and Community Edition is available at docs.alfresco.com.
- Ask questions about web scripts in the community.
- If you are ready to cover new ground, try another ecmarchitect.com tutorial in the Alfresco Developer Series.
- Learn more about FreeMarker at freemarker.sourceforge.net.
- Learn more about JSON at json.org


Next: Sharpsign Asterisk, Previous: Sharpsign Single-Quote, Up: Sharpsign
#( and ) are used to notate a simple vector.
If an unsigned decimal integer appears between the # and (, it specifies explicitly the length of the vector. The consequences are undefined if the number of objects specified before the closing ) exceeds the unsigned decimal integer. If the number of objects supplied before the closing ) is less than the unsigned decimal integer but greater than zero, the last object is used to fill all remaining elements of the vector.
[Editorial Note by Barmar: This should say "signals...".] The consequences are undefined if the unsigned decimal integer is non-zero and number of objects supplied before the closing ) is zero. For example,
#(a b c c c c) #6(a b c c c c) #6(a b c) #6(a b c c)
all mean the same thing: a vector of length 6 with elements a, b, and four occurrences of c. Other examples follow:
#(a b c) ;A vector of length 3 #(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47) ;A vector containing the primes below 50 #() ;An empty vector
The notation #() denotes an empty vector, as does #0().
For information on how the Lisp printer prints vectors, see Printing Strings, Printing Bit Vectors, or Printing Other Vectors.


About 45 percent of European orders for the CX-30 compact crossover are specified with the SkyActiv-X, a four-cylinder engine with spark-controlled compression ignition, a supercharger, the Miller cycle, and a 24-volt electrical system. The mild-hybrid powerplant develops 180 PS and 224 Nm, which works out at 178 horsepower and 165 pound-feet of torque.
As opposed to the variable-compression turbo four-cylinder engine from Infiniti, there’s no denying Mazda came up with the winning design. The SkyActiv-X is proof that internal combustion is more relevant than ever before, although BEVs and fuel-cell hydrogen vehicles are increasingly popular.
"Since we launched the engine, we are seeing a very encouraging feedback," said Yasuhiro Aoyama as per Automotive News Europe. "60 percent of the orders for the Mazda3 are currently for cars equipped with the Skyactiv-X engine," concluded the chief exec of Mazda's European division. This demand is all the more surprising if you remember that there’s a 3,500-euro difference between the Mazda3 hatchback with the SkyActiv-G and the same car with the revolutionary powerplant.
SPCCI technology can be summed up as the best of both worlds. SkyActiv-X combines the compression ignition of a diesel with the spark ignition of a gasoline engine, and the transition between the two is seamless. Mazda claims the X is up to 10 percent more efficient than the G and 7 percent compared to the 1.8-liter turbo diesel known as SkyActiv-D in Mazda nomenclature.
CO2 emissions? Make that 122 to 157 grams per kilometer under the WLTP, thank you! The Roots-style supercharger on the side of the engine is meant to lean out the air-fuel mixture, thus improving fuel economy while reducing combustion temperatures, NOx emissions, and the amount of fuel.
Mazda would’ve wished to run 80-octane gasoline in the SkyActiv-X rather than 87 to 90 because higher octane makes combustion more difficult. In order to prevent knocking, the spark plugs control exactly when to ignite the air-fuel mixture in the combustion chambers at a compression ratio of 16:1.
Almost the same thing as the Atkinson cycle in hybrids and plug-in hybrids, the Miller cycle of the SkyActiv-X leaves the intake valves open for a portion of the compression stroke. This pushes air and fuel out of the cylinders to reduce the compression ratio, ensuring that knock doesn’t occur. 🏁


Sort
Order
From
To
| Driver | From | To | Departure Time | Is Mine | Join trip |
|---|---|---|---|---|---|
| {{ trip.driverName }} | {{ trip.from }} | {{ trip.to }} | {{ trip.departureDate }} | Join |
| Driver | From | To | Departure Time | Is Mine |
|---|---|---|---|---|
| {{ trip.driverName }} | {{ trip.from }} | {{ trip.to }} | {{ trip.departureDate }} |


QALD is an open challenge that aims at all systems that is mediate between a user (expressing his or her information need in natural language) and structured (in particular RDF) data.
Our goal is to get a picture of the strengths and shortcomings of state-of-the-art systems,
as well as to gain insight into how we can develop approaches that deal with the fact that
the amount of RDF data available is huge, that this data is distributed between different datasets,
and that it is heterogeneous, noisy and sometimes even inconsistent.
Task
The general task for participants is the following one:
Given one or several RDF dataset(s) and natural language questions, return the correct answers or a SPARQL query that retrieves these answers.
Benchmark
The QALD-3 to QALD-5 question sets for multilingual question answering over DBpedia have a DOI.
Please use this when citing the data.
QALD-3:
doi:10.4119/unibi/citec.2013.6
- Reference: DBpedia 3.8
- Languages: English, German, Spanish, Italian, French, Dutch
- 200 questions (train: 1-100, test: 101-200)
QALD-4:
doi:10.4119/unibi/2687439
- Reference: DBpedia 3.9
- Languages: English, German, Spanish, Italian, French, Dutch, Romanian
- 250 questions (train: 1-200, test: 201-250)
QALD-5:
doi:10.4119/unibi/2900686
- Reference: DBpedia 2014
- Languages: English, German, Spanish, Italian, French, Dutch, Romanian
- 420 questions (multilingual QA - train: 1-340, test: 341-390, hybrid QA - train: 391-410, test: 411-420)


THE RAIN MAKER
Review by Luke Buckmaster
After many exciting court thrillers, it comes as somewhat of a surprise that The Rain Maker is a tame yet well acted drama. John Grisham and director Francis Ford Coppola have been careful not to throw in more than its simplistic plot can handle, and although The RainMaker is not as spectacular as A Time to Kill or The Pelican Brief, its a much more complete film in many regards.
Rising Hollywood star Matt Damon plays Rudy Baylor, an inexperienced young lawyer who teams up with Deck Shifflet (Danny DeVito) to bring a shoddy insurance company to justice. Along the way he encounters problems with staying financially secure, protecting a troubled friend from her violent husband and becoming recognized in a town swamped with money hungry lawyers.
After being employed by known sleaze Bruiser Stone (Mickey Rourke), Rudy and Deck quit their job and start small law firm of their own. Rudy soon finds himself relying on a solitary court case to get them through the threat of bankruptcy, and battles high-class lawyer Leo F. Drummond (Jon Voight) to fight for what is morally correct.
With a mixed cast of young and old Hollywood players, it comes as a disappointment that no real climax ever eventuates; and although this well acted drama is of fine tune and taste this alone is not enough to make it a great film. A worth while watch, yes, and although nothing here is ground making material, The RainMaker has enough substance and interest to make it a successful court drama.
3 stars out of 5 Finely crafted and well acted court drama
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


jQuery QueryBuilder
Example
Theme:
Default
Dark
Language:
Albanian
Arabic
Azerbaijani
Bulgarian
Simplified Chinese
Czech
German
Danish
Dutch
English
Farsi
French
Greek
Hebrew
Italian
Norwegian
Polish
Portuguese
Brazilian Portuguese
Romanian
Russian
Spanish
Turkish
Ukrainian
Reset
Set filters
Set:
From JSON
From MongoDB
From SQL
Get:
JSON
SQL
SQL statement
MongoDB
Output


--- title: Current status of a job type: docs repo: https://github.com/rstudio/cloudml menu: main: name: Current status of a job - cloudml parent: cloudml-reference aliases: - /reference/cloudml/job_status.html - /cloudml/reference/job_status.html - /guide/cloudml/reference/job_status.html - /tools/tools/cloudml/reference/job_status.html - /installation/cloudml/reference/job_status.html - /tutorials/cloudml/reference/job_status.html - /guide/tools/cloudml/reference/job_status.html - /deploy/cloudml/reference/job_status.html - /tools/cloudml/reference/job_status.html - /tutorials/tools/cloudml/reference/job_status.html ---
Get the status of a job, as an R list.
job_status(job = "latest")
| job | Job name or job object. Pass "latest" to indicate the most recently submitted job. |
Other job management functions: job_cancel
,
job_collect
, job_list
,
job_stream_logs
, job_trials


Welcome to C++ examples
Max & Min
Max&Min number calculator
Grade
Grade calculator
Array List
Creating and sorting array list
powered by kappasutra ®


Erp Data Visualizer is an open source tool to plot the absent summary data from ERP of ncuindia into beautiful and informative graphs and tables.
We take the ASP Session ID to fetch your absent summary data.
You can find the ASP Session ID in the cookies
after a successful login in erp website.
Data is fetched by calling a Google Cloud Function, We respect privacy, no data is logged and all the processing is done client side.
You can also fetch data using your username and password, all communication are HTTPS encrypted.
You can also take a look at the source code here.
Tip : Enter demo as Session ID to fetch a demo data
OR


 Dot Com
Special thanks to Brandon for original dataset.
See that
original dataset here. Drop them a Venmo.
(Or if you like this website, maybe drop me a Venmo @granthussey)


The cocoa bean or simply cocoa (/ˈkoʊ.koʊ/), which is also called the cacao bean or cacao (/kəˈkaʊ/),[1] is the dried and fully fermented seed of
The cocoa bean or simply cocoa (/ˈkoʊ.koʊ/), which is also called the cacao bean or cacao (/kəˈkaʊ/),[1] is the dried and fully fermented seed of


Comment Tracker - Stand-alone App
Posts / Searches
Add URL
Search G+
Search Twitter
Tracking URL
Not a valid URL... Please try again.
Searching...
Comments / Search results
Show hidden
Oldest first
Colours
Comments / Search results


NuGet Package to monitor table changes
SqlTableDependency is a high-level implementation component to monitor record table changes. It receive notifications containing modified record table values from SQL Server when the content of a specified table change.
If you want to use SQL Server notifications to audit your table change without paying attention to the underlying Service Broker infrastructure, the SqlTableDependency is your choice.
A full documentation can be found on GitHub site at SqlTableDependency.
Nuget package contentSqlTableDependency nuget package contains two dll:
TableDependency.dll contains abstractions and base class, as well as common funtionality.
SqlTableDependency.dll contains concrete implementation for monitor table changes on SQL Server.
InstallationYou can download and install SqlTableDependency from Visual Studio Package Manager Console using the following command:
You can also search and download SqlTableDependency from Manage Nuget Package:
System requirements
SqlTableDependency is open source C# projects built with:
Detect record table changes: Last edit on 22 July 2017 by Christian Del Bianco
For more info on monitor table record changes see SqlTableDependency's GitHub project site.


Happy Hours
Home
About Us
Sign In
▾
Sign Out
Sign In
Add New
Filters
Time
Max Price
Proximity
Zip Code
Filter Now


|
Standard Reference Data Program |
Data Gateway |
Chemistry WebBook |
Go To: Top, References, Notes / Error Report
Data compilation copyright by the U.S. Secretary of Commerce on behalf of the U.S.A. All rights reserved.
Data compiled by: K.P. Huber and G. Herzberg
| State | Te | ωe | ωexe | ωeye | Be | αe | γe | De | βe | re | Trans. | ν00 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Ab initio calculated potential energy curves for the ground and excited states Liu and Verhaegen, 1970. | ||||||||||||
| C 2Σ+ | (35000) | 2150.56 Z | 73.07 | 13.2652 1 | 0.7891 | .0089 | 20.0E-4 2 | -8E-5 | 1.1626 | c → X 3 R | 34561.27 4 | |
| missing citation; Colin and Douglas, 1968; missing citation | ||||||||||||
| B 2Δi | (23300) 5 | (2280) | [13.516] | [19E-4] | [1.1518] | B → X 3 R | 22960.46 4 | |||||
| missing citation | ||||||||||||
| A 2Σ- | (22200) | [1585.49] Z | (61) 6 | -2.87 | 11.4553 7 | 0.6897 | [20.2E-4] 8 | 1.2511 | A → X 3 R | 21567.67 4 | ||
| missing citation | ||||||||||||
| State | Te | ωe | ωexe | ωeye | Be | αe | γe | De | βe | re | Trans. | ν00 |
| a 4Σ- | (500) | [2520] 9 | [14.69] 9 | (17E-4) | [1.105] | (a-X) | 354 4 9 | |||||
| X 2Πr | 0 10 | [2922] 11 | [15.35] 11 12 | (0.64) 11 | [17E-4] 11 | 1.070 |
| 1 | Spin-splitting constants γ0 = +0.105,γ1= +0.108, γ2= +0.111. |
| 2 | H0 ~ +11E-8. |
| 3 | Lifetimes: τ(A)= 1.09 μs Brzozowski, Elander, et al., 1974, τ(B)= 0.98 μs Brzozowski, Elander, et al., 1974, τ(C)= 0.40 μs Brzozowski, Elander, et al., 1974. |
| 4 | Energy of lowest rotational level relative to the lowest level [F1e(1/2)] of the ground state. |
| 5 | A0 = -3.6. |
| 6 | Estimated from the ΔG(1/2) values for NH+ and ND+. |
| 7 | Spin-splitting constants γ0 = -0.097, γ1 = -0.100. |
| 8 | D1 = 18.6E-4, H0 = +l8.5E-8. |
| 9 | From the analysis of perturbations in X 2Π(v=0,1) Colin and Douglas, 1968. |
| 10 | A0 = +77.8. |
| 11 | v=0,1 strongly perturbed by v=0,l, respectively, of a 4Σ- Colin and Douglas, 1968, Krishnamurthy and Saraswathy, 1976. |
| 12 | Λ-doubling in 2Π1/2(v=0,J=1/2); Δ vfe = +0.451 ± 0.010 cm-1. |
| 13 | Theoretical value Liu and Verhaegen, 1970, corresponding to N(4S) + H+. The 2Π ground state must dissociate into N+(3P) + H(2S). |
| 14 | A0 = -3.5. |
| 15 | H0 = +1.5E-8. |
| 16 | Spin-splitting constants γ0 = -0.052, γ1 = -0.053. |
| 17 | D1 = 5.5E-4. |
| 18 | A0 = +78.4. |
| 19 | v=1 strongly perturbed by v=1 of a 4Σ-. |
Go To: Top, Constants of diatomic molecules, Notes / Error Report
Data compilation copyright by the U.S. Secretary of Commerce on behalf of the U.S.A. All rights reserved.
Liu and Verhaegen, 1970
Liu, H.P.D.; Verhaegen, G.,
Electronic states of CH and NH+,
J. Chem. Phys., 1970, 53, 735. [all data]
Colin and Douglas, 1968
Colin, R.; Douglas, A.E.,
New emission band systems of the NH+ molecule,
Can. J. Phys., 1968, 46, 61-73. [all data]
Brzozowski, Elander, et al., 1974
Brzozowski, J.; Elander, N.; Erman, P.; Lyyra, M.,
Lifetimes of excited levels in some important ion-molecules part I: NH+, OH+ and SH+,
Phys. Scr., 1974, 10, 241-243. [all data]
Krishnamurthy and Saraswathy, 1976
Krishnamurthy, G.; Saraswathy, M.,
The emission band system c2Σ+ - x2Π of NH+,
Pramana, 1976, 6, 4, 235-243. [all data]
Go To: Top, Constants of diatomic molecules, References
Copyright for NIST Standard Reference Data is governed by the Standard Reference Data Act.
|
Standard Reference Data Program |
Data Gateway |
Chemistry WebBook |
If you have comments or questions about this site, please contact us.


Top 5 Songs For The End of the World
Rank
Artist
Title
Album
1
R.E.M.
It's the End of the World As We Know It
2
Radiohead
Idioteque
3
Morrissey
Everyday Is Like Sunday
4
Prince
1999
5
The Cure
The End of the World


Overview
Package
Class
Tree
Index
Help
PREV NEXT
FRAMES
NO FRAMES
All Classes
A
C
D
E
F
G
I
M
N
P
R
S
T
U
V
A
actionPerformed(ActionEvent)
- Method in class ParticleSystem.ExampleProgram.
ParticleTestPanel
add(Vector2D)
- Method in class ParticleSystem.Utility.
Vector2D
Adds a second vector to this vector, mutably.
addChangeListener(PropertyChangeListener)
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
addChangeListener(PropertyChangeListener)
- Method in interface ParticleSystem.Utility.
PropertyChangeGenerator
Adds a ChangeListener to the list of listeners that will be notified if the implementing class's state changes.
addParticleEffect(String, ParticleEffect)
- Method in class ParticleSystem.
ParticleManager
addParticleSprite(String, BufferedImage)
- Method in class ParticleSystem.
ParticleManager
arcWidth
- Variable in class ParticleSystem.
SprayEffect
C
CenterOnCoords(Point)
- Method in class ParticleSystem.
ViewScreen
CenterOnObject(Rectangle)
- Method in class ParticleSystem.
ViewScreen
changeListeners
- Variable in class ParticleSystem.ExampleProgram.
ProgramSettings
D
DEFAULT_SCREEN_HEIGHT
- Variable in class ParticleSystem.ExampleProgram.
ProgramSettings
DEFAULT_SCREEN_WIDTH
- Variable in class ParticleSystem.ExampleProgram.
ProgramSettings
CONSTANTS
destroy()
- Method in class ParticleSystem.
Particle
direction
- Variable in class ParticleSystem.
SprayEffect
dotProduct(Vector2D, Vector2D)
- Static method in class ParticleSystem.Utility.
Vector2D
Returns the
dot product
of a pair of vectors.
draw(Graphics)
- Method in class ParticleSystem.
Particle
draw(Graphics, int, int)
- Method in class ParticleSystem.Utility.
Vector2D
Given an origin and Graphics surface to draw on, draws this vector as a line relative to the origin.
draw(Graphics, Point2D.Double)
- Method in class ParticleSystem.Utility.
Vector2D
Given an origin and Graphics surface to draw on, draws this vector as a line relative to the origin.
drawParticles(Graphics)
- Method in class ParticleSystem.
ParticleManager
E
ExplosionEffect
- Class in
ParticleSystem
Write a description of class ExplosionEvent here.
ExplosionEffect(double, double, double, int, ViewScreen)
- Constructor for class ParticleSystem.
ExplosionEffect
F
forceMultiplier
- Variable in class ParticleSystem.
ExplosionEffect
forceMultiplier
- Variable in class ParticleSystem.
ShowerEffect
forceMultiplier
- Variable in class ParticleSystem.
SprayEffect
friction
- Variable in class ParticleSystem.
ExplosionEffect
friction
- Variable in class ParticleSystem.
ShowerEffect
G
getAge()
- Method in class ParticleSystem.
Particle
getGraphicalDetailLevel()
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
getInstance()
- Static method in class ParticleSystem.ExampleProgram.
SpriteLoader
getParticleEvent()
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
getRandomSign()
- Static method in class ParticleSystem.Utility.
Util_Random
Returns either 1 or -1, with equal probability.
getScreenBounds()
- Method in class ParticleSystem.
ViewScreen
getSprite(int)
- Method in class ParticleSystem.ExampleProgram.
SpriteLoader
gravity
- Variable in class ParticleSystem.
ExplosionEffect
gravity
- Variable in class ParticleSystem.
ShowerEffect
gravity
- Variable in class ParticleSystem.
SprayEffect
I
initialize()
- Method in class ParticleSystem.ExampleProgram.
ParticleTestPanel
A method to initialize the graphics and input-related properties of this Panel.
isOnscreen(Point)
- Method in class ParticleSystem.
ViewScreen
isOnscreen(Rectangle)
- Method in class ParticleSystem.
ViewScreen
isParticleAlive()
- Method in class ParticleSystem.
Particle
M
main(String[])
- Static method in class ParticleSystem.ExampleProgram.
ParticleTestMain
minimumAge
- Variable in class ParticleSystem.
ExplosionEffect
minimumAge
- Variable in class ParticleSystem.
ShowerEffect
Move(Point)
- Method in class ParticleSystem.
ViewScreen
N
normalize(Vector2D)
- Static method in class ParticleSystem.Utility.
Vector2D
Given a vector, creates from it a new unit vector, (where the magnitude is exactly 1) but whose direction remains unchanged.
notifyListeners(PropertyChangeEvent)
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
notifyListeners(PropertyChangeEvent)
- Method in interface ParticleSystem.Utility.
PropertyChangeGenerator
Calls the StateChanged() method on each attached ChangeListener, with the provided ChangeEvent message.
P
paintComponent(Graphics)
- Method in class ParticleSystem.ExampleProgram.
ParticleTestPanel
Particle
- Class in
ParticleSystem
Particle(Point2D.Double, Point2D.Double, double, double, double, int, BufferedImage, ViewScreen)
- Constructor for class ParticleSystem.
Particle
particleCoords
- Variable in class ParticleSystem.
Particle
ParticleEffect
- Interface in
ParticleSystem
Abstract class ParticleEvent - write a description of the class here
ParticleManager
- Class in
ParticleSystem
Object which handles the creation, storage, removal, and display of all game particles.
ParticleManager(ViewScreen, int)
- Constructor for class ParticleSystem.
ParticleManager
ParticleSystem
- package ParticleSystem
ParticleSystem.ExampleProgram
- package ParticleSystem.ExampleProgram
ParticleSystem.Utility
- package ParticleSystem.Utility
ParticleTestMain
- Class in
ParticleSystem.ExampleProgram
ParticleTestMain()
- Constructor for class ParticleSystem.ExampleProgram.
ParticleTestMain
ParticleTestPanel
- Class in
ParticleSystem.ExampleProgram
ParticleTestPanel(ProgramSettings)
- Constructor for class ParticleSystem.ExampleProgram.
ParticleTestPanel
ProgramSettings
- Class in
ParticleSystem.ExampleProgram
A collection of top-level program settings
ProgramSettings()
- Constructor for class ParticleSystem.ExampleProgram.
ProgramSettings
ProgramSettings(int)
- Constructor for class ParticleSystem.ExampleProgram.
ProgramSettings
projectAOntoB(Vector2D, Vector2D)
- Static method in class ParticleSystem.Utility.
Vector2D
Returns a new vector that is the length of vectorA, in vectorB's direction.
PropertyChangeGenerator
- Interface in
ParticleSystem.Utility
A complementary interface to Swing's
ChangeListener
interface, used to enforce the ways an implementing object can interact with ChangeListeners.
R
randomGen
- Variable in class ParticleSystem.
ExplosionEffect
randomGen
- Variable in class ParticleSystem.
ShowerEffect
randomGen
- Variable in class ParticleSystem.
SprayEffect
randomPointInCircle(double, Point2D.Double, double, double)
- Static method in class ParticleSystem.Utility.
Util_Random
Generates a point randomly within a circle of the given radius and constraints.
removeChangeListener(PropertyChangeListener)
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
removeChangeListener(PropertyChangeListener)
- Method in interface ParticleSystem.Utility.
PropertyChangeGenerator
Removes a ChangeListener from the list of listeners that will be notified if the implementing class's state changes.
removeParticleEffect(String)
- Method in class ParticleSystem.
ParticleManager
removeParticleSprite(String)
- Method in class ParticleSystem.
ParticleManager
resize(Rectangle)
- Method in class ParticleSystem.
ViewScreen
S
screen
- Variable in class ParticleSystem.
ExplosionEffect
screen
- Variable in class ParticleSystem.
Particle
screen
- Variable in class ParticleSystem.
ShowerEffect
screen
- Variable in class ParticleSystem.
SprayEffect
setGraphicalDetailLevel(int)
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
setMaxParticleNumber(int)
- Method in class ParticleSystem.
ParticleManager
setParticleEvent(int)
- Method in class ParticleSystem.ExampleProgram.
ProgramSettings
ShowerEffect
- Class in
ParticleSystem
Write a description of class ShowerEvent here.
ShowerEffect(double, double, double, int, ViewScreen)
- Constructor for class ParticleSystem.
ShowerEffect
spawnParticles(BufferedImage, Point2D.Double, int)
- Method in class ParticleSystem.
ExplosionEffect
spawnParticles(BufferedImage, Point2D.Double, int)
- Method in interface ParticleSystem.
ParticleEffect
spawnParticles(BufferedImage, Point2D.Double, int)
- Method in class ParticleSystem.
ShowerEffect
spawnParticles(BufferedImage, Point2D.Double, int)
- Method in class ParticleSystem.
SprayEffect
SprayEffect
- Class in
ParticleSystem
Write a description of class ShowerEvent here.
SprayEffect(double, double, double, double, ViewScreen)
- Constructor for class ParticleSystem.
SprayEffect
SpriteLoader
- Class in
ParticleSystem.ExampleProgram
subtract(Vector2D)
- Method in class ParticleSystem.Utility.
Vector2D
Subtracts a second vector to this vector, mutably.
T
triggerParticleEffect(String, String, Point2D.Double, int)
- Method in class ParticleSystem.
ParticleManager
U
update()
- Method in class ParticleSystem.
Particle
update()
- Method in class ParticleSystem.
ParticleManager
updateAge()
- Method in class ParticleSystem.
Particle
updatePosition()
- Method in class ParticleSystem.
Particle
updateVelocity()
- Method in class ParticleSystem.
Particle
Util_Random
- Class in
ParticleSystem.Utility
A class containing static utility methods for generating various random objects and data.
Util_Random()
- Constructor for class ParticleSystem.Utility.
Util_Random
V
Vector2D
- Class in
ParticleSystem.Utility
A vector class with most common operations.
Vector2D()
- Constructor for class ParticleSystem.Utility.
Vector2D
Vector2D(double, double)
- Constructor for class ParticleSystem.Utility.
Vector2D
Vector2D(Point2D.Double)
- Constructor for class ParticleSystem.Utility.
Vector2D
ViewScreen
- Class in
ParticleSystem
Object which manages where the viewable area is, in relation to world coordinates.
ViewScreen(Rectangle)
- Constructor for class ParticleSystem.
ViewScreen
A
C
D
E
F
G
I
M
N
P
R
S
T
U
V
Overview
Package
Class
Tree
Index
Help
PREV NEXT
FRAMES
NO FRAMES
All Classes


2135 Sunset Blvd
Los Angeles, CA 90026
P:
(213) 484-6829
Receipt:
Product
#
Price
Total
Subtotal:
Tax:
$0.00
$0.00
Total:


To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
See the project homepage.
Check out the project repo.
Copyright 2014 Carles Galan Cladera


The playground server is generously funded by ❤ GitHub Sponsors ❤.
If rgeometry is useful for you, consider becoming a sponsor or boosting the project's visibility with a GitHub Star.
Support RGeometry
The playground server is generously funded by ❤ GitHub Sponsors ❤.
If rgeometry is useful for you, consider becoming a sponsor or boosting the project's visibility with a GitHub Star.
Playground Instructions
RGeometry Examples


ARIA Switch Demo
Last updated:
Examples of ARIA Switch buttons.
Pattern Demo
Item 2
Item 3 (purposefully disabled)
Item 4 (purposefully missing aria-label)
Item 5
Pattern Details
A switch is meant to function similarly to a checkbox or toggle button, but assistive technologies are meant to announce it in a manner that is consistent with its on-screen appearance as an on/off sort of UI component.
This particular pattern uses a button
as the base element that is then modified into a role="switch"
with JavaScript. When a button
becomes a switch, the aria-checked
attribute needs to be added, and managed, to appropriately convey the current state to screen reader users.
For a more traditional visual style of a switch UI, as well as further information about switches and how screen readers interpret them, please review the checkbox switch pattern.
For more information on the JavaScript and markup used here, please see the readme on the GitHub repo for this pattern.
Usage note
There are various issues with screen readers and ARIA switches listed in the README. Please review them before using this pattern to create switch components.
Continue reading
For more information about this switches, please read the ARIA specification for the switch role. Inclusive Components Toggle Buttons, by Heydon Pickering.


Healthcare Advisors
Help you with all you need to get Medicare and Medicaid for you and your family
Full-Time Staff of Licensed Master Social Workers (LMSWs)
The Medicare & Medicaid Advisory Group has a full-time staff of caring, professional Licensed Master Social Workers (LMSWs) that will assist, guide, and advocate on your behalf, helping you or your loved one to successfully obtain Medicare & Medicaid. Our LMSWS are experts in navigating the health care system.
Once a LMSW is assigned to your case they will develop a personalized care plan. The next step is to manage all your long-term care needs by communicating with Medicare & Medicaid agencies and health care professionals. Additionally, they provide weekly family “touch base” calls as an automatic built-in support network. This support is a lifetime service that does not diminish in frequency as time goes on.
Our LMSWS are Medicare Minute™ program facilitators, in partnership with the Medicare Rights Center. Medicare Minute™ is a nationwide campaign to bring clarity and empowerment to the disabled and aged by presenting short, timely presentations educating them on matters that affect Medicare beneficiaries on a local level.


Here's a funny joke: Why do you pay a plumber $200 an hour? Because it's worth it! Ha! Hang on, what do you mean that's not funny?
8/11/2009, 7:05:20 PM
Favs: 0
Retweets: 0
link--- layout: tweet ---
Here's a funny joke: Why do you pay a plumber $200 an hour? Because it's worth it! Ha! Hang on, what do you mean that's not funny?
8/11/2009, 7:05:20 PM
Favs: 0
Retweets: 0
link


You have 150 units to wager each week.
Units refresh Monday morning. Units do not carry over, and winning units are not returned to you.
1 - 5
You can bet 1-5 units per bet type, and up to 15 on a single game. Place your unit size according to your confidence.


In this example we want to introduce how to use images and figures within the text.
Inline images, such as , are created simply by adding it in the document.
Figure boxes are created by simply including them in a new paragraph and anchored as a character, without any other textual element in it, followed by a caption paragraph by means of the appropriate default stylesheet, as shown as follows.
This is just another paragraph.


?Proserpinaca
palustris
L.
var.
palustris
Determined
by
Dr.
P.M.
Catling
-
May
2002
Agriculture
&
Agri-Food
Canada
DAO
Herbarium
PLANTS
OF
BAHAMA
ISLANDS
Proserpinaca
palustris
L*
Great
Abaco:
in
water
of
marsh
below
pine
-covered
hills
along
Forest
Drive,
abou
1
mile
north
of
Marsh
Harbour.
July
11,
1974
D.
S.
Correll
J4.2…O…
Herbarium
FAIRCHILD
TROPICAL
GARDEN
01500434
Legend -
Level of confidence that token is an accurately-transcribed word
extremely low
very low
low
undetermined
medium
high
very high