I wanted to spin this off from #1299914: make Party label customizable, as it's not really related to the topic of that issue.
We've got two ideas for presenting data below a party, coming in from different angles. I'll try to summarize them as I understand them.
Pieces
These are really just menu structure tabs. Technically, we could just let other modules use their own hook_menu() to chuck in whatever they want under 'party/%crm_party/foobar', but I wanted to do it with modules declaring a piece to our hook_crm_party_party_pieces(), with party module takes charge of all the tabs, because I saw possibilities with doing niftier things if we know what those all are.
Because they are just menu callbacks, modules can output whatever they like about a party. It can be a single entity, it can be a view, it can be a picture of a kitten.
Because we have our own hook, we provide our own Views display plugin, which lets you magically put any View into a party tab (same sort of idea as the plugins http://drupal.org/project/views_attach, which let you put a View into node or user content).
I see this as a pretty powerful tool, because it lets application designers (ie, those who create extra modules and Features to wrap up into a complete CRM application) stick in views of whatever they like, and it lets site builders and even end users customize the views.
Beyond that, we'd have pieces for the party's activity log, and we'd have glue modules to things like Commerce to provide lists of a party/customer's recent orders.
And beyond that, for a niftier and dare I say it CiviCRM style UI, we'd implement #1300094: wider ecosystem: ajax tabs and turn the tabs ajaxy.
Data sets
Rob's approach is from a more pure data standpoint. I'm going to quote from the other issue:
an api that allows other modules to define "Data Sets" (which at the moment are always profiles 2's or users, but could be facebook data/ civicrm records etc). What we'd end up with is some hooks such as:
hook_data_set_info()
hook_data_set_display($delta)
hook_data_set_form($delta)And I guess somewhere in that API we could allow the modules so pass certain fields to the party label. We do need a label though, especially if we eventually want to ever have party autocomplete boxes
Just to make what I was saying about the data set api clearer, crm_profile might implement it like so:
<?php function crm_profile_data_set_info() { foreach(profile2_get_types() as $type) { $sets['profile2_'.$type] = array( 'label' => $type_name, 'multiple' => FALSE, ); } return $sets; } function crm_profile_data_set_display($delta) { //load profile //use profile2 functions to add fields to $build array //return $build } function crm_profile_data_set_form($delta) { // build a form for that individual profile } ?>
My feeling here is that this is a lot cleaner than my approach. It's more about data and entities and how they relate, whereas pieces is just a bit of footwork with the drupalisms like the menu system and tabs.
But different as they are, I am not sure that we need to choose one or the other!
I think the flexibility of pieces makes sense because of how much you can do with it. I think the purity of data sets makes sense for things where we can easily say "This entity / these entities are a set of things about a party". We can say that with users and profile2 entities; with things like activity log and orders maybe less so -- those are entities but outputting them without Views seems a bit like hard work.
In conclusion, I'm not yet sure how to put these two approaches together. Two ideas are kicking around in my head at the moment:
- everything is a party piece, but some pieces are generated from datasets
- everything is both a piece and a dataset: the dataset defines the data connections; the piece does the actual output (and probably everything with Views...)
Comments
Comment #1
rlmumfordThanks joachim! so I understand party pieces alot better now. I was thinking that once everything was a data set, we could build a panels like UI, that allowed you to add tabs and drag and drop "data sets" into place. To be honest I hadn't thought about views for this, but I guess you could make a module that treated views as data sets (at which point data-set probably isn't the best name anymore).
To put a data set into a party you would go to the party controller UI.
Click add data set
Be presented with a list of available data sets (profile types, salesforce wrappers, user, facebook wrappers and views (panes?))
Click on which one you want to use (for example the Main Profile)
Drag the dataset into place. You can order them, put them into different tabs etc.
To add a new type of data set
Click create new data set type
Be presented with a list of modules that allow you to create a data set type
Click on the type (eg profile2)
Be taken to the form to create a data set of that type (eg the Create Profile type form)
Make the new data set.
To add a new tab
Click add tab (piece?)
Put in details
Tab appears, then you can drag data sets into it
I realise that's more about UI, but hopefully it highlights how I understand "data sets". I think I'm saying that we should find one solution that's kind of a hybrid of both rather than trying to use a combination of the two. I would separate tabs (or display areas?) from data sets (which are the atomic sets of stuff you can display) though.
Comment #2
rlmumfordJust had another though about this, there is a distinction to be made here between party pieces (which are all about display) and data sets (which are all about data storage). Maybe what we need is two hooks, hook_data_set_info(); and hook_party_piece_info(); and if your module defines a data_set it will probably also define a piece.
That way, party extensions could define one data_set (for example, relating to a user) but many displays (e.g. username, username+roles, username+friends)
Proposed API
hook_data_set_info()
Returns an array of data sets with information about storage and permissions ammong other things key'd by data_set_type
hook_data_set_load($party_id, $delta, $set_id)
Param string $delta the data_set_type
Param int $set_id the id for the set, usually this will be the Id of a particular entity (with the $delta defining which table it comes from using hook_data_set_info
Param int $party_id the id of the party we're looking at
Returns an object of the data set
hook_data_set_form($party_id, $delta, $set_id)
Param string $delta the data_set_type
Param int $set_id the id for the set, usually this will be the Id of a particular entity (with the $delta defining which table it comes from using hook_data_set_info
Param int $party_id the id of the party we're looking at
Returns a form array
hook_data_set_save() ?
I guess then we also want things like hook_data_set_presave(), hook_data_set_alter(), hook_data_set_form_alter() ??
And then for party pieces
hook_party_piece_info()
Returns an array similar to hook_data_set_info(), one bit of which will be data set type
hook_party_piece_display($party_id, $delta, $piece_id, $set_type = null, $set_id = null)
Param string $delta the data_set_type
Param int $piece_id the id for the piece, usually this will be the id of the data set.
Param int $party_id the id of the party we're looking at
Returns an object of the data set
Notes:
Questions:
Comment #3
joachim commented> if your module defines a data_set it will probably also define a piece.
Yup, I think that's the way to go. The piece is probably defined automatically if you define a dataset?
> Menu's use callbacks which means we don't run loads of pointless functions but things can't be overwirtten
Callbacks can be overwritten with hook_menu_alter()... so if we have callbacks (which are the way Drupal is going towards, rather than hooks which are really magic callbacks) we can have a hook_party_data_set_info_alter().
> hook_data_set_info()
Best to prefix all hooks we invent.
Comment #4
rlmumfordFrom IRC: Its looks like hook_data_set_form() is now going to be a callback
Comment #5
joachim commentedIs the idea that the set form callback lets you edit all the entities in a set in single form?
Is that workable from the formAPI and fieldAPI point of view? Certainly in D6 editing multiple nodes in one form was a total nightmare.
How much work are we doing to make this possible? Could it go in entityAPI module, or a contrib module for generally editing multiple entities?
Comment #6
rlmumfordso the two things our form callback needs to be able to do is. Make a form so that that entity (or data set) can be edited on its own and make a set of fields that can be added to an 'add party' form. Profiles2 does this really nicely, and I've made a wrapper that means you can edit Profile2's on their own. So If we could do something like that for everything that would be cool.
Whatever function calls the callbacks (probably hook_party_form_alter() and the data set edit page callback) could could put the wrapper put the submit button and form id things in place. So the party data set callback would add some fields and then add a submit handler. Is this a good way of doing things? Unless we want to seperate it out - have an set_edit_form callback and an party_edit_form callback?
Comment #7
rlmumfordMost of the intricacies of Party Pieces is now achieved with Panel's, although Party Pieces are still used to make tabs quickly - often using a views plugin.
Either way, discussion has moved on.