In Panelizer, we have a need to extend the IPE by having the save button give the user additional options: "Save as custom" and "Save as default" -- and to add a "Revert to default" button when the Panels display has been made custom.
Looking at the IPE's Javascript code, I'm not sure exactly how to do that, and I'm beginning to suspect that we'll need to change the code to make this possible.
For example, Drupal.behaviors.panels_ipe.attach() calls Drupal.panels_ipe.init() on the body element at startup. I have a feeling that we'll need other modules to be able to subscribe to an event or something, that allows them to take their own actions after Drupal.panels_ipe.init() or after Drupal.behaviors.panels_ipe.attach().
Also, I think most of the changes that I'd need to do for Panelizer would be adding new tabs to the tab_collection in Drupal.panels_ipe.init(). How can I do that? I'll also need to some of the data saved with the layout when we save, to tell it to "Save as custom" or "Save as default" - how can I replace or extend the layout model?
Maybe we need another object that's responsible for wiring up the whole Backbone app, and then an event to allow other modules to change that wiring, before actually kicking it off?
Another thing, is that we'll need for Panelizer to only mess with the IPE, when it knows that the IPE is editing a Panels display stored in Panelizer. It can figure this out via drupalSettings.panels_ipe.panels_display.storage_type, but ideally it'd be passed in to this magic event (rather than referenced globally) in case there is ever a future where the IPE can edit multiple Panels displays on the same page (which we couldn't do in D7, but it's very possible for multiple Panels displays to exist on the same page, so it'd be cool to support it one day).
Thoughts?
| Comment | File | Size | Author |
|---|---|---|---|
| #14 | interdiff.txt | 3.18 KB | japerry |
| #14 | panels-ipe-extend-api-2659566-12.patch | 18.65 KB | japerry |
Comments
Comment #2
samuel.mortensonI have two ideas around this, without delving deeply into the specific questions yet (but I will when I have more time to open issues/write up plans):
- Panelizer is going to push the limits of our Backbone implementation in IPE, and that's a good thing. For your examples/use cases, we should have globally accessible methods and events in the AppModel and AppView that allow for the most common contrib modifications - post render modifications, adding tabs, changing tabs views, etc.
- We should have an example module that implements this set of methods/events for a simple and practical use case. We can then use this example module in documentation and in future Javascript tests.
The code we're writing here is breaking a lot of new ground for Drupal, and while that's exciting we do need to make sure that we make it easy for new devs to jump in and start working with the IPE. In the end our API should be good enough that it feel better writing IPE integrations than it would be to write multiple preprocess hooks and chains of unstable jQuery selectors in a Drupal.behaviors implementation. I think we have the tools to make that happen.
Comment #3
samuel.mortensonYep, we should have a document-level event before the IPE is actually rendered, and then an event at the AppView level every time the IPE is re-rendered.
Tabs have two components - the TabModel, which contains metadata about the tab, and then the related Backbone View which Drupal.panels_ipe.TabsView is aware of. You can add TabModels without any issues right now, but can't interact with our Views globally. We can change that and even add a helper method like:
Drupal.panels_ipe.app_view.addTab = function (tab_model, backbone_view)To improve DX with a common task.
Your use case (modifying the Save tab) is a bit more complicated as the AppView is listening to the Save tab's TabModel change:active (click) event, but you could either remove the old tab and add a new one or we could rework that part of the App to support you. Save/Edit are the only tabs that have non-standard functionality.
I think a global (Drupal.panels_ipe.*) AppView and AppModel should serve this task just fine, users can directly interact with our events/methods without calling another container object.
Top two action items right now to unblock you are: 1) Make the AppView global 2) Trigger a global event when the IPE is rendered
Comment #4
dsnopekThanks for all the input!
Yeah, I was thinking about removing and adding a new tab (or actually, on Saturday I would have been happy with just adding a new tab that did something close-ish so I could start getting my mind around Backbone. :-))
Getting this working is really the main remaining thing for getting the Panelizer release out.
Comment #5
samuel.mortensonHere's a patch that should get you going on Panelizer this week. I'm not putting the issue into review as there's likely more I want to do this week, but assume that what's provided now will still be around by the time we get an alpha out.
The patch adds:
Drupal.panels_ipe.app_viewvariable, which lets you interact with the IPE's Backbone View from other modules.hook_panels_ipe_panels_display_presave($panels_display, $layout_model), which should allow you to modify the Panels Display before save based on the data sent from the App. For your use case you would calllayout.set('some_flag', true)before callinglayout.save(), in your custom Backbone View.Here's an example of how you might disable the normal Save Tab functionality and add your own Backbone View (I add the BlockPicker() here, but it's just an example):
I plan to look at this more this week, as I said, and try to add some simple methods for adding/removing/editing tabs.
Comment #6
dsnopekAwesome, thanks! :-)
Comment #7
dsnopekHere's a patch which adds
hook_panels_build_alter()to Panels, which we need to actually add the new Javascript to the page when the IPE is being used. It also cleans out panels.api.php which had all out-dated D7 hooks in it.Comment #9
dsnopekThis should fix the tests!
Comment #10
phenaproximaWhy have so many hooks been removed? I don't think it presents a problem or anything (indeed, I didn't even know these hooks existed until I started reviewing this patch), but is this relevant to the patch or just random cleanup?
s/build/built
It'd be nice to say a few more words about $build here. Is the layout divvied up by region? Any other relevant stuff that developers should know?
&$build should be type hinted.
This is a bit of a nitpick, but it's preferable to keep a local reference to the app view (
Drupal.panels_ipe.app_view = var app_view = new Drupal.panels_ipe.AppView), so that the namespace can be changed in one place later, if need be. The code will also be a little more readable that way.It might be more future-proof to namespace these events. For example,
panels_ipe.initialized.Both parameters should be type hinted.
Comment #11
dsnopekThanks!
#10.1: None of the hooks in panels.api.php are current - they don't exist in D8. This patch adds a hook, so I it's relevant to this patch in that I need to put something in there, but the removal itself isn't really relevant. We could make a seperate issue that just to remove panels.api.php, but I took the easy way. :-) Whether or not this is acceptable will be up to the maintainers
#10.2: Fixed!
#10.3: Unfortunately, we can't say anything useful because it's essentially undefined. All the Panels layouts will put render arrays named like the region which contain the blocks, but the DS layouts (which we can use) work a totally different way and have the blocks in a flat structure and break them up into regions on rendering. And any custom layout could really do whatever they want. For our use here, that doesn't matter because we're just messing with
$build['#attached']['library'].#10.4: Fixed!
#10.5/6: I'm going to leave these to mortenson to respond to, since this part was his code.
#10.7: Fixed!
Comment #12
dsnopekAlright! For allowing the two saving modes needed by Panelizer ("Save as custom" and "Save as default") this patch works perfectly! I've got both frontend code (modifying the UI and getting all the interactions right) and the backend code (making all the necessary changes via hooks). So, I think we're good there. :-)
The last thing that Panelizer needs to do, is add a new button for "Revert to default". Unlike the saving buttons, this isn't saving any of the current state that's in the Backbone models, instead we need to call back to the server and get a complete replacement for the current layout and blocks.
How should we implement this? I think Panelizer should probably provide a new route that does the backend stuff and returns the data. But how do we wire that into the Backbone app?
Comment #13
samuel.mortensonI would copy the same logic that's used for the Cancel Tab and just add a new Tab to the tray. Luckily, this is one of the least complicated functions in the IPE as it does a full page refresh instead of AJAX'ing in the previous display configuration. When we get around to improving that, we can implement similar logic in Panelizer for the Revert Tab.
Comment #14
japerryI've committed #2636516: Allow creating new content blocks from the IPE which made it not possible to apply this patch anymore. When merging it in this is what I got, but not convinced this re-roll is exactly right...
Comment #15
dsnopekBranch tests are currently failing (a fix in #2663530: Update for move of page_manager UI's to page_manager_ui) which is why the tests for #14 aren't running. But, I tested your re-roll and everything is working for me!
Also, I just finished getting the "Revert to default" button working in Panelizer per @samuel.mortenson's suggestion in #13! No new changes were required to this patch.
So, as far as I'm concerned this patch is ready! Any final review? @japerry? @phenaproxima? Please RTBC at will. :-)
Comment #16
phenaproxima#10.5 and #10.6 are still unaddressed, but if I'm honest, they are nitpicks and can be fixed at a later time. This looks fine to me otherwise.
Comment #17
dsnopekI just pushed my Panelizer changes that work with this to the 8.x-3.x branch of Panelizer. So, if you want to experience how the Panelizer integration into Panels IPE works, then you can check that out! All that remains is committing this patch. :-)
Comment #19
japerryYup, lets make the rest in a follow up issue probably... for now, this is in!
Comment #21
dsnopek