Is it possible to use the Org Chart extension to pull the information for an org chart from the LDAP connection?
Looks like there is no direct support for this. Maybe you can query your LDAP directory via script and convert the result to JSON/a list and use this with Org Chart.
Just out of curiosity: Which LDAP server are you using?
Which LDAP attribute would you use to establish parent child relationships for the Org Chart?
We’re using Active Directory for our authentication.
Since we have our org structure in place in AD, it would be nice to be able to pull dynamically from there so we don’t have to maintain two different systems.
What entities do you want to show in the org chart? Employees, OrgUnits?
How is you orgstructure modelled in AD? Do you have a structure of OUs or do you use the manager field in AD users?
While I have nothing to do with the org chart macro, I find your case quite interesting and maybe it would be possible to prepare a json from AD that could be loaded in the org chart macro.
What would work well for us would be to have Employee Name (cn), Title (title), and Department (department). Then, the Manager (manager) would be above the employee in the org chart.
Really similar to the one in the example.
I have started to write a groovy script to extract the information from LDAP and generate the JSON.
Two questions arise:
First it depends on the deprecated API xwiki.getXWiki().getConfig() to read LDAP config from xwiki.cfg. Is there a newer version of the API accessible from groovy to achieve this?
Assuming we had the script ready and it produced the JSON how could we inject this in the Org Chart macro?
Could I create a macro from this and nest into the Org Chart macro?
Here is the javadoc of that method:
/**
* @deprecated since 6.1M2, use {@link ConfigurationSource} component with hint <code>xwikicfg</code> instead
*/
The easiest to get a component in Groovy is something like this:
services.component.getInstance(org.xwiki.configuration.ConfigurationSource.class, 'xwikicfg')
That being said you don’t even need that since the LDAP extension actually expose an API to access the configuration:
import org.xwiki.contrib.ldap.XWikiLDAPConfig;
XWikiLDAPConfig configuration = new XWikiLDAPConfig(null);
which is much better because it provide dedicated method to parse special properties like group mapping and also deal with the jingling between XWikiPreference and xwiki.cfg.
Thank you very much for the tips, will definitely update my script.
Anything to the other question? Can I nest macros? Is there any better way to fill the macro body of the org chart macro (or any macro I guess)?
I don’t know this macro very well but if it’s a wiki macro then it works like any other wiki content: you include a page which contain some script and you use class or functions defined in that script.
So I have a first draft of all the building blocks and I can get it to work somehow, but to be honest it’s plain ugly right now. But I guess it’s a start.
Also it’s completely AD dependent (requires the calculated field “directReports”, which points to all managed people on the manager object.)
Here is the groovy script:
{{groovy}}
import org.xwiki.contrib.ldap.XWikiLDAPConfig;
import org.xwiki.contrib.ldap.XWikiLDAPConnection;
import org.xwiki.contrib.ldap.XWikiLDAPSearchAttribute;
import com.novell.ldap.LDAPConnection;
import groovy.json.JsonOutput;
//Adjust to the DN of the root of your Org Chart
managerDN='CN=supermanager,CN=Users,dc=example,dc=local'
ldapconfig=new XWikiLDAPConfig(null)
server=ldapconfig.getLDAPParam("ldap_server","")
port=ldapconfig.getLDAPParam("ldap_port","")
binddn=ldapconfig.getLDAPParam("ldap_bind_DN","")
bindpw=ldapconfig.getLDAPParam("ldap_bind_pass","")
basedn=ldapconfig.getLDAPParam("ldap_base_DN","")
ssl=ldapconfig.getLDAPParam("ldap_ssl","")
keys=ldapconfig.getLDAPParam("ldap_ssl_keystore","")
orgChartMap=null
def connection = new XWikiLDAPConnection();
connection.open(binddn, bindpw, xcontext.getContext())
String[] params = new String[6]
params[0] = "dn"
params[1] = "samAccountName"
params[2] = "directReports"
params[3] = "manager"
params[4] = "title"
params[5] = "cn"
def resolveTree(ldapconnection, managerDN, managerMap, params){
def results = ldapconnection.search(managerDN, '(objectClass=*)' , params, LDAPConnection.SCOPE_BASE);
def childMap=[:]
while (results.hasMore()){
def entry = results.next()
childDN=entry.getDN()
childMap['dn']=childDN
titleAttr=entry.getAttribute('title')
childMap['name']=entry.getAttribute('cn').getStringValue()
if(entry.getAttribute('title')){
childMap['title']=titleAttr.getStringValue()
}
childMap['children']=[]
directReports=entry.getAttribute("directReports")
if(managerMap==null){
managerMap=childMap
}
if(directReports!=null){
for (child in directReports.getStringValues()){
childMap['children'].add(resolveTree(ldapconnection, child, childMap, params))
}
}
}
return childMap
}
orgChartMap=resolveTree(connection, managerDN, orgChartMap, params)
def json = JsonOutput.toJson(orgChartMap)
println(JsonOutput.prettyPrint(json))
{{/groovy}}
I put this in a page OrgChartTest and consumed it in a page OrgChart like so (I guess there should be a better way).
{{velocity}}
#set($json=$xwiki.getURLContent("http://mywikiserver:8080/xwiki/bin/get/OrgChartTest/?outputSyntax=plain"))
{{orgchart source="json"}}
$json
{{/orgchart}}
{{/velocity}}
Instead of going trough HTTP it would probably be better to load the document and render it. Something like:
#set($chartDocument = $xwiki.getDocument('OrgChartTest.WebHome'))
#set($json = $chartDocument.getRenderedContent($services.rendering.resolveSyntax('plain/1.0')))
Here are other alternatives:
{{include reference="OrgChartTest.WebHome"/}}
and directly use variables set in the{{groovy}}
macro. Are shared only the global variables (which means remove thedef
keyword). The limitation is that OrgChart also needs programming right (since include make the included content behave as if it was part of the includer document)
...
json = JsonOutput.toJson(orgChartMap)
{{/groovy}}
{{velocity}}
{{orgchart source="json"}}
$json
{{/orgchart}}
{{/velocity}}
-
when I need to share stuff implemented in groovy to other script with lower rights I generally do the same than in Java: I put my groovy code in a script service. Right now the simplest is generally to implement a listener as a wiki component and inject the groovy script service in the listener initialize method. Here is an example: the page
InvoicesCode.ServiceInitializerListener
(it actually contain a lot more than the script service) in extension https://extensions.xwiki.org/xwiki/bin/view/Extension/Invoices (that I more or less use to see the pain points of implementing an advanced extension in full wiki pages) -
put complex code in Java instead of groovy and expose a ScriptService
Thank you very much, @tmortagne. I think I’ll go with the rendering (your first suggestion) for now. Actually I’m not sure if this all is still required, @mity-dave never sent a comment and for me it was just a little test project.
Sorry @rbr and @tmortagne for the lack of response from me.
We decided to go with https://teamorgchart.com/ because it has all of the functionality we need already, and it’s easy enough for our executive team and HR Department to use.
Hopefully the code you made can help someone else out in the future though.