text:
Note: This topic has been added to the official documentation, and so this page is no longer maintained. The official documentation for Web Scripts in Alfresco 5.0 is here: http://docs.alfresco.com/5.0/concepts/ws-presentation-intro.html
Welcome to the Web Scripts WIKI, the home for all Web Script related documentation.
NOTE: This document describes features found in Alfresco v3 onwards (Older versions: v2)
Table of Contents
- 1 Why Web Scripts?
- 2 What's new in Web Scripts v3?
- 3 What is a Web Script?
- 4 Alfresco RESTful API Reference
- 5 How Do I Use a Web Script?
- 6 How Do I Create a Web Script?
- 6.1 Examples and Guidelines
- 6.2 Choosing a URI
- 6.3 Deciding Where to Place Web Script Implementation
- 6.4 Creating a Description Document
- 6.5 Advanced Description Options
- 6.6 URI Templates
- 6.7 Creating a Controller Script
- 6.8 Debugging a Controller Script
- 6.9 Creating a Response Template
- 6.10 Multiple Response Formats
- 6.11 Response Status
- 7 Advanced Implementations
- 8 Java-Backed Implementations
- 9 How Do I Administer Web Scripts?
- 10 Web Script Framework
- 11 Appendix A: Web Script Root Objects
Why Web Scripts?
Web Scripts allow you to:
- Build custom URI-identified and HTTP accessible Content Management Web Services
- Turn your Alfresco repository into a content management powered HTTP Server
- Easily access, manage, and cross-link your content via a tailored RESTful API
You do not need tooling or Java knowledge. All you need is your favourite text editor or the Alfresco Explorer web client: no compilation, generators, server restarts, or complex installs.
- Background
Alfresco Web Scripts (introduced in 2006) brought together the worlds of content repository and the web. There's a blindingly obvious synergy between the two. Consider the web. It's one huge single collection of inter-related documents. Physically, the documents are located in many machines, in many data centers across the globe, stored and managed with many types of software. And yet, to the user in their browser it's one huge collection with easy to click links. This logical simplicity is all due to the underpinnings of the web; HTTP, URIs and HTML (and other types of document e.g. XML). So, what has this to do with ECM repositories. ECM Repositories are containers of documents, important documents that are the heart of Enterprises. However, ECM Repositories have traditionally provided proprietary access points therefore restricting potential use of those documents. This is made worse when ECM Repositories multiply within the Enterprise. Those proprietary access points make it difficult to link documents between repositories. But, wait a minute, users on their home PCs can access the world's huge collection of documents just using their browser. Why can't it be that easy within the Enterprise?
Traditional ECM Repository interfaces are APIs, a collection of programmer methods for interacting with the Repository. These APIs are barriers to accessing the documents held within the Repository. You can't get to the documents unless you have a tool that can speak the API. Documents are often identified by some kind of repository-scoped identifier. This style of interface is broadly known as RPC (remote procedure call). It turns out that RPC is not particularly suited to providing the capabilities of the web where distribution and uniform access of content is key. The web underpinnings of HTTP, URIs and media types follow a style known as REST, which stands for Representational State Transfer. Procedure calls are no longer central. Instead, we have uniquely identified Resources (document, feed, stock price, person) via URIs, representations of those resources (HTML, Atom, JSON) containing links to other resources and a small well-defined uniform interface for access (HTTP GET, POST, PUT & DELETE). So, until ECM Repositories become RESTful, it will be difficult to write applications and services that can access all ECM managed content distributed across the Enterprise. Think back to the web. The very essence of a uniform interface gave birth to Google (a search for the world's documents), Digg (a rating system for the worlds documents) and Delicious (a bookmarking and categorization system for the worlds documents). Within the Enterprise, content oriented services such as Transformation, Encryption, Workflow, Entitlement and Deployment can flourish with a uniform interface.
Web Scripts provide RESTful access to content held within your Alfresco Enterprise Content Repository. This allows you to place controls on your enterprise content to manage it, and at the same time, provide uniform access for a wide variety of client applications and services e.g. browser, portal, search engine or custom application. Due to the inherent distributed nature of this interface, all Alfresco Content Repositories within the Enterprise can resemble one logical collection of inter-related documents, just like the web. And as with any other web citizen, you can apply web technologies such as caching, authentication, proxies, negotiation etc. to your repository resources.
With Web Scripts, you can either build your own RESTful interface using light-weight scripting technologies such as JavaScript and Freemarker, allowing you to arbitrarily map any content in the Repository to resources on the web, or you can use pre-built out-of-the-box Web Scripts that already encapsulate many of the mappings. In fact, CMIS (a proposed standard for ECM web services) is implemented in Alfresco as a series of Web Scripts.
Since 2006, Web Scripts proved themselves exceedingly useful and have been used in all kinds of solutions:
- Integrating Alfresco with 3rd party systems
- Providing feeds
- Developing data services
- Allowing Alfresco to act as a different kind of repository (eg. ActiveResource)
- Developing UI services such as portlets
- Customizing search
- Acting as a back-end to client tools such as Orbean Forms
- Office integration
- Facebook application development
- UI component building in Alfresco SURF, a web site construction platform
What's new in Web Scripts v3?
- Web Script framework now a stand-alone component allowing it to host Web Scripts in tiers other than the Alfresco Repository e.g. within Alfresco SURF
- URI Templates now comply with JSR-311 (JAX-RS)
- Run As User
- Ability to Customize Web Scripts with Web Script-specific Configuration
- Per Web Script Message Bundle
- Improved Form Processing (Multi-part Form fields also mapped to args/argsM)
- Access to Request Headers
- Request Body Processing
- Content Negotiation
- Declarative and programmatic Cache Control
- Web Script Families
- Specialized Kinds of Web Script
What is a Web Script?
A Web Script is simply a service bound to a URI which responds to HTTP methods such as GET, POST, PUT and DELETE. While using the same underlying code, there are broadly two kinds of Web Scripts.
Data Web Scripts
Data Web Scripts encapsulate access and modification of content/data held in the repository therefore are provided and exposed only by the Alfresco Repository server. They provide a repository interface for client applications to query, retrieve, update and perform processes typically using document formats such as XML & JSON. Out-of-the-box, Alfresco provides a series of Data Web Scripts e.g. for tagging, activities, site management...
Presentation Web Scripts
See the Web Scripts FAQ for more information.
The interface of a Web Script comprises of a URI, HTTP methods, and document types. This is all you need to know if you're just using a pre-built Web Script. However, if you want build your own RESTful interface, it has been made as simple as possible to construct your own Web Script. You do not need tooling or Java knowledge. All you need is your favourite text editor or Alfresco Explorer: no compilation, generators, server restarts, or complex installs.
A Web Script implementation consists of the following components (often summarized as MVC):
- A description document, which describes the URI that will initiate the script. For example, the service is given a short name and description, along with authentication and transactional needs. URI bindings are described as URI templates.
- An optional controller script, written in JavaScript, which will do the actual work. For example, the script may query the Alfresco repository to build a set of data items, known as a model to render in the response, or for URIs that intend to modify the repository (hopefully, PUT, POST & DELETE method bindings), the script may update the Alfresco repository. The JavaScript has access to all URI arguments, Alfresco services and repository data entry points.
- One or more FreeMarker response templates, known as views (or Representations in REST parlance) that will render output in the correct format for your specific needs. For example, HTML, ATOM, XML, RSS, JSON, CSV, or any combination of these. The URL response is rendered via one of the supplied templates where the chosen template is based on the required response content-type or status outcome. The template has access to all URI arguments, common repository data entry points and any data items built by the optional controller script.
Advanced implementations may fall-back to Java. The different components (description doc, controller, and templates) are tied together through a document convention.
Web Scripts are registered and executed by the Web Scripts Framework.
Alfresco RESTful API Reference
Out-of-the-box, Alfresco provides several RESTful APIs (built as Web Scripts):
- Repository API Reference - remote services for interacting with the Alfresco Repository
- TODO: References to older and future versions of API?
- OpenSearch
- CMIS API Reference - Content Management Interoperability Services
- Portlets such as My Inbox, My Checked-Out for hosting in any Portal
- Office Integration for hosting in Microsoft Office
How Do I Use a Web Script?
This section focuses on using pre-built Web Scripts. You will need to understand:
- What is a URI
- How to invoke HTTP methods such GET & POST from your environment of choice
- Optionally, how to construct or parse XML & JSON if using Data Web Scripts
Basically, Web scripts are URI & HTTP accessible, so it is a case of putting together the correct HTTP request. Each Web Script is uniquely identified by its URI, although some Web Scripts may support multiple URIs.
Where Can I Use a Web Script?
Web Scripts may be used in a number of environments:
- HTTP Client (e.g. Web Browser, HTTP Client API)
- Alfresco SURF
- Alfresco Explorer web client
- JSR-168 Portal
- JSF Page
- Facebook Application
Anatomy of a Web Script URI
Web Script URIs are of the form:
host - name or address of server hosting the Web Script
port - port where Web Script is exposed on server
contextPath - path where application is deployed to, generally /alfresco
servicePath - path where Web Script service is mapped to, generally /service
scriptPath - path to Web Script (as defined by Web Script)
scriptArgs - arguments to pass to Web Script
Some examples:
Depending on where a Web Script is invoked, the URI may be absolute or relative. In some cases Web Scripts may be deployed to the root application which means the contextPath is not required.
Some environments (such as Alfresco SURF and the Web Script Portal Runtime) only require the script path to identify a Web Script.
The servicePath is generally mapped to /service. However, the means for authenticating may be controlled via this path.
Listing Available Web Scripts
An index of all registered Web Scripts is available at the following URIs:
A description of each registered Web Script is provided including its URI(s) for calling the Web Script. Some URIs contain {tokens} - those are also valid URIs - they are expressed as URI Templates allowing for the URI path to be parameterized.
The index provides a view of Web Scripts by:
- Package
- Family
Also, if there happens to be an issue with the registration of a Web Script, the index also provides a list of Web Scripts that failed registration.
Note: This index page is itself implemented as a Web Script
Note: The Alfresco RESTful API Reference pages in this WIKI are just index pages rendered as MediaWIKI markup instead of HTML
Web Scripts may require the caller to be authenticated. Each Web Script dictates its own authentication requirements. If a Web Script requires authenticated access and the call is unauthenticated an appropriate authentication process is initiated. Upon successful authentication the Web Script is invoked.
The style of authentication depends on the environment within which the Web Script is hosted e.g. within a servlet run-time HTTP Auth may be used. Within a portal run-time, the portal may decide.
- Repository Authentication Options
When Web Scripts are hosted within the Repository server two options are available for authentication.
- HTTP BASIC Authentication - the authentication process follows the handshake as defined by RFC2617
- Alfresco Explorer Authentication - the authentication process supported by the Explorer Web Client, that is the authentication mechanism as defined by the alfresco global properties authentication.chain.
The first is useful for external systems that wish to programmatically invoke Alfresco RESTfully (that is, using standard HTTP mechanisms), whereas the second is useful when single sign on is required across both the Alfresco Explorer Client and the Web Script, such as linking from the Explorer to a Web Script page.
The caller can choose which authentication option by specifying an appropriate <servicePath> in the Web Script URI.
- /service => HTTP Basic Authentication e.g. http://localhost:8080/service/api/path/Workspace/SpacesStore/Company%20Home/children
- /wcservice => Alfresco Explorer Authentication e.g. http://localhost:8080/wcservice/api/path/Workspace/SpacesStore/Company%20Home/children
Note: In both cases, the successful authentication may be remembered in the caller session, thus only requiring the authentication process to be initiated once
- Pre-Authorized Calls
To force authenticated access specify the following URI argument:
For example
Tickets may be established by the calling environment (e.g. Alfresco SURF) or explicitly using the Login Web Script APIs.
- Guest Access
Web scripts may allow guest access. To force guest access specify the following URL argument:
For example
Content Negotiation and Response Formats
Each Web Script returns its result in a reponse stream. The result may be encoded in one or more formats (i.e. mimetypes) such as HTML, XML or JSON depending on the capabilities of the invoked Web Script.
If a Web Script call does not provide any hints on which response encoding to use, the Web Script uses its pre-defined default encoding. However, a client can provide the following hints:
- URL extension e.g. http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home/children.atom
- Format Argument e.g. http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home/children?format=atom
- Accept Header (as defined by HTTP 1.1)
Hints 1 & 2 specify a Web Script Format which a registered short name for a mimetype. The typical mimetypes are mapped:
- html => text/html
- text => text/plain
- xml => text/xml
- atom => application/atom+xml
- rss => application/rss+xml
- json => application/json
Hint 3 is typically provided by a Web Browser, although may be controlled programmatically via many HTTP client libraries and is specified as a comma-separated of mimetypes and preferences.
Note: Each Web Script dictates which of the above hints are adhered to via its Web Script Description
Tunneling HTTP Methods
Not all clients can issue all HTTP methods. In the most severe case, a client may be restricted to GET and POST only. In this situation, web scripts provide two mechanisms for issuing any HTTP method via POST:
- HTTP Request Header 'X-HTTP-Method-Override'
- URI parameter 'alf_method'
Note: If both the header and query parameter are specified in the request, the header takes precedence over the query parameter.
In either case, the value is set to the HTTP method name to be invoked.
For example, if the HTTP DELETE method is not supported by a client, but the client wishes to delete a repository folder, the following may be issued:
POST X-HTTP-Method-Override=DELETE http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home/
POST http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home?alf_method=DELETE
Forcing Success Response Status
Not all clients can gracefully handle non-success HTTP Responses, for example, the Flash runtime player - the runtime for Adobe Flex applications.
In this situation, web scripts provide a mechanism to force all HTTP Responses to indicate success in their Response Header. However, the Response body will still represent the content as if a non-success status had occurred.
To force HTTP Response Header success set the following HTTP Request Header:
- alf-force-success-response = true
JSON Callbacks
If your web scripts provide JSON responses, it is likely they will be invoked directly from within a Web Browser via the XMLHttpRequest object. However, due to security reasons, you may run into cross-domain issues, a restriction that requires you to proxy your requests on the server side. Typically, to workaround these issues, public services such as Yahoo! JSON Services provide a callback mechanism.
Web scripts also provide this mechanism which wraps the JSON output text in parentheses and a function name of your choosing. A callback is invoked by adding the following URL parameter on JSON URL requests:
- alf_callback=function
where function is the name of a client-side JavaScript function to be invoked.
The following simple HTML page demonstrates this:
This will result in the JSON response looking like this:
showTitle( ...json output... );
NOTE: For security reasons, this mechanism is disabled by default. To enable it on any SpringWebScripts container, set the bean property: allowCallbacks = true, for example:
<bean id='webscripts.container' parent='webscripts.abstractcontainer' class='org.springframework.extensions.webscripts.PresentationContainer'>
<property name='name'><value>Spring Web Scripts Container</value></property>
<property name='allowCallbacks'><value>true</value></property>
Web Scripts comply with HTTP Caching supporting the notions of Last Modified Time and ETag allowing a Web Script response to be cached. A cache manager (suitable for the client environment) can interpret the cache controls (if any) returned with the Web Script response and cache appropriately.
A typical scenario is where a Repository-hosted Web Script is invoked via HTTP. In this case, any HTTP caching proxy may be used between the client and Alfresco Repository Server.
Note: Each Web Script dictates its caching controls, if any
How Do I Create a Web Script?
This section demonstrates how to create a Web Script. The Web Script in question is a simple Hello World example requiring authenticated access which simply greets the user in both HTML and XML.
You will need to understand:
- XML for expressing the Web Script description
- Optionally, JavaScript for writing Web Script behaviour
- Freemarker for rendering a Web Script response
The only tools you'll need are an Alfresco Repository server and the Alfresco Explorer web client. Alternatively, advanced Web Scripts may be developed in Java.
Examples and Guidelines
Before diving into the technical bit, you may want to browse or refer to the available Web Scripts Examples.
If you're really impatient, you can create your first Web Script within the next minute or so.
You may also wish to refer to the Alfresco guidelines for designing RESTful Web Scripts.
Choosing a URI
First, choose a URI and HTTP method for your Hello World Web Script...
GET /<contextPath>/<servicePath>/sample/helloworld?to={name?}
The URI is under your control, but must be unique.
Note the following URL namespaces are reserved for Alfresco use:
Deciding Where to Place Web Script Implementation
With a URI decided, it is necessary to bind a Web Script implementation to it, consisting of the following files:
- A Description Document
- A Controller Script (optional)
- One or more Response Templates
- A Configuration Document (optional)
- A Locale Bundle (optional)
Our example Hello World implementation will consist of the following files:
These files need to be stored in a folder somewhere. They can live in either the Alfresco Repository or the Java ClassPath. The following folders are listed in the sequence in which Alfresco searches for Web Script implementation files:
- repository folder:
/Company Home/Data Dictionary/Web Scripts Extensions
- repository folder:
/Company Home/Data Dictionary/Web Scripts
- class path folder:
- class path folder:
Within any of these folders, you can use subfolders to organize web scripts.
New web scripts will not be visible until the 'Refresh Web Scripts' link is clicked on the /alfresco/service/ page.
Warning! do not store web scripts in:
or any of its subfolders - these folders are reserved for Alfresco use.
Creating a Description Document
Our URI is described by creating an .xml description document in one of the above Web Script folders. For now, assume all files are placed into the repository folder: /Company Home/Data Dictionary/Web Scripts.
Within any Web Script folder, sub-folders may be used to organise Web Scripts. For example, we can place our Hello World Web Script in the folder
/Company Home/Data Dictionary/Web Scripts/org/alfresco/sample
and within this folder, create the file
Naming is important. Web Scripts employ convention over configuration for their definition to reduce the amount of required typing. The postfix .desc.xml informs Alfresco that this is a Web Script description document.
The complete folder and file name
- A package of org/alfresco/sample
- A service id of helloworld
- A binding to the HTTP GET method
The content of the Hello World Web Script description document is defined as follows:
<shortname>Hello World</shortname>
<description>Greet a user</description>
<format default='html'>extension</format>
- shortname is a human readable name for the Web Script
- description (optional) is documentation for the Web Script
- url (one or more) is a URI Template to which the Web Script is bound; variants of the URI template which specify a format do not need to be registered, however, they are useful for documentation purposes
- format (optional) controls how the content-type of the response can be specified via the URI; valid values are:
- argument the content-type is specified via the format query string parameter, for example /helloworld?to=dave&format=xml
- extension the content-type is specified via the URI extension, for example /hello/world.xml?to=dave
- any either of the above can be used
- Note: if not specified, the default value is any
- Note: if the caller does not specify a required content-type at all, the default content-type is taken from the (optional) default attribute of the format element. By default, if not set, the html format is assumed. In some cases, a URI may decide upon a response content-type at runtime. For these URIs, specify an empty format, for example format default=''
- argument the content-type is specified via the format query string parameter, for example /helloworld?to=dave&format=xml
- authentication (optional) is the required level of authentication; valid values are:
- none specifies that no authentication is required at all
- guest specifies that at least guest authentication is required
- user specifies that at least named user authentication is required
- admin specifies that at least a named admin authentication is required
- Note: if not specified, the default value is none
- Note: The optional runas attribute can be used to force the execution of a web script as a specific user. This can only be specified for web scripts that are stored in the Java Class path.
- none specifies that no authentication is required at all
The description provides Alfresco with all the necessary information to bind the web script to one or more URIs. It also provides enough to document the Web Script.
Advanced Description Options
Description documents also provide the following options, which are important, but not as frequently used or mandated.
- transaction (optional) is the required level transaction; valid values are:
- none specifies that no transaction is required at all
- required specifies that a transaction is required (and will inherit an existing transaction, if open)
- requiresnew specifies that a new transaction is required
- Note: If not specified, the default value is none if authentication is also none, otherwise the value is required
- none specifies that no transaction is required at all
- family (optional) is a tag for categorizing similar or related web scripts. The family tag may be repeated if this script belongs to more than one family.
- cache (optional) is the required level of caching; valid child elements are:
- never (optional) specifies whether caching should be applied at all; valid values (optional) are:
- true (default) specifies the web script response should never be cached
- false specifies the web script response may be cached
- Note: If never is not specified, the default is true
- true (default) specifies the web script response should never be cached
- public (optional) specifies whether authenticated responses should be cached in a public cache; valid values (optional) are:
- true (default) specifies the web script authenticated response may be cached in a public cache
- false specifies the web script authenticated response may not be cached in a public cache
- Note: If public is not specified, the default is false
- true (default) specifies the web script authenticated response may be cached in a public cache
- mustrevalidate (optional) specifies whether a cache must revalidate its version of the web script response in order to ensure freshness; valid values (optional) are:
- true (default) specifies that validation must occur
- false specifies that validation may occur
- Note: If mustrevalidate is not specified, the default is true
- true (default) specifies that validation must occur
- never (optional) specifies whether caching should be applied at all; valid values (optional) are:
- negotiate (zero or more) associates an Accept header MIME type to a specific web script format of response; the (mandatory) value specifies the format while the mandatory accept attribute specifies the MIME type
- Note: Content Negotiation is enabled with the definition of at least one negotiate element
- Note: Content Negotiation is enabled with the definition of at least one negotiate element
- kind (optional) is an attribute discriminator for overriding the kind of web script. See list of available web script kinds.
- lifecycle (optional) is an attribute for describing the lifecycle of the web script. Web scripts typically start out in some sort of draft state, are promoted to full production use, and are then retired at the end of their life.
- noneThis web script is not part of a lifecycle.
- sample This web script is a sample and is not intended for production use.
- draft This method may be incomplete, experimental, or still subject to change.
- public_api This method is part of the Alfresco public API and should be stable and well tested.
- draft_public_api This method is intended to eventually become part of the public API but is incomplete or still subject to change.
- deprecated This method should be avoided. It may be removed in future versions of Alfresco.
- internal This script is for Alfresco use only. This script should not be relied upon between versions. It is likely to change.
- noneThis web script is not part of a lifecycle.
An example of a full web script description follows:
<webscript kind='org.alfresco.httpsonly'>
<shortname>Hello World</shortname>
<description>Greet a user</description>
<format default='html'>extension</format>
<authentication runas='fred'>user</authentication>
<negotiate accept='text/html'>html</negotiate>
<negotiate accept='text/xml'>xml</negotiate>
URI Templates
A URI template is simply a URI containing tokens which may be substituted with actual values. The URI template syntax is minimal and simple, and as of Alfresco v3, complies with the template syntax of JSR-311 (JAX-RS). Common forms of URI template include:
A token of the form {<name>?} means it is optional. Typically, tokens are used for URI arguments, but may also be used for path segments.
Note the third example passes two parameters to the Web Script. Normally an ampersand (&) is used to separate parameters, though in your XML Web Script Description document one must use the escaped literal '&' instead.
For now, Web Scripts do not enforce mandatory arguments. However, it is still useful to provide a full URI template for documentation purposes.
URI templates are specified relative to http://<host>:<port>/<contextPath>/<servicePath>.
When a URI request is made, Alfresco locates the appropriate Web Script by finding the closest match to a URI template in a Web Script description document.
Given the following URI Templates:
1 /a
2 /a/b
3 /a/{val1}
4 /a/{val1}/b
5 /a/{val1}/b/{val1}
6 /a/{val1}/b/{val2}
The following URIs match the above templates as follows:
/a => template 1
/a/b => template 2
/a/c => template 3
/a/b/b => template 4
/a/c/b => template 4
/a/c/b/c => template 5
/a/c/b/d => template 6
However, the following URIs do not match any of the above templates:
As an implementor of a Web Script you have access to the values provided for tokens in the URI template e.g. access to the values for {val1} and {val2}. The values are available in both the Web Script Controller Script and Response Templates via the root object url.templateArgs.
When using java backed controllers the tokens can be accessed through the
org.alfresco.web.scripts.WebScriptRequest.getServiceMatch() API. In Community Edition 3.2r2 this will return a
org.alfresco.web.scripts.Match instance that has a
getTemplateVars() method returning a Map of all tokens.
Creating a Controller Script
A Web Script may optionally execute some JavaScript on invocation of its respective URI. The JavaScript can perform queries or updates against a back-end store. Also, the JavaScript may build a data model that is passed to the appropriate Web Script response template for subsequent response rendering.
For our example Hello World service we create the following file in the same folder as the Web Script description document
Again, naming conventions apply where controller script file names have the following structure
The following JavaScript root objects are available to all Web Scripts, regardless of their hosting environment.
- args: an associative array of all URI parameters. See example.
- argsM: an associative array of all URI parameters (where each key is an argument name and each value is an array containing all respective argument values, even if only one is supplied). See example.
- headers: an associative array of all request headers.
- headersM: an associative array of all request headers (where each key is an header name and each value is an array containing all respective header values, even if only one is supplied). See example.
- url: provides access to the URI (or parts of the URI) that triggered the web script.
- model: An empty associative array which may be populated by the JavaScript. Values placed into this array are available as root objects in Web Script response templates.
- status: provides ability to control response status codes.
- cache: provides ability to control caching of response.
- config: provides access to the Web Script configuration.
- msg: provides access to Web Script localized messages.
- format: provides information on the response format to be rendered.
- guest: A boolean indicating whether the web script is executing as 'Guest'.
- webscript: An associative array of meta-data properties describing the Web Script.
- server: An associative array of meta-data properties describing the server hosting the Web Script.
- jsonUtils: A host object for parsing and generating JSON objects.
- atom: A host object for parsing and generating Atom (Publishing) documents.
- logger: A host object providing access to console logging facilities for debugging of scripts. See the Logging API.
Web Scripts hosted within the Alfresco Repository tier also have access to the following root objects which provide direct access to Repository services and content.
- roothome: the repository root node (only available if authenticated)
- companyhome: the company home folder (only available if authenticated)
- person: the person node of the currently authenticated user (only available if authenticated)
- userhome: the user home folder (only available if authenticated)
- search: A host object providing access to Lucene and Saved Search results. See the Search API.
- people: A host object providing access to Alfresco people and groups. See the People API.
- actions: A host object providing invocation of registered Alfresco Actions. See the Actions API.
- session: Session related information such as the current authentication ticket. See the Session API.
- classification: Access to the root elements of the classification API. See the Classification API.
- utils: Access to a library of useful helper functions not provided as part of generic JavaScript. See the Utility Functions.
- avm: Access to WCM objects such as AVM paths and searching within AVM stores and web projects. See the AVM API.
- crossRepoCopy: Cross repository copy support. See the Cross Repository Copy.
- workflow: Start workflows and access, control in-flight workflows. See the JavaScript API#Workflow API.
Web Scripts hosted within the presentation tier (i.e. within Alfresco SURF) have their own extras as described in the Surf Platform - Freemarker Template and JavaScript API.
For our example Hellow World, we can write the following JavaScript to either greet the currently authenticated user via their user name, or via the name provided as an URI argument:
model.toWho = (args.to != null) ? args.to : person.properties.userName;
The result of the script is added to the model root object and given the name who. Web Script response templates rendered after the execution of the JavaScript will now also have the root object named who, which in this case represents the name of the person to greet.
Of course, a Web Script can also perform updates, in which case, the model root object can be used to record items that have been updated, status, and so on.
Debugging a Controller Script
The Alfresco Server provides a built-in Javascript debugger.
Creating a Response Template
The final stage of a Web Script is to render a response for the HTTP request. Multiple response formats may be provided. Responses are rendered by FreeMarker templates.
For our example Hello World service, we create the following file in the same folder as the Web Script description document
Again, naming conventions apply where response template file names have the following structure
- format specifies the format of the response template whose value is one of the following formats.
The following FreeMarker template root objects are available to all Web Scripts, regardless of their hosting environment.
- args: an associative array of all URI parameters. See example.
- argsM: an associative array of all URI parameters (where each key is an argument name and each value is an array containing all argument values, even if only one is supplied). See example.
- headers: an associative array of all request headers.
- headersM: an associative array of all request headers (where each key is an header name and each value is an array containing all respective header values, even if only one is supplied). See example.
- url: provides access to the URI (or parts of the URI) that triggered the web script.
- status: provides ability to control response status codes.
- cache: provides ability to control caching of response.
- config: provides access to Web Script configuration.
- messages: a JSON representation of all localized messages for the Web Script.
- format: provides information on the response format to be rendered.
- guest: A boolean indicating whether the web script is executing as 'Guest'.
- webscript: An associative array of meta-data properties describing the Web Script.
- server: An associative array of meta-data properties describing the server hosting the Web Script.
- date: A date representing the date and time of the web script request.
Web Scripts hosted within the Alfresco Repository tier also have access to the following template root objects which provide direct access to Repository services and content.
- roothome: the repository root node (only available if authenticated)
- companyhome: the company home folder (only available if authenticated)
- person: the person node of the currently authenticated user (only available if authenticated)
- userhome: the user home folder (only available if authenticated)
Web Scripts hosted within the presentation tier (i.e. within Alfresco SURF) have their own extras as described in the Surf Platform - Freemarker Template and JavaScript API.
And remember, the FreeMarker template also has access to any root objects created by the controller script, if one has been associated with the Web Script.
Web script templates also have access to the following methods:
- absurl(url): Returns an absolute URL representation of the passed url. Useful when rendering links within ATOM (and similar formats).
- xmldate(date): Returns an ISO8601 formatted result of the passed date. Useful when rendering dates within XML.
- scripturl(queryString): Returns a url that references this web script. The passed queryString is added to the url. System arguments such as guest and format are automatically added. Note, this method is particularly useful for protection against the runtime environment within which the web script is executing. In some environments (such as a Portal) the url may be encoded.
- clienturlfunction(funcName): Generates a client-side JavaScript function that can generate a URL back to this web script.
- argreplace(argString, argName, argValue, ...): Replace specified argName with argValue, or add argName if it does not exist in argString.
- encodeuri(uriString): encode string into URL-safe form.
For our example blog search, we can write the following FreeMarker to render the HTML response:
At ${date?datetime}, ${person.properties.userName} says hello to ${toWho?html}
Note how the above template makes use of standard root objects, for example ${date?datetime} and root objects created by the controller script, for example ${toWho?html}.
Multiple Response Formats
Multiple response formats are simply supported by creating further template files following the naming convention outlined above.
Note: New and updated Templates are automatically registered and will be honoured when the Web Script is next invoked.
For our Hello World example, we can support add a machine-readable XML response by adding the following file
whose content is
With our Hello World example description document and associated templates, the following URI mappings are available:
- HTML template: /<contextPath>/<servicePath>/sample/helloworld?to=friend
- HTML template: /<contextPath>/<servicePath>/sample/helloworld?to=friend&format=html
- HTML template: /<contextPath>/<servicePath>/sample/helloworld.html?to=friend
- XML template: /<contextPath>/<servicePath>/sample/helloworld?to=friend&format=xml
- XML template: /<contextPath>/<servicePath>/sample/helloworld.xml?to=friend
Response Status
Web Scripts use HTTP response status codes to:
- inform a client of an error situation (for example, item not found)
- inform a client of an occurrence of an event (for example, item created)
- instruct a client to perform a follow-up request (for example, authorization required)
- inform a client of success
Response status codes are set in the controller script via the status root object.
We can add a status to our Hello World example indicating that we can only greet one person at a time i.e. the 'to' argument is specified only once. We can use status code 400 (Bad Request) to represent this. The controller script is extended as follows:
if (argsM.to != null && argsM.to.length > 1)
status.code = 400;
status.message = 'Can only greet one person at a time.';
status.redirect = true;
model.toWho = (args.to != null) ? args.to : person.properties.userName;
Redirect allows the rendering of a custom response template for the specified status code. If redirect is not set (that is, false), the response header status code is set but the relevant standard web script response template is used, as would normally apply.
If redirect is true, then an appropriate status response template is searched for. The search is performed in the following order:
- Web Script specific template named <scriptid>.<method>.<format>.<code>.ftl : located in the same folder as the web script description document and used for the specified status code only, for example
- Web Script specific template named <scriptid>.<method>.<format>.status.ftl : located in the same folder as the web script description document used for any status code, for example
- Package level template named <format>.<code>.ftl : this template is first located in the package of the web script, but if not found, is searched for in the parent package hierarchy, up to the root package, for example
- Package level template named <format>.status.ftl : this template is first located in the package of the web script, but if not found, is searched for in the parent package hierarchy, up to the root package, for example
- Default template named <code>.ftl : located in the root package and always renders html, for example
- Default template named status.ftl : located in the root package (provided out-of-the-box) and always renders html, for example
Status Response templates have access to the same root objects as standard Web Script response templates. The exception is that the default templates /<code>.ftl and /status.ftl only have access to the root objects 'url', 'status', 'server' and 'date'.
To provide a custom Bad Request status response for our Hello World example we create the following file in the same folder as the web script description document
whose content is
We can also provide a meaningful response for machine-readable XML requests:
Remember, these templates are optional, as the search for an appropriate status template will always eventually find the default template /status.ftl.
Advanced Implementations
Configuration is accessed via the config root object, which is available during both controller script and template execution.
There are 3 types of configuration, 'script', 'scoped' and 'global'. Script configuration is defined in an XML document with an arbitary structure stored locally with the Web Script. Global and scoped configuration are specified in Alfresco configuration files, as used in Alfresco Explorer.
Web Script Configuration
Web Script configuration is read from an XML file packaged with the Web Script.
For our example Hello World service we create the following file in the same folder as the Web Script description document
Again, naming conventions apply where configuration file names have the following structure
The content of the configuration may be any valid XML.
Within a Controller Script, access to the configuration is via E4X which is essentially 'ECMAScript For XML' (tutorials can be found at http://www.w3schools.com/xml/xml_e4x.asp and/or http://wso2.org/library/1050, http://phpforms.net/tutorial/tutorial.html).
We can update our Hello World Controller script example to determine how to display 'who' the greeting is from.
model.toWho = (args.to != null) ? args.to : person.properties.userName;
var s = new XML(config.script);
model.fromWho = person.properties[s.fromproperty];
FreeMarker has builtin support for processing XML data allowing response templates direct access to configuration too. We can update our Hello World template example to extract the greeting from the configuration.
At ${date?datetime}, ${fromWho?html} says ${'''config.script.helloworld.greeting'''?html} to ${toWho?html}
Global and Scoped Config
Global and scoped config is read by the Alfresco Configuration Service. By default the following files are read:
- Versions 4.x:
- classpath:alfresco/web-scripts-config.xml (already exists in 'alfresco.jar'; don't overwrite it!)
- classpath:alfresco/web-scripts-cmis-config.xml
- classpath:alfresco/extension/web-api-config-custom.xml
- classpath:alfresco/extension/web-scripts-config-custom.xml
- classpath:alfresco/extension/web-scripts-cmis-config.xml
- classpath:alfresco/web-scripts-config.xml (already exists in 'alfresco.jar'; don't overwrite it!)
- Versions 5.x:
- classpath:alfresco/web-scripts-config.xml (already exists in 'alfresco.jar'; don't overwrite it!)
- classpath:alfresco/extension/web-scripts-config-custom.xml
Configuration sections that do not have an evaluator or condition are known as 'global' config sections i.e. these will always appear in a configuration lookup, a typical global configuration section has the following appearance.
<guesthome enabled='true'>/jsp/guesthome.jsp</guesthome>
Scoped configuration, on the other hand, is a configuration section that does have an evaluator and condition, for example.
<config evaluator='string-compare' condition='Remote'>
For changes in these files to take effect, the ALFRESCO server must be stopped and restarted (refreshing the webscripts is not sufficient).
Accessing Global and Scoped Configuration
Accessing the configuration above is achieved using the same techniques and syntax as any other model data, the global configuration is exposed via the config.global root object and the scoped config is exposed via the config.scoped root object.
For example to access the server configuration from the example above the following syntax would be used in a JavaScript:
var serverCfg = config.global.server;
and the following syntax would be used in a FreeMarker template:
Each line of the file represents a message identified by a unique key. The key may be dot separated.
Our example consists of:
Messages are accessed via the template method msg.
The Hello World HTML template is updated as follows:
${msg('at')} ${date?datetime}, ${fromWho?html} ${msg('says')} ${msg('greeting.' + config.script.helloworld.greeting)} ${msg('to')} ${toWho?html}
Multi-part Form Processing
When posting a request of mimetype multipart/form-data the following additional root object is available to the Controller Script.
- formdata: provides access to multipart/form-data requests allowing the upload of files.
This root object allows a Web Script to read all fields included in the form including those of type 'file'. It provides a mechanism for iterating through each field and examining its meta-data such as name, value, content type etc.
All fields that are not of type 'file' are also mapped into the args and argsM root objects allowing access to fields by name.
View the File Upload Example to see how formdata is used to upload files into the Alfresco Repository.
Request Processing
A Controller Script can gain access to a request body allowing the processing of content posted to a Web Script.
By default, the following root object is provided:
- requestbody: a ScriptContent representing the content of the request body
As with formdata, the content may be converted to a string (if plausible) or written to an output stream such as a content object held in the Alfresco Repository.
Often, content is posted in a structured form such as XML or JSON. In these cases, the content can be converted to a string and subsequently parsed by the controller script. However, this can become cumbersome or error prone if the parsing is required by several Web Scripts implementations. To alleviate this problem, the Web Scripts framework provides the notion of a Format Reader which parses a request of a given mimetype into an object structure that is then automatically provided to the Controller Script.
Out-of-the-box, the Web Script framework provides the following Format Readers.
- JSON: parses a request of mimetype application/json into a JSON object named json
- Atom Feed: parses a request of mimetype application/atom+xml;type=feed into an Apache Abdera Feed object named feed
- Atom Entry: parses a request of mimetype application/atom+xml;type=entry into an Apache Abdera Entry object named entry
- Atom : parses a request of mimetype application/atom+xml into either an Abdera Feed or Entry object named feed and entry respectively
Format Readers are not invoked automatically i.e. sending a JSON request to a Web Script does not automatically provide a json root object to the Controller Script. The fall-back requestbody is provided instead.
To explicitly initiate a Format Reader requires a Controller Script whose name is structured:
folder.post.json.js => create 'json' root object for Controller Script when application/json mimetype is posted
folder.post.atomentry.js => create 'entry' root object for Controller Script when application/atom+xml;type=entry mimetype is posted
Runtime Cache Controls
Runtime cache control is optionally specified via the root object cache which is available to the Web Script Controller script. At runtime, expiry criteria may be set, and definition-time controls may be overridden.
Java-Backed Implementations
Sometimes, scripts and templates are not powerful enough to implement the required functionality behind a URI. For example, streaming content.
For these scenarios it is possible to bind an Alfresco Spring configured Java Bean into the Web Script. The Java Bean may take full control, or work in conjunction with the script and templates.
For reference, you may browse these Java samples.
Spring Bean Declaration
To bind a Spring bean to a Web Script it is only necessary to create a bean with an id of the following structure:
and ensure its parent is the 'webscript' bean:
Note: <packageId> is the package (directory) that the Web Script descriptor (.desc.xml file) is located in, not the package of the Java class that implements the logic for the Web Script.
For example:
<bean id='webscript.org.alfresco.sample.helloworld.get'
parent='webscript' />
Note that the bean id is made up of:
- prefix = webscript (must always start with this value)
- packageId = org.alfresco.sample
- serviceId = helloworld
- httpMethod = get
From this we can determine that the descriptor for this Web Script is called helloworld.get.desc.xml and is located in the directory org/alfresco/sample (either in the repository or in the classpath, as described in Deciding Where to place Web Script Implementation).
As with any Spring bean, other service(s) your Java class requires (including the Alfresco APIs) may be dependency injected into your Java bean.
Web Script Java Class
The Java Class must implement the Java Interface:
which declares the following methods:
* Gets the Service Description
* @return service description
public WebScriptDescription getDescription();
* Execute service
* @param req
* @param res
* @throws IOException
public void execute(WebScriptRequest req, WebScriptResponse res)
throws IOException;
Java Helper Classes
Abstract helper classes are also provided to simplify the development of a Java backed Web Script.
The first helper class is:
which allows a Web Script to take full control over the request. This is achieved by providing an implementation of the execute method. The helper class provides access to information such as the Web Script description document and methods for invoking JavaScript and templates.
The second helper class is:
which allows a web script to mix Java, JavaScript, and templates. This kind of Web Script supports all the behaviour described in this document, with the addition of first executing some Java. As with the controller script, the Java code can build a data model that is available to Response templates. The Java to be executed is placed into the executeImpl method.
Managing Response Status
A Java implemented web script also has support for response status codes. There are two approaches to sending a response code:
- Approach 1 - Throw an Exception
All exceptions thrown from a Web Script are caught by the Web Script Runtime and converted to response status codes. By default, the status code 500 (Internal Error) is used. However, the exception WebScriptException supports the constructor:
public WebScriptException(int statusCode, String message)
This constructor is used to explicitly set a response code in an error situation, for example
if (req.getParameter('q') == null || req.getParameter('q').length() == 0)
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, 'Search term not provided');
Using this approach will always render the relevant status response template.
- Approach 2 - Explicit Setting of Response Status
The helper class DeclarativeWebScript provides the method executeImpl whose signature is:
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status, Cache cache)
The status argument may be used to explicitly set a response status code & message, as well as control the redirect to a status response template, for example
if (req.getParameter('q') == null || req.getParameter('q').length() == 0)
status.setMessage('Search term not provided');
Exceptions may be handled as follows:
catch(AuthenticationException e)
status.setMessage('Invalid username & password');
Creating a New Kind of Web Script
By default, Alfresco Web Scripts support an MVC style of implementation. Sometimes, it's useful to override the default behavior to add an extra capability, or provide a completely different style of Web Script such as those that need to stream large amounts of data back on the response. This can be done by developing a Java-backed implementation, however, it cannot be re-used across several Web Scripts without changes to Spring configuration.
A Web Script 'Kind' allows a Java-backed implementation to be named, and for any Web Script Description Document to state it wants to use that implementation instead of the out-of-the-box one provided by Alfresco. See list of currently available Web Script kinds.
The steps for creating a new 'Kind' are:
- Implement a Web Script backing bean
The steps here are exactly the same as for any other Java-backed implementation as described above.
- (optional) Implement a Description Document Extension
The backing bean may be configured via extensions to the Web Script Description Document. To access those extensions it is necessary to provide an implementation of the interface:
which defines the following method:
* Gets the custom description extensions
* @param serviceDescPath path to service doc
* @param serviceDesc service doc input stream
* @return extensions mapped by name
public Map<String, Serializable> parseExtensions(String serviceDescPath, InputStream servicedesc);
The method parses the complete Description Document (XML), extracts its extensions and places them into a map (indexed by the name of extension) which is returned. It is called when the Web Script is registered with the Web Script framework.
Spring is used to register the Description Extension implementation:
<bean id='webscriptdesc.my.kind.of.web.script' class='my.kind.of.WebScriptDocumentExtension'/>
The id is the same as the Web Script backing bean except it starts with webscriptdesc instead of webscript.
- (optional) Access Extensions within your Web Script Implementation
To gain access to your extension values within your backing bean you can use the following method which returns the map created above:
That's it. To use your new 'Kind' of Web Script refer to Advanced Description Options. Note: The id referenced in the kind attribute is specified without the webscript. prefix.
How Do I Administer Web Scripts?
Resetting the Web Script Index
New and updated Web Scripts can be registered without restarting the Alfresco server. This makes it easy to use the Alfresco Repository to develop Web Scripts.
To register a Web Script:
- Visit the index page as described in Listing Available Web Scripts.
- Click on Refresh to re-register all Web Scripts.
The 3.0 Web Scripts Framework provides logging of Web Script registration and execution.
Various tools are available for testing Web Scripts.
Dump & Load
Web Scripts that are not Java-backed may be exported from one Repository and imported to another via the simple Web Script Dump and Load utility.
The utility is also useful for viewing the complete implementation of a Web Script in a single page, allowing it to be copied and sent via e-mail or posted to the forums for sharing or diagnosing a problem.
Localization of Status Code Messages
The resource bundle /alfresco/messages/webscripts.properties contains status code names and descriptions for all HTTP codes.
Web Script Framework
Learn more about
- Web Script MVC
- Configuration of the framework
- Design of the framework
- Runtimes, Authenticators and Containers
- Extending the Framework
Appendix A: Web Script Root Objects
A host object providing access to multipart/form-data requests allowing file upload from a web script. See example.
Note: You must use enctype='multipart/form-data' in your form tag or the formdata variable will not be populated with fields.
- hasField: boolean indicating the existence of a named form field in the multipart request
- fields: an array of formfield where each entry represents a field within the multipart request
A host object providing access to a form field within a multipart/form-data request.
- name: the name of the form field (Note that form fields may not be uniquely named)
- isFile: boolean indicating whether this field represents a file (content)
- value: string representing the field value (in the case of isFile is true, the filename will be returned, use content instead)
- content: a ScriptContent representing the content of the field
- mimetype: the mimetype of the content (or null, if isFile is false)
- filename: the filename of the source file used to provide the content (or null, if isFile is false, or a filename was not provided)
A host object providing access to the URL (or parts of the URL) that triggered the web script.
- context: Alfresco context path, for example /alfresco
- serviceContext: Alfresco service context path, for example /alfresco/service
- service: Web script path, for example /alfresco/service/blog/search
- full: Web script URL, for example /alfresco/service/blog/search?q=tutorial
- templateArgs: a map of substituted token values (within the URI path) indexed by token name
- args: Web script URL arguments, for example q=tutorial
- match: The part of the web script URL that matched the web script URL template
- extension: The part of the web script URL that extends beyond the match path (if there is no extension, an empty string is returned)
For example, imagine a web script URL template of
and a web script request URL of
The url root object will respond as follows:
- url.context => /alfresco
- url.serviceContext => /alfresco/service
- url.service => /alfresco/service/user/fred
- url.full => /alfresco/service/user/fred?profile=full&format=html
- url.args => profile=full&format=html
- url.templateArgs['userid'] => fred
- url.match => /user/
- url.extension => fred
URL and authentication
You can reuse the web clients authentication by using the /alfresco/wcservice/ URL to reference you web script. Change the URL to /alfresco/service/ and you will get an HTTP authentication prompt even if you are logged in in the Alfresco web client. The first option is the most convenient for the end user, the second may be useful while testing with different accounts.
An associative array of response status properties that allow control over the status and content of the Web Script response.
- code: (read/write) status code (primarily a HTTP status code, but can be any number)
- codeName: (read) human readable status code name
- codeDescription: (read) human readable status code description
- message: (read/write) status message
- redirect: (read/write) a boolean indicating whether to redirect to a status specific response template
- exception: (read/write) the exception (if any) which has caused this status
- location: (read/write) the absolute URI to which the client should resubmit a request - often used with 3xx redirect status codes. See example.
An associative array of cache control properties that allow control over how the Web Script response is cached. See example.
- neverCache: (read/write boolean) control whether Web Script response should be cached at all; true means never cache. If not set, the default value is specified by the cache control section of the Web Script definition.
- isPublic: (read/write boolean) control whether web script response should be cached by public caches. If not set, the default value is specified by the cache control section of the web script definition.
- mustRevalidate: (read/write boolean) control whether cache must re-validate its version of the web script response to ensure freshness. If not set, the default value is specified by the cache control section of the web script definition.
- maxAge: (read/write long) specifies the maximum amount of time (in seconds, relative to the time of request) that the response will be considered fresh. If not set, the default value is null.
- lastModified: (read/write date) specifies the time that the content of the response last changed. If not set, the default value is null.
- ETag: (read/write string) specifies a unique identifier that changes each time the content of the response changes. If not set, the default value is null.
The format of the response.
- name: (read only) format name
- mimetype: (read only) mimetype associated with format
If the content is JSON then an object called 'json' is added to the model.
The json object is either a JSONArray or JSONObject depending upon whether the content is an array or not.
jsonUtils allows access to the json data from the controller script.
- var jsonData = jsonUtils.toObject(requestbody.content);
- var jsonString = jsonUtils.toJSONString(object);
These are the commonly used methods of JSONArray.
- length()
- getJSONObject(index)
These are the commonly used methods of JSONObject.
- get(fieldName)
- has(filedName)
- isNull(fieldName)
- getJSONArray(fieldName)
An associative array of meta-data properties that describe the hosting Alfresco server.
- versionMajor: server major version number, for example 1.2.3
- versionMinor: server minor version number, for example 1.2.3
- versionRevision: server revision number, for example 1.2.3
- versionLabel: server version label, for example Dev
- versionBuild: server build number, for example build-1
- version: server version, for example major.minor.revision (label)
- edition: server edition, for example Enterprise
- schema: server schema, for example 10
An associative array of meta-data properties describing the web script.
- id: The web script Id
- shortName: The web script short name
- description: The web script description
- defaultFormat: The default response format if none explicitly specified in web script URL
- formatStyle: The accepted ways of specifying the format in the web script URL
- URIs: A (string) array of URL templates
- method: HTTP Method
- requiredAuthentication: required level of authentication
- requiredTransaction: required level of transaction
- storePath: the path of the persistent store where the web script is stored
- scriptPath: the path (within storePath) of web script implementation files
- descPath: the path (within storePath) of the web script description document


text:
Used 2018 ChevroletSilverado 2500HD LT Truck Crew Cab
- Stock: 239813M
- Odometer
- 24,323 miles
- Exterior Color
- Summit White
- Interior Color
- Jet Black
- Body
- Truck Crew Cab
- Transmission
- Allison 1000 6-Speed Automatic
- Drivetrain
- 4x4
- Engine
- 6.6L V-8 cyl
Chevrolet Silverado 2500HD
Highlighted Features
- Premium audio system
- Wireless phone connectivity
- Exterior parking camera rear
- Split folding rear seat
- Perimeter/approach lights
- Remote keyless entry
- Steering wheel mounted audio controls
- Fully automatic headlights
Detailed Specifications
Dealer Notes
**Z71**FULLY SERVICED w/ RECORDS AVAILABLE, **CLEAN CARFAX, **ONE OWNER, **PREMIUM WHEELS, **4X4.
We offer Market Based Pricing and sell our cars fast, so Please Call Brett @ 270.704.1103 to check on the availability of this vehicle. "We'll buy your vehicle, even if you don't buy ours"
We offer Market Based Pricing and sell our cars fast, so Please Call Brett @ 270.704.1103 to check on the availability of this vehicle. "We'll buy your vehicle, even if you don't buy ours"
KBB.com Consumer Reviews
- 5.0
Very clean, no damage pickup
By Jack on Wednesday, March 07, 2018Driven by my 60 yr old wife that parks ALL the vehicles shes ever owned in the far reaches of any parking lot to avoid door dings. Oil changes/service by dealers or top rated lube shops regularly. WeatherTech floor mats and one of the best seat cover sets always on interior. Rhino type lining in bed since new.
- 2.0
Wouldnt Do It Again
By glmcauley on Tuesday, June 05, 2018Purchased in November 2017. It sits in the garage while I drive my 2000 Nissan. Bought the 2018 Silverado 2500HD LTZ Z71 with the Duramax/Allison combo. The one thing Ill say positive about it is the low-end torque on this thing is unmatched. That is the only good side. Beyond that, in my own opinion, everything is over-engineered and way overly complicated which leads to a lack of reliability. The designs are poorly thought out and reminds me of my early engineering classes in which we were challenged to see just how far we could take a design idea. In engineering school you learn the old adage that just because you can, doesnt mean you should. First example is Chevys tired HVAC controls. Look...you dont need to make everything in the world automatic. The computer chips cant tell when so many different conditions exist. So, when its 70 degrees outside and I have the thermostat on 71, it doesnt need to blow 100-degree air on me for several minutes struggling to get the temp to 71. Take a tip from foreign manufacturers...red means hot...blue means cold...select a point and go with it...make minor changes to suit your comfort. You cant do that with Chevys system. Perhaps its a minor point for some, but for 60k - 70k, you shouldnt get blasted out of the cab and have to mess around with the HVAC controls to prevent it. In addition, because Chevy decided not to put HVAC controls and/or vents in the back seat of the crew cab, they compensated by making the lowest setting on the blower motor way too high. If you like a soft breeze coming from your AC you can forget that. All of this to say...they should have put vents and controls in the back seat, and made all of the controls less complicated. Next we have the the Diesel Exhaust Fluid (DEF) tank fill. The 2500HD stands fairly tall, which looks cool. But, when you go to add DEF, the fill nozzle is all the way at the back of the engine compartment making it impossible to fill without spilling 30% urea all over the engine. I purchased a special funnel recommended by the manufacturer of Blue DEF, but its still difficult to pour a 2.5 gallon jug of DEF into that little nozzle at that angle. Yes, Ill survive, but for this kind of money, one shouldnt have to deal with that kind of nonsense. The on/off button on the head unit isnt really a power button...its a mute button. You have to go through several steps when your phone is connected via Bluetooth to prevent songs continuing to play in the background. Not a life or death situation, obviously, but why? What the heck? Just one more annoying little design flaw that makes you shake your head. I spent 60k+ for THIS? The most negative feature for me is OnStar. I was in a hurry when I bought the truck due to the death of my father. Long story...but I needed a truck that day. I didnt realize I had signed the OnStar agreement and somehow they got my email address through that process. About a month after the purchase I received an email with my driving habit analysis. After a moment of self-admiration of being in the top 10% of Oklahoma drivers (by whatever measure OnStar applies), I started to get that creepy feeling. They had how many hard stops I had performed, my acceleration performance, how many miles I had driven, etc. I really did not realize that I was providing this data to OnStar who was all to happy to provide the data to insurance companies so that I could qualify for a discount. Yet one more Facebook-type invasion of our privacy. Finally, and not quite as annoyingly, whoever I gave my email address to when I purchased this vehicle, shared it with EVERYBODY. I cant tell you how many survey emails Ive received. Tell us how you like your new Silverado! Over and over again. Then, the tons of snail-mail in my mailbox is incredible. I could have saved hundreds of dollars on the truck if they gave me the value of all of the mailings Ive received from Chevy and from the dealership; and Im not exaggerating. Theyve flooded my email box and my snail mail box. So there you have it. Im not sure that Ford or Dodge will be much better. Im waiting for Toyota to come out with a 3/4 ton diesel. So far, after owning Chevy and Dodge, and a couple of friends with Fords, the Toyota Tundra (2012 Crewmax) has been, by far, the best truck Ive ever owned or ridden in. I very much dislike this Chevy. Can you tell?
- 5.0
Love this truck!
By since 72 on Thursday, September 22, 2016This new HD 2500 is a beast, pulls our 11,000lb. 33' travel trailer with ease. Has good road manors loaded down or not. Get good compliments on ours all the time. Go try all the other heavy duty brands & compare it all like we did, then maybe an HD is in your future.
Used 2018 Chevrolet Silverado 2500HD Truck Crew Cab LT
Exterior Color: Summit White
Interior Color: Jet Black
Stock: 239813M
View this 2018 Chevrolet Silverado 2500HD Truck Crew Cab LT for sale at Seay Motors in Mayfield, KY
New Arrivals
2006 Ford F-150 SuperCrew Lari…
- $8,900
- Truck SuperCrew Cab
- Black Exterior
- 5.4L V-8 cyl Engine
2018 Ram 2500 Tradesman Truck…
- $34,999
- Truck Crew Cab
- Silver Exterior
- 6.4L V-8 cyl Engine
2013 Ram 2500 Tradesman Truck…
- $29,900
- Truck Crew Cab
- Silver Exterior
- 6.7L I-6 cyl Engine
2011 Ford F-150 XLT Truck Supe…
- $18,914
- Truck SuperCrew Cab
- Brown Exterior
- 5.0L V-8 cyl Engine

subset: commoncrawl


text:
Cincinnati nightclub shooting: What we know
One person was killed, 15 others were injured in the weekend shooting
One person was killed, 15 others were injured in the weekend shooting
One person was killed, 15 others were injured in the weekend shooting
16 people were shot -- one fatally -- inside a Cincinnati nightclub Sunday morning.
Here's what we know so far.
Police got calls about shots fired at Cameo Nightclub on Kellogg Avenue in the city's Linwood neighborhood at about 1:30 a.m. Sunday.
When first responders arrived, most people had run from the building, but victims were still inside.
"It was my understanding that they literally had to step over some victims to get to more critically wounded victims and worked on the one person that passed away," Fraternal Order of Police President Dan Hils said.
Witnesses told WLWT that several - perhaps 20 - shots were fired inside the club. One man said he heard the DJ call for security about 10 minutes before the shooting.
Police said that there were multiple shooters.
O'Bryan Spikes, 27, was killed. He was a father and junior college student who frequented the club, his family said.
15 others were shot. One is in "extremely critical condition," Cincinnati police Chief Eliot Isaac said Sunday.
University of Cincinnati Medical Center said Monday morning that two victims are in critical condition and four others are still in the hospital. The other victims have been released.
Police have said that a dispute earlier in the day may have led to the shooting inside the club. They do not suspect terrorism, they said.
Police are working to interview as many of the 200 or so people who were inside the club as possible. When shots rang out, patrons ran from the club and many got into their cars and left before police arrived.
There are surveillance cameras at the club, police said, and they are reviewing the tapes.
Cameo Nightclub is in a large building near warehouses and a handful of homes near the Ohio River. The club is 18 and up on some nights and is open until 4 a.m. on Saturdays.
Four Cincinnati police officers were stationed in the parking lot at the time of the shooting.
Cameo has had problems with violence in the past, including two shootings at the club in 2015.
Owner Julian Rodgers said that Cameo has four or five police officers and 15 to 20 security personnel on duty on Saturday nights.
"We're doing everything we are supposed to. We are cooperating wholeheartedly. We're going to get them," he said.
Cincinnati leaders call the shooting horrific and "totally evil."
"You have innocent people shot just for going out to have a good time. That's totally unacceptable. It's totally evil," said Cincinnati Mayor John Cranley.
City leaders will get an update on the shooting and the status on the search for shooters at a meeting Monday morning at City Hall.
"We've got to get guns out of the hands of young people. I mean, it's a real challenge. There are way too many guns getting into the hands of individuals who don't need them" said Cincinnati councilwoman Yvette Simpson.
The investigation is still in its early stages and police still have many questions to find the answers to. No arrests have been made.
Who shot Spikes, and why, has not been determined.
Many ask how guns got into the club in the first place. Cincinnati police said that security guards use metal detector wands and pat down patrons as they enter the club.


text:
0 Members and 1 Guest are viewing this topic.
Oh come now, Lurk.The internet is a magical place, where linguistic and dialectic possibilities are endless. Why not embrace those variations and see where they lead you.
I never said nobody noticed. I was just asking for it to end.
Goddamnit kitkat, you make me look late
Quote from: Slinky2* on April 14, 2012, 01:40:09 pmMe Gusta moonPlease don't call it that. :\
Me Gusta moon
What is this, Donkey Kong Country in Space?
Sam is basically right, he's just cranky.
Page created in 0.067 seconds with 21 queries.


text:
LESS THAN THE COST OF CONVENTIONAL FOUNDATION CONSTRUCTION IN Caledonia!
Ready in just one day!
No digging required!
Introducing the SMARTFOUNDATION™ a new and revolutionary foundation system developed through a collaboration between steel piles, patented insulated forms and reinforced concrete surface.
Together, this system provides an alternate solution to conventional foundations without digging.
Up to 1,000 square feet of slab-on-grade foundation and concrete floor ready in just one day!
If you are looking to build a slab-on-grade foundation in Caledonia, Contact us for more information about SMARTFOUNDATION™ Concrete Slab Foundation


text:
Working with AfricaGuide for 15 years
We are a privately owned Closed Corporation registered with the South African Registrar of Companies. We operate independent – plan, book and guide our safaris personally.
Operating throughout Southern Africa, we also offer a host of value adding sightseeing tours and transfers country wide – from tours and accommodation in Cape Town, Garden Route, Victoria Falls, Botswana, Namibia and we specialise in customising safaris.
All Guides and Drivers are professionally trained with current Public Drivers Permits, Tour Guide registrations and First Aid certificates and are registered as Tourist Guides and General Field Guides with the provincial tourism authorities.
For open vehicle safaris, walking safaris and night safaris we use Specialist Field Guides that have additional training in conducting safaris in wilderness areas where dangerous game are present.
All guides have a valid Public Drivers Permit (PDP).
We can provide French, German, Italian, Russian, Chinese, Dutch, Portuguese and Spanish speaking guides upon request, an extra fee may be charged


text:
Target 11. By 2020 to have achieved a significant improvement in the lives of at least 100 million slum dwellers
Goal 7. Ensure environmental sustainability
E - Estimated. The figure is estimated by the international agency, when corresponding country data on a specific year or set of years are not available, or when multiple sources exist, or there are issues of data quality. Estimates are based on national data, such as surveys or administrative records, or other sources but on the same variable being estimated.
Source: United Nations Statistics Division - Unless otherwise noted, information in this page is accurate as of February 15, 2007


text:
Hermes Passe-Passe BagCategories:Other Brands
You've probably already read our oh-so-famous article: 'Hermes Classic Bag Collection'. Oh, if you haven't, then we recommend you to read it as we have invested a lot of sweat and tears putting it together.
Apart from what's already classic and iconic, let us talk about something different than the Hermes Kelly and Birkin Totes. Let's talk about, uh, the bags that are under the radar. Presenting the Hermes Passe-Passe Bag.
Now, I love French language but I suck at it. I am so bad in French that if my high school teacher didn't have a good temper, she would probably have broken her desk in two pieces. So I went for a search on Google translation, I wanted to know what 'Passe-Passe' means. And it says: 'Sleight'. Now allow me to explain the function of this accessory.
You see, just like the Hermes Pliplat Bag, this tote has two forms. It's an innocent small tote bag and when it's in black, it looks quite classic. The handles are long, you can carry it by hand or simply sling it on your shoulder. Nice right?
It's also a bit deceiving, because the Hermes Passe-Passe Bag, when it's folded, it looks like it has limited space. But surprising enough, when you unfold it, this bag changes into a, what should I call it? A shopping tote. It's now enormous, now let's see who is neverful (you know what I mean)?
It's made from toile Goeran and Vaud-Barenia, if you do not what this is, please go to our Hermes Leather Guide. The front is beautified with a small tag that reads: 'Hermes Paris'. Now let's dive into the details:
Hermes Passe-Passe Bag
Size: 21 x 35 x 17 (H x B x D) in cm
Tags: bag Hermes Passe-Passe


text:
fptechnoDec 11, 2012 09:43:56 IST
Intel presented new manufacturing technology that it said keeps it on track to launch a new generation of chips for smartphones and tablets as it rushes to catch up with Qualcomm and other rivals in the fast-growing mobile market.
The world's largest chipmaker dominates the PC industry but has been slow to adapt its processors for mobile gadgets that depend on batteries and demand power efficiency.
At an industry conference in San Francisco on Monday, Intel reported on the progress of its technology for making SoCs, or "systems-on-a-chip" with features measuring 22 nanometers.
"Intel's 22 nm SoC technology will be ready for high volume manufacturing in 2013," Intel said in a copy of the presentation.
Right on track
Intel's current mobile SoCs are manufactured at 32 nm, while Qualcomm makes its top-end SoCs at 28 nm and Nvidia uses 40 nm technology. Manufacturing chips with smaller features allows for better performance and power efficiency.
Intel already makes PC processors at 22 nm, but since SoCs pack more features onto one piece of silicon, manufacturing them at 22 nm is more complicated, said Patrick Moorhead, of Moor Insights & Analysis.
"They (Intel) have all the right ingredients to make a very competitive mobile chip but we won't know if they do until 2013," Moorhead said.
While Intel's industry lead in manufacturing technology is well established, competitors and many on Wall Street say the chipmaker's mobile SoC designs do not stand up to SoCs designed by Qualcomm, Apple and others with technology licensed from Britain's ARM Holdings .
Find our entire collection of stories, in-depth analysis, live updates, videos & more on Chandrayaan 2 Moon Mission on our dedicated #Chandrayaan2TheMoon domain.


text:
VANCOUVER, BC – Vancouver Whitecaps FC announced today that the club has transferred striker Camilo Sanvezzo to Mexico's Liga MX side Queretaro Fútbol Club for a multi-million dollar fee. Per club and league policies, detailed terms of the deal were not disclosed.
"We have made the decision to part ways with Camilo Sanvezzo. In doing so, we were able to negotiate a club record transfer fee that will give us additional flexibility as we continue to build our club for the upcoming season and beyond," said Whitecaps FC head coach Carl Robinson.
"We are pleased to report that we've come to an agreement with Camilo Sanvezzo and Vancouver Whitecaps FC. On behalf of our club, we sincerely regret our misunderstanding in regards to Camilo's contract earlier in the process," said Adolfo Ríos, Queretaro FC president.
Camilo, 25, joined Whitecaps FC on a preseason trial prior to the start of the club's inaugural Major League Soccer campaign and officially signed with the club on March 17, 2011. The Brazilian made a total of 92 MLS regular season appearances with Whitecaps FC, recording 39 goals and registering 16 assists during that period.
Prior to his arrival in Vancouver, Camilo had spells with Oeste Paulista Esporte Club and Sport Club Corinthians in his native Brazil, Qormi FC in Malta, and Gyeongnam FC in South Korea.


text:
Education Calendar
Novalash Classic Certification Class
November 26, 2017
About the Event
You will receive your Training Manual and Business in a Box on the first day of training. Upon completing theoretical and practical instruction, trainees take a written certification and practical application skills test. Students must provide their own model. Upon successful completion, students will receive a NovaLash Certificate and join the worldwide NovaLash team of Lash Artists. Certification training is $1213.50 Includes the following*: -FREE listing on NovaLash.com's Find a Stylist -FREE access to marketing materials -Business in a Box (over a $1400.00 value including NovaLash Products) -Full 12 hours of training over the course of two days -Seminar Skills Training Manual -Refresher course that you may take within 6 months of the original class (a $525.00 value!) *Student must attend and complete the entire training to receive above listed benefits
Londonderry, NH


text:
20 Amazing Reimagined Book Covers
Under the influence of characters, setting, and plot, a number of artists have recently taken it upon themselves to recreate book covers of some of the most beloved literature, often with fantastic results. Whether it be through illustration or painting, collage or embroidery, reimagined cover art isn't limited to the cardboard backings of books, but takes on a life of its own that ranges from extravagant and crafty, with kaleidoscopic-colored thread twirling on the page, to simple but powerful, with bold graphic designs and eerie color pallets. And while each piece is aesthetically different, these book covers have one thing in common — they all pay homage to the authors and works we hold so dear. See 20 of our favorite works inspired by the likes of J.D Salinger, The Brothers Grimm and Roald Dahl after the jump.
Anne Benjamin, Pride And Prejudice


text:
BLOOMINGTON, Ind. – Led by the distance runners, more Lipscomb indoor track and field records fell on day two of the Hoosier Hills Invitational, Saturday.
In the women's 3000m run, three Lady Bison competitors eclipsed the school record. Freshmen Minna Fields (10:05.96s) and Kellie Foley (10:08.74s) finished sixth and ninth, respectively, while sophomore Tessa Hoefle's 10:05.20s mark earned her fifth place and the top indoor mark in Lipscomb history.
After setting the school record in the mile run, Friday, sophomore Dani Walker broke the 800m mark Saturday. Her 2:15.47s mark placed her third in the invitational event.
"We had a great prep meet for conference," said Lipscomb head coach Bill Taylor. "I'm proud of how our kids competed here. This is always a great meet for us.
"We had big improvement in our distance races, as we expected. Getting a couple of quality weeks of work in has certainly helped a ton. Our 3000m women had an incredible race. All three eclipsed the previous school record and had huge personal-bests over their previous marks."
Sophomore Tucker Peabody continued his winning ways in the 60m dash, taking the top of the podium with a 7.09s mark. It was his fourth finals victory this season and second this weekend; he won the 200m dash event, Friday night.
The Lipscomb track and field teams will be back in action next weekend when they make a return trip to Johnson City, Tenn. for the Atlantic Sun Indoor Championships.
"We are ready for the conference meet," Taylor said. "More importantly, we are building the right foundation for the outdoor season."
Check LipscombSports.com for all the Lipscomb cross country and track and field news, and be sure to like us on Facebook (Lipscomb-university XC-track) and follow us on Twitter (@LipscombUTrack).


text:
A U.S. and Russian Soyuz crew docked with the International Space Station late Sept. 25, successfully completing a third consecutive "express" four orbit launch-to-rendezvous transit to restore the orbiting science lab to six-astronaut status.
The Soyuz TMA-10M delivered Mike Hopkins, Oleg Kotov and Sergey Ryazanskiy for a six month tour of duty as the capsule carried out an automated docking with the station's Russian segment Poisk module at 10:45 p.m., EDT.
The "express" mission opens the door anew for Orbital Sciences' Cygnus re-supply capsule to make a second attempt to rendezvous and berth with the space station as soon as Sept. 28.
Orbital launched Cygnus on Sept. 18 with a non-critical 1,543 pound cargo of crew provisions to complete the final demonstration phase of the Dulles, Va.,-based company's $288 million NASA COTS agreement. A successful rendezvous and berthing qualifies Orbital to launch ISS supplies on a commercial basis under a $1.9 billion, eight-mission NASA contract.
Cygnus was to approach the station on Sept. 22, maneuvering close enough for Parmitano and Nyberg to grapple it with the Canadian robot arm. A GPS navigation software mismatch between Cygnus and the station prompted a one-day delay in the final approach phase and then a subsequent decision for the well-fueled Cygnus to loiter in orbit until the Soyuz operations were complete.
Soyuz TMA-10M crew was greeted by ISS Expedition 37 commander Fyodor Yurchikin, NASA astronaut Karen Nyberg and European Space Agency astronaut Luca Parmitano. The TMA-10M fliers replace U. S. and Russian astronauts Chris Cassidy, Pavel Vinogradov and Alexander Misurkin, who descended to Earth on Sept. 10, following 5 1/2 months in orbit.
The TMA-10M flight, like the Soyuz crew transport missions launched in late May and March, followed the "express" four-orbit, six-hour launch-to-docking timeline rather than the traditional two day transit.
Kotov, Ryazanskiy and Hopkins lifted off from the Baikonur Cosmodrome in Kazakhstan on Sept. 25 at 4:58 p.m., EDT, or Sept. 26 at 2:58 a.m., local time, climbing to orbit through clear but darkened skies. Solar arrays and KURS system navigation antennas deployed normally and four rendezvous maneuvers followed over the next two hours to set up the accelerated final approach and docking.
Hopkins, a U. S. Air Force colonel and flight test engineer, is the first of NASA's 2009 class of astronauts to launch. Kotov, a physician and colonel in the Russian Air Force, logged 359 days in orbit during ISS missions in 2010 and 2007. Ryazanskiy, a biochemist who joined Russia's cosmonaut corps in 2003, is also flying for the first time.
Their tour of duty is expected to include three Russian spacewalks for the installation of science equipment and external maintenance and visits by a Cygnus cargo craft in December as well as SpaceX Dragon and Russian Progress freighters early next year.
The newly fortified Expedition 37 crew, led by Yurchikhin, has a research agenda that includes nearly 120 experiments in human health, technology demonstrations, biology and the other physical sciences as well as Earth observations
One of the spacewalks by Kotov and Ryazanski, tentatively planned for Nov. 9, will expose an Olympic torch to space a few days before it returns to Earth with Yurchikhin, Nyberg and Parmitano.
The space torch will then make its way to the 2014 Winter Game scheduled for Soichi, Russia in February.
A U.S. and Russian Soyuz crew docked with the International Space Station late Sept. 25, successfully completing a third consecutive "express" four orbit launch-to-rendezvous transit to restore the orbiting science lab to six-astronaut status.


text:
A CAM image represents a photograph taken with a Casio QV-10/100 camera. Casio's QV series was launched in mid 1990s and at the time it represented an early type of digital camera, featuring an LCD screen and providing low resolution images. The CAM format is currently considered obsolete and can be opened with a limited number of image handling programs.
The HRU file format is a bitmap image format associated with the Soil and Water Assessment Tool (SWAT), which is a model developed through the SWAT-CUP program in Microsoft Windows. This software generates a detailed simulation model aimed to assess the result of various land management techniques in the case of large-size watersheds. HRUs represent output files containing detailed information compiled through SWAT, generally under the form of extensive tables with a large number of variables.
The quick and simple way to handle your files is to get a quality piece of software, such as reaConverter. Although it is fast, this software is extremely efficient in managing a wide range of conversions. As you will soon realize, reaConverter will help you avoid spending countless hours trying to figure out how to convert CAMs. But at the same time, it will allow you to apply a wide range of editing options.
reaConverter is quick to download, install and launch, and you don't need to be an IT specialist in order to understand in a few minutes how it operates.
Start reaConverter and load all the .cam files you intend to convert into .hru because, as opposed to most free online converters, reaConverter supports batch conversion. So you can save the time and energy you would lose with doing repetitive operations.
Select CAMs from a folder or by drag-and-dropping them directly into the reaConverter window.
Go to the Saving options tab and decide where you want to save new .hru files. You can also spend a few more moments to apply additional editing to the images prepared for conversion.
Then pick the HRU as output format. To help you do this, a series of buttons is conveniently placed at the bottom of the reaConverter window, so you can simply press on the one you need or click Plus button to add new options.
Then simply press the Start button and your conversion will begin in no time!Try Free Trial Version
Advanced users can convert CAM to HRU via command-line interface in manual or automated mode. You are welcome to contact our technical support when you have any questions about reaConverter.Download reaConverter Pro with CLI


text:
Marketing Recruitment Agency Sussex
CV Screen is a leading marketing recruitment agency which recruits in Sussex and throughout the UK.
Established in 2000, we have built up a large database of marketing professionals in the Sussex area, and our experienced recruiters are able to source highly skilled marketing staff for your business.
Our permanent placement fees of 12.5% of the basic salary are excellent value within the marketplace and we also offer a 100% rebate for 100 days to provide you with added peace of mind when you are recruiting.
Our Consultants recruit for both Agency and Client side roles and recruit across a wide range of marketing related roles including: Brand Management, Creative, Digital, CRM, Marcoms, SEO and eCommerce.
We pride ourselves on our honest, ethical and professional approach to recruitment and have successfully recruited for over 1500 companies in the UK including household names such as Mamas & Papas, Links of London and Red Bull.
Why not call us on 01273 915 102 to discuss how we can assist you to recruit within your marketing department.
Register a CVMore Info >
Register a VacancyMore Info >
Local OfficeCV Screen Ltd
01273 915 102
Search Jobs
Current Jobs in Sussex
Marketing Manager
Are you an experience Marketing Manager, with experience working for a Law Firm or Legal Practise? Do you have a Marketing degree? Have you got previous experience in a marketing role in a professiona … more
Digital Marketing Executive
Digital Marketing ExecutiveA Digital Marketing Executive with strong Digital campaign management and Social Media skills is required by our Consultancy client based in Lewes near Brighton, East Sussex … more
Marketing Executive
Marketing ExecutiveA Marketing Executive with strong Digital campaign management and Social Media skills is required by our Consultancy client based in Lewes near Brighton, East SussexThe Marketing Ex … more


text:
Reviewed by Else H. (age 8)
I think this book was a good book because the pictures bring out the words. The pictures are full of detail and emotion. I think the book was meaningful because it brought out problems and actually fixed the problems. One problem was when Arthur and Moira got drunk. They fixed it by getting a doctor and having the doctor check on them until they weren't sick. Lastly, I think the book was funny. When Moira asked Arthur if he had ever thought about being a vet, the book says, "Arthur hadn't but he had a sudden vision of himself surrounded by sick animals. Some with bandaged legs, some on crutches." My favorite part was when they figured out that Pauline was not in the pot of chicken because I wanted everyone to be safe. I liked the characters of Bernadette and Arthur's mom because they both are going to have babies and it was not just one.
I recommend this book to 7-10 year olds because people who are younger may not understand it and people who are older may think that it is too easy. It is 117 pages long. I think people who like problems should read this book because it has a lot of problems in it. People in grades 2-4 should read it. This book is a problem solver.