I've been using some of the functions available in CiviNode for a custom module I'm building. I ran into a permission issue the other day, and I found myself having trouble displaying an error message to the screen. For my project, Anonymous users do not have access to View All Contacts yet I want to display my "Board of Directors" group on a page in my Drupal site for everyone to view.
Here's my code:

$list = civinode_util_group_contacts($group_id, $start = 0, $num_recs = 0);

  foreach($list as $member) {        
    $contact = civinode_util_load_cdata($member, $pid = 5);  

A call to civinode_util_group_contacts() returned an array of NULL values when an Anonymous user views my "Board of Directors" page. Maybe it should not do that, but with an array of NULL values and no error message it's not easy to write a clean check of the $list array before running a foreach.

So I start my foreach and loop through these NULL values. Passing a NULL to civinode_util_load_cdata() returns an error object with two error messages:

[message] => 22 contacts matching input params.
[message] => You do not have permission to access group with id:

Both are listed as Fatal errors. I'm not sure why the first one is an error, but what I really want to grab and display on the screen is the second one about permissions.

I noticed a couple of things in civinode_utils.inc.

1. In function &_civinode_get_last_error(), the last error is assigned to $lastError on line 185, but the function returns variable $last_error.

  //For now, let's see how "deep the rabbit hole goes" and
  //return just the tip of the stack.
  if ($error_stack->hasErrors()) {
    $errors = $error_stack->getErrors();
    $lastError = array_pop($errors);
  }
  
  return $last_error;
}

2. function &_civinode_get_last_error() does what I want (if the variable name is changed to $last_error), but I think it would be better if I could use civinode_get_last_error(). Like so:

$contact = civinode_util_load_cdata($member, $pid = 5);
if(civinode_check_error($contact)) {
      $err = civinode_get_last_error();      
      print $err['message'];  // just keeping it simple here  
}

In order for this code to work above requires some changes to civinode_check_error()

From:
(starts after the //Docs are wrong comment)

  if($obj and is_a($obj, 'CRM_Core_Error')){
    $msg = $obj->_errors[0]['message'];
    _civinode_set_error($msg);    
    if (!$log_error)

to

  if($obj and is_a($obj, 'CRM_Core_Error')){
    _civinode_set_error(_civinode_get_last_error());
    if (!$log_error)

I wasn't sure how these changes might affect the rest of the module, so I thought I'd bring it up here first

CommentFileSizeAuthor
#2 issue-157103-0.patch4.47 KBTorenware

Comments

Torenware’s picture

robeano,

Some good catches on your part, first of all.

I'm going to do this in stages. First, try this code for civinode_util_group_contacts() instead:

/**
 * Wrapper for getting back an array of contact ids.
 *
 * @ingroup CiviCRM_Groups
 * @param int $gid CiviCRM group ID
 * @param int $start index of first record 
 * @param int $num_recs number of records to pull. 0 is "all records"
 *
 */
function civinode_util_group_contacts($gid, $start = 0, $num_recs = 0){
  if (!civinode_check_init()){
    watchdog('CRM', t('CiviCRM must be installed for CiviNode'), WATCHDOG_ERROR);
    return NULL;
  }
  $groups = array( $gid => 1);
  $params = array( 'group' => $groups );
  $returnProperties = array('contact_id' => 1);
  $limit = $num_recs ? $num_recs : 1000; 
  $contacts = crm_contact_search($params, $returnProperties, NULL, $start, $limit);
  $list = array();
  foreach($contacts[0] as $contact){
    $list[] = $contact['contact_id'];
  }
  return $list;
}

This code *should* allow you to do what you want: show contacts to the anonymous user, as long as you've set up permissions so that the anonymous user has access rights to the group. This code also works for smart groups, which the old code never did. It also should not return an error, since if a group does not exist, it will simply return no contacts.

If it fails, it's a permissions issue within CiviCRM itself, and you or I will need to log an issue in JIRA against 1.7.

I'm looking at the rest of your issues now as well, and will add a comment when I'm through them.

Torenware’s picture

Assigned: Unassigned » Torenware
Status: Active » Needs review
StatusFileSize
new4.47 KB

As for the error handling related issues:

1) You're completely correct about the $lastError and $last_error business. Fixed.

2) I like the fact that civinode_get_last_error() returns a string, but I see your point. So here's what I propose:

  • I'm accepting your code to change civinode_check_error() to save away the error object itself, rather than just the message.
  • civinode_get_last_error() now extracts the string itself, so it behaves the same way it should have before
  • I've added a new API call, civinode_get_last_error_object(), which will give you back the raw error object. Note that its structure is a little more complex than your sample code suggests. I suspect you really want to get back $err_obj->_errors[0] if I understand your example. But the new call will give you all the info you need to do something more informative.

I'm posting this as a patch. Try it out, and if it works for you, I'll check it in and create a new packages tag for it.

robeano’s picture

Thanks Tokenware!

The new civinode_util_group_contacts() works great.

To respond to your second post, specifically #2), I don't actually need the object either and I like the idea of civinode_get_last_error() returning a string too. Thanks for taking it a step further.

The new civinode_get_last_error_object() is a good addition, but I'll probably just use your new civinode_get_last_error(). All I wanted was to make sure I could capture the permission error not just the first error returned.

I'm trying your patch now.

robeano’s picture

Oops I mean Torenware! Sorry about that. :)

Torenware’s picture

Status: Needs review » Fixed

I was mostly worried about the change to the groups code (due to the bug you found, the old error reporting code clearly never worked anyway), so I'm going to complete the check-in.

Thanks for giving the code a good looking over. I've generated a 5.x-1.1 version, and it should be up in a couple of minutes.

Rob

robeano’s picture

Status: Fixed » Closed (fixed)

Patch works well. Thanks for updating the code so quickly.