I am working with a use case that created a need to have custom content types that can be fieldable. So in an attempt to get this to work I started working on a fieldable panels pane module to essentially marry a custom ctools content type with drupal entities. So far so good, but I have ran into a part that is stumping me, and I could use some pointers from some one that has more indepth knowledge of the panels API in hopes that it could point me in the right direction.
Merging panel panes with the entity api was fairly straight forward and seems to be working pretty well. The problem I am running into is when I create a new panel pane. When a panel pane is submitted on first creation the $pane object has the pane ID as "new". Which is as expected. What I am trying to wrap my head around is the process workflow from when a panel pane is created to when it is saved and finally gets a pid value.
It appears that the panel pane getting created gets saved in the panels_save_display() function. Which from what it looks like this isnt actually called until the overall panel is saved. Are these assumptions correct?
fieldable_panels_pane.inc:
<?php
$plugin = array(
'title' => t('Fielded custom content'),
'description' => t('Create custom panels pane with fields'),
'top level' => TRUE,
'category' => t('Custom'),
'all contexts' => TRUE,
'defaults' => array(),
);
/*
* Implementation of hook_content_type_render
*/
function fieldable_panels_panes_fieldable_panels_pane_content_type_render($subtype, $conf, $panel_args, $context) {
$block = new StdClass;
$block->title = !empty($conf['override_title']) ? $conf['override_title_text'] : '';
$block->content = fieldable_panels_pane_view($conf);
if (empty($block->content)) return;
return $block;
}
/*
* Implementation of hook_content_type_edit_form
*/
function fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form($form, &$form_state) {
$pane = $form_state['pane'];
$pane->bundle = 'fieldable_panels_pane';
$form['is_new'] = array(
'#type' => 'hidden',
'#value' => is_numeric($pane->pid) ? FALSE : TRUE,
);
field_attach_load('fieldable_panels_pane', array($pane->pid => $pane));
field_attach_form('fieldable_panels_pane', $pane, $form, $form_state);
return $form;
}
function fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_submit($form, &$form_state) {
$pane = $form_state['pane'];
field_attach_submit('fieldable_panels_pane', $pane, $form, $form_state);
$pane = array_merge($form_state['values'], (array)$pane);
if ($form_state['values']['is_new']) {
field_attach_insert('fieldable_panels_pane', (object)$pane);
} else {
field_attach_update('fieldable_panels_pane', (object)$pane);
}
$form_state['conf']['pid'] = $pane['pid'];
}
Above is the code that I am working with currently. In fieldable_panels_panes_fieldable_panels_pane_content_type_edit_form_submit() I attempt to save the field values which works fine when a panel pane has already been created, just not when the pane is new. Trying to save the pane in this function just created duplicate panes as panels ends up saving a new pane later on in the process.
So is there a better place that I can try and save this field data or am i just trying to go way beyond the limitations of whats possible with panels?
Any advice is greatly appriciated
| Comment | File | Size | Author |
|---|---|---|---|
| #10 | fieldable_panels_panes.tar_.gz | 2.64 KB | cangeceiro |
Comments
Comment #1
merlinofchaos commentedOk, you don't want to work with pids. Pretty much ever, if you can avoid it. As far as we're concerned, they're internal data to Panels itself. And they can change. Export a display and the display stays the same, but all the pids change. Clone a display and the new display has new pids.
Instead, I think you want the content itself to be fieldable. In that sense, I would think you'd create a custom content similar to custom.inc -- it can create a new content right there on the fly, and put it in the database. Then, the pane refers to the content ID (aka, the entity ID) and as much as possible, all other configuration is stored on your entity.
Your entity then is agnostic to the pane itself; it's just a blob of content (as it should be) that later gets formatted within a pane.
The custom content type which is extended by ctools_custom_content module almost already does this, except it's built off export.inc rather than entities, so it's not fieldable. But duplicating that and changing it around so that it's built off an entity is better.
Comment #2
cangeceiro commentedah thanks so much merlin. this definatly points me in the right direction.
Comment #3
merlinofchaos commentedBy the way, a custom content pane entity would be very very valuable and I'd consider pulling it into CTools directly. Though. I guess the downside is that then we have confusion between the exportable and non exportable. But I don't know how to export fields, especially file fields, so it's kind of a question of capabilities.
Comment #4
cangeceiro commentedok, cool. ill repost what i have once i get it to a stable level. Currently I am trying to hash out a couple things that i could use some further pointers on.
1. Removing the entity when a pane is deleted. Currently i can't find anything in the panels api that i can hook into when a pane is being removed so that i can go back in and clean up the fields and entity tables. In panels_save_display it looks like panes are cleaned up at:
and once again cleaned up in panels_delete_display simply with:
Could a hook be introduced here in panels to modify panels panes or is there a better method?
2. I seem to be loosing the pane config by introducing field_attach_form() to the pane configuration form. Actually, I am not actually loosing it, I can dpm() form_state and the conf key is set in my submit function. But when I save the panel display. What is the magic behind the voodoo from the time i save a pane and how that data is stored until i save the panel display?
Comment #5
merlinofchaos commented1) What if a panel is exported into code? Then the actual pane is deleted, but the reference still exists! Because of exportability, there is simply no way to tell if there is a reference to your entity, which means once created, you can't delete them, at least not automatically.
2) Content config is stored in a temporary location which varies based upon the actual application running the panel. Since the content configuration is a CTools wizard, I believe it is copied back into the actual pane config when the wizard form is finished. The logic for this is in the display renderer editor plugin, and you can look at ajax_add_pane and ajax_edit_pane to see the flow. It looks like the _submit functions directly write to $conf and $conf should be a reference to the $conf in the $pane. As long as you're not doing something that will trigger form caching, this should all work ok. If you're triggering form caching, woe betide us because that will bork the references I think.
Comment #6
cangeceiro commentedafter several days of beating my head up against a wall trying to get this to work the only solution i could find is probably more of a hack then a real fix. It appears that somewhere $form_state is getting copied and it looses its reference to the $pane object. Im not really sure why this is happening but it seems directly related to adding the field elements to the form.
So this is what i ended up having to do in the display renderer:
When fields_attach_form() is commented out in the form and i submit the form the $pane object gets its configuration settings after getting passed into ctools_content_form. when adding fields_attach_form() to the form in my form function the $pane object never gets its configuration, adding $pane = $form_state['pane'] seems to reestablish the reference that is needed to ensure these config settings get passed along. If i had to guess a hook somewhere in the chain copies $form_state and the reference is lost. I am open to suggestions as to why this might be happening but at this point i have spent to much time on it and will have to take the hack fix for the time being.
Comment #7
zilverdistel commentedI had exactly the same problem (the configuration not being saved), and the approach in #6 worked for me too. I had to add the line in both the functions ajax_add_pane() and ajax_edit_pane().
Fortunately, I had already extended the panels_renderer_editor, so I could add the 2 functions in my class and alter them there.
Comment #8
merlinofchaos commentedI think the hack in #6 is because of form caching and ajax and it's actually probably an okay way to do it. I think the reference gets broken by form caching and pulling $form_state['pane'] directly seems to fix that problem.
Comment #9
merlinofchaos commentedIs your code for this somewhere? I need entity panes now too. Would like to not have to reinvent the wheel.
Comment #10
cangeceiro commentedI've attached it to this comment. I am also sure that it would need a little more work to be community ready. And there is one issue that i have been scratching my head for weeks and have no solution for at the moment. see #1286316: Cannot use imagefield/filefield widgets in modal dialog
Comment #11
jenlamptonSubscribe
Comment #12
merlinofchaos commentedAwesome.
I'm going to go ahead and turn this into a real module, but my approach is going to be a little bit different. While I think it is fine to be able to create these entities as a pane, they also need a separate management interface. There is no way to reliably tie an entity to a pane. For example, there's no notification for when a pane is deleted. Even if there were, it might be inaccurate if the pane were to be exported and then deleted from the database. In this case, the pane still exists, but its existence cannot be proven from with in Drupal, because it may exist somewhere in the ephemera. The linkage, therefore, MUST be soft.
But also, the requirement I have is that the entity can be created independently, so like reusable custom content it has to also be able to appear as content that can be added. This means multiple panes could point to an entity. Or no panes.
The problem with imagefield probably has to do with ajax/form caching and a bit of code in Panels that I think EclipseGc has figured out. That shouldn't be an issue. I'll know more over the course of the next couple weeks.
What would you like me to do with this module when I'm ready?
My inclination is to create it as a project and add you as a full maintainer, if that's what you want. Alternatively, I could rename it because some of the changes I need to make here will be significant and if I go in a direction you don't want to go, it might be best to be purely forked. Not quite sure how to address that just yet.
Comment #13
cangeceiro commentedNothing you have proposed here strays away from the needs of this module in the application i am trying to apply it to, so i dont think any of that is a problem. with that in mind i would be happy to work towards getting this out as an official module and working towards getting the improvements you mentioned into place.
Just let me know how you would like to proceed.
Comment #14
bgilhome commentedsubscribe
Comment #15
merlinofchaos commentedOk I have gotten a big start on this and checked in my initial work here: http://drupal.org/project/fieldable_panels_panes
It certainly doesn't work well enough to be usable right now but I've given it a pretty big makeover and it does a lot. I'll push pretty hard on this over the next week or two.
Comment #16
cangeceiro commentedsounds good. let me know if there is anything i can do to help out
Comment #17
merlinofchaos commentedI'd love it if you'd try out the new module.
Comment #18
cangeceiro commentedI checked it out of git the other day, played with it a little but not much, but already have it tasked out to bounce back to this in the afternoon today...busy busy busy here lately
Comment #19
cangeceiro commentedPlayed around a little bit with the module today. Its feeling pretty good so far, no major issues. though i ran into a few errors here and there on some admin pages that will need addressed. Also committed a small fix to the revision changes you made. Basically new panes gave a php notice that the vid and current_vid properties didn't exist, the function that creates the dummy new property didn't have them. The issue with imagefields does still exist.
Hopefully ill have a little bit of time to play around with this some more tonight. What do you propose is the best course for resolving issues so that we dont step on toes, or (probably more importantly) anything i do doesn't veer off track
Comment #20
jenlamptonI've been posting issues against the new module.
Comment #21
merlinofchaos commentedThe issue with the imagefields will be fixed if you get Panels latest -dev.