As a result of asking many questions, studying code, and tinkering, I have documented the process I used to create additional acs-subsite roles (in addition to Members and Administrators).
This document describes the use of relation types, relational segments, and application groups, within the context of an acs-subsite. It adds two roles: Guests and Managers, and contains sample tcl api and db calls used to implement the expansion. It should be general enough to be used as guide for others.
I am interested in scrutiny by "those that know", but my real purpose is to provide a shortcut for others that may wish to do the same.
Randy
OACS Subsite Community Roles
Author: Randy O'Meara <omeara at got dot net>
Date: 17 August 2003
Summary
This document describes the addition and utilization of an expanded
role system. This expanded role system is based on acs-subsite, its
associated application group, and relational segments. We assume that
the application group is created (as it is in oacs 4.6) at subsite
instantiation. At the same time, two relational segments are also
created: the Members and Administrators segments. This document
describes the steps and calls required to create and assign additional
roles within an subsite-based community.
The reason I created this extended role system was to provide more
granularity in assigning rights and permissions to community members. I
wanted to allow access and services to four levels of users, where each
increasing level included the rights of the next lower level *plus* some
additional rights. The levels I defined, from lowest to highest, were:
Guest, Member, Manager, and Administrator. In order to enforce and
control access to each separate community, it is necessary to change
some default settings including turning of inheritance of permissions
from the main site. These steps are included below but are optional if
strict community member control and privacy are not at issue.
This document describes one way to go about creating the system. There
are decisions to be made at every step, the results of which may lead
to other ways to achieve an equivalent end result. For example, it is
not necessary to create the pretty-named roles. The decision to not do
this would result in the role names not being available through a
DB query later to generate a drop-down selection list. However, the
role names and their values could be hard coded at the appropriate place
and used in place of the query that retrieves this information.
Beware... This document was created after I spent hours researching,
perusing code, posting to forums, tinkering, and testing. Thanks to the
few that guided me in the right direction! Part of the difficulty in
creating this code was that there really are (at least, were) no
working examples of a role system based on acs-subsite and application
groups. As such, there may be far easier methods to do what I've done
here. I have (minimally) tested the code included in the examples
below. But...not exhaustively, and NOT IN PRODUCTION. I am interested
in errors you may find. Please let me know what you find.
The process described below consists of the following steps:
1) Create Relation Types
2) Instantiate Subsite
3) Modify Default Subsite Policy
4) Create Relational Segments and Apply Privileges
5) Assign User to a Role
6) Use the OACS permissions:: System to Control Actions and Access
Relation Types Creation
Recall that we need only create the Guest and Manager roles since the
Member and Administrator roles already exist.
The first step is to create pretty-named roles:
set role guest; set pn Guest; set pp Guests
db_1row new_role {
select acs_rel_type__create_role(:role,
:pn, :pp)
}
set role manager; set pn Manager; set pp Managers
db_1row new_role {
select acs_rel_type__create_role(:role,
:pn, :pp)
}
The second step is to create relation types:
# Create rel_types
set m_rel_type
[rel_types::new \
-supertype "membership_rel" \
-role_one "" \
-role_two "guest" \
"guest_rel" \
"Guest" \
"Guests" \
"group" \
"0" \
"" \
"person" \
"0" \
"1"]
rel_types::add_permissible application_group guest_rel
set m_rel_type
[rel_types::new \
-supertype "membership_rel" \
-role_one "" \
-role_two "manager" \
"manager_rel" \
"Manager" \
"Managers" \
"group" \
"0" \
"" \
"person" \
"0" \
"1"]
rel_types::add_permissible application_group manager_rel
Subsite Instantiation
A subsite can be instantiated and mounted directly below the root (Main
Site) with the following tcl api call:
set comm_obj_id [site_node::instantiate_and_mount \
-node_name "subsite1" \
-package_name "subsite1 Community" \
-package_key "acs-subsite"]
The subsite is created, mounted in the site map, and accessible at
"/subsite1". The community package_id (comm_obj_id) is returned and used
to further modify the subsite. All of the information for this subsite
node (including its package_id) may be retrieved at a later time with
site_node::get_from_url -url "/subsite1" .
Subsite Policy Modification
Subsite permission inheritance can be eliminated with the permissions
subsystem.
# Do not inherit permissions from the main site
permission::set_not_inherit -object_id $comm_obj_id
The subsite member join policy can be restricted.
# Get the subsite application group
set app_grp [application_group::group_id_from_package_id \
-package_id $comm_obj_id]
# Set subsite application group join_policy to "needs approval"
db_dml update_join_policy {
update groups
set join_policy = 'needs approval'
where group_id = :app_grp
}
# There is another location to set the join policy?
# Oh well, for good measure...
parameter::set_value -package_id $comm_obj_id \
-parameter RegistrationRequiresApprovalP
-value 1
Relational Segments Create & Permission
When all is said and done, we want four relational segments that
coincide with the four roles we defined earlier. Two of these segments
already exist by way of acs-subsite instantiation: Members and
Administrators. We need to create the Guests and Managers segments. In
addition to creating the segments, we need to assign the standard oacs
privileges to each segment. The code below actually removes the "create"
privilege from the previously created Members segment. You may not want
to do this. Later, a user will be related to the subsite application
group through a particular type of relation, and the user will obtain
privileges based on the relation.
# Create and grant: Guests
set g_seg [rel_segments_new $app_grp \
guest_rel "subsite1 Guests"]
permission::grant -party_id $g_seg \
-object_id $comm_obj_id -privilege read
# Create and grant: Managers
set m_seg [rel_segments_new $app_grp \
manager_rel "subsite1 Managers"]
permission::grant -party_id $m_seg \
-object_id $comm_obj_id -privilege read
permission::grant -party_id $m_seg \
-object_id $comm_obj_id -privilege create
permission::grant -party_id $m_seg \
-object_id $comm_obj_id -privilege write
# Get the "Members" segment and remove the create priv
set u_seg [db_string rel_seg_id_get {
select segment_id
from rel_segments
where group_id = :app_grp
and rel_type= 'membership_rel'
} -default ""]
permission::revoke -party_id $u_seg \
-object_id $comm_obj_id -privilege create
# Get the "Administrators" segment
set a_seg [db_string rel_seg_id_get {
select segment_id
from rel_segments
where group_id = :app_grp
and rel_type= 'admin_rel'
} -default ""]
The last section "Get the Administrators segment" is shown for
completeness although it's not necessary. This also shows how to
retrieve the segment IDs if needed. You might want to stash g_seg,
u_seg, m_seg, and a_seg.
The subsite is created, mounted in the site map, and accessible at
"/subsite1". The community package_id (comm_obj_id) is returned and used
to further modify the subsite. All of the information for this subsite
node (including its package_id) may be retrieved at a later time with
the site_node::get_from_url -url "/subsite1" tcl api call.
Community Member Role Assignment
Ahhh! We can finally assign a prospective community member to a role.
I'm certain that there are many methods of obtaining the user_id that we
wish to use in role assignment. The method I used was to have the
cummunity applicant apply for membership through a slightly modified
/register/user-join page. The standard oacs user-join page was modified
to accept (in its query vars) a valid application group_id, an
invitation code, and then to validate the user by a call to
ad_maybe_redirect_for_registration. This modified page, along with the
subsite "join policy" changes that were made above, result in:
1) a gurantee that the applicant is a Main Site
"Registered User", and
2) the applicant possesses the correct invitation
code.
Once these conditions are staisfied, the user-join page adds the
applicant to the subsite's application group, but with a member_state of
"needs approval". Until a subsite Manager or Administrator assigns the
applicant to one of the pre-defined roles, the applicant cannot access
the subsite. The action of assigning the applicant to a role
simultaneously changes the applicant's member_state to "approved".
The process of assigning the applicant to a community role can be
performed any number of ways. One way might be to use ad_form with a
drop-down role list populated by
# Get subsite node_id
set role_opts [group::get_rel_types_options \
-group_id $app_grp]
lappend role_opts [list -REMOVE- -REMOVE-]
and use the "-REMOVE-" flag to trigger a relation removal.
Ultimately, to change/add/remove a user in a role, the following tcl
api calls are used. To change a user's role, first remove the existing
relation and then add the new relation:
# Retrieve a user's role along with other user information
set rel_id [db_0or1row role_get {
select
pe.last_name || ', ' ||
pe.first_names as name,
pa.email as email,
pe.person_id as member_id,
mm.rel_type as role,
mm.rel_id as rel_id,
mr.member_state as
member_state
from group_member_map mm,
membership_rels mr,
parties pa, persons pe
where mm.member_id = pe.person_id
and mm.member_id = pa.party_id
and mm.rel_id = mr.rel_id
and mm.group_id = :app_grp
and pe.person_id = :member_id
}]
# Remove a user from a role
relation_remove $rel_id
# Add a user in a role
relation_add -member_state "approved" \
$role $app_grp $member_id
Use OACS Permissions System
You should now be set to use the standard AOCS permissions:: system to
determine what actions your pages should present to the user based on
the privileges assigned to the user through his/her association with the
assigned role.