add.tcl

Add a member to a group. If there are subtypes of the specified rel_type, we ask the user to select a precise rel_type before continuing

Location:
/packages/acs-subsite/www/admin/relations/add.tcl
Author:
mbryzek@mit.edu
Created:
2000-12-11
CVS ID:
$Id: add.tcl,v 1.17.2.4 2023/10/06 12:36:15 gustafn Exp $

Related Files

[ hide source ] | [ make this the default ]

File Contents

ad_page_contract {
    Add a member to a group. If there are subtypes of the specified
    rel_type, we ask the user to select a precise rel_type before
    continuing

    @author mbryzek@mit.edu
    @creation-date 2000-12-11
    @cvs-id $Id: add.tcl,v 1.17.2.4 2023/10/06 12:36:15 gustafn Exp $
} {
    group_id:integer,notnull
    rel_type:notnull
    { party_id:naturalnum "" }
    { exact_p:boolean,notnull "f" }
    { return_url:localurl "" }
    { allow_out_of_scope_p:boolean,notnull "f" }
} -properties {
    context:onevalue
    role_pretty_name:onevalue
    group_name:onevalue
    export_form_vars:onevalue
    rel_types:multirow
    rel_type_pretty_name:onevalue
    add_party_url:onevalue
} -validate {
    party_in_scope_p -requires {party_id:notnull} {
        if { $allow_out_of_scope_p == "f"
             && ![application_group::contains_party_p -party_id $party_id]} {
            ad_complain "The party either does not exist or does not belong to this subsite."
        }
    }
    rel_type_valid_p -requires {group_id:notnull rel_type:notnull exact_p:notnull} {
        if {$exact_p == "t"
            && ![relation_type_is_valid_to_group_p -group_id $group_id $rel_type]} {
            ad_complain "Relations of this type to this group would violate a relational constraint."
        }
    }
}
# ISSUES / TO DO: still need to check that party_id is not already in the
# group through this relation.  Actually, we should handle this with
# double-click protection (which we're not doing yet).  We also need
# to check permissions on the party.

set context [list "Add relation"]

set export_var_list {group_id rel_type exact_p return_url allow_out_of_scope_p}
if {$party_id ne ""} {
    lappend export_var_list party_id
}

group::get -group_id $group_id -array group_info

# We assume the group is on side 1...
db_1row rel_type_info {
    select t.object_type_two,
           t.role_two as role,
           (select pretty_name from acs_rel_roles
            where role = t.role_two) as role_pretty_name,
           (select pretty_name from acs_object_types
            where object_type = t.object_type_two) as object_type_two_name
    from acs_rel_types t
    where rel_type = :rel_type
}

# The role pretty names can be message catalog keys that need
# to be localized before they are displayed
set role_pretty_name [lang::util::localize $role_pretty_name]

if { $exact_p == "f"
     && [subsite::util::sub_type_exists_p $rel_type] } {

    # Sub rel-types exist... select one
    set exact_p "t"
    set export_url_vars [export_vars -exclude rel_type $export_var_list ]

    relation_types_valid_to_group_multirow \
            -datasource_name object_types \
            -start_with $rel_type \
            -group_id $group_id

    set object_type_pretty_name [subsite::util::object_type_pretty_name $rel_type]
    set this_url [ad_conn url]
    set object_type_variable rel_type

    ad_return_template ../parties/add-select-type
    return
}

template::form create add_relation

foreach var $export_var_list {
    template::element create add_relation $var \
            -value [set $var] \
            -datatype text \
            -widget hidden
}

# Build a URL used to create a new party of type object_type_two
set party_type $object_type_two
set party_type_exact_p f
set add_to_group_id $group_id
set add_with_rel_type $rel_type
set add_party_url [export_vars -base [ad_conn package_url]admin/parties/new {add_to_group_id add_with_rel_type party_type party_type_exact_p return_url}]

# Build a URL used to select an existing party from the system (as opposed
# to limiting the selection to parties on the current subsite).
set saved_allow_out_of_scope_p $allow_out_of_scope_p
set allow_out_of_scope_p t
set add_out_of_scope_url [export_vars -base [ad_conn url] $export_var_list]

# Build a URL used to select an existing party from the current subsite
set allow_out_of_scope_p f
set add_in_scope_url [export_vars -base [ad_conn url] $export_var_list]

set allow_out_of_scope_p $saved_allow_out_of_scope_p

# We select out all parties that are to not already belong to the
# specified group with the specified rel_type. Note that we take
# special care to not allow the user to select the group itself... we
# don't want circular references. It remains to be seen if this query
# will be faster if we assume we are only adding parties, and thus
# drive off the smaller parties table. We still somehow need to get
# the object type though...

set user_id [ad_conn user_id]
set package_id [ad_conn package_id]

attribute::add_form_elements -form_id add_relation -start_with relationship -object_type $rel_type
element::create add_relation rel_id -widget hidden -value [db_nextval "acs_object_id_seq"]

if { [template::form is_valid add_relation] } {

    if {[catch {
        group::add_member -group_id $group_id -user_id $party_id -rel_type $rel_type
    } errorMsg]} {
        ad_complain $errorMsg
    }

    if { $return_url eq "" } {
        set return_url [export_vars -base one rel_id]
    }
    ad_returnredirect $return_url
    ad_script_abort
}


if {$party_id ne ""} {
    # ISSUES / TO DO: add a check to make sure the party is not
    # already in the group.  We only want to do this on is_request,
    # in which case we know its not a double-click issue.

    set party_name [acs_object_name $party_id]

    # Note: party_id is not null, which means that it got added already
    # to $export_var_list, which means that there is already a hidden
    # form element containing the party_id variable.

    # Inform user which party will be on side two of the new relation.
    template::element create add_relation party_inform \
            -widget "inform" -value "$party_name" -label "$role_pretty_name"

} else {

    # We special case 'party' because we don't want to include
    # parties whose direct object_type is:
    #    'rel_segment' - users will get confused by segments here.
    #    'party' - this is an abstract type and should have no objects,
    #              but the system creates party -1 which users
    #              shouldn't see.

    # The $allow_out_of_scope_p flag controls whether or not we limit
    # the list of parties to those that belong to the current subsite
    # (allow_out_of_scope_p = 'f').  Even when allow_out_of_scope_p = 't',
    # permissions checks and relational constraints may limit
    # the list of parties that can be added to $group_id with a relation
    # of type $rel_type.

    # SENSITIVE PERFORMANCE - this comment tag is here to make it
    # easy for us to find all the queries that we know may be unscalable.
    # This query has been tuned as well as possible given development
    # time constraints, but more tuning may be necessary.
    set party_option_list [db_list_of_lists select_parties {
        with recursive subtypes as (
            select object_type
              from acs_object_types
             where (object_type = :object_type_two
                    and :object_type_two <> 'party') or
                   (object_type in ('person', 'group')
                    and :object_type_two = 'party')

             union all

            select t.object_type
              from acs_object_types t,
                   subtypes s
             where t.supertype = s.object_type
        )
        select DISTINCT
               case when groups.group_id is null then
                   case when persons.person_id is null then 'INVALID'
                   else persons.first_names || ' ' || persons.last_name end
               else groups.group_name end as party_name,
               p.object_id as party_id
          from acs_objects p
               left join groups on groups.group_id = p.object_id
               left join persons on persons.person_id = p.object_id,
               subtypes s,
               rc_parties_in_required_segs pirs
            where p.object_type = s.object_type
                  -- do not list the group as a possible member
              and p.object_id <> :group_id
                  -- do not list parties that are already members
              and not exists (select 1 from group_element_map
                               where element_id = p.object_id
                                 and group_id = :group_id
                                 and rel_type = :rel_type)
              and pirs.rel_type = :rel_type
              and pirs.group_id = :group_id
              and pirs.party_id = p.object_id
              and (:allow_out_of_scope_p <> 'f' or
                   exists (select 1 from application_group_element_map
                            where package_id = :package_id
                              and element_id = p.object_id))
    }]

    if { [llength $party_option_list] == 0 } {
        ad_return_template add-no-valid-parties
        return
    }

    template::element create add_relation party_id \
            -datatype "text" \
            -widget select \
            -options $party_option_list \
            -label "Select $role_pretty_name"
}

ad_return_template

# Local variables:
#    mode: tcl
#    tcl-indent-level: 4
#    indent-tabs-mode: nil
# End: