At the moment i am in the situation where i have created a module that will edit Moodle's admin list when a users admin rights in Drupal are altered.

The module works only by the fact that in the user.module, i have had to insert a function call to the my module:-

foreach (array_keys($array['roles']) as $rid) {
if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);

moodle_update();
}

I know that this isn't good practice as at any point Drupal is upgraded, this will scrub my piece of code in the user.module.

How can i alter my module so that it is the one that does all the work?

Thanks
AMRIT

Comments

nevets’s picture

You could use the user hook with op = 'update' (and maybe 'insert'). While that may fire when a user changes there information you could limit calling your update function to when user_access('administer access control') is true. So while there may be times when you do an unneed update they should be small in number and you do not need to edit core to achieve your goal.

coreyp_1’s picture

In this case, the hook is not called.

There might be a work-around, but I haven't tested it. Perhaps you could hijack the page by declaring your own menu callback, something like this:


function hijack_help($section) {
  global $user;

  switch ($section) {
    case 'admin/modules#description':
      return t('Overrides the user permissions form for Moodle integration.');
        return $output;
  }

}

function hijack_menu($may_cache) {
  $items = array();

  $access_access = user_access('administer access control');

  if ($may_cache) {

    // Admin access pages
    $items[] = array('path' => 'admin/access', 'title' => t('access control'),
      'callback' => 'hijack_user_admin_perm', 'access' => $access_access);
    $items[] = array('path' => 'admin/access/permissions', 'title' => t('permissions'),
      'callback' => 'hijack_user_admin_perm', 'access' => $access_access,
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);

  }

  return $items;
}

function hijack_user_admin_perm_submit($form_id, $edit) {
  user_admin_perm_submit($form_id, $edit);

//  default stuff is done,
//  add your code here

  moodle_update();
}

function hijack_user_admin_perm($str_rids = NULL) {
  return user_admin_perm($str_rids) {
}

In this case, the fictional module's name is "hijack". Now for the standard disclaimer: use at your own risk. This code is completely untested (i.e., I'm typing off the top of my head). Subject to errors (it's been a while since I programmed a 4.7.x module). Back-up everything first, just in case the server decides to melt.

If this works, then I will probably celebrate by taking the rest of the day off (not that this helps me any, I just want an excuse to be lazy today)!

- Corey

EDIT: corrected a function name typo

amrit09’s picture

Hi Corey

You may have to hold your celebrations a while longer!!

As far as i understand (this happened more by accident that anything else), because i named my module "moodle_update" and the subsequent function within it called the same, i think it is using the the update hook.

From this point i am lost....i assume because i used the update hook, this allows the link between the user.module and my own module through the above function call.

There must be a way that i can "reverse" this hook, so it works the other way around??

AMRIT

amrit09’s picture

I also tried this code and it doesnt seem to work. The call to my function doesnt appear to be invoked.

AMRIT

amrit09’s picture

Is it then true that i can name the function:-

function moodle_user_access()

To make use of both hooks??

coreyp_1’s picture

That depends on what you're trying to do.

What this simple override does is steal control of the "admin/access" and "admin/access/permissions" (actually the same page) callbacks. The new (hijack) module still calls the original functions to create and submit the correct form, but also serves as a "wrapper" so that additional code can be run upon submission.

If you change the function callback to a different function, then you will lose the functionality of the Drupal function. In other words, I don't think it will do what you are needing. HOWEVER, I have never used Moodle, so I don't know exactly what the moodle_user_access function is for. You can always just try it on your test site and see what happens.

- Corey

amrit09’s picture

Hi

I am still having problems with this. I am using an update hook and, when i make a function call to my function in my module from the user module, it works. However, i do not want to use alter the core user module because of issues during possible upgrading of Drupal.

I have tried using a user hook, but this doesnt fire off my code at all and I dont understand why (my knowledge of hooks is limited). Am i right in making the assumption that my code isn't firing because there is no obvious place for it to be fired?

I have attached the code below for my module:-

<?php
// $Id: moodle_user.module,v 1.612.2.21 2006/10/18 20:14:42 killes Exp $

function moodle_user($op = 'update') {

if ($op == 'update' || $op == 'insert')
{

//query first database
$results = db_query('SELECT abhachudrupal.users.name, abhachudrupal.users.uid FROM abhachudrupal.users, abhachudrupal.users_roles WHERE abhachudrupal.users.uid = abhachudrupal.users_roles.uid and abhachudrupal.users_roles.rid=3');

db_set_active('moodle');

//count the number of record in the admins table
$results4 = db_query('SELECT * FROM abhachu.mdl_user_admins');
$row4 = mysql_num_rows($results4);
//echo "num:-";
//echo $row4;
//echo "
";

//create a duplicates table
$create = db_query('CREATE TABLE abhachu.mdl_dup_admins (id INTEGER(10) NOT NULL auto_increment, userid INTEGER(10) NOT NULL, PRIMARY KEY(id))');

//To echo(display) results
while ($row = mysql_fetch_array ($results))
{
extract ($row);
//echo $name;
//echo ":-uid =";
//echo $uid;
//echo "

";

//query 2nd DB
$results1 = db_query('SELECT abhachu.mdl_user.username, abhachu.mdl_user.id FROM abhachu.mdl_user, abhachu.mdl_user_admins WHERE abhachu.mdl_user.id <> abhachu.mdl_user_admins.userid ORDER BY abhachu.mdl_user.id');

while ($row1 = mysql_fetch_array ($results1))
{
extract ($row1);
//echo $username;
//echo ":-id =";
//echo $id;
//echo "
";
if ($username == $name)
{
//echo $name;
///echo "
";
//echo $id;
//echo "
";

//Insert value into test table
//echo "INSERTING:-";
//echo $name;
$insert = "INSERT INTO abhachu.mdl_dup_admins (abhachu.mdl_dup_admins.userid) VALUE ('$id')";
$results3 = mysql_query($insert) or die(mysql_error());

//Count the number of times the ID has been put into the test table
$dupTab = "SELECT abhachu.mdl_dup_admins.userid FROM abhachu.mdl_dup_admins WHERE abhachu.mdl_dup_admins.userid = '$id'";
$rDup = mysql_query($dupTab) or die(mysql_error());
$duplicate = mysql_num_rows($rDup);
//echo "num2:-";
//echo $duplicate;
//echo "
";

//Check if the ID is already in the table
//If ID is in duplicate table the same number of times
//as the number of values in admin table, then add
if ($duplicate == $row4)
{ //echo "IM IN!!!";
$insertFull = "INSERT INTO abhachu.mdl_user_admins (abhachu.mdl_user_admins.userid) VALUE ('$id')";
$rinsertFull = mysql_query($insertFull) or die(mysql_error());
//add 1 to the number of records in the admin table
$row4 = $row4 + 1;
}
}
}

}

$dropDup = db_query('DROP TABLE abhachu.mdl_dup_admins');
//echo "dropppingDUP";

db_set_active('default');
}
}

nevets’s picture

From the API documents, the hook should be declared as

function moodle_user($op, &$edit, &$account, $category = NULL)

If you are still have problems, you might want to try this real simple version of the hook

function moodle_user($op, &$edit, &$account, $category = NULL) {
drupal_set_messsage("moodle_user($op, ..., ..., $category) ");
};

to see that it is being called when expected.

amrit09’s picture

I tried the simple test code and as far as i can see, my code isn't getting fired off at any point, which is a worry.

Another thing that i have noticed is that for my original code to work, with the update hook, my module must be named:-

moodle_update

and the function must be named

function moodle_update()

to work, with any changes throwing up an error.

Any ideas?

nevets’s picture

Did you try editing your own account information? You do not need to change anything, just edit and save. You should see something if your theme has a message area. As for moodle_update(), that would normally run if you have defined a content type and have just saved an instance of it. It should otherwise not even run.

amrit09’s picture

Ahh, ok, when i now edit my own stuff, it gives the error:-
Fatal error: Call to undefined function drupal_set_messsage() in /var/www/abhachu/drupalforum/modules/moodleupdate.module on line 5

which doesn't really matter i suppose because at least the module is being fired off. I then tried it with my module and, by taking out the "for" statemant, the module works. The only thing is now, it gets fired off several times, which isnt a problem apart from the wasted server process time (minimal).

Thanks for the help.

AMRIT

amrit09’s picture

One more question, why does the function only work when it is named moodle_user and nothing else, e.g. "i_user" or "moodleUp_user" ??

nevets’s picture

In Drupal there are a number of hook functions and you will find them referenced iin the api as hook_somefunction (ex: hook_user). Modules have a name (in your case 'moodle'). When a hook is implemented by a module the actual name to use is modulename_somefunction, so in your case the user hook needs to be moodle_user

amrit09’s picture

Yes, sorry, i was in the middle of editing my previous comment to say that when i got side tracked.....

I have the first module working now as required, and i have written a second similar module which deals with a different database.

What the problem is now is that it doesn't seem to like the two modules working at the same time. The two functions use the same user hook:-

function moodleupdate_user($op, &$edit, &$account, $category = NULL)
function flashmeetingUpdate_user($op, &$edit, &$account, $category = NULL)

They both work when the other module isnt on, but i get the error message:-
INSERT command denied to user 'abhachu'@'localhost' for table 'new_user'

In the administrator section, the error log reads:-
Type php
Date Wednesday, February 28, 2007 - 18:54
User abhachu
Location http://dewey.computing.dundee.ac.uk/abhachu/drupalforum/?q=user/24/edit
Referrer http://dewey.computing.dundee.ac.uk/abhachu/drupalforum/?q=admin/user
Message Unknown column 'sid' in 'field list' query: INSERT INTO sessions (sid, uid, cache, hostname, session, timestamp) VALUES ('5d4bee3cfbc789557049b7f4d75cd065', 1, 0, '134.36.37.136', 'watchdog_overview_filter|s:3:\"all\";lastaccess|i:1172688460;', 1172688892) in /var/www/abhachu/drupalforum/includes/database.mysql.inc on line 121.
Severity error
Hostname 134.36.37.136

This appears to be something associated with the drupal core modules and i'm getting way out of my depth here!

Thanks
AMRIT

nevets’s picture

One question is how you have "written a second similar module which deals with a different database", how does it deal with the other databas, have you followed the suggestions under: how to connect to multiple databases

The purpose of hooks is to allow modules react to events, so in general having two (or more) is not an issue. Depending on what your hooks do it is possible for them to collide but I am guessing it has to do with the way you are dealing with the other database.

amrit09’s picture

The second module is only similar in the fact that it uses the user hook and that it contains SQL dealing with a different database.

The databases are connecting fine as i previously implemented the code as shown in the how to connect to multiple databases.

As an individual modules, they both work fine, it is when they are being at the same time. When i go to my user/edit page, the error i previously stated is shown. The INSERT that is being referred to is a new table that has been created prior to this INSERT query, so it can be assumed that drupal is having no problems in connecting to the databases.

the error reported in the log mentions:-
Unknown column 'sid' in 'field list' query: INSERT INTO sessions

where sessions is a core table of drupals and on investigation does have the column 'sid'.

This is why i think that running the two modules, which are getting called in the same page are confusing durpal, but i dont know why or how.

AMRIT

nevets’s picture

The flashmeetingUpdate will run before moodle by default unless you have set the weight in the system table to do otherwise. Here is how I would approach this, first I would make sure the database is being switched back when the flashmeetingUpdate is done doing it's thing (for all possible cases).

If that is OK, I would then strip out the case update code in flashmeetingUpdate (obviously saving it somewhere) and replacing it with a call to Drupal_set_message('Something like you are here') and seeing if things work. I would then add back the code upto but not including the code that switches the databases. Does it work now. Next I would add the code back that switches the database followed by the code that switches the database back (with nothing in between. If things are working, I would then start adding back chunks of code (broken on logical bountries) bwtween the database switches.

The goal is to localize the code that is causing the problem, which depending on the amount of code can take some time.

amrit09’s picture

I have just looked at it again on getting in this morning, and i put in some "echo" statements into the modules to see what was going on.

I realised, because i think the way a drupal form is built up and the user hook works, that individually, with the other disabled, each of the modules is fired off 4 times when "user---edit" is selected.

When modules are both running, they both fire off fine the first time they are called, and the SQL performs as required and inserts into the correct table. However, when it comes into the flashmeetingUpdate.module the 2nd time, this is when the error occurs.

Is it something to do with session access rights (i came to that conclusion based on the log files i had in the previous post)??

If so, is there a way i can limit these modules to only occur once when any user form is called??

AMRIT

amrit09’s picture

Solved

I tried a few things but finally got it working by putting the "if ($op == update || $op == insert)" back into the code (notice no quotations around update and insert)

This now only fires off the modules once as needed when submit is selected.

Regarding the log error, if anyone has a solution as to why this was coming up then i would still be interested to know.

AMRIT