Skip to content
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

Fixed event dispatching inconsistencies leading to incomplete API #4308

Closed
wants to merge 6 commits into from
Closed

Conversation

amazeika
Copy link
Contributor

Reason

Incomplete API. We spotted inconsistencies on some component models which are not properly triggering events as expected. This results in an incomplete and inconsistent API for developers. For example in our case we would like to be able to keep track of extension events, which is currently not possible.

Problem

Some model’s methods are being overridden without calling their parent method. By not calling the parent the event dispatching process is being bypassed.

Example

MenusModelItem::save does not trigger the before/after save events because it does not call its parent.

Patch

This patch includes the following fixes and enhancements:

  • It aims to solve the issue by properly taking care of the dispatching process on model actions.
  • It also improves JModelAdmin a bit by reducing the amount of required code in some cases, e.g. extending JModelAdmin and wanting to trigger events on plugins other than content.
  • Additionally event support was also added to com_config component save actions.

The API

Extreme care has been taken in order to provide full backwards compatibility, meaning that no API change has been introduced in this patch. However, we would like to report some anomalies that are a direct consequence of the previous inheritance approach being used.

Delete and state change actions from PluginsModelPlugin are inherited from JModelAdmin. This means that until now, the events being triggered for these actions are onContent(Before/After)Delete and onContentChangeState respectively.

On the other hand, onExtension(Before/After)Save events are triggered on PluginsModelPlugin::save calls. This introduces inconsistencies in the API.

One possible approach to fix this would be to use extension events for the delete and change state actions, i.e. onExtension(Before/After)Delete and onExtensionChangeState respectively.

However, and in order to keep things clean while ensuring backward compatibility, we decided not to introduce these changes, but to report them.

This same behavior can be observed on ModulesModelModule and LanguagesLanguage.

onExtension(After/Before)Delete events have been introduced on ModulesModelModule::delete and TemplatesModelStyle::delete calls. Since no events were previously triggered on these calls, triggering these new events does not introduce an API change which might lead to backward compatibility problems.

Tests

In order to properly test this patch, we have developed a small package for catching events. We have called it Catcher.

The package includes a library and set of configurable plugins. These plugins catch events and reports them (optionally with event data) to the UI using the Joomla! messaging queue.

The Catcher package may be found in the following repo:

https://github.com/nooku/joomla-catcher

The package may be installed in using composer or by building a package.

Composer install

This is the easiest way to install the Catcher. The first step is to download composer on your Joomla! site root path. Then create a new composer.json file (also in the root directory of the site) with the following content:

{
    "require": {
        "nooku/pkg_catcher": "1.*"
    }
}

The next and last step is to run the following command from your terminal while located in the site root directory:

php composer.phar install

Package install

For convenience, a phing script for building the package is also available under the /scripts/build folder of the repo.

After the package is installed, make sure to enable the plugins and configure them to catch and report events.

Thanks in advance for considering this patch.

The Timble team.
http://www.timble.net

@Bakual
Copy link
Contributor

Bakual commented Sep 19, 2014

This looks great, thanks for that work!
Let's get that tested 😄

@wilsonge
Copy link
Contributor

What @Bakual said. Something I've been meaning to do for ages! Thank you very much!

@@ -173,12 +176,17 @@ public function save($data)
throw new RuntimeException($table->getError());
}

// Store the data.
if (!$table->store())
$result = $dispatcher->trigger('onExtensionBeforeSave', array($context, &$table, false));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't read the whole changelog yet, but shouldn't we use the events_map array here?

@amazeika
Copy link
Contributor Author

@Bakual, @wilsonge You are welcome!.

@Hackwar Hi!, thank you for taking the time to review the proposed changes.

The events_map array is used to map events to plugin groups, and this so that the model knows which plugin group to load before triggering these events. Let's remember that in Joomla!, events are tied to plugin groups, and that in a given model ... we may need to load different plugin groups for different actions. The events_map array allows for this.

If you check JModelAdmin, you will see that all of the events are overridable in the constructor, and this was already the case before before the PR. This PR aims to have a minimal impact on the current existing codebase while providing solutions to the problems mentioned above.

There are some models where the events on the trigger calls are hardcoded, and this because those models are not extending from JModelAdmin. Most of them extend from JModelForm. These are not re-usable models but implementations, so no need to make those overridable unless the extra flexibility might get handy. ConfigModelComponent and MenusModelMenu are examples.

Does this help?.

@Hackwar
Copy link
Member

Hackwar commented Sep 29, 2014

@amazeika Yes, you are right. I read the code wrong initially. I just stumbled over this, since I want us to have several more events and make those configurable. In any case, its fine the way it is.

@amazeika
Copy link
Contributor Author

Hi @Hackwar!. Okay, good. Thank you again for taking the time to check this out.

@nicksavov
Copy link
Contributor

Looks like there are some potential merge conflicts. @amazeika could you update to the latest staging? This could be tested during PBF by some developers.

@amazeika
Copy link
Contributor Author

@nicksavov Hi!, I've just rebased the PR changes on top of the latest staging.

However, Travis isn't liking this anymore. Something to do with your unit checks?.

@wilsonge
Copy link
Contributor

Hi yes, It's our (@joomla 's) fault the tests are failing here. If you merge in/rebase on staging again should be fixed :) sorry!

@amazeika
Copy link
Contributor Author

@wilsonge Thank you George!. Not a problem. Let's see how it goes now :).

@haydenyoung
Copy link

Testing the specified Models (Template Style, Module, Menu) firing inherited triggers with patch.

Menu Add

Before

onContentBeforeDelete triggered in com_menus.item context
onContentAfterDelete triggered in com_menus.item context
onContentPrepareForm has been triggered

After

onContentPrepareData triggered in com_menus.item context
onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_menus.item context
The item is NEW
onContentAfterSave triggered in com_menus.item context
The item is NEW
onContentPrepareForm has been triggered

Template Delete

Before

Nothing triggered

After

onContentBeforeDelete triggered in com_templates.style context
onContentAfterDelete triggered in com_templates.style context

Module Delete

Before

Nothing triggered

After

onExtensionBeforeDelete triggered in com_modules.module context
onExtensionAfterDelete triggered in com_modules.module context

Also tested Content, WebLinks, Users, Groups with same triggers being fired before and after patch applied (appears to be correct triggers being fired in both situations).

@amazeika please let me know if there are any other inherited triggers that require testing.

This comment was created with the J!Tracker Application at issues.joomla.org/joomla-cms/4308.

@RCheesley
Copy link

Tried to test this, got as far as building the extension but couldn't figure out what I was meant to do thereafter. Thought the file attached might speed the process up for some who might not have or know how to using phing/composer.

https://in.kato.im/fef531c4c39e133b37f42f4615ffce8f1e39435148bba0be7448f3e71b8f546b/pkg_catcher.tar.gz

Ruth

This comment was created with the J!Tracker Application at issues.joomla.org/joomla-cms/4308.

@anibalsanchez
Copy link
Contributor

@test OK

  • Installed Ruth's Catcher (thank you!) and enabled CRUD events for Content, Extension, and User

Content - Creation

onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_content.article context
The item is NEW
onContentPrepareData triggered in com_content.article context
onContentPrepareForm has been triggered

Content - Retrieval

onContentPrepareData triggered in com_content.article context
onContentPrepareForm has been triggered

Content - Modification

onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_content_article context
The item is NOT new
onContentAfterSave triggered in com_content_article context
The item is NOT new
onContentPrepareForm has been triggered

Content - Removal

onContentStateChange triggered in com_content.article context
onContentPrepareForm has been triggered

  • Empty Trash

onContentBeforeDelete triggered in com_content.article context
onContentAfterDelete triggered in com_content.article context
onContentPrepareForm has been triggered

Extension - Creation

onExtensionAfterInstall has been triggered

Extension - Retrieval

onContentPrepareData triggered in com_modules.module context
onContentPrepareForm has been triggered

Extension - Modification

onContentPrepareForm has been triggered
onExtensionBeforeSave triggered in com_modules.module context
The item is NOT new
onExtensionAfterSave triggered in com_modules.module context
The item is NOT new

Extension - Removal

onContentStateChange triggered in com_modules.module context

  • Empty Trash

onExtensionBeforeDelete triggered in com_modules.module context
onExtensionAfterDelete triggered in com_modules.module context

User - Creation

onUserBeforeSave has been triggered
The item is NEW
onUserAfterSave has been triggered
The item is NEW
onContentPrepareForm has been triggered

User - Retrieval

onContentPrepareData triggered in com_users.user context
onContentPrepareData triggered in com_users.profile context
onContentPrepareForm has been triggered
onContentPrepareData triggered in com_users.user context

User - Modification

onUserBeforeSave has been triggered
The item is NOT new
onUserAfterSave has been triggered
The item is NOT new
onContentPrepareForm has been triggered

User - Removal

onUserBeforeDelete has been triggered
onUserAfterDelete has been triggered
onContentPrepareForm has been triggered


This comment was created with the J!Tracker Application at issues.joomla.org/joomla-cms/4308.

@amazeika
Copy link
Contributor Author

@haydenyoung Thanks for testing. Your test results look good.

Please also make sure that events on component config save actions are being triggered. This is something new.

Template style duplication (duplicate action) is something that wasn't triggering events. Now save events should trigger when duplicating styles.

Menus save and delete actions were not triggering events either. This should be tested as well.

@RCheesley These actions were not triggering events:

Menu item: add, edit
Menu: add, edit, delete
Module: delete
Template style: add (duplicate), delete

This is a good start for testing purposes. The PR includes changes deep in the base admin model so ideally each component making use of it should be tested. Events triggered by all the models that got changed in this PR must be tested.

Thank you for providing a catcher package.

@ALL Thank you all for taking some time to test this :).

@aDaneInSpain
Copy link
Contributor

Here are my test results:

Content

Content Creation

onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_content_article context
The item is NEW
onContentAfterSave triggered in com_content_article context
The item is NEW
onContentPrepareForm has been triggered

Content Retrieval

onContentPrepareData triggered in com_content.article context
onContentPrepareForm has been triggered

Content modification

onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_content_article context
The item is NOT new
onContentAfterSave triggered in com_content_article context
The item is NOT new
onContentPrepareData triggered in com_content.article context
onContentPrepareForm has been triggered

Content Removal

onContentStateChange triggered in com_content.article context
onContentPrepareForm has been triggered

Emptying Trash

onContentStateChange triggered in com_content.article context
onContentPrepareForm has been triggered

At this point I will assume that the rest of the already passed tests will still pass, and instead test the missing areas requested by @amazeika

Component Config

Saving config

onContentPrepareForm has been triggered
onExtensionBeforeSave triggered in com_config.component context
The item is NOT new
onExtensionAfterSave triggered in com_config.component context
The item is NOT new
onContentPrepareForm has been triggered

Template style

Template style duplication

onContentBeforeSave triggered in com_templates.style context
The item is NEW
onContentAfterSave triggered in com_templates.style context
The item is NEW

Menu item

Menu item creation

onContentPrepareData triggered in com_menus.item context
onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_menus.item context
The item is NEW
onContentAfterSave triggered in com_menus.item context
The item is NEW
onContentPrepareForm has been triggered

Menu item update

onContentPrepareData triggered in com_menus.item context
onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_menus.item context
The item is NOT new
onContentAfterSave triggered in com_menus.item context
The item is NOT new
onContentPrepareData triggered in com_menus.item context
onContentPrepareForm has been triggered

Menu item deletion

onContentStateChange triggered in com_menus.item context
onContentPrepareForm has been triggered

Emptying menu item trash

onContentBeforeDelete triggered in com_menus.item context
onContentAfterDelete triggered in com_menus.item context
onContentPrepareForm has been triggered

Menu

Menu save

onContentPrepareData triggered in com_menus.menu context
onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_menus.menu context
The item is NEW
onContentAfterSave triggered in com_menus.menu context
The item is NEW

Menu edit save

onContentPrepareData triggered in com_menus.menu context
onContentPrepareForm has been triggered
onContentBeforeSave triggered in com_menus.menu context
The item is NOT new
onContentAfterSave triggered in com_menus.menu context
The item is NOT new
onContentPrepareData triggered in com_menus.menu context
onContentPrepareForm has been triggered

Menu delete

onContentBeforeDelete triggered in com_menus.menu context
onContentAfterDelete triggered in com_menus.menu context

Module

Delete

onContentStateChange triggered in com_modules.module context

Empty trash

onExtensionBeforeDelete triggered in com_modules.module context
onExtensionAfterDelete triggered in com_modules.module context

DONE!

@nicksavov
Copy link
Contributor

Great testing!

@amazeika, looks like there are some merge conflicts with the latest staging. Could you update the PR again? Thanks in advance!

@yireo
Copy link

yireo commented Nov 8, 2014

Another long long test result:

Menus

Menu Browse:

Before:

  • No event

After:

  • No event

Menu Read

Before:

  • onContentPrepareForm triggered in com_menus.menu

After:

  • onContentPrepareData triggered in com_menus.menu
  • onContentPrepareForm triggered in com_menus.menu

Menu Create

Before:

  • No event

After:

  • onContentBeforeSave triggered in com_menus.menu
  • onContentAfterSave triggered in com_menus.menu

Menu Update

Before:

  • No event

After:

  • onContentBeforeSave triggered in com_menus.menu
  • onContentAfterSave triggered in com_menus.menu

Menu Delete

Before:

  • No event

After:

  • onContentBeforeDelete triggered in com_menus.menu
  • onContentAfterDelete triggered in com_menus.menu

Menu-Items

Menu-Item Browse:

Before:

  • onContentPrepareForm triggered in com_menus.items.filter

After:

  • onContentPrepareForm triggered in com_menus.items.filter

Menu-Item Read

Before:

  • onContentPrepareForm triggered in com_menus.item
  • onContentPrepareForm triggered in com_menus.items.filter

Menu-Item Create

Before:

  • No event

After:

  • onContentBeforeSave triggered in com_menus.item

Menu-Item Update

Before:

  • No event

After:

  • onContentBeforeSave triggered in com_menus.item

Menu-Item Delete

Before:

  • onContentChangeState triggered in com_menus.item

After:

  • onContentChangeState triggered in com_menus.item

Template Styles

Template Style Browse:

Before:

  • No event

After:

  • No event

Template Style Read

Before:

  • onContentPrepareForm triggered in com_templates.style

After:

  • onContentPrepareForm triggered in com_templates.style
  • onContentPrepareData triggered in com_templates.style

Template Style Create

Before:

  • onExtensionBeforeSave triggered in com_templates.style
  • onExtensionAfterSave triggered in com_templates.style

After:

  • onExtensionBeforeSave triggered in com_templates.style
  • onExtensionAfterSave triggered in com_templates.style

Template Style Update

Before:

  • onExtensionBeforeSave triggered in com_templates.style
  • onExtensionAfterSave triggered in com_templates.style

After:

  • onExtensionBeforeSave triggered in com_templates.style
  • onExtensionAfterSave triggered in com_templates.style

Template Style Delete

Before:

  • No event

After:

  • onContentBeforeDelete triggered in com_templates.style
  • onContentAfterDelete triggered in com_templates.style

Modules

Module Browse:

Before:

  • No event

After:

  • No event

Module Read

Before:

  • onContentPrepareData triggered in com_modules.module
  • onContentPrepareForm triggered in com_modules.module

After:

  • onContentPrepareData triggered in com_modules.module
  • onContentPrepareForm triggered in com_modules.module

Module Create

Before:

  • onContentPrepareForm triggered in com_modules.module
  • onExtensionBeforeSave triggered in com_modules.module
  • onExtensionAfterSave triggered in com_modules.module

After:

  • onContentPrepareForm triggered in com_modules.module
  • onExtensionBeforeSave triggered in com_modules.module
  • onExtensionAfterSave triggered in com_modules.module

Module Update

Before:

  • onContentPrepareForm triggered in com_modules.module
  • onExtensionBeforeSave triggered in com_modules.module
  • onExtensionAfterSave triggered in com_modules.module

After:

  • onContentPrepareForm triggered in com_modules.module
  • onExtensionBeforeSave triggered in com_modules.module
  • onExtensionAfterSave triggered in com_modules.module

Module Delete

Before:

  • onContentChangeState triggered in com_modules.module

After:

  • onContentChangeState triggered in com_modules.module

Articles / Content

Content Browse

Before:

  • onContentPrepareForm triggered in com_content.articles.filter

After:

  • onContentPrepareForm triggered in com_content.articles.filter

Content Read

Before:

  • onContentPrepareForm triggered in com_content.article
  • onContentPrepareData triggered in com_content.article

After:

  • onContentPrepareForm triggered in com_content.article
  • onContentPrepareData triggered in com_content.article

Content Create

Before:

  • onContentBeforeSave triggered in com_content.article
  • onContentAfterSave triggered in com_content.article
  • onContentPrepare triggered in com_finder.indexer
  • onContentPrepare triggered in com_finder.indexer

After:

  • onContentBeforeSave triggered in com_content.article
  • onContentAfterSave triggered in com_content.article
  • onContentPrepare triggered in com_finder.indexer
  • onContentPrepare triggered in com_finder.indexer

Content Update

Before:

  • onContentBeforeSave triggered in com_content.article
  • onContentAfterSave triggered in com_content.article

After:

  • onContentBeforeSave triggered in com_content.article
  • onContentAfterSave triggered in com_content.article

Content Delete

Before:

  • onContentChangeState triggered in com_content.article
  • onContentPrepare triggered in com_finder.indexer

After:

  • onContentChangeState triggered in com_content.article
  • onContentPrepare triggered in com_finder.indexer

Users

User Browse

Before:

  • onContentPrepareForm triggered in com_users.users.default.filter

After:

  • onContentPrepareForm triggered in com_users.users.default.filter

User Read

Before:

  • onContentPrepareData triggered in com_users.profile
  • onContentPrepareForm triggered in com_users.user
  • onContentPrepareData triggered in com_users.user

After:

  • onContentPrepareData triggered in com_users.profile
  • onContentPrepareForm triggered in com_users.user
  • onContentPrepareData triggered in com_users.user

User Create

Before:

  • onUserBeforeSave triggered in onUserBeforeSave
  • onUserAfterSave triggered in onUserAfterSave

After:

  • onUserBeforeSave triggered in onUserBeforeSave
  • onUserAfterSave triggered in onUserAfterSave

User Update

Before:

  • onUserBeforeSave triggered in onUserBeforeSave
  • onUserAfterSave triggered in onUserAfterSave

After:

  • onUserBeforeSave triggered in onUserBeforeSave
  • onUserAfterSave triggered in onUserAfterSave

User Delete

Before:

  • onUserBeforeDelete triggered in onUserBeforeDelete
  • onUserAfterDelete triggered in onUserAfterDelete

After:

  • onUserBeforeDelete triggered in onUserBeforeDelete
  • onUserAfterDelete triggered in onUserAfterDelete

@yireo
Copy link

yireo commented Nov 8, 2014

@aDaneInSpain and I have been testing this all night. Code is good already. However testing is a must. @anibalsanchez tests are also good. I think this is just ready for commit ....

@brianteeman
Copy link
Contributor

thanks for testing guys setting RTC

This comment was created with the J!Tracker Application at issues.joomla.org/joomla-cms/4308.

@Bakual Bakual closed this in 1b1ad6e Nov 8, 2014
@Bakual
Copy link
Contributor

Bakual commented Nov 8, 2014

Merged, thanks!

The conflict was a small codestyle issue. Resolved that myself.

@Bakual Bakual added this to the Joomla! 3.4.0 milestone Nov 8, 2014
@nicksavov
Copy link
Contributor

Congrats guys! :)

@phproberto
Copy link
Contributor

This broke groups management. Please check #5152

Bakual pushed a commit that referenced this pull request Nov 21, 2014
[fix] Broken groups management by #4308
@phproberto
Copy link
Contributor

Another issue caused by this PR. Plugins broken. Please check #5202

amazeika added a commit to amazeika/joomla-cms that referenced this pull request Nov 25, 2014
The typo resulted on malformed contexts on save action events.
Bakual pushed a commit that referenced this pull request Nov 25, 2014
Fixed typo introduced on PR #4308
@amazeika
Copy link
Contributor Author

@phproberto Thank you for noticing the issues exposed above.

The first problem was related to a bad merge, either while rebasing to resolve conflicts or when the PR got merged. I was able to confirm this on a local clone. The double else statement wasn't there. In any case thank you for the fix.

Regarding the second issue, we've just submitted a PR for fixing the TYPO that got mistakenly introduced. This wasn't intentional. Thank you again for reporting it.

@wilsonge
Copy link
Contributor

@amazeika ThankYOU for submitting this in the first place. Every major PR like this has small bugs or something that creep in but you went through and fixed everything :) So thanks :)

@amazeika
Copy link
Contributor Author

@wilsonge Thank you George ... I appreciate your comment :).

pmorrisarctg added a commit to ArcTechnologyGroup/joomla-cms that referenced this pull request Nov 26, 2014
staging

# By Michael Babker (143) and others
# Via infograf768 (131) and others
* 'staging' of https://github.com/joomla/joomla-cms: (1128 commits)
  Fixed typo introduced on PR joomla#4308
  Fixed missing space
  Correcting comments
  [fix joomla#5173] JRouterInstallation::build() strict standards
  trim the author and created_by_alias before saving
  Remove all \r\n
  Update en-GB.plg_system_debug.ini
  JRouterInstallation::parse() made compatible with the interface (Fix
joomla#5173)
  Clean up non-printable characters.
  Fixed creationDate
  Fixes Author and Copyright info for Editor - None
  Routing: Implementing an interface for component router rules (Fix
joomla#4849)
  Routing: Adding application and menu objects to component routers (Fix
joomla#4848)
  Fixing JGithub repository contents api (Fix joomla#5067)
  [bug] Protostar template doesn't work with component links in SEF.
Fixes joomla#5085
  Fix undefined variable in Article Model
  ... use the right variable, dummy.
  fix the codemirror display bug, happens when codemirror is initialized
in a hidden element.
  Use the created_by_alias in the author meta tag. This is consistent
with how author name is show elsewhere.
  [fix] Broken groups management by joomla#4308
  ...

Conflicts:
	installation/sql/sqlazure/sample_blog.sql
	installation/sql/sqlazure/sample_testing.sql
@johanjanssens
Copy link
Contributor

Issue joomlatools#3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.