My post on developing a custom search at http://drupal.org/node/124963 has helped one other person besides myself, so I am going to continue the technique here with a different problem.

Bear in mind, I am new to this platform and trying to learn it at a low-enough level that I can leverage it quickly in the future instead of always flailing around for a quick fix.

I need to get content from my node onto the users profile in a particular format/order.

I know that the user.module does it when the user clicks goes to view their own profile, so I start by hunting in there for clues.

Here is the user_menu stripped down to semi-pseudo code.

if ($may_cache) {

  }
  else {
    if (WE HAVE A VALID USER ID) {
      if ($user !== FALSE) {
        $items[] = array('path' => 'user/'. arg(1), 'title' => t('user'),
          'type' => MENU_CALLBACK, 'callback' => 'user_view',
          'callback arguments' => array(arg(1)), 'access' => $view_access);

        $items[] = array('path' => 'user/'. arg(1) .'/view', 'title' => t('View Profile'),
          'access' => $view_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
        //edit user
        //delete user
        }
      }
    }
  }

What jumps out here is that only the 'user/.arg(1) url invokes a callback, but testing both 'path' => 'user/'. arg(1) and user/'. arg(1) .'/view' on my site gave me the same view.

So, lesson there is that there *may* be something special about MENU_DEFAULT_LOCAL_TASK that automagically invokes hook_view...I don't know, but it is something to file away

Ok, back to my task...I know that the callback->user_view is where I probably need to be, so lets look at that next.

Comments

prgrcmpny’s picture

Here is the user.module user_view hook.

function user_view($uid = 0) {
  global $user;

  $account = user_load(array('uid' => $uid));
  if ($account === FALSE || ($account->access == 0 && !user_access('administer users'))) {
    return drupal_not_found();
  }
  // Retrieve and merge all profile fields:
  $fields = array();
  foreach (module_list() as $module) {
    if ($data = module_invoke($module, 'user', 'view', '', $account)) {
      foreach ($data as $category => $items) {
        foreach ($items as $item) {
          $item['class'] = "$module-". $item['class'];
          $fields[$category][] = $item;
        }
      }
    }
  }
  drupal_set_title($account->name);
  return theme('user_profile', $account, $fields);
}

the guts seems to be in the

$fields = array();
  foreach (module_list() as $module) {
    if ($data = module_invoke($module, 'user', 'view', '', $account)) {
      foreach ($data as $category => $items) {
        foreach ($items as $item) {
          $item['class'] = "$module-". $item['class'];
          $fields[$category][] = $item;
        }
      }
    }
  }
  

loop, so lets grok that next.

prgrcmpny’s picture

ok, lets build from the ground up.

In my custom module, I define a menu callback for learning/debugging...

		$items[] = array('path'     => 'ld/hack'
										,'title'    => t('hack')
										,'access'   => user_access('access ld')
										,'type'     => MENU_CALLBACK
										,'callback' => 'ld_hack'
										);				

with callback to my debugging function ld_hack where I incrementally grok this bear.

function ld_hack(){
  $fields = array();
	print 'in hack';
  foreach (module_list() as $module) {
			print $module . '<br>';
			
	}

I pull up the ld/hack url on my site and get the following...

in hackfilter
node
system
user
watchdog
actions
aggregator
aggregator2
archive.......

i.e. a list of modules.

First question that comes up is this all modules in the modules directory? or only installed modules.

A quick check yields: ONLY INSTALLED MODULES. hooray, ok on to the next part...

prgrcmpny’s picture

Here is ld_hack with the original user_view code commented out as a reference below my working hacks

Since we know that we have a list of installed modules, it seems that user_view invokes hook_something_or_other on all installed modules.

First off, reading the docs on module_invoke ()shows a very different structure than what I see here.

http://api.drupal.org/api/HEAD/function/module_invoke

function module_invoke() {
  $args = func_get_args();
  $module = array_shift($args);
  $hook = array_shift($args);
  $function = $module .'_'. $hook;
  if (module_hook($module, $hook)) {
    return call_user_func_array($function, $args);
  }
} 

I don't know what's going on here, but lets continue and see what we can get.
here is ld_hack for next step.

function ld_hack(){
  $fields = array();
	print 'in hack<br>';
  foreach (module_list() as $module) {
	 	print $module . '<br>';
	  //if ($data = module_invoke($module, 'user', 'view', '', $account)) {
		$data = module_invoke($module, 'user', 'view', '', $account) ;
		print 'data = '. $data .'<br>';
	}
	/*
  foreach (module_list() as $module) {
	  if ($data = module_invoke($module, 'user', 'view', '', $account)) {
      foreach ($data as $category => $items) {
        foreach ($items as $item) {
          $item['class'] = "$module-". $item['class'];
          $fields[$category][] = $item;
        }
      }
    }
  }
  */
}

running it, I get:

n hack
filter
data =
node
data =
system
data =
user
data = Array
....

okey dokey loooks like the user module invokes its own hook...
none of the other modules were invoked.

the next question, is what hook?
hmmm.

prgrcmpny’s picture

lets do something stupid.

we know the user module invokes a hook on itself (There is a lesson there!--a module can invoke a hook on itself)

lets modify the ld/hack to get some idea of what is going on..

function ld_hack(){
  $fields = array();
	print 'in hack<br>';
 $data = module_invoke('user', 'user', 'view', '', $account) ;
 		print 'data = '. $data .'<br>';
		$data = module_invoke('user', 'user', 'view') ;
		print 'data = '. $data .'<br>';
    $data = module_invoke('user', 'user');
 		print 'data = '. $data .'<br>';
	/*

with output

in hack
data = Array
data = Array
data =

which seems to be saying to invoke the user_user hook in the user module.
lets see if there is a user_user hook there.

hmm.

whell there is!

here is the first few lines


function user_user($type, &$edit, &$user, $category = NULL) {
  if ($type == 'view') {
    $items[] = array('title' => t('Member for'),
      'value' => format_interval(time() - $user->created),
      'class' => 'member',
    );

   return array(t('History') => $items);
  }

so I need to implement a user hook taking args...

which returns and array

lets see what I can do.

prgrcmpny’s picture

Boss wants me on another task for a bit, will pick this up asap.

t.

prgrcmpny’s picture

ok

We know that the ld_user('view') hook will be invoked from ld_hack (and from user_user as well)
so lets implement it.

Here is a stripped down version of the ld_user$op) hook

function ld_user($op, &$edit, &$account, $category = NULL) {
			switch($op){
  		case 'categories:  
	 		case 'delete':     
	 		case 'form':       
	 		case 'submit':     
		  case 'insert':     
	 		case 'login':      
	 		case 'logout':     
	 		case 'load':       
	 		case 'register':   
	 		case 'update':     
	 		case 'validate':   
	 		case 'view':       
					 $items[] = array('title' => t('DUDE for'),
                            'value' => format_interval(time() - $user->created),
                             'class' => 'member',
                            );

           return array(t('DUDE') => $items);
		}
}
 

Now, before I go to ld_hack to dereference the above, I know that the user_user hook is calling this as well, so I go to my view user url

?q=user/1/view

to see what gives, and I see.

History

Member for
25 weeks 3 days

LT->DUDE

LD:DUDE for
37 years 10 weeks

contact information

City
orlando
Zip Code
32712

Now, one of the things that is worrying me is my lack of control over the layout...I don't think I will have much--if any--but I don't know that yet.

I also need to specify which of the presented items is the header and the detail, but I don't have view of the original form.

I will get that in the next post

prgrcmpny’s picture

My ld_user('view') hook evolved between posts above. Here is a clarification with some additional modifications to make things clear.

here is the ld_user('view") hook

case 'view':                                                             //The user's account information is being displayed. The module should format its custom additions for display.
					 $items[] = array('title' => t('ITEM TITLE:DUDE'),
                            'value' => format_interval(time() - $user->created),
                             'class' => 'member',
                            );

           return array(t('HEADER TITLE->DUDE') => $items);
		}

and here is the output

History

Member for
25 weeks 3 days

HEADER TITLE->DUDE

ITEM TITLE:DUDE
37 years 10 weeks

contact information

City
orlando
Zip Code
32712

So, we build an array of items each with its caption and its value and then encapsulate that array in another array with a Title for the subsection that will be disiplayed.

Ok, this is making sense.

So, ld_hack needs to :

1. build an array of items to be displayed
2. encase that array in another array with a title.

what I need to do next is identify exactly the structure of the items array....

okey dokey,

progress.

prgrcmpny’s picture

my last post, I left off here...

1. build an array of items to be displayed
2. encase that array in another array with a title.

what I need to do next is identify exactly the structure of the items array....

so, lets do that.

to start, I grepped the modules directory to see what modules implement hook_user..
bash-3.00$ grep -i _user\(

and I get the following list of modules.

  • block
  • blog
  • blogapi
  • comment
  • contact
  • locale
  • node
  • poll
  • profile
  • stats
  • system
  • tracker
  • watchdog

So what I will do next is pull the form structure from each of these and merge them into a form ..note. this will NOT mean that I have ALL the possible items things for the array, but it should help out for now.

t.

prgrcmpny’s picture

here is stage one of my analysis.
only 2 modules (besides user) implement the 'view' case for hook_user.

Here is the breakdown by 'case' passed to the hook...

locale_user 'form','category'
block_user 'form', 'validate'
comment_user 'form'
system_user 'form'
contact_user 'form'
node_user 'delete'
poll_user 'delete'
statistics 'delete'
watchdog 'delete'
profile 'view'
blog 'view'
blogapi does not implement hook_user
tracker does not implement hook_user

some observations.

  • only two modules besides user.module implement the 'view' case
  • the 'form' case is important
  • the 'delete' case is important

next up is the structure of the returned array....

t.

prgrcmpny’s picture

Okey Dokey,

I have found two cases.

The first case is the easy case, it is from the blog_user


    $items[] = array('title' => 'title of this item'
                                 'value' => string, drupal link or external url (technically, a string in all cases.
                                 'class'  => another string
                                )
    return array(t('Header Name') => $items)
    

so we return an array arrays of strings named Header Name??

comes the profile_user case.

I am not going to post the code from the profile.module here, as it will clutter up the post.

However the relevant functions are profile_user, profile_view_profile and profile_view_field.

The answer to this quest is contained in the profile_view_profile function and here is the gist of the matter...

while ($field = db_fetch_object($result)) {
    if ($value = profile_view_field($user, $field)) {
      $description = ($field->visibility == PROFILE_PRIVATE) ? t('.some string.') : '';
      $title = ($field->type != 'checkbox') ? check_plain($field->title) : NULL;
      $item = array('title' => $title,  // work phone, home phone,  etc...
        'value' => $value,                  //string whether 123.456.7890 or drupal url or external url
        'class' => $field->name,        //profile_x name of db field from profile_fields table
      );
      $fields[$field->category][] = $item;  'category of the profile. at our site, it is "Contact Information"
    }
  }
  return $fields;    
}

Translating, what is returned will be an array similar/identical to the simple case array, but which should be more usefull for my needs in ld.module

basically, profile_user('view') will return something like this.

('Contact Information'=>
('title'=>'work phone','value'=>'727.123.4567','class'=>'profile_work_phone')()....)

So for my ld module, I will return

('LD HEADING HERE'=>
('title'=>'work phone','value'=>'727.123.4567','class'=>'profile_work_phone')()....)
etc.

and that answers my question.

hope it helps.

t.