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