1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42:41 +01:00

Conpherence - people widget

Summary:
adds ye olde people widget. Features include

 - handle-based display, so we get status for free. (Note less pretty than M14 would have it!)
 - can add a person
 - can remove a person
 - can see the people already in the conpherence

Test Plan: added and removed people and noted they joined / re-added as appropriate. Tried to add someone already in the conpherence and got a "transaction has no effect" message

Reviewers: epriestley, chad

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T2399

Differential Revision: https://secure.phabricator.com/D5466
This commit is contained in:
Bob Trahan 2013-04-02 09:32:40 -07:00
parent 5bd54e35bc
commit a97968b9ff
13 changed files with 485 additions and 95 deletions

View file

@ -903,7 +903,7 @@ celerity_register_resource_map(array(
),
'conpherence-widget-pane-css' =>
array(
'uri' => '/res/bd8ca250/rsrc/css/application/conpherence/widget-pane.css',
'uri' => '/res/65c7c73f/rsrc/css/application/conpherence/widget-pane.css',
'type' => 'css',
'requires' =>
array(
@ -1253,7 +1253,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-conpherence-menu' =>
array(
'uri' => '/res/35811cd4/rsrc/js/application/conpherence/behavior-menu.js',
'uri' => '/res/29585411/rsrc/js/application/conpherence/behavior-menu.js',
'type' => 'js',
'requires' =>
array(
@ -1283,7 +1283,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-conpherence-widget-pane' =>
array(
'uri' => '/res/45d53f1f/rsrc/js/application/conpherence/behavior-widget-pane.js',
'uri' => '/res/c0990399/rsrc/js/application/conpherence/behavior-widget-pane.js',
'type' => 'js',
'requires' =>
array(

View file

@ -239,6 +239,7 @@ phutil_register_library_map(array(
'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php',
'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php',
'ConpherencePeopleMenuEventListener' => 'applications/conpherence/events/ConpherencePeopleMenuEventListener.php',
'ConpherencePeopleWidgetView' => 'applications/conpherence/view/ConpherencePeopleWidgetView.php',
'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php',
'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php',
'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
@ -252,6 +253,7 @@ phutil_register_library_map(array(
'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php',
'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php',
'ConpherenceWidgetController' => 'applications/conpherence/controller/ConpherenceWidgetController.php',
'ConpherenceWidgetView' => 'applications/conpherence/view/ConpherenceWidgetView.php',
'DarkConsoleController' => 'aphront/console/DarkConsoleController.php',
'DarkConsoleCore' => 'aphront/console/DarkConsoleCore.php',
'DarkConsoleDataController' => 'aphront/console/DarkConsoleDataController.php',
@ -1941,7 +1943,7 @@ phutil_register_library_map(array(
'ConpherenceController' => 'PhabricatorController',
'ConpherenceDAO' => 'PhabricatorLiskDAO',
'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor',
'ConpherenceFileWidgetView' => 'AphrontView',
'ConpherenceFileWidgetView' => 'ConpherenceWidgetView',
'ConpherenceFormDragAndDropUploadControl' => 'AphrontFormControl',
'ConpherenceImageData' => 'ConpherenceConstants',
'ConpherenceLayoutView' => 'AphrontView',
@ -1952,6 +1954,7 @@ phutil_register_library_map(array(
'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery',
'ConpherenceParticipationStatus' => 'ConpherenceConstants',
'ConpherencePeopleMenuEventListener' => 'PhutilEventListener',
'ConpherencePeopleWidgetView' => 'ConpherenceWidgetView',
'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler',
'ConpherenceSettings' => 'ConpherenceConstants',
'ConpherenceThread' =>
@ -1969,6 +1972,7 @@ phutil_register_library_map(array(
'ConpherenceUpdateController' => 'ConpherenceController',
'ConpherenceViewController' => 'ConpherenceController',
'ConpherenceWidgetController' => 'ConpherenceController',
'ConpherenceWidgetView' => 'AphrontView',
'DarkConsoleController' => 'PhabricatorController',
'DarkConsoleDataController' => 'PhabricatorController',
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',

View file

@ -38,7 +38,7 @@ final class ConpherenceUpdateController
$action = $request->getStr('action', 'metadata');
$latest_transaction_id = null;
$fancy_ajax_style = true;
$response_mode = 'ajax';
$error_view = null;
$e_file = array();
$errors = array();
@ -56,11 +56,36 @@ final class ConpherenceUpdateController
switch ($action) {
case 'message':
$message = $request->getStr('text');
$latest_transaction_id = $request->getInt('latest_transaction_id');
$xactions = $editor->generateTransactionsFromText(
$conpherence,
$message);
break;
case 'add_person':
$xactions = array();
$person_tokenizer = $request->getArr('add_person');
$person_phid = reset($person_tokenizer);
if ($person_phid) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceTransactionType::TYPE_PARTICIPANTS)
->setNewValue(array('+' => array($person_phid)));
}
break;
case 'remove_person':
$xactions = array();
if (!$request->isContinueRequest()) {
// do nothing; we'll display a confirmation dialogue instead
break;
}
$person_phid = $request->getStr('remove_person');
if ($person_phid && $person_phid == $user->getPHID()) {
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(
ConpherenceTransactionType::TYPE_PARTICIPANTS)
->setNewValue(array('-' => array($person_phid)));
$response_mode = 'go-home';
}
break;
case 'notifications':
$notifications = $request->getStr('notifications');
$participant = $conpherence->getParticipant($user->getPHID());
@ -116,7 +141,7 @@ final class ConpherenceUpdateController
// use the existing title in this image upload case
$title = $conpherence->getTitle();
$updated = true;
$fancy_ajax_style = false;
$response_mode = 'redirect';
}
// all other metadata updates are continue requests
@ -159,20 +184,29 @@ final class ConpherenceUpdateController
if ($xactions) {
try {
$xactions = $editor->applyTransactions($conpherence, $xactions);
if ($fancy_ajax_style) {
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
->setException($ex);
}
switch ($response_mode) {
case 'ajax':
$latest_transaction_id = $request->getInt('latest_transaction_id');
$content = $this->loadAndRenderUpdates(
$conpherence_id,
$latest_transaction_id);
return id(new AphrontAjaxResponse())
->setContent($content);
} else {
break;
case 'go-home':
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI());
break;
case 'redirect':
default:
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI($conpherence->getID().'/'));
}
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
->setException($ex);
break;
}
}
}
@ -185,6 +219,9 @@ final class ConpherenceUpdateController
}
switch ($action) {
case 'remove_person':
$dialogue = $this->renderRemovePersonDialogue($conpherence);
break;
case 'metadata':
default:
$dialogue = $this->renderMetadataDialogue($conpherence, $error_view);
@ -201,6 +238,37 @@ final class ConpherenceUpdateController
}
private function renderRemovePersonDialogue(
ConpherenceThread $conpherence) {
$request = $this->getRequest();
$user = $request->getUser();
$remove_person = $request->getStr('remove_person');
$participants = $conpherence->getParticipants();
$message = pht(
'Are you sure you want to remove yourself from this conpherence? ');
if (count($participants) == 1) {
$message .= pht(
'The conpherence will be inaccessible forever and ever.');
} else {
$message .= pht(
'Someone else in the conpherence can add you back later.');
}
$body = phutil_tag(
'p',
array(
),
$message);
require_celerity_resource('conpherence-update-css');
return id(new AphrontDialogView())
->setTitle(pht('Update Conpherence Participants'))
->addHiddenInput('action', 'remove_person')
->addHiddenInput('__continue__', true)
->addHiddenInput('remove_person', $remove_person)
->appendChild($body);
}
private function renderMetadataDialogue(
ConpherenceThread $conpherence,
$error_view) {
@ -225,15 +293,15 @@ final class ConpherenceUpdateController
'src' =>
$conpherence->loadImageURI(ConpherenceImageData::SIZE_HEAD),
))))
->appendChild(
id(new AphrontFormCropControl())
->setLabel(pht('Crop Image'))
->setValue($image)
->setWidth(ConpherenceImageData::HEAD_WIDTH)
->setHeight(ConpherenceImageData::HEAD_HEIGHT))
->appendChild(
id(new ConpherenceFormDragAndDropUploadControl())
->setLabel(pht('Change Image')));
->appendChild(
id(new AphrontFormCropControl())
->setLabel(pht('Crop Image'))
->setValue($image)
->setWidth(ConpherenceImageData::HEAD_WIDTH)
->setHeight(ConpherenceImageData::HEAD_HEIGHT))
->appendChild(
id(new ConpherenceFormDragAndDropUploadControl())
->setLabel(pht('Change Image')));
} else {
$form
->appendChild(
@ -274,11 +342,15 @@ final class ConpherenceUpdateController
$header = $this->buildHeaderPaneContent($conpherence);
$widget_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/');
$file_widget = id(new ConpherenceFileWidgetView())
->setUser($this->getRequest()->getUser())
->setConpherence($conpherence)
->setUpdateURI(
$this->getApplicationURI('update/'.$conpherence->getID().'/'));
->setUpdateURI($widget_uri);
$people_widget = id(new ConpherencePeopleWidgetView())
->setUser($user)
->setConpherence($conpherence)
->setUpdateURI($widget_uri);
$content = array(
'transactions' => $rendered_transactions,
@ -286,7 +358,8 @@ final class ConpherenceUpdateController
'nav_item' => hsprintf('%s', $nav_item),
'conpherence_phid' => $conpherence->getPHID(),
'header' => hsprintf('%s', $header),
'file_widget' => $file_widget->render()
'file_widget' => $file_widget->render(),
'people_widget' => $people_widget->render()
);
return $content;
}

View file

@ -69,6 +69,9 @@ final class ConpherenceWidgetController extends
Javelin::initBehavior(
'conpherence-widget-pane',
array(
'header' => 'conpherence-header-pane',
'messages' => 'conpherence-messages',
'people_widget' => 'widgets-people',
'file_widget' => 'widgets-files',
'settings_widget' => 'widgets-settings',
'widgetRegistery' => array(
@ -83,7 +86,8 @@ final class ConpherenceWidgetController extends
$conpherence = $this->getConpherence();
$widgets = phutil_tag(
$widgets = array();
$widgets[] = phutil_tag(
'div',
array(
'class' => 'widgets-header'
@ -167,50 +171,50 @@ final class ConpherenceWidgetController extends
'class' => 'sprite-conpherence conpherence_settings_off',
),
'')
))).
phutil_tag(
)));
$user = $this->getRequest()->getUser();
// now the widget bodies
$widgets[] = phutil_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-people',
'style' => 'display: none;'
),
$this->renderPeopleWidgetPaneContent()).
javelin_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-files',
'sigil' => 'conpherence-widget-files',
),
id(new ConpherenceFileWidgetView())
->setUser($this->getRequest()->getUser())
->setConpherence($conpherence)
->setUpdateURI(
$this->getApplicationURI('update/'.$conpherence->getID().'/'))
->render()).
phutil_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-calendar',
'style' => 'display: none;'
),
$this->renderCalendarWidgetPaneContent()).
phutil_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-settings',
'style' => 'display: none'
),
$this->renderSettingsWidgetPaneContent());
id(new ConpherencePeopleWidgetView())
->setUser($user)
->setConpherence($conpherence)
->setUpdateURI($this->getWidgetURI()));
$widgets[] = phutil_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-files',
),
id(new ConpherenceFileWidgetView())
->setUser($user)
->setConpherence($conpherence)
->setUpdateURI($this->getWidgetURI()));
$widgets[] = phutil_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-calendar',
'style' => 'display: none;'
),
$this->renderCalendarWidgetPaneContent());
$widgets[] = phutil_tag(
'div',
array(
'class' => 'widgets-body',
'id' => 'widgets-settings',
'style' => 'display: none'
),
$this->renderSettingsWidgetPaneContent());
return array('widgets' => $widgets);
}
private function renderPeopleWidgetPaneContent() {
return 'TODO - people';
// without this implosion we get "," between each element in our widgets
// array
return array('widgets' => phutil_implode_html('', $widgets));
}
private function renderSettingsWidgetPaneContent() {
@ -244,8 +248,6 @@ final class ConpherenceWidgetController extends
->setName('notifications')
->setValue($notifications);
$href = $this->getApplicationURI(
'update/'.$conpherence->getID().'/');
$layout = array(
$options,
phutil_tag(
@ -268,7 +270,7 @@ final class ConpherenceWidgetController extends
$user,
array(
'method' => 'POST',
'action' => $href,
'action' => $this->getWidgetURI(),
),
$layout);
}
@ -369,4 +371,9 @@ final class ConpherenceWidgetController extends
return $timestamps;
}
private function getWidgetURI() {
$conpherence = $this->getConpherence();
return $this->getApplicationURI('update/'.$conpherence->getID().'/');
}
}

View file

@ -159,23 +159,35 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
}
break;
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
$participants = array();
foreach ($xaction->getNewValue() as $participant) {
if ($participant == $this->getActor()->getPHID()) {
$participants = $object->getParticipants();
$old_map = array_fuse($xaction->getOldValue());
$new_map = array_fuse($xaction->getNewValue());
$remove = array_keys(array_diff_key($old_map, $new_map));
foreach ($remove as $phid) {
$remove_participant = $participants[$phid];
$remove_participant->delete();
unset($participants[$phid]);
}
$add = array_keys(array_diff_key($new_map, $old_map));
foreach ($add as $phid) {
if ($phid == $this->getActor()->getPHID()) {
$status = ConpherenceParticipationStatus::UP_TO_DATE;
} else {
$status = ConpherenceParticipationStatus::BEHIND;
}
$participants[] =
$participants[$phid] =
id(new ConpherenceParticipant())
->setConpherencePHID($object->getPHID())
->setParticipantPHID($participant)
->setParticipantPHID($phid)
->setParticipationStatus($status)
->setDateTouched(time())
->setBehindTransactionPHID($xaction->getPHID())
->save();
}
$participants = mpull($participants, null, 'getParticipantPHID');
$object->attachParticipants($participants);
break;
}
@ -224,11 +236,14 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$to_phids = array();
$participants = $object->getParticipants();
if (empty($participants)) {
return $to_phids;
}
$preferences = id(new PhabricatorUserPreferences())
->loadAllWhere('userPHID in (%Ls)', array_keys($participants));
$preferences = mpull($preferences, null, 'getUserPHID');
$to_phids = array();
foreach ($participants as $phid => $participant) {
$default = ConpherenceSettings::EMAIL_ALWAYS;
$preference = idx($preferences, $phid);

View file

@ -21,6 +21,17 @@ final class ConpherenceTransaction extends PhabricatorApplicationTransaction {
return pht('conpherence');
}
public function getNoEffectDescription() {
switch ($this->getTransactionType()) {
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
return pht(
'You can not add a participant who has already been added.');
break;
}
return parent::getNoEffectDescription();
}
public function shouldHide() {
$old = $this->getOldValue();

View file

@ -1,25 +1,9 @@
<?php
final class ConpherenceFileWidgetView extends AphrontView {
private $conpherence;
private $updateURI;
public function setUpdateURI($update_uri) {
$this->updateURI = $update_uri;
return $this;
}
public function getUpdateURI() {
return $this->updateURI;
}
public function setConpherence(ConpherenceThread $conpherence) {
$this->conpherence = $conpherence;
return $this;
}
public function getConpherence() {
return $this->conpherence;
}
/**
* @group conpherence
*/
final class ConpherenceFileWidgetView extends ConpherenceWidgetView {
public function render() {
require_celerity_resource('sprite-docs-css');

View file

@ -0,0 +1,110 @@
<?php
/**
* @group conpherence
*/
final class ConpherencePeopleWidgetView extends ConpherenceWidgetView {
public function render() {
$conpherence = $this->getConpherence();
$widget_data = $conpherence->getWidgetData();
$user = $this->getUser();
$conpherence = $this->getConpherence();
$participants = $conpherence->getParticipants();
$handles = $conpherence->getHandles();
// ye olde add people widget
$add_widget = phabricator_form(
$user,
array(
'method' => 'POST',
'action' => $this->getUpdateURI(),
),
array(
id(new AphrontFormTokenizerControl())
->setPlaceholder(pht('Add a person...'))
->setName('add_person')
->setUser($user)
->setDatasource('/typeahead/common/users/')
->setLimit(1),
javelin_tag(
'button',
array(
'sigil' => 'add-person',
'class' => 'people-add-button',
'meta' => array(
'action' => 'add_person',
'latest_transaction_id' => $this->getLatestTransactionID()
)
),
pht('Add'))
));
$header = phutil_tag(
'div',
array(
'class' => 'people-widget-header'
),
array(
phutil_tag(
'div',
array(
'class' => 'add-people-widget',
),
$add_widget),
phutil_tag(
'div',
array(
'class' => 'divider'
),
'')
));
$body = array();
// future proof by using participants to iterate through handles;
// we may have non-people handles sooner or later
foreach ($participants as $user_phid => $participant) {
$handle = $handles[$user_phid];
$remove_html = '';
if ($user_phid == $user->getPHID()) {
$remove_html = javelin_tag(
'a',
array(
'class' => 'remove',
'sigil' => 'remove-person',
'meta' => array(
'remove_person' => $handle->getPHID(),
'action' => 'remove_person',
'latest_transaction_id' => $this->getLatestTransactionID()
)
),
phutil_tag(
'span',
array(
'class' => 'icon'
),
'x'));
}
$body[] = phutil_tag(
'div',
array(
'class' => 'person-entry'
),
array(
phutil_tag(
'a',
array(
'class' => 'pic',
),
phutil_tag(
'img',
array(
'src' => $handle->getImageURI()
),
'')),
$handle->renderLink(),
$remove_html));
}
return array($header, $body);
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* @group conpherence
*/
abstract class ConpherenceWidgetView extends AphrontView {
private $conpherence;
private $updateURI;
public function setUpdateURI($update_uri) {
$this->updateURI = $update_uri;
return $this;
}
public function getUpdateURI() {
return $this->updateURI;
}
public function setConpherence(ConpherenceThread $conpherence) {
$this->conpherence = $conpherence;
return $this;
}
public function getConpherence() {
return $this->conpherence;
}
public function getLatestTransactionID() {
$transactions = $this->getConpherence()->getTransactions();
$latest = end($transactions);
return $latest->getID();
}
}

View file

@ -217,6 +217,13 @@ abstract class PhabricatorBaseEnglishTranslation
),
),
'%d people(s)' => array(
array(
'%d person',
'%d people',
),
),
'%s Line(s)' => array(
'%s Line',
'%s Lines',

View file

@ -192,6 +192,91 @@
padding-bottom: 1px;
}
/* people widget */
.conpherence-widget-pane .people-widget-header {
float: left;
width: 280px;
}
.conpherence-widget-pane .people-widget-header .divider {
float: left;
clear: both;
width: 260px;
margin: 0px 0px 0px 10px;
border: 1px dashed #bfbfbf;
}
.conpherence-widget-pane .people-widget-header .add-people-widget {
float: left;
padding: 10px 0px 10px 0px;
width: 280px;
}
.conpherence-widget-pane .people-widget-header .add-people-widget
.aphront-form-control-tokenizer {
float: left;
width: 180px;
padding: 0px 0px 0px 10px
}
.conpherence-widget-pane .people-widget-header .add-people-widget
.jx-tokenizer-input {
padding: 1px 3px 1px 3px;
}
.conpherence-widget-pane .people-widget-header .add-people-widget
.people-add-button {
float: right;
margin: 0px 10px 0px 0px;
padding: 3px 16px 4px 16px;
}
.conpherence-widget-pane .person-entry {
float: left;
width: 270px;
clear: both;
padding: 10px 0px 0px 8px;
}
.conpherence-widget-pane .person-entry a {
float: left;
clear: none;
font-size: 14px;
font-weight: bold;
width: 166px;
}
.conpherence-widget-pane .person-entry .pic {
float: left;
clear: left;
margin: 0px 10px 0px 0px;
width: 50px;
padding: 0;
}
.conpherence-widget-pane .person-entry .remove {
float: right;
clear: right;
margin: 0;
width: 34px;
height: 36px;
text-align: center;
font-size: 22px;
font-weight: bold;
padding: 8px 0px 8px 0px;
}
.conpherence-widget-pane .person-entry .remove:hover {
text-decoration: none;
}
.conpherence-widget-pane .person-entry .remove .icon {
color: #bfbfbf;
}
.conpherence-widget-pane .person-entry .remove:hover .icon {
color: #18559d;
}
/* settings widget */
.conpherence-widget-pane .notifications-update {
margin: 2px 0px 0px 8px;

View file

@ -136,11 +136,17 @@ JX.behavior('conpherence-menu', function(config) {
JX.$H(r.header)
);
// update the menu entry as well
// update the menu entry
JX.DOM.replace(
JX.$(r.conpherence_phid + '-nav-item'),
JX.$H(r.nav_item)
);
// update the people widget
JX.DOM.setContent(
JX.$(config.people_widget),
JX.$H(r.people_widget)
);
})
.start();
});

View file

@ -39,6 +39,61 @@ JX.behavior('conpherence-widget-pane', function(config) {
}
);
/* people widget */
var peopleRoot = JX.$(config.people_widget);
var peopleUpdateHandler = function (r) {
// update the transactions
var messages = JX.$(config.messages);
JX.DOM.appendContent(messages, JX.$H(r.transactions));
messages.scrollTop = messages.scrollHeight;
// update the menu entry as well
JX.DOM.replace(
JX.$(r.conpherence_phid + '-nav-item'),
JX.$H(r.nav_item)
);
// update the header
JX.DOM.setContent(
JX.$(config.header),
JX.$H(r.header)
);
// update the people widget
JX.DOM.setContent(
JX.$(config.people_widget),
JX.$H(r.people_widget)
);
};
JX.DOM.listen(
peopleRoot,
['click'],
'add-person',
function (e) {
e.kill();
var form = JX.DOM.find(peopleRoot, 'form');
var data = e.getNodeData('add-person');
JX.Workflow.newFromForm(form, data)
.setHandler(peopleUpdateHandler)
.start();
}
);
JX.DOM.listen(
peopleRoot,
['click'],
'remove-person',
function (e) {
var form = JX.DOM.find(peopleRoot, 'form');
var data = e.getNodeData('remove-person');
JX.Workflow.newFromForm(form, data)
.setHandler(peopleUpdateHandler)
.start();
}
);
/* settings widget */
var settingsRoot = JX.$(config.settings_widget);
var onsubmitSettings = function (e) {