diff --git a/packages/acs-kernel/acs-kernel.info b/packages/acs-kernel/acs-kernel.info
index d48d6d8..79ea97e 100644
--- a/packages/acs-kernel/acs-kernel.info
+++ b/packages/acs-kernel/acs-kernel.info
@@ -7,7 +7,7 @@
t
t
-
+
OpenACS Core Team
Routines and data models providing the foundation for OpenACS-based Web services.
2011-06-12
@@ -15,7 +15,7 @@
The OpenACS kernel contains the core datamodel create and drop scripts for such things as objects, groups, partiies and the supporting PL/SQL and PL/pgSQL procedures.
3
-
+
diff --git a/packages/acs-kernel/sql/oracle/acs-relationships-create.sql b/packages/acs-kernel/sql/oracle/acs-relationships-create.sql
index 3f0645c..98845ee 100644
--- a/packages/acs-kernel/sql/oracle/acs-relationships-create.sql
+++ b/packages/acs-kernel/sql/oracle/acs-relationships-create.sql
@@ -52,6 +52,7 @@ create table acs_rel_types (
max_n_rels_two integer
constraint acs_rel_types_max_n_2_ck
check (max_n_rels_two >= 0),
+ composable_p boolean default 't' not null,
constraint acs_rel_types_n_rels_one_ck
check (min_n_rels_one <= max_n_rels_one),
constraint acs_rel_types_n_rels_two_ck
diff --git a/packages/acs-kernel/sql/oracle/groups-body-create.sql b/packages/acs-kernel/sql/oracle/groups-body-create.sql
index 310e25e..01dd46a 100644
--- a/packages/acs-kernel/sql/oracle/groups-body-create.sql
+++ b/packages/acs-kernel/sql/oracle/groups-body-create.sql
@@ -50,9 +50,10 @@ begin
raise_application_error(-20000,v_error);
end if;
- select object_id_one, object_id_two, rel_type
+ select object_id_one, object_id_two, r.rel_type, composable_p
into v_object_id_one, v_object_id_two, v_rel_type
- from acs_rels
+ from acs_rels r
+ join acs_rel_types t on (r.rel_type = t.rel_type)
where rel_id = :new.rel_id;
-- Insert a row for me in the group_member_index.
@@ -67,23 +68,25 @@ begin
party_approved_member.add(v_object_id_one, v_object_id_two, v_rel_type);
end if;
- -- For all groups of which I am a component, insert a
- -- row in the group_member_index.
- for map in (select distinct group_id
- from group_component_map
- where component_id = v_object_id_one) loop
- insert into group_element_index
- (group_id, element_id, rel_id, container_id,
- rel_type, ancestor_rel_type)
- values
- (map.group_id, v_object_id_two, :new.rel_id, v_object_id_one,
- v_rel_type, 'membership_rel');
-
- if :new.member_state = 'approved' then
- party_approved_member.add(map.group_id, v_object_id_two, v_rel_type);
- end if;
+ if v_composable_p = 't' then
+ -- For all groups of which I am a component, insert a
+ -- row in the group_member_index.
+ for map in (select distinct group_id
+ from group_component_map
+ where component_id = v_object_id_one) loop
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (map.group_id, v_object_id_two, :new.rel_id, v_object_id_one,
+ v_rel_type, 'membership_rel');
+
+ if :new.member_state = 'approved' then
+ party_approved_member.add(map.group_id, v_object_id_two, v_rel_type);
+ end if;
- end loop;
+ end loop;
+ end if;
end;
/
show errors
@@ -157,15 +160,17 @@ begin
party_approved_member.add(v_object_id_one, members.member_id, members.rel_type);
end loop;
- -- Make my elements be elements of my new composite group
+ -- Make my composable elements be elements of my new composite group
insert into group_element_index
(group_id, element_id, rel_id, container_id,
rel_type, ancestor_rel_type)
select distinct
v_object_id_one, element_id, rel_id, container_id,
- rel_type, ancestor_rel_type
+ m.rel_type, ancestor_rel_type
from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
where group_id = v_object_id_two
+ and t.composable_p = 't'
and not exists (select 1
from group_element_map
where group_id = v_object_id_one
@@ -173,7 +178,7 @@ begin
and rel_id = m.rel_id);
-- For all direct or indirect containers of my new composite group,
- -- add me and add my elements
+ -- add me and add my composable elements
for map in (select distinct group_id
from group_component_map
where component_id = v_object_id_one) loop
@@ -186,11 +191,13 @@ begin
(map.group_id, v_object_id_two, :new.rel_id, v_object_id_one,
v_rel_type, 'composition_rel');
- -- Add rows for my elements
+ -- Add rows for my composable elements
for members in (select distinct member_id, rel_type
from group_approved_member_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
where group_id = v_object_id_two
+ and t.composable_p = 't'
and not exists (select 1
from group_element_map
where group_id = map.group_id
@@ -207,7 +214,9 @@ begin
map.group_id, element_id, rel_id, container_id,
rel_type, ancestor_rel_type
from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
where group_id = v_object_id_two
+ and t.composable_p = 't'
and not exists (select 1
from group_element_map
where group_id = map.group_id
diff --git a/packages/acs-kernel/sql/oracle/groups-create.sql b/packages/acs-kernel/sql/oracle/groups-create.sql
index d659cd2..8c0cfb3 100644
--- a/packages/acs-kernel/sql/oracle/groups-create.sql
+++ b/packages/acs-kernel/sql/oracle/groups-create.sql
@@ -89,7 +89,8 @@ begin
object_type_one => 'group', role_one => 'composite',
min_n_rels_one => 0, max_n_rels_one => null,
object_type_two => 'group', role_two => 'component',
- min_n_rels_two => 0, max_n_rels_two => null
+ min_n_rels_two => 0, max_n_rels_two => null,
+ composable_p => 't'
);
--
@@ -107,7 +108,8 @@ begin
object_type_one => 'group',
min_n_rels_one => 0, max_n_rels_one => null,
object_type_two => 'person', role_two => 'member',
- min_n_rels_two => 0, max_n_rels_two => null
+ min_n_rels_two => 0, max_n_rels_two => null,
+ composable_p => 't'
);
acs_rel_type.create_role ('admin', 'Administrator', 'Administrators');
@@ -123,7 +125,8 @@ begin
object_type_one => 'group',
min_n_rels_one => 0, max_n_rels_one => null,
object_type_two => 'person', role_two => 'admin',
- min_n_rels_two => 0, max_n_rels_two => null
+ min_n_rels_two => 0, max_n_rels_two => null,
+ composable_p => 'f'
);
commit;
diff --git a/packages/acs-kernel/sql/oracle/test/rel-segments-test-types-create.sql b/packages/acs-kernel/sql/oracle/test/rel-segments-test-types-create.sql
index 5f88b3d..5fb5823 100644
--- a/packages/acs-kernel/sql/oracle/test/rel-segments-test-types-create.sql
+++ b/packages/acs-kernel/sql/oracle/test/rel-segments-test-types-create.sql
@@ -10,7 +10,8 @@ begin
object_type_one => 'group',
min_n_rels_one => 0, max_n_rels_one => null,
object_type_two => 'party', role_two => 'member',
- min_n_rels_two => 0, max_n_rels_two => null
+ min_n_rels_two => 0, max_n_rels_two => null,
+ composable_p => 't'
);
@@ -25,7 +26,8 @@ begin
object_type_one => 'group',
min_n_rels_one => 0, max_n_rels_one => null,
object_type_two => 'party', role_two => 'member',
- min_n_rels_two => 0, max_n_rels_two => null
+ min_n_rels_two => 0, max_n_rels_two => null,
+ composable_p => 't'
);
end;
/
diff --git a/packages/acs-kernel/sql/oracle/upgrade/upgrade-5.8.0d3-5.8.0d4.sql b/packages/acs-kernel/sql/oracle/upgrade/upgrade-5.8.0d3-5.8.0d4.sql
new file mode 100644
index 0000000..2883df2
--- /dev/null
+++ b/packages/acs-kernel/sql/oracle/upgrade/upgrade-5.8.0d3-5.8.0d4.sql
@@ -0,0 +1,344 @@
+-- add extended attribute to rel types
+alter table acs_rel_types add column composable_p boolean default 't' not null;
+update acs_rel_types set composable_p = 'f' where rel_type = 'admin_rel';
+
+create or replace trigger membership_rels_in_tr
+after insert on membership_rels
+for each row
+declare
+ v_object_id_one acs_rels.object_id_one%TYPE;
+ v_object_id_two acs_rels.object_id_two%TYPE;
+ v_rel_type acs_rels.rel_type%TYPE;
+ v_error varchar2(4000);
+begin
+
+ -- First check if added this relation violated any relational constraints
+ v_error := rel_constraint.violation(:new.rel_id);
+ if v_error is not null then
+ raise_application_error(-20000,v_error);
+ end if;
+
+ select object_id_one, object_id_two, r.rel_type, composable_p
+ into v_object_id_one, v_object_id_two, v_rel_type
+ from acs_rels r
+ join acs_rel_types t on (r.rel_type = t.rel_type)
+ where rel_id = :new.rel_id;
+
+ -- Insert a row for me in the group_member_index.
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (v_object_id_one, v_object_id_two, :new.rel_id, v_object_id_one,
+ v_rel_type, 'membership_rel');
+
+ if :new.member_state = 'approved' then
+ party_approved_member.add(v_object_id_one, v_object_id_two, v_rel_type);
+ end if;
+
+ if v_composable_p = 't' then
+ -- For all groups of which I am a component, insert a
+ -- row in the group_member_index.
+ for map in (select distinct group_id
+ from group_component_map
+ where component_id = v_object_id_one) loop
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (map.group_id, v_object_id_two, :new.rel_id, v_object_id_one,
+ v_rel_type, 'membership_rel');
+
+ if :new.member_state = 'approved' then
+ party_approved_member.add(map.group_id, v_object_id_two, v_rel_type);
+ end if;
+
+ end loop;
+ end if;
+end;
+/
+show errors
+
+create or replace trigger composition_rels_in_tr
+after insert on composition_rels
+for each row
+declare
+ v_object_id_one acs_rels.object_id_one%TYPE;
+ v_object_id_two acs_rels.object_id_two%TYPE;
+ v_rel_type acs_rels.rel_type%TYPE;
+ v_error varchar2(4000);
+begin
+
+ -- First check if added this relation violated any relational constraints
+ v_error := rel_constraint.violation(:new.rel_id);
+ if v_error is not null then
+ raise_application_error(-20000,v_error);
+ end if;
+
+ select object_id_one, object_id_two, rel_type
+ into v_object_id_one, v_object_id_two, v_rel_type
+ from acs_rels
+ where rel_id = :new.rel_id;
+
+ -- Insert a row for me in group_element_index
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (v_object_id_one, v_object_id_two, :new.rel_id, v_object_id_one,
+ v_rel_type, 'composition_rel');
+
+ for members in (select distinct member_id, rel_type
+ from group_approved_member_map m
+ where group_id = v_object_id_two
+ and not exists (select 1
+ from group_element_map
+ where group_id = v_object_id_one
+ and element_id = m.member_id
+ and rel_id = m.rel_id))
+ loop
+ party_approved_member.add(v_object_id_one, members.member_id, members.rel_type);
+ end loop;
+
+ -- Make my composable elements be elements of my new composite group
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ select distinct
+ v_object_id_one, element_id, rel_id, container_id,
+ m.rel_type, ancestor_rel_type
+ from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
+ where group_id = v_object_id_two
+ and t.composable_p = 't'
+ and not exists (select 1
+ from group_element_map
+ where group_id = v_object_id_one
+ and element_id = m.element_id
+ and rel_id = m.rel_id);
+
+ -- For all direct or indirect containers of my new composite group,
+ -- add me and add my composable elements
+ for map in (select distinct group_id
+ from group_component_map
+ where component_id = v_object_id_one) loop
+
+ -- Add a row for me
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (map.group_id, v_object_id_two, :new.rel_id, v_object_id_one,
+ v_rel_type, 'composition_rel');
+
+ -- Add rows for my composable elements
+
+ for members in (select distinct member_id, rel_type
+ from group_approved_member_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
+ where group_id = v_object_id_two
+ and t.composable_p = 't'
+ and not exists (select 1
+ from group_element_map
+ where group_id = map.group_id
+ and element_id = m.member_id
+ and rel_id = m.rel_id))
+ loop
+ party_approved_member.add(map.group_id, members.member_id, members.rel_type);
+ end loop;
+
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ select distinct
+ map.group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type
+ from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
+ where group_id = v_object_id_two
+ and t.composable_p = 't'
+ and not exists (select 1
+ from group_element_map
+ where group_id = map.group_id
+ and element_id = m.element_id
+ and rel_id = m.rel_id);
+ end loop;
+
+end;
+/
+show errors
+
+create or replace package acs_rel_type
+as
+
+ procedure create_role (
+ role in acs_rel_roles.role%TYPE,
+ pretty_name in acs_rel_roles.pretty_name%TYPE default null,
+ pretty_plural in acs_rel_roles.pretty_plural%TYPE default null
+ );
+
+ procedure drop_role (
+ role in acs_rel_roles.role%TYPE
+ );
+
+ function role_pretty_name (
+ role in acs_rel_roles.role%TYPE
+ ) return acs_rel_roles.pretty_name%TYPE;
+
+ function role_pretty_plural (
+ role in acs_rel_roles.role%TYPE
+ ) return acs_rel_roles.pretty_plural%TYPE;
+
+ procedure create_type (
+ rel_type in acs_rel_types.rel_type%TYPE,
+ pretty_name in acs_object_types.pretty_name%TYPE,
+ pretty_plural in acs_object_types.pretty_plural%TYPE,
+ supertype in acs_object_types.supertype%TYPE
+ default 'relationship',
+ table_name in acs_object_types.table_name%TYPE,
+ id_column in acs_object_types.id_column%TYPE,
+ package_name in acs_object_types.package_name%TYPE,
+ abstract_p in acs_object_types.abstract_p%TYPE default 'f',
+ type_extension_table in acs_object_types.type_extension_table%TYPE
+ default null,
+ name_method in acs_object_types.name_method%TYPE default null,
+ object_type_one in acs_rel_types.object_type_one%TYPE,
+ role_one in acs_rel_types.role_one%TYPE default null,
+ min_n_rels_one in acs_rel_types.min_n_rels_one%TYPE,
+ max_n_rels_one in acs_rel_types.max_n_rels_one%TYPE,
+ object_type_two in acs_rel_types.object_type_two%TYPE,
+ role_two in acs_rel_types.role_two%TYPE default null,
+ min_n_rels_two in acs_rel_types.min_n_rels_two%TYPE,
+ max_n_rels_two in acs_rel_types.max_n_rels_two%TYPE,
+ composable_p in acs_rel_types.composable_p%TYPE
+ );
+
+ procedure drop_type (
+ rel_type in acs_rel_types.rel_type%TYPE,
+ cascade_p in char default 'f'
+ );
+
+end acs_rel_type;
+/
+show errors
+
+create or replace package body acs_rel_type
+as
+
+ procedure create_role (
+ role in acs_rel_roles.role%TYPE,
+ pretty_name in acs_rel_roles.pretty_name%TYPE default null,
+ pretty_plural in acs_rel_roles.pretty_plural%TYPE default null
+ )
+ is
+ begin
+ insert into acs_rel_roles
+ (role, pretty_name, pretty_plural)
+ values
+ (create_role.role, nvl(create_role.pretty_name,create_role.role), nvl(create_role.pretty_plural,create_role.role));
+ end;
+
+ procedure drop_role (
+ role in acs_rel_roles.role%TYPE
+ )
+ is
+ begin
+ delete from acs_rel_roles
+ where role = drop_role.role;
+ end;
+
+ function role_pretty_name (
+ role in acs_rel_roles.role%TYPE
+ ) return acs_rel_roles.pretty_name%TYPE
+ is
+ v_pretty_name acs_rel_roles.pretty_name%TYPE;
+ begin
+ select r.pretty_name into v_pretty_name
+ from acs_rel_roles r
+ where r.role = role_pretty_name.role;
+
+ return v_pretty_name;
+ end role_pretty_name;
+
+
+ function role_pretty_plural (
+ role in acs_rel_roles.role%TYPE
+ ) return acs_rel_roles.pretty_plural%TYPE
+ is
+ v_pretty_plural acs_rel_roles.pretty_plural%TYPE;
+ begin
+ select r.pretty_plural into v_pretty_plural
+ from acs_rel_roles r
+ where r.role = role_pretty_plural.role;
+
+ return v_pretty_plural;
+ end role_pretty_plural;
+
+ procedure create_type (
+ rel_type in acs_rel_types.rel_type%TYPE,
+ pretty_name in acs_object_types.pretty_name%TYPE,
+ pretty_plural in acs_object_types.pretty_plural%TYPE,
+ supertype in acs_object_types.supertype%TYPE
+ default 'relationship',
+ table_name in acs_object_types.table_name%TYPE,
+ id_column in acs_object_types.id_column%TYPE,
+ package_name in acs_object_types.package_name%TYPE,
+ abstract_p in acs_object_types.abstract_p%TYPE default 'f',
+ type_extension_table in acs_object_types.type_extension_table%TYPE
+ default null,
+ name_method in acs_object_types.name_method%TYPE default null,
+ object_type_one in acs_rel_types.object_type_one%TYPE,
+ role_one in acs_rel_types.role_one%TYPE default null,
+ min_n_rels_one in acs_rel_types.min_n_rels_one%TYPE,
+ max_n_rels_one in acs_rel_types.max_n_rels_one%TYPE,
+ object_type_two in acs_rel_types.object_type_two%TYPE,
+ role_two in acs_rel_types.role_two%TYPE default null,
+ min_n_rels_two in acs_rel_types.min_n_rels_two%TYPE,
+ max_n_rels_two in acs_rel_types.max_n_rels_two%TYPE,
+ composable_p in acs_rel_types.composable_p%TYPE
+ )
+ is
+ begin
+ acs_object_type.create_type(
+ object_type => rel_type,
+ pretty_name => pretty_name,
+ pretty_plural => pretty_plural,
+ supertype => supertype,
+ table_name => table_name,
+ id_column => id_column,
+ package_name => package_name,
+ abstract_p => abstract_p,
+ type_extension_table => type_extension_table,
+ name_method => name_method
+ );
+
+ insert into acs_rel_types
+ (rel_type,
+ object_type_one, role_one,
+ min_n_rels_one, max_n_rels_one,
+ object_type_two, role_two,
+ min_n_rels_two, max_n_rels_two, composable_p)
+ values
+ (create_type.rel_type,
+ create_type.object_type_one, create_type.role_one,
+ create_type.min_n_rels_one, create_type.max_n_rels_one,
+ create_type.object_type_two, create_type.role_two,
+ create_type.min_n_rels_two, create_type.max_n_rels_two, create_type.composable_p);
+ end;
+
+ procedure drop_type (
+ rel_type in acs_rel_types.rel_type%TYPE,
+ cascade_p in char default 'f'
+ )
+ is
+ begin
+ -- XXX do cascade_p
+ delete from acs_rel_types
+ where acs_rel_types.rel_type = acs_rel_type.drop_type.rel_type;
+
+ acs_object_type.drop_type(acs_rel_type.drop_type.rel_type, acs_rel_type.drop_type.cascade_p);
+ end;
+
+end acs_rel_type;
+/
+show errors
diff --git a/packages/acs-kernel/sql/postgresql/acs-relationships-create.sql b/packages/acs-kernel/sql/postgresql/acs-relationships-create.sql
index b733327..1baf660 100644
--- a/packages/acs-kernel/sql/postgresql/acs-relationships-create.sql
+++ b/packages/acs-kernel/sql/postgresql/acs-relationships-create.sql
@@ -51,7 +51,8 @@ create table acs_rel_types (
constraint acs_rel_types_n_rels_one_ck
check (min_n_rels_one <= max_n_rels_one),
constraint acs_rel_types_n_rels_two_ck
- check (min_n_rels_two <= max_n_rels_two)
+ check (min_n_rels_two <= max_n_rels_two),
+ composable_p boolean default 't' not null
);
create index acs_rel_types_objtypeone_idx on acs_rel_types (object_type_one);
@@ -198,10 +199,10 @@ $$ LANGUAGE plpgsql stable strict;
-- added
-select define_function_args('acs_rel_type__create_type','rel_type,pretty_name,pretty_plural,supertype;relationship,table_name,id_column,package_name,object_type_one,role_one;null,min_n_rels_one,max_n_rels_one,object_type_two,role_two;null,min_n_rels_two,max_n_rels_two');
+select define_function_args('acs_rel_type__create_type','rel_type,pretty_name,pretty_plural,supertype;relationship,table_name,id_column,package_name,object_type_one,role_one;null,min_n_rels_one,max_n_rels_one,object_type_two,role_two;null,min_n_rels_two,max_n_rels_two,composable_p;t');
--
--- procedure acs_rel_type__create_type/15
+-- procedure acs_rel_type__create_type/16
--
CREATE OR REPLACE FUNCTION acs_rel_type__create_type(
create_type__rel_type varchar,
@@ -218,7 +219,8 @@ CREATE OR REPLACE FUNCTION acs_rel_type__create_type(
create_type__object_type_two varchar,
create_type__role_two varchar, -- default null
create_type__min_n_rels_two integer,
- create_type__max_n_rels_two integer
+ create_type__max_n_rels_two integer,
+ create_type__composable_p boolean
) RETURNS integer AS $$
DECLARE
@@ -245,13 +247,15 @@ BEGIN
object_type_one, role_one,
min_n_rels_one, max_n_rels_one,
object_type_two, role_two,
- min_n_rels_two, max_n_rels_two)
+ min_n_rels_two, max_n_rels_two,
+ composable_p)
values
(create_type__rel_type,
create_type__object_type_one, create_type__role_one,
create_type__min_n_rels_one, create_type__max_n_rels_one,
create_type__object_type_two, create_type__role_two,
- create_type__min_n_rels_two, create_type__max_n_rels_two);
+ create_type__min_n_rels_two, create_type__max_n_rels_two,
+ create_type__composable_p);
return 0;
END;
@@ -263,7 +267,7 @@ $$ LANGUAGE plpgsql;
--
--- procedure acs_rel_type__create_type/14
+-- procedure acs_rel_type__create_type/15
--
CREATE OR REPLACE FUNCTION acs_rel_type__create_type(
create_type__rel_type varchar,
@@ -279,7 +283,8 @@ CREATE OR REPLACE FUNCTION acs_rel_type__create_type(
create_type__max_n_rels_one integer,
create_type__object_type_two varchar,
create_type__min_n_rels_two integer,
- create_type__max_n_rels_two integer
+ create_type__max_n_rels_two integer,
+ create_type__composable_p boolean
) RETURNS integer AS $$
DECLARE
@@ -308,13 +313,15 @@ BEGIN
object_type_one, role_one,
min_n_rels_one, max_n_rels_one,
object_type_two, role_two,
- min_n_rels_two, max_n_rels_two)
+ min_n_rels_two, max_n_rels_two,
+ composable_p)
values
(create_type__rel_type,
create_type__object_type_one, create_type__role_one,
create_type__min_n_rels_one, create_type__max_n_rels_one,
create_type__object_type_two, create_type__role_two,
- create_type__min_n_rels_two, create_type__max_n_rels_two);
+ create_type__min_n_rels_two, create_type__max_n_rels_two,
+ create_type__composable_p);
return 0;
END;
diff --git a/packages/acs-kernel/sql/postgresql/groups-body-create.sql b/packages/acs-kernel/sql/postgresql/groups-body-create.sql
index 86b1288..edfe5ae 100644
--- a/packages/acs-kernel/sql/postgresql/groups-body-create.sql
+++ b/packages/acs-kernel/sql/postgresql/groups-body-create.sql
@@ -48,9 +48,10 @@ BEGIN
raise EXCEPTION '-20000: %', v_error;
end if;
- select object_id_one, object_id_two, rel_type
- into v_object_id_one, v_object_id_two, v_rel_type
- from acs_rels
+ select object_id_one, object_id_two, r.rel_type, composable_p
+ into v_object_id_one, v_object_id_two, v_rel_type, v_composable_p
+ from acs_rels r
+ join acs_rel_types t on (r.rel_type = t.rel_type)
where rel_id = new.rel_id;
-- Insert a row for me in the group_element_index.
@@ -65,26 +66,29 @@ BEGIN
perform party_approved_member__add(v_object_id_one, v_object_id_two, new.rel_id, v_rel_type);
end if;
- -- For all groups of which I am a component, insert a
- -- row in the group_element_index.
- for map in select distinct group_id
+ -- If this rel_type composable...
+ if v_composable_p = 't' then
+
+ -- For all groups of which I am a component, insert a
+ -- row in the group_element_index.
+ for map in select distinct group_id
from group_component_map
where component_id = v_object_id_one
- loop
-
- insert into group_element_index
- (group_id, element_id, rel_id, container_id,
- rel_type, ancestor_rel_type)
- values
- (map.group_id, v_object_id_two, new.rel_id, v_object_id_one,
- v_rel_type, 'membership_rel');
+ loop
- if new.member_state = 'approved' then
- perform party_approved_member__add(map.group_id, v_object_id_two, new.rel_id, v_rel_type);
- end if;
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (map.group_id, v_object_id_two, new.rel_id, v_object_id_one,
+ v_rel_type, 'membership_rel');
- end loop;
+ if new.member_state = 'approved' then
+ perform party_approved_member__add(map.group_id, v_object_id_two, new.rel_id, v_rel_type);
+ end if;
+ end loop;
+ end if;
return new;
END;
@@ -214,15 +218,17 @@ BEGIN
and element_id = m.member_id
and rel_id = m.rel_id);
- -- Make my elements be elements of my new composite group
+ -- Make my composable elements be elements of my new composite group
insert into group_element_index
(group_id, element_id, rel_id, container_id,
rel_type, ancestor_rel_type)
select distinct
v_object_id_one, element_id, rel_id, container_id,
- rel_type, ancestor_rel_type
+ m.rel_type, ancestor_rel_type
from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
where group_id = v_object_id_two
+ and t.composable_p = 't'
and not exists (select 1
from group_element_map
where group_id = v_object_id_one
@@ -247,25 +253,29 @@ BEGIN
-- Add to party_approved_member_map
- perform party_approved_member__add(map.group_id, member_id, rel_id, rel_type)
+ perform party_approved_member__add(map.group_id, member_id, rel_id, m.rel_type)
from group_approved_member_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
where group_id = v_object_id_two
+ and t.composable_p = 't'
and not exists (select 1
from group_element_map
where group_id = map.group_id
and element_id = m.member_id
and rel_id = m.rel_id);
- -- Add rows for my elements
+ -- Add rows for my composable elements
insert into group_element_index
(group_id, element_id, rel_id, container_id,
rel_type, ancestor_rel_type)
select distinct
map.group_id, element_id, rel_id, container_id,
- rel_type, ancestor_rel_type
+ m.rel_type, ancestor_rel_type
from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
where group_id = v_object_id_two
+ and t.composable_p = 't'
and not exists (select 1
from group_element_map
where group_id = map.group_id
diff --git a/packages/acs-kernel/sql/postgresql/groups-create.sql b/packages/acs-kernel/sql/postgresql/groups-create.sql
index ab77ba5..eccffed 100644
--- a/packages/acs-kernel/sql/postgresql/groups-create.sql
+++ b/packages/acs-kernel/sql/postgresql/groups-create.sql
@@ -108,7 +108,8 @@ BEGIN
'group',
'component',
0,
- null
+ null,
+ 't'
);
@@ -132,7 +133,8 @@ BEGIN
'person', -- object_type_two
'member', -- role_two
0, -- min_n_rels_two
- null -- max_n_rels_two
+ null, -- max_n_rels_two
+ 't'
);
--
@@ -155,7 +157,8 @@ BEGIN
'person', -- object_type_two
'admin', -- role_two
0, -- min_n_rels_two
- null -- max_n_rels_two
+ null, -- max_n_rels_two
+ 'f'
);
return 0;
diff --git a/packages/acs-kernel/sql/postgresql/test/rel-segments-test-types-create.sql b/packages/acs-kernel/sql/postgresql/test/rel-segments-test-types-create.sql
index 68ba261..316022d 100644
--- a/packages/acs-kernel/sql/postgresql/test/rel-segments-test-types-create.sql
+++ b/packages/acs-kernel/sql/postgresql/test/rel-segments-test-types-create.sql
@@ -16,7 +16,8 @@ BEGIN
'party',
'member',
0,
- null
+ null,
+ 't'
);
@@ -35,7 +36,8 @@ BEGIN
'party',
'member',
0,
- null
+ null,
+ 't'
);
return null;
diff --git a/packages/acs-kernel/sql/postgresql/upgrade/upgrade-5.8.0d3-5.8.0d4.sql b/packages/acs-kernel/sql/postgresql/upgrade/upgrade-5.8.0d3-5.8.0d4.sql
new file mode 100644
index 0000000..40793f7
--- /dev/null
+++ b/packages/acs-kernel/sql/postgresql/upgrade/upgrade-5.8.0d3-5.8.0d4.sql
@@ -0,0 +1,329 @@
+-- add extended attribute to rel types
+alter table acs_rel_types add column composable_p boolean default 't' not null;
+update acs_rel_types set composable_p = 'f' where rel_type = 'admin_rel';
+
+drop trigger membership_rels_in_tr on membership_rels;
+drop function membership_rels_in_tr ();
+
+
+
+--
+-- procedure membership_rels_in_tr/0
+--
+CREATE OR REPLACE FUNCTION membership_rels_in_tr(
+
+) RETURNS trigger AS $$
+DECLARE
+ v_object_id_one acs_rels.object_id_one%TYPE;
+ v_object_id_two acs_rels.object_id_two%TYPE;
+ v_rel_type acs_rels.rel_type%TYPE;
+ v_composable_p acs_rel_types.composable_p%TYPE;
+ v_error text;
+ map record;
+BEGIN
+
+ -- First check if added this relation violated any relational constraints
+ v_error := rel_constraint__violation(new.rel_id);
+ if v_error is not null then
+ raise EXCEPTION '-20000: %', v_error;
+ end if;
+
+ select object_id_one, object_id_two, r.rel_type, composable_p
+ into v_object_id_one, v_object_id_two, v_rel_type, v_composable_p
+ from acs_rels r
+ join acs_rel_types t on (r.rel_type = t.rel_type)
+ where rel_id = new.rel_id;
+
+ -- Insert a row for me in the group_element_index.
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (v_object_id_one, v_object_id_two, new.rel_id, v_object_id_one,
+ v_rel_type, 'membership_rel');
+
+ if new.member_state = 'approved' then
+ perform party_approved_member__add(v_object_id_one, v_object_id_two, new.rel_id, v_rel_type);
+ end if;
+
+ -- If this rel_type composable...
+ if v_composable_p = 't' then
+
+ -- For all groups of which I am a component, insert a
+ -- row in the group_element_index.
+ for map in select distinct group_id
+ from group_component_map
+ where component_id = v_object_id_one
+ loop
+
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (map.group_id, v_object_id_two, new.rel_id, v_object_id_one,
+ v_rel_type, 'membership_rel');
+
+ if new.member_state = 'approved' then
+ perform party_approved_member__add(map.group_id, v_object_id_two, new.rel_id, v_rel_type);
+ end if;
+
+ end loop;
+ end if;
+ return new;
+
+END;
+$$ LANGUAGE plpgsql;
+
+create trigger membership_rels_in_tr after insert on membership_rels
+for each row execute procedure membership_rels_in_tr ();
+
+drop trigger composition_rels_in_tr on composition_rels;
+drop function composition_rels_in_tr ();
+
+
+
+--
+-- procedure composition_rels_in_tr/0
+--
+CREATE OR REPLACE FUNCTION composition_rels_in_tr(
+
+) RETURNS trigger AS $$
+DECLARE
+ v_object_id_one acs_rels.object_id_one%TYPE;
+ v_object_id_two acs_rels.object_id_two%TYPE;
+ v_rel_type acs_rels.rel_type%TYPE;
+ v_error text;
+ map record;
+BEGIN
+
+ -- First check if added this relation violated any relational constraints
+ v_error := rel_constraint__violation(new.rel_id);
+
+ if v_error is not null then
+ raise EXCEPTION '-20000: %', v_error;
+ end if;
+
+ select object_id_one, object_id_two, rel_type
+ into v_object_id_one, v_object_id_two, v_rel_type
+ from acs_rels
+ where rel_id = new.rel_id;
+
+ -- Insert a row for me in group_element_index
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (v_object_id_one, v_object_id_two, new.rel_id, v_object_id_one,
+ v_rel_type, 'composition_rel');
+
+ -- Add to the denormalized party_approved_member_map
+
+ perform party_approved_member__add(v_object_id_one, member_id, rel_id, rel_type)
+ from group_approved_member_map m
+ where group_id = v_object_id_two
+ and not exists (select 1
+ from group_element_map
+ where group_id = v_object_id_one
+ and element_id = m.member_id
+ and rel_id = m.rel_id);
+
+ -- Make my composable elements be elements of my new composite group
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ select distinct
+ v_object_id_one, element_id, rel_id, container_id,
+ m.rel_type, ancestor_rel_type
+ from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
+ where group_id = v_object_id_two
+ and t.composable_p = 't'
+ and not exists (select 1
+ from group_element_map
+ where group_id = v_object_id_one
+ and element_id = m.element_id
+ and rel_id = m.rel_id);
+
+ -- For all direct or indirect containers of my new composite group,
+ -- add me and add my elements
+ for map in select distinct group_id
+ from group_component_map
+ where component_id = v_object_id_one
+ LOOP
+
+ -- Add a row for me
+
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ values
+ (map.group_id, v_object_id_two, new.rel_id, v_object_id_one,
+ v_rel_type, 'composition_rel');
+
+ -- Add to party_approved_member_map
+
+ perform party_approved_member__add(map.group_id, member_id, rel_id, m.rel_type)
+ from group_approved_member_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
+ where group_id = v_object_id_two
+ and t.composable_p = 't'
+ and not exists (select 1
+ from group_element_map
+ where group_id = map.group_id
+ and element_id = m.member_id
+ and rel_id = m.rel_id);
+
+ -- Add rows for my composable elements
+
+ insert into group_element_index
+ (group_id, element_id, rel_id, container_id,
+ rel_type, ancestor_rel_type)
+ select distinct
+ map.group_id, element_id, rel_id, container_id,
+ m.rel_type, ancestor_rel_type
+ from group_element_map m
+ join acs_rel_types t on (m.rel_type = t.rel_type)
+ where group_id = v_object_id_two
+ and t.composable_p = 't'
+ and not exists (select 1
+ from group_element_map
+ where group_id = map.group_id
+ and element_id = m.element_id
+ and rel_id = m.rel_id);
+ end loop;
+
+ return new;
+
+END;
+$$ LANGUAGE plpgsql;
+
+create trigger composition_rels_in_tr after insert on composition_rels
+for each row execute procedure composition_rels_in_tr ();
+
+select define_function_args('acs_rel_type__create_type','rel_type,pretty_name,pretty_plural,supertype;relationship,table_name,id_column,package_name,object_type_one,role_one;null,min_n_rels_one,max_n_rels_one,object_type_two,role_two;null,min_n_rels_two,max_n_rels_two,composable_p;t');
+
+--
+-- procedure acs_rel_type__create_type/16
+--
+CREATE OR REPLACE FUNCTION acs_rel_type__create_type(
+ create_type__rel_type varchar,
+ create_type__pretty_name varchar,
+ create_type__pretty_plural varchar,
+ create_type__supertype varchar, -- default 'relationship'
+ create_type__table_name varchar,
+ create_type__id_column varchar,
+ create_type__package_name varchar,
+ create_type__object_type_one varchar,
+ create_type__role_one varchar, -- default null
+ create_type__min_n_rels_one integer,
+ create_type__max_n_rels_one integer,
+ create_type__object_type_two varchar,
+ create_type__role_two varchar, -- default null
+ create_type__min_n_rels_two integer,
+ create_type__max_n_rels_two integer,
+ create_type__composable_p boolean
+
+) RETURNS integer AS $$
+DECLARE
+
+ type_extension_table acs_object_types.type_extension_table%TYPE default null;
+ abstract_p acs_object_types.abstract_p%TYPE default 'f';
+ name_method acs_object_types.name_method%TYPE default null;
+BEGIN
+ PERFORM acs_object_type__create_type(
+ create_type__rel_type,
+ create_type__pretty_name,
+ create_type__pretty_plural,
+ create_type__supertype,
+ create_type__table_name,
+ create_type__id_column,
+ create_type__package_name,
+ abstract_p,
+ type_extension_table,
+ name_method
+ );
+
+ insert into acs_rel_types
+ (rel_type,
+ object_type_one, role_one,
+ min_n_rels_one, max_n_rels_one,
+ object_type_two, role_two,
+ min_n_rels_two, max_n_rels_two,
+ composable_p)
+ values
+ (create_type__rel_type,
+ create_type__object_type_one, create_type__role_one,
+ create_type__min_n_rels_one, create_type__max_n_rels_one,
+ create_type__object_type_two, create_type__role_two,
+ create_type__min_n_rels_two, create_type__max_n_rels_two,
+ create_type__composable_p);
+
+ return 0;
+END;
+$$ LANGUAGE plpgsql;
+
+
+
+-- procedure create_type
+
+
+--
+-- procedure acs_rel_type__create_type/15
+--
+CREATE OR REPLACE FUNCTION acs_rel_type__create_type(
+ create_type__rel_type varchar,
+ create_type__pretty_name varchar,
+ create_type__pretty_plural varchar,
+ create_type__supertype varchar, -- default 'relationship'
+ create_type__table_name varchar,
+ create_type__id_column varchar,
+ create_type__package_name varchar,
+ create_type__type_extension_table varchar, -- default null
+ create_type__object_type_one varchar,
+ create_type__min_n_rels_one integer,
+ create_type__max_n_rels_one integer,
+ create_type__object_type_two varchar,
+ create_type__min_n_rels_two integer,
+ create_type__max_n_rels_two integer,
+ create_type__composable_p boolean
+
+) RETURNS integer AS $$
+DECLARE
+
+ abstract_p acs_object_types.abstract_p%TYPE default 'f';
+ name_method acs_object_types.name_method%TYPE default null;
+ create_type__role_one acs_rel_types.role_one%TYPE default null;
+ create_type__role_two acs_rel_types.role_two%TYPE default null;
+BEGIN
+
+ PERFORM acs_object_type__create_type(
+ create_type__rel_type,
+ create_type__pretty_name,
+ create_type__pretty_plural,
+ create_type__supertype,
+ create_type__table_name,
+ create_type__id_column,
+ create_type__package_name,
+ abstract_p,
+ create_type__type_extension_table,
+ name_method
+ );
+
+ insert into acs_rel_types
+ (rel_type,
+ object_type_one, role_one,
+ min_n_rels_one, max_n_rels_one,
+ object_type_two, role_two,
+ min_n_rels_two, max_n_rels_two,
+ composable_p)
+ values
+ (create_type__rel_type,
+ create_type__object_type_one, create_type__role_one,
+ create_type__min_n_rels_one, create_type__max_n_rels_one,
+ create_type__object_type_two, create_type__role_two,
+ create_type__min_n_rels_two, create_type__max_n_rels_two,
+ create_type__composable_p);
+
+ return 0;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/packages/acs-kernel/sql/test/rel-segments-test-types-create.sql b/packages/acs-kernel/sql/test/rel-segments-test-types-create.sql
index 3181bae..b1d2777 100644
--- a/packages/acs-kernel/sql/test/rel-segments-test-types-create.sql
+++ b/packages/acs-kernel/sql/test/rel-segments-test-types-create.sql
@@ -17,7 +17,8 @@ begin
''party'',
''member'',
0,
- null
+ null,
+ ''f''
);
@@ -36,7 +37,8 @@ begin
''party'',
''member'',
0,
- null
+ null,
+ ''f''
);
return null;
diff --git a/packages/acs-subsite/sql/oracle/email-image.sql b/packages/acs-subsite/sql/oracle/email-image.sql
index 51a837c..2680254 100644
--- a/packages/acs-subsite/sql/oracle/email-image.sql
+++ b/packages/acs-subsite/sql/oracle/email-image.sql
@@ -26,7 +26,8 @@ begin
max_n_rels_one => 1,
object_type_two => 'content_item',
min_n_rels_two => 0,
- max_n_rels_two => 1
+ max_n_rels_two => 1,
+ composable_p => 'f'
);
commit;
diff --git a/packages/acs-subsite/sql/oracle/portraits.sql b/packages/acs-subsite/sql/oracle/portraits.sql
index 9342aaa..d49c21c 100644
--- a/packages/acs-subsite/sql/oracle/portraits.sql
+++ b/packages/acs-subsite/sql/oracle/portraits.sql
@@ -29,7 +29,8 @@ begin
max_n_rels_one => 1,
object_type_two => 'content_item',
min_n_rels_two => 0,
- max_n_rels_two => 1
+ max_n_rels_two => 1,
+ composable_p => 'f'
);
commit;
diff --git a/packages/acs-subsite/sql/oracle/user-profiles-create.sql b/packages/acs-subsite/sql/oracle/user-profiles-create.sql
index 2f6b36a..4accec3 100644
--- a/packages/acs-subsite/sql/oracle/user-profiles-create.sql
+++ b/packages/acs-subsite/sql/oracle/user-profiles-create.sql
@@ -35,7 +35,8 @@ begin
object_type_two => 'user',
role_two => 'user',
min_n_rels_two => 0,
- max_n_rels_two => null
+ max_n_rels_two => null,
+ composable_p => 'f'
);
end;
diff --git a/packages/acs-subsite/sql/postgresql/email-image.sql b/packages/acs-subsite/sql/postgresql/email-image.sql
index 5842164..37eefd5 100644
--- a/packages/acs-subsite/sql/postgresql/email-image.sql
+++ b/packages/acs-subsite/sql/postgresql/email-image.sql
@@ -30,7 +30,8 @@ begin
''content_item'',
null,
0,
- 1
+ 1,
+ ''f''
);
return 0;
diff --git a/packages/acs-subsite/sql/postgresql/portraits.sql b/packages/acs-subsite/sql/postgresql/portraits.sql
index d6d9f74..eb49371 100644
--- a/packages/acs-subsite/sql/postgresql/portraits.sql
+++ b/packages/acs-subsite/sql/postgresql/portraits.sql
@@ -58,7 +58,8 @@ begin
''content_item'',
null,
0,
- 1
+ 1,
+ ''f''
);
return 0;
diff --git a/packages/acs-subsite/sql/postgresql/user-profiles-create.sql b/packages/acs-subsite/sql/postgresql/user-profiles-create.sql
index bdf5f15..a6c870c 100644
--- a/packages/acs-subsite/sql/postgresql/user-profiles-create.sql
+++ b/packages/acs-subsite/sql/postgresql/user-profiles-create.sql
@@ -66,7 +66,8 @@ begin
''user'',
''user'',
0,
- null
+ null,
+ ''f''
);
return 0;
diff --git a/packages/acs-subsite/tcl/rel-types-procs-oracle.xql b/packages/acs-subsite/tcl/rel-types-procs-oracle.xql
index 99be86e..94b1701 100644
--- a/packages/acs-subsite/tcl/rel-types-procs-oracle.xql
+++ b/packages/acs-subsite/tcl/rel-types-procs-oracle.xql
@@ -40,7 +40,8 @@ begin acs_rel_type.drop_type(:rel_type); end;
object_type_two => :object_type_two,
role_two => :role_two,
min_n_rels_two => :min_n_rels_two,
- max_n_rels_two => :max_n_rels_two
+ max_n_rels_two => :max_n_rels_two,
+ composable_p => :composable_p
);
end;
diff --git a/packages/acs-subsite/tcl/rel-types-procs-postgresql.xql b/packages/acs-subsite/tcl/rel-types-procs-postgresql.xql
index b23e25d..996e120 100644
--- a/packages/acs-subsite/tcl/rel-types-procs-postgresql.xql
+++ b/packages/acs-subsite/tcl/rel-types-procs-postgresql.xql
@@ -54,7 +54,8 @@ select acs_rel_type__create_type (
:object_type_two,
:role_two,
:min_n_rels_two,
- :max_n_rels_two
+ :max_n_rels_two,
+ :composable_p
);
diff --git a/packages/acs-subsite/tcl/rel-types-procs.tcl b/packages/acs-subsite/tcl/rel-types-procs.tcl
index 040bc72..25fb9ce 100644
--- a/packages/acs-subsite/tcl/rel-types-procs.tcl
+++ b/packages/acs-subsite/tcl/rel-types-procs.tcl
@@ -109,6 +109,7 @@ namespace eval rel_types {
object_type_two
min_n_rels_two
max_n_rels_two
+ {composable_p "t"}
} {
Creates a new relationship type named rel_type
diff --git a/packages/acs-subsite/tcl/test/acs-subsite-procs.tcl b/packages/acs-subsite/tcl/test/acs-subsite-procs.tcl
index 99f2536..04fd312 100644
--- a/packages/acs-subsite/tcl/test/acs-subsite-procs.tcl
+++ b/packages/acs-subsite/tcl/test/acs-subsite-procs.tcl
@@ -104,3 +104,84 @@ aa_register_case -cats smoke acs_subsite_unregistered_visitor {
and g.group_id <> a.object_id
and a.name = 'the_pubic'" -default 0] 0
}
+
+aa_register_case -cats smoke acs_subsite_check_composite_group {
+ Build a 3-level hierachy of composite groups and check memberships. This test case covers the membership and composition rel insertion triggers and composability of basic membership and admin rels.
+
+ @author Michael Steigman
+} {
+
+ aa_run_with_teardown \
+ -rollback \
+ -test_code {
+
+ db_transaction {
+ # create groups and relate them to one another
+ set level_1_group [group::new -group_name "Level 1 Group"]
+ set level_2_group [group::new -group_name "Level 2 Group"]
+ relation_add composition_rel $level_1_group $level_2_group
+
+ array set user_1 [auth::create_user \
+ -username "__test1" \
+ -email "__user1@test.test" \
+ -first_names "__user1.Test first" \
+ -last_name "__user1.Test last" \
+ -password 1 \
+ -password_confirm 1]
+ set user_1_id $user_1(user_id)
+
+ array set user_2 [auth::create_user \
+ -username "__test2" \
+ -email "__user2@test.test" \
+ -first_names "__user2.Test first" \
+ -last_name "__user2.Test last" \
+ -password 1 \
+ -password_confirm 1]
+ set user_2_id $user_2(user_id)
+
+ group::add_member -group_id $level_2_group -user_id $user_1_id -rel_type membership_rel
+ group::add_member -group_id $level_2_group -user_id $user_1_id -rel_type admin_rel
+
+ # check that user_1 is a member of level_1_group but not admin
+ aa_true "User 1 is a member of Level 1 Group" [db_0or1row member_p {
+ SELECT 1
+ FROM group_member_map
+ WHERE group_id = :level_1_group
+ AND member_id = :user_1_id
+ AND rel_type = 'membership_rel'
+ }]
+
+ aa_false "User 1 is not an admin of Level 1 Group" [db_0or1row member_p {
+ SELECT 1
+ FROM group_member_map
+ WHERE group_id = :level_1_group
+ AND member_id = :user_1_id
+ AND rel_type = 'admin_rel'
+ }]
+
+ # create new group then relate it to level_2_group
+ set level_3_group [group::new -group_name "Level 3 Group"]
+ group::add_member -group_id $level_3_group -user_id $user_2(user_id) -rel_type membership_rel
+ group::add_member -group_id $level_3_group -user_id $user_2(user_id) -rel_type admin_rel
+ relation_add composition_rel $level_2_group $level_3_group
+
+ # check that user_2 is a member of level_1_group but not admin
+ aa_true "User 2 is a member of Level 1 Group" [db_0or1row member_p {
+ SELECT 1
+ FROM group_member_map
+ WHERE group_id = :level_1_group
+ AND member_id = :user_2_id
+ AND rel_type = 'membership_rel'
+ }]
+
+ aa_false "User 2 is not an admin of Level 1 Group" [db_0or1row member_p {
+ SELECT 1
+ FROM group_member_map
+ WHERE group_id = :level_1_group
+ AND member_id = :user_2_id
+ AND rel_type = 'admin_rel'
+ }]
+
+ }
+ }
+}
\ No newline at end of file