Forum OpenACS Development: Response to Group definitions and use

Collapse
Posted by Andrew Piskorski on
Nick, there are several obvious ways of creating ACS users and groups which work, but which don't do everything the ACS expects. For example, you need to use acs.add_user(), not acs_user.new(). Naturally, the differences between these various functions are not documented. Your problem is probably that you did the obvious thing, which left out some fiddly step that the ACS assumes was done.

Your best bet is to look carefully at the existing ACS login and admin pages that create users and groups, and do it the same way.

But for comparison, here's some Oracle PL/SQL code that I used in testing an ACS 4.2 site. If I remember correctly, this code creates users and groups exactly the same way as the ACS 4.2 UI pages do, and it also expunges them from the system cleanly and completely. (I don't know if any of this low level user/group stuff has been changed for OpenACS 4, but I suspect not.)

Creates a test user and group:

declare
  v_group_id    integer;
  v_person_id   integer;
  v_rel_id      integer;
begin
  -- Create company A:
  v_group_id := p_account.new(
     v_account_code => 'A'
    ,v_context_id => p_const.package_id_is
  );

  v_person_id := acs.add_user (
    email       => 'x@a.com',
    first_names => 'X',
    last_name   => 'X',
    password    => 'X',
    salt        => 'X'
  );

  -- add X into A
  v_rel_id := membership_rel.new(
     object_id_one => v_group_id
    ,object_id_two => v_person_id
  );
end; 
/
show errors

Deletes the test user and group:

(Now, why this calls acs_group.delete instead of p_account.delete, I don't know. Probably that's a bug.)

declare
  test_users    pl_list.integer_list;
  test_groups   pl_list.integer_list;
  test_parties  pl_list.integer_list;
begin
  select party_id  bulk collect into test_users
  from parties
  where email in ('x@a.com', 'y@b.com') ;

  select party_id  bulk collect into test_groups
  from parties
  where party_id in (
    select account_id  from p_accounts
    where account_code in ('A', 'B')
  );

  select party_id  bulk collect into test_parties
  from parties
  where email in ('x@a.com', 'y@b.com')
  or party_id in (
    select account_id  from p_accounts
    where account_code in ('A', 'B')
  );

  for i in test_groups.first .. test_groups.last loop
    acs_group.delete(test_groups(i));

    -- Note that deleting the group also deletes the membership_rels for
    -- the group, so we don't have to do it explicitly - imagine that!
  end loop;
 
  for i in test_users.first .. test_users.last loop
    for j in (
      select rel_id  from acs_rels
      where rel_type = 'membership_rel'
        and object_id_two = test_users(i)
    ) loop
      membership_rel.delete(j.rel_id);
    end loop;

    delete from acs_permissions p
    where p.object_id  = test_users(i)
       or p.grantee_id = test_users(i) ;
    
    acs_user.delete(test_users(i));
    -- Note that acs.remove_user simply delets the row from the users table,
    -- which is not what we want!
  end loop;
end;
/
show errors

Some other stuff:

Of course, I'd extended the ACS groups table with a "p_accounts" table, so here's the part that actually calls the ACS group create function:

create or replace package body p_account
is


function new (
   v_account_code         in p_accounts.account_code%TYPE
  -- AKA group_name:      
  ,v_account_pretty_name  in groups.group_name%TYPE  default null
  ,v_allow_auto_join_p    in p_accounts.allow_auto_join_p%TYPE  default 't'
  ,v_account_id           in p_accounts.account_id%TYPE default null
  ,v_creation_date        in acs_objects.creation_date%TYPE  default sysdate
  ,v_creation_user        in acs_objects.creation_user%TYPE  default null
  ,v_creation_ip          in acs_objects.creation_ip%TYPE    default null
  ,v_context_id           in acs_objects.context_id%TYPE     default null 
) return p_accounts.account_id%TYPE
is
  v_id  integer;
  w_account_pretty_name  groups.group_name%TYPE;
begin
  if v_account_pretty_name is null then
    w_account_pretty_name := v_account_code;
  else
    w_account_pretty_name := v_account_pretty_name;
  end if;

  v_id := acs_group.new(
     group_id       => v_id
    ,object_type    => 'p_account'
    ,creation_date  => v_creation_date
    ,creation_user  => v_creation_user
    ,creation_ip    => v_creation_ip
    ,context_id     => v_context_id
    ,group_name     => w_account_pretty_name
  );

  insert into p_accounts (
    account_id, account_code, address_id
    ,allow_auto_join_p
  ) values (
    v_id, v_account_code, null
    ,v_allow_auto_join_p
  );

  return v_id;
end new;


procedure delete (
  v_account_id  in p_accounts.account_id%TYPE
)
is
begin
  -- Our application logic says that any address mapped directly to the
  -- account belongs ONLY to that account, so if we're deleting the account
  -- delete the address too:
  delete from p_addresses  where address_id = (
    select address_id  from p_accounts
    where account_id = v_account_id
  );

  delete from p_accounts where account_id = v_account_id;

  delete from acs_permissions p
  where p.object_id  = v_account_id
     or p.grantee_id = v_account_id ;

  acs_group.delete(v_account_id);
end delete;


end p_account;
/
show errors