The goal of this module is to change the way you make websites. Gone are the days of static web pages and lackluster experiences. The Engagement module gives you the necessary tools to create a unique web experience for each user on the site.
By tracking user events, you are able to deliver new/different content to users in real-time based on there actions. Some examples: reward users for viewing a certain number of blog posts. Give users the 'Tech Junky' role if they read a lot of articles tagged with 'tech'. Send a user an email if they haven't viewed any articles in a week. Because all of the data is stored locally, creating your own, custom queries is very easy.
The possibilities are endless...
This module tracks user activity throughout the site.
By default, user registration/edits, and pageviews are tracked.
This module provides a powerful API to add custom events:
engagement_event_track($event_name, $event_value, $event_data);
This module can also track events with JavaScript.
Drupal.engagement.track(event_name, event_value, event_data, uid);
There are numerous ways to easily get event information for a given user.
To get how many times someone has viewed 'node/123', we can use:
$count = engagement_event_count('node_view', 'node/123', $uid = 24);
By using the Engagement module, your events can be sent to Google Analytics if you have the Google Analaytics module enabled.
Project Page:
http://drupal.org/sandbox/donutdan4114/1588634
Git Page:
http://drupal.org/project/1588634/git-instructions
| Comment | File | Size | Author |
|---|---|---|---|
| #3 | Coder Tough Love | 1 MB | andymantell |
Comments
Comment #1
donutdan4114 commentedComment #2
andymantell commentedHi Dan,
Sounds like quite an interesting module. I will give it a manual review in a moment but first off, there are a few (relatively minor) coding style issues.
> It seems like you´ve a misnamed branch/tag created in your repository, please have a look at the release naming conventions. Essentially, your dev branch should simply be called 7.x-1.x, not 7.x-1.x-dev as you have now. This is where you should do all development work. Your master branch should be empty, except for a README.txt saying something like "See major version branches".
> Documentation-wise, for a developer oriented module such as this it would be nice to see some more examples of how it could be used to track and react to custom events. Perhaps you could create an engagement_example sub module demonstrating it's use.
> An automated code review revealed a couple of very minor coding style issues. It should only be a couple of minutes to get a completely clean slate. You can view the results here:
http://ventral.org/pareview/httpgitdrupalorgsandboxdonutdan41141588634git
> I notice you have a testing menu callback. You could perhaps consider using Drupal's testing framework here to run automated tests.
> In your JavaScript, it might be nicer to attach your _event function to the Drupal object. e.g. Drupal.engagement.trackEvent or something similar. See line 98 of /misc/ajax.js for an example (
Drupal.ajax= function() { ... })> In engagement_page_alter() you are using $field as an array without declaring it. It's minor but it would be nicer to see
$field = array();before you use it.On the whole though, it's all looking fairly clean and tidy, good job! I'm going to install it now and have a bash at tracking some events.
Cheers,
Andy
Comment #3
andymantell commentedcoder and coder_tough_love are flagging a few more errors that ventral didn't pick up. I've attached them to this issue as an image to preserve the formatting, but you can easily view and retest them yourself by installing http://drupal.org/project/coder and http://drupal.org/project/coder_tough_love.
Most of it is stylistic again, but it raises a potential problem with the unfiltered $_POST variables.
Also, I'm curious to know your motivation for this constant as you only appear to use it once.
define('ENGAGEMENT_NODE_VIEW', 'node_view');Comment #4
andymantell commentedOk. Manual review now.
> It would be nice to have a page in the reporting section showing the tracked events, perhaps with some way of filtering it down, much like the "Recent log entries" reporting section.
> Could you move debug mode messages into a drupal_set_message() call, or perhaps watchdog() instead. Or perhaps test if the site has develinstalled and use dpm(). The reason I say this is that when I put engagement_event_track() inside my module's form submit function that is called via ajax, the var_dump() caused extra output which broke the ajax call. Switching it to dpm() allowed me to track the event without errors.
> Tracking my custom event was quite easy ultimately. In a form submit callback I tracked an event called postcode_check, and then elsewhere I tested that value and output something when it was greater than 5.
My main comment would be that since engagement_event_count() is generally meant to track the current user, it is slightly clumsy to *have* to pass the $uid every time. My function didn't have
global $user;in it, I had to declare it just to use this function. Perhaps if you reworked this function something like this:That way I could track the current user without having to pass it in manually, but it would still allow overrides if necessary.
> As a feature request, it might be nice to integrate with the Context module and provide a tracking action as well as a event count reaction. So for example, if my custom event had been triggered more than 10 times, I could use Context to display an extra block. I think Context would be a good partner for your module as it would open up your API to people who don't want to write code, at least on a basic level.
All in all, I think this module has quite a lot of potential. A few tweaks here and there to make the function calls a little friendlier to use (the $uid comments above) perhaps, and personally I would really like the Context integration although I would be happy to RTBC this without it.
Look forward to seeing how this one progresses.
Cheers,
Andy
Comment #5
donutdan4114 commentedAndy,
Thanks for taking the time to review this module, it has been very helpful.
I will make the changes you requested, fix the coding issues, and check out the reports integration within Drupal.
I will update here when I have that completed.
Comment #6
donutdan4114 commentedOkay I have updated the module. Fixes include:
Removed code from the master branch.
Created branch 7.x-1.x for main code repo.
Changed _event js object => Drupal.engagement object.
Fixed some functions so UID would be optional.
Fixed some coding problems for standardization.
Andy,
I like your ideas about the Context module integration, and the reports section that shows user activity.
I think the module can be promoted to a public project without those features though, as this module is primarily focused on developers who want to act upon user events.
However, I will work on the reports interface, but I'll probably let someone else work on the Context integration.
There might even be other modules this module could integrate with (views, rules, etc)...
Comment #7
andymantell commentedAgreed, I would be happy to RTBC this without the feature requests. I've found a couple of issues though:
1> Whenever you use a node_load(), you need to check that you actually have a node returned correctly. For example, on line 214 you do this:
However, on a clean install of my site, using Drupal's default frontpage (which is "node"), this results in a PHP error. You should do something like this instead:
The node_load has gone into the if statement in an extra set of brackets. If node_load() returns FALSE here, this will not fall through to the code inside the if statement.
Another common pattern here is to say:
That stops it matching the frontpage with a path of just "node". At the moment, viewing Drupal's default frontpage is being counted as a node_view, not to mention that it throws an error.
2> I have been trying to call your JavaScript API via Firebug and it just comes back saying "Invalid token". It works fine when I am logged in as user 1, but not for anonymous users.
3> I haven't been able to confirm this with anonymous users yet due to the tokens issue, but when logged in as user 1 I can run the following piece of JavaScript:
And this will empty the entire engagement table. Some checking of parameters here would be good. Also, reading through the code I can't see any access checking occcuring on the API calls. So once the aforementioned token issue is fixed, I would be able to come along as an anonymous user and empty your engagement table. The solution would be to define some more permissions, perhaps one for each event type and test whether the current user is allowed to perform this action (see user_access())
As for the automated review. It's coming back nearly clean now:
http://ventral.org/pareview/httpgitdrupalorgsandboxdonutdan41141588634git
Comment #8
donutdan4114 commentedOkay I pushed the fix for the node_load issue.
Fixed issue with anonymous token.
Added permission for deleting events. By default, only global admin can delete events.
Updated event_delete function to take more params similar to the other functions.
Comment #9
andymantell commentedHi,
Looking a bit better. Just want to run through the logic in this function though:
You are now testing the permission which is great. However, let's say I am logged in as a user who doesn't have permission to delete events. If I call this function and pass the $uid of a user who does have permission to delete events, I can neatly sidestep this access check.
If I understand this correctly, you want to pass the $uid in this function into the query in order to only delete events for that $uid. However, I don't think you want to be performing the user_access() call using this user. I think the following snippet is probably what you want?
On a related note, I think the "delete events" permission is slightly too generic and might collide with other modules. It should probably be something like "delete engagement events".
The documented parameters for this function are out of date. Might be worth just double checking they're all up to date.
Will continue this in a moment, have to go cook tea...!
Comment #10
donutdan4114 commentedNice catch on the delete function.
I have updated it.
Having a delete function that can be activated through js is a scary thing...
Also I renamed the permission to 'delete engagement events'.
Is there a place on this site to see what other module permission names are.
I noticed there are at least a few areas where modules can collide:
- project names
- permission names
- page URLs
- variable names
I suppose one should always try to prefix everything with the module name..
Can't wait for a more object oriented approach... and name spacing :)
Comment #11
andymantell commentedJust tried to enable your module on a fresh Drupal install and got a fatal error caused by this line:
As per the docs for this function, the second argument needs to be an array (http://api.drupal.org/api/drupal/modules%21user%21user.module/function/u...).
Comment #12
andymantell commentedOk, nearly there I think!
Looking at your menu hook, your API call should technically have another argument in the URL. So instead of this:
You should have a wildcard in the URL as you are using this URL component on line 37:
$token = arg(2);:There are a couple of outstanding issues on the automated code review:
http://ventral.org/pareview/httpgitdrupalorgsandboxdonutdan41141588634git
The first one about using drupal_ prefixed string functions isn't a major problem, but it's also easy to fix. E.g. drupal_strtoupper() might be used instead of the native PHP function. They all do the same thing but to varying degrees they cope with edge cases that the native functions don't.
The second one regarding the argument order is about this function here:
It's complaining because all of the arguments have default values apart from $start in the middle. All of your non optional arguments should come before your optional ones. If it was me I might use this order:
We're talking about returning events for a date range, so the most pertinent arguments should be the start and end date. Start date being required, but omitting the end date would return all events until the present. The function then optionally allows filtering by event name, value and user.
Comment #13
donutdan4114 commentedOkay I think I fixed all the issues.
Here is an updated code review:
http://ventral.org/pareview/httpgitdrupalorgsandboxdonutdan41141588634git
Comment #14
andymantell commentedCool. I will take another (hopefully final) look later this week, probably wednesday.
Comment #15
donutdan4114 commentedComment #16
andymantell commentedCool, looking good now. I'm going to RTBC this but there's a couple of minor things I spotted since:
* in engagement.admin.inc you have "Implements hook_FORM_ID()" in the documentation but this isn't acually a hook, it's just a form callback function.
* You have this at the top of the form:
But you are already testing the permissions in the hook_menu() under "access arguments" so you can just remove this line. On a side note, it's not good practice to print something and then exit() like that. You probably should have returned drupal_access_denied() instead
* Moving forward, I would really like to see an engagement_example.module with examples of how developers can use your module. This isn't a blocker but it would be extremely useful.
Comment #17
donutdan4114 commentedThanks andy, I fixed the issues that you mentioned. I have learned a lot through this process.
I will be sure to add and example file with some good documentation about what you could potentially use this module for.
Comment #18
klausiWe have currently exceeded the proposed project application thresholds, so this is on hold for me for now. Get a review bonus and I will review/approve this right away.
Comment #19
patrickd commentedfunction engagement_api($token) {should do the same job, but easier$obj->... = check_plain($_POST['...']);The concept of drupal is to filter output - not input!why don't you make use of drupal_add_js's 'settings' function ?
got to stop, will continue later
Comment #20
donutdan4114 commentedWill apply these fixes. Some questions:
1) When I remove check_plain for the $_POST input the code checker yells at me for not sanitizing input.
2) It won't let me delete master branch remotely.
3) What should I do for a screenshot? What is convention for a mostly code based API?
I will work on documentation in the readme file.
Comment #21
patrickd commentedThere's no convention about this as far I know, but any kind of picture that gives a non-programmer user an idea what it does would be nice.
Comment #22
donutdan4114 commentedRemoved check_plain().
Added screenshot to project page.
Added some documentation and an example to the README file.
Questions:
1) How can I make a documentation page for the project? It would be easier to view code samples that way..
2) I still can't delete master branch. I get error "by default deleting the current branch is denied... refusing to delete the current branch :refs/origin/master ..." However, I am not on master, I am on branch 7.x-1.x
Comment #23
patrickd commentedGo to https://drupal.org/documentation/modules/contributions
and at the bottom you'll find "Add child page", name the child page like your module.
Maybe you got to switch the default branch first:
goto your project > edit > default branch > set 7.x-1.x as default branch
Comment #24
donutdan4114 commentedAdded module documentation page. Will add more examples soon, will probably make separate pages for those.
Deleted master branch. Indeed, you cannot delete a branch that is set as your default :)
Comment #25
patrickd commentedlet's continue
module_invoke_all('track_event', $uid, $event_name, $event_value, $event_data);all hooks you invoke must be prefixed with your module's machine name! also all of them must be documented in a api.php file (https://drupal.org/node/161085#api_php)
can you always be sure that these variables contain secure content? if they can contain user content you have to make sure to check_plain() them also for drupal_add_js()!
t("Manage") . ' ' . l(t('Google Analytics'), 'admin/config/system/googleanalytics');if your always concatenating strings this way, translators have the problem of missing context because they only see "Manage" and "Google analytics" seperately - on the other side its not good to use html within translatable strings. But this is okay and also used in core:
t('Manage <a href="@ga_url">Google Analytics</a>', array('ga_url' => url(admin/config/system/googleanalytics)));t($description),- ALWAYS translate static string directly in any other case use placeholders of t() (@ ! or %)! as the contents of $description are already translated some lines above just use $descriptions without t() here$description = "(module not found)";1. this code will have no impact it's out of scope 2. Translate static strings with t()!'Separate by new lines, not commas.'Translate ALL static strings by t()! (except within hook_menu, hook_schema and watchdog-calls)this is not necessary.
user_role_change_permissions(user_role_load_by_name('global administrator')->rid, array('delete engagement events' => TRUE));As far I know there is no global administrator role on a standart drupal installation - but even if it were - never rely on this, simply remove it and let the user decide.Fix points 3, 6, 7, 8 and we're done here
leaving rtbc
Comment #26
donutdan4114 commented1) fixed
2) You are right that using JavaScript is probably a better way to track node_views. I will probably implement this in the future.
3) done
4) No unsafe data should be in the variables, as they are set in code.
5-9) fixed
10) Is it 'bad' to set the variables though? I would rather not set default values for every variable_get() I do.
11) fixed
Thanks a lot for your help on this. I've learned a lot through this whole process.
Comment #27
patrickd commented10) It's not bad, but it's not the way it's designed for. Worst-case: some of the variables are deleted from the variables table manually and as the default values are not given they'll all return NULL. You had to reinstall the module for having the default values back.
7) not fixed
Thanks for your contribution and welcome to the community of project contributors on drupal.org!
I've granted you the git vetted user role which will let you promote this to a full project and also create new projects as either sandbox or "full" projects depending on which you feel is best.
Thanks, also, for your patience with the review process. Anyone is welcome to participate in the review process. Please consider reviewing other projects that are pending review. I encourage you to learn more about that process and join the group of reviewers.
As you continue to work on your module, keep in mind: Commit messages - providing history and credit and Release naming conventions.
Thanks to the dedicated reviewer(s) as well.
Comment #28.0
(not verified) commentedUpdated description with recent code changes.