<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Parties in OpenACS 4.7.0d</title><meta name="generator" content="DocBook XSL Stylesheets V1.58.1"><link rel="home" href="index.html" title="OpenACS Documentation"><link rel="up" href="dev-guide.html" title="Chapter�11.�Development Reference"><link rel="previous" href="subsites.html" title="Writing OpenACS 4.7.0d Application Pages"><link rel="next" href="permissions-tediously-explained.html" title="OpenACS 4.x Permissions Tediously Explained"><link rel="stylesheet" href="openacs.css" type="text/css"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><a href="http://openacs.org"><img src="images/alex.jpg" border="0"></a><table width="100%" summary="Navigation header" border="0"><tr><td width="20%" align="left"><a accesskey="p" href="subsites.html">Prev</a> </td><th width="60%" align="center">Chapter�11.�Development Reference</th><td width="20%" align="right"> <a accesskey="n" href="permissions-tediously-explained.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><h2 class="title" style="clear: both"><a name="parties"></a>Parties in OpenACS 4.7.0d</h2></div></div><div class="authorblurb"><p>
by <a href="http://planitia.org" target="_top">Rafael H. Schloming</a><br>
          OpenACS docs are written by the named authors, and may be edited
          by OpenACS documentation staff.
        </p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="parties-intro"></a>Introduction</h3></div></div><p>While many applications must deal with individuals and many applications
must deal with groups, most applications must deal with individuals
<span class="emphasis"><em>or</em></span> groups. It is often the case with such applications that in many
respects both individuals and groups are treated in an identical manner. It
is this latter class of application that makes it extremely useful to model
individuals and groups as specializations of one supertype. This concept is
so commonly used throughout human language and thought that we don't even
need to invent for it some bizarre and specialized terminology. This
supertype is called a &quot;party&quot;.</p><p>A classic example use of the &quot;party&quot; supertype is evident
in a common address book. A typical person's address book might contain
the address of his doctor, and his cable company. So what do we label the
first field in an entry in his address book? It isn't a person, and it
isn't a company. It is a &quot;party&quot;.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="parties-data-model"></a>The Data Model</h3></div></div><p>Most developers who do significant work with the OpenACS will become
intimately familiar with the parties data model, and probably extend it on
many occasions. For this reason the parties developer guide will begin with
an introduction to the data model.</p><p><span class="strong">Parties</span></p><p>The central table in the parties data model is the parties table itself.
Every party has exactly one row in this table. Every party has an optional
unique email address and an optional url. A party is an acs object, so
permissions may granted and revoked on parties and auditing information is
stored in the acs objects table.</p><pre class="programlisting">

<tt>create table parties (
    party_id    not null
            constraint parties_party_id_fk references
            acs_objects (object_id)
            constraint parties_pk primary key,
    email       varchar(100)
            constraint parties_email_un unique,
    url     varchar(200)
);
</tt>

</pre><p>There are two tables that extend the parties table. One is the persons
table and one is the groups table. A row in the persons table represents the
most basic form of individual that is modeled by the parties data model. A
row in the groups table represents the most basic form of an aggregation of
individuals that is represented.</p><p><span class="strong">Persons</span></p><p>If a party is an individual then there will be a row in the persons table
containing first_names and last_name for that individual. Note that the
primary key of the persons table (person_id) references the primary key of
the parties table (party_id). This guarantees that if there is a row in the
persons table then there must be a corresponding row in the parties table.
Also note that an individual need not be known to the system as a user. A
user is in fact a further specialized form of an individual.</p><pre class="programlisting">

<tt>create table persons (
    person_id   not null
            constraint persons_person_id_fk
            references parties (party_id)
            constraint persons_pk primary key,
    first_names varchar(100) not null,
    last_name   varchar(100) not null
);
</tt>

</pre><p><span class="strong">Users</span></p><p>The users table is an even more specialized form of an individual. A row
in the users table represents an individual that has login access to the
system. Note that the primary key of the users table references the primary
key of the persons table. Once again this guarantees that if there is a row
in the users table then there must be a row in the persons table and a row in
the parties table.</p><p>One of the interesting benefits of decomposing all the information
associated with a user into the four tables (acs_objects, parties, persons,
users) is that it is now possible to &quot;nuke&quot; a user from a live
system by removing his entry from the users table, but leaving the rest of
his information present (i.e. turning him from a user into a person). This is
because wherever possible the OpenACS 4.7.0d data model references the persons or
parties table, <span class="strong">not</span> the users table. If this feature is
desired when extending the system, then the developers should be careful to
only references the users table in situations where it is clear that the
references is to a user and not to an individual.</p><pre class="programlisting">

<tt>create table users (
    user_id         not null
                constraint users_user_id_fk
                references persons (person_id)
                constraint users_pk primary key,
    password        varchar(100),
    salt            varchar(100),
    screen_name     varchar(100)
                constraint users_screen_name_un
                unique,
    priv_name       integer default 0 not null,
    priv_email      integer default 5 not null,
    email_verified_p    char(1) default 't'
                constraint users_email_verified_p_ck
                check (email_verified_p in ('t', 'f')),
    email_bouncing_p    char(1) default 'f' not null
                constraint users_email_bouncing_p_ck
                check (email_bouncing_p in ('t','f')),
    no_alerts_until     date,
    last_visit      date,
    second_to_last_visit    date,
    n_sessions      integer default 1 not null,
    password_question   varchar(1000),
    password_answer     varchar(1000)
);
</tt>

</pre><p><span class="strong">Groups</span></p><p>The final piece of the parties data model is the groups data model. A
group is a specialization of a party that represents an aggregation of other
parties. The only extra information directly associated with a group (beyond
that in the parties table) is the name of the group. As you might guess there
is another piece to the groups data model that records relations between
parties and groups.</p><pre class="programlisting">

<tt>create table groups (
    group_id    not null
            constraint groups_group_id_fk
            references parties (party_id)
            constraint groups_pk primary key,
    group_name  varchar(100) not null
);
</tt>

</pre><p><span class="strong">Group Relations</span></p><p>One surprise here is that there are actually two relations involved. One
is the normal membership relation between parties and groups. A party may be
a &quot;member&quot; of a group. The other relation is a composition
relation between two groups. To fully understand why two relations are
necessary, and the situations in which each is appropriate, let's
consider an example. Greenpeace is an organization that can have as members
both individuals and organizations. Hence the membership relation between
groups and <span class="emphasis"><em>parties</em></span>. But just because you are a member of an
organization that is a member of Greenpeace, that doesn't make you a
member of Greenpeace, i.e., membership is not transitive with respect to
itself. Now let's consider a multinational corporation. This corporation
might have a U.S. division and a European division. A member of either the
U.S. or European division is automatically a member of the company. In this
situation the U.S. and European divisions are &quot;components&quot; of
the company, i.e., membership <span class="emphasis"><em>is</em></span> transitive with respect to
composition. Having a membership relation between groups and parties, and
having a composition relation between groups and groups allows us to easily
model the full range of sophisticated group structures that exist in the real
world.</p><p><span class="strong">Group Membership</span></p><p>Group memberships can be created and manipulated using the membership_rel
package. Note that you can only create one membership object for a given
group, party pair. Because of composition, it is possible in some
circumstances to make someone a member of a group of which they are already a
member. This is because there is a distinction between direct membership and
indirect membership (membership via some component or sub component).</p><pre class="programlisting">

<tt>create or replace package membership_rel
as

  function new (
    rel_id      in membership_rels.rel_id%TYPE default null,
    rel_type        in acs_rels.rel_type%TYPE default 'membership_rel',
    object_id_one   in acs_rels.object_id_one%TYPE,
    object_id_two   in acs_rels.object_id_two%TYPE,
    member_state    in membership_rels.member_state%TYPE default null,
    creation_user   in acs_objects.creation_user%TYPE default null,
    creation_ip     in acs_objects.creation_ip%TYPE default null
  ) return membership_rels.rel_id%TYPE;

  procedure ban (
    rel_id  in membership_rels.rel_id%TYPE
  );

  procedure approve (
    rel_id  in membership_rels.rel_id%TYPE
  );

  procedure reject (
    rel_id  in membership_rels.rel_id%TYPE
  );

  procedure unapprove (
    rel_id  in membership_rels.rel_id%TYPE
  );

  procedure deleted (
    rel_id  in membership_rels.rel_id%TYPE
  );

  procedure delete (
    rel_id  in membership_rels.rel_id%TYPE
  );

end membership_rel;
/
show errors
</tt>

</pre><p><span class="strong">Group Composition</span></p><p>Composition relations can be created or destroyed using the
composition_rel package. The only restriction on compositions is that there
cannot be a cycle, i.e., a group cannot be a component of itself either
directly or indirectly. This constraint is maintained for you by the API, but
if you don't want users seeing some random PL/SQL error message, it is a
good idea to not give them the option to create a composition relation that
would result in a cycle.</p><pre class="programlisting">

<tt>create or replace package composition_rel
as

  function new (
    rel_id      in composition_rels.rel_id%TYPE default null,
    rel_type        in acs_rels.rel_type%TYPE default 'composition_rel',
    object_id_one   in acs_rels.object_id_one%TYPE,
    object_id_two   in acs_rels.object_id_two%TYPE,
    creation_user   in acs_objects.creation_user%TYPE default null,
    creation_ip     in acs_objects.creation_ip%TYPE default null
  ) return composition_rels.rel_id%TYPE;

  procedure delete (
    rel_id  in composition_rels.rel_id%TYPE
  );

end composition_rel;
/
show errors
</tt>

</pre></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="parties-views"></a>Views</h3></div></div><p>The data model described above does a reasonable job of representing many
of the situations one is likely to encounter when modeling organizational
structures, but we still need to be able to efficiently answer questions like
&quot;what members are in this group and all of its components?&quot;, and
&quot;of what groups is this party a member either directly or
indirectly?&quot;. Composition relations allow you to describe an arbitrary
Directed Acyclic Graph (DAG) between a group and its components. For these
reasons the party system provides a bunch of views that take advantage of the
internal representation of group relations to answer questions like these
very quickly.</p><p>This view returns all the subcomponents of a group including components of
sub components and so forth. The container_id column is the group_id of the
group in which component_id is directly contained. This allows you to easily
distinguish whether a component is a direct component or an indirect
component. (If a component is a direct component then group_id will be equal
to container_id.) You can think of this view as having a primary key of
group_id, component_id, and container_id. The rel_id column points to the row
in acs_rels that contains the relation object that relates component_id to
container_id. The rel_id might be useful for retrieving or updating standard
auditing info for the relation.</p><pre class="programlisting">

<tt>create or replace view group_component_map
as select group_id, component_id, container_id, rel_id
...
</tt>

</pre><p>This is similar to group_component_map except for membership relations.
Note that this view will return all membership relations regardless of
membership state.</p><pre class="programlisting">

<tt>create or replace view group_member_map
as select group_id, member_id, container_id, rel_id
...
</tt>

</pre><p>The group_approved_member_map is the same as the group_member_map except
it only returns entries that relate to approved members.</p><pre class="programlisting">

<tt>create or replace view group_approved_member_map
as select group_id, member_id, container_id, rel_id
...
</tt>

</pre><p>This view is useful if you don't care about the distinction between
direct membership and indirect membership. It simply returns all members of a
group including members of components. (It is the transitive closure.)</p><pre class="programlisting">

<tt>create or replace view group_distinct_member_map
as select group_id, member_id
...
</tt>

</pre><p>This view is the same as group_distinct_member_map, except it includes the
identity mapping. In other words it maps from a party to the fully expanded
list of parties represented by that party including the party itself. So if a
party is an individual this view will have exactly one mapping that is from
that party to itself. If a view is a group containing three individuals, this
view will have four rows for that party, one for each member, and one from
the party to itself.</p><pre class="programlisting">

<tt>create or replace view party_member_map
as select party_id, member_id
...
</tt>

</pre><p>This view is the same as above except that when it expands groups, it only
pays attention to approved members.</p><pre class="programlisting">

<tt>create or replace view party_approved_member_map
as select party_id, member_id
...
</tt>

</pre></div><div class="sect2" lang="en"><div class="titlepage"><div><h3 class="title"><a name="parties-extending-data-model"></a>Extending The Parties Data Model</h3></div></div><p>As is, the parties data model can represent some fairly sophisticated real
world situations, and a lot of work has been put into making this efficient,
but it is foolish to assume that this data model is sufficient for every
application. It therefore seems likely that developers will want to extend
the parties data model in a number of ways. This section will describe some
of the more common ways.</p><p><span class="strong">Specializing Users</span></p><p>It is conceivable that some applications will want to collect more
detailed information for people using the system. If it is the case that
there can be only one such piece of information per user, then it might make
sense to create another type of individual that is a further specialization
of a user. For example a MENSA community web site might want to record IQs
for each user. In a situation like this it would be appropriate to create a
subtype of users, say mensa_users. This child table of the users table would
have a primary key that references the users table, thereby guaranteeing that
each row in the mensa_users table has a corresponding row in each of the
users, persons, parties, and acs_objects tables. This child table could then
store any extra information relevant to the MENSA community.</p><p><span class="strong">Specializing Groups</span></p><p>If one were to build an intranet application on top of the 4.7.0d party
system, it is likely that one would want to take advantage of the systems
efficient representation of sophisticated organizational structures, but
there would be much more specialized information associated with each group.
In this case it would make sense to specialize the group party type into a
company party type in the same manner as above.</p><p><span class="strong">Specializing Membership Relations</span></p><p>The final portion of the parties data model that is designed to be
extended is the membership relationship. Consider the intranet example again.
It is likely that a membership in a company would have more information
associated with it than a membership in an ordinary group. An obvious example
of this would be a salary. It is exactly this need to be able to extend
membership relations with mutable pieces of state that drove us to include a
single integer primary key in what could be thought of as a pure relation.
Because a membership relation is an ordinary acs object with <a href="object-identity.html" target="_top">object identity</a>, it is as easy to extend the
membership relation to store extra information as it is to extend the users
table or the groups table.</p><div class="cvstag">($Id: parties.html,v 1.11 2003/06/28 05:07:02 joela Exp $)</div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="subsites.html">Prev</a> </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right"> <a accesskey="n" href="permissions-tediously-explained.html">Next</a></td></tr><tr><td width="40%" align="left">Writing OpenACS 4.7.0d Application Pages </td><td width="20%" align="center"><a accesskey="u" href="dev-guide.html">Up</a></td><td width="40%" align="right"> OpenACS 4.x Permissions Tediously Explained</td></tr></table><hr><address><a href="mailto:docs@openacs.org">docs@openacs.org</a></address></div><a name="comments"></a><center><a href="http://openacs.org/doc/parties.html#comments">View comments on this page at openacs.org</a></center></body></html>