Page MenuHomePhabricator

D14152.id34195.diff
No OneTemporary

D14152.id34195.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1423,6 +1423,36 @@
'PHUITypeaheadExample' => 'applications/uiexample/examples/PHUITypeaheadExample.php',
'PHUIWorkboardView' => 'view/phui/PHUIWorkboardView.php',
'PHUIWorkpanelView' => 'view/phui/PHUIWorkpanelView.php',
+ 'PackagesConduitAPIMethod' => 'applications/packages/conduit/PackagesConduitAPIMethod.php',
+ 'PackagesController' => 'applications/packages/controller/PackagesController.php',
+ 'PackagesCreatePackageCapability' => 'applications/packages/capability/PackagesCreatePackageCapability.php',
+ 'PackagesDAO' => 'applications/packages/storage/PackagesDAO.php',
+ 'PackagesDefaultViewCapability' => 'applications/packages/capability/PackagesDefaultViewCapability.php',
+ 'PackagesEditController' => 'applications/packages/controller/PackagesEditController.php',
+ 'PackagesListController' => 'applications/packages/controller/PackagesListController.php',
+ 'PackagesPackage' => 'applications/packages/storage/PackagesPackage.php',
+ 'PackagesPackageDatasource' => 'applications/packages/typeahead/PackagesPackageDatasource.php',
+ 'PackagesPackageEditor' => 'applications/packages/editor/PackagesPackageEditor.php',
+ 'PackagesPackagePHIDType' => 'applications/packages/phid/PackagesPackagePHIDType.php',
+ 'PackagesPackageQuery' => 'applications/packages/query/PackagesPackageQuery.php',
+ 'PackagesPackageSearchEngine' => 'applications/packages/query/PackagesPackageSearchEngine.php',
+ 'PackagesPackageTransaction' => 'applications/packages/storage/PackagesPackageTransaction.php',
+ 'PackagesPackageTransactionQuery' => 'applications/packages/query/PackagesPackageTransactionQuery.php',
+ 'PackagesSignVersionConduitAPIMethod' => 'applications/packages/conduit/PackagesSignVersionConduitAPIMethod.php',
+ 'PackagesSignature' => 'applications/packages/storage/PackagesSignature.php',
+ 'PackagesSignaturePHIDType' => 'applications/packages/phid/PackagesSignaturePHIDType.php',
+ 'PackagesSignatureQuery' => 'applications/packages/query/PackagesSignatureQuery.php',
+ 'PackagesSignatureSearchEngine' => 'applications/packages/query/PackagesSignatureSearchEngine.php',
+ 'PackagesVersion' => 'applications/packages/storage/PackagesVersion.php',
+ 'PackagesVersionEditController' => 'applications/packages/controller/PackagesVersionEditController.php',
+ 'PackagesVersionEditor' => 'applications/packages/editor/PackagesVersionEditor.php',
+ 'PackagesVersionListController' => 'applications/packages/controller/PackagesVersionListController.php',
+ 'PackagesVersionPHIDType' => 'applications/packages/phid/PackagesVersionPHIDType.php',
+ 'PackagesVersionQuery' => 'applications/packages/query/PackagesVersionQuery.php',
+ 'PackagesVersionSearchEngine' => 'applications/packages/query/PackagesVersionSearchEngine.php',
+ 'PackagesVersionTransaction' => 'applications/packages/storage/PackagesVersionTransaction.php',
+ 'PackagesVersionViewController' => 'applications/packages/controller/PackagesVersionViewController.php',
+ 'PackagesViewController' => 'applications/packages/controller/PackagesViewController.php',
'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php',
'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php',
'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php',
@@ -2473,6 +2503,7 @@
'PhabricatorPHPASTApplication' => 'applications/phpast/application/PhabricatorPHPASTApplication.php',
'PhabricatorPHPConfigSetupCheck' => 'applications/config/check/PhabricatorPHPConfigSetupCheck.php',
'PhabricatorPHPMailerConfigOptions' => 'applications/config/option/PhabricatorPHPMailerConfigOptions.php',
+ 'PhabricatorPackagesApplication' => 'applications/packages/application/PhabricatorPackagesApplication.php',
'PhabricatorPagedFormUIExample' => 'applications/uiexample/examples/PhabricatorPagedFormUIExample.php',
'PhabricatorPagerUIExample' => 'applications/uiexample/examples/PhabricatorPagerUIExample.php',
'PhabricatorPassphraseApplication' => 'applications/passphrase/application/PhabricatorPassphraseApplication.php',
@@ -5250,6 +5281,59 @@
'PHUITypeaheadExample' => 'PhabricatorUIExample',
'PHUIWorkboardView' => 'AphrontTagView',
'PHUIWorkpanelView' => 'AphrontTagView',
+ 'PackagesConduitAPIMethod' => 'ConduitAPIMethod',
+ 'PackagesController' => 'PhabricatorController',
+ 'PackagesCreatePackageCapability' => 'PhabricatorPolicyCapability',
+ 'PackagesDAO' => 'PhabricatorLiskDAO',
+ 'PackagesDefaultViewCapability' => 'PhabricatorPolicyCapability',
+ 'PackagesEditController' => 'PackagesController',
+ 'PackagesListController' => 'PackagesController',
+ 'PackagesPackage' => array(
+ 'PackagesDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorProjectInterface',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorSubscribableInterface',
+ 'PhabricatorMentionableInterface',
+ 'PhabricatorFlaggableInterface',
+ 'PhabricatorTokenReceiverInterface',
+ 'PhabricatorDestructibleInterface',
+ ),
+ 'PackagesPackageDatasource' => 'PhabricatorTypeaheadDatasource',
+ 'PackagesPackageEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PackagesPackagePHIDType' => 'PhabricatorPHIDType',
+ 'PackagesPackageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PackagesPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PackagesPackageTransaction' => 'PhabricatorApplicationTransaction',
+ 'PackagesPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PackagesSignVersionConduitAPIMethod' => 'PackagesConduitAPIMethod',
+ 'PackagesSignature' => array(
+ 'PackagesDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorDestructibleInterface',
+ ),
+ 'PackagesSignaturePHIDType' => 'PhabricatorPHIDType',
+ 'PackagesSignatureQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PackagesSignatureSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PackagesVersion' => array(
+ 'PackagesDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorProjectInterface',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorSubscribableInterface',
+ 'PhabricatorMentionableInterface',
+ 'PhabricatorFlaggableInterface',
+ 'PhabricatorDestructibleInterface',
+ ),
+ 'PackagesVersionEditController' => 'PackagesController',
+ 'PackagesVersionEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PackagesVersionListController' => 'PackagesController',
+ 'PackagesVersionPHIDType' => 'PhabricatorPHIDType',
+ 'PackagesVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PackagesVersionSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PackagesVersionTransaction' => 'PhabricatorApplicationTransaction',
+ 'PackagesVersionViewController' => 'PackagesController',
+ 'PackagesViewController' => 'PackagesController',
'PassphraseAbstractKey' => 'Phobject',
'PassphraseConduitAPIMethod' => 'ConduitAPIMethod',
'PassphraseController' => 'PhabricatorController',
@@ -6474,6 +6558,7 @@
'PhabricatorPHPASTApplication' => 'PhabricatorApplication',
'PhabricatorPHPConfigSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorPHPMailerConfigOptions' => 'PhabricatorApplicationConfigOptions',
+ 'PhabricatorPackagesApplication' => 'PhabricatorApplication',
'PhabricatorPagedFormUIExample' => 'PhabricatorUIExample',
'PhabricatorPagerUIExample' => 'PhabricatorUIExample',
'PhabricatorPassphraseApplication' => 'PhabricatorApplication',
diff --git a/src/applications/packages/application/PhabricatorPackagesApplication.php b/src/applications/packages/application/PhabricatorPackagesApplication.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/application/PhabricatorPackagesApplication.php
@@ -0,0 +1,64 @@
+<?php
+
+final class PhabricatorPackagesApplication extends PhabricatorApplication {
+
+ public function getName() {
+ return pht('Packages');
+ }
+
+ public function getShortDescription() {
+ return pht('Package Listing');
+ }
+
+ public function getFlavorText() {
+ return pht('Expand your Arcanist');
+ }
+
+ public function getBaseURI() {
+ return '/packages/';
+ }
+
+ public function getFontIcon() {
+ return 'fa-battery-3';
+ }
+
+ public function isPrototype() {
+ return true;
+ }
+
+ protected function getCustomCapabilities() {
+ return array(
+ PackagesDefaultViewCapability::CAPABILITY => array(
+ 'caption' => pht('Default view policy for newly created packages.'),
+ 'template' => PackagesPackagePHIDType::TYPECONST,
+ ),
+ PackagesCreatePackageCapability::CAPABILITY => array(
+ 'default' => PhabricatorPolicies::POLICY_ADMIN,
+ ),
+ );
+ }
+
+ public function getRoutes() {
+ return array(
+ '/packages/' => array(
+ '(?:query/(?P<queryKey>[^/]+)/)?' => 'PackagesListController',
+ 'package/' => array(
+ '(?P<id>[1-9]\d*)/' => 'PackagesViewController',
+ 'create/' => 'PackagesEditController',
+ 'edit/(?P<id>[1-9]\d*)/' => 'PackagesEditController',
+ ),
+ 'version/' => array(
+ '(?:query/(?P<queryKey>[^/]+)/)?' => 'PackagesVersionListController',
+ '(?P<id>[1-9]\d*)/' => 'PackagesVersionViewController',
+ 'edit/(?P<id>[1-9]\d*)/' => 'PackagesVersionEditController',
+ 'create/' => 'PackagesVersionEditController',
+ ),
+ 'signature/' => array(
+ '(?:query/(?P<queryKey>[^/]+)/)?' => 'PackagesSignatureListControler',
+ '(?P<id>[1-9]\d*)/' => 'PackagesSignatureViewController',
+ ),
+ ),
+ );
+ }
+
+}
diff --git a/src/applications/packages/capability/PackagesCreatePackageCapability.php b/src/applications/packages/capability/PackagesCreatePackageCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/capability/PackagesCreatePackageCapability.php
@@ -0,0 +1,16 @@
+<?php
+
+final class PackagesCreatePackageCapability
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'packages.create';
+
+ public function getCapabilityName() {
+ return pht('Can Create packages');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to create Fund initiatives.');
+ }
+
+}
diff --git a/src/applications/packages/capability/PackagesDefaultViewCapability.php b/src/applications/packages/capability/PackagesDefaultViewCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/capability/PackagesDefaultViewCapability.php
@@ -0,0 +1,15 @@
+<?php
+
+final class PackagesDefaultViewCapability extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'packages.default.view';
+
+ public function getCapabilityName() {
+ return pht('Default View Policy');
+ }
+
+ public function shouldAllowPublicPolicySetting() {
+ return true;
+ }
+
+}
diff --git a/src/applications/packages/conduit/PackagesConduitAPIMethod.php b/src/applications/packages/conduit/PackagesConduitAPIMethod.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/conduit/PackagesConduitAPIMethod.php
@@ -0,0 +1,17 @@
+<?php
+
+abstract class PackagesConduitAPIMethod extends ConduitAPIMethod {
+
+ final public function getApplication() {
+ return PhabricatorApplication::getByClass(
+ 'PhabricatorPackagesApplication');
+ }
+
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_UNSTABLE;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht('All Packages APIs are new and subject to change.');
+ }
+}
diff --git a/src/applications/packages/conduit/PackagesSignVersionConduitAPIMethod.php b/src/applications/packages/conduit/PackagesSignVersionConduitAPIMethod.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/conduit/PackagesSignVersionConduitAPIMethod.php
@@ -0,0 +1,53 @@
+<?php
+
+final class PackagesSignVersionConduitAPIMethod
+ extends PackagesConduitAPIMethod {
+
+ public function getAPIMethodName() {
+ return 'packages.signversion';
+ }
+
+ public function getMethodSummary() {
+ return pht('Vouch for a Package Version.');
+ }
+
+ public function getMethodDescription() {
+ return 'Use this method to sign and vouch for a version of package.';
+ }
+
+ protected function defineParamTypes() {
+ return array(
+ 'versionPHID' => 'phid',
+ 'publicKey' => 'string',
+ 'signature' => 'string',
+ );
+ }
+
+ protected function defineReturnType() {
+ return 'phid';
+ }
+
+ protected function execute(ConduitAPIRequest $request) {
+ $viewer = $request->getUser();
+
+
+ $version = id(new PackagesVersionQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($request->getValue('versionPHID')))
+ ->executeOne();
+ if (!$version) {
+ throw new Exception(pht('No such version'));
+ }
+
+ $signature = PackagesSignature::initializeNewSignature($viewer);
+
+ $signature->setVersionPHID($version->getPHID());
+ $signature->setSignature($request->getValue('publicKey'));
+ $signature->setPublicKey($request->getValue('signature'));
+
+ $signature->save();
+
+ return $signature->getPHID();
+ }
+
+}
diff --git a/src/applications/packages/controller/PackagesController.php b/src/applications/packages/controller/PackagesController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesController.php
@@ -0,0 +1,3 @@
+<?php
+
+abstract class PackagesController extends PhabricatorController {}
diff --git a/src/applications/packages/controller/PackagesEditController.php b/src/applications/packages/controller/PackagesEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesEditController.php
@@ -0,0 +1,193 @@
+<?php
+
+final class PackagesEditController
+ extends PackagesController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ if ($id) {
+ $package = id(new PackagesPackageQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$package) {
+ return new Aphront404Response();
+ }
+ $is_new = false;
+ } else {
+ $package = PackagesPackage::initializeNewPackage($viewer);
+ $is_new = true;
+ }
+
+ if ($is_new) {
+ $title = pht('Create Package');
+ $button_text = pht('Create Package');
+ $cancel_uri = $this->getApplicationURI();
+ } else {
+ $title = pht(
+ 'Edit Package %s',
+ $package->getName());
+ $button_text = pht('Save Changes');
+ $cancel_uri = $package->getURI();
+ }
+
+ $e_name = true;
+ $v_name = $package->getName();
+
+ $v_desc = $package->getDescription();
+
+ $e_uri = true;
+ $v_uri = $package->getDownloadUri();
+
+ if ($is_new) {
+ $v_projects = array();
+ } else {
+ $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $package->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_uri = $request->getStr('uri');
+ $v_view = $request->getStr('viewPolicy');
+ $v_edit = $request->getStr('editPolicy');
+ $v_projects = $request->getArr('projects');
+
+ $type_name = PackagesPackageTransaction::TYPE_NAME;
+ $type_desc = PackagesPackageTransaction::TYPE_DESCRIPTION;
+ $type_uri = PackagesPackageTransaction::TYPE_URI;
+ $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ $xactions = array();
+
+ $xactions[] = id(new PackagesPackageTransaction())
+ ->setTransactionType($type_name)
+ ->setNewValue($v_name);
+
+ $xactions[] = id(new PackagesPackageTransaction())
+ ->setTransactionType($type_desc)
+ ->setNewValue($v_desc);
+
+ $xactions[] = id(new PackagesPackageTransaction())
+ ->setTransactionType($type_uri)
+ ->setNewValue($v_uri);
+
+ $xactions[] = id(new PackagesPackageTransaction())
+ ->setTransactionType($type_view)
+ ->setNewValue($v_view);
+
+ $xactions[] = id(new PackagesPackageTransaction())
+ ->setTransactionType($type_edit)
+ ->setNewValue($v_edit);
+
+ $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
+ $xactions[] = id(new PackagesPackageTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $proj_edge_type)
+ ->setNewValue(array('=' => array_fuse($v_projects)));
+
+ $editor = id(new PackagesPackageEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ try {
+ $editor->applyTransactions($package, $xactions);
+
+ return id(new AphrontRedirectResponse())
+ ->setURI($package->getURI());
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $validation_exception = $ex;
+
+ $e_name = $ex->getShortMessage($type_name);
+ $e_uri = $ex->getShortMessage($type_uri);
+
+ $package->setViewPolicy($v_view);
+ $package->setEditPolicy($v_edit);
+ }
+ }
+
+ $policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($package)
+ ->execute();
+
+ $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())
+ ->setUser($viewer)
+ ->setName('description')
+ ->setLabel(pht('Description'))
+ ->setValue($v_desc))
+ ->appendChild(
+ id(new AphrontFormTextControl()) // TODO validate uri.
+ ->setName('uri')
+ ->setLabel(pht('Download URI'))
+ ->setValue($v_uri)
+ ->setError($e_uri))
+ ->appendControl(
+ id(new AphrontFormTokenizerControl())
+ ->setLabel(pht('Projects'))
+ ->setName('projects')
+ ->setValue($v_projects)
+ ->setDatasource(new PhabricatorProjectDatasource()))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('viewPolicy')
+ ->setPolicyObject($package)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('editPolicy')
+ ->setPolicyObject($package)
+ ->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 package'));
+ } else {
+ $crumbs->addTextCrumb($package->getName());
+ $crumbs->addTextCrumb(pht('Edit'));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setValidationException($validation_exception)
+ ->setHeaderText($title)
+ ->appendChild($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+}
diff --git a/src/applications/packages/controller/PackagesListController.php b/src/applications/packages/controller/PackagesListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesListController.php
@@ -0,0 +1,56 @@
+<?php
+
+final class PackagesListController
+ extends PackagesController {
+
+
+ public function handleRequest(AphrontRequest $request) {
+ $querykey = $request->getURIData('queryKey');
+
+ $controller = id(new PhabricatorApplicationSearchController())
+ ->setQueryKey($querykey)
+ ->setSearchEngine(new PackagesPackageSearchEngine())
+ ->setNavigation($this->buildSideNavView());
+
+ return $this->delegateToController($controller);
+ }
+
+
+ public function buildSideNavView() {
+ $viewer = $this->getViewer();
+
+ $nav = new AphrontSideNavFilterView();
+ $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+
+ id(new PackagesPackageSearchEngine())
+ ->setViewer($viewer)
+ ->addNavigationItems($nav->getMenu());
+
+ $nav->addLabel(pht('Versions'));
+ $nav->addFilter('version/', pht('Manage Versions'));
+
+ $nav->selectFilter(null);
+
+ return $nav;
+ }
+
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $can_create = $this->hasApplicationCapability(
+ PackagesCreatePackageCapability::CAPABILITY);
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Create Package'))
+ ->setHref($this->getApplicationURI('package/create/'))
+ ->setIcon('fa-plus-square')
+ ->setDisabled(!$can_create)
+ ->setWorkflow(!$can_create));
+
+ return $crumbs;
+ }
+
+
+}
diff --git a/src/applications/packages/controller/PackagesVersionEditController.php b/src/applications/packages/controller/PackagesVersionEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesVersionEditController.php
@@ -0,0 +1,241 @@
+<?php
+
+final class PackagesVersionEditController
+ extends PackagesController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ if ($id) {
+ $version = id(new PackagesVersionQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$version) {
+ return new Aphront404Response();
+ }
+ $is_new = false;
+ } else {
+ $version = PackagesVersion::initializeNewVersion($viewer);
+ $is_new = true;
+ }
+
+ if ($is_new) {
+ $title = pht('Create Package Version');
+ $button_text = pht('Create Version');
+ $cancel_uri = $this->getApplicationURI();
+ } else {
+ $title = pht(
+ 'Edit Package Version %s',
+ $version->getTitle());
+ $button_text = pht('Save Changes');
+ $cancel_uri = $version->getURI();
+ }
+
+
+ $e_package = true;
+ $v_package_phid = $version->getPackagePHID();
+
+ $package_id = $request->getInt('package');
+ if ($is_new && $package_id) {
+ $package = id(new PackagesPackageQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($package_id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ $v_package_phid = $package->getPHID();
+ }
+
+
+ $e_title = true;
+ $v_title = $version->getTitle();
+
+ $v_desc = $version->getDescription();
+
+ $e_commit = true;
+ $v_commit = $version->getCommitHash();
+
+ $e_epoch = true;
+ $v_epoch = $version->getReleaseEpoch();
+
+ if ($is_new) {
+ $v_projects = array();
+ } else {
+ $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $version->getPHID(),
+ PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
+ $v_projects = array_reverse($v_projects);
+ }
+
+ $validation_exception = null;
+ if ($request->isFormPost()) {
+ $v_title = $request->getStr('title');
+ $v_desc = $request->getStr('description');
+ $v_package_phid = $request->getStr('package');
+ $v_commit = $request->getStr('commit');
+ $v_epoch = $request->getStr('epoch');
+ $v_view = $request->getStr('viewPolicy');
+ $v_edit = $request->getStr('editPolicy');
+ $v_projects = $request->getArr('projects');
+
+ $type_title = PackagesVersionTransaction::TYPE_TITLE;
+ $type_desc = PackagesVersionTransaction::TYPE_DESCRIPTION;
+ $type_package = PackagesVersionTransaction::TYPE_PACKAGE;
+ $type_commit = PackagesVersionTransaction::TYPE_COMMIT_HASH;
+ $type_epoch = PackagesVersionTransaction::TYPE_RELEASE_EPOCH;
+ $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ $xactions = array();
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_title)
+ ->setNewValue($v_title);
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_desc)
+ ->setNewValue($v_desc);
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_package)
+ ->setNewValue($v_package_phid);
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_commit)
+ ->setNewValue($v_commit);
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_epoch)
+ ->setNewValue($v_epoch);
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_view)
+ ->setNewValue($v_view);
+
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType($type_edit)
+ ->setNewValue($v_edit);
+
+ $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
+ $xactions[] = id(new PackagesVersionTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $proj_edge_type)
+ ->setNewValue(array('=' => array_fuse($v_projects)));
+
+ $editor = id(new PackagesVersionEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ try {
+ $editor->applyTransactions($version, $xactions);
+
+ return id(new AphrontRedirectResponse())
+ ->setURI($version->getURI());
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $validation_exception = $ex;
+
+ $e_title = $ex->getShortMessage($type_title);
+ $e_package = $ex->getShortMessage($type_package);
+ $e_commit = $ex->getShortMessage($type_commit);
+ $e_epoch = $ex->getShortMessage($type_epoch);
+
+ $version->setViewPolicy($v_view);
+ $version->setEditPolicy($v_edit);
+ }
+ }
+
+ $policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($version)
+ ->execute();
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormStaticControl())
+ ->setLabel(pht('Package'))
+ ->setValue($viewer->renderHandle($v_package_phid)))
+ ->addHiddenInput('package', $v_package_phid)
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName('title')
+ ->setLabel(pht('Title'))
+ ->setValue($v_title)
+ ->setError($e_title))
+ ->appendChild(
+ id(new PhabricatorRemarkupControl())
+ ->setUser($viewer)
+ ->setName('description')
+ ->setLabel(pht('Description'))
+ ->setValue($v_desc))
+ ->appendChild(
+ id(new AphrontFormTextControl()) // TODO validate
+ ->setName('commit')
+ ->setLabel(pht('Commit Hash'))
+ ->setValue($v_commit)
+ ->setError($e_commit))
+ ->appendChild(
+ id(new AphrontFormDateControl()) // TODO
+ ->setName('epoch')
+ ->setLabel(pht('Release Time'))
+ ->setUser($viewer)
+ ->setValue($v_epoch)
+ ->setError($e_epoch))
+ ->appendControl(
+ id(new AphrontFormTokenizerControl())
+ ->setLabel(pht('Projects'))
+ ->setName('projects')
+ ->setValue($v_projects)
+ ->setDatasource(new PhabricatorProjectDatasource()))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('viewPolicy')
+ ->setPolicyObject($version)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('editPolicy')
+ ->setPolicyObject($version)
+ ->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 package'));
+ } else {
+ $crumbs->addTextCrumb($version->getTitle());
+ $crumbs->addTextCrumb(pht('Edit'));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setValidationException($validation_exception)
+ ->setHeaderText($title)
+ ->appendChild($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+}
diff --git a/src/applications/packages/controller/PackagesVersionListController.php b/src/applications/packages/controller/PackagesVersionListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesVersionListController.php
@@ -0,0 +1,36 @@
+<?php
+
+final class PackagesVersionListController
+ extends PackagesController {
+
+
+ public function handleRequest(AphrontRequest $request) {
+ $querykey = $request->getURIData('queryKey');
+
+ $controller = id(new PhabricatorApplicationSearchController())
+ ->setQueryKey($querykey)
+ ->setSearchEngine(new PackagesVersionSearchEngine())
+ ->setNavigation($this->buildSideNavView());
+
+ return $this->delegateToController($controller);
+ }
+
+
+ public function buildSideNavView() {
+ $viewer = $this->getViewer();
+
+ $nav = new AphrontSideNavFilterView();
+ $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+
+ id(new PackagesVersionSearchEngine())
+ ->setViewer($viewer)
+ ->addNavigationItems($nav->getMenu());
+
+ $nav->selectFilter(null);
+
+ return $nav;
+ }
+
+
+
+}
diff --git a/src/applications/packages/controller/PackagesVersionViewController.php b/src/applications/packages/controller/PackagesVersionViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesVersionViewController.php
@@ -0,0 +1,150 @@
+<?php
+
+final class PackagesVersionViewController
+ extends PackagesController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ $version = id(new PackagesVersionQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$version) {
+ return 'no';
+ }
+
+ $package = id(new PackagesPackageQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($version->getPackagePHID()))
+ ->executeOne();
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($package->getName(), $package->getURI());
+ $crumbs->addTextCrumb($version->getTitle());
+
+ $title = pht(
+ '%s',
+ $version->getTitle());
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader($version->getTitle())
+ ->setUser($viewer)
+ ->setPolicyObject($version);
+
+ $properties = $this->buildPropertyListView($version);
+ $actions = $this->buildActionListView($version);
+ $properties->setActionList($actions);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+
+ $signatures = $this->buildSignaturesList($version);
+
+ $timeline = $this->buildTransactionTimeline(
+ $version,
+ new FundInitiativeTransactionQuery());
+ $timeline
+ ->setShouldTerminate(true);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ $signatures,
+ $timeline,
+ ),
+ array(
+ 'title' => $title,
+ 'pageObjects' => array($version->getPHID()),
+ ));
+ }
+
+ private function buildPropertyListView(PackagesVersion $version) {
+ $viewer = $this->getRequest()->getUser();
+
+ $view = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($version);
+
+ $view->invokeWillRenderEvent();
+
+
+ $view->addProperty(
+ pht('Package'),
+ $viewer->renderHandle($version->getPackagePHID()));
+
+ $view->addProperty(
+ pht('Commit Hash'),
+ $version->getCommitHash());
+ $view->addProperty(
+ pht('Release Date'),
+ $version->getReleaseEpoch()); // TODO
+
+ $description = $version->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(PackagesVersion $version) {
+ $viewer = $this->getRequest()->getUser();
+ $id = $version->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $version,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $view = id(new PhabricatorActionListView())
+ ->setUser($viewer)
+ ->setObject($version);
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Version'))
+ ->setIcon('fa-pencil')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setHref($this->getApplicationURI("version/edit/{$id}/")));
+
+ return $view;
+ }
+
+ private function buildSignaturesList(PackagesVersion $version) {
+ $viewer = $this->getRequest()->getUser();
+ $signatures = id(new PackagesSignatureQuery())
+ ->setViewer($viewer)
+ ->withVersionPHIDs(array($version->getPHID()))
+ ->execute();
+
+ $list = new PHUIObjectItemListView();
+ $list->setNoDataString(pht('Nobody.'));
+
+ foreach ($signatures as $sig) {
+ $list->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader($viewer->renderHandle($sig->getSignerPHID())));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeaderText('Vouched For By')
+ ->setObjectList($list);
+
+ return $box;
+ }
+}
diff --git a/src/applications/packages/controller/PackagesViewController.php b/src/applications/packages/controller/PackagesViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/controller/PackagesViewController.php
@@ -0,0 +1,138 @@
+<?php
+
+final class PackagesViewController
+ extends PackagesController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ $package = id(new PackagesPackageQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$package) {
+ return new Aphront404Response();
+ }
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($package->getName());
+
+ $title = pht(
+ '%s',
+ $package->getName());
+
+ $status_icon = 'fa-archive';
+ $status_color = 'bluegrey';
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader($package->getName())
+ ->setUser($viewer)
+ ->setPolicyObject($package);
+
+ $properties = $this->buildPropertyListView($package);
+ $actions = $this->buildActionListView($package);
+ $properties->setActionList($actions);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+
+ // TODO list last k versions.
+
+ $timeline = $this->buildTransactionTimeline(
+ $package,
+ new FundInitiativeTransactionQuery());
+ $timeline
+ ->setShouldTerminate(true);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ $timeline,
+ ),
+ array(
+ 'title' => $title,
+ 'pageObjects' => array($package->getPHID()),
+ ));
+ }
+
+ private function buildPropertyListView(PackagesPackage $package) {
+ $viewer = $this->getRequest()->getUser();
+
+ $view = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($package);
+
+ $view->invokeWillRenderEvent();
+
+ $uri = $package->getDownloadUri();
+
+ $uri = PhabricatorMarkupEngine::renderOneObject(
+ id(new PhabricatorMarkupOneOff())->setContent($uri),
+ 'default',
+ $viewer);
+
+ $view->addProperty(
+ pht('URI'),
+ $uri);
+
+ $description = $package->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(PackagesPackage $package) {
+ $viewer = $this->getRequest()->getUser();
+ $id = $package->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $package,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $view = id(new PhabricatorActionListView())
+ ->setUser($viewer)
+ ->setObject($package);
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Package'))
+ ->setIcon('fa-pencil')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setHref($this->getApplicationURI("package/edit/{$id}/")));
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('See Versions'))
+ ->setIcon('fa-search')
+ ->setHref($this->getApplicationURI("version/?packages={$package->getPHID()}")));
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Create a Version'))
+ ->setIcon('fa-scissors')
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setHref($this->getApplicationURI("version/create/?package={$id}")));
+
+ return $view;
+ }
+
+}
diff --git a/src/applications/packages/editor/PackagesPackageEditor.php b/src/applications/packages/editor/PackagesPackageEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/editor/PackagesPackageEditor.php
@@ -0,0 +1,187 @@
+<?php
+
+final class PackagesPackageEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Extension Package');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PackagesPackageTransaction::TYPE_NAME;
+ $types[] = PackagesPackageTransaction::TYPE_DESCRIPTION;
+ $types[] = PackagesPackageTransaction::TYPE_URI;
+ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case PackagesPackageTransaction::TYPE_NAME:
+ return $object->getName();
+ case PackagesPackageTransaction::TYPE_DESCRIPTION:
+ return $object->getDescription();
+ case PackagesPackageTransaction::TYPE_URI:
+ return $object->getDownloadUri();
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PackagesPackageTransaction::TYPE_NAME:
+ case PackagesPackageTransaction::TYPE_DESCRIPTION:
+ case PackagesPackageTransaction::TYPE_URI:
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ $type = $xaction->getTransactionType();
+ switch ($type) {
+ case PackagesPackageTransaction::TYPE_NAME:
+ $object->setName($xaction->getNewValue());
+ return;
+ case PackagesPackageTransaction::TYPE_DESCRIPTION:
+ $object->setDescription($xaction->getNewValue());
+ case PackagesPackageTransaction::TYPE_URI:
+ $object->setDownloadUri($xaction->getNewValue());
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ $type = $xaction->getTransactionType();
+ switch ($type) {
+ case PackagesPackageTransaction::TYPE_NAME:
+ case PackagesPackageTransaction::TYPE_DESCRIPTION:
+ case PackagesPackageTransaction::TYPE_URI:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+ protected function validateTransaction(
+ PhabricatorLiskDAO $object,
+ $type,
+ array $xactions) {
+
+ $errors = parent::validateTransaction($object, $type, $xactions);
+
+ switch ($type) {
+ case PackagesPackageTransaction::TYPE_NAME:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getName(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Package name is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ case PackagesPackageTransaction::TYPE_URI: // TODO
+ $missing = $this->validateIsEmptyTextField(
+ $object->getName(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Download URI is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ }
+
+ return $errors;
+ }
+
+ protected function shouldSendMail(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ public function getMailTagsMap() {
+ return array(
+ PackagesPackageTransaction::MAILTAG_OTHER =>
+ pht('Other Packages activity not listed above occurs.'),
+ );
+ }
+
+ protected function buildMailTemplate(PhabricatorLiskDAO $object) {
+ $id = $object->getID();
+ $name = $object->getName();
+
+ return id(new PhabricatorMetaMTAMail())
+ ->setSubject("Package: {$name}")
+ ->addHeader('Thread-Topic', "package {$id}");
+ }
+
+ protected function buildMailBody(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+
+ $body = parent::buildMailBody($object, $xactions);
+
+ $body->addLinkSection(
+ pht('PACKAGE DETAIL'),
+ PhabricatorEnv::getProductionURI($object->getURI()));
+
+ return $body;
+ }
+
+ protected function getMailTo(PhabricatorLiskDAO $object) {
+ return array();
+ }
+
+ protected function getMailSubjectPrefix() {
+ return 'Packages';
+ }
+
+ protected function shouldPublishFeedStory(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ protected function supportsSearch() {
+ return true;
+ }
+
+}
diff --git a/src/applications/packages/editor/PackagesVersionEditor.php b/src/applications/packages/editor/PackagesVersionEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/editor/PackagesVersionEditor.php
@@ -0,0 +1,207 @@
+<?php
+
+final class PackagesVersionEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Package Version');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PackagesVersionTransaction::TYPE_TITLE;
+ $types[] = PackagesVersionTransaction::TYPE_DESCRIPTION;
+ $types[] = PackagesVersionTransaction::TYPE_PACKAGE;
+ $types[] = PackagesVersionTransaction::TYPE_COMMIT_HASH;
+ $types[] = PackagesVersionTransaction::TYPE_RELEASE_EPOCH;
+ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case PackagesVersionTransaction::TYPE_TITLE:
+ return $object->getTitle();
+ case PackagesVersionTransaction::TYPE_DESCRIPTION:
+ return $object->getDescription();
+ case PackagesVersionTransaction::TYPE_PACKAGE:
+ return $object->getPackagePHID();
+ case PackagesVersionTransaction::TYPE_COMMIT_HASH:
+ return $object->getCommitHash();
+ case PackagesVersionTransaction::TYPE_RELEASE_EPOCH:
+ return $object->getReleaseEpoch();
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PackagesVersionTransaction::TYPE_TITLE:
+ case PackagesVersionTransaction::TYPE_DESCRIPTION:
+ case PackagesVersionTransaction::TYPE_PACKAGE: // TODO?
+ case PackagesVersionTransaction::TYPE_COMMIT_HASH:
+ case PackagesVersionTransaction::TYPE_RELEASE_EPOCH: // TODO?
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ $type = $xaction->getTransactionType();
+ switch ($type) {
+ case PackagesVersionTransaction::TYPE_TITLE:
+ $object->setTitle($xaction->getNewValue());
+ return;
+ case PackagesVersionTransaction::TYPE_DESCRIPTION:
+ $object->setDescription($xaction->getNewValue());
+ return;
+ case PackagesVersionTransaction::TYPE_PACKAGE:
+ $object->setPackagePHID($xaction->getNewValue());
+ return;
+ case PackagesVersionTransaction::TYPE_COMMIT_HASH:
+ $object->setCommitHash($xaction->getNewValue());
+ return;
+ case PackagesVersionTransaction::TYPE_RELEASE_EPOCH:
+ $object->setReleaseEpoch($xaction->getNewValue());
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ $type = $xaction->getTransactionType();
+ switch ($type) {
+ case PackagesVersionTransaction::TYPE_TITLE:
+ case PackagesVersionTransaction::TYPE_DESCRIPTION:
+ case PackagesVersionTransaction::TYPE_PACKAGE:
+ case PackagesVersionTransaction::TYPE_COMMIT_HASH:
+ case PackagesVersionTransaction::TYPE_RELEASE_EPOCH:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+ protected function validateTransaction(
+ PhabricatorLiskDAO $object,
+ $type,
+ array $xactions) {
+
+ $errors = parent::validateTransaction($object, $type, $xactions);
+
+ switch ($type) {
+ case PackagesVersionTransaction::TYPE_TITLE:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getTitle(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Package name is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ case PackagesVersionTransaction::TYPE_PACKAGE: // TODO
+ $missing = $this->validateIsEmptyTextField(
+ $object->getPackagePHID(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('Package is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ case PackagesVersionTransaction::TYPE_COMMIT_HASH: // TODO
+ case PackagesVersionTransaction::TYPE_RELEASE_EPOCH:// TODO
+ break;
+ }
+
+ return $errors;
+ }
+
+ protected function shouldSendMail(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ public function getMailTagsMap() {
+ return array(
+ PackagesVersionTransaction::MAILTAG_OTHER =>
+ pht('Other Packages activity not listed above occurs.'),
+ );
+ }
+
+ protected function buildMailTemplate(PhabricatorLiskDAO $object) {
+ $id = $object->getID();
+ $title = $object->getTitle();
+
+ return id(new PhabricatorMetaMTAMail()) // TODO add pakcage info
+ ->setSubject("Version: {$title}")
+ ->addHeader('Thread-Topic', "version {$id}");
+ }
+
+ protected function buildMailBody(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+
+ $body = parent::buildMailBody($object, $xactions);
+
+ $body->addLinkSection(
+ pht('PACKAGE DETAIL'),
+ PhabricatorEnv::getProductionURI($object->getURI()));
+// TODO add package info
+ return $body;
+ }
+
+ protected function getMailTo(PhabricatorLiskDAO $object) {
+ return array();
+ }
+
+ protected function getMailSubjectPrefix() {
+ return 'Packages';
+ }
+
+ protected function shouldPublishFeedStory(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ protected function supportsSearch() {
+ return true;
+ }
+
+}
diff --git a/src/applications/packages/phid/PackagesPackagePHIDType.php b/src/applications/packages/phid/PackagesPackagePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/phid/PackagesPackagePHIDType.php
@@ -0,0 +1,72 @@
+<?php
+
+final class PackagesPackagePHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'PPKG';
+
+ public function getTypeName() {
+ return pht('Extension Package');
+ }
+
+ public function newObject() {
+ return new PackagesPackage();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PackagesPackageQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $initiative = $objects[$phid];
+
+ $id = $initiative->getID();
+ $name = $initiative->getName();
+
+ $handle->setName($name);
+ $handle->setURI("/packages/package/{$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 PackagesPackageQuery())
+ ->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;
+ }
+
+}
diff --git a/src/applications/packages/phid/PackagesSignaturePHIDType.php b/src/applications/packages/phid/PackagesSignaturePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/phid/PackagesSignaturePHIDType.php
@@ -0,0 +1,53 @@
+<?php
+
+final class PackagesSignaturePHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'PSIG';
+
+ public function getTypeName() {
+ return pht('Package Version Signature');
+ }
+
+ public function newObject() {
+ return new PackagesSignature();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PackagesSignatureQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $certs = $objects[$phid];
+
+ $id = 3;
+
+ $handle->setName('TODO sig');
+ $handle->setURI("/packages/certificate/{$id}");
+ }
+ }
+
+ public function canLoadNamedObject($name) {
+ return false;
+ }
+
+ public function loadNamedObjects(
+ PhabricatorObjectQuery $query,
+ array $names) {
+
+ return array();
+ }
+
+}
diff --git a/src/applications/packages/phid/PackagesVersionPHIDType.php b/src/applications/packages/phid/PackagesVersionPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/phid/PackagesVersionPHIDType.php
@@ -0,0 +1,68 @@
+<?php
+
+final class PackagesVersionPHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'PVER';
+
+ public function getTypeName() {
+ return pht('Extension Package Version');
+ }
+
+ public function newObject() {
+ return new PackagesVersion();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PackagesPackageQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $initiative = $objects[$phid];
+
+ $id = $initiative->getID();
+ $name = $initiative->getName();
+
+ $handle->setName($name);
+ $handle->setURI("/packages/package/{$id}");
+ }
+ }
+
+ 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 PackagesPackageQuery())
+ ->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;
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesPackageQuery.php b/src/applications/packages/query/PackagesPackageQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesPackageQuery.php
@@ -0,0 +1,92 @@
+<?php
+
+final class PackagesPackageQuery
+ 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 needProjectPHIDs($need) {
+ $this->needProjectPHIDs = $need;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new PackagesPackage();
+ $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;
+ }
+
+ protected 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 'PhabricatorPackagesApplication';
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesPackageSearchEngine.php b/src/applications/packages/query/PackagesPackageSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesPackageSearchEngine.php
@@ -0,0 +1,108 @@
+<?php
+
+final class PackagesPackageSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('Packages');
+ }
+
+ public function getApplicationClassName() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ 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 PackagesPackageQuery())
+ ->needProjectPHIDs(true);
+
+ return $query;
+ }
+
+ public function buildSearchForm(
+ AphrontFormView $form,
+ PhabricatorSavedQuery $saved) {}
+
+ protected function getURI($path) {
+ return '/packages/'.$path;
+ }
+
+ protected function getBuiltinQueryNames() {
+ $names = array();
+
+ $names['all'] = pht('All Packages');
+
+ return $names;
+ }
+
+ public function buildSavedQueryFromBuiltin($query_key) {
+ $query = $this->newSavedQuery();
+ $query->setQueryKey($query_key);
+
+ switch ($query_key) {
+ case 'all':
+ return $query;
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function getRequiredHandlePHIDsForResultList(
+ array $packages,
+ PhabricatorSavedQuery $query) {
+
+ $phids = array();
+
+ return $phids;
+ }
+
+ protected function renderResultList(
+ array $packages,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+ assert_instances_of($packages, 'PackagesPackage');
+
+ $viewer = $this->requireViewer();
+
+ $list = id(new PHUIObjectItemListView());
+ foreach ($packages as $package) {
+ $item = id(new PHUIObjectItemView())
+ ->setObjectName($package->getName())
+ ->setHeader($package->getName())
+ ->setHref('/packages/package/'.$package->getID());
+
+ $project_handles = array_select_keys(
+ $handles,
+ $package->getProjectPHIDs());
+ if ($project_handles) {
+ $item->addAttribute(
+ id(new PHUIHandleTagListView())
+ ->setLimit(4)
+ ->setSlim(true)
+ ->setHandles($project_handles));
+ }
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No packages found.'));
+
+ return $result;
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesPackageTransactionQuery.php b/src/applications/packages/query/PackagesPackageTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesPackageTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PackagesPackageTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PackagesPackageTransaction();
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesSignatureQuery.php b/src/applications/packages/query/PackagesSignatureQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesSignatureQuery.php
@@ -0,0 +1,114 @@
+<?php
+
+final class PackagesSignatureQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $signerPHIDs;
+ private $versionPHIDs;
+ private $packagePHIDs;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withSignerPHIDs(array $phids) {
+ $this->signerPHIDs = $phids;
+ return $this;
+ }
+
+ public function withVersionPHIDs(array $phids) {
+ $this->versionPHIDs = $phids;
+ return $this;
+ }
+
+ public function withPackagePHIDs(array $phids) {
+ $this->packagePHIDs = $phids;
+ return $this;
+ }
+
+
+ protected function loadPage() {
+ $table = new PackagesSignature();
+ $conn_r = $table->establishConnection('r');
+
+ $rows = queryfx_all(
+ $conn_r,
+ 'SELECT * FROM %T packages_signature %Q %Q %Q %Q',
+ $table->getTableName(),
+ $this->buildJoinClause($conn_r),
+ $this->buildWhereClause($conn_r),
+ $this->buildOrderClause($conn_r),
+ $this->buildLimitClause($conn_r));
+
+ return $table->loadAllFromArray($rows);
+ }
+
+ protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
+ $joins = array();
+
+ if ($this->packagePHIDs !== null) {
+ $joins[] = qsprintf(
+ $conn_r,
+ 'JOIN %T v ON v.phid = packages_signature.versionPHID',
+ id(new PackagesVersion())->getTableName());
+ }
+
+ return implode(' ', $joins);
+ }
+
+ protected 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->signerPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'signerPHID in (%Ls)',
+ $this->signerPHIDs);
+ }
+
+ if ($this->versionPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'versionPHID in (%Ls)',
+ $this->versionPHIDs);
+ }
+
+ if ($this->packagePHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'v.packagePHID in (%Ls)',
+ $this->packagePHIDs);
+ }
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesSignatureSearchEngine.php b/src/applications/packages/query/PackagesSignatureSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesSignatureSearchEngine.php
@@ -0,0 +1,99 @@
+<?php
+
+final class PackagesSignatureSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('Signatures');
+ }
+
+ public function getApplicationClassName() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ public function buildSavedQueryFromRequest(AphrontRequest $request) {
+ $saved = new PhabricatorSavedQuery();
+// signer
+// packages/
+// version
+ $saved->setParameter(
+ 'signerPHIDs',
+ $this->readUsersFromRequest($request, 'signerPHIDs'));
+
+ return $saved;
+ }
+
+ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
+ $query = id(new PackagesSignatureQuery());
+
+ return $query;
+ }
+
+ public function buildSearchForm(
+ AphrontFormView $form,
+ PhabricatorSavedQuery $saved) {
+ // TODO
+ }
+
+ protected function getURI($path) {
+ return '/packages/'.$path;
+ }
+
+ protected function getBuiltinQueryNames() {
+ $names = array();
+
+ $names['all'] = pht('All Signatures');
+
+ return $names;
+ }
+
+ public function buildSavedQueryFromBuiltin($query_key) {
+ $query = $this->newSavedQuery();
+ $query->setQueryKey($query_key);
+
+ switch ($query_key) {
+ case 'all':
+ return $query;
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function getRequiredHandlePHIDsForResultList(
+ array $signatures,
+ PhabricatorSavedQuery $query) {
+ assert_instances_of($signatures, 'PackagesSignature');
+ $phids = array();
+ foreach ($signatures as $sig) {
+ $phids[] = $sig->getSignerPHID();
+ }
+
+ return $phids;
+ }
+
+ protected function renderResultList(
+ array $signatures,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+ assert_instances_of($signatures, 'PackagesSignature');
+
+ $viewer = $this->requireViewer();
+
+ $list = id(new PHUIObjectItemListView());
+ foreach ($signatures as $sig) {
+ $item = id(new PHUIObjectItemView());
+
+ $signer = $handles[$sig->getSignerPHID()];
+ $item->addByline(pht('Signed by: %s', $signer->renderLink()));
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No signatures found.'));
+
+ return $result;
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesVersionQuery.php b/src/applications/packages/query/PackagesVersionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesVersionQuery.php
@@ -0,0 +1,103 @@
+<?php
+
+final class PackagesVersionQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $packagePHIDs;
+
+ private $needProjectPHIDs;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withPackagePHIDs(array $phids) {
+ $this->packagePHIDs = $phids;
+ return $this;
+ }
+
+ public function needProjectPHIDs($need) {
+ $this->needProjectPHIDs = $need;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new PackagesVersion();
+ $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;
+ }
+
+ protected 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->packagePHIDs !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'packagePHID in (%Ls)',
+ $this->packagePHIDs);
+ }
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+}
diff --git a/src/applications/packages/query/PackagesVersionSearchEngine.php b/src/applications/packages/query/PackagesVersionSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/query/PackagesVersionSearchEngine.php
@@ -0,0 +1,125 @@
+<?php
+
+final class PackagesVersionSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('version Versions');
+ }
+
+ public function getApplicationClassName() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ public function buildSavedQueryFromRequest(AphrontRequest $request) {
+ $saved = new PhabricatorSavedQuery();
+
+ $saved->setParameter(
+ 'packagePHIDs',
+ $this->readPHIDsFromRequest($request, 'packages'));
+
+ return $saved;
+ }
+
+ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
+ $query = id(new PackagesVersionQuery())
+ ->needProjectPHIDs(true);
+
+ $package_phids = $saved->getParameter('packagePHIDs');
+ if ($package_phids) {
+ $query->withPackagePHIDs($package_phids);
+ }
+
+ return $query;
+ }
+
+ public function buildSearchForm(
+ AphrontFormView $form,
+ PhabricatorSavedQuery $saved) {
+
+ $packages = $saved->getParameter('packagePHIDs', array());
+ $form
+ ->appendControl(
+ id(new AphrontFormTokenizerControl())
+ ->setLabel(pht('Packages'))
+ ->setName('packages')
+ ->setDatasource(new PackagesPackageDatasource())
+ ->setValue($packages));
+
+ }
+
+ protected function getURI($path) {
+ return '/packages/version/'.$path;
+ }
+
+ protected function getBuiltinQueryNames() {
+ $names = array();
+
+ $names['all'] = pht('All Package Versions');
+
+ return $names;
+ }
+
+ public function buildSavedQueryFromBuiltin($query_key) {
+ $query = $this->newSavedQuery();
+ $query->setQueryKey($query_key);
+
+ switch ($query_key) {
+ case 'all':
+ return $query;
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function getRequiredHandlePHIDsForResultList(
+ array $objects,
+ PhabricatorSavedQuery $query) {
+ assert_instances_of($objects, 'PackagesVersion');
+
+ $phids = array();
+ foreach ($objects as $version) {
+ $phids[] = $version->getPackagePHID();
+ }
+
+ return $phids;
+ }
+
+ protected function renderResultList(
+ array $objects,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+ assert_instances_of($objects, 'PackagesVersion');
+
+ $viewer = $this->requireViewer();
+
+ $list = id(new PHUIObjectItemListView());
+ foreach ($objects as $version) {
+ $package = $handles[$version->getPackagePHID()];
+ $item = id(new PHUIObjectItemView())
+ ->setObjectName($package->getName())
+ ->setHeader($version->getTitle())
+ ->setHref($version->getURI());
+
+ $project_handles = array_select_keys(
+ $handles,
+ $version->getProjectPHIDs());
+ if ($project_handles) {
+ $item->addAttribute(
+ id(new PHUIHandleTagListView())
+ ->setLimit(4)
+ ->setSlim(true)
+ ->setHandles($project_handles));
+ }
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No Versions found.'));
+
+ return $result;
+ }
+
+}
diff --git a/src/applications/packages/storage/PackagesDAO.php b/src/applications/packages/storage/PackagesDAO.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/storage/PackagesDAO.php
@@ -0,0 +1,9 @@
+<?php
+
+abstract class PackagesDAO extends PhabricatorLiskDAO {
+
+ public function getApplicationName() {
+ return 'packages';
+ }
+
+}
diff --git a/src/applications/packages/storage/PackagesPackage.php b/src/applications/packages/storage/PackagesPackage.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/storage/PackagesPackage.php
@@ -0,0 +1,162 @@
+<?php
+
+final class PackagesPackage extends PackagesDAO
+ implements
+ PhabricatorPolicyInterface,
+ PhabricatorProjectInterface,
+ PhabricatorApplicationTransactionInterface,
+ PhabricatorSubscribableInterface,
+ PhabricatorMentionableInterface,
+ PhabricatorFlaggableInterface,
+ PhabricatorTokenReceiverInterface,
+ PhabricatorDestructibleInterface {
+
+ protected $name;
+ protected $description;
+ protected $downloadUri;
+ protected $viewPolicy;
+ protected $editPolicy;
+ protected $mailKey;
+
+ private $projectPHIDs = self::ATTACHABLE;
+
+
+ public static function initializeNewPackage(PhabricatorUser $actor) {
+ $app = id(new PhabricatorApplicationQuery())
+ ->setViewer($actor)
+ ->withClasses(array('PhabricatorPackagesApplication'))
+ ->executeOne();
+
+ $view_policy = $app->getPolicy(PackagesDefaultViewCapability::CAPABILITY);
+
+ return id(new PackagesPackage())
+ ->setViewPolicy($view_policy)
+ ->setEditPolicy($actor->getPHID());
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text255',
+ 'description' => 'text',
+ 'downloadUri' => 'text255',
+ 'mailKey' => 'bytes20',
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(PackagesPackagePHIDType::TYPECONST);
+ }
+
+ public function getProjectPHIDs() {
+ return $this->assertAttached($this->projectPHIDs);
+ }
+
+ public function attachProjectPHIDs(array $phids) {
+ $this->projectPHIDs = $phids;
+ return $this;
+ }
+
+ public function getURI() {
+ return '/packages/package/'.$this->getID();
+ }
+
+ public function save() {
+ if (!$this->mailKey) {
+ $this->mailKey = Filesystem::readRandomCharacters(20);
+ }
+ return parent::save();
+ }
+
+
+/* -( 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 PackagesPackageEditor();
+ }
+
+ public function getApplicationTransactionObject() {
+ return $this;
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PackagesPackageTransaction();
+ }
+
+ public function willRenderTimeline(
+ PhabricatorApplicationTransactionView $timeline,
+ AphrontRequest $request) {
+
+ return $timeline;
+ }
+
+
+/* -( PhabricatorSubscribableInterface )----------------------------------- */
+
+
+ public function isAutomaticallySubscribed($phid) {
+ return false;
+ }
+
+ 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();
+ // TODO: delete all signatures
+ $this->saveTransaction();
+ }
+
+}
diff --git a/src/applications/packages/storage/PackagesPackageTransaction.php b/src/applications/packages/storage/PackagesPackageTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/storage/PackagesPackageTransaction.php
@@ -0,0 +1,143 @@
+<?php
+
+final class PackagesPackageTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'packages:name';
+ const TYPE_DESCRIPTION = 'packages:description';
+ const TYPE_URI = 'packages:uri';
+
+ const MAILTAG_OTHER = 'packages.other';
+
+ public function getApplicationName() {
+ return 'packages';
+ }
+
+ public function getApplicationTransactionType() {
+ return PackagesPackagePHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return null;
+ }
+
+ public function getRequiredHandlePHIDs() {
+ $phids = parent::getRequiredHandlePHIDs();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+ switch ($type) {}
+
+ return $phids;
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+
+ return pht('%s made a change', $this->renderHandleLink($author_phid));
+
+ // TODO
+ switch ($type) {
+ case self::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 self::TYPE_DESCRIPTION:
+ // TODO
+ return pht(
+ '%s edited the risks for this initiative.',
+ $this->renderHandleLink($author_phid));
+ }
+
+ return parent::getTitle();
+ }
+
+ public function getTitleForFeed() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+
+ return pht('%s changed', $this->renderHandleLink($author_phid));
+
+// TODO
+ $type = $this->getTransactionType();
+ switch ($type) {
+ case self::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;
+
+ }
+
+ return parent::getTitleForFeed();
+ }
+
+ public function getMailTags() {
+ $tags = parent::getMailTags();
+
+ switch ($this->getTransactionType()) {
+
+ default:
+ $tags[] = self::MAILTAG_OTHER;
+ break;
+ }
+
+ return $tags;
+ }
+
+
+ public function shouldHide() {
+ $old = $this->getOldValue();
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($old === null);
+ }
+ return parent::shouldHide();
+ }
+
+ public function hasChangeDetails() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($this->getOldValue() !== null);
+ }
+
+ return parent::hasChangeDetails();
+ }
+
+ public function renderChangeDetails(PhabricatorUser $viewer) {
+ return $this->renderTextCorpusChangeDetails(
+ $viewer,
+ $this->getOldValue(),
+ $this->getNewValue());
+ }
+}
diff --git a/src/applications/packages/storage/PackagesSignature.php b/src/applications/packages/storage/PackagesSignature.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/storage/PackagesSignature.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * This is an auxilary DAO. It's only viewable in the context of a version.
+ */
+final class PackagesSignature extends PackagesDAO
+ implements
+ PhabricatorPolicyInterface,
+ PhabricatorDestructibleInterface {
+
+ protected $signerPHID;
+ protected $versionPHID;
+ protected $signature;
+ protected $publicKey;
+
+ protected $editPolicy;
+ protected $viewPolicy;
+ // TODO add pubkey fingerprint, for search?
+
+ public static function initializeNewSignature(PhabricatorUser $actor) {
+ $app = id(new PhabricatorApplicationQuery())
+ ->setViewer($actor)
+ ->withClasses(array('PhabricatorPackagesApplication'))
+ ->executeOne();
+
+ $view_policy = $app->getPolicy(PackagesDefaultViewCapability::CAPABILITY);
+
+ return id(new PackagesSignature())
+ ->setSignerPHID($actor->getPHID());
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'publicKey' => 'text',
+ 'signature' => 'text',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_signer' => array(
+ 'columns' => array('signerPHID'),
+ ),
+ 'key_version' => array(
+ 'columns' => array('versionPHID'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ PackagesSignaturePHIDType::TYPECONST);
+ }
+
+/* -( 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;
+ }
+
+/* -( PhabricatorDestructibleInterface )----------------------------------- */
+
+
+ public function destroyObjectPermanently(
+ PhabricatorDestructionEngine $engine) {
+
+ $this->openTransaction();
+ $this->delete();
+ $this->saveTransaction();
+ }
+
+}
diff --git a/src/applications/packages/storage/PackagesVersion.php b/src/applications/packages/storage/PackagesVersion.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/storage/PackagesVersion.php
@@ -0,0 +1,149 @@
+<?php
+
+final class PackagesVersion extends PackagesDAO
+ implements
+ PhabricatorPolicyInterface,
+ PhabricatorProjectInterface,
+ PhabricatorApplicationTransactionInterface,
+ PhabricatorSubscribableInterface,
+ PhabricatorMentionableInterface,
+ PhabricatorFlaggableInterface,
+ PhabricatorDestructibleInterface {
+
+ protected $title;
+ protected $packagePHID;
+ protected $commitHash;
+ protected $releaseEpoch;
+ protected $description;
+ protected $editPolicy;
+ protected $viewPolicy;
+
+ private $projectPHIDs = self::ATTACHABLE;
+
+
+ public static function initializeNewVersion(PhabricatorUser $actor) {
+ $app = id(new PhabricatorApplicationQuery())
+ ->setViewer($actor)
+ ->withClasses(array('PhabricatorPackagesApplication'))
+ ->executeOne();
+
+ $view_policy = $app->getPolicy(PackagesDefaultViewCapability::CAPABILITY);
+
+ return id(new PackagesVersion())
+ ->setViewPolicy($view_policy)
+ ->setEditPolicy($actor->getPHID());
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'title' => 'text255',
+ 'description' => 'text',
+ 'commitHash' => 'text40',
+ 'releaseEpoch' => 'epoch',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_package' => array(
+ 'columns' => array('packagePHID'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(PackagesPackagePHIDType::TYPECONST);
+ }
+
+ public function getProjectPHIDs() {
+ return $this->assertAttached($this->projectPHIDs);
+ }
+
+ public function attachProjectPHIDs(array $phids) {
+ $this->projectPHIDs = $phids;
+ return $this;
+ }
+
+ public function getURI() {
+ return '/packages/version/'.$this->getID();
+ }
+
+
+/* -( 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 PackagesVersionEditor();
+ }
+
+ public function getApplicationTransactionObject() {
+ return $this;
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PackagesVersionTransaction();
+ }
+
+ public function willRenderTimeline(
+ PhabricatorApplicationTransactionView $timeline,
+ AphrontRequest $request) {
+
+ return $timeline;
+ }
+
+
+/* -( PhabricatorSubscribableInterface )----------------------------------- */
+
+
+ public function isAutomaticallySubscribed($phid) {
+ return false;
+ }
+
+ public function shouldShowSubscribersProperty() {
+ return true;
+ }
+
+ public function shouldAllowSubscription($phid) {
+ return true;
+ }
+
+/* -( PhabricatorDestructibleInterface )----------------------------------- */
+
+
+ public function destroyObjectPermanently(
+ PhabricatorDestructionEngine $engine) {
+
+ $this->openTransaction();
+ $this->delete();
+ // TODO: delete all signatures
+ $this->saveTransaction();
+ }
+
+}
diff --git a/src/applications/packages/storage/PackagesVersionTransaction.php b/src/applications/packages/storage/PackagesVersionTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/storage/PackagesVersionTransaction.php
@@ -0,0 +1,145 @@
+<?php
+
+final class PackagesVersionTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_TITLE = 'packages:title';
+ const TYPE_DESCRIPTION = 'packages:description';
+ const TYPE_PACKAGE = 'packages:package';
+ const TYPE_COMMIT_HASH = 'packages:commithash';
+ const TYPE_RELEASE_EPOCH = 'packages:releaseepoch';
+
+ const MAILTAG_OTHER = 'packages.other';
+
+ public function getApplicationName() {
+ return 'packages';
+ }
+
+ public function getApplicationTransactionType() {
+ return PackagesVersionPHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return null;
+ }
+
+ public function getRequiredHandlePHIDs() {
+ $phids = parent::getRequiredHandlePHIDs();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+ switch ($type) {}
+
+ return $phids;
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+
+ return pht('%s made a change', $this->renderHandleLink($author_phid));
+
+ // TODO
+ switch ($type) {
+ case self::TYPE_Title:
+ if ($old === null) {
+ return pht(
+ '%s created this Version.',
+ $this->renderHandleLink($author_phid));
+ } else {
+ return pht(
+ '%s renamed this version from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
+ }
+ break;
+ case self::TYPE_DESCRIPTION:
+ // TODO
+ return pht(
+ '%s edited the description for this Version.',
+ $this->renderHandleLink($author_phid));
+ }
+
+ return parent::getTitle();
+ }
+
+ public function getTitleForFeed() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+
+ return pht('%s changed', $this->renderHandleLink($author_phid));
+
+// TODO
+ $type = $this->getTransactionType();
+ switch ($type) {
+ case self::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;
+
+ }
+
+ return parent::getTitleForFeed();
+ }
+
+ public function getMailTags() {
+ $tags = parent::getMailTags();
+
+ switch ($this->getTransactionType()) {
+
+ default:
+ $tags[] = self::MAILTAG_OTHER;
+ break;
+ }
+
+ return $tags;
+ }
+
+
+ public function shouldHide() {
+ $old = $this->getOldValue();
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($old === null);
+ }
+ return parent::shouldHide();
+ }
+
+ public function hasChangeDetails() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($this->getOldValue() !== null);
+ }
+
+ return parent::hasChangeDetails();
+ }
+
+ public function renderChangeDetails(PhabricatorUser $viewer) {
+ return $this->renderTextCorpusChangeDetails(
+ $viewer,
+ $this->getOldValue(),
+ $this->getNewValue());
+ }
+}
diff --git a/src/applications/packages/typeahead/PackagesPackageDatasource.php b/src/applications/packages/typeahead/PackagesPackageDatasource.php
new file mode 100644
--- /dev/null
+++ b/src/applications/packages/typeahead/PackagesPackageDatasource.php
@@ -0,0 +1,38 @@
+<?php
+
+final class PackagesPackageDatasource
+ extends PhabricatorTypeaheadDatasource {
+
+ public function getBrowseTitle() {
+ return pht('Browse Packages');
+ }
+
+ public function getPlaceholderText() {
+ return pht('Type a package name...');
+ }
+
+ public function getDatasourceApplicationClass() {
+ return 'PhabricatorPackagesApplication';
+ }
+
+ public function loadResults() {
+ $viewer = $this->getViewer();
+ $raw_query = $this->getRawQuery();
+
+ $results = array();
+
+ // TODO: Make this use real typeahead logic.
+ $query = new PackagesPackageQuery();
+ $packages = $this->executeQuery($query);
+
+ foreach ($packages as $package) {
+ $results[] = id(new PhabricatorTypeaheadResult())
+ ->setName($package->getName())
+ ->setURI($package->getURI())
+ ->setPHID($package->getPHID());
+ }
+
+ return $this->filterResultsAgainstTokens($results);
+ }
+
+}
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -108,6 +108,7 @@
'db.spaces' => array(),
'db.phurl' => array(),
'db.badges' => array(),
+ 'db.packages' => array(),
'0000.legacy.sql' => array(
'legacy' => 0,
),

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 26, 1:02 AM (1 w, 4 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/cj/4n/rwxm3vlwrkmecqog
Default Alt Text
D14152.id34195.diff (92 KB)

Event Timeline