A story about plugins, plugin views, and linking

Hi all

For several days I tried to build a plugin package, but I am hanging at certain points. It could be that I am completely misunderstanding the concepts as I am quite new to Neos. My gut however says that the plugin and plugin-views parts are missing a few key parts to really be usable in real-world cases. After a quick talk with Christian Müller, and a few responses of “I don’t really use that part of Neos”, I decided to write up a little story to share my point of view. Maybe it aids in future development of Neos, maybe not.

Quick overview

  • A group of editors manage certain event data on a thid-party system.
  • Data from said third-party system should be displayed in various forms in Neos, configurable through the editor
  • Data only comes from the third-party system, there is no data manipulation inside Neos

What would Hans do?

Let’s name him Hans. Hans sets up a page “Events” and puts a plugin node on it that acts as overview (list, filtering, etc) and detail view for events.

Hans knows that a lot of people coming to their location like hiking trips, that’s why they organize guided hikes around the location. This is why Hans puts up another page, “Activities / Hiking” and fills it with information about the area, and about their guided hikes. “What would a page visitor expect on this page?”, Hans thinks. That’s why he wants to put an events teaser on this hiking page which lists the next five guided hikes. The same steps are repeated for other activity pages. Of course, the teaser entries properly link to the main events page’s show action.

The developer’s point of view.

The “Plugin views” approach.

Coming from TYPO3 CMS, setting up a flow package was nothing special. I spun up an EventsController with a teaserAction(), indexAction(), and showAction(), injected the EventRepository, set up templates, and that’s it… except for the rocks that are standing in my way.

First, I was trying around, maybe started debugging at some point of trying around more for a few hours, until I found out that the plugin view editor was broken. Checking out neos/neos-ui on master resolved the problem. At least that’s a small stone that can be easily removed.

Using the f:link.action view helper works as expected and the URI is adapted to link to the events page containing the plugin. So far so good. Now let me add a few properties for the editor to configure their teasers (as in “desired category” to not display cooking events on the hikings page, and “record limit” to reduce the amount of events displayed). Which is where the next stone is: Where do I configure these? I can add them to the master plugin, but this is the wrong context, for mainly two reasons:

  1. As an Editor, I expect to configure my teaser where I insert it, not on another page
  2. It simply collides with having multiple teasers (hiking, biking, cooking, …)

Additionally, the master plugin won’t show all nodes containing a plugin-view to itself, but only the first one. Thus, turning around master-view and plugin-view can’t resolve this problem.

One idea was to update the plugin views node type with properties that are only shown when certain conditions are met. Reading the the note of “rather low-level and still experimental” lets me fear that I need to fix this thing sometime when an update is made. I still tried it inside a simple plugin, but nevertheless, that would not work.

tl;dr: Plugin views node type lack properties for the plugin view context, pluginViews part of a plugin node type inside the inspector only shows one “linked” plugin.

The “separate plugins” approach.

Well then, this should be solvable by simply creating two plugins; One for the teaser, another one for the index/show actions. I set up the teaser plugin, gave it the desired properties, adapted the repository query to match the expected output, and voilà.

Now let’s adapt the f:link.action params to link to another node (which I could either define through a property or fetch from e.g. a flow query and a bit of php similiar to the dynamic dropdowns do it from the plugin-view node type). Now I just need to link them together, simple task, isn’t it? … unfortunately, no, it isn’t. In this case, from my point of view, there were two possibilities for linking from A to B:

  1. Using f:link.action and giving it a target node similiar to pageUid from the known f:link.action that extbase provides. Except there is no option to give f:link.action anything like this.
  2. Using neos:link.node to link to the node and packing everything that the plugin expects into arguments. Except inside a plugin, passing a node:// URI to the node argument raises “node:// URI conversion requires a context to be passed”. I tried assigning the current node/ context to the view and play around with “baseNodeName” and “nodeVariableName” to pass the required context but without success.

tl;dr: f:link.action misses node argument, neos:link.node does not work inside plugins.

The “nodes” approach.

After talking with Stephan because of his own thread, who had the same problem as I had in the second approach above, he told me that he solved it by building an aspect he put in place with @Flow\Around("method(Neos\Flow\Mvc\Routing\UriBuilder->mergeArgumentsWithRequestArguments())") and walked the path of “what’s the target node” by foot.

He also told me that he would, for my case, use a node-based approach by storing events as nodes under the correct node (in my case: the main events page will have child nodes for each event that is synchronized).

It basically sounded good, but at some point, I felt like this is wrong. Also, I was not sure if there would be another stone in my way when I refactor anything, basically leaving me at the same percentage of “task accomplished” as I am now.

My thoughts

Personally, I think either the plugin-view concept needs to be expanded to be usable for cases that contain more complexity as the current state of it allows. Alternatively, linking across plugins would also solve a big problem.

Thanks for reading. Any ideas and hints appreciated!

While digging around, I solved the main problem of generating links inside plugin templates via neos:link.node by calling Neos\Neos\Service\LinkingService->convertUriToObject() inside the controller, giving it the node:// uri to the detail page node, and as second context argument, the node from the current request.

As far as I understand the problem: Giving neos:link.node a string URI leads to the view helper trying to fetch the base node as context. This, however, only works inside “fusion-aware contexts”, which is not given here. I guess this could be solved by falling back to the given request’s document node which should, to my understanding, provide the same context that is expected.