A page for taking my notes on server- and client-side plugin development with DeepaMehta 4.
Table of Contents
On this page you find my personal notes (as hints) for:
- DeepaMehta Webclient Development
- DeepaMehta Service Development
- Stuff to keep in mind when designing your new plugin
DeepaMehta Webclient Development
Webclient Hooks: Reacting to events of the dm4-webclient
For a most probably up-to-date list of all events currently fired by the DeepaMehta 4 Webclient see https://github.com/jri/deepamehta/blob/master/modules/dm4-webclient/src/main/resources/docs/events.txt or your local copy of that under $DM-Home/modules/dm4-webclient/.
Implementing Many Simple Renderers
It seems to me that one can not register many simple_renderer-implementations* in one js-renderer file.
Doing so, will make the webclient crash on initial startup with random error messages, mostly of the like "(parsererror: TypeCacheError?: topic type "dm4.core.topic_type" not found)".
Hint: A simple_renderer implementaton looks usually like this: dm4c.add_simple_renderer('tld.project.type.renderer_name', { render_info:function(model, $parent){}, render_form:function(model, $parent){}})
After slicing all my simple_renderers into seperate .js-files, webclient startup works fine again.
Implementing the render_form function of a complete page_renderer
This might be a rare case, but it happens. See #569 for full details.
Referencing existing topic instances from client-side topic-models (JavaScript? / JSON Syntax) serialization syntax
For creating a complete composite (with all new/or existing child-topics) in one Request, the topic models (JSON) array of child-topics needs to use the following semantics like this:
(comment by jri, see updateAggregationMany() in AttachedCompositeValue?. 4 cases are supported:)
// for deleting or referencing existing topics, child topic array (of the one specific type) needs to look like this // (no full json-objects needs to be constructed here), e.g.: [ "del_id:40190", "ref_id:51291", "ref_id:51131", "ref_id:11318"]
An example request making use of all 4 cases could carry a payload (HTTP Body in the POST) could look like the following one (withou the comments):
[ // deletes a reference to an existing topic "del_id:40190", // creates a new topic and references it { "id":-1, "uri": "", "type_uri":"dm4.tags.tag", "value": "my_personal_tag"}, // references an existing topic by id "ref_id:51131", // references an existing topic by uri "ref_uri: "my.cool.instance.data_uri"]
As soon one knows this syntax, it frees the developer from the error-prone solutions of creating topics in multiple-steps, e.g. creating them independently (e.g. in JavaScript?) in a 1st step and then reference them in a 2nd step.
DeepaMehta Service Development
DeepaMehta Core-Service exposed via REST
Have a look at all annotated core-service methods in de.deepamehta.plugins.webservice.WebservicePlugin? which is available with the DeepaMehta SourceCode? and to find in the modules/dm4-webservice module.
Building plugins from source / maven usage
If you build everything from source mvn clean install is what you need to satisfy a projects maven dependencies.
This is a plugins parent pom you want to use (when developing a plugin):
<parent> <groupId>de.deepamehta</groupId> <artifactId>deepamehta-plugin-parent</artifactId> <version>4.1.1-SNAPSHOT</version> </parent>
Working on server-side with dms (ApplicationService?
As a rule of thumb: Whenever you pass the ClientState?-Object into a dms.*-call, the Workspace-Assignment will be set automatically by DeepaMehta. This happens because the information which "User" is currrently active in which "Workspace" is part of the Session-Cookie. Vice-versa: If you pass ClientState? a null into a createTopic call your topic will most probably have no "Workspace" assigned at all and you should do so manually.
If ACL-Settings are set correctly depends on the availability of a "HttpSession?", resp. if the ACL-Module knows who's the currently logged in user. The latter is never the case if your server-side methods are do not have a "HttpRequest?"-Scope (e.g. in a Thread or when just embedding the DeepaMehtaService? in your Java-Application). In that case, you' will have to take care of setting ACLEntries and a Workspace-Assignments for all the topics/assocs you create manually.
Example given:
private Topic setDefaultMoodleAdminACLEntries(Topic item) { // Let's repair broken/missing ACL-Entries ACLEntry writeEntry = new ACLEntry(Operation.WRITE, UserRole.CREATOR, UserRole.OWNER); aclService.setACL(item, new AccessControlList(writeEntry)); aclService.setCreator(item, USERNAME_OF_SETTINGS_ADMINISTRATOR); // which resolves to "admin" in this case aclService.setOwner(item, USERNAME_OF_SETTINGS_ADMINISTRATOR); // which resolves to "admin" in this case return item; }
Constructing a composite (complex) topic object imperatively
One way how i often create new, complex topic instances, in three steps: 1) CompositValueModel?, 2) TopicModel?, 3) Topic
// create the CompositeValueModel CompositeValueModel fileComposite = new CompositeValueModel(); // build it up according to your model fileComposite.put("org.deepamehta.moodle.filename", filename); fileComposite.put("org.deepamehta.moodle.filepath", filepath); // create a new TopicModel TopicModel fileModel = new TopicModel(MOODLE_FILE_URI, fileComposite); // finally create the new topic instance (with proper ACLs) Topic moodleFile = dms.createTopic(fileModel, clientState);
Introducing my custom types to DeepaMehta's default workspace
Assigning your custom types to a workspace is not yet configurable. And manually assigning custom types to the default workspace opens up space for errors.
So, especially in a clean installation (DB from scratch) your plugins migration assigning your types to the defualt workspace depends on the "de.deepamehta.workspaces"-plugin which initially creates the "Default Workspace Topic (uri: de.workspaces.deepamehta)".
Adding de.deepamehta.workspaces into the line "importModels=" of my plugin.poperties file makes sure that the dm4-workspaces-plugin is started, and thus the default workspace topic is created, before my custom plugin starts running any of its migrations.
Addendum: Since the "importModels=" property influences the starting order of all plugins, a rule of thumb was identified in Ticket #465: "If your plugin introduces new types, you have to import the "de.deepamehta.accesscontrol" model". Otherwise ACL-Entries may not be set correct during installation of your plugin since the ACL-Plugin is not there to do it's job.
Introducing a shared workspace and set up your topics to editable by all members of this shared workspace
A proper workspace assignment in DeepaMehta involves the "Username"-Topic (and not the "User Account"-Topic) to be the:
"Username" (Parent) <- "Aggregation" -> (Child) "My Workspace"
Furthermore, the topics to be edited by all "Members" of "My Workspace", should be set up to match at least the following ACL:
ACLEntry writeList = new ACLEntry(Operation.WRITE, UserRole.MEMBER, UserRole.CREATOR, UserRole.OWNER);
DeepaMehtas? default behaviour for new types introduced by a plugin
DeepaMehtas? default behaviour for new (programmatically introduced) types is to not automatically assign type's to a "Workspace" if the types uri does not begin with the identifier "dm4.*".
Introducing (editable) topic instances in a declarative migration, e.g. "Config"-Topics
SSince topic instances introduced programmatically within a migration don't get an AccessControlList? set, the developer must take care of this, e.g. in the postInstall-Hook and with/after consuming the AccessControlService?.
Example: Just the user "admin" should be able to edit a certain configuration, as it should happen with the new mapping-moodle plugin:
@Override public void postInstall() { if (aclService != null) { // panic check (allPluginsMustBeActive) Topic serviceEndpointUri = getMoodleServiceUrl(); aclService.setCreator(serviceEndpointUri.getId(), "admin"); aclService.setOwner(serviceEndpointUri.getId(), "admin"); AccessControlList aclist = new AccessControlList() .addEntry(new ACLEntry(Operation.WRITE, UserRole.CREATOR)); aclService.setACL(serviceEndpointUri.getId(), aclist); } }
Referencing topic instances in a declarative migration
Example giving, referecing an existing topic by uri works like this:
"composite": { "my.topic.type.uri" : "ref_uri:my.awesome.topic.instance.uri" }
Stuff to keep in mind when designing your new plugin
I hope you can take something out of my observations (see below) of the FF API and some of my experiences I made when starting a DM4 plugin.
Read more at http://lists.deepamehta.de/pipermail/devel-lists.deepamehta.de/2014-June/000499.html