mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Scaffolding for Fund
Summary: Ref T5835. This is all pretty boilerplate, and does not interact with Phortune at all yet. You can create "Initiatives", which have a title and description, and support most of the expected infrastructure (policies, transactions, mentions, edges, appsearch, remakrup, etc). Only notable decisions: - Initiatives have an explicit owner. I think it's good to have a single clearly-responsible user behind an initiative. - I think that's it? Test Plan: - Created an initiative. - Edited an initiative. - Changed application policy defaults. - Searched for initiatives. - Subscribed to an initiative. - Opened/closed an initiative. - Used `I123` and `{I123}` in remarkup. - Destroyed an initiative. Reviewers: chad, btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T5835 Differential Revision: https://secure.phabricator.com/D10481
This commit is contained in:
parent
cae59d8345
commit
e4f399b9fa
28 changed files with 1722 additions and 0 deletions
15
resources/sql/autopatches/20140911.fund.1.initiative.sql
Normal file
15
resources/sql/autopatches/20140911.fund.1.initiative.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE {$NAMESPACE}_fund.fund_initiative (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
ownerPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
description LONGTEXT NOT NULL,
|
||||
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
status VARCHAR(32) NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
KEY `key_status` (status),
|
||||
KEY `key_owner` (ownerPHID)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
19
resources/sql/autopatches/20140911.fund.2.xaction.sql
Normal file
19
resources/sql/autopatches/20140911.fund.2.xaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_fund.fund_initiativetransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARCHAR(64) COLLATE utf8_bin NOT NULL,
|
||||
authorPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
|
||||
objectPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
|
||||
viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
|
||||
editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
|
||||
commentPHID VARCHAR(64) COLLATE utf8_bin DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) COLLATE utf8_bin NOT NULL,
|
||||
oldValue LONGTEXT COLLATE utf8_bin NOT NULL,
|
||||
newValue LONGTEXT COLLATE utf8_bin NOT NULL,
|
||||
contentSource LONGTEXT COLLATE utf8_bin NOT NULL,
|
||||
metadata LONGTEXT COLLATE utf8_bin NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
15
resources/sql/autopatches/20140911.fund.3.edge.sql
Normal file
15
resources/sql/autopatches/20140911.fund.3.edge.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE {$NAMESPACE}_fund.edge (
|
||||
src VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
type VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
dst VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
seq INT UNSIGNED NOT NULL,
|
||||
dataID INT UNSIGNED,
|
||||
PRIMARY KEY (src, type, dst),
|
||||
KEY (src, type, dateCreated, seq)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_fund.edgedata (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
data LONGTEXT NOT NULL COLLATE utf8_bin
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
|
@ -654,6 +654,28 @@ phutil_register_library_map(array(
|
|||
'FlagDeleteConduitAPIMethod' => 'applications/flag/conduit/FlagDeleteConduitAPIMethod.php',
|
||||
'FlagEditConduitAPIMethod' => 'applications/flag/conduit/FlagEditConduitAPIMethod.php',
|
||||
'FlagQueryConduitAPIMethod' => 'applications/flag/conduit/FlagQueryConduitAPIMethod.php',
|
||||
'FundBacking' => 'applications/fund/storage/FundBacking.php',
|
||||
'FundBackingEditor' => 'applications/fund/editor/FundBackingEditor.php',
|
||||
'FundBackingPHIDType' => 'applications/fund/phid/FundBackingPHIDType.php',
|
||||
'FundBackingQuery' => 'applications/fund/query/FundBackingQuery.php',
|
||||
'FundBackingTransaction' => 'applications/fund/storage/FundBackingTransaction.php',
|
||||
'FundBackingTransactionQuery' => 'applications/fund/query/FundBackingTransactionQuery.php',
|
||||
'FundController' => 'applications/fund/controller/FundController.php',
|
||||
'FundCreateInitiativesCapability' => 'applications/fund/capability/FundCreateInitiativesCapability.php',
|
||||
'FundDAO' => 'applications/fund/storage/FundDAO.php',
|
||||
'FundDefaultViewCapability' => 'applications/fund/capability/FundDefaultViewCapability.php',
|
||||
'FundInitiative' => 'applications/fund/storage/FundInitiative.php',
|
||||
'FundInitiativeCloseController' => 'applications/fund/controller/FundInitiativeCloseController.php',
|
||||
'FundInitiativeEditController' => 'applications/fund/controller/FundInitiativeEditController.php',
|
||||
'FundInitiativeEditor' => 'applications/fund/editor/FundInitiativeEditor.php',
|
||||
'FundInitiativeListController' => 'applications/fund/controller/FundInitiativeListController.php',
|
||||
'FundInitiativePHIDType' => 'applications/fund/phid/FundInitiativePHIDType.php',
|
||||
'FundInitiativeQuery' => 'applications/fund/query/FundInitiativeQuery.php',
|
||||
'FundInitiativeRemarkupRule' => 'applications/fund/remarkup/FundInitiativeRemarkupRule.php',
|
||||
'FundInitiativeSearchEngine' => 'applications/fund/query/FundInitiativeSearchEngine.php',
|
||||
'FundInitiativeTransaction' => 'applications/fund/storage/FundInitiativeTransaction.php',
|
||||
'FundInitiativeTransactionQuery' => 'applications/fund/query/FundInitiativeTransactionQuery.php',
|
||||
'FundInitiativeViewController' => 'applications/fund/controller/FundInitiativeViewController.php',
|
||||
'HarbormasterBuild' => 'applications/harbormaster/storage/build/HarbormasterBuild.php',
|
||||
'HarbormasterBuildAbortedException' => 'applications/harbormaster/exception/HarbormasterBuildAbortedException.php',
|
||||
'HarbormasterBuildActionController' => 'applications/harbormaster/controller/HarbormasterBuildActionController.php',
|
||||
|
@ -1600,6 +1622,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagsApplication' => 'applications/flag/application/PhabricatorFlagsApplication.php',
|
||||
'PhabricatorFlagsUIEventListener' => 'applications/flag/events/PhabricatorFlagsUIEventListener.php',
|
||||
'PhabricatorFormExample' => 'applications/uiexample/examples/PhabricatorFormExample.php',
|
||||
'PhabricatorFundApplication' => 'applications/fund/application/PhabricatorFundApplication.php',
|
||||
'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php',
|
||||
'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollectorDaemon.php',
|
||||
|
@ -3422,6 +3445,42 @@ phutil_register_library_map(array(
|
|||
'FlagDeleteConduitAPIMethod' => 'FlagConduitAPIMethod',
|
||||
'FlagEditConduitAPIMethod' => 'FlagConduitAPIMethod',
|
||||
'FlagQueryConduitAPIMethod' => 'FlagConduitAPIMethod',
|
||||
'FundBacking' => array(
|
||||
'FundDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
),
|
||||
'FundBackingEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'FundBackingPHIDType' => 'PhabricatorPHIDType',
|
||||
'FundBackingQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'FundBackingTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'FundBackingTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'FundController' => 'PhabricatorController',
|
||||
'FundCreateInitiativesCapability' => 'PhabricatorPolicyCapability',
|
||||
'FundDAO' => 'PhabricatorLiskDAO',
|
||||
'FundDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'FundInitiative' => array(
|
||||
'FundDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorMentionableInterface',
|
||||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorTokenReceiverInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
),
|
||||
'FundInitiativeCloseController' => 'FundController',
|
||||
'FundInitiativeEditController' => 'FundController',
|
||||
'FundInitiativeEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'FundInitiativeListController' => 'FundController',
|
||||
'FundInitiativePHIDType' => 'PhabricatorPHIDType',
|
||||
'FundInitiativeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'FundInitiativeRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'FundInitiativeSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'FundInitiativeTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'FundInitiativeTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'FundInitiativeViewController' => 'FundController',
|
||||
'HarbormasterBuild' => array(
|
||||
'HarbormasterDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
@ -4465,6 +4524,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlagsApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorFlagsUIEventListener' => 'PhabricatorEventListener',
|
||||
'PhabricatorFormExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorFundApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorGarbageCollector' => 'Phobject',
|
||||
'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFundApplication extends PhabricatorApplication {
|
||||
|
||||
public function getName() {
|
||||
return pht('Fund');
|
||||
}
|
||||
|
||||
public function getBaseURI() {
|
||||
return '/fund/';
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht('Donate');
|
||||
}
|
||||
|
||||
public function getIconName() {
|
||||
return 'phund';
|
||||
}
|
||||
|
||||
public function getTitleGlyph() {
|
||||
return "\xE2\x99\xA5";
|
||||
}
|
||||
|
||||
public function getApplicationGroup() {
|
||||
return self::GROUP_UTILITIES;
|
||||
}
|
||||
|
||||
public function isBeta() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getRemarkupRules() {
|
||||
return array(
|
||||
new FundInitiativeRemarkupRule(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/I(?P<id>[1-9]\d*)' => 'FundInitiativeViewController',
|
||||
'/fund/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'FundInitiativeListController',
|
||||
'create/' => 'FundInitiativeEditController',
|
||||
'edit/(?:(?P<id>[^/]+)/)?' => 'FundInitiativeEditController',
|
||||
'close/(?P<id>[^/]+)/' => 'FundInitiativeCloseController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
FundDefaultViewCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default view policy for newly created initiatives.'),
|
||||
),
|
||||
FundCreateInitiativesCapability::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class FundCreateInitiativesCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'fund.create';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Create Initiatives');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to create Fund initiatives.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
final class FundDefaultViewCapability extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'fund.default.view';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default View Policy');
|
||||
}
|
||||
|
||||
public function shouldAllowPublicPolicySetting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
3
src/applications/fund/controller/FundController.php
Normal file
3
src/applications/fund/controller/FundController.php
Normal file
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
abstract class FundController extends PhabricatorController {}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeCloseController
|
||||
extends FundController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$initiative = id(new FundInitiativeQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$initiative) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$initiative_uri = '/'.$initiative->getMonogram();
|
||||
|
||||
$is_close = !$initiative->isClosed();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$type_status = FundInitiativeTransaction::TYPE_STATUS;
|
||||
|
||||
if ($is_close) {
|
||||
$new_status = FundInitiative::STATUS_CLOSED;
|
||||
} else {
|
||||
$new_status = FundInitiative::STATUS_OPEN;
|
||||
}
|
||||
|
||||
$xaction = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_status)
|
||||
->setNewValue($new_status);
|
||||
|
||||
$editor = id(new FundInitiativeEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$editor->applyTransactions($initiative, array($xaction));
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($initiative_uri);
|
||||
}
|
||||
|
||||
if ($is_close) {
|
||||
$title = pht('Close Initiative?');
|
||||
$body = pht('Really close this initiative?');
|
||||
$button_text = pht('Close Initiative');
|
||||
} else {
|
||||
$title = pht('Reopen Initiative?');
|
||||
$body = pht('Really reopen this initiative?');
|
||||
$button_text = pht('Reopen Initiative');
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle($title)
|
||||
->appendParagraph($body)
|
||||
->addCancelButton($initiative_uri)
|
||||
->addSubmitButton($button_text);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeEditController
|
||||
extends FundController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = idx($data, 'id');
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
if ($this->id) {
|
||||
$initiative = id(new FundInitiativeQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$initiative) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$is_new = false;
|
||||
} else {
|
||||
$initiative = FundInitiative::initializeNewInitiative($viewer);
|
||||
$is_new = true;
|
||||
}
|
||||
|
||||
if ($is_new) {
|
||||
$title = pht('Create Initiative');
|
||||
$button_text = pht('Create Initiative');
|
||||
$cancel_uri = $this->getApplicationURI();
|
||||
} else {
|
||||
$title = pht(
|
||||
'Edit %s %s',
|
||||
$initiative->getMonogram(),
|
||||
$initiative->getName());
|
||||
$button_text = pht('Save Changes');
|
||||
$cancel_uri = '/'.$initiative->getMonogram();
|
||||
}
|
||||
|
||||
$e_name = true;
|
||||
$v_name = $initiative->getName();
|
||||
|
||||
$v_desc = $initiative->getDescription();
|
||||
|
||||
if ($is_new) {
|
||||
$v_projects = array();
|
||||
} else {
|
||||
$v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$initiative->getPHID(),
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
$v_projects = array_reverse($v_projects);
|
||||
}
|
||||
|
||||
$validation_exception = null;
|
||||
if ($request->isFormPost()) {
|
||||
$v_name = $request->getStr('name');
|
||||
$v_desc = $request->getStr('description');
|
||||
$v_view = $request->getStr('viewPolicy');
|
||||
$v_edit = $request->getStr('editPolicy');
|
||||
$v_projects = $request->getArr('projects');
|
||||
|
||||
$type_name = FundInitiativeTransaction::TYPE_NAME;
|
||||
$type_desc = FundInitiativeTransaction::TYPE_DESCRIPTION;
|
||||
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_name)
|
||||
->setNewValue($v_name);
|
||||
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_desc)
|
||||
->setNewValue($v_desc);
|
||||
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_view)
|
||||
->setNewValue($v_view);
|
||||
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_edit)
|
||||
->setNewValue($v_edit);
|
||||
|
||||
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $proj_edge_type)
|
||||
->setNewValue(array('=' => array_fuse($v_projects)));
|
||||
|
||||
$editor = id(new FundInitiativeEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
try {
|
||||
$editor->applyTransactions($initiative, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/'.$initiative->getMonogram());
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
|
||||
$e_name = $ex->getShortMessage($type_name);
|
||||
|
||||
$initiative->setViewPolicy($v_view);
|
||||
$initiative->setEditPolicy($v_edit);
|
||||
}
|
||||
}
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($initiative)
|
||||
->execute();
|
||||
|
||||
if ($v_projects) {
|
||||
$project_handles = $this->loadViewerHandles($v_projects);
|
||||
} else {
|
||||
$project_handles = array();
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setName('name')
|
||||
->setLabel(pht('Name'))
|
||||
->setValue($v_name)
|
||||
->setError($e_name))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setName('description')
|
||||
->setLabel(pht('Description'))
|
||||
->setValue($v_desc))
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Projects'))
|
||||
->setName('projects')
|
||||
->setValue($project_handles)
|
||||
->setDatasource(new PhabricatorProjectDatasource()))
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('viewPolicy')
|
||||
->setPolicyObject($initiative)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('editPolicy')
|
||||
->setPolicyObject($initiative)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue($button_text)
|
||||
->addCancelButton($cancel_uri));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
if ($is_new) {
|
||||
$crumbs->addTextCrumb(pht('Create Initiative'));
|
||||
} else {
|
||||
$crumbs->addTextCrumb(
|
||||
$initiative->getMonogram(),
|
||||
'/'.$initiative->getMonogram());
|
||||
$crumbs->addTextCrumb(pht('Edit'));
|
||||
}
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setValidationException($validation_exception)
|
||||
->setHeaderText($title)
|
||||
->appendChild($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeListController
|
||||
extends FundController {
|
||||
|
||||
private $queryKey;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->queryKey = idx($data, 'queryKey');
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$controller = id(new PhabricatorApplicationSearchController($request))
|
||||
->setQueryKey($this->queryKey)
|
||||
->setSearchEngine(new FundInitiativeSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
}
|
||||
|
||||
public function buildSideNavView() {
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
id(new FundInitiativeSearchEngine())
|
||||
->setViewer($user)
|
||||
->addNavigationItems($nav->getMenu());
|
||||
$nav->selectFilter(null);
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
public function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
FundCreateInitiativesCapability::CAPABILITY);
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Initiative'))
|
||||
->setHref($this->getApplicationURI('create/'))
|
||||
->setIcon('fa-plus-square')
|
||||
->setDisabled(!$can_create)
|
||||
->setWorkflow(!$can_create));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeViewController
|
||||
extends FundController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$initiative = id(new FundInitiativeQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->executeOne();
|
||||
if (!$initiative) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($initiative->getMonogram());
|
||||
|
||||
$title = pht(
|
||||
'%s %s',
|
||||
$initiative->getMonogram(),
|
||||
$initiative->getName());
|
||||
|
||||
if ($initiative->isClosed()) {
|
||||
$status_icon = 'fa-times';
|
||||
$status_color = 'bluegrey';
|
||||
} else {
|
||||
$status_icon = 'fa-check';
|
||||
$status_color = 'bluegrey';
|
||||
}
|
||||
$status_name = idx(
|
||||
FundInitiative::getStatusNameMap(),
|
||||
$initiative->getStatus());
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setObjectName($initiative->getMonogram())
|
||||
->setHeader($initiative->getName())
|
||||
->setUser($viewer)
|
||||
->setPolicyObject($initiative)
|
||||
->setStatus($status_icon, $status_color, $status_name);
|
||||
|
||||
$properties = $this->buildPropertyListView($initiative);
|
||||
$actions = $this->buildActionListView($initiative);
|
||||
$properties->setActionList($actions);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($properties);
|
||||
|
||||
$xactions = id(new FundInitiativeTransactionQuery())
|
||||
->setViewer($viewer)
|
||||
->withObjectPHIDs(array($initiative->getPHID()))
|
||||
->execute();
|
||||
|
||||
$timeline = id(new PhabricatorApplicationTransactionView())
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($initiative->getPHID())
|
||||
->setTransactions($xactions);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
$timeline,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
));
|
||||
}
|
||||
|
||||
private function buildPropertyListView(FundInitiative $initiative) {
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setUser($viewer)
|
||||
->setObject($initiative);
|
||||
|
||||
$owner_phid = $initiative->getOwnerPHID();
|
||||
$this->loadHandles(array($owner_phid));
|
||||
|
||||
$view->addProperty(
|
||||
pht('Owner'),
|
||||
$this->getHandle($owner_phid)->renderLink());
|
||||
|
||||
$view->invokeWillRenderEvent();
|
||||
|
||||
$description = $initiative->getDescription();
|
||||
if (strlen($description)) {
|
||||
$description = PhabricatorMarkupEngine::renderOneObject(
|
||||
id(new PhabricatorMarkupOneOff())->setContent($description),
|
||||
'default',
|
||||
$viewer);
|
||||
|
||||
$view->addSectionHeader(pht('Description'));
|
||||
$view->addTextContent($description);
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function buildActionListView(FundInitiative $initiative) {
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
$id = $initiative->getID();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$initiative,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$view = id(new PhabricatorActionListView())
|
||||
->setUser($viewer)
|
||||
->setObject($initiative);
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Initiative'))
|
||||
->setIcon('fa-pencil')
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setHref($this->getApplicationURI("/edit/{$id}/")));
|
||||
|
||||
if ($initiative->isClosed()) {
|
||||
$close_name = pht('Reopen Initiative');
|
||||
$close_icon = 'fa-check';
|
||||
} else {
|
||||
$close_name = pht('Close Initiative');
|
||||
$close_icon = 'fa-times';
|
||||
}
|
||||
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName($close_name)
|
||||
->setIcon($close_icon)
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setHref($this->getApplicationURI("/close/{$id}/")));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
}
|
13
src/applications/fund/editor/FundBackingEditor.php
Normal file
13
src/applications/fund/editor/FundBackingEditor.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
final class FundBackingEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorFundApplication';
|
||||
}
|
||||
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('Fund Backing');
|
||||
}
|
||||
}
|
123
src/applications/fund/editor/FundInitiativeEditor.php
Normal file
123
src/applications/fund/editor/FundInitiativeEditor.php
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorFundApplication';
|
||||
}
|
||||
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('Fund Initiatives');
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = FundInitiativeTransaction::TYPE_NAME;
|
||||
$types[] = FundInitiativeTransaction::TYPE_DESCRIPTION;
|
||||
$types[] = FundInitiativeTransaction::TYPE_STATUS;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
return $object->getName();
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
return $object->getDescription();
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
return $object->getStatus();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
return $xaction->getNewValue();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionNewValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
$object->setName($xaction->getNewValue());
|
||||
return;
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
$object->setDescription($xaction->getNewValue());
|
||||
return;
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
$object->setStatus($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
switch ($type) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
$object->getName(),
|
||||
$xactions);
|
||||
|
||||
if ($missing) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('Initiative name is required.'),
|
||||
nonempty(last($xactions), null));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
|
||||
}
|
41
src/applications/fund/phid/FundBackingPHIDType.php
Normal file
41
src/applications/fund/phid/FundBackingPHIDType.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
final class FundBackingPHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'FBAK';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Variable');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new FundInitiative();
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new FundInitiativeQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$initiative = $objects[$phid];
|
||||
|
||||
$id = $initiative->getID();
|
||||
$monogram = $initiative->getMonogram();
|
||||
$name = $initiative->getName();
|
||||
|
||||
$handle->setName($name);
|
||||
$handle->setFullName("{$monogram} {$name}");
|
||||
$handle->setURI("/fund/view/{$id}/");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
74
src/applications/fund/phid/FundInitiativePHIDType.php
Normal file
74
src/applications/fund/phid/FundInitiativePHIDType.php
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativePHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'FITV';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Initiative');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new FundInitiative();
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new FundInitiativeQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$initiative = $objects[$phid];
|
||||
|
||||
$id = $initiative->getID();
|
||||
$monogram = $initiative->getMonogram();
|
||||
$name = $initiative->getName();
|
||||
|
||||
if ($initiative->isClosed()) {
|
||||
$handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED);
|
||||
}
|
||||
|
||||
$handle->setName($name);
|
||||
$handle->setFullName("{$monogram} {$name}");
|
||||
$handle->setURI("/I{$id}");
|
||||
}
|
||||
}
|
||||
|
||||
public function canLoadNamedObject($name) {
|
||||
return preg_match('/^I\d*[1-9]\d*$/i', $name);
|
||||
}
|
||||
|
||||
public function loadNamedObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $names) {
|
||||
|
||||
$id_map = array();
|
||||
foreach ($names as $name) {
|
||||
$id = (int)substr($name, 1);
|
||||
$id_map[$id][] = $name;
|
||||
}
|
||||
|
||||
$objects = id(new FundInitiativeQuery())
|
||||
->setViewer($query->getViewer())
|
||||
->withIDs(array_keys($id_map))
|
||||
->execute();
|
||||
|
||||
$results = array();
|
||||
foreach ($objects as $id => $object) {
|
||||
foreach (idx($id_map, $id, array()) as $name) {
|
||||
$results[$name] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
60
src/applications/fund/query/FundBackingQuery.php
Normal file
60
src/applications/fund/query/FundBackingQuery.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
final class FundBackingQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new FundBacking();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$rows = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($rows);
|
||||
}
|
||||
|
||||
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorFundApplication';
|
||||
}
|
||||
|
||||
}
|
10
src/applications/fund/query/FundBackingTransactionQuery.php
Normal file
10
src/applications/fund/query/FundBackingTransactionQuery.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class FundBackingTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new FundBackingTransaction();
|
||||
}
|
||||
|
||||
}
|
116
src/applications/fund/query/FundInitiativeQuery.php
Normal file
116
src/applications/fund/query/FundInitiativeQuery.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $ownerPHIDs;
|
||||
private $statuses;
|
||||
|
||||
private $needProjectPHIDs;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withOwnerPHIDs(array $phids) {
|
||||
$this->ownerPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStatuses(array $statuses) {
|
||||
$this->statuses = $statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needProjectPHIDs($need) {
|
||||
$this->needProjectPHIDs = $need;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new FundInitiative();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$rows = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($rows);
|
||||
}
|
||||
|
||||
protected function didFilterPage(array $initiatives) {
|
||||
|
||||
if ($this->needProjectPHIDs) {
|
||||
$edge_query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(mpull($initiatives, 'getPHID'))
|
||||
->withEdgeTypes(
|
||||
array(
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||
));
|
||||
$edge_query->execute();
|
||||
|
||||
foreach ($initiatives as $initiative) {
|
||||
$phids = $edge_query->getDestinationPHIDs(
|
||||
array(
|
||||
$initiative->getPHID(),
|
||||
));
|
||||
$initiative->attachProjectPHIDs($phids);
|
||||
}
|
||||
}
|
||||
|
||||
return $initiatives;
|
||||
}
|
||||
|
||||
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->ownerPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'ownerPHID IN (%Ls)',
|
||||
$this->ownerPHIDs);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'status IN (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorFundApplication';
|
||||
}
|
||||
|
||||
}
|
180
src/applications/fund/query/FundInitiativeSearchEngine.php
Normal file
180
src/applications/fund/query/FundInitiativeSearchEngine.php
Normal file
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
public function getResultTypeDescription() {
|
||||
return pht('Fund Initiatives');
|
||||
}
|
||||
|
||||
public function getApplicationClassName() {
|
||||
return 'PhabricatorFundApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
|
||||
$saved->setParameter(
|
||||
'ownerPHIDs',
|
||||
$this->readUsersFromRequest($request, 'owners'));
|
||||
|
||||
$saved->setParameter(
|
||||
'statuses',
|
||||
$this->readListFromRequest($request, 'statuses'));
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new FundInitiativeQuery())
|
||||
->needProjectPHIDs(true);
|
||||
|
||||
$owner_phids = $saved->getParameter('ownerPHIDs');
|
||||
if ($owner_phids) {
|
||||
$query->withOwnerPHIDs($owner_phids);
|
||||
}
|
||||
|
||||
$statuses = $saved->getParameter('statuses');
|
||||
if ($statuses) {
|
||||
$query->withStatuses($statuses);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved) {
|
||||
|
||||
$statuses = $saved->getParameter('statuses', array());
|
||||
$statuses = array_fuse($statuses);
|
||||
|
||||
$owner_phids = $saved->getParameter('ownerPHIDs', array());
|
||||
|
||||
$all_phids = array_mergev(
|
||||
array(
|
||||
$owner_phids,
|
||||
));
|
||||
|
||||
$handles = id(new PhabricatorHandleQuery())
|
||||
->setViewer($this->requireViewer())
|
||||
->withPHIDs($all_phids)
|
||||
->execute();
|
||||
|
||||
$status_map = FundInitiative::getStatusNameMap();
|
||||
$status_control = id(new AphrontFormCheckboxControl())
|
||||
->setLabel(pht('Statuses'));
|
||||
foreach ($status_map as $status => $name) {
|
||||
$status_control->addCheckbox(
|
||||
'statuses[]',
|
||||
$status,
|
||||
$name,
|
||||
isset($statuses[$status]));
|
||||
}
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Owners'))
|
||||
->setName('owners')
|
||||
->setDatasource(new PhabricatorPeopleDatasource())
|
||||
->setValue(array_select_keys($handles, $owner_phids)))
|
||||
->appendChild($status_control);
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/fund/'.$path;
|
||||
}
|
||||
|
||||
public function getBuiltinQueryNames() {
|
||||
$names = array();
|
||||
|
||||
$names['open'] = pht('Open Initiatives');
|
||||
if ($this->requireViewer()->isLoggedIn()) {
|
||||
$names['owned'] = pht('Owned Initiatives');
|
||||
}
|
||||
$names['all'] = pht('All Initiatives');
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromBuiltin($query_key) {
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
switch ($query_key) {
|
||||
case 'all':
|
||||
return $query;
|
||||
case 'owned':
|
||||
return $query->setParameter(
|
||||
'ownerPHIDs',
|
||||
array(
|
||||
$this->requireViewer()->getPHID(),
|
||||
));
|
||||
case 'open':
|
||||
return $query->setParameter(
|
||||
'statuses',
|
||||
array(
|
||||
FundInitiative::STATUS_OPEN,
|
||||
));
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function getRequiredHandlePHIDsForResultList(
|
||||
array $initiatives,
|
||||
PhabricatorSavedQuery $query) {
|
||||
|
||||
$phids = array();
|
||||
foreach ($initiatives as $initiative) {
|
||||
$phids[] = $initiative->getOwnerPHID();
|
||||
foreach ($initiative->getProjectPHIDs() as $project_phid) {
|
||||
$phids[] = $project_phid;
|
||||
}
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $initiatives,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
assert_instances_of($initiatives, 'FundInitiative');
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$list = id(new PHUIObjectItemListView());
|
||||
foreach ($initiatives as $initiative) {
|
||||
$owner_handle = $handles[$initiative->getOwnerPHID()];
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setObjectName($initiative->getMonogram())
|
||||
->setHeader($initiative->getName())
|
||||
->setHref('/'.$initiative->getMonogram())
|
||||
->addByline(pht('Owner: %s', $owner_handle->renderLink()));
|
||||
|
||||
if ($initiative->isClosed()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
$project_handles = array_select_keys(
|
||||
$handles,
|
||||
$initiative->getProjectPHIDs());
|
||||
if ($project_handles) {
|
||||
$item->addAttribute(
|
||||
id(new PHUIHandleTagListView())
|
||||
->setLimit(4)
|
||||
->setSlim(true)
|
||||
->setHandles($project_handles));
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new FundInitiativeTransaction();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeRemarkupRule extends PhabricatorObjectRemarkupRule {
|
||||
|
||||
protected function getObjectNamePrefix() {
|
||||
return 'I';
|
||||
}
|
||||
|
||||
protected function loadObjects(array $ids) {
|
||||
$viewer = $this->getEngine()->getConfig('viewer');
|
||||
|
||||
return id(new FundInitiativeQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs($ids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
85
src/applications/fund/storage/FundBacking.php
Normal file
85
src/applications/fund/storage/FundBacking.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
final class FundBacking extends FundDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface {
|
||||
|
||||
protected $initiativePHID;
|
||||
protected $backerPHID;
|
||||
protected $purchasePHID;
|
||||
protected $amountInCents;
|
||||
protected $status;
|
||||
protected $properties = array();
|
||||
|
||||
private $initiative = self::ATTACHABLE;
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
return PhabricatorPHID::generateNewPHID(FundBackingPHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
public function getProperty($key, $default = null) {
|
||||
return idx($this->properties, $key, $default);
|
||||
}
|
||||
|
||||
public function setProperty($key, $value) {
|
||||
$this->properties[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
// If we have the initiative, use the initiative's policy.
|
||||
// Otherwise, return NOONE. This allows the backer to continue seeing
|
||||
// a backing even if they're no longer allowed to see the initiative.
|
||||
|
||||
$initiative = $this->getInitiative();
|
||||
if ($initiative) {
|
||||
return $initiative->getPolicy($capability);
|
||||
}
|
||||
return PhabricatorPolicies::POLICY_NOONE;
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return ($viewer->getPHID() == $this->getBackerPHID());
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return pht('A backer can always see what they have backed.');
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new FundBackingEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new FundBackingTransaction();
|
||||
}
|
||||
|
||||
}
|
18
src/applications/fund/storage/FundBackingTransaction.php
Normal file
18
src/applications/fund/storage/FundBackingTransaction.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class FundBackingTransaction
|
||||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'fund';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return FundBackingPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
9
src/applications/fund/storage/FundDAO.php
Normal file
9
src/applications/fund/storage/FundDAO.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
abstract class FundDAO extends PhabricatorLiskDAO {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'fund';
|
||||
}
|
||||
|
||||
}
|
157
src/applications/fund/storage/FundInitiative.php
Normal file
157
src/applications/fund/storage/FundInitiative.php
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiative extends FundDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorSubscribableInterface,
|
||||
PhabricatorMentionableInterface,
|
||||
PhabricatorFlaggableInterface,
|
||||
PhabricatorTokenReceiverInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
|
||||
protected $name;
|
||||
protected $ownerPHID;
|
||||
protected $description;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $status;
|
||||
|
||||
private $projectPHIDs = self::ATTACHABLE;
|
||||
|
||||
const STATUS_OPEN = 'open';
|
||||
const STATUS_CLOSED = 'closed';
|
||||
|
||||
public static function getStatusNameMap() {
|
||||
return array(
|
||||
self::STATUS_OPEN => pht('Open'),
|
||||
self::STATUS_CLOSED => pht('Closed'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function initializeNewInitiative(PhabricatorUser $actor) {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($actor)
|
||||
->withClasses(array('PhabricatorFundApplication'))
|
||||
->executeOne();
|
||||
|
||||
$view_policy = $app->getPolicy(FundDefaultViewCapability::CAPABILITY);
|
||||
|
||||
return id(new FundInitiative())
|
||||
->setOwnerPHID($actor->getPHID())
|
||||
->setViewPolicy($view_policy)
|
||||
->setEditPolicy($actor->getPHID())
|
||||
->setStatus(self::STATUS_OPEN);
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
return PhabricatorPHID::generateNewPHID(FundInitiativePHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return 'I'.$this->getID();
|
||||
}
|
||||
|
||||
public function getProjectPHIDs() {
|
||||
return $this->assertAttached($this->projectPHIDs);
|
||||
}
|
||||
|
||||
public function attachProjectPHIDs(array $phids) {
|
||||
$this->projectPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isClosed() {
|
||||
return ($this->getStatus() == self::STATUS_CLOSED);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return $this->getEditPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new FundInitiativeEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new FundInitiativeTransaction();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function isAutomaticallySubscribed($phid) {
|
||||
return ($phid == $this->getOwnerPHID());
|
||||
}
|
||||
|
||||
public function shouldShowSubscribersProperty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function shouldAllowSubscription($phid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorTokenRecevierInterface )---------------------------------- */
|
||||
|
||||
|
||||
public function getUsersToNotifyOfTokenGiven() {
|
||||
return array(
|
||||
$this->getOwnerPHID(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
public function destroyObjectPermanently(
|
||||
PhabricatorDestructionEngine $engine) {
|
||||
|
||||
$this->openTransaction();
|
||||
$this->delete();
|
||||
$this->saveTransaction();
|
||||
}
|
||||
|
||||
}
|
136
src/applications/fund/storage/FundInitiativeTransaction.php
Normal file
136
src/applications/fund/storage/FundInitiativeTransaction.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
final class FundInitiativeTransaction
|
||||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
const TYPE_NAME = 'fund:name';
|
||||
const TYPE_DESCRIPTION = 'fund:description';
|
||||
const TYPE_STATUS = 'fund:status';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'fund';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return FundInitiativePHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
$object_phid = $this->getObjectPHID();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$type = $this->getTransactionType();
|
||||
switch ($type) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s created this initiative.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s renamed this initiative from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$old,
|
||||
$new);
|
||||
}
|
||||
break;
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
return pht(
|
||||
'%s edited the description of this initiative.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
switch ($new) {
|
||||
case FundInitiative::STATUS_OPEN:
|
||||
return pht(
|
||||
'%s reopened this initiative.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case FundInitiative::STATUS_CLOSED:
|
||||
return pht(
|
||||
'%s closed this initiative.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
}
|
||||
|
||||
public function getTitleForFeed(PhabricatorFeedStory $story) {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
$object_phid = $this->getObjectPHID();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$type = $this->getTransactionType();
|
||||
switch ($type) {
|
||||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s created %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
|
||||
} else {
|
||||
return pht(
|
||||
'%s renamed %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
}
|
||||
break;
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
return pht(
|
||||
'%s updated the description for %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
switch ($new) {
|
||||
case FundInitiative::STATUS_OPEN:
|
||||
return pht(
|
||||
'%s reopened %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
case FundInitiative::STATUS_CLOSED:
|
||||
return pht(
|
||||
'%s closed %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getTitleForFeed($story);
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
$old = $this->getOldValue();
|
||||
switch ($this->getTransactionType()) {
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
return ($old === null);
|
||||
}
|
||||
return parent::shouldHide();
|
||||
}
|
||||
|
||||
public function hasChangeDetails() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
return ($this->getOldValue() !== null);
|
||||
}
|
||||
|
||||
return parent::hasChangeDetails();
|
||||
}
|
||||
|
||||
public function renderChangeDetails(PhabricatorUser $viewer) {
|
||||
return $this->renderTextCorpusChangeDetails(
|
||||
$viewer,
|
||||
$this->getOldValue(),
|
||||
$this->getNewValue());
|
||||
}
|
||||
}
|
|
@ -119,6 +119,7 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'db.phragment' => array(),
|
||||
'db.dashboard' => array(),
|
||||
'db.system' => array(),
|
||||
'db.fund' => array(),
|
||||
'0000.legacy.sql' => array(
|
||||
'legacy' => 0,
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue