Friday, September 10, 2010

Custom endpoint for Alfresco remote access

It happen more than often that we want to perform actions in the repository that require custom authentication processes. Usually we need to define a This is a way to implement such a thing.

1. Customize webscripts-framework-config.xml (Alfresco <>or share-config-custom (Alfresco 3.2 >)

1.1. Define an Authenticator

 <authenticator> 
<id>alfresco-custom-ticket</id>
<name>Alfresco Authenticator</name>
<description>Alfresco Authenticator&lt;/description>
<class>com.westernacher.wps.share.authenticate.customAuthenticator</class>
</authenticator>

Where the customAuthenticator Class encloses the logic of our customized authentication.

1.2. Define a Connector

 <connector>  
<id>external-alfresco</id>
<name>Simple Http Connector</name>
<description>Simple Alfresco - HTTP Connector</description>
<class>org.alfresco.connector.AlfrescoConnector</class>
<authenticator-id>alfresco-custom-ticket</authenticator-id>
</connector>
Its however important to remind that in alfresco 3.2 > the classes for the connector were mostly ported to the spring framework package.

example:
the class org.alfresco.connector.AlfrescoConnector should be replaced with org.springframework.extensions.webscripts.connector.AlfrescoConnector in alfresco 3.2 and more.

1.3. Define the endpoint

 <endpoint>
<id>my-custom-end-point</id>
<name>Custom Endpoint</name>
<description>System account access to Alfresco</description>
<connector-id>http</connector-id>
<endpoint-url>url-tha-prefixes-the-scripts-to-be-called</endpoint-url>
<unsecure>true</unsecure>
</endpoint>

In alfresco by example the end point URL is standardly known as : http://localhost:8080/alfresco/s

1.4. Implement our CustomAuthenticator class


 public class CustomAuthenticator extends AbstractAuthenticator { 
public final static String CS_PARAM_ALF_TICKET = "alfTicket";
private static
Log logger = LogFactory.getLog(CustomAuthenticator.class);
@Override
public ConnectorSession authenticate(String endpoint, Credentials credentials, ConnectorSession connectorSession) throws AuthenticationException {
ConnectorSession cs = null;
if (credentials != null) {
//get a valid ticket from Alfresco via a standard or custom method
final
String url = endpoint + "/getTicket";
GetMethod get = new GetMethod(url);
// username and coockie
String aCoockie = (String) credentials.getProperty(Credentials.CREDENTIAL_PASSWORD);
get.setRequestHeader("Cookie", aCoockie);
HttpClient client = new HttpClient();
try {
int res = client.executeMethod(get);
// read back the ticket
if (res == 200) {
String ticket;
try {
ticket = get.getResponseBodyAsString();
} catch (
Throwable err) {
logger.error("Error to obtain ticket for custom authorization", err);
// the ticket that came back could not be parsed
// this will cause the entire handshake to fail

throw new
AuthenticationException("Unable to retrieve login ticket from Alfresco");
}
if (logger.isDebugEnabled()) {
logger.debug("
Parsed ticket: " + ticket);
}
// place the ticket back into the connector session
if (connectorSession != null) {
connectorSession.setParameter(
CS_PARAM_ALF_TICKET, ticket);
// signal that this succeeded
cs = connectorSession;
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Authentication failed, received response code: " + res);
}
}
} catch (
Throwable e) {
logger.error("
Error to authenticate by custom Authentication", e);
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("
Authentication failed, no credentials provided");
}
}
return cs;
}
@Override
public
boolean isAuthenticated(String endpoint, ConnectorSession connectorSession) {
return (connectorSession.getParameter(
CS_PARAM_ALF_TICKET) != null);
}
}





Tuesday, August 31, 2010

Create A transaction supported cron Job in Alfresco

1. spring bean configuration

<bean id="customService" class="com.alfresco.ibrahim.services.impl.CustomServiceImpl">
<property name="customBeanDAO">
<ref bean="customBeanDAO" />
</property>
<property name="nodeService">
<ref bean="nodeService" />
</property>
<property name="searchService">
<ref bean="searchService" />
</property>
</bean>

2. cron config bean

<bean id="customGenerator" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.alfresco.ibrahim.jobs.impl.customExecutorImpl"/>
<property name="jobDataAsMap">
<map>
<entry key="customService">
<ref bean="customService" />
</entry>
<entry key="transactionService">
<ref bean="transactionService" />
</entry>
</map>
</property>
</bean>

<bean id="regularUpdateTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="dailyReportGenerator"/>
<property name="cronExpression" value="30 * * * * ?"/>
</bean>

3. Job bean class


public class CustomExecutorImpl extends QuartzJobBean implements ReportExecutorJob{

private CustomService customService;
private TransactionService transactionService;


@Override
public void executeReportGeneration() {

AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>() {

@Override
public Void doWork() throws Exception {

transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() {

@Override
public Void execute() throws Throwable {
customService.doSomeJob();
return null;
}
}, false, true);

return null;

}
},AuthenticationUtil.getAdminUserName());
}

Sunday, March 28, 2010

Perform repository actions as administrator in a transaction

I have faced the need to run a code as admin several times and every time i needed to look for how this was done correctly. Let's put a stick on my wall :-)


you will need:
  1. An AuthenticationUtil for handling the Authentication as and access of the method as a given user: runAs.
  2. A RetryingTransactionHelper to handle transactions requirements
  3. optionally I use here the synchronized modificator for the specific need of this example.

public synchronized Long getNextContractNumber() {
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Long>() {
public Long doWork() throws Exception {
Long actualValue= null;
RetryingTransactionCallback<Long> transaction = new RetryingTransactionCallback<Long>() {
public Long execute() throws Exception {

List<String> currentNumberPathNames = getPathNames(currentNumberNodePath);
final NodeRef currentNumberNode = getNodeFromPathNames(currentNumberPathNames);
Long currentValue = (Long) nodeService.getProperty(currentNumberNode, PhagModel.PROP_CURRENT_NUMBER);
Long nextValue = currentValue + 1 ;
nodeService.setProperty(currentNumberNode, PhagModel.PROP_CURRENT_NUMBER, nextValue);
return nextValue;
}
};
actualValue = getTransactionHelper().doInTransaction(transaction);
return actualValue;
}
}, AuthenticationUtil.getAdminUserName());
}
it is a useful tool, it might certainly be useful for many.

Monday, February 8, 2010

ALfresco 3.1 Sending Ajax Request to Backend Webscript

To Send a Backend Ajax request in Alfresco you will need few things.
  1. The URL of the webscript you are invoking.
  2. The Object to be send in the request body if the method used is POST
  3. The Format of the Data to be Send if the method to be used is POST (optional).
  4. The success callback function
  5. The failure callback function
Prepare the URL to be called:

var actionUrl = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/oase-metadata/node/{nodeRef}",
{
nodeRef: file.nodeRef.replace(":/", "")
});


Using the substitute method of the Yui framework, the URL is built up using the values passed in the JSON Object.

Prepare the success and failure callback function:

This method are used to handle the AJAX request return results. If everything goes fine the success handler is executed at the end of the AJAX request, otherwise the failure handler is called.


...
,

onSuccess: function _SuccessNotify(){


Alfresco.util.PopupManager.displayMessage( {
text : "Metadata Updated Sucessfully"
});
YAHOO.lang.later(2000,this,function(){
window.location.reload(true);
});
},
onFailure: function _FailureNotify(){


Alfresco.util.PopupManager.displayMessage( {
text : "Metadata Updated Failure"
});
}
,
...



Implement the Ajax request using the previously defined assets:

Some additional parameters are needed.

RequestContentType: is Optional but indicates here that the Object passed as parameter o the request has to be transmitted as a JSON object, not as JavaScript Native Object.

Method: Specifies the HTTP method used for processing the request. here POST is used so the data wll be transmitted in the body of a HTTP-POST Request.

DataObj: is the Object to be transmitted to the server. Its construction is similar to a JSON object definition, and usinh the requestContentType formatter value can also be sent as such.

SuccessCallback and failureCallback fields define the success an failure handlers for this AJAX request.



Alfresco.util.Ajax.request(
{
url: actionUrl,
requestContentType: Alfresco.util.Ajax.JSON,
method: Alfresco.util.Ajax.POST,
dataObj:
{
htmlid: this.id,

dataObject:
{
dateofdoc : dateofdoc,
dateofrec : dateofrec,
subjectofdoc : subjectofdoc,
typeofdoc : typeofdoc,
sendername : sendername,
senderstreet : senderstreet,
senderpostcode : senderpostcode,
sendercity: sendercity
}
},
successCallback:
{
fn: this.onSuccess,
scope: this
},
failureCallback:
{
fn: this.onFailure,
scope: this
},
execScripts: true,
});
...
},

Sunday, February 7, 2010

Alfresco 3.1 CIFS Quick Configuration.

Short and Effective!!!

In your alfresco installation directory locate
file-server-custom.xml.sample.
It is encouraged to changes configuration files of Alfresco only in the shared directory so:
In the
/tomcat/shared/classes/alfresco/extension directory locate the above named file.


The content looks like this:

 <alfresco-config area="file-servers">
<!-- To override the default Alfresco filesystem use replace="true", to -->
<!-- add additional filesystems remove the replace="true" attribute -->
<config evaluator="string-compare" condition="Filesystems" replace="true">
<filesystems>
<!-- Alfresco repository access shared filesystem -->
<filesystem name="${filesystem.name}">
<store>workspace://SpacesStore</store>
<rootPath>/app:company_home</rootPath>
<!-- Add a URL file to each folder that links back to the web client -->
<urlFile>
<filename>__Alfresco.url</filename>
<webpath>http://${localname}:8080/alfresco/</webpath>
</urlFile>
<!-- Mark locked files as offline -->
<offlineFiles/>
...
</filesystem>
<!-- AVM virtualization view of all stores/versions for WCM -->
<!-- virtual view can be any of the following: normal, site, staging, author, preview -->
<avmfilesystem name="AVM">
<virtualView stores="site,staging,author"/>
</avmfilesystem>
</filesystems>
</config>
</alfresco-config>


In the configuration file, filesystem.name refers to the name of the file system you are creating and localname refers to the name of you user to access your server via a browser URL. To avoid using localhost you might consider creating a new entry in the host configuration file in windows: c:\windows\system32\drivers\etc\ directory.

Rename the file-server-custom.xml.sample to
file-server-custom.xml
Change the values
${filesystem.name} and ${server.local.name} accordingly. In my case i used alfresco and cifs4real.com respectively (the latest one configured in the host file and bound to my network IP address).

Save and close the
file-server-custom.xml file in the same folder as the sample. That means in the extension folder.

Now before strarting you alfreso server make sure the port 445 is not in use. To do it just run the command netstats -a from a command prompt. If it appears that the port is in used turn off the service by sending net stop SERVICE_NAME from the command prompt. Do the same with start instead of stop when you are done with this exercise if you need to start the service again. We are almost done here.

First Run the command.
nbtstat -a SERVER_IP_ADDRESS

Now start the alfresco server.

Now we need to map a space of Alfresco as a device in your local machine. Do this using for instance the command.
net use X: \\SERVER_NAMEa\Alfresco\SPACE_NAME /user:ALFRESCO_USERNAME

The "a" at the end of your machine SERVER_NAME is not optional. The space name can be for instance "Sites".


That's all, now you can access your mounted alfresco space in the windows explorer.