Skip to Content.
Sympa Menu

comanage-dev - [comanage-dev] r365 - in registry/trunk/app: Config/Schema Controller Lib Model View/CoEnrollmentAttributes View/CoEnrollmentFlows View/CoInvites View/CoPeople View/CoPetitions View/Elements View/EmailAddresses View/Emails/html View/Emails/text View/Layouts View/OrgIdentities

Subject: COmanage Developers List

List archive

[comanage-dev] r365 - in registry/trunk/app: Config/Schema Controller Lib Model View/CoEnrollmentAttributes View/CoEnrollmentFlows View/CoInvites View/CoPeople View/CoPetitions View/Elements View/EmailAddresses View/Emails/html View/Emails/text View/Layouts View/OrgIdentities


Chronological Thread 
  • From:
  • To:
  • Subject: [comanage-dev] r365 - in registry/trunk/app: Config/Schema Controller Lib Model View/CoEnrollmentAttributes View/CoEnrollmentFlows View/CoInvites View/CoPeople View/CoPetitions View/Elements View/EmailAddresses View/Emails/html View/Emails/text View/Layouts View/OrgIdentities
  • Date: Tue, 25 Sep 2012 20:53:01 -0400

Author: benno
Date: 2012-09-25 20:53:01 -0400 (Tue, 25 Sep 2012)
New Revision: 365

Added:
registry/trunk/app/View/CoInvites/authconfirm.ctp
registry/trunk/app/View/CoInvites/petition-attributes.inc
registry/trunk/app/View/CoPetitions/petition-attributes.inc
Modified:
registry/trunk/app/Config/Schema/schema.xml
registry/trunk/app/Controller/AppController.php
registry/trunk/app/Controller/CoInvitesController.php
registry/trunk/app/Controller/CoOrgIdentityLinksController.php
registry/trunk/app/Controller/CoPeopleController.php
registry/trunk/app/Controller/CoPetitionsController.php
registry/trunk/app/Lib/enum.php
registry/trunk/app/Lib/lang.php
registry/trunk/app/Model/CoEnrollmentAttribute.php
registry/trunk/app/Model/CoEnrollmentFlow.php
registry/trunk/app/Model/CoInvite.php
registry/trunk/app/Model/CoPetition.php
registry/trunk/app/Model/CoPetitionHistoryRecord.php
registry/trunk/app/Model/EmailAddress.php
registry/trunk/app/Model/OrgIdentity.php
registry/trunk/app/View/CoEnrollmentAttributes/index.ctp
registry/trunk/app/View/CoEnrollmentFlows/fields.inc
registry/trunk/app/View/CoInvites/reply.ctp
registry/trunk/app/View/CoPeople/fields.inc
registry/trunk/app/View/CoPeople/index.ctp
registry/trunk/app/View/CoPetitions/fields.inc
registry/trunk/app/View/CoPetitions/index.ctp
registry/trunk/app/View/Elements/dropMenu.ctp
registry/trunk/app/View/EmailAddresses/fields.inc
registry/trunk/app/View/Emails/html/coinvite.ctp
registry/trunk/app/View/Emails/text/coinvite.ctp
registry/trunk/app/View/Layouts/default.ctp
registry/trunk/app/View/OrgIdentities/fields.inc
Log:
Add support for account linking enrollment flow (CO-436)

Modified: registry/trunk/app/Config/Schema/schema.xml
===================================================================
--- registry/trunk/app/Config/Schema/schema.xml 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/Config/Schema/schema.xml 2012-09-26 00:53:01 UTC (rev
365)
@@ -280,6 +280,12 @@
<col>type</col>
<col>co_person_id</col>
</index>
+
+ <index name="identifiers_i2">
+ <col>identifier</col>
+ <col>type</col>
+ <col>org_identity_id</col>
+ </index>
</table>

<table name="co_invites">
@@ -301,10 +307,16 @@

<index name="co_invites_i1">
<col>co_person_id</col>
- </index>
+ </index>
+
<index name="co_invites_i2">
<col>invitation</col>
- </index>
+ </index>
+
+ <index name="co_invites_i3">
+ <col>co_person_id</col>
+ <col>mail</col>
+ </index>
</table>

<table name="addresses">
@@ -343,6 +355,7 @@
</field>
<field name="mail" type="C" size="256" />
<field name="type" type="C" size="2" />
+ <field name="verified" type="L" />
<field name="co_person_id" type="I">
<constraint>REFERENCES cm_co_people(id)</constraint>
</field>
@@ -354,10 +367,13 @@

<index name="email_addresses_i1">
<col>co_person_id</col>
- </index>
+ </index>
<index name="email_addresses_i2">
<col>org_identity_id</col>
- </index>
+ </index>
+ <index name="email_addresses_i3">
+ <col>mars</col>
+ </index>
</table>

<table name="telephone_numbers">
@@ -522,7 +538,10 @@
<field name="authz_co_group_id" type="I">
<constraint>REFERENCES cm_co_groups(id)</constraint>
</field>
+ <field name="match_policy" type="C" size="2" />
<field name="approval_required" type="L" />
+ <field name="verify_email" type="L" />
+ <field name="require_authn" type="L" />
<field name="early_provisioning_exec" type="C" size="128" />
<field name="provisioning_exec" type="C" size="128" />
<field name="notify_on_early_provision" type="C" size="128" />
@@ -598,6 +617,9 @@
<field name="approver_co_person_id" type="I">
<constraint>REFERENCES cm_co_people(id)</constraint>
</field>
+ <field name="co_invite_id" type="I">
+ <constraint>REFERENCES cm_co_invites(id)</constraint>
+ </field>
<field name="status" type="C" size="2" />
<field name="created" type="T" />
<field name="modified" type="T" />
@@ -614,6 +636,9 @@
<index name="co_petitions_i4">
<col>petitioner_co_person_id</col>
</index>
+ <index name="co_petitions_i5">
+ <col>co_invite_id</col>
+ </index>
</table>

<table name="co_petition_attributes">

Modified: registry/trunk/app/Controller/AppController.php
===================================================================
--- registry/trunk/app/Controller/AppController.php 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/Controller/AppController.php 2012-09-26 00:53:01
UTC (rev 365)
@@ -126,9 +126,12 @@
if(isset($this->CoPetition->EnrolleeCoPerson->Identifier)) {
$this->CoPetition->EnrolleeCoPerson->Identifier->coId = $coid;
}
+
if(isset($this->CoInvite->CoPetition->EnrolleeCoPerson->Identifier)) {
+ $this->CoInvite->CoPetition->EnrolleeCoPerson->Identifier->coId
= $coid;
+ }
} else {
$this->Session->setFlash(_txt('er.co.unk-a', array($coid)), '',
array(), 'error');
- $this->redirect(array('controller' => 'cos', 'action' =>
'select'));
+ $this->redirect("/");
}
}
}
@@ -1053,6 +1056,8 @@
$coid = $this->request->data['Co']['id'];
elseif(isset($this->request->data[$req]['co_id']))
$coid = $this->request->data[$req]['co_id'];
+ elseif(isset($this->impliedCoId))
+ $coid = $this->impliedCoId;

return $coid;
}
@@ -1243,7 +1248,10 @@

// Manage any CO (or COU) population?
$p['menu']['cos'] = $cmr['admin'] || $cmr['subadmin'];
-
+
+ // Select from available enrollment flows?
+ $p['menu']['enrollmentflows'] = $cmr['user'];
+
// Manage any CO (or COU) population?
$p['menu']['petitions'] = $cmr['admin'] || $cmr['subadmin'];

@@ -1285,14 +1293,16 @@
function menuContent() {
// Get org identity ID
$orgIDs = $this->Session->read('Auth.User.org_identities');
+
+ if(!empty($orgIDs)) {
+ // Find name associated with that ID
+ $this->loadModel('OrgIdentity');
+ $orgName = $this->OrgIdentity->read('o',$orgIDs[0]['org_id']);
+
+ // Set for home ID name in menu
+ $menu['orgName'] = $orgName['OrgIdentity']['o'];
+ }

- // Find name associated with that ID
- $this->loadModel('OrgIdentity');
- $orgName = $this->OrgIdentity->read('o',$orgIDs[0]['org_id']);
-
- // Set for home ID name in menu
- $menu['orgName'] = $orgName['OrgIdentity']['o'];
-
// Set the COs for display
if($this->viewVars['permissions']['menu']['admin']) {
// Show all active COs for admins

Modified: registry/trunk/app/Controller/CoInvitesController.php
===================================================================
--- registry/trunk/app/Controller/CoInvitesController.php 2012-09-24
06:33:39 UTC (rev 364)
+++ registry/trunk/app/Controller/CoInvitesController.php 2012-09-26
00:53:01 UTC (rev 365)
@@ -22,12 +22,12 @@
* @version $Id$
*/

-App::uses('CakeEmail', 'Network/Email');
-
class CoInvitesController extends AppController {
// We don't extend StandardController because there's so much unique stuff
going on here
public $name = "CoInvites";

+ public $helpers = array('Time');
+
public $paginate = array(
'limit' => 25,
'order' => array(
@@ -50,6 +50,23 @@
}

/**
+ * Confirm the requested invite, requiring authentication
+ * - precondition: $invite must exist, be valid, and attached to a valid
CO person
+ * - postcondition: CO Person status set to 'Active' and/or CO Petition
updated
+ * - postcondition: $inviteid deleted
+ * - postcondition: Session flash message updated (HTML)
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer Invitation ID
+ */
+
+ public function authconfirm($inviteid) {
+ // This behaves just like confirm(), except that authentication is
required to get here.
+
+ $this->process_invite($inviteid, true,
$this->Session->read('Auth.User.username'));
+ }
+
+ /**
* Callback before other controller methods are invoked or views are
rendered.
* - postcondition: Auth component is configured
*
@@ -60,7 +77,26 @@
function beforeFilter() {
if($this->action == "send")
$this->requires_co = true;
-
+
+ if($this->action == "confirm" || $this->action == "authconfirm") {
+ // Identifier assignment requires the CO ID to be set, but since CO ID
isn't
+ // provided as an explicit parameter, beforeFilter can't find it.
+
+ $args = array();
+ $args['conditions']['CoInvite.invitation'] =
$this->request->params['pass'][0];
+ $args['joins'][0]['table'] = 'co_invites';
+ $args['joins'][0]['alias'] = 'CoInvite';
+ $args['joins'][0]['type'] = 'INNER';
+ $args['joins'][0]['conditions'][0] =
'CoPerson.id=CoInvite.co_person_id';
+ $args['contain'] = false;
+
+ $coPerson = $this->CoInvite->CoPerson->find('first', $args);
+
+ if(isset($coPerson['CoPerson']['co_id'])) {
+ $this->impliedCoId = $coPerson['CoPerson']['co_id'];
+ }
+ }
+
// Since we're overriding, we need to call the parent to run the authz
check
parent::beforeFilter();

@@ -71,7 +107,7 @@
/**
* Confirm the requested invite
* - precondition: $invite must exist, be valid, and attached to a valid
CO person
- * - postcondition: CO Person status set to 'Active'
+ * - postcondition: CO Person status set to 'Active' and/or CO Petition
updated
* - postcondition: $inviteid deleted
* - postcondition: Session flash message updated (HTML)
*
@@ -152,6 +188,9 @@

// Send an invite? (REST only)
$p['add'] = $cmr['apiuser'];
+
+ // Confirm an invite? (HTML, auth reequired)
+ $p['authconfirm'] = true;

// Confirm an invite? (HTML only)
$p['confirm'] = true;
@@ -180,65 +219,63 @@
* - postcondition: Session flash message updated (HTML) or HTTP status
returned (REST)
*
* @since COmanage Registry v0.1
- * @param Integer ID invitation
- * @param Boolean If true, confirm the invitation; if false, decline it
+ * @param Integer ID invitation
+ * @param Boolean If true, confirm the invitation; if false, decline it
+ * @param String Authenticated login identifier, if set
*/

- function process_invite($inviteid, $confirm) {
- if(!$this->restful)
- {
+ function process_invite($inviteid, $confirm, $loginIdentifier=null) {
+ if(!$this->restful) {
// Set page title
$this->set('title_for_layout', _txt('op.inv.reply'));
}

- $invite = $this->CoInvite->findByInvitation($inviteid);
-
- if(!$invite)
- {
- if($this->restful)
- {
- $this->restResultHeader(404, "CoInvite Unknown");
+ try {
+ $this->CoInvite->processReply($inviteid, $confirm, $loginIdentifier);
+ }
+ catch(InvalidArgumentException $e) {
+ if($this->restful) {
+ if($e->getMessage() == _txt('er.inv.nf')) {
+ $this->restResultHeader(404, "CoInvite Unknown");
+ } else {
+ $this->restResultHeader(400, "CoPerson Unknown");
+ }
+ } else {
+ $this->Session->setFlash($e->getMessage(), '', array(), 'error');
+ }
+ }
+ catch(OutOfBoundsException $e) {
+ if($this->restful) {
+ $this->restResultHeader(403, "Expired");
+ } else {
+ $this->Session->setFlash($e->getMessage(), '', array(), 'error');
+ }
+ }
+ catch(Exception $e) {
+ if($e->getMessage() == _txt('er.auth')) {
+ // This invitation requires authentication, so issue a redirect
+ $this->redirect(array('action' => 'authconfirm', $inviteid));
return;
+ } else {
+ if($this->restful) {
+ $this->restResultHeader(500, "Other Error");
+ } else {
+ $this->Session->setFlash($e->getMessage(), '', array(), 'error');
+ }
}
- else
- $this->Session->setFlash(_txt('er.inv.nf'), '', array(), 'error');
}
- else
- {
- // Check invite validity
-
- if(time() < strtotime($invite['CoInvite']['expires']))
- {
- // Update CO Person
+
+ if($this->restful)
+ $this->restResultHeader(200, "Deleted");
+ else {
+ if($loginIdentifier) {
+ // If a login identifier was provided, force a logout

- $this->CoInvite->CoPerson->id = $invite['CoPerson']['id'];
-
- if($this->CoInvite->CoPerson->saveField('status', $confirm ? 'A' :
'X'))
- {
- if($this->restful)
- $this->restResultHeader(200, "Deleted");
- else
- $this->Session->setFlash($confirm ? _txt('rs.inv.conf') :
_txt('rs.inv.dec'), '', array(), 'success');
- }
- else
- {
- if($this->restful)
- $this->restResultHeader(400, "CoPerson Unknown");
- else
- $this->Session->setFlash(_txt('er.cop.nf',
$invite['CoPerson']['id']), '', array(), 'error');
- }
+ $this->Session->setFlash(_txt('rs.pt.relogin'), '', array(),
'success');
+ $this->redirect("/auth/logout");
+ } else {
+ $this->Session->setFlash($confirm ? _txt('rs.inv.conf') :
_txt('rs.inv.dec'), '', array(), 'success');
}
- else
- {
- if($this->restful)
- $this->restResultHeader(403, "Expired");
- else
- $this->Session->setFlash(_txt('er.inv.exp'), '', array(), 'error');
- }
-
- // Toss the invite
-
- $this->CoInvite->delete($invite['CoInvite']['id']);
}
}

@@ -277,6 +314,43 @@
$this->set('cur_co', $co);
$this->set('invite', $invite);
$this->set('invitee', $invitee);
+
+ // We also want to pull the enrollment flow and petition attributes,
if appropriate
+
+ if(isset($invite['CoPetition']['id'])) {
+ $args = array();
+ $args['conditions']['CoEnrollmentFlow.id'] =
$invite['CoPetition']['co_enrollment_flow_id'];
+ $args['contain'][] = 'CoEnrollmentAttribute';
+
+ $enrollmentFlow =
$this->CoInvite->CoPetition->CoEnrollmentFlow->find('first', $args);
+
+ $this->set('co_enrollment_flow', $enrollmentFlow);
+
+ $this->set('co_enrollment_attributes',
+
$this->CoInvite->CoPetition->CoEnrollmentFlow->CoEnrollmentAttribute->enrollmentFlowAttributes($invite['CoPetition']['co_enrollment_flow_id']));
+
+ $pArgs = array();
+ $pArgs['conditions']['CoPetition.id'] = $invite['CoPetition']['id'];
+ $pArgs['contain'][] = 'CoPetitionHistoryRecord';
+ $pArgs['contain']['CoPetitionHistoryRecord'][0] = 'ActorCoPerson';
+ $pArgs['contain']['CoPetitionHistoryRecord']['ActorCoPerson'] =
'Name';
+
+ $petition = $this->CoInvite->CoPetition->find('all', $pArgs);
+
+ $this->set('co_petitions', $petition);
+
+ $vArgs = array();
+ $vArgs['conditions']['CoPetitionAttribute.co_petition_id'] =
$invite['CoPetition']['id'];
+ $vArgs['fields'] = array(
+ 'CoPetitionAttribute.attribute',
+ 'CoPetitionAttribute.value',
+ 'CoPetitionAttribute.co_enrollment_attribute_id'
+ );
+
+ $vAttrs =
$this->CoInvite->CoPetition->CoPetitionAttribute->find("list", $vArgs);
+
+ $this->set('co_petition_attribute_values', $vAttrs);
+ }
}
}

@@ -357,10 +431,6 @@

if($cop)
{
- // Toss any prior invitations for $cpid
-
- $this->CoInvite->deleteAll(array("co_person_id" => $cpid));
-
// Find the associated Org Identity to get an email address

// XXX fix this getting link to get org identity
@@ -378,76 +448,40 @@
if(isset($orgp) && count($orgp['EmailAddress']) > 0)
{
// XXX There could be multiple email addresses, we'll use the first
one
- // (but could allow the inviter to select one)
+ // (but we could allow one to be selected)

- // XXX make expiration time configurable
- $invite = array("CoInvite" => array('co_person_id' => $cpid,
- 'invitation' =>
Security::generateAuthKey(),
- 'mail' =>
$orgp['EmailAddress'][0]['mail'],
- 'expires' => date('Y-m-d H:i:s',
strtotime('+1 day')))); // XXX date format may not be portable
-
- $this->CoInvite->create($invite);
+ try {
+ $this->CoInvite->send($cpid,
+ $orgp['OrgIdentity']['id'],
+
$this->Session->read('Auth.User.co_person_id'),
+ $orgp['EmailAddress'][0]['mail'],
+
isset($this->cur_co['CoEnrollmentFlow'][0]['notify_from'])
+ ?
$this->cur_co['CoEnrollmentFlow'][0]['notify_from']
+ : null,
+ $this->cur_co['Co']['name']);
+ }
+ catch(Exception $e) {
+ $this->Session->setFlash($e->getMessage(), '', array(), 'error');
+ }
+
+ // Set CO Person status to I
+ // XXX probably don't want to do this if status = A. May need a new
password reset status.

- if($this->CoInvite->save()) {
- // Set up and send the invitation via email
- $email = new CakeEmail('default');
- $viewVariables = $invite;
- $viewVariables['Co'] = $this->cur_co['Co'];
-
- try {
- $email->template('coinvite', 'basic')
- ->emailFormat('text')
- ->to($orgp['EmailAddress'][0]['mail'])
- ->viewVars($viewVariables)
- ->subject(_txt('em.invite.subject',
array($this->cur_co['Co']['name'])));
-
- // If this enrollment has a default email address set, use it,
otherwise leave in the default for the site.
-
if(isset($this->viewVars['cur_co']['CoEnrollmentFlow'][0]['notify_from']))
-
$email->from($this->viewVars['cur_co']['CoEnrollmentFlow'][0]['notify_from']);
-
- // Send the email
- $email->send();
-
- // Notify user of success
- $this->Session->setFlash(_txt('em.invite.ok',
-
array($orgp['EmailAddress'][0]['mail'])),
- '',
- array(),
- 'success');
-
- } catch(Exception $e) {
- // Display error to user
- $this->Session->setFlash($e->getMessage(), '', array(), 'error');
- }
- // Set CO Person status to I
- // XXX probably don't want to do this if status = A. May need a
new password reset status.
-
- $this->CoInvite->CoPerson->id = $cpid;
-
- if($this->CoInvite->CoPerson->saveField('status', 'I'))
+ $this->CoInvite->CoPerson->id = $cpid;
+
+ if($this->CoInvite->CoPerson->saveField('status', 'I'))
+ {
+ if($this->restful)
{
- if($this->restful)
- {
- // $this->restResultHeader(201, "Sent");
- $this->restResultHeader(501, "Not Implemented");
- $this->set('co_invite_id', $this->CoInvite->id);
- }
- else
- {
- $this->set('cur_co', $this->cur_co);
- $this->set('invite',
$this->CoInvite->findById($this->CoInvite->id));
- $this->set('invitee', $cop);
- }
+ // $this->restResultHeader(201, "Sent");
+ $this->restResultHeader(501, "Not Implemented");
+ $this->set('co_invite_id', $this->CoInvite->id);
}
else
{
- if($this->restful)
- {
- $this->restResultHeader(400, "Invalid Fields");
- $this->set('invalid_fields', $this->CoInvite->invalidFields());
- }
- else
-
$this->Session->setFlash($this->fieldsErrorToString($this->CoInvite->CoPerson->invalidFields()),
'', array(), 'error');
+ $this->set('cur_co', $this->cur_co);
+ $this->set('invite',
$this->CoInvite->findById($this->CoInvite->id));
+ $this->set('invitee', $cop);
}
}
else
@@ -458,7 +492,7 @@
$this->set('invalid_fields', $this->CoInvite->invalidFields());
}
else
-
$this->Session->setFlash($this->fieldsErrorToString($this->CoInvite->invalidFields()),
'', array(), 'error');
+
$this->Session->setFlash($this->fieldsErrorToString($this->CoInvite->CoPerson->invalidFields()),
'', array(), 'error');
}
}
else

Modified: registry/trunk/app/Controller/CoOrgIdentityLinksController.php
===================================================================
--- registry/trunk/app/Controller/CoOrgIdentityLinksController.php
2012-09-24 06:33:39 UTC (rev 364)
+++ registry/trunk/app/Controller/CoOrgIdentityLinksController.php
2012-09-26 00:53:01 UTC (rev 365)
@@ -119,9 +119,14 @@

ActionEnum::CoPersonOrgIdLinked);
break;
case 'delete':
- // As of v0.7, unlinking preceeds deletion of an identity/person
that will therefore
- // cause this history record to be deleted. As such, we don't
record anything. This is
- // subject to being revisited in a future release.
+ // In most cases, unlinking preceeds deletion of an
identity/person that will therefore
+ // cause this history record to be deleted. However, if multiple
identities are linked
+ // to a person and one is deleted, we want to record that since it
will probably stick around.
+
$this->CoOrgIdentityLink->CoPerson->HistoryRecord->record($olddata['CoOrgIdentityLink']['co_person_id'],
+ null,
+
$olddata['CoOrgIdentityLink']['org_identity_id'],
+
$this->Session->read('Auth.User.co_person_id'),
+
ActionEnum::CoPersonOrgIdUnlinked);
break;
case 'edit':
// As of v0.7, we don't really have a use case for editing a link.
@@ -167,4 +172,27 @@
$this->set('permissions', $p);
return($p[$this->action]);
}
+
+ /**
+ * Perform a redirect back to the controller's default view.
+ * - postcondition: Redirect generated
+ *
+ * @since COmanage Registry v0.1
+ */
+
+ function performRedirect() {
+ // Generally speaking, this controller isn't called via the web
interface.
+ // An exception is unlinking an org identity, so we redirect back to the
CO Person.
+
+ if(isset($this->CoOrgIdentityLink->data['CoPerson']['id'])) {
+ $this->redirect(array('controller' => 'co_people',
+ 'action' => 'edit',
+ $this->CoOrgIdentityLink->data['CoPerson']['id'],
+ 'co' =>
Sanitize::html($this->cur_co['Co']['id'])));
+ } else {
+ $this->redirect(array('controller' => 'co_people',
+ 'action' => 'index',
+ 'co' =>
Sanitize::html($this->cur_co['Co']['id'])));
+ }
+ }
}

Modified: registry/trunk/app/Controller/CoPeopleController.php
===================================================================
--- registry/trunk/app/Controller/CoPeopleController.php 2012-09-24
06:33:39 UTC (rev 364)
+++ registry/trunk/app/Controller/CoPeopleController.php 2012-09-26
00:53:01 UTC (rev 365)
@@ -425,8 +425,25 @@

// Match against existing CO People?
// Note this same permission exists in CO Petitions
- $p['match'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));

+ // Some operations are authorized according to the flow configuration.
+ $flowAuthorized = false;
+
+ // If an enrollment flow was specified, check the authorization for that
flow
+
+ $p['match'] = false;
+
+ if(isset($this->request->named['coef'])) {
+ $flowAuthorized =
$this->CoPerson->Co->CoPetition->CoEnrollmentFlow->authorizeById($this->request->named['coef'],
+
$cmr['copersonid']);
+
+ $p['match_policy'] =
$this->CoPerson->Co->CoPetition->CoEnrollmentFlow->field('match_policy',
+
array('CoEnrollmentFlow.id' => $this->request->named['coef']));
+ $p['match'] = ($flowAuthorized &&
+ ($p['match_policy'] ==
EnrollmentMatchPolicyEnum::Advisory
+ || $p['match_policy'] ==
EnrollmentMatchPolicyEnum::Automatic));
+ }
+
// View an existing CO Person?
$p['view'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']) || $self);


Modified: registry/trunk/app/Controller/CoPetitionsController.php
===================================================================
--- registry/trunk/app/Controller/CoPetitionsController.php 2012-09-24
06:33:39 UTC (rev 364)
+++ registry/trunk/app/Controller/CoPetitionsController.php 2012-09-26
00:53:01 UTC (rev 365)
@@ -72,26 +72,47 @@
*/

function add() {
- if(!$this->restful && $this->request->is('post')) {
+ if(!$this->restful) {
$enrollmentFlowID = $this->enrollmentFlowID();
-
- // Set the view var. We need this on both success and failure.

- $this->set('co_enrollment_attributes',
-
$this->CoPetition->CoEnrollmentFlow->CoEnrollmentAttribute->enrollmentFlowAttributes($enrollmentFlowID));
-
- try {
- $this->CoPetition->createPetition($enrollmentFlowID,
- $this->cur_co['Co']['id'],
- $this->request->data,
-
$this->Session->read('Auth.User.co_person_id'));
+ if($this->request->is('post')) {
+ // Set the view var. We need this on both success and failure.
+
+ $this->set('co_enrollment_attributes',
+
$this->CoPetition->CoEnrollmentFlow->CoEnrollmentAttribute->enrollmentFlowAttributes($enrollmentFlowID));

- $this->Session->setFlash(_txt('rs.pt.create'), '', array(),
'success');
- $this->performRedirect();
+ try {
+ $this->CoPetition->createPetition($enrollmentFlowID,
+ $this->cur_co['Co']['id'],
+ $this->request->data,
+
$this->Session->read('Auth.User.co_person_id'));
+
+ $matchPolicy =
$this->CoPetition->CoEnrollmentFlow->field('match_policy',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ $authnReq =
$this->CoPetition->CoEnrollmentFlow->field('require_authn',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ if($matchPolicy == EnrollmentMatchPolicyEnum::Self) {
+ if($authnReq) {
+ $this->Session->setFlash(_txt('rs.pt.login'), '', array(),
'success');
+ $this->redirect("/auth/logout");
+ } else {
+ // Not really clear where to send a self-enrollment person...
+ $this->Session->setFlash(_txt('rs.pt.create'), '', array(),
'success');
+ $this->redirect("/");
+ }
+ } else {
+ $this->Session->setFlash(_txt('rs.pt.create'), '', array(),
'success');
+ $this->performRedirect();
+ }
+ }
+ catch(Exception $e) {
+ $this->Session->setFlash($e->getMessage(), '', array(), 'error');
+ }
+ } else {
+ parent::add();
}
- catch(Exception $e) {
- $this->Session->setFlash($e->getMessage(), '', array(), 'error');
- }
} else {
// REST API gets standard behavior

@@ -163,8 +184,34 @@
if(($this->action == 'add' || $this->action == 'edit' || $this->action
== 'view')
&& $this->request->is('get')) {
// If we processed a post, this will have already been set.
+
+ $defaultValues = array();
+
+ $enrollmentFlowID = $this->enrollmentFlowID();
+
+ if($enrollmentFlowID) {
+ // Provide default values for name for self enrollment.
+
+ $p['match_policy'] =
$this->CoPetition->CoEnrollmentFlow->field('match_policy',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ if($p['match_policy'] == EnrollmentMatchPolicyEnum::Self) {
+ $defName = $this->Session->read('Auth.User.name');
+
+ if(!empty($defName)) {
+ // Populate select attributes only
+ $defaultValues['EnrolleeOrgIdentity.Name']['honorific'] =
$defName['honorific'];
+ $defaultValues['EnrolleeOrgIdentity.Name']['given'] =
$defName['given'];
+ $defaultValues['EnrolleeOrgIdentity.Name']['middle'] =
$defName['middle'];
+ $defaultValues['EnrolleeOrgIdentity.Name']['family'] =
$defName['family'];
+ $defaultValues['EnrolleeOrgIdentity.Name']['suffix'] =
$defName['suffix'];
+ }
+ }
+ }
+
$this->set('co_enrollment_attributes',
-
$this->CoPetition->CoEnrollmentFlow->CoEnrollmentAttribute->enrollmentFlowAttributes($this->enrollmentFlowID()));
+
$this->CoPetition->CoEnrollmentFlow->CoEnrollmentAttribute->enrollmentFlowAttributes($this->enrollmentFlowID(),
+
$defaultValues));
}

if(($this->action == 'edit' || $this->action == 'view')
@@ -253,34 +300,42 @@

// If an enrollment flow was specified, check the authorization for that
flow

- if(isset($this->request->named['coef'])) {
- $flowAuthorized =
$this->CoPetition->CoEnrollmentFlow->authorizeById($this->request->named['coef'],
-
$cmr['copersonid']);
+ if($this->enrollmentFlowID() != -1) {
+ $flowAuthorized =
$this->CoPetition->CoEnrollmentFlow->authorizeById($this->enrollmentFlowID(),
$cmr['copersonid']);
}

// Add a new CO Petition?
- $p['add'] = ($flowAuthorized
- && ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin'])));
+ $p['add'] = $flowAuthorized
+ // Or we have an index view
+ || ($this->enrollmentFlowID() == -1 & ($cmr['cmadmin'] ||
$cmr['coadmin'] || !empty($cmr['couadmin'])));

// Approve a CO Petition?
$p['approve'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));
$p['deny'] = $p['approve'];

// Delete an existing CO Petition?
- $p['delete'] = ($flowAuthorized
- && ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin'])));
+ // For now, this is restricted to CMP and CO Admins, until we have a
better policy
+ $p['delete'] = ($cmr['cmadmin'] || $cmr['coadmin']);

// Edit an existing CO Petition?
- $p['edit'] = ($flowAuthorized
- && ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin'])));
+ $p['edit'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));

- // Match against existing CO People?
+ // Match against existing CO People? If the match policy is Advisory or
Automatic, we
+ // allow matching to take place as long as $flowAuthorized is also true.
// Note this same permission exists in CO People
- $p['match'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));

+ $p['match_policy'] =
$this->CoPetition->CoEnrollmentFlow->field('match_policy',
+
array('CoEnrollmentFlow.id' => $this->enrollmentFlowID()));
+ $p['match'] = ($flowAuthorized &&
+ ($p['match_policy'] == EnrollmentMatchPolicyEnum::Advisory
+ || $p['match_policy'] ==
EnrollmentMatchPolicyEnum::Automatic));
+
// View all existing CO Petitions?
$p['index'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));
-
+
+ // Resend invitations?
+ $p['resend'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));
+
// View an existing CO Petition? We allow the usual suspects to view a
Petition, even
// if they don't have permission to edit it.
$p['view'] = ($cmr['cmadmin'] || $cmr['coadmin'] ||
!empty($cmr['couadmin']));
@@ -309,4 +364,36 @@
parent::performRedirect();
}
}
+
+ /**
+ * Resend an invitation associated with a Petition.
+ * - precondition: Petition exists in a Pending Confirmation state
+ * - postcondition: Invitation sent
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer CO Petition ID
+ */
+
+ public function resend($id) {
+ $recipient = null;
+
+ try {
+ $recipient = $this->CoPetition->resend($id);
+ }
+ catch(Exception $e) {
+ $this->Session->setFlash($e->getMessage(), '', array(), 'error');
+ }
+
+ if($recipient) {
+ $this->Session->setFlash(_txt('rs.inv.sent', array($recipient)), '',
array(), 'success');
+ }
+
+ // Redirect back to index
+
+ $this->redirect(array(
+ 'controller' => 'co_petitions',
+ 'action' => 'index',
+ 'co' => $this->cur_co['Co']['id']
+ ));
+ }
}

Modified: registry/trunk/app/Lib/enum.php
===================================================================
--- registry/trunk/app/Lib/enum.php 2012-09-24 06:33:39 UTC (rev 364)
+++ registry/trunk/app/Lib/enum.php 2012-09-26 00:53:01 UTC (rev 365)
@@ -29,16 +29,23 @@
const CoPersonAddedPetition = 'ACPP';
const CoPersonEditedManual = 'ECPM';
const CoPersonEditedPetition = 'ECPP';
+ const CoPersonMatchedPetition = 'MCPP';
const CoPersonRoleAddedManual = 'ACRM';
const CoPersonRoleAddedPetition = 'ACRP';
const CoPersonRoleDeletedManual = 'DCRM';
const CoPersonRoleEditedManual = 'ECRM';
const CoPersonRoleEditedPetition = 'ECRP';
const CoPersonOrgIdLinked = 'LOCP';
+ const CoPersonOrgIdUnlinked = 'UOCP';
+ const EmailAddressVerified = 'EMLV';
const IdentifierAutoAssigned = 'AIDA';
+ const InvitationConfirmed = 'INVC';
+ const InvitationDeclined = 'INVD';
+ const InvitationSent = 'INVS';
const OrgIdAddedManual = 'AOIM';
const OrgIdAddedPetition = 'AOIP';
const OrgIdEditedManual = 'EOIM';
+ const OrgIdEditedPetition = 'EOIP';
}

class AdministratorEnum
@@ -110,6 +117,13 @@
const None = 'N';
}

+class EnrollmentMatchPolicyEnum {
+ const Advisory = "A";
+ const Automatic = "M";
+ const None = "N";
+ const Self = "S";
+}
+
class ExtendedAttributeEnum {
const Integer = 'INTEGER';
const Timestamp = 'TIMESTAMP';
@@ -205,6 +219,8 @@
const Denied = 'PN';
const Finalized = 'PF';
const IdentifiersAssigned = 'IA';
+ const InviteConfirmed = 'IC';
+ const InviteSent = 'IS';
}

class RequiredEnum
@@ -228,15 +244,16 @@

class StatusEnum
{
- const Active = 'A';
- const Approved = 'Y';
- const Deleted = 'D';
- const Denied = 'N';
- const Invited = 'I';
- const Pending = 'P';
- const PendingApproval = 'PA';
- const Suspended = 'S';
- const Declined = 'X';
+ const Active = 'A';
+ const Approved = 'Y';
+ const Deleted = 'D';
+ const Denied = 'N';
+ const Invited = 'I';
+ const Pending = 'P';
+ const PendingApproval = 'PA';
+ const PendingConfirmation = 'PC';
+ const Suspended = 'S';
+ const Declined = 'X';
/*
public $from_api = array(
"Active" => Active,

Modified: registry/trunk/app/Lib/lang.php
===================================================================
--- registry/trunk/app/Lib/lang.php 2012-09-24 06:33:39 UTC (rev 364)
+++ registry/trunk/app/Lib/lang.php 2012-09-26 00:53:01 UTC (rev 365)
@@ -111,16 +111,23 @@
ActionEnum::CoPersonAddedPetition => 'CO Person Created
(Petition)',
ActionEnum::CoPersonEditedManual => 'CO Person Edited',
ActionEnum::CoPersonEditedPetition => 'CO Person Edited (Petition)',
+ ActionEnum::CoPersonMatchedPetition => 'CO Person Matched
(Petition)',
ActionEnum::CoPersonRoleAddedManual => 'CO Person Role Created
(Manual)',
ActionEnum::CoPersonRoleAddedPetition => 'CO Person Role Created
(Petition)',
ActionEnum::CoPersonRoleDeletedManual => 'CO Person Role Deleted
(Manual)',
ActionEnum::CoPersonRoleEditedManual => 'CO Person Role Edited',
ActionEnum::CoPersonRoleEditedPetition => 'CO Person Role Edited
(Petition)',
ActionEnum::CoPersonOrgIdLinked => 'CO Person and Org Identity
Linked',
+ ActionEnum::CoPersonOrgIdUnlinked => 'CO Person and Org Identity
Unlinked',
+ ActionEnum::EmailAddressVerified => 'Email Address Verified',
ActionEnum::IdentifierAutoAssigned => 'Identifier Auto Assigned',
+ ActionEnum::InvitationConfirmed => 'Invitation Confirmed',
+ ActionEnum::InvitationDeclined => 'Invitation Declined',
+ ActionEnum::InvitationSent => 'Invitation Sent',
ActionEnum::OrgIdAddedManual => 'Org Identity Created
(Manual)',
ActionEnum::OrgIdAddedPetition => 'Org Identity Created
(Petition)',
- ActionEnum::OrgIdEditedManual => 'Org Identity Edited'
+ ActionEnum::OrgIdEditedManual => 'Org Identity Edited
(Manual)',
+ ActionEnum::OrgIdEditedPetition => 'Org Identity Edited
(Petition)',
),

'en.admin' => array(AdministratorEnum::NoAdmin => 'None',
@@ -168,6 +175,13 @@
EnrollmentAuthzEnum::None => 'None'
),

+ 'en.enrollment.match' => array(
+ EnrollmentMatchPolicyEnum::Advisory => 'Advisory',
+ EnrollmentMatchPolicyEnum::Automatic => 'Automatic',
+ EnrollmentMatchPolicyEnum::None => 'None',
+ EnrollmentMatchPolicyEnum::Self => 'Self'
+ ),
+
'en.extattr' => array(ExtendedAttributeEnum::Integer => 'Integer',
ExtendedAttributeEnum::Timestamp => 'Timestamp',
ExtendedAttributeEnum::Varchar32 => 'String
(32)'),
@@ -190,14 +204,15 @@
RequiredEnum::Optional => 'Optional',
RequiredEnum::NotPermitted => 'Not Permitted'),

- 'en.status' => array(StatusEnum::Active => 'Active',
- StatusEnum::Approved => 'Approved',
- StatusEnum::Declined => 'Declined',
- StatusEnum::Denied => 'Denied',
- StatusEnum::Invited => 'Invited',
- StatusEnum::Pending => 'Pending',
- StatusEnum::PendingApproval => 'Pending
Approval',
- StatusEnum::Suspended => 'Suspended'),
+ 'en.status' => array(StatusEnum::Active => 'Active',
+ StatusEnum::Approved => 'Approved',
+ StatusEnum::Declined => 'Declined',
+ StatusEnum::Denied => 'Denied',
+ StatusEnum::Invited => 'Invited',
+ StatusEnum::Pending => 'Pending',
+ StatusEnum::PendingApproval => 'Pending
Approval',
+ StatusEnum::PendingConfirmation => 'Pending
Confirmation',
+ StatusEnum::Suspended => 'Suspended'),

// Demographics
'en.nsf.gender' => array(NSFGenderEnum::Male => 'Male',
@@ -232,6 +247,7 @@
NSFDisabilityEnum::Other => 'Other
Impairment'),

// Errors
+ 'er.auth' => 'Not authenticated',
'er.co.cm.edit' => 'Cannot edit COmanage CO',
'er.co.cm.rm' => 'Cannot remove COmanage CO',
'er.co.exists' => 'A CO named "%1$s" already exists',
@@ -293,9 +309,10 @@
'er.person.noex' => 'Person does not exist',
'er.person.none' => 'No CO Person, CO Person Role, or Org Identity
specified',
'er.pt.status' => 'Change of petition status from %1$s to %2$s is not
permitted',
+ 'er.pt.resend.status' => 'Cannot resend an invitation not in Pending
Confirmation status',
'er.reply.unk' => 'Unknown Reply',
'er.timeout' => 'Your session has expired. Please login again.',
- 'er.orgp.nomail' => '%1$s (Org Identity %2$s) has no known email
address.<br />Add an email address and then resend the invitation.',
+ 'er.orgp.nomail' => '%1$s (Org Identity %2$s) has no known email
address.<br />Add an email address and then try again.',
'er.orgp.pool' => 'Failed to pool organizational identities',
'er.orgp.unk-a' => 'Unknown Org Identity "%1$s"',
'er.orgp.unpool' => 'Failed to unpool organizational identities',
@@ -354,8 +371,12 @@
'fd.ef.aee.desc' => 'If administrator enrollment is enabled, require
enrollees to confirm their email address in order to complete their
enrollment',
'fd.ef.appr' => 'Require Approval For Enrollment',
'fd.ef.appr.desc' => 'If administrator approval is required, a member of
the appropriate <tt>admin.approvers</tt> group must approve the enrollment',
+ 'fd.ef.authn' => 'Require Authentication',
+ 'fd.ef.authn.desc' => 'Require enrollee to authenticate in order to
complete their enrollment',
'fd.ef.authz' => 'Enrollment Authorization',
'fd.ef.authz.desc' => 'Authorization required to execute this enrollment
flow, see <a
href="https://spaces.internet2.edu/display/COmanage/Registry+Enrollment+Flow+Configuration#RegistryEnrollmentFlowConfiguration-EnrollmentAuthorization";>Enrollment
Authorization</a> for details',
+ 'fd.ef.ce' => 'Require Confirmation of Email',
+ 'fd.ef.ce.desc' => 'Confirm email addresses provided by sending a
confirmation URL to the address',
'fd.ef.cf.cmp' => 'Platform Enrollment Configuration',
'fd.ef.coef' => 'Enable Attributes Via CO Enrollment Flow',
'fd.ef.coef.desc' => 'If enabled, allow organizational identity attributes
to be collected via forms during CO enrollment flows (these attributes will
be less authoritative than those obtained via LDAP or SAML)',
@@ -365,6 +386,8 @@
'fd.ef.epx.desc' => '(Need for this TBD)',
'fd.ef.ldap' => 'Enable LDAP Attribute Retrieval',
'fd.ef.ldap.desc' => 'If the enrollee is affiliated with an organization
with a known LDAP server, query the LDAP server for authoritative attributes',
+ 'fd.ef.match' => 'Identity Matching',
+ 'fd.ef.match.desc' => 'Identity Matching policy for this enrollment flow,
see <a
href="https://spaces.internet2.edu/display/COmanage/Registry+Enrollment+Flow+Configuration#RegistryEnrollmentFlowConfiguration-IdentityMatching";>Identity
Matching</a> for details',
'fd.ef.noa' => 'Notify On Active Status',
'fd.ef.noa.desc' => 'Email address to notify upon status being set to
active',
'fd.ef.noep' => 'Notify On Early Provisioning',
@@ -384,6 +407,8 @@
// (End enrollment configuration fields)
// This must be named fd.model.validation-field
'fd.email_address.mail' => 'Email',
+ 'fd.email_address.verified' => 'Verified',
+ 'fd.email_address.unverified' => 'Unverified',
'fd.enrollee' => 'Enrollee',
'fd.false' => 'False',
'fd.group.desc.adm' => '%1$s Administrators',
@@ -416,6 +441,8 @@
'fd.identifier.login.desc' => 'Allow this identifier to login to
Registry',
'fd.ids' => 'Identifiers',
'fd.index' => 'Index',
+ 'fd.inv.for' => 'Invitation for %1$s',
+ 'fd.inv.to' => 'Invitation to %1$s',
'fd.lan.desc' => 'Lowercase alphanumeric characters only',
'fd.members' => 'Members',
'fd.modified' => 'Modified',
@@ -470,6 +497,7 @@
'me.population' => 'My Population',

// Operations
+ 'op.accept' => 'Accept',
'op.add' => 'Add',
'op.add-a' => 'Add "%1$s"',
'op.add.new' => 'Add a New %1$s',
@@ -478,7 +506,9 @@
'op.begin' => 'Begin',
'op.cancel' => 'Cancel',
'op.compare' => 'Compare',
+ 'op.confirm' => 'Confirm',
'op.db.ok' => 'Database schema update successful',
+ 'op.decline' => 'Decline',
'op.delete' => 'Delete',
'op.delete.consfdemographics' => 'this NSF demographic entry',
'op.delete.ok' => 'Are you sure you wish to remove "%1$s"? This action
cannot be undone.',
@@ -510,6 +540,8 @@
'op.save' => 'Save',
'op.select' => 'Select',
'op.select-a' => 'Select a %1$s',
+ 'op.unlink' => 'Unlink',
+ 'op.unlink.confirm' => 'Are you sure you wish to unlink this identity?',
'op.view' => 'View',
'op.view-a' => 'View "%1$s"',

@@ -518,10 +550,19 @@
'rs.added-a' => '"%1$s" Added',
'rs.ia.ok' => 'Identifiers Assigned',
'rs.inv.conf' => 'Invitation Confirmed',
+ 'rs.inv.conf-a' => 'Invitation to %1$s confirmed',
'rs.inv.dec' => 'Invitation Declined',
- 'rs.pt.approve' => 'Petition Approved',
+ 'rs.inv.dec-a' => 'Invitation to %1$s declined',
+ 'rs.inv.sent' => 'Invitation sent to %1$s',
+ 'rs.mail.verified' => 'Email Address "%1$s" verified',
+ 'rs.pt.approve' => 'Petition Approved',
+ 'rs.pt.confirm' => 'Petition Confirmed',
'rs.pt.create' => 'Petition Created',
- 'rs.pt.deny' => 'Petition Denied',
+ 'rs.pt.deny' => 'Petition Denied',
+ 'rs.pt.id.attached' => 'Authenticated identifier "%1$s" attached to
organizational identity',
+ 'rs.pt.id.login' => 'Identifier "%1$s" flagged for login',
+ 'rs.pt.login' => 'Petition Created. You have been logged out, and an
activation URL has been sent to your email address. Please click the link in
that email to continue.',
+ 'rs.pt.relogin' => 'Petition Confirmed. You have been logged out, and
will need to login again for your new identity to take effect.',
'rs.updated' => '"%1$s" Updated',

// Setup

Modified: registry/trunk/app/Model/CoEnrollmentAttribute.php
===================================================================
--- registry/trunk/app/Model/CoEnrollmentAttribute.php 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/Model/CoEnrollmentAttribute.php 2012-09-26 00:53:01
UTC (rev 365)
@@ -166,10 +166,11 @@
*
* @since COmanage Registry 0.5
* @param integer CO Enrollment Flow ID
+ * @param Array Default values, keyed on Model name
* @return Array Configured attributes and metadata
*/

- public function enrollmentFlowAttributes($coef) {
+ public function enrollmentFlowAttributes($coef, $defaultValues=array()) {
$attrs = array();

// First, retrieve the configured attributes
@@ -245,6 +246,11 @@
// Field, in cake's Model.field
$attr['field'] = $attrName;

+ // See if there is a default value for this field
+ if(isset($defaultValues[ $attr['model'] ][ $attr['field'] ])) {
+ $attr['default'] = $defaultValues[ $attr['model'] ][
$attr['field'] ];
+ }
+
// Attach the validation rules so the form knows how to render the
field.
if($attrCode == 'o') {
$attr['validate'] = $attrModel->validate[$attrName];
@@ -375,8 +381,8 @@
&&
!$attrModel->validate[$k]['allowEmpty']);

- // We hide type and status
- $attr['hidden'] = ($k == 'type' || $k == 'status' ? 1 : 0);
+ // We hide type, status, and verified
+ $attr['hidden'] = ($k == 'type' || $k == 'status' || $k ==
'verified' ? 1 : 0);

if($attr['hidden']) {
// Populate a default value.
@@ -390,6 +396,10 @@
// For now, status is always set to Active
$attr['default'] = StatusEnum::Active;
break;
+ case 'verified':
+ // Verified defaults to false
+ $attr['default'] = 0;
+ break;
}

} else {
@@ -406,6 +416,11 @@
// Field, in cake's Model.field
$attr['field'] = $k;

+ // See if there is a default value for this field
+ if(isset($defaultValues[$m][$k])) {
+ $attr['default'] = $defaultValues[$m][$k];
+ }
+
// Attach the validation rules so the form knows how to render
the field.
$attr['validate'] = $attrModel->validate[$k];


Modified: registry/trunk/app/Model/CoEnrollmentFlow.php
===================================================================
--- registry/trunk/app/Model/CoEnrollmentFlow.php 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/Model/CoEnrollmentFlow.php 2012-09-26 00:53:01
UTC (rev 365)
@@ -73,17 +73,42 @@
'required' => true,
'message' => 'A CO ID must be provided'
),
- 'self_enroll' => array(
- 'rule' => array('boolean')
+ 'authz_level' => array(
+ 'rule' => array('inList',
+ array(EnrollmentAuthzEnum::CoAdmin,
+ EnrollmentAuthzEnum::CoGroupMember,
+ EnrollmentAuthzEnum::CoOrCouAdmin,
+ EnrollmentAuthzEnum::CoPerson,
+ EnrollmentAuthzEnum::CouAdmin,
+ EnrollmentAuthzEnum::CouPerson,
+ EnrollmentAuthzEnum::None))
),
- 'admin_enroll' => array(
- 'rule' => array('inList', array(AdministratorEnum::NoAdmin,
- AdministratorEnum::CoOrCouAdmin,
- AdministratorEnum::CoAdmin))
+ 'authz_cou_id' => array(
+ 'rule' => 'numeric',
+ 'required' => false,
+ 'allowEmpty' => true
),
+ 'authz_co_group_id' => array(
+ 'rule' => 'numeric',
+ 'required' => false,
+ 'allowEmpty' => true
+ ),
+ 'match_policy' => array(
+ 'rule' => array('inList',
+ array(EnrollmentMatchPolicyEnum::Advisory,
+ EnrollmentMatchPolicyEnum::Automatic,
+ EnrollmentMatchPolicyEnum::None,
+ EnrollmentMatchPolicyEnum::Self))
+ ),
'approval_required' => array(
'rule' => array('boolean')
),
+ 'confirm_email' => array(
+ 'rule' => array('boolean')
+ ),
+ 'require_authn' => array(
+ 'rule' => array('boolean')
+ ),
'notify_on_early_provision' => array(
'rule' => 'email',
'allowEmpty' => true

Modified: registry/trunk/app/Model/CoInvite.php
===================================================================
--- registry/trunk/app/Model/CoInvite.php 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/Model/CoInvite.php 2012-09-26 00:53:01 UTC (rev
365)
@@ -22,6 +22,8 @@
* @version $Id$
*/

+App::uses('CakeEmail', 'Network/Email');
+
class CoInvite extends AppModel {
// Define class name for cake
public $name = "CoInvite";
@@ -29,9 +31,14 @@
// Current schema version for API
public $version = "1.0";

+ // Add behaviors
+ public $actsAs = array('Containable');
+
// Association rules from this model to other models
public $belongsTo = array("CoPerson"); // An invitation belongs to a CO
Person

+ public $hasOne = array("CoPetition");
+
// Default display field for cake generated views
public $displayField = "invitation";

@@ -57,4 +64,248 @@
'required' => false
)
);
+
+ /**
+ * Process the reply to an invite.
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer CO Invite ID
+ * @param Boolean If true, confirm the invitation; if false, decline it
+ * @param String Login identifier, if authentication is needed for this
invite
+ * @throws InvalidArgumentException
+ * @throws OutOfBoundsException
+ * @throws RuntimeException
+ */
+
+ function processReply($inviteId, $confirm, $loginIdentifier=null) {
+ $args = array();
+ $args['conditions']['CoInvite.invitation'] = $inviteId;
+
+ // Start a transaction
+ $dbc = $this->getDataSource();
+ $dbc->begin();
+
+ $invite = $this->find('first', $args);
+
+ if($invite) {
+ // Check invite validity
+
+ if(time() < strtotime($invite['CoInvite']['expires'])) {
+ if(isset($invite['CoPetition']['id'])) {
+ // Let CoPetition handle the relevant updates
+
+ // We don't bother checking if confirm_email is still true, since
it's not really
+ // clear how we should handle the enrollment flow configuration
being changed
+ // in the middle of an enrollment.
+
+ if($confirm) {
+ // If a login identifier was provided, attach it to the org
identity if not already present.
+
+ if($loginIdentifier) {
+ // Validate the identifier, even if null. (If null but authn
was required, we'll
+ // get an Exception, which will ultimately pass back up to a
redirect.)
+
+ try {
+
$this->CoPetition->validateIdentifier($invite['CoPetition']['id'],
+ $loginIdentifier,
+
$invite['CoPetition']['enrollee_co_person_id']);
+ }
+ catch(RuntimeException $e) {
+ // Re-throw the exception
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+
+ // Update status to Approved. If this petition requires
approval, updateStatus()
+ // will detect that and change the status to PendingApproval
instead.
+
+ try {
+ $this->CoPetition->updateStatus($invite['CoPetition']['id'],
+ StatusEnum::Approved,
+
$invite['CoPetition']['enrollee_co_person_id']);
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ } else {
+ // Simply deny the petition. We're not authenticated, so we just
assume the
+ // enrollee CO Person is also the actor.
+
+ try {
+ $this->CoPetition->updateStatus($invite['CoPetition']['id'],
+ StatusEnum::Denied,
+
$invite['CoPetition']['enrollee_co_person_id']);
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+
+ // Before we can delete the invitation, we need to unlink it from
the petition
+
+ $this->CoPetition->id = $invite['CoPetition']['id'];
+ $this->CoPetition->saveField('co_invite_id', null);
+ } else {
+ // Default (ie: non-enrollment flow) behavior: update CO Person
+
+ $this->CoPerson->id = $invite['CoPerson']['id'];
+
+ if(!$this->CoInvite->CoPerson->saveField('status', $confirm ?
StatusEnum::Active : StatusEnum::Declined)) {
+ $dbc->rollback();
+ throw new RuntimeException(_txt('er.cop.nf',
array($invite['CoPerson']['id'])));
+ }
+ }
+
+ // Mark the email address associated with this invite as verified.
+
+ if($confirm) {
+ // We're actually verifying an org identity email address even
though we're
+ // getting to the EmailAddress object via CoPerson
+
+ $orgId = null;
+
+ if(isset($invite['CoPetition']['enrollee_org_identity_id'])) {
+ $orgId = $invite['CoPetition']['enrollee_org_identity_id'];
+ } elseif(empty($invite['CoPetition'])) {
+ // Try to find the org identity associated with this invite
+
+ $args = array();
+ $args['conditions']['CoOrgIdentityLink.co_person_id'] =
$invite['CoPerson']['co_person_id'];
+ $args['conditions']['EmailAddress.mail'] =
$invite['CoInvite']['mail'];
+ $args['joins'][0]['table'] = 'cm_email_addresses';
+ $args['joins'][0]['alias'] = 'EmailAddress';
+ $args['joins'][0]['type'] = 'INNER';
+ $args['joins'][0]['conditions'][0] =
'CoOrgIdentityLink.org_identity_id=EmailAddress.org_identity_id';
+ $args['contain'] = false;
+
+ // This *should* generate one result...
+ $link = $this->CoPerson->CoOrgIdentityLink->find('first', $args);
+
+ if(!empty($link['CoOrgIdentityLink']['org_identity_id'])) {
+ $orgId = $link['CoOrgIdentityLink']['org_identity_id'];
+ }
+ }
+
+ if($orgId) {
+ try {
+ $this->CoPerson->EmailAddress->verify($orgId,
$invite['CoInvite']['mail'], $invite['CoPetition']['enrollee_co_person_id']);
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+ }
+
+ // Toss the invite
+ $this->delete($invite['CoInvite']['id']);
+ } else {
+ $this->delete($invite['CoInvite']['id']);
+ // Commit here, don't roll back!
+ $dbc->commit();
+ throw new OutOfBoundsException(_txt('er.inv.exp'));
+ }
+
+ // Create a history record
+
+ try {
+
$this->CoPerson->HistoryRecord->record($invite['CoInvite']['co_person_id'],
+
isset($invite['CoPetition']['enrollee_co_person_role_id']) ?
$invite['CoPetition']['enrollee_co_person_role_id'] : null,
+
isset($invite['CoPetition']['enrollee_org_identity_id']) ?
$invite['CoPetition']['enrollee_org_identity_id'] : null,
+
$invite['CoPetition']['enrollee_co_person_id'],
+ ($confirm ?
ActionEnum::InvitationConfirmed : ActionEnum::InvitationDeclined),
+ _txt(($confirm ?
'rs.inv.conf-a' : 'rs.inv.dec-a'), array($invite['CoInvite']['mail'])));
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ } else {
+ $dbc->rollback();
+ throw new InvalidArgumentException(_txt('er.inv.nf'));
+ }
+
+ $dbc->commit();
+ }
+
+ /**
+ * Create and send an invitation. Any existing invitation for the CO
Person will be removed.
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer CO Person ID associated with the invitation
+ * @param Integer Org Identity ID associated with the invitation
+ * @param Integer CO Person ID of actor sending the invite
+ * @param String Email Address to send the invite to
+ * @param String Email Address to send the invite from
+ * @param String CO Name (to pass into invite)
+ * @return Integer CO Invitation ID
+ * @throws RuntimeException
+ */
+
+ function send($coPersonId, $orgIdentityID, $actorPersonId, $toEmail,
$fromEmail=null, $coName) {
+ // Toss any prior invitations for $coPersonId to $toEmail
+
+ $this->deleteAll(array('co_person_id' => $coPersonId,
+ 'mail' => $toEmail));
+
+ $invite = array();
+ $invite['CoInvite']['co_person_id'] = $coPersonId;
+ $invite['CoInvite']['invitation'] = Security::generateAuthKey();
+ $invite['CoInvite']['mail'] = $toEmail;
+ // XXX make expiration time configurable
+ // XXX date format may not be portable
+ $invite['CoInvite']['expires'] = date('Y-m-d H:i:s', strtotime('+1
day'));
+
+ $this->create($invite);
+
+ if($this->save()) {
+ // Try to send the invite
+
+ // Set up and send the invitation via email
+ $email = new CakeEmail('default');
+
+ $viewVariables = array();
+ $viewVariables['invite_id'] = $invite['CoInvite']['invitation'];
+ $viewVariables['co_name'] = $coName;
+
+ try {
+ $email->template('coinvite', 'basic')
+ ->emailFormat('text')
+ ->to($toEmail)
+ ->viewVars($viewVariables)
+ ->subject(_txt('em.invite.subject', array($coName)));
+
+ // If this enrollment has a default email address set, use it,
otherwise leave in the default for the site.
+ if($fromEmail) {
+ $email->from($fromEmail);
+ }
+
+ // Send the email
+// $email->send();
+ } catch(Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+
+ // Create a history record
+
+ try {
+ $this->CoPerson->HistoryRecord->record($coPersonId,
+ null,
+ $orgIdentityID,
+ $actorPersonId,
+ ActionEnum::InvitationSent,
+ _txt('rs.inv.sent',
array($toEmail)));
+ }
+ catch(Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+
+ return $this->id;
+ } else {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+ }
}

Modified: registry/trunk/app/Model/CoPetition.php
===================================================================
--- registry/trunk/app/Model/CoPetition.php 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/Model/CoPetition.php 2012-09-26 00:53:01 UTC (rev
365)
@@ -39,6 +39,7 @@
'foreignKey' => 'approver_co_person_id'
),
"Co", // A CO Petition is associated with a CO
+ "CoInvite",
"Cou", // A CO Petition may be associated with a COU
"CoEnrollmentFlow", // A CO Petition follows a CO Enrollment Flow
"EnrolleeCoPerson" => array(
@@ -123,6 +124,11 @@
'required' => false,
'allowEmpty' => true
),
+ 'co_invite_id' => array(
+ 'rule' => 'numeric',
+ 'required' => false,
+ 'allowEmpty' => true
+ ),
'status' => array(
'rule' => array('inList', array(StatusEnum::Approved,
StatusEnum::Declined,
@@ -246,6 +252,25 @@

$fail = false;

+ // Determine an initial status. We don't jump straight to Active, since
post-creation actions may be required for that.
+
+ $confirmEmail = $this->CoEnrollmentFlow->field('confirm_email',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ $requireAuthn = $this->CoEnrollmentFlow->field('confirm_email',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ $approvalPolicy = $this->CoEnrollmentFlow->field('approval_required',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ $initialStatus = StatusEnum::Approved;
+
+ if($confirmEmail || $requireAuthn) {
+ $initialStatus = StatusEnum::PendingConfirmation;
+ } elseif($approvalPolicy) {
+ $initialStatus = StatusEnum::PendingApproval;
+ }
+
// Start a transaction. We don't really need to save until we validate
CO Person Role
// (which needs co_person_id), but for consistency we'll follow a
validate/save/rollback-on-error
// pattern.
@@ -352,47 +377,139 @@
// but that could change.

$coData = array();
- $coData['EnrolleeCoPerson'] =
$this->EnrolleeCoPerson->filterModelAttributes($requestData['EnrolleeCoPerson']);
- $coData['EnrolleeCoPerson']['co_id'] = $coId;
- $coData['EnrolleeCoPerson']['status'] = StatusEnum::PendingApproval;

- // Dynamically adjust validation rules according to the enrollment flow
- $this->adjustValidationRules('EnrolleeCoPerson', $efAttrs);
+ // Check the Match policy for this Enrollment Flow.

- // Manually validate CoPerson
- $this->EnrolleeCoPerson->create($coData);
+ $matchPolicy = $this->CoEnrollmentFlow->field('match_policy',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));

- // Make sure to use invalidFields(), which won't try to validate
(possibly
- // missing) related models.
- $errFields = $this->EnrolleeCoPerson->invalidFields();
-
- if(!empty($errFields)) {
- $fail = true;
+ if($matchPolicy == EnrollmentMatchPolicyEnum::Self) {
+ // The enrollee is also the petitioner, so just take the petitioner's
CO Person identity
+
+ $coPersonID = $petitionerId;
+
+ // Create a history record
+ try {
+ $this->EnrolleeCoPerson->HistoryRecord->record($coPersonID,
+ null,
+ $orgIdentityID,
+ $petitionerId,
+
ActionEnum::CoPersonMatchedPetition);
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ } else {
+ $coData['EnrolleeCoPerson'] =
$this->EnrolleeCoPerson->filterModelAttributes($requestData['EnrolleeCoPerson']);
+ $coData['EnrolleeCoPerson']['co_id'] = $coId;
+ $coData['EnrolleeCoPerson']['status'] = $initialStatus;
+
+ // Dynamically adjust validation rules according to the enrollment flow
+ $this->adjustValidationRules('EnrolleeCoPerson', $efAttrs);
+
+ // Manually validate CoPerson
+ $this->EnrolleeCoPerson->create($coData);
+
+ // Make sure to use invalidFields(), which won't try to validate
(possibly
+ // missing) related models.
+ $errFields = $this->EnrolleeCoPerson->invalidFields();
+
+ if(!empty($errFields)) {
+ $fail = true;
+ }
+
+ // Now validate related models
+
+ $v = $this->validateRelated("EnrolleeCoPerson", $requestData, $coData,
$efAttrs);
+
+ if($v) {
+ $coData = $v;
+ } else {
+ $fail = true;
+ }
+
+ // Save the CO Person Data
+
+ if(!$fail) {
+ if($this->EnrolleeCoPerson->saveAssociated($coData, array("validate"
=> false, "atomic" => true))) {
+ $coPersonID = $this->EnrolleeCoPerson->id;
+
+ // Create a history record
+ try {
+ $this->EnrolleeCoPerson->HistoryRecord->record($coPersonID,
+ null,
+ $orgIdentityID,
+ $petitionerId,
+
ActionEnum::CoPersonAddedPetition);
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException($e->getMessage());
+ }
+ } else {
+ $dbc->rollback();
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+ }
}

- // Now validate related models
+ // Validate CO Person Role, but only if CO Person Role data was provided

- $v = $this->validateRelated("EnrolleeCoPerson", $requestData, $coData,
$efAttrs);
+ $coRoleData = array();

- if($v) {
- $coData = $v;
- } else {
- $fail = true;
- }
-
- // Save the CO Person Data
-
- if(!$fail) {
- if($this->EnrolleeCoPerson->saveAssociated($coData, array("validate"
=> false, "atomic" => true))) {
- $coPersonID = $this->EnrolleeCoPerson->id;
+ if(isset($requestData['EnrolleeCoPersonRole'])) {
+ $coRoleData['EnrolleeCoPersonRole'] =
$this->EnrolleeCoPersonRole->filterModelAttributes($requestData['EnrolleeCoPersonRole']);
+ $coRoleData['EnrolleeCoPersonRole']['status'] = $initialStatus;
+ $coRoleData['EnrolleeCoPersonRole']['co_person_id'] = $coPersonID;
+
+ // Dynamically adjust validation rules according to the enrollment flow
+
+ // XXX If we didn't generate a CO Person ID above for some reason,
that validation will fail
+ // here. With dynamic validation rules in Cake 2.2 we could drop that
rule. (CO-353)
+ $this->adjustValidationRules('EnrolleeCoPersonRole', $efAttrs);
+
+ // Manually validate CoPersonRole
+ $this->EnrolleeCoPersonRole->set($coRoleData);
+
+ // Make sure to use invalidFields(), which won't try to validate
(possibly
+ // missing) related models.
+ $errFields = $this->EnrolleeCoPersonRole->invalidFields();
+
+ if(!empty($errFields)) {
+ $fail = true;
+ }
+
+ // Now validate related models. This will handle Extended Attributes
as well.
+
+ $v = $this->validateRelated("EnrolleeCoPersonRole", $requestData,
$coRoleData, $efAttrs);
+
+ if($v) {
+ $coRoleData = $v;
+ } else {
+ $fail = true;
+ }
+
+ // We're done validating user data at this point, so we can fail if
there were
+ // any validation errors.
+
+ if($fail) {
+ $dbc->rollback();
+ throw new InvalidArgumentException(_txt('er.fields'));
+ }
+
+ // Save the CO Person Role data
+
+ if($this->EnrolleeCoPersonRole->saveAssociated($coRoleData,
array("validate" => false, "atomic" => true))) {
+ $coPersonRoleID = $this->EnrolleeCoPersonRole->id;

// Create a history record
try {
$this->EnrolleeCoPerson->HistoryRecord->record($coPersonID,
- null,
+ $coPersonRoleID,
$orgIdentityID,
$petitionerId,
-
ActionEnum::CoPersonAddedPetition);
+
ActionEnum::CoPersonRoleAddedPetition);
}
catch(Exception $e) {
$dbc->rollback();
@@ -403,71 +520,7 @@
throw new RuntimeException(_txt('er.db.save'));
}
}
-
- // Validate CO Person Role

- $coRoleData = array();
- $coRoleData['EnrolleeCoPersonRole'] =
$this->EnrolleeCoPersonRole->filterModelAttributes($requestData['EnrolleeCoPersonRole']);
- $coRoleData['EnrolleeCoPersonRole']['status'] =
StatusEnum::PendingApproval;
- $coRoleData['EnrolleeCoPersonRole']['co_person_id'] = $coPersonID;
-
- // Dynamically adjust validation rules according to the enrollment flow
-
- // XXX If we didn't generate a CO Person ID above for some reason, that
validation will fail
- // here. With dynamic validation rules in Cake 2.2 we could drop that
rule. (CO-353)
- $this->adjustValidationRules('EnrolleeCoPersonRole', $efAttrs);
-
- // Manually validate CoPersonRole
- $this->EnrolleeCoPersonRole->set($coRoleData);
-
- // Make sure to use invalidFields(), which won't try to validate
(possibly
- // missing) related models.
- $errFields = $this->EnrolleeCoPersonRole->invalidFields();
-
- if(!empty($errFields)) {
- $fail = true;
- }
-
- // Now validate related models. This will handle Extended Attributes as
well.
-
- $v = $this->validateRelated("EnrolleeCoPersonRole", $requestData,
$coRoleData, $efAttrs);
-
- if($v) {
- $coRoleData = $v;
- } else {
- $fail = true;
- }
-
- // We're done validating user data at this point, so we can fail if
there were
- // any validation errors.
-
- if($fail) {
- $dbc->rollback();
- throw new InvalidArgumentException(_txt('er.fields'));
- }
-
- // Save the CO Person Role data
-
- if($this->EnrolleeCoPersonRole->saveAssociated($coRoleData,
array("validate" => false, "atomic" => true))) {
- $coPersonRoleID = $this->EnrolleeCoPersonRole->id;
-
- // Create a history record
- try {
- $this->EnrolleeCoPerson->HistoryRecord->record($coPersonID,
- $coPersonRoleID,
- $orgIdentityID,
- $petitionerId,
-
ActionEnum::CoPersonRoleAddedPetition);
- }
- catch(Exception $e) {
- $dbc->rollback();
- throw new RuntimeException($e->getMessage());
- }
- } else {
- $dbc->rollback();
- throw new RuntimeException(_txt('er.db.save'));
- }
-
// Create a CO Org Identity Link

$coOrgLink = array();
@@ -507,14 +560,17 @@

$coPetitionData['CoPetition']['enrollee_org_identity_id'] =
$orgIdentityID;
$coPetitionData['CoPetition']['enrollee_co_person_id'] = $coPersonID;
- $coPetitionData['CoPetition']['enrollee_co_person_role_id'] =
$coPersonRoleID;

+ if($coPersonRoleID) {
+ $coPetitionData['CoPetition']['enrollee_co_person_role_id'] =
$coPersonRoleID;
+ }
+
// Figure out the petitioner person ID. As of now, it is the
authenticated
// person completing the form. This could be NULL if a CMP admin who is
not
// a member of the CO initiates the petition.

$coPetitionData['CoPetition']['petitioner_co_person_id'] = $petitionerId;
- $coPetitionData['CoPetition']['status'] = StatusEnum::PendingApproval;
+ $coPetitionData['CoPetition']['status'] = $initialStatus;

if($this->save($coPetitionData)) {
$coPetitionID = $this->id;
@@ -739,14 +795,158 @@
throw new RuntimeException(_txt('er.db.save'));
}

+ // Send email invite if configured
+
+ if($confirmEmail) {
+ // We need an email address to send to. Since we don't have a
mechanism for
+ // picking from multiple at the moment, we just pick the first one
provided
+ // (which in most cases will be sufficient).
+
+ $toEmail = "";
+
+ if(isset($orgData['EmailAddress'])) {
+ // EmailAddresses are indexed by email_address_id, so we need to
figure one.
+ // We don't use array_shift since we don't want to muck with the
array.
+
+ $i = array_keys($orgData['EmailAddress']);
+
+ if(count($i) > 0) {
+ $toEmail = $orgData['EmailAddress'][ $i[0] ]['mail'];
+ }
+ }
+
+ if($toEmail != "") {
+ $notifyFrom = $this->CoEnrollmentFlow->field('notify_from',
+
array('CoEnrollmentFlow.id' => $enrollmentFlowID));
+
+ $coName = $this->Co->field('name', array('Co.id' => $coId));
+
+ $coInviteId = $this->CoInvite->send($coPersonID,
+ $orgIdentityID,
+ $petitionerId,
+ $toEmail,
+ $notifyFrom,
+ $coName);
+
+ // Add the invite ID to the petition record
+
+ $this->saveField('co_invite_id', $coInviteId);
+
+ // And add a petition history record
+
+ try {
+ $this->CoPetitionHistoryRecord->record($coPetitionID,
+ $petitionerId,
+
PetitionActionEnum::InviteSent,
+ _txt('rs.inv.sent',
array($toEmail)));
+ }
+ catch(Exception $e) {
+ $dbc->rollback();
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+ } else {
+ $dbc->rollback();
+ throw new RuntimeException(_txt('er.orgp.nomail',
array(generateCn($orgData['Name']), $orgIdentityID)));
+ }
+ }
+
$dbc->commit();

return $this->id;
}
+
+ /**
+ * Resend an invite for a Petition.
+ * - postcondition: Invite sent
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer CO Petition ID
+ * @throws InvalidArgumentException
+ * @return String Address the invitation was resent to
+ */
+
+ function resend($coPetitionId) {
+ // We don't set up a transaction because once the invite goes out we've
basically
+ // committed (and it doesn't make sense to execute a rollback), and
we're mostly
+ // doing reads before that.
+
+ // Petition status must be Pending Confirmation
+
+ $this->id = $coPetitionId;
+
+ if($this->field('status') != StatusEnum::PendingConfirmation) {
+ throw new InvalidArgumentException(_txt('er.pt.resend.status'));
+ }
+
+ // There must be an email address associated with the org identity
associated with this petition
+
+ $args = array();
+ $args['conditions']['EmailAddress.org_identity_id'] =
$this->field('enrollee_org_identity_id');
+ $args['contain'] = false;
+
+ $email = $this->EnrolleeOrgIdentity->EmailAddress->find('first', $args);
+
+ if(empty($email)) {
+ throw new InvalidArgumentException(_txt('er.notfound',
+ array('ct.email_addresses.1',
+
$args['conditions']['EmailAddress.org_identity_id'])));
+ }
+
+ // Unlink any existing invite
+
+ if(!$this->saveField('co_invite_id', null)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+
+ // Find enrollment flow
+
+ $args = array();
+ $args['conditions']['CoEnrollmentFlow.id'] =
$this->field('co_enrollment_flow_id');
+ $args['contain'] = false;
+
+ $enrollmentFlow = $this->CoEnrollmentFlow->find('first', $args);
+
+ if(empty($enrollmentFlow)) {
+ throw new InvalidArgumentException(_txt('er.notfound',
+
array('ct.co_enrollment_flows.1',
+
$args['conditions']['CoEnrollmentFlow.id'])));
+ }
+
+ // Resend invite
+
+ $coInviteId =
$this->CoInvite->send($this->field('enrollee_co_person_id'),
+
$this->field('enrollee_org_identity_id'),
+
$this->field('petitioner_co_person_id'),
+ $email['EmailAddress']['mail'],
+
$enrollmentFlow['CoEnrollmentFlow']['notify_from'],
+ $this->Co->field('name',
+ array('Co.id' =>
$enrollmentFlow['CoEnrollmentFlow']['co_id'])));
+
+ // Update the CO Petition with the new invite ID
+
+ if(!$this->saveField('co_invite_id', $coInviteId)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+
+ // Add petition history record
+
+ try {
+ $this->CoPetitionHistoryRecord->record($coPetitionId,
+
$this->field('petitioner_co_person_id'),
+ PetitionActionEnum::InviteSent,
+ _txt('rs.inv.sent',
array($email['EmailAddress']['mail'])));
+ }
+ catch(Exception $e) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+
+ return $email['EmailAddress']['mail'];
+ }

/**
* Update the status of a CO Petition.
* - precondition: The Petition must be in a state suitable for the
desired new status.
+ * - postcondition: The new status may be altered according to the
enrollment configuration.
*
* @since COmanage Registry v0.5
* @param Integer CO Petition ID
@@ -767,17 +967,66 @@
}

// Do we have a valid new status? If so, do we need to update CO Person
status?
+
$valid = false;
+ $newPetitionStatus = $newStatus;
$newCoPersonStatus = null;

+ // Find the enrollment flow associated with this petition to determine
some configuration parameters
+
+ $args = array();
+ $args['conditions']['CoEnrollmentFlow.id'] =
$this->field('co_enrollment_flow_id');
+ $args['contain'] = false;
+
+ $enrollmentFlow = $this->CoEnrollmentFlow->find('first', $args);
+
+ if(!$enrollmentFlow) {
+ throw new InvalidArgumentException(_txt('er.notfound',
+
array('ct.co_enrollment_flows.1',
$args['conditions']['CoEnrollmentFlow.id'])));
+ }
+
+ if($curStatus == StatusEnum::PendingConfirmation) {
+ // A Petition can go from Pending Confirmation to Pending Approval,
Approved, or Denied.
+
+ if($newStatus == StatusEnum::Approved
+ || $newStatus == StatusEnum::Denied
+ || $newStatus == StatusEnum::PendingApproval) {
+ $valid = true;
+ }
+
+ // If newStatus is Approved but approval is required, update to
PendingApproval instead
+ // and create an additional history record.
+
+ if($newStatus == StatusEnum::Approved
+ && $enrollmentFlow['CoEnrollmentFlow']['approval_required']) {
+ $newPetitionStatus = StatusEnum::PendingApproval;
+
+ try {
+ $this->CoPetitionHistoryRecord->record($id,
+ $actorCoPersonID,
+
PetitionActionEnum::InviteConfirmed);
+ }
+ catch (Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+ }
+
if($curStatus == StatusEnum::PendingApproval) {
// A Petition can go from PendingApproval to Approved or Denied

if($newStatus == StatusEnum::Approved
|| $newStatus == StatusEnum::Denied) {
$valid = true;
- $newCoPersonStatus = $newStatus;
}
+ }
+
+ // If a CO Person Role is defined update the CO Person (& Role) status
+
+ $coPersonRoleID = $this->field('enrollee_co_person_role_id');
+
+ if($coPersonRoleID) {
+ $newCoPersonStatus = $newStatus;

// XXX This is temporary for CO-321 since there isn't currently a way
for an approved person
// to become active. This should be dropped when a more
workflow-oriented mechanism is implemented.
@@ -796,12 +1045,16 @@

// Update the Petition status

- $this->saveField('status', $newStatus);
+ if(!$this->saveField('status', $newPetitionStatus)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }

// If this is an approval, update the approver field as well

- if($newStatus == StatusEnum::Approved) {
- $this->saveField('approver_co_person_id', $actorCoPersonID);
+ if($newPetitionStatus == StatusEnum::Approved) {
+ if(!$this->saveField('approver_co_person_id', $actorCoPersonID)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
}

// Write a Petition History Record
@@ -818,21 +1071,21 @@
break;
}

- try {
- $this->CoPetitionHistoryRecord->record($id,
- $actorCoPersonID,
- $petitionAction);
+ if($petitionAction) {
+ try {
+ $this->CoPetitionHistoryRecord->record($id,
+ $actorCoPersonID,
+ $petitionAction);
+ }
+ catch (Exception $e) {
+ $fail = true;
+ }
}
- catch (Exception $e) {
- $fail = true;
- }
}

// Update CO Person Role state

if(!$fail && isset($newCoPersonStatus)) {
- $coPersonRoleID = $this->field('enrollee_co_person_role_id');
-
if($coPersonRoleID) {
$this->EnrolleeCoPersonRole->id = $coPersonRoleID;
$curCoPersonRoleStatus =
$this->EnrolleeCoPersonRole->field('status');
@@ -950,6 +1203,117 @@
}

/**
+ * Validate an identifier obtained via authentication, possibly attaching
it to the
+ * Org Identity.
+ * - postcondition: Identifier attached to Org Identity
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer CO Petition ID
+ * @param String Login Identifier
+ * @param Integer Actor CO Person ID
+ * @throws InvalidArgumentException
+ * @throws RuntimeException
+ */
+
+ public function validateIdentifier($id, $loginIdentifier,
$actorCoPersonId) {
+ // Find the enrollment flow associated with this petition to determine
some configuration parameters
+
+ $this->id = $id;
+
+ $args = array();
+ $args['conditions']['CoEnrollmentFlow.id'] =
$this->field('co_enrollment_flow_id');
+ $args['contain'] = false;
+
+ $enrollmentFlow = $this->CoEnrollmentFlow->find('first', $args);
+
+ if(!$enrollmentFlow) {
+ throw new InvalidArgumentException(_txt('er.notfound',
+
array('ct.co_enrollment_flows.1',
$args['conditions']['CoEnrollmentFlow.id'])));
+ }
+
+ if(!$loginIdentifier) {
+ // If authn is required but loginidentifier is null, throw an exception
+ // (otherwise don't do anything)
+
+ if($enrollmentFlow['CoEnrollmentFlow']['require_authn']) {
+ throw new RuntimeException(_txt('er.auth'));
+ }
+ } else {
+ // If the identifier is already linked to the org identity, do nothing
+
+ $orgId = $this->field('enrollee_org_identity_id');
+
+ if($orgId) {
+ // For now, we assume the identifier type is ePPN. XXX This probably
isn't right,
+ // and should be customizable.
+
+ $args = array();
+ $args['conditions']['Identifier.identifier'] = $loginIdentifier;
+ $args['conditions']['Identifier.org_identity_id'] = $orgId;
+ $args['conditions']['Identifier.type'] = IdentifierEnum::ePPN;
+
+ $identifier = $this->EnrolleeOrgIdentity->Identifier->find('first',
$args);
+
+ if(!empty($identifier)) {
+ // Make sure login flag is set
+ debug("mark3");
+
+ if(!$identifier['Identifier']['login']) {
+ $this->EnrolleeOrgIdentity->Identifier->id =
$identifier['Identifier']['id'];
+
+ if(!$this->EnrolleeOrgIdentity->Identifier->saveField('login',
true)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+
+ // Create a history record
+
+ try {
+ $this->EnrolleeCoPerson->HistoryRecord->record(null,
+ null,
+ $orgId,
+
$actorCoPersonId,
+
ActionEnum::OrgIdEditedPetition,
+
_txt('rs.pt.id.login', $loginIdentifier));
+ }
+ catch(Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+ } else {
+ // Add the identifier and update petition and org identity history
+
+ $identifier = array();
+ $identifier['Identifier']['identifier'] = $loginIdentifier;
+ $identifier['Identifier']['org_identity_id'] = $orgId;
+ $identifier['Identifier']['type'] = IdentifierEnum::ePPN;
+ $identifier['Identifier']['login'] = true;
+ $identifier['Identifier']['status'] = StatusEnum::Active;
+
+ if(!$this->EnrolleeOrgIdentity->Identifier->save($identifier)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+
+ // Create a history record
+
+ try {
+ $this->EnrolleeCoPerson->HistoryRecord->record(null,
+ null,
+ $orgId,
+ $actorCoPersonId,
+
ActionEnum::OrgIdEditedPetition,
+
_txt('rs.pt.id.attached', $loginIdentifier));
+ }
+ catch(Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+ } else {
+ throw new InvalidArgumentException(_txt('er.notprov.id',
array('ct.org_identities.1')));
+ }
+ }
+ }
+
+ /**
* Validate related model data, and assemble it for saving.
*
* @since COmanage Registry v0.7

Modified: registry/trunk/app/Model/CoPetitionHistoryRecord.php
===================================================================
--- registry/trunk/app/Model/CoPetitionHistoryRecord.php 2012-09-24
06:33:39 UTC (rev 364)
+++ registry/trunk/app/Model/CoPetitionHistoryRecord.php 2012-09-26
00:53:01 UTC (rev 365)
@@ -68,11 +68,15 @@
$coPetitionHistoryData['CoPetitionHistoryRecord']['comment'] =
$comment;
} else {
// Figure out a default value
+ // XXX this should really use txt en.somethingorother like
HistoryRecord::record()

switch($action) {
case PetitionActionEnum::Approved:
$coPetitionHistoryData['CoPetitionHistoryRecord']['comment'] =
_txt('rs.pt.approve');
break;
+ case PetitionActionEnum::InviteConfirmed:
+ $coPetitionHistoryData['CoPetitionHistoryRecord']['comment'] =
_txt('rs.pt.confirm');
+ break;
case PetitionActionEnum::Created:
$coPetitionHistoryData['CoPetitionHistoryRecord']['comment'] =
_txt('rs.pt.create');
break;

Modified: registry/trunk/app/Model/EmailAddress.php
===================================================================
--- registry/trunk/app/Model/EmailAddress.php 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/Model/EmailAddress.php 2012-09-26 00:53:01 UTC (rev
365)
@@ -29,6 +29,9 @@
// Current schema version for API
public $version = "1.0";

+ // Add behaviors
+ public $actsAs = array('Containable');
+
// Association rules from this model to other models
public $belongsTo = array(
// An email address may be attached to a CO Person
@@ -58,6 +61,9 @@
'required' => false,
'allowEmpty' => false
),
+ 'verified' => array(
+ 'rule' => array('boolean')
+ ),
'co_person_id' => array(
'rule' => 'numeric',
'required' => false
@@ -90,9 +96,58 @@
$args = array();
$args['conditions']['EmailAddress.co_person_id'] = $coPersonID;
$args['conditions']['EmailAddress.type'] = $emailType;
+ $args['contain'] = false;

$r = $this->findForUpdate($args['conditions'], array('mail'));

return !empty($r);
}
+
+ /**
+ * Mark an address as verified.
+ *
+ * @since COmanage Registry v0.7
+ * @param Integer Org Identity ID
+ * @param String Email address to mark verified
+ * @param Integer CO Person ID of verifier
+ * @throws InvalidArgumentException
+ * @throws RuntimeException
+ */
+
+ public function verify($orgIdentityId, $address, $verifierCoPersonId) {
+ // First find the record
+
+ $args = array();
+ $args['conditions']['EmailAddress.org_identity_id'] = $orgIdentityId;
+ $args['conditions']['EmailAddress.mail'] = $address;
+ $args['contain'] = false;
+
+ $mail = $this->find('first', $args);
+
+ if(empty($mail)) {
+ throw new InvalidArgumentException(_txt('er.notfound',
array(_txt('ct.email_addresses.1'), $address)));
+ }
+
+ // And then update it
+
+ $this->id = $mail['EmailAddress']['id'];
+
+ if(!$this->saveField('verified', true)) {
+ throw new RuntimeException(_txt('er.db.save'));
+ }
+
+ // Finally, create a history record
+
+ try {
+ $this->CoPerson->HistoryRecord->record(null,
+ null,
+ $orgIdentityId,
+ $verifierCoPersonId,
+
ActionEnum::EmailAddressVerified,
+ _txt('rs.mail.verified',
array($address)));
+ }
+ catch(Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+ }
}

Modified: registry/trunk/app/Model/OrgIdentity.php
===================================================================
--- registry/trunk/app/Model/OrgIdentity.php 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/Model/OrgIdentity.php 2012-09-26 00:53:01 UTC (rev
365)
@@ -84,8 +84,7 @@
AffiliationEnum::Affiliate,
AffiliationEnum::Employee,
AffiliationEnum::LibraryWalkIn)),
- 'required' => true,
- 'message' => 'A valid affiliation must be selected'
+ 'required' => false
),
'co_id' => array(
'rule' => 'numeric',

Modified: registry/trunk/app/View/CoEnrollmentAttributes/index.ctp
===================================================================
--- registry/trunk/app/View/CoEnrollmentAttributes/index.ctp 2012-09-24
06:33:39 UTC (rev 364)
+++ registry/trunk/app/View/CoEnrollmentAttributes/index.ctp 2012-09-26
00:53:01 UTC (rev 365)
@@ -26,6 +26,14 @@
$params = array('title' => $title_for_layout);
print $this->element("pageTitle", $params);

+ print $this->Html->link(_txt('op.back'),
+ array('controller' => 'co_enrollment_flows',
+ 'action' => ($permissions['edit'] ? 'edit' :
'view'),
+
Sanitize::html($this->request->params['named']['coef']),
+ 'co' => $coid),
+ array('class' => 'backbutton')) . '
+ ';
+
if($permissions['add'])
print $this->Html->link(_txt('op.add') . ' ' .
_txt('ct.co_enrollment_attributes.1'),
array('controller' =>
'co_enrollment_attributes', 'action' => 'add', 'coef' =>
Sanitize::html($this->request->params['named']['coef'])),

Modified: registry/trunk/app/View/CoEnrollmentFlows/fields.inc
===================================================================
--- registry/trunk/app/View/CoEnrollmentFlows/fields.inc 2012-09-24
06:33:39 UTC (rev 364)
+++ registry/trunk/app/View/CoEnrollmentFlows/fields.inc 2012-09-26
00:53:01 UTC (rev 365)
@@ -101,7 +101,7 @@
</tr>
<tr class="line2">
<td>
- <?php print _txt('fd.status'); ?><font class="required">*</font>
+ <b><?php print _txt('fd.status'); ?></b><font
class="required">*</font>
</td>
<td>
<?php
@@ -200,6 +200,31 @@
</tr>
<tr class="line2">
<td>
+ <b><?php print _txt('fd.ef.match'); ?></b><br />
+ <font class="desc"><?php print _txt('fd.ef.match.desc'); ?></font>
+ </td>
+ <td>
+ <?php
+ global $cm_lang, $cm_texts;
+ $attrs['value'] = (isset($co_enrollment_flows) ?
$co_enrollment_flows[0]['CoEnrollmentFlow']['match_policy'] :
EnrollmentMatchPolicyEnum::Advisory);
+ $attrs['empty'] = false;
+
+ if($e) {
+ print $this->Form->select('match_policy',
+ $cm_texts[ $cm_lang
]['en.enrollment.match'],
+ $attrs);
+
+ if($this->Form->isFieldError('match_policy')) {
+ print $this->Form->error('match_policy');
+ }
+ } else {
+ print _txt('en.enrollment.match', null,
$co_enrollment_flows[0]['CoEnrollmentFlow']['match_policy']);
+ }
+ ?>
+ </td>
+ </tr>
+ <tr class="line1">
+ <td>
<b><?php print _txt('fd.ef.appr'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.appr.desc'); ?></font>
</td>
@@ -209,8 +234,30 @@
:
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['approval_required']));
?>
</td>
</tr>
+ <tr class="line2">
+ <td>
+ <b><?php print _txt('fd.ef.ce'); ?></b><br />
+ <font class="desc"><?php print _txt('fd.ef.ce.desc'); ?></font>
+ </td>
+ <td>
+ <?php print ($e
+ ? $this->Form->input('confirm_email')
+ :
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['confirm_email']));
?>
+ </td>
+ </tr>
<tr class="line1">
<td>
+ <b><?php print _txt('fd.ef.authn'); ?></b><br />
+ <font class="desc"><?php print _txt('fd.ef.authn.desc'); ?></font>
+ </td>
+ <td>
+ <?php print ($e
+ ? $this->Form->input('require_authn')
+ :
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['require_authn']));
?>
+ </td>
+ </tr>
+ <tr class="line2">
+ <td>
<b><?php print _txt('fd.ef.epx'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.epx.desc'); ?></font>
</td>
@@ -220,7 +267,7 @@
:
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['early_provisioning_exec']));
?>
</td>
</tr>
- <tr class="line2">
+ <tr class="line1">
<td>
<b><?php print _txt('fd.ef.px'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.px.desc'); ?></font>
@@ -231,7 +278,7 @@
:
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['provisioning_exec']));
?>
</td>
</tr>
- <tr class="line1">
+ <tr class="line2">
<td>
<b><?php print _txt('fd.ef.noep'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.noep.desc'); ?></font>
@@ -242,7 +289,7 @@
:
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['notify_on_early_provision']));
?>
</td>
</tr>
- <tr class="line2">
+ <tr class="line1">
<td>
<b><?php print _txt('fd.ef.nop'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.nop.desc'); ?></font>
@@ -253,7 +300,7 @@
:
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['notify_on_provision']));
?>
</td>
</tr>
- <tr class="line1">
+ <tr class="line2">
<td>
<b><?php print _txt('fd.ef.noa'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.noa.desc'); ?></font>
@@ -264,7 +311,7 @@
:
Sanitize::html($co_enrollment_flows[0]['CoEnrollmentFlow']['notify_on_active']));
?>
</td>
</tr>
- <tr class="line2">
+ <tr class="line1">
<td>
<b><?php print _txt('fd.ef.efn'); ?></b><br />
<font class="desc"><?php print _txt('fd.ef.efn.desc'); ?></font>


Property changes on: registry/trunk/app/View/CoInvites/petition-attributes.inc
___________________________________________________________________
Added: svn:special
+ *

Modified: registry/trunk/app/View/CoInvites/reply.ctp
===================================================================
--- registry/trunk/app/View/CoInvites/reply.ctp 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/View/CoInvites/reply.ctp 2012-09-26 00:53:01 UTC (rev
365)
@@ -22,28 +22,36 @@
* @version $Id$
*/
-->
+<?php if(!empty($invite)): ?>
<?php
- $params = array('title' => "Invitation to " . $cur_co['Co']['name']);
+ $params = array('title' => _txt('fd.inv.to',
array($cur_co['Co']['name'])));
print $this->element("pageTitle", $params);
?>

-<p>
-Invitation for <b><?php echo generateCn($invitee['Name']); ?></b>
-</p>
+<h2 class="ui-state-default"><?php print _txt('fd.inv.for',
array(generateCn($invitee['Name']))); ?></h2>

-<ul>
- <li>
-<?php
- $u = $this->Html->url(array('controller' => 'co_invites', 'action' =>
'confirm', $invite['CoInvite']['invitation']), true);
+<?php
+ print $this->Html->link(
+ _txt('op.accept'),
+ array('controller' => 'co_invites',
+ 'action' =>
(isset($co_enrollment_flow['CoEnrollmentFlow']['require_authn'])
+ &&
$co_enrollment_flow['CoEnrollmentFlow']['require_authn']) ? 'authconfirm' :
'confirm',
+ $invite['CoInvite']['invitation']),
+ array('class' => 'checkbutton')
+ );
+
+ print $this->Html->link(
+ _txt('op.decline'),
+ array('controller' => 'co_invites',
+ 'action' => 'decline',
+ $invite['CoInvite']['invitation']),
+ array('class' => 'cancelbutton')
+ );

- echo $this->Html->link('Confirm', $u);
+ $e = false;
+
+ if(isset($co_petitions)) {
+ include ("petition-attributes.inc");
+ }
?>
- </li>
- <li>
-<?php
- $u = $this->Html->url(array('controller' => 'co_invites', 'action' =>
'decline', $invite['CoInvite']['invitation']), true);
-
- echo $this->Html->link('Decline', $u);
-?>
- </li>
-</ul>
+<?php endif;
\ No newline at end of file

Modified: registry/trunk/app/View/CoPeople/fields.inc
===================================================================
--- registry/trunk/app/View/CoPeople/fields.inc 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/View/CoPeople/fields.inc 2012-09-26 00:53:01 UTC (rev
365)
@@ -43,22 +43,13 @@
if(!$e && !$permissions['view'])
return(false);

- if($e)
- {
- print $this->Html->link(_txt('op.back'),
- array('controller' => 'co_people', 'action' =>
'index', 'co' => $cur_co['Co']['id']),
- array('class' => 'cancelbutton'));
-
- if($this->action != "invite")
- {
- print $this->Html->link(_txt('ct.org_identities.1'),
- array('controller' => 'org_identities',
- 'action' => ($e && !$es ? 'edit' : 'view'),
-
$co_people[0]['CoOrgIdentityLink'][0]['org_identity_id'],
- 'co' => ($pool_org_identities ? false :
$cur_co['Co']['id'])),
- array('class' => 'linkbutton'));
-
- // If demographics data does not exist, demographics controller will
redirect to add
+ print $this->Html->link(_txt('op.back'),
+ array('controller' => 'co_people', 'action' =>
'index', 'co' => $cur_co['Co']['id']),
+ array('class' => 'backbutton'));
+
+ if($this->action != "invite") {
+ if($e) {
+ // If demographics data does not exist, demographics controller will
redirect to add
$args = array('controller' => 'co_nsf_demographics',
'action' => 'editself',
'co' => ($pool_org_identities ? false :
$cur_co['Co']['id']));
@@ -66,48 +57,41 @@
print $this->Html->link(_txt('ct.co_nsf_demographics.1'),
$args,
$classArg);
+
+ // Autogenerate Identifiers button
+ if(!empty($co_identifier_assignments)) {
+ print '<a
+ class="autobutton"
+ title="' . _txt('op.id.auto') . '"
+ onclick="javascript:js_confirm_autogenerate(\'' .
$this->Html->url(array('controller' => 'identifiers',
+
'action' => 'assign',
+
'copersonid' => $co_people[0]['CoPerson']['id'],
+
'co' => $cur_co['Co']['id'])) . '\')";>'
+ . _txt('op.id.auto')
+ . "</a>\n";
+ }
+
+ // Populate the cross reference (used for when we're adding a new CO
Person entry manually)
+ print $this->Form->hidden('CoPerson.co_id', array('default' =>
$cur_co['Co']['id'])). "\n";
+ print $this->Form->hidden('CoOrgIdentityLink.0.id'). "\n";
+ print $this->Form->hidden('CoOrgIdentityLink.0.org_identity_id',
array('default' =>
$co_people[0]['CoOrgIdentityLink'][0]['org_identity_id'])). "\n";
+ // Default status is 'Pending'
+ echo $this->Form->hidden('status', array('default' => 'P')). "\n";
}

- // Populate the cross reference
- print $this->Form->hidden('CoPerson.co_id', array('default' =>
$cur_co['Co']['id'])). "\n";
- print $this->Form->hidden('CoOrgIdentityLink.0.id'). "\n";
- print $this->Form->hidden('CoOrgIdentityLink.0.org_identity_id',
array('default' =>
$co_people[0]['CoOrgIdentityLink'][0]['org_identity_id'])). "\n";
- // Default status is 'Pending'
- echo $this->Form->hidden('status', array('default' => 'P')). "\n";
+ // View History button
+ if($this->action != "add") {
+ print $this->Html->link(
+ _txt('op.history'),
+ array(
+ 'controller' => 'history_records',
+ 'action' => 'index',
+ 'copersonid' => $co_people[0]['CoPerson']['id']
+ ),
+ array('class' => 'historybutton')
+ );
+ }
}
- else
- {
- // Back button
- print $this->Html->link(_txt('op.back'),
- array('controller' => 'co_people', 'action' =>
'index', 'co' => $cur_co['Co']['id']),
- array('class' => 'backbutton'));
- }
-
- // View History button
- if($this->action != "add" && $this->action != "invite") {
- print $this->Html->link(
- _txt('op.history'),
- array(
- 'controller' => 'history_records',
- 'action' => 'index',
- 'copersonid' => $co_people[0]['CoPerson']['id']
- ),
- array('class' => 'historybutton')
- );
- }
-
- // Autogenerate Identifiers button
- if(!empty($co_identifier_assignments)) {
- print '<a
- class="autobutton"
- title="' . _txt('op.id.auto') . '"
- onclick="javascript:js_confirm_autogenerate(\'' .
$this->Html->url(array('controller' => 'identifiers',
-
'action' => 'assign',
-
'copersonid' => $co_people[0]['CoPerson']['id'],
-
'co' => $cur_co['Co']['id'])) . '\')";>'
- . _txt('op.id.auto')
- . "</a>\n";
- }

// Line number, for rendering
$l = 1;
@@ -140,14 +124,14 @@
$('#dialog').dialog('open');
}

-// Turn on Tabs
-$(function() {
+ // Turn on Tabs
+ $(function() {
$( "#tabs" ).tabs();
});
</script>

<div id="<?php print $this->action; ?>_co_person" style=" float:left;
height:auto;">
- <div id="tabs" style="min-height:350px; width:550px">
+ <div id="tabs" style="min-height:350px; min-width:550px">
<ul>
<li>
<a href="#tabs-name">
@@ -175,6 +159,11 @@
<?php print _txt('fd.attrs.copr'); ?>
</a>
</li>
+ <li>
+ <a href="#tabs-orgid">
+ <?php print _txt('ct.org_identities.pl'); ?>
+ </a>
+ </li>
<?php endif; ?>
</ul>
<div id="tabs-name">
@@ -420,9 +409,10 @@
print '">';
print '<td>';
print $this->Html->link($ea['mail'],
array('controller' => 'email_addresses', 'action' => 'edit', $ea['id'], 'co'
=> $cur_co['Co']['id']));
- print " (" . _txt('en.contact', null, $ea['type'])
. ")<br />\n";
+ print " (" . _txt('en.contact', null, $ea['type'])
. ", "
+ . ($ea['verified'] ?
_txt('fd.email_address.verified') : _txt('fd.email_address.unverified')) .
")<br />\n";
print '</td>';
-
+
print '<td>';
// XXX we already checked for
$permissions['edit'], but not ['delete']... should we?
print '<a class="deletebutton" title="' .
_txt('op.delete') . '" onclick="javascript:js_confirm_delete(\'' .
_jtxt(Sanitize::html($ea['mail'])) . '\', \'' .
$this->Html->url(array('controller' => 'email_addresses', 'action' =>
'delete', $ea['id'], 'co' => $cur_co['Co']['id'])) . '\')";>' .
_txt('op.delete') . '</a>' . "\n";
@@ -728,5 +718,76 @@
</tbody>
</table>
</div> <!-- tabs-role -->
+ <div id="tabs-orgid">
+ <table>
+ <tbody>
+ <tr>
+ <td valign="top">
+ <!-- Org Identity Data -->
+ <table id="<?php print $this->action; ?>_org_identities"
class="ui-widget">
+ <tbody>
+ <tr class="line<?php print ($l % 2); $l++; ?>">
+ <th class="ui-widget-header"></th>
+ <th class="ui-widget-header"><?php print _txt('fd.o');
?></th>
+ <th class="ui-widget-header"><?php print
_txt('fd.ou'); ?></th>
+ <th class="ui-widget-header"><?php print
_txt('fd.title'); ?></th>
+ <th class="ui-widget-header"><?php print
_txt('fd.affiliation'); ?></th>
+ <th class="ui-widget-header"><?php print
_txt('fd.actions'); ?></th>
+ </tr>
+ <?php foreach($co_people[0]['CoOrgIdentityLink'] as
$link): ?>
+ <tr class="line<?php print ($l % 2); $l++; ?>">
+ <td>
+ <?php print $link['OrgIdentity']['id']; ?>
+ </td>
+ <td>
+ <?php
+ if(isset($link['OrgIdentity']['o']))
+ print Sanitize::html($link['OrgIdentity']['o']);
+ ?>
+ </td>
+ <td>
+ <?php
+ if(isset($link['OrgIdentity']['ou']))
+ print Sanitize::html($link['OrgIdentity']['ou']);
+ ?>
+ </td>
+ <td>
+ <?php
+ if(isset($link['OrgIdentity']['title']))
+ print
Sanitize::html($link['OrgIdentity']['title']);
+ ?>
+ </td>
+ <td>
+ <?php
+ if(isset($link['OrgIdentity']['affiliation']))
+ print $cm_texts[ $cm_lang ]['en.affil'][
$link['OrgIdentity']['affiliation'] ];
+ ?>
+ </td>
+ <td>
+ <?php
+ print $this->Html->link(_txt('op.view'),
+ array('controller' =>
'org_identities',
+ 'action' => ($e &&
!$es ? 'edit' : 'view'),
+
$link['OrgIdentity']['id'],
+ 'co' =>
($pool_org_identities ? false : $cur_co['Co']['id'])),
+ array('class' => ($e &&
!$es ? 'editbutton' : 'viewbutton'))) . "\n";
+
+ if($permissions['delete']
+ && count($co_people[0]['CoOrgIdentityLink']) >
1) {
+ // An Org Identity Link can only be removed if
there is at least one other remaining
+
+ print '<a class="unlinkbutton" title="' .
_txt('op.unlink') . '" onclick="javascript:js_confirm_generic(\'' .
_jtxt(_txt('op.unlink.confirm')) . '\', \'' .
$this->Html->url(array('controller' => 'co_org_identity_links', 'action' =>
'delete', $link['id'], 'co' => $cur_co['Co']['id'])) . '\')";>' .
_txt('op.unlink') . '</a>' . "\n";
+ }
+ ?>
+ </td>
+ </tr>
+ <?php endforeach; ?>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div> <!-- tabs-orgid -->
<?php endif; // if not invite ?>
</div>

Modified: registry/trunk/app/View/CoPeople/index.ctp
===================================================================
--- registry/trunk/app/View/CoPeople/index.ctp 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/View/CoPeople/index.ctp 2012-09-26 00:53:01 UTC (rev
365)
@@ -23,7 +23,7 @@
*/
-->
<?php
- $params = array('title' => $cur_co['Co']['name'] . " People");
+ $params = array('title' => $cur_co['Co']['name'] . " People"); // XXX I18N
print $this->element("pageTitle", $params);

if($permissions['enroll'] && !empty($co_enrollment_flows)) {

Modified: registry/trunk/app/View/CoPetitions/fields.inc
===================================================================
--- registry/trunk/app/View/CoPetitions/fields.inc 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/CoPetitions/fields.inc 2012-09-26 00:53:01
UTC (rev 365)
@@ -30,6 +30,7 @@
// XXX Don't hardcode fields here, or /registry prefix
$.ajax({
url: '/registry/co_people/match/co:<?php print $cur_co['Co']['id'];
?>'
+ + '/coef:' + <?php print
Sanitize::html($co_enrollment_flow_id); ?>
+ '/given:' +
document.getElementById('EnrolleeCoPersonNameGiven').value
+ '/family:' +
document.getElementById('EnrolleeCoPersonNameFamily').value
}).done(function(data) {
@@ -40,7 +41,6 @@
});
</script>
<?php
-
// Determine if fields are editable
$e = false;

@@ -159,163 +159,4 @@
<?php endif; // action == add ?>

<?php
- // Enumerate over all attributes defined for the enrollment flow
associated with this petition.
- // We do a series of <?php tags because we can't mix and match embedded
tag styles.
-?>
-
-<h2 class="ui-state-default"><?php print _txt('fd.attrs.pet'); ?></h2>
-
-<div>
- <div style="float:left;width:65%">
-
-<table id="<?php print $this->action; ?>_co_petition_attrs"
class="ui-widget">
- <tbody>
- <?php foreach ($co_enrollment_attributes as $ea): ?>
- <?php $fieldName = $ea['model'] . '.' . $ea['field']; ?>
- <?php if($ea['hidden']): ?>
- <?php print $this->Form->hidden($fieldName, array('default' =>
$ea['default'])) . "\n"; ?>
- <?php else: ?>
- <tr class="line<?php print ($l % 2); $l++; ?>">
- <td>
- <?php
- // Emit the label for this field
-
- print "<b>" . $ea['label'] . "</b>";
-
- if($ea['required']) {
- print "<font class=\"required\">*</font>\n";
- }
-
- if(isset($ea['description'])
- && $ea['description'] != "") {
- print "</br>\n<font class=\"desc\">" . $ea['description'] .
"</font>\n";
- }
- ?>
- </td>
- <td>
- <?php
- // Emit the field itself, according to the type of field
-
- // The type of validation rule can influence what we output.
- $ruleType = 'default';
-
- if(isset($ea['validate']['rule'][0]))
- $ruleType = $ea['validate']['rule'][0];
-
- // XXX need to retrieve current values for edit and view
-
- if($e) {
- switch($ruleType) {
- case 'inList':
- // This is a select
- $attrs = array();
-// $attrs['value'] =
(isset($co_person_roles[0]['CoPersonRole']['affiliation'])
-// ?
$co_person_roles[0]['CoPersonRole']['affiliation']
-// : "M");
- $attrs['empty'] = !$ea['required'];
-
- print $this->Form->select($fieldName,
- $ea['select'],
- $attrs);
-
- if($this->Form->isFieldError($fieldName)) {
- print $this->Form->error($fieldName);
- }
- break;
- case 'validateTimestamp':
- // Handle dates specially to generate the popup calendar
- $c = 'datepicker';
-
- if($ea['field'] == 'valid_from')
- $c = 'datepicker-f';
- elseif($ea['field'] == 'valid_until')
- $c = 'datepicker-c';
-
- print $this->Form->text($fieldName, array('class' => $c));
-
- if($this->Form->isFieldError($fieldName)) {
- print $this->Form->error($fieldName);
- }
- break;
- default:
- // Use default field
- if($permissions['match']
- && ($fieldName == 'EnrolleeCoPerson.Name.given'
- || $fieldName == 'EnrolleeCoPerson.Name.family')) {
- # XXX Temp hack to enable real-time query. This should
- # instead be enabled for fields with an appropriate flag.
- print $this->Form->input($fieldName, array('class' =>
'matchable'));
- } else {
- print $this->Form->input($fieldName);
- }
- break;
- }
-
- print "\n";
- } else {
- // Just emit the current value for this attribute, if set
-
- if(isset($co_petition_attribute_values[ $ea['id'] ][
$ea['field'] ])) {
- switch($ruleType) {
- case 'inList':
- print $ea['select'][ $co_petition_attribute_values[
$ea['id'] ][ $ea['field'] ]];
- break;
- case 'validateTimestamp':
- print $this->Time->nice($co_petition_attribute_values[
$ea['id'] ][ $ea['field'] ]);
- break;
- default:
- print $co_petition_attribute_values[ $ea['id'] ][
$ea['field'] ];
- break;
- }
- }
- }
- ?>
- </td>
- </tr>
- <?php endif; ?>
- <?php endforeach; ?>
- <tr>
- <td>
- <i><font class="required"><?php echo _txt('fd.req');
?></font></i><br />
- </td>
- <td>
- <?php
- if($e)
- print $this->Form->submit($submit_label);
- ?>
- </td>
- </tr>
- </tbody>
-</table>
-
- </div>
-</div>
-
-<div>
- <div style="float:right;width:35%" id="results">
- </div>
-</div>
-
-<?php if($this->action != 'add'): ?>
-<h2 class="ui-state-default"><?php print _txt('fd.history.pt'); ?></h2>
-
-<table id="<?php print $this->action; ?>_co_petition_attrs"
class="ui-widget">
- <tbody>
- <tr>
- <th class="ui-state-default"><?php print _txt('fd.action'); ?></th>
- <th class="ui-state-default"><?php print _txt('fd.actor'); ?></th>
- <th class="ui-state-default"><?php print _txt('fd.comment'); ?></th>
- <th class="ui-state-default"><?php print _txt('fd.timestamp'); ?></th>
- </tr>
-
- <?php foreach ($co_petitions[0]['CoPetitionHistoryRecord'] as $hr): ?>
- <tr class="line<?php print ($l % 2); $l++; ?>">
- <td><?php print $hr['action']; ?></td>
- <td><?php print generateCn($hr['ActorCoPerson']['Name']); ?></td>
- <td><?php print $hr['comment']; ?></td>
- <td><?php print $this->Time->nice($hr['created']); ?></td>
- </tr>
- <?php endforeach; // CoPetitionHistoryRecord ?>
- </tbody>
-</table>
-<?php endif; // action == add ?>
+ include('petition-attributes.inc');

Modified: registry/trunk/app/View/CoPetitions/index.ctp
===================================================================
--- registry/trunk/app/View/CoPetitions/index.ctp 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/CoPetitions/index.ctp 2012-09-26 00:53:01
UTC (rev 365)
@@ -23,7 +23,7 @@
*/
-->
<?php
- $params = array('title' => $cur_co['Co']['name'] . " People");
+ $params = array('title' => $cur_co['Co']['name'] . " Petitions"); // XXX
I18N
print $this->element("pageTitle", $params);

if($permissions['add']) {
@@ -34,8 +34,6 @@
<br />
';
}
-
-// debug($co_petitions);
?>

<table id="co_people" class="ui-widget">
@@ -113,22 +111,22 @@
<?php
global $status_t;

- if(!empty($p['EnrolleeCoPerson']['status'])) {
- print _txt('en.status', null, $p['EnrolleeCoPerson']['status']);
+ if(!empty($p['CoPetition']['status'])) {
+ print _txt('en.status', null, $p['CoPetition']['status']);
}
?>
</td>
<td>
<?php
- if(!empty($p['EnrolleeCoPerson']['created'])) {
- print $this->Time->niceShort($p['EnrolleeCoPerson']['created']);
+ if(!empty($p['CoPetition']['created'])) {
+ print $this->Time->niceShort($p['CoPetition']['created']);
}
?>
</td>
<td>
<?php
- if(!empty($p['EnrolleeCoPerson']['modified'])) {
- print $this->Time->niceShort($p['EnrolleeCoPerson']['modified']);
+ if(!empty($p['CoPetition']['modified'])) {
+ print $this->Time->niceShort($p['CoPetition']['modified']);
}
?>
</td>
@@ -143,12 +141,25 @@
'coef' =>
$p['CoPetition']['co_enrollment_flow_id']),
array('class' => 'editbutton')) . "\n";
}
+
+ if($permissions['delete'])
+ print '<button class="deletebutton" title="' . _txt('op.delete')
. '" onclick="javascript:js_confirm_delete(\'' .
_jtxt(Sanitize::html($p['CoPetition']['id'])) . '\', \'' .
$this->Html->url(array('controller' => 'co_petitions', 'action' => 'delete',
$p['CoPetition']['id'], 'co' => $cur_co['Co']['id'])) . '\')";>' .
_txt('op.delete') . "</button>\n";
+
+ if($permissions['resend'] && $p['CoPetition']['status'] ==
StatusEnum::PendingConfirmation) {
+ print $this->Html->link(_txt('op.inv.resend'),
+ array('controller' => 'co_petitions',
+ 'action' => 'resend',
+ $p['CoPetition']['id'],
+ 'co' => $cur_co['Co']['id'],
+ 'coef' =>
$p['CoPetition']['co_enrollment_flow_id']),
+ array('class' => 'invitebutton')) . "\n";
+ }
?>
<?php ; ?>
</td>
</tr>
<?php $i++; ?>
- <?php endforeach; // $co_people ?>
+ <?php endforeach; // $co_petitions ?>
</tbody>

<tfoot>

Modified: registry/trunk/app/View/Elements/dropMenu.ctp
===================================================================
--- registry/trunk/app/View/Elements/dropMenu.ctp 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/Elements/dropMenu.ctp 2012-09-26 00:53:01
UTC (rev 365)
@@ -69,6 +69,17 @@
print "</li>";
}

+ if(isset($permissions['menu']['enrollmentflows']) &&
$permissions['menu']['enrollmentflows']) {
+ print "<li>";
+ $args = array(
+ 'controller' => 'co_enrollment_flows',
+ 'action' => 'select',
+ 'co' => $menuCoId
+ );
+ print
$this->Html->link(_txt('ct.co_enrollment_flows.pl'), $args);
+ print "</li>";
+ }
+
if(isset($permissions['menu']['petitions']) &&
$permissions['menu']['petitions']) {
print "<li>";
$args = array(

Modified: registry/trunk/app/View/EmailAddresses/fields.inc
===================================================================
--- registry/trunk/app/View/EmailAddresses/fields.inc 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/EmailAddresses/fields.inc 2012-09-26 00:53:01
UTC (rev 365)
@@ -91,6 +91,14 @@
?>
</td>
</tr>
+ <tr class="line1">
+ <td>
+ <?php print _txt('fd.email_address.verified'); ?>
+ </td>
+ <td>
+ <?php print ($email_addresses[0]['EmailAddress']['verified'] ?
_txt('fd.yes') : _txt('fd.no')); ?>
+ </td>
+ </tr>
<tr>
<td>
<i><font class="required"><?php echo _txt('fd.req');
?></font></i><br />

Modified: registry/trunk/app/View/Emails/html/coinvite.ctp
===================================================================
--- registry/trunk/app/View/Emails/html/coinvite.ctp 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/Emails/html/coinvite.ctp 2012-09-26 00:53:01
UTC (rev 365)
@@ -22,11 +22,11 @@
* @version $Id$
*/

- print _txt('em.invite.body', array($this->viewVars['Co']['name']));
+ print _txt('em.invite.body', array($this->viewVars['co_name']));
?>
<br><br>
<?php
- $u = $this->Html->url(array('controller' => 'co_invites', 'action' =>
'reply', $this->viewVars['CoInvite']['invitation']), true);
+ $u = $this->Html->url(array('controller' => 'co_invites', 'action' =>
'reply', $this->viewVars['invite_id']), true);
print $u;
?>


Modified: registry/trunk/app/View/Emails/text/coinvite.ctp
===================================================================
--- registry/trunk/app/View/Emails/text/coinvite.ctp 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/Emails/text/coinvite.ctp 2012-09-26 00:53:01
UTC (rev 365)
@@ -22,12 +22,12 @@
* @version $Id$
*/

- print _txt('em.invite.body', array($this->viewVars['Co']['name']));
+ print _txt('em.invite.body', array($this->viewVars['co_name']));

print '
';

- $u = $this->Html->url(array('controller' => 'co_invites', 'action' =>
'reply', $this->viewVars['CoInvite']['invitation']), true);
+ $u = $this->Html->url(array('controller' => 'co_invites', 'action' =>
'reply', $this->viewVars['invite_id']), true);
print $u;
?>


Modified: registry/trunk/app/View/Layouts/default.ctp
===================================================================
--- registry/trunk/app/View/Layouts/default.ctp 2012-09-24 06:33:39 UTC (rev
364)
+++ registry/trunk/app/View/Layouts/default.ctp 2012-09-26 00:53:01 UTC (rev
365)
@@ -72,6 +72,28 @@
$('#dialog').dialog('open');
}

+ function js_confirm_generic(txt, url)
+ {
+ // Generate a dialog box confirming <txt>. On confirmation, forward
to <url>.
+
+ // Set the title of the dialog
+ $("#dialog").dialog("option", "title", "<?php print
_txt('op.confirm'); ?>" + " " + name);
+
+ // Set the body of the dialog
+ $("#dialog-text").text(txt);
+
+ // Set the dialog buttons
+ $("#dialog").dialog("option",
+ "buttons",
+ {
+ "<?php print _txt('op.cancel'); ?>": function()
{ $(this).dialog("close"); },
+ "<?php print _txt('op.remove'); ?>": function()
{ window.location=url; }
+ });
+
+ // Open the dialog
+ $('#dialog').dialog('open');
+ }
+
function js_confirm_reinvite(name, url)
{
// Generate a dialog box confirming a resend of an invitation to
<name>. On confirmation, forward to <url>, which executes the invite.
@@ -228,6 +250,20 @@
text: false
});

+ $(".unlinkbutton").button({
+ icons: {
+ primary: 'ui-icon-cancel'
+ },
+ text: false
+ });
+
+ $(".viewbutton").button({
+ icons: {
+ primary: 'ui-icon-extlink'
+ },
+ text: false
+ });
+
// Datepickers

$(".datepicker").datepicker({

Modified: registry/trunk/app/View/OrgIdentities/fields.inc
===================================================================
--- registry/trunk/app/View/OrgIdentities/fields.inc 2012-09-24 06:33:39
UTC (rev 364)
+++ registry/trunk/app/View/OrgIdentities/fields.inc 2012-09-26 00:53:01
UTC (rev 365)
@@ -413,7 +413,8 @@
'action' =>
'edit',
$ea['id'],
'co' =>
($pool_org_identities ? false : $cur_co['Co']['id'])));
- print "&nbsp;(" . _txt('en.contact', null, $ea['type'])
. ")";
+ print "&nbsp;(" . _txt('en.contact', null, $ea['type'])
. ", "
+ . ($ea['verified'] ? _txt('fd.email_address.verified')
: _txt('fd.email_address.unverified')) . ")";
print '</div>';

// XXX we already checked for $permissions['edit'], but
not ['delete']... should we?



  • [comanage-dev] r365 - in registry/trunk/app: Config/Schema Controller Lib Model View/CoEnrollmentAttributes View/CoEnrollmentFlows View/CoInvites View/CoPeople View/CoPetitions View/Elements View/EmailAddresses View/Emails/html View/Emails/text View/Layouts View/OrgIdentities, svnlog, 09/25/2012

Archive powered by MHonArc 2.6.16.

Top of Page