This was originally requested by @Andrew_Mallis on #2155377-26: Only show one widget preview at a time when adding content in panels:
It would be really, really awesome if instead of a live preview, we could optionally load an image.
I could not find a hook for this.
Widgets out of context can look really strange sometimes. Many widgets require a context to render any data at all.
Proposal
I propose a multi-facted approach:
- Allow CTools content type plugins to provide a 'preview image' key in their info array which points at an image file. If Panoploy Magic finds this key filled in, it should just use that image as the preview.
- Allow CTools content type plugins to provide a 'preview callback' key in their info array which is a function that returns a render array or an HTML string. It should take at least
$type,$subtypeand$plugin. This will allow for several use cases:- Content types that provide multiple subtypes - each will need it's own preview image
- If you want to provide an HTML preview, rather than an image. This will allow the preview to match the theme and respond to the available space, but it doesn't need to actually render the widget which may lack context. You can even have the HTML respond in simple ways to the changing settings if appropriate
- Allow CTools content type plugins to provide an 'preview settings' key in their info array which will include some settings to be merged into the pane configuration before rendering. This should be done by the default 'preview callback' so that we can have other preview callbacks that change this behavior. For example, an 'preview callback' for FPPs that uses those values to create a default entity.
This will also allow us to refactor our rendering code a little bit, making our current live preview the default fallback for 'preview callback' and more easily segregate the FPP rendering from the other previews.
Examples
An image for an FPP with the bundle "complex_widget":
/**
* Implements hook_ctools_content_subtype_alter().
*/
function MYMODULE_ctools_content_subtype_alter(&$subtype, $plugin) {
if ($plugin['name'] == 'fieldable_panels_pane' && $subtype['bundle'] == 'complex_widget') {
$subtype['preview image'] = drupal_get_path('module', 'MYMODULE') . '/images/complex_widget.png';
}
}
Default settings for an FPP with the bundle "something_cool" which has a Text field called "field_some_text":
/**
* Implements hook_ctools_content_subtype_alter().
*/
function MYMODULE_ctools_content_subtype_alter(&$subtype, $plugin) {
if ($plugin['name'] == 'fieldable_panels_pane' && $subtype['bundle'] == 'something_cool') {
$subtype['preview settings'] = array(
'title' => 'An awesome title',
'field_some_text' => array(
LANGUAGE_NONE => array(
0 => array(
'value' => 'Example text!',
),
),
),
);
}
}
A completely custom preview callback for an FPP with the bundle "dynamic widget":
/**
* Implements hook_ctools_content_subtype_alter().
*/
function MYMODULE_ctools_content_subtype_alter(&$subtype, $plugin) {
if ($plugin['name'] == 'fieldable_panels_pane' && $subtype['bundle'] == 'dynamic_widget') {
$subtype['preview callback'] = 'MYMODULE_dynamic_widget_preview_callback';
}
}
/**
* Preview callback.
*/
function MYMODULE_dynamic_widget_preview_callback($type, $subtype, $plugin, $renderer) {
// Return a string or render array!
return "Here's the preview HTML";
}
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | interdiff-12-20.txt | 550 bytes | cboyden |
| #20 | panopoly_magic-custom-preview-2484843-20.patch | 9.02 KB | cboyden |
| #12 | interdiff.txt | 1017 bytes | dsnopek |
| #12 | panopoly_magic-custom-preview-2484843-12.patch | 9.01 KB | dsnopek |
Comments
Comment #1
dsnopekThinking about this again after so long!
One thing that the issue description doesn't seem to consider is that we show live previews in two situations: when selecting a widget on the "Add content" dialog, and when configuring a widget. The issue summary seems to be talking about using them for both situations, which I could see. But I could also see an argument for using a static image for just selecting the widget on the "Add content" dialog, but rendering a real live preview when configuring the widget.
Should this be configurable on a per widget basis (ie. two different keys on the CTools content type)? Or, a global setting that affects all widgets? Or should we just pick one way?
Also, for FPPs, it's probably most sustainable to created and render (but not save) an actually FPP in code than use an image. Should there be special support for that? I could see a special configuration just for FPPs that is the field values for the preview to be shown in the "Add content" dialog, which would be much simpler to manage than having to create a new static image every time a widget's appearance changes.
Comment #2
dsnopekUpdating the title and issue description to only address the previews in the "Add content" dialog. And, actually, those aren't really previews, let alone live previews (even though that's what they are called in code where it's all tangled up) but actually "examples" of the widget, so, I've switched the terminology to talk about examples.
Hopefully, this'll make things easier in the future too and we can talk about "previews" (where we show a preview of the values the user entered) and "examples" (where we show an example of the widget so users have an idea before they even pick it).
I also removed the idea about the hook, because that just creates too many branches. If a module wants to make an example for another modules widget, it can alter the other module's plugin definitions.
And, I added the idea of an 'example settings' so that widgets can simply give some example settings, which can overloaded for entity values in FPPs. This settles on the idea that we'll have two defaults for 'example callback': one for normal panes, and one for FPPs so they can handle their cases specially.
We can make a new issue later for allowing widgets to control their live preview, but that seems like a less common use case.
Comment #3
dsnopekGah, started trying to make this work in code but the Panopoly code has too many things connected with this called 'preview' that I don't want to change for BC reasons, for example: the constants (
PANOPOLY_ADD_PREVIEW_SINGLE, etc) and the theme functions ('panopoly_magic_preview','panopoly_magic_preview_link', etc). We can't change those without breaking contrib, but I don't want a mix of things called 'example' and 'preview'. Blergh. No extra clarity for us on this issue :-(I updated the issue summary to revert back to the 'preview' terminology
Comment #4
dsnopekComment #5
dsnopekAdd some code examples of how to actually use this
Comment #6
dsnopekAdded an example for a custom preview callback to the issue summary
Comment #7
dsnopekAnd here's a patch! This works in my limited testing, but it'll need to run through the tests and get a bunch more manual testing.
EDIT: Here's a Travis build: https://travis-ci.org/panopoly/panopoly/builds/312081483
Comment #8
dsnopekHere's a new patch based on a bug that @cboyden found! Before this patch, we show "No Preview" for new FPPs, and with this patch we were showing a rendered version of the FPP, but with no data in it, which led to some really, really weird results. This patch will simply return no preview if the FPP has no 'preview settings' and maintain the old behavior. If we want to come up with a way to allow previews of new FPPs (maybe fill in the fields with their default values, and "lorem ipsum" for required values?) we can do that on a separate issue.
EDIT: Here's a Travis build for the new patch https://travis-ci.org/panopoly/panopoly/builds/317094403
Comment #9
dsnopekAnother cool idea that came out of @cboyden's testing: this patch sets a property on the FPP so you can tell it's being rendered in the preview. For example, if you want to check in the FPP template, you could do:
This way you could render a little differently if necessary
EDIT: Here's the Travis build: https://travis-ci.org/panopoly/panopoly/builds/317105066
Comment #10
cboyden commentedThis is looking great so far. I'm using the 'preview image' and 'preview settings' implementations; I haven't tested the 'preview callback' nor the new FPP property that allows it to know whether it's being rendered as a preview.
Comment #11
cboyden commentedWhen a custom preview using image or settings is in place, reusable FPPs show the generic custom preview instead of what they actually look like. This will make it harder to distinguish different reusable widgets.
I don't see a way in the alter callback to pass in any information about whether a preview is of a reusable widget or not. Should this be done in the implementation of the hook, or in panopoly_magic?
Comment #12
dsnopekHere's a new patch that adds a special case for reusable FPPs. Basically, if a reusable FPP is being previewed, it'll ignore the 'preview callback' and 'preview image' and force the 'preview callback' to be the default FPP one. Because I could see a case where someone had a widget where they want to always use their custom preview, you can set a special key, 'preview always', to a truthy value to prevent this.
I agonized over how to integrate this into the logic in a way that didn't make it convoluted - I'm pretty happy with that part. This keeps the same sort of "cascading" behavior where we look at the callback, the image, and then one of two defaults - it just adds a special case before any of that happens.
I also struggled with what to name the new key to disable this special case. I'm not super happy with what I came up with ('preview always') but I wanted it to be short and similar to the other two keys ('preview callback' and 'preview image'). Other ideas I had just ended up being too long or too weird, ex: 'preview reusable fpp always', 'disable reusable fpp preview default', etc. If anyone has any suggestions, I'd love to hear them! However, in the end, that is probably a rare edge case, so odds are few people will ever need to care about it.
Comment #13
cboyden commentedThanks, that's fixed it. I've tested the preview image, preview settings, and reusable widgets preview aspects of the latest patch, and they're all working great.
Comment #14
dsnopekI think I'm going to commit this. It shouldn't cause any regressions - the default behavior shouldn't change. There could be more bugs, but those will only affect people using the new feature.
Comment #16
dsnopekCommited!
Comment #17
cboyden commentedWe've begun testing on PHP 7 and ran into an error using this patch.
The function
fieldable_panels_pane_view()expects a third argument,$langcode.Comment #18
cboyden commentedIt seems like the function in fieldable_panels_panes should allow that argument to be NULL, as it does for the function it's essentially duplicating:
but in the meantime I've attached a patch that adds the langcode argument.
Comment #19
dsnopekI don't know that this is right. The
$langcodeargument is the language to render in, and the$fpp->languageis the language the FPP is saved in. It'd be better, I think, to explicitly passNULLbecause that will use the current language of the request for rendering.That said, I don't have a lot of experience with FPP's and multi-lingual, and haven't tested this in any way.
Comment #20
cboyden commentedThanks - updated patch and interdiff are attached.
Comment #22
dsnopekThanks! Committed :)
Comment #23
cboyden commented