-
-
Notifications
You must be signed in to change notification settings - Fork 235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add NotifierManager::insertContent for easy content insertion. #6064
base: master
Are you sure you want to change the base?
Add NotifierManager::insertContent for easy content insertion. #6064
Conversation
if (method_exists($obs['obs'], $snake_case_method)) { | ||
$obs['obs']->{$snake_case_method}($this, $eventID, $params); | ||
} else { | ||
// If no update handler method exists then trigger an error so the problem is logged |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This else
clause might be skippable, since it's guarding against older/legacy observer syntax support.
So if what's being built here won't ever need that legacy support, maybe the clause isn't needed.
Not that it's harmful to keep either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought this was still useful. If an observer registers interest in CONTENT_FOO
and then does not declare a handler function, that's a bug that needs raising. Note that if the contentDefinePageMap
contains a mapping, the loop is continue
d and the error would not be raised, as intended.
Now I think about it, my 'sniff for define page' logic is poor implementation of the design edit removed some rubbish I wrote here :)
FYI I have done a second take of development on a new branch, https://github.com/neekfenwick/zencart/tree/content-notifiers-take-two-issue-6053 as discussed in #6053 (comment) using suggestions from @lat9 to keep the new content notifier separate from NotifierManager, by instantiating a second global $zcc_notifier
, similar to $zco_notifier
. This work was a little while ago and if it's actually more sensible to keep everything under one hood in NotifierManager I can back out that branch and stick to the original.
I like the simplicity of the one-line call to import/inject other content into certain places in a template. NomenclatureThere are lots of ways to "name" this. I've often referred to this sort of approach as using "render hooks", depending on what needs to be "rendered". Define Pages as content sourceRelying on Define Pages as the content source has its pros and cons. prosThe main "pro" is that everyone's always begging for Admin-controlled editing of dynamic page content. And the merits you mentioned tie in to that and support why you've gone that route. I support it, although I've always disliked Define Pages (although it's served a purpose). consThe "cons" include increasing the demand to optimize the Define Pages list in the Admin, mainly to make it easier to navigate through dozens of pages that will make that list grow if people really embrace this. Not hard to do, just hasn't been a big demand yet. Using content observer for realtime context-specific data enhancementYour approach offers some degree of real-time dynamic content modification. One of your examples proposes altering the displayed order-status-name based on some criteria. That's gotta be done in the observer class, which is actually better than the past/current approach of having someone alter their template file to manipulate the data "there" before it's displayed: locating that logic in the observer class to be handled when that insertContent hook is called, is less "obvious" to the coder who just wants to tamper with the data procedurally, but keeps the templates cleaner, easier to upgrade without losing the changes to custom logic, etc etc etc. Define Pages vs PHP codeWhile Define Pages "can" execute PHP code that's in proper Your proposal isn't geared so much around these Define Pages "processing" any PHP, but more around using them to "include content" at various "hooks" in the flow of outputting content to the browser. Design ChoicesI'm okay with the choices you've proposed, ie:
suggested changesNitpicky suggestions:
|
Question: Perhaps the I dunno that it would be necessary to support passing a Closure (numerous complexities to that, but doable), but even passing a function call as a parameter to Possible use-cases that come to mind include drop-shipping-provider status details, tracking number details, account-feature details like rewards-balances, or bad-address-format alerts. |
Coming up with all the ideal places to build-in these render-hook calls to Another benefit of defining (and documenting) a list of built-in hooks is that it can make adaptation to different display-sizes quite a bit easier ... provided those hooks are placed in spots that are tested to render well under those conditions. As you mentioned also, there may be value in encapsulating some of these calls to insertContent inside new |
Many thanks for the review, hugely appreciated.
Yes, see my Example in the OP, I have a handler
This is the intent of the lines in
Agreed. Perhaps my
Certainly daunting. I haven't been involved in the similar task of sprinkling current notifier hooks around, a similar problem. Would want to exhaust discussion of use cases to get the API right before diving in. Your mention of closures as arguments is interesting, I don't understand your point well. Can you elaborate an example?
I could not decide on this. Often, wrapping content in a |
Just my 3.9 cents worth. |
Many will be tempted to include their own DIV wrapper in whatever they generate to send to the renderhook, so that they can style their bespoke block themselves. So, my point about wrapping content in In shorter words: do we use: <?php renderHook('foo'); ?> or <div class="renderHook rhCreateAccount">
<?php renderHook('foo'); ?>
</div> Obviously, whatever code calls the renderHook needs to be mindful that it doesn't inject invalid HTML or content blocks that break the layout. But that's on them. |
I have a client whose define pages use substitute tags (%%SOMETHING%%) so his code can be syndicated - I'll add on whatever is required to make that all work once this is merged. Where do we stand with this PR? |
Not sure what you mean by 'syndicated', that means to publish or broadcast, right? On this PR, my approach was to use function calls in the same vein as the On my main client's site, I actually use the Smarty templating system to pre-process many of my define pages so I can use substitutions and also more advanced things like custom PHP functions in the templates, which I like but as was mentioned in the email system overhaul (also on hiatus) Smarty is not necessarily the best for everyone, I ended up using the Laravel Blade engine there. Maybe that would be an option here too? You don't specify how the
I got stuck on the discussion at #6053 when the different approaches to modify NotifierManager, people stopped responding to my attempts, see latest comment on Dec 4. Both implementations work, it's a question of what is likely to be merged. I didn't want to continue spending my limited time experimenting with so little feedback. I know we're probably all volunteers so not trying to sling mud. |
One of the observers would add string substitution. The specifics aren't important, just that you have more flexibility than you do by simply pulling a file directly into the content, as is done currently.
He licenses it to other people who use different values for %%SOMETHING%%. Substitution is done dynamically based on database files so the stores can share the same fileset. |
Yeah, I like the idea of substitution. Using a more advanced templating engine gives a lot more flexibility. For example, I use Smarty to allow me to use custom functions in my define pages that look up the SEO friendly URL from the CEON URI Mappings module, so I don't have to hardcode URLs to our product pages. If we change the URL mapping in the CEON database table, our define pages all update automatically to use the new URL. Blade works by turning your text into PHP code and executing it, so you can use globals directly in the template, e.g. {{ STORE_NAME }}. You don't have to write your own PHP code to do the substitutions. But, of course you have to deal with invoking the Blade engine. On my email work I borrowed a Blade helper class that encapsulates that as much as possible. I could add this as a proof of concept on this branch if that sounds good to you. One sticking point I reached with the email work is that the Blade template can contain directives which are very unfriendly to a WYSIWYG HTML editor, for example having loops and conditional statements embedded in and around the HTML of the email content didn't render well in the CK Editor. Complex emails like the order confirmation, for example, have loops over products in the order. This would be a similar problem in define pages, but a much lesser problem in that their contents is usually very simple so you'd only have simple things like
Dealing with "licensing" it sounds a bit heavyweight :) I'd suggest we just build it into core ZC as code we write ourselves. I imagine having something like a |
@scottcwilson what did you think of my points about using a more advanced templating solution, instead of simple string substitutions? Is it worth me building a working proof of concept? I'm concerned that this, like my conversations on the email template overhaul, has gone quiet. I'm reluctant to put work into something that will ultimately be ignored and not merged. |
Would prefer that you keep it simple. My only intention in adding my comments was to show how your change might be used, not to suggest a roadmap. Most people who use this at all will just use the new feature you added and stop there. |
Ah, then I misunderstood your earlier comment about your buddy with code that could be syndicated. It sounded like you were eyeing his alternative implementation of the same feature with the intent to merge that. I can keep the initial implementation simple, with a notifier hook to allow modification of the define page content for e.g. processing through a substitution / templating solution of choice. I'm left with a couple of implementation options that have been raised before: 1/ Mentioned in #6053 (comment) it seems strange for NotifierManager to brute force hunt an array for matching observers, instead of having a data structure keyed by event ID that can be indexed into much more efficiently. I have re-written it on one of my branches, it doesn't change the API of NotifierManager, just means a more efficient internal operation. Does this sound terrible to anyone? 2/ Should the new 'CONTENT_' event ID's be stored in the same data structure in NotifierManager as all the current notifier events, or should we have a separate system for them? e.g. keep the existing |
@scottcwilson I tweaked things based on your comments, adding a If the CONTENT_* event results in a define page being found, the result is wrapped in a I haven't introduced any new class like |
@neekfenwick Can you squash this and fix the conflicts? I'd like to get this in 2.1.0. |
aaed03c
to
8e7daf2
Compare
I've rebased onto I imagine you guys will find things you want to tweak about it as we actually use the thing. I'm still interested in rewriting the core EventDto mechanism to remove the brute force array lookup, I'm pretty sure I wrote an implementation on a branch but can't find it now. There's only one template file updated that calls the new system, |
Fixes #6053 .
Needs Review
This PR is not finished, submitted for review as a concept. I include just a few example hooks in the Account History Info page. We would need to scatter
$zco_notifier->insertContent('CONTENT_FOO_BAR');
calls throughout the template files, similar to hownotify()
calls were.Improving NotifierManager Performance
I did some work on improving the array hunting done in NotifierManager, but it involves changing the core
EventDto::$observers
data structure which I think some addons rely on (I saw Watching Observers (https://www.zen-cart.com/showthread.php?226649-Watching-Observers-Support-Thread) for example). See https://github.com/neekfenwick/zencart/tree/content-notifiers-issue-6053This PR is a completely separate branch, without the EventDto etc modifications.
Description
This PR takes the idea of NotifierManager and Observers, and extends it for arbitrary content inclusion at key points on shop front pages. We are used to e.g.:
Observers may then register for the
NOTIFY_ACCOUNT_HISTORY_INFO_EXTRA_COLUMN_HEADING
event and be called byNotifierManager
when that event is fired. This PR introducesCONTENT_FOO_BAR
events with different behaviour.One line define page inclusion
The main aim is to allow templates to position define page content easily in a single line of code. For example modify
tpl_account_history_info_default.php
to have this line just under the<h1>
:Whether or not any Observers are registered, this will attempt to replace the 'CONTENT_' with 'DEFINE_', lowercase the result, and require a define page, e.g.
define_account_history_info_intro.php
, wrapped in a suitable div, e.g.:If a define page of that name is not found, no automatic output is made and no error is raised.
Event name to Define Page mapping
If an Observer wants to register for an event like
CONTENT_ACCOUNT_HISTORY_INFO_INTRO
but wants to use a differently named define page, e.g. one shipped with their addon, the Observer can declare a map instead of implementing a handler function, e.g.:You can equally handle the event in a custom handler function, and call the define page output function helper, but this is neater.
Custom handler functions
Similar to the existing Notify system, if a method is found matching the snakeCased event name, it is invoked. It may then use the define page output helper function, or directly output content, using data passed in.
Difference from Existing Notifiers
All this looks a lot like what we already had before, but is conceptually and functionally different. We can output HTML from a normal Notifier but this approach brings a couple of benefits:
$contentDefinePageMap
).Example
The system allows for Observers to register, too, i.e.
auto.content_notify_test.php:
NotifierManager
had some legacy cruft relating to what methods might be on the Observer class; theupdate()
function and different snake_cased method names. This PR simplifies all that and assumes the function will becontent_<snake_cased_eventID>()
. There is also no'*'
event registration possible.Note that I am using Parameter Lists
&...$params
in passing data to the observer, instead of&$param1, &$param2, etc
. The downside is that the handler methods no longer have named parameters, such as$order
,$some_data
etc. I could go either way on that.