← Back to team overview

dhis2-devs team mailing list archive

Re: Attemping to post JSON through the web API

 

Just one other question (see attached code). When getting the JSON response
from one server, you get a "pager" node. Is there anyway to not get this,
or does it even matter if I post it back to "metaData" to the destination
server?

Reason it has to be done this way, is that we do not have access to the
metaData endpoint in the source server (but do on the destination server).
A hack of course, but maybe there is a better way?

Below, a simple script (no error handling) to sync orgunits from one server
to another (in R of course, haha). Any further suggestions would be
welcome.

Regards,
Jason

require(rjson)
require(RCurl)
#Modify to suit your needs
url.base<-"******"
username.base<-"******"
password.base<-"******"
url.dest<-"******"
username.dest<-"******"
password.dest<-"******"


#Get the number of pages from the source server
curl <- getCurlHandle()
curlSetOpt(cookiejar='cookies.txt',httpauth = 1L,ssl.verifypeer = FALSE,
curl=curl)
login.url<-paste0(url.base,"/dhis-web-commons/security/login.action")
response<-getURL(login.url,
userpwd=paste0(username.base,":",password.base),curl=curl)
url<-paste0(url.base,"api/organisationUnits.json?viewClass=export")
pages<-fromJSON(getURL(url,curl=curl))$pager$pageCount

#We are going to make a few posts, so get a curl handle
curl.dest <- getCurlHandle()
curlSetOpt(cookiejar='cookies_dest.txt',httpauth = 1L,ssl.verifypeer =
FALSE, curl=curl.dest)
login.url<-paste0(url.dest,"/dhis-web-commons/security/login.action")
response<-getURL(login.url,
userpwd=paste0(username.dest,":",password.dest),curl=curl.dest)

#Loop through each page and post to the destination server
for ( i in 1:pages) {
cat("Getting ", i, "of ",pages,"\n")
url<-paste0(url.base,"api/organisationUnits.json?viewClass=export&page=",i)
response<-getURL(url,curl=curl)
#Get rid of the pager node. Maybe not required?
foo<-fromJSON(response)$organisationUnits
foo<-toJSON(list(organisationUnits=foo))
getURL(paste0(url.dest,"api/metaData?preheatCache=false"),
    customrequest='POST',
    httpheader=c('Content-Type'='application/json'),
    postfields=noquote(foo),
    curl=curl.dest
  )
}



On Fri, Aug 15, 2014 at 7:00 AM, Morten Olav Hansen <mortenoh@xxxxxxxxx>
wrote:

> Yeah, just to clarify, every endpoint (except /api/metadata) can only take
> one single object. If you want to have multiple of one or more types, you
> will have to post them through /api/metadata endpoint.
>
> Good that you got it working :)
>
> --
> Morten
>
>
> On Fri, Aug 15, 2014 at 11:56 AM, Jason Pickering <
> jason.p.pickering@xxxxxxxxx> wrote:
>
>> Thanks for all the tips and code.
>>
>> Changing "orgunits" to "organisationUnits" and posting to the /metaData
>> endpoint seems to do the trick.
>>
>> Best regards,
>> Jason
>>
>>
>>
>> On Fri, Aug 15, 2014 at 12:11 AM, Jan Henrik Øverland <
>> janhenrik.overland@xxxxxxxxx> wrote:
>>
>>> You are probably aware of this, but if there is no particular reason you
>>> want to post to api/organisationUnits, it will work with multiple orgunits
>>> if you wrap it like this
>>>
>>> {
>>>     "organisationUnits": [
>>>         ...,
>>>         ...,
>>>         ...,
>>>         ...,
>>>         etc
>>>     ]
>>> }
>>>
>>> and post it to api/metaData instead.
>>>
>>>
>>> On Thu, Aug 14, 2014 at 10:40 PM, Dan Cocos <dan@xxxxxxxxxxxx> wrote:
>>>
>>>> I don’t think you can post multiple, here’s some PHP code I wrote to
>>>> add them one by one they all had the same parent so my code is somewhat
>>>> less complex, you’ll have to have more arrays containing the correct parent
>>>> and other details for your OUs.
>>>>
>>>>
>>>>  <?php //You need to make sure to add a ROOT unit and then make this
>>>> set to the root user $parentId = "JFSc3tndTlI"; $instanceURL = "
>>>> http://localhost:8080/api/organisationUnits";; $username = "admin";
>>>> $password = "district";  $countryNames = array("Angola", "Antigua and
>>>> Barbuda", "Asia Regional Program", "Bahamas, The", "Barbadas", "Belize
>>>> - Carribean", "Belize - Central America", "Botswana", "Burma",
>>>> "Burundi", "Cambodia", "Cameroon", "Caribbean Region", "Central
>>>> America Region", "Central Asia Region", "China", "Democratic Republic
>>>> of the Congo", "Costa Rica", "Cote d'Ivoire", "Dominica", "Dominican
>>>> Republic", "El Salvador", "Ethiopia", "Ghana", "Grenada", "Guatemala",
>>>> "Guyana", "Haiti", "Honduras", "India", "Indonesia", "Jamaica",
>>>> "Kazakhstan", "Kenya", "Kyrgyz Republic", "Laos", "Lesotho", "Malawi",
>>>> "Mozambique", "Namibia", "Nicaragua", "Nigeria", "Panama", "Papua New
>>>> Guinea", "Russia", "Rwanda", "Saint Kitts and Nevis", "Saint Lucia", "Saint
>>>> Vincent and the Grenadines", "South Africa", "South Sudan", "Suriname",
>>>> "Swaziland", "Tajikistan", "Tanzania", "Thailand", "Trinidad and
>>>> Tobago", "Turkmenistan", "Uganda", "Ukraine", "Uzbekistan", "Vietnam",
>>>> "Zambia", "Zimbabwe");   //I couldn't get DHIS2 to ingest with one
>>>> call with several children so instead I just make a bunch of calls.
>>>> foreach($countryNames as $ouname){  $data = array("name" => $ouname,
>>>> "shortName" => $ouname,  "parent" => array("id" =>$parentId));
>>>> $data_string = json_encode($data);     $ch = curl_init($instanceURL);
>>>> curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");  curl_setopt($ch,
>>>> CURLOPT_POSTFIELDS, $data_string);  curl_setopt($ch,
>>>> CURLOPT_RETURNTRANSFER, true);  curl_setopt($ch, CURLOPT_USERPWD, "
>>>> $username:$password");  curl_setopt($ch, CURLOPT_HTTPHEADER, array(  'Content-Type:
>>>> application/json',  'Content-Length: ' . strlen($data_string),  'Accept:
>>>> application/json',   )  );   $result = curl_exec($ch);  print $result .
>>>> "\n"; }   ?>
>>>>
>>>>
>>>>
>>>> On Thu, Aug 14, 2014 at 10:29 PM, Mark Polak <markpo@xxxxxxxxxx> wrote:
>>>>
>>>>> Jason,
>>>>>
>>>>> I tried to look up if it was possible to post a list and from what i
>>>>> can tell you can’t post a list of objects currently.
>>>>>
>>>>> But Morten would have to clarify that as i am not 100% sure if that is
>>>>> correct.
>>>>>
>>>>> Kind regards,
>>>>>
>>>>> Mark Polak
>>>>> mark@xxxxxxxxxxxxxxx
>>>>> markpo@xxxxxxxxxx
>>>>> +31 6 38 600 925
>>>>>
>>>>> On 14 Aug 2014, at 21:03, Jason Pickering <jason.p.pickering@xxxxxxxxx>
>>>>> wrote:
>>>>>
>>>>> Unfortunately, I cannot post it a public list, but I think I may be
>>>>> missing some brackets etc.
>>>>>
>>>>> Basically, I need to sync some orgunits between instances with JSON
>>>>> (after a bit of processing).
>>>>>
>>>>> Will the below work (in theory)? I could not really find an example of
>>>>> POSTing multiple orgunits with JSON through the API in the docs.
>>>>>
>>>>> Regards,
>>>>> Jason
>>>>>
>>>>> {
>>>>>     "orgunits": [
>>>>>         {
>>>>>             "code": "XXXXX",
>>>>>             "name": "XXXXX",
>>>>>             "created": "2013-03-14T06:17:28.334+0000",
>>>>>             "lastUpdated": "2014-05-27T09:45:58.804+0000",
>>>>>             "shortName": "XXXX",
>>>>>             "parent": {
>>>>>                 "name": "XXXX",
>>>>>                 "created": "2012-07-14T18:57:55.059+0000",
>>>>>                 "lastUpdated": "2013-09-02T08:04:08.693+0000",
>>>>>                 "href": "XXXXXXX",
>>>>>                 "id": "XXXXXX"
>>>>>             },
>>>>>             "openingDate": "1900-01-01",
>>>>>             "active": true,
>>>>>             "contactPerson": "XXXX",
>>>>>             "address": "XXXXXX",
>>>>>             "email": "XXXXXX",
>>>>>             "phoneNumber": "XXXXXX",
>>>>>             "level": 5,
>>>>>             "href": "XXXXXX",
>>>>>             "id": "XXXX"
>>>>>         } ]}
>>>>>
>>>>>
>>>>> On Thu, Aug 14, 2014 at 7:22 PM, Mark Polak <markpo@xxxxxxxxxx> wrote:
>>>>>
>>>>>> Hey Jason,
>>>>>>
>>>>>> Perhaps it could also be useful if you provide the JSON you’re
>>>>>> sending? That might make it easier to figure out why it happened :)
>>>>>>
>>>>>> Kind regards,
>>>>>>
>>>>>> Mark Polak
>>>>>> mark@xxxxxxxxxxxxxxx
>>>>>> markpo@xxxxxxxxxx
>>>>>>
>>>>>> On 14 Aug 2014, at 17:25, Jason Pickering <
>>>>>> jason.p.pickering@xxxxxxxxx> wrote:
>>>>>>
>>>>>> Hi Morten,
>>>>>>
>>>>>> I am trying to post some JSON to the /api/organisationUnit end point.
>>>>>> I think it looks OK (???) but am something does not seem to be right. I see
>>>>>> this exception. Could you translate for me? :)
>>>>>>
>>>>>> Regards,
>>>>>> Jason
>>>>>>
>>>>>>
>>>>>>
>>>>>> SEVERE: Servlet.service() for servlet [webapi] in context with path
>>>>>> [/dhis] threw exception
>>>>>> com.fasterxml.jackson.databind.JsonMappingException: Can not
>>>>>> deserialize instance of org.hisp.dhis.organisationunit.OrganisationUnit out
>>>>>> of START_ARRAY token
>>>>>>  at [Source: org.apache.catalina.connector.CoyoteInputStream@1d72f464;
>>>>>> line: 1, column: 1]
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:691)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:685)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromArray(BeanDeserializerBase.java:1215)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:151)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:126)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2993)
>>>>>>         at
>>>>>> com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2144)
>>>>>>         at
>>>>>> org.hisp.dhis.dxf2.render.DefaultRenderService.fromJson(DefaultRenderService.java:79)
>>>>>>         at
>>>>>> org.hisp.dhis.webapi.controller.AbstractCrudController.postJsonObject(AbstractCrudController.java:352)
>>>>>>         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>>>>>         at
>>>>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
>>>>>>         at
>>>>>> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
>>>>>>         at java.lang.reflect.Method.invoke(Method.java:606)
>>>>>>         at
>>>>>> org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
>>>>>>         at
>>>>>> org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
>>>>>>         at
>>>>>> org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
>>>>>>         at
>>>>>> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
>>>>>>         at
>>>>>> org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:685)
>>>>>>         at
>>>>>> org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
>>>>>>         at
>>>>>> org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:919)
>>>>>>         at
>>>>>> org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:851)
>>>>>>         at
>>>>>> org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953)
>>>>>>         at
>>>>>> org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:855)
>>>>>>         at
>>>>>> javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
>>>>>>         at
>>>>>> org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829)
>>>>>>         at
>>>>>> javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
>>>>>>         at
>>>>>> org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
>>>>>>         at
>>>>>> org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:201)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.hisp.dhis.security.filter.CustomAuthenticationFilter.doFilter(CustomAuthenticationFilter.java:64)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.hisp.dhis.security.filter.AutomaticAccessFilter.doFilter(AutomaticAccessFilter.java:113)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
>>>>>>         at
>>>>>> org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
>>>>>>         at
>>>>>> org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
>>>>>>         at
>>>>>> org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
>>>>>>         at
>>>>>> org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
>>>>>>         at
>>>>>> org.springframework.orm.hibernate4.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:151)
>>>>>>         at
>>>>>> org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
>>>>>>         at
>>>>>> org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
>>>>>>         at
>>>>>> org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
>>>>>>         at
>>>>>> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
>>>>>>         at
>>>>>> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
>>>>>>         at
>>>>>> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
>>>>>>         at
>>>>>> org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
>>>>>>         at
>>>>>> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
>>>>>>         at
>>>>>> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
>>>>>>         at
>>>>>> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
>>>>>>         at
>>>>>> org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
>>>>>>         at
>>>>>> org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
>>>>>>         at
>>>>>> org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
>>>>>>         at
>>>>>> org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313)
>>>>>>         at
>>>>>> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
>>>>>>         at
>>>>>> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
>>>>>>         at java.lang.Thread.run(Thread.java:744)
>>>>>>
>>>>>> _______________________________________________
>>>>>> Mailing list: https://launchpad.net/~dhis2-devs
>>>>>> Post to     : dhis2-devs@xxxxxxxxxxxxxxxxxxx
>>>>>> Unsubscribe : https://launchpad.net/~dhis2-devs
>>>>>> More help   : https://help.launchpad.net/ListHelp
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> Mailing list: https://launchpad.net/~dhis2-devs
>>>>> Post to     : dhis2-devs@xxxxxxxxxxxxxxxxxxx
>>>>> Unsubscribe : https://launchpad.net/~dhis2-devs
>>>>> More help   : https://help.launchpad.net/ListHelp
>>>>>
>>>>>
>>>>
>>>> _______________________________________________
>>>> Mailing list: https://launchpad.net/~dhis2-devs
>>>> Post to     : dhis2-devs@xxxxxxxxxxxxxxxxxxx
>>>> Unsubscribe : https://launchpad.net/~dhis2-devs
>>>> More help   : https://help.launchpad.net/ListHelp
>>>>
>>>>
>>>
>>
>> _______________________________________________
>> Mailing list: https://launchpad.net/~dhis2-devs
>> Post to     : dhis2-devs@xxxxxxxxxxxxxxxxxxx
>> Unsubscribe : https://launchpad.net/~dhis2-devs
>> More help   : https://help.launchpad.net/ListHelp
>>
>>
>

Follow ups

References