2011-02-08 19:53:59 +01:00
|
|
|
<?php
|
|
|
|
|
2013-10-22 01:58:37 +02:00
|
|
|
final class ManiphestTransactionEditor
|
|
|
|
extends PhabricatorApplicationTransactionEditor {
|
2011-02-08 19:53:59 +01:00
|
|
|
|
2013-10-22 01:58:37 +02:00
|
|
|
public function getTransactionTypes() {
|
|
|
|
$types = parent::getTransactionTypes();
|
2011-05-10 01:31:26 +02:00
|
|
|
|
2013-10-22 01:58:37 +02:00
|
|
|
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_PRIORITY;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_STATUS;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_TITLE;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_DESCRIPTION;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_OWNER;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_CCS;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_PROJECTS;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_ATTACH;
|
|
|
|
$types[] = ManiphestTransaction::TYPE_EDGE;
|
2014-02-27 18:39:59 +01:00
|
|
|
$types[] = ManiphestTransaction::TYPE_SUBPRIORITY;
|
2014-03-04 00:58:00 +01:00
|
|
|
$types[] = ManiphestTransaction::TYPE_PROJECT_COLUMN;
|
2013-10-22 01:58:37 +02:00
|
|
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
|
|
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
|
|
|
|
|
|
|
return $types;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getCustomTransactionOldValue(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransaction::TYPE_PRIORITY:
|
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (int)$object->getPriority();
|
|
|
|
case ManiphestTransaction::TYPE_STATUS:
|
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (int)$object->getStatus();
|
|
|
|
case ManiphestTransaction::TYPE_TITLE:
|
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return $object->getTitle();
|
|
|
|
case ManiphestTransaction::TYPE_DESCRIPTION:
|
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return $object->getDescription();
|
|
|
|
case ManiphestTransaction::TYPE_OWNER:
|
|
|
|
return nonempty($object->getOwnerPHID(), null);
|
|
|
|
case ManiphestTransaction::TYPE_CCS:
|
|
|
|
return array_values(array_unique($object->getCCPHIDs()));
|
|
|
|
case ManiphestTransaction::TYPE_PROJECTS:
|
|
|
|
return array_values(array_unique($object->getProjectPHIDs()));
|
|
|
|
case ManiphestTransaction::TYPE_ATTACH:
|
|
|
|
return $object->getAttached();
|
|
|
|
case ManiphestTransaction::TYPE_EDGE:
|
2014-03-04 00:58:00 +01:00
|
|
|
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
2013-10-22 01:58:37 +02:00
|
|
|
// These are pre-populated.
|
|
|
|
return $xaction->getOldValue();
|
2014-02-27 18:39:59 +01:00
|
|
|
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
|
|
|
return $object->getSubpriority();
|
2013-10-22 01:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getCustomTransactionNewValue(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransaction::TYPE_PRIORITY:
|
|
|
|
case ManiphestTransaction::TYPE_STATUS:
|
|
|
|
return (int)$xaction->getNewValue();
|
|
|
|
case ManiphestTransaction::TYPE_CCS:
|
|
|
|
case ManiphestTransaction::TYPE_PROJECTS:
|
|
|
|
return array_values(array_unique($xaction->getNewValue()));
|
|
|
|
case ManiphestTransaction::TYPE_OWNER:
|
|
|
|
return nonempty($xaction->getNewValue(), null);
|
|
|
|
case ManiphestTransaction::TYPE_TITLE:
|
|
|
|
case ManiphestTransaction::TYPE_DESCRIPTION:
|
|
|
|
case ManiphestTransaction::TYPE_ATTACH:
|
|
|
|
case ManiphestTransaction::TYPE_EDGE:
|
2014-02-27 18:39:59 +01:00
|
|
|
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
2014-03-04 00:58:00 +01:00
|
|
|
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
2013-10-22 01:58:37 +02:00
|
|
|
return $xaction->getNewValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add capabilities for editing task triage details (priority, assignee, etc)
Summary:
This is primarily a client request, and a little bit use-case specific, but policies seem to be holding up well and I'm getting more comfortable about maintaining this. Much if it can run through ApplicationTransactions.
Allow the ability to edit status, policies, priorities, assignees and projects of a task to be restricted to some subset of users. Also allow bulk edit to be locked. This affects the editor itself and the edit, view and list interfaces.
Test Plan: As a restricted user, created, edited and commented on tasks. Tried to drag them around.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D7357
2013-10-22 01:59:06 +02:00
|
|
|
|
2013-10-22 01:58:37 +02:00
|
|
|
protected function transactionHasEffect(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
$old = $xaction->getOldValue();
|
|
|
|
$new = $xaction->getNewValue();
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransaction::TYPE_PROJECTS:
|
|
|
|
case ManiphestTransaction::TYPE_CCS:
|
|
|
|
sort($old);
|
|
|
|
sort($new);
|
|
|
|
return ($old !== $new);
|
2014-03-04 00:58:00 +01:00
|
|
|
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
|
|
|
$new_column_phids = $new['columnPHIDs'];
|
|
|
|
$old_column_phids = $old['columnPHIDs'];
|
|
|
|
sort($new_column_phids);
|
|
|
|
sort($old_column_phids);
|
|
|
|
return ($old !== $new);
|
2013-10-22 01:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return parent::transactionHasEffect($object, $xaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function applyCustomInternalTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransaction::TYPE_PRIORITY:
|
|
|
|
return $object->setPriority($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_STATUS:
|
|
|
|
return $object->setStatus($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_TITLE:
|
|
|
|
return $object->setTitle($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_DESCRIPTION:
|
|
|
|
return $object->setDescription($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_OWNER:
|
2013-11-19 23:10:54 +01:00
|
|
|
$phid = $xaction->getNewValue();
|
|
|
|
|
|
|
|
// Update the "ownerOrdering" column to contain the full name of the
|
|
|
|
// owner, if the task is assigned.
|
|
|
|
|
|
|
|
$handle = null;
|
|
|
|
if ($phid) {
|
|
|
|
$handle = id(new PhabricatorHandleQuery())
|
|
|
|
->setViewer($this->getActor())
|
|
|
|
->withPHIDs(array($phid))
|
|
|
|
->executeOne();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($handle) {
|
|
|
|
$object->setOwnerOrdering($handle->getName());
|
|
|
|
} else {
|
|
|
|
$object->setOwnerOrdering(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $object->setOwnerPHID($phid);
|
2013-10-22 01:58:37 +02:00
|
|
|
case ManiphestTransaction::TYPE_CCS:
|
|
|
|
return $object->setCCPHIDs($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_PROJECTS:
|
|
|
|
return $object->setProjectPHIDs($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_ATTACH:
|
|
|
|
return $object->setAttached($xaction->getNewValue());
|
|
|
|
case ManiphestTransaction::TYPE_EDGE:
|
|
|
|
// These are a weird, funky mess and are already being applied by the
|
|
|
|
// time we reach this.
|
|
|
|
return;
|
2014-02-27 18:39:59 +01:00
|
|
|
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
|
|
|
$data = $xaction->getNewValue();
|
|
|
|
$new_sub = $this->getNextSubpriority(
|
|
|
|
$data['newPriority'],
|
|
|
|
$data['newSubpriorityBase']);
|
|
|
|
$object->setSubpriority($new_sub);
|
|
|
|
return;
|
2014-03-04 00:58:00 +01:00
|
|
|
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
|
|
|
// these do external (edge) updates
|
|
|
|
return;
|
2013-10-22 01:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-02-27 18:39:59 +01:00
|
|
|
protected function expandTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
$xactions = parent::expandTransaction($object, $xaction);
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
|
|
|
$data = $xaction->getNewValue();
|
|
|
|
$new_pri = $data['newPriority'];
|
|
|
|
if ($new_pri != $object->getPriority()) {
|
|
|
|
$xactions[] = id(new ManiphestTransaction())
|
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
|
|
|
->setNewValue($new_pri);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $xactions;
|
|
|
|
}
|
|
|
|
|
2013-10-22 01:58:37 +02:00
|
|
|
protected function applyCustomExternalTransaction(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
2014-03-04 00:58:00 +01:00
|
|
|
|
|
|
|
switch ($xaction->getTransactionType()) {
|
|
|
|
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
|
|
|
$new = $xaction->getNewValue();
|
|
|
|
$old = $xaction->getOldValue();
|
|
|
|
$src = $object->getPHID();
|
|
|
|
$dst = head($new['columnPHIDs']);
|
|
|
|
$edges = $old['columnPHIDs'];
|
|
|
|
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN;
|
|
|
|
// NOTE: Normally, we expect only one edge to exist, but this works in
|
|
|
|
// a general way so it will repair any stray edges.
|
|
|
|
$remove = array();
|
|
|
|
$edge_missing = true;
|
|
|
|
foreach ($edges as $phid) {
|
|
|
|
if ($phid == $dst) {
|
|
|
|
$edge_missing = false;
|
|
|
|
} else {
|
|
|
|
$remove[] = $phid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$add = array();
|
|
|
|
if ($edge_missing) {
|
|
|
|
$add[] = $dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This should never happen because of the code in
|
|
|
|
// transactionHasEffect, but keep it for maximum conservativeness
|
|
|
|
if (!$add && !$remove) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$editor = id(new PhabricatorEdgeEditor())
|
|
|
|
->setActor($this->getActor())
|
|
|
|
->setSuppressEvents(true);
|
|
|
|
|
|
|
|
foreach ($add as $phid) {
|
|
|
|
$editor->addEdge($src, $edge_type, $phid);
|
|
|
|
}
|
|
|
|
foreach ($remove as $phid) {
|
|
|
|
$editor->removeEdge($src, $edge_type, $phid);
|
|
|
|
}
|
|
|
|
$editor->save();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-10-22 01:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function shouldSendMail(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
2014-03-04 00:58:00 +01:00
|
|
|
|
2014-03-05 02:01:33 +01:00
|
|
|
$xactions = mfilter($xactions, 'shouldHide', true);
|
|
|
|
return $xactions;
|
2013-10-22 01:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getMailSubjectPrefix() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getMailThreadID(PhabricatorLiskDAO $object) {
|
|
|
|
return 'maniphest-task-'.$object->getPHID();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getMailTo(PhabricatorLiskDAO $object) {
|
|
|
|
return array(
|
|
|
|
$object->getOwnerPHID(),
|
|
|
|
$this->requireActor()->getPHID(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getMailCC(PhabricatorLiskDAO $object) {
|
|
|
|
return $object->getCCPHIDs();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
|
|
|
return id(new ManiphestReplyHandler())
|
|
|
|
->setMailReceiver($object);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
|
|
|
$id = $object->getID();
|
|
|
|
$title = $object->getTitle();
|
|
|
|
|
|
|
|
return id(new PhabricatorMetaMTAMail())
|
|
|
|
->setSubject("T{$id}: {$title}")
|
|
|
|
->addHeader('Thread-Topic', "T{$id}: ".$object->getOriginalTitle());
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildMailBody(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
$body = parent::buildMailBody($object, $xactions);
|
|
|
|
|
|
|
|
if ($this->getIsNewObject()) {
|
|
|
|
$body->addTextSection(
|
|
|
|
pht('TASK DESCRIPTION'),
|
|
|
|
$object->getDescription());
|
|
|
|
}
|
|
|
|
|
|
|
|
$body->addTextSection(
|
|
|
|
pht('TASK DETAIL'),
|
|
|
|
PhabricatorEnv::getProductionURI('/T'.$object->getID()));
|
|
|
|
|
|
|
|
return $body;
|
|
|
|
}
|
|
|
|
|
2014-03-05 02:01:33 +01:00
|
|
|
protected function shouldPublishFeedStory(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
return $this->shouldSendMail($object, $xactions);
|
2013-10-22 01:58:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function supportsSearch() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-03-05 21:06:59 +01:00
|
|
|
protected function shouldApplyHeraldRules(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
2013-10-22 01:58:37 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildHeraldAdapter(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
array $xactions) {
|
|
|
|
|
|
|
|
return id(new HeraldManiphestTaskAdapter())
|
|
|
|
->setTask($object);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function didApplyHeraldRules(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
HeraldAdapter $adapter,
|
|
|
|
HeraldTranscript $transcript) {
|
|
|
|
|
2014-03-05 21:06:59 +01:00
|
|
|
// TODO: Convert these to transactions. The way Maniphest deals with these
|
|
|
|
// transactions is currently unconventional and messy.
|
|
|
|
|
2013-10-22 01:58:37 +02:00
|
|
|
$save_again = false;
|
|
|
|
$cc_phids = $adapter->getCcPHIDs();
|
|
|
|
if ($cc_phids) {
|
|
|
|
$existing_cc = $object->getCCPHIDs();
|
|
|
|
$new_cc = array_unique(array_merge($cc_phids, $existing_cc));
|
|
|
|
$object->setCCPHIDs($new_cc);
|
|
|
|
$save_again = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$project_phids = $adapter->getProjectPHIDs();
|
|
|
|
if ($project_phids) {
|
|
|
|
$existing_projects = $object->getProjectPHIDs();
|
|
|
|
$new_projects = array_unique(
|
|
|
|
array_merge($project_phids, $existing_projects));
|
|
|
|
$object->setProjectPHIDs($new_projects);
|
|
|
|
$save_again = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($save_again) {
|
|
|
|
$object->save();
|
|
|
|
}
|
2014-03-05 21:06:59 +01:00
|
|
|
|
|
|
|
$xactions = array();
|
|
|
|
|
|
|
|
$assign_phid = $adapter->getAssignPHID();
|
|
|
|
if ($assign_phid) {
|
|
|
|
$xactions[] = id(new ManiphestTransaction())
|
|
|
|
->setTransactionType(ManiphestTransaction::TYPE_OWNER)
|
|
|
|
->setNewValue($assign_phid);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $xactions;
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|
2011-12-17 22:27:11 +01:00
|
|
|
|
Add capabilities for editing task triage details (priority, assignee, etc)
Summary:
This is primarily a client request, and a little bit use-case specific, but policies seem to be holding up well and I'm getting more comfortable about maintaining this. Much if it can run through ApplicationTransactions.
Allow the ability to edit status, policies, priorities, assignees and projects of a task to be restricted to some subset of users. Also allow bulk edit to be locked. This affects the editor itself and the edit, view and list interfaces.
Test Plan: As a restricted user, created, edited and commented on tasks. Tried to drag them around.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Differential Revision: https://secure.phabricator.com/D7357
2013-10-22 01:59:06 +02:00
|
|
|
protected function requireCapabilities(
|
|
|
|
PhabricatorLiskDAO $object,
|
|
|
|
PhabricatorApplicationTransaction $xaction) {
|
|
|
|
|
|
|
|
parent::requireCapabilities($object, $xaction);
|
|
|
|
|
|
|
|
$app_capability_map = array(
|
|
|
|
ManiphestTransaction::TYPE_PRIORITY =>
|
|
|
|
ManiphestCapabilityEditPriority::CAPABILITY,
|
|
|
|
ManiphestTransaction::TYPE_STATUS =>
|
|
|
|
ManiphestCapabilityEditStatus::CAPABILITY,
|
|
|
|
ManiphestTransaction::TYPE_PROJECTS =>
|
|
|
|
ManiphestCapabilityEditProjects::CAPABILITY,
|
|
|
|
ManiphestTransaction::TYPE_OWNER =>
|
|
|
|
ManiphestCapabilityEditAssign::CAPABILITY,
|
|
|
|
PhabricatorTransactions::TYPE_EDIT_POLICY =>
|
|
|
|
ManiphestCapabilityEditPolicies::CAPABILITY,
|
|
|
|
PhabricatorTransactions::TYPE_VIEW_POLICY =>
|
|
|
|
ManiphestCapabilityEditPolicies::CAPABILITY,
|
|
|
|
);
|
|
|
|
|
|
|
|
$transaction_type = $xaction->getTransactionType();
|
|
|
|
$app_capability = idx($app_capability_map, $transaction_type);
|
|
|
|
|
|
|
|
if ($app_capability) {
|
|
|
|
$app = id(new PhabricatorApplicationQuery())
|
|
|
|
->setViewer($this->getActor())
|
|
|
|
->withClasses(array('PhabricatorApplicationManiphest'))
|
|
|
|
->executeOne();
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$this->getActor(),
|
|
|
|
$app,
|
|
|
|
$app_capability);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-27 18:39:59 +01:00
|
|
|
private function getNextSubpriority($pri, $sub) {
|
2013-09-25 22:44:14 +02:00
|
|
|
|
2012-04-02 21:12:04 +02:00
|
|
|
if ($sub === null) {
|
|
|
|
$next = id(new ManiphestTask())->loadOneWhere(
|
|
|
|
'priority = %d ORDER BY subpriority ASC LIMIT 1',
|
|
|
|
$pri);
|
|
|
|
if ($next) {
|
|
|
|
return $next->getSubpriority() - ((double)(2 << 16));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$next = id(new ManiphestTask())->loadOneWhere(
|
|
|
|
'priority = %d AND subpriority > %s ORDER BY subpriority ASC LIMIT 1',
|
|
|
|
$pri,
|
|
|
|
$sub);
|
|
|
|
if ($next) {
|
|
|
|
return ($sub + $next->getSubpriority()) / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (double)(2 << 32);
|
|
|
|
}
|
|
|
|
|
2011-02-08 19:53:59 +01:00
|
|
|
}
|