Popular Children logs the page views in specified menus, and enables creation of special blocks listing the most popular items selected from children or siblings of a particular menu item. The menu item to which each block is related can be fixed, or configured to show links related to user's current position in the menu.

It is possible to provide listings more tailored to each user by enabling role filtering for blocks - in that configuration, the block will count the link popularity based on number of visits made by users whose roles match the roles of the user for whom the popularity is calculated.

In other words, one could create a block containing 'Most popular links in this section' that would list most popular children of the menu parent of the item where the block is shown, or a block called 'Recommended for you' that would list most popular items as counted by entries from members of user's groups.

Project page

Popular Children

Core version

7.x

Git clone command

git clone --branch 7.x-1.x https://git.drupal.org/sandbox/karoop/2744957.git

Manual reviews of other projects

  1. Bulk Delete Nodes
  2. AddressField CR plugin
  3. BNM Currency rates

Comments

karoop created an issue. See original summary.

PA robot’s picture

Status: Needs review » Needs work

There are some errors reported by automated review tools, did you already check them? See http://pareview.sh/pareview/httpsgitdrupalorgsandboxkaroop2744957git

We are currently quite busy with all the project applications and we prefer projects with a review bonus. Please help reviewing and put yourself on the high priority list, then we will take a look at your project right away :-)

Also, you should get your friends, colleagues or other community members involved to review this application. Let them go through the review checklist and post a comment that sets this issue to "needs work" (they found some problems with the project) or "reviewed & tested by the community" (they found no major flaws).

I'm a robot and this is an automated message from Project Applications Scraper.

karoop’s picture

Status: Needs work » Needs review

I fixed all pareview errors that I could. Looking forward to reading your constructive feedback!

David Fiaty’s picture

Status: Needs review » Needs work

Hello karoop,

Your module looks good. There are however a few things that should be double checked:

1. There are still a lot of pareview errors in popular_children.module:
http://pareview.sh/pareview/httpsgitdrupalorgsandboxkaroop2744957git
You should check the code according to the warnings and errors displayed there.

2. In popular_children.install line 16 there is a mysql table field definition called 'date', which is a reserved word in both PHP and MySQL.
It could generate issues when using your module. You should rename this field and update any code that calls the field.

3. In popular_children.install line 39 there is a mysql table field definition called 'count', which is a reserved word in both PHP and MySQL.
It could generate issues when using your module. You should rename this field and update any code that calls the field.

4. In popular_children.install, for all tables declared, index names should begin with the name of the table they depend on.
See Drupal SQL coding conventions (https://www.drupal.org/node/2497).

5. In popular_children.module line 876 there is an SQL query that could/should be adjusted:
The $date_condition and $role_condition PHP variables should be moved out of the query body and passed in as separate parameters.
See Drupal SQL coding conventions (https://www.drupal.org/node/2497).

Thank you

karoop’s picture

Status: Needs work » Needs review

Hi David,

Many thanks for the thorough review. Thanks to you, I have improved the SQL handling of the module a lot. Let me address each point of your review:

Parevew

I made a few changes, and my reasoning for leaving the rest is as follows:

popular_children.test

  • All Unused bound variable warnings refer to arrays which get modified inside the closure. The code does not work otherwise, so these errors are false positives.
  • Line 150: The comment ends with a description of a table structure which is not a sentence and therefore does not need a full stop.
  • Line 172: Drupal API documentation standard does not mention how to specify type for an anonymous function. I can't find a way to type hint it (suggestions welcome!)
  • Line 778: The translated string is a part of an xpath query which is why it's concatenated.
  • Lines 881, 987, 1057: Those sentences start with the name of the column, 'mlid' - hence the lower case.

popular_children.module

  • Lines 95, 100, 636, 1075: 'mlid' is the name of the field in a table. I consider it to be explicitly all lower-case, hence no need for a capital letter.
  • Lines 1043 - 1051: The indentation errors are caused by arrays being defined in chained method calls on an object. Reducing indentation would cause a right mess.

Column names

I have renamed the columns date and count as advised. Thanks for the suggestion. The indexes have also been renamed, thank you for bringing this requirement to my attention.

SQL query

You are of course right, and your comment made me realise that I can actually write the whole thing using Drupal's SelectQuery interface. I rewrote the function calling the query and you should find it a lot more compatible and in line with Drupal standards.

Again, looking forward to constructive criticism!

karoop’s picture

Quick note: I found a small bug and fixed it. Some line numbers in my previous comment might be slightly off now. I hope that this does not impede your review!

karoop’s picture

Issue summary: View changes
chaitanya17’s picture

Hi karoop,

I have done code review of your module, can you please do below fixes,

1) Use Drupal behaviors in your popular_children.js file.

Drupal.behaviors.myModule = function(context) {
   
}

2) Add $('[data-pc-external] a').click(linkHandler); in behaviors.

3) Invert condition if (count($menu_info) == 2) { for early return. and will reduce else section.

4) I have seen use of $_POST['form_token'] can we get this value from $form_state

Thanks,

chaitanya17’s picture

Status: Needs review » Needs work
karoop’s picture

Issue summary: View changes
karoop’s picture

Status: Needs work » Needs review

Thanks very much for the thorough review.

I have implemented all of your suggestions - thank you for making me aware of the Drupal behaviors!

I'm looking forward to reading more constructive reviews :)

ashwinsh’s picture

Hello @karoop,

I have review your module and following are my findings,

File: popular_children.module

Line 95 and 100	: Doc comment short description must start with a capital letter

Line 634: Inline comments must start with a capital letter

Line 639: Do not concatenate strings to translatable strings, they should be part of the t() argument and you 	should use placeholders

Line 890: Parameter comment must end with a full stop

Line 1040: Array indentation error, expected 6 spaces but found 8
Same for Line 1041, 1043, 1044, 1045, 1046, 1047, 1048, 1049

Line 1073: Parameter comment must start with a capital letter

File: popular_children.test

Line 150:Doc comment long description must end with a full stop

Line 172: Type hint "function" missing for $fn

Line 780: Do not concatenate strings to translatable strings, they should be part of the t() argument and you should use placeholders

Line 883, 989, 1059: Parameter comment must start with a capitalletter

Thank you,

karoop’s picture

Hello ashwin.shaharkar,

Your review looks word to word like the automated review from pareview. All of the issues you mentioned are discussed and explained in #5. If, however, you can suggest a way for me to typehint Function for $fn in the tests, I'd be more than happy to implement it.

Thank you for your time!

ashwinsh’s picture

Hello @karoop,

Review which I posted here is showing by command line version of codesniffer which is slightly more up to date than pareview.sh.

Thank you,

karoop’s picture

ashwin.shaharkar,

Thanks for letting me know. The errors the command line tool reported are word to word the same as the ones from pareview, hence my assumption you got it from there. I can see now that your tool caught one additional error regarding t() concatenation, which I have now fixed.

As explained before, I have addressed all other issues in my comment above. If you know how to typehint an argument that is a name of a function or an inline function, please let me know.

karoop’s picture

Issue summary: View changes
karoop’s picture

Issue tags: +PAreview: review bonus

I did the required 3 manual reviews, so I'm adding the PAReview: review bonus tag.

neha_patil’s picture

Hi karoop,

I have reviewd your module. It is looking good for me.
Please check the below link and fix the issues :
http://pareview.sh/pareview/httpsgitdrupalorgsandboxkaroop2744957git.

karoop’s picture

Hello neha_patil,

Thanks for taking time to review the module. My reasoning for leaving the issues from pareview is explained in this comment. Would it be OK to ask you to list the ones that you think I should fix nonetheless? If you agree with my reasoning, however, would you mind changing the status to 'Reviewed and tested by community'?

Many thanks,

karoop

jofitz’s picture

Status: Needs review » Needs work

Automated Review

You have addressed many of the Automated Review results in previous comments, but I have included a suggestion in the Coding Style section below.

Manual Review

Individual user account
Yes: Follows the guidelines for individual user accounts.
No duplication
Yes: Does not cause module duplication and/or fragmentation.
Master Branch
Yes: Follows the guidelines for master branch.
Licensing
Yes: Follows the licensing requirements.
3rd party assets/code
Yes: Follows the guidelines for 3rd party assets/code.
README.txt/README.md
No: Does not follow the guidelines for in-project documentation and/or the README Template.

Does not include all of the required sections of the README, e.g. Introduction, Requirements, etc

Code long/complex enough for review
Yes: Follows the guidelines for project length and complexity.
Secure code
Yes: Meets the security requirements.
Coding style & Drupal API usage
[List of identified issues in no particular order. Use (*) and (+) to indicate an issue importance. Replace the text below by the issues themselves:
  1. popular_children.test:172 - param type should be string
  2. popular_children.test:780 - avoid concatenating the result of t(), see https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/t/7....
  3. popular_children.test:883 - Parameter comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  4. popular_children.test:989 - Parameter comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  5. popular_children.test:1059 - Parameter comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  6. popular_children.module:95 - Doc comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  7. popular_children.module:100 - Doc comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  8. popular_children.module:634 - Inline comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  9. popular_children.module:639 - Translatable strings must not begin or end with white spaces, see https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/t/7....
  10. popular_children.module:1073 - Parameter comment must start with a capital letter. Perhaps prepend the word "The" at the beginning of the comment.
  11. Many examples of incorrect array indentation.

This review uses the Project Application Review Template.

karoop’s picture

Status: Needs work » Needs review

Hello Jo,

Thank you very much for the in-depth review and suggestions. I realised that I have been a bit stubborn with pareview issues about comment formatting, and corrected them. If you have a look now, you'll find all of them gone (except for the 'Unused bound variable' error which I explained already).

Magically, the array indentation errors have disappeared as well!

I have rewritten the Readme file, and took the opportunity to beef up the help page.

Do you guys think I have to change a lot more things before getting approved? I'm looking forward to hearing your thoughts.

arun ak’s picture

Status: Needs review » Needs work
Issue tags: +PAreview: security

Hi karoop,

I installed this module in my localhost. Please see my comments below.

For external links you are using ajax callback to increment the counter. But I found an issue in that, because anyone can increase the click count of a link in db using javascript. As you are setting access callback as 1 for ajax url, anyone can post data to that url and is getting entered in to the database. Using below script user can easily insert any random numbers in to the database.

jQuery.ajax({
  type: "POST",
  url: '/popular_children/click_logger',
  data: {mlid:999999999},
  success: function(data){console.log(data);},
  dataType: 'jSon'
});

Thanks,
ARUN AK

greggles’s picture

In case it helps find solutions to the problem that ARUN AK found: that sounds like a Cross Site Request Forgery and/or a Lack of Flood Control vulnerability.

greggles’s picture

I realize now that might also be a lack of input validation and access bypass.

klausi’s picture

Status: Needs work » Needs review
Issue tags: -PAreview: security

Removing security tag because click tracking is hard to protect and usually open for anyone to create records. If you look at for example Drupal core's statistics.php file you will see that it also logs requests unconditionally. Even Google Analytics has this problem that it cannot distinguish fake page hits from some attacker vs. real page hits from human users.

Any other vulnerability you found ARUN AK?

afinnarn’s picture

Hey karoop,

I noticed a few things, I'll comment on...

- While using the define() function for constants, I feel like most Drupal developers use the variable_set() and variable_get() functions and do the initial setting in the install file/hook. I see you are using both ways in your module, but I feel switching to variable_set() and variable_get() aligns more with Drupal and allowing other modules to access the state of configuration. Also, if I'm looking through your code and trying to see what a constant is, I have to jump back and forth.

- I think you are using drupal_load() in the schema hook just to get the POPULAR_CHILDREN_BLOCK_TYPE_CHILDREN constant? If so, I've seen that setting default values is best done in hook_enable() and you can rely on the .module file being loaded in that hook. Reference

- In popular_children_add_block_form(), I think you can just call the actual form from hook_menu() rather than merging the $form into _popular_children_block_config_form(). Another IMHO, but using array_merge in your form definition can make it harder to read. You shouldn't really need to worry about overwriting your form keys in your own module.

- I tried going through the process of making a block but I could never view it. I tried stepping through the code but only got to the part in _popular_children_block_content() where there were no returned links. I guess I'm just no understanding how to use this module from poking around and using the readme. Could just be that I'm a dummy, but I guess my review will have to end here.

Overall, it looks like good quality code and what I pointed out are not huge deals, but since I couldn't fully use the module, I don't know how helpful this review will be.

karoop’s picture

Hello ARUN AK, greggles, klausi, and afinnarn.

Thanks very much for your comments. Let me address them in order:

Input validation of ajax calls

Well spotted, ARUN AK! I want the module to be able to count anonymous clicks, so I could not put access control on the ajax callback. To reduce the risk of malicious ajax calls, I have taken the following steps:

  • I added an option to the module that allows administrators to decide whether they want to count anonymous users' clicks on the external links.
  • If that option is enabled, a session is started even for anonymous users and the ajax call must have a valid token, which is supplied in JS settings. Therefore, users without a valid session won't be able to add a click.
  • The mlid supplied is now also validated, and only a valid menu link ID can be saved to the database.

Working on this issue helped me improve the way the clicks are logged to the database. Thank you!

Use of constants

affinnarn,
While I agree that loading the module file just to get the constant in hook_install() is too much, my view is that the values that do not ever change should not be saved as variables. I believe that we should also avoid using unnamed literals to be able to better catch mistakes. I would argue that by plentiful use of constants in the module, I made my code less prone to typos and provided an explanation of what different values mean. I have also seen plenty of constants used in other contributed modules and in the core, so it doesn't seem like an un-Drupal way of doing things.

You mention that you have to jump back and forth to find out what a constant is, but without the constants you would not be able to even read the description or name of a string or a number used in code.

I have changed the default value of the 'type' column in the 'popular_children_blocks' following your comment, and was therefore able to resign from loading the module file beforehand. Thanks for bringing this to my attention.

Form creation

I use the function _popular_children_block_config_form() to create fields for the block configuration, but it varies slightly depending on where it is used. It so happens that when called in popular_children_add_block_form() it doesn't require any parameters, but this form requires my special validation callback.

I could add the custom validation entry in the _popular_children_block_config_form(), but in my opinion it would make the function too complex (because in other cases we don't have to add it). I have rewritten the popular_children_add_block_form() a little to make it a bit more readable.

Empty / invisible block

The module has been updated, so it would be best for you to pull the latest commit and clear the cache.
After installing the module and enabling a menu, you have to make sure that you visit some of the pages inside it for the module to register some clicks. Check the settings of the block and see if the menu items it's pointing to have been clicked. If they did, try clearing the cache or turning off caching of the blocks in the module settings. If you turned on the role filter, you should try looking at the block using the same user as the one that generated the clicks. I guess that it's not worth mentioning that the block must be assigned to a region ;)

Finally, you could try running the test suite to see if the module is working at all. If it fails, I would be very grateful if you could let me know what your environment is so I can try to duplicate the errors.

It would also be nice to see the steps you tried in your attempt to get the module to work, if you don't mind writing them up for me.

Once again, thank you all for great and thorough reviews!

I'm looking forward to getting the application approved - do keep the constructive critique coming :)

arulan_pari’s picture

Hi Karoop,

FILE NAME: popular_children.test
LINE NUMBER:3083
STATUS: WARNING
MESSAGE: Unused variable $edit_path.

Please, remove unused variable "$edit_path".

karoop’s picture

Oops! Missed that one.

Thanks for spotting it, Arulan Pari :) It's fixed now.

klausi’s picture

Status: Needs review » Fixed

manual review:

  1. popular_children_admin_form_submit(): why do you validate the form token here? The form API will already do that for you, so this can be removed?
  2. popular_children_page_alter(): if you are just adding JS and not modifying $page here then you should use hook_page_build() instead.
  3. popular_children_init(): hook_init() does not seem a good fit, since you only want to log clicks when an actual page is built. Also a case for hook_page_build()?
  4. popular_children_boot(): why do you need to start a session for anonymous users? Probably because of Drupal.settings.popularChildren.token? I think that is bad because it breaks page caching. As I said: when counting statistics for anonymous users it is not really possible to prevent CSRF.

But otherwise looks good to me.

Thanks for your contribution, karoop!

I updated your account so you can promote this to a full project and also create new projects as either a sandbox or a "full" project.

Here are some recommended readings to help with excellent maintainership:

You can find lots more contributors chatting on IRC in #drupal-contribute. So, come hang out and stay involved!

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.

Thanks to the dedicated reviewer(s) as well.

karoop’s picture

Thank you klausi!

I joined the code from hook_init() and hook_page_alter() in hook_page_build() as you suggested. Pareview is complaining about drupal_add_js() used in that hook, but since the content of the setting changes with every request, I believe that it can be used there safely - do let me know if I should fix that and how (setting $page['#attached']['js'][] didn't work).

Starting the session in hook_boot is configurable and described in detail in the module's UI. Administrators who turn that option on are notified about the consequences.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.