Forum OpenACS Development: Custom Code: New development solution

As a company we have a rule that says: Work out of the OpenACS CVS tree as much as possible to prevent redundancy (and merge headaches afterwards). It also encourages us to be extra careful with what we work and always keep reusability in mind.

But how are we managing this with the high demands of clients that are not applicable broadly or may not be released?

Two methods:

  1. Overloading of procedure names. We do this only in the most dire circumstances and disable the "reload package" function on the production site. I don't encourage this, but it sometimes is the only way. BUT....
  2. since 5.2 we have CALLBACKS. And despite some arguments it is one of the best inventions OpenACS has seen in a long time. Because we can define callbacks and hooks in the packages like forums and have custom code hook in there. How can this be done:
    • You want to add new custom fields to the data entry and it cannot be achieved by using Dynamic Types, Flexbase, AMS or whatever other functionality we come up with to add dynamic attributes to existing object_types. Add a package_key::form::create_form callback that allows you to extend the form with the desired elements. the package_key::form::submit_form callback will then trigger the callback implementation in your custom package that deals with the additional user input.
    • You have a list and want to extend it with an additional bulk action (e.g. select files and folders and send the selected files/folders via E-Mail to a person. Look at file-storage/www/folder-chunk.tcl for the hook and contacts-callback-procs.tcl for the implementation). You create a hook right after setting the bulk_action_list variable (it is mandatory that you change your listbuilder code to work with preset variables instead of having a manual list in the -bulk_actions switch). This callback could be called fs::folder_chunk::add_bulk_actions and has the variables to path "-folder_id -bulk_action_var_name -export_var_list". The folder_id should be clear, the -bulk_action_var_name should be the name of the variable that holds the bulk actions and the var_list is the list that holds the variables to be exported. In your callback implementation (e.g. in contacts) you upvar the two passed variables as well as any custom variable you want / need to set. Then you just add the bulk action to your local var name for $bulk_action_var_name and off you go.
Another thing that has helped us a lot is the ability for application_link and application_data_links. An application link links to package_instances together. This is useful if you want to execute code in the callback only if there is a connection between the packages. The equivalent is true for application_data_links, though there the data items are related. I realized a little bit too late that we could probably have reused the functionality in related_items if we had thought about it a little bit more, but well, nobody is perfect :).

How does this help you to work out of CVS of OpenACS and why should you do it? First, you can get rid of most custom code in the packages using this methodology (even some UI changes are ammendable this way). Furthermore it (hopefully) prevents you from forking your code (if there are too many changes in your codebase). And last but not least development should be faster for all.

Special bonus for Project/Open (and others who want to build products on OpenACS code): You don't have to rewrite OpenACS code. You just add a callback hook in the OpenACS code and let your proprietary functions "hook" in there without a GPL violation. Now, if this isn't an argument for using OpenACS!

Enough from me for now, need to write more callbacks :). If you are interested in this and want to learn more I'm planning to have an online workshop explaining how to use these methods probably first weekend of July and /or first weekend in August. Furthermore, don't hesitate to tell me what I wrote is rubbish and does not work for customer code because ... (the because is important, so we can fine tune the approach and methods).

Please let me know when are you going to have the oline workshop, I would like to be there =)

jopez

Collapse
Posted by Nima Mazloumi on
Sounds great - let us know when the online workshop takes place :)
Collapse
Posted by Richard Hamilton on
Also me please! 😊
Collapse
Posted by Dave Bauer on
Malte,

I think its a good idea to think about some standard places callbacks make sense, and provide some standard callback names and definitions that can be used in several packages, if possible. This way we can limit duplication and the number of callbacks that end up with similar, but not exactly the same interfaces.

One example is the navagation callback that was added.

Collapse
Posted by Dave Bauer on
In general I'd like to see much more community ivolvement and communication if you are planning on adding callbacks definitions. For example recently callback definitons were added to contacts, file-storage, and project-manager without any communication with the community. Only those who follow CVS commits had any idea what was going on.

At a minimum, when you add a new interface it would be helpful to post on the forums, and if possible discuss the interface with the community to see if it is generally useful,and if anyone has any feedback or ideas to contribute.

Collapse
Posted by Malte Sussdorff on
Though I agree with the notion of greater community engagement, please keep in mind that a callback is non intrusive and does not harm the workflow of a page in any way. And most additions that have been made will only work if you actually link e.g. the contacts package and the .LRN instance (so it is not possible for functionality to pop up suddenly out of nowhere). At least this is how nearly all our callback implementations have been designed.

Common standards should include e.g. a callback after a creation of an object.

Collapse
Posted by Dave Bauer on
Malte,

I agree. I just wanted to make sure everyone was aware of what callbacks are available! I am replacing the service contract code in search and in all the packages that provide search data (on HEAD for OpenACS 5.3) as I have mentioned earlier.

Collapse
Posted by Malte Sussdorff on
Usually all callbacks should be stored in the ${package_key}-callback-procs.tcl file. I found one situation where I think it might make sense to deviate from that rule:
In contacts we have a "contacts::populate" namespace, which contains optional procedures that can be run once after installation (manually using the OpenACS shell) to create a custom environment (e.g. contacts::populate::crm will setup our vision of a bare CRM system's attributes). Other setups could be stored in these procedures as well (grand vision: provide an interface to show all ::populate::* functions in the various packages keys so a user can click his initial setup together this way instead of relying on the install.xml).

Anyway, this brought me off my topic. So we have this procedure(s) which might be only called once, if at all. But they can have a lot of callbacks (e.g. for the CRM system every combination of person/organization with groups might be a good idea, so other packages could add package specific custom attributes). I would prefer not to clutter the callback-procs.tcl file with the definitions, so for contacts::populate::* callbacks I stored them in the contacts-populate-procs.tcl file and made them private.

Will this be too confusing for developers? What do others think?

As people have been asking me about the workshop, I'm currently planning to do it on a sunday in July, but I'm not sure if people would prefer a working day instead. To accomodate West Coast people the workshop will start at around 4pm GMT (9am PT, 6pm Hamburg time). Once I'm sure about possible dates, I will create an "election" so interested community members could vote on the date.

Collapse
Posted by Richard Hamilton on
In this case would it make more sense to keep this information somewhere in the database and have the ability for a single callback implementation to read different parameters on each execution? This would avoid multiple hard-coded definitions.

R.

Collapse
Posted by Enrique Catalan on
Malte,

I found the same situation as you. I did the same you say: "store the callbacks in *APP-procs.tcl*". For me, it isn't confusing but I don't know if it is the correct way.