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 @@ -1450,6 +1450,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', @@ -2503,6 +2533,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', @@ -5313,6 +5344,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', @@ -6540,6 +6624,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 @@ + 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[^/]+)/)?' => 'PackagesListController', + 'package/' => array( + '(?P[1-9]\d*)/' => 'PackagesViewController', + 'create/' => 'PackagesEditController', + 'edit/(?P[1-9]\d*)/' => 'PackagesEditController', + ), + 'version/' => array( + '(?:query/(?P[^/]+)/)?' => 'PackagesVersionListController', + '(?P[1-9]\d*)/' => 'PackagesVersionViewController', + 'edit/(?P[1-9]\d*)/' => 'PackagesVersionEditController', + 'create/' => 'PackagesVersionEditController', + ), + 'signature/' => array( + '(?:query/(?P[^/]+)/)?' => 'PackagesSignatureListControler', + '(?P[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 @@ + 'phid', + 'publicKey' => 'string', + 'details' => 'dict', + 'description' => 'optional string', + ); + } + + protected function defineReturnType() { + return 'phid'; + } + + protected function execute(ConduitAPIRequest $request) { + $viewer = $request->getUser(); + + // TODO some kind of validation on the details. + + $details = $request->getValue('details'); + if (!is_array($details)) { + throw new Exception(pht('details field must be a dictionary')); + } + + $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->setDetails($details); + $signature->setPublicKey($request->getValue('publicKey')); + $signature->setDescription($request->getValue('description', '')); + + $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 @@ +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(); + + + 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_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_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_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); + + $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)) + ->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 @@ +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,204 @@ +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->getProperty('description'); + + 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_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_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_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)) + ->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 @@ +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,151 @@ +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($package->getName().' '.$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->getProperty('version:commithash')); + $view->addProperty( + pht('Release Date'), + $version->getProperty('version:releasedate')); + + $description = $version->getProperty('version:description'); + 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) { + $item = id(new PHUIObjectItemView()) + ->setHeader($viewer->renderHandle($sig->getSignerPHID())) + ->addAttribute($sig->getDescription()); + $list->addItem($item); + } + + $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,165 @@ +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); + + $versions = $this->buildVersionsList($package); + + $timeline = $this->buildTransactionTimeline( + $package, + new FundInitiativeTransactionQuery()); + $timeline + ->setShouldTerminate(true); + + return $this->buildApplicationPage( + array( + $crumbs, + $box, + $versions, + $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(); + + + $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; + } + + private function buildVersionsList(PackagesPackage $package) { + $viewer = $this->getRequest()->getUser(); + $versions = id(new PackagesVersionQuery()) + ->setViewer($viewer) + ->withPackagePHIDs(array($package->getPHID())) + ->setOrder('newest') + ->setLimit(6) + ->execute(); + + $list = new PHUIObjectItemListView(); + $list->setNoDataString(pht('This package has no versions.')); + + foreach ($versions as $version) { + $item = id(new PHUIObjectItemView()) + ->setHeader($version->getTitle()) + ->setHref($version->getURI()); + $description = $version->getProperty('version:description'); + if (strlen($description)) { + $description = PhabricatorMarkupEngine::renderOneObject( + id(new PhabricatorMarkupOneOff())->setContent($description), + 'default', + $viewer); + $item->addAttribute($description); + } + + $list->addItem($item); + } + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText('Latest Versions') + ->setObjectList($list); + + return $box; + } + +} 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,164 @@ +getTransactionType()) { + case PackagesPackageTransaction::TYPE_NAME: + return $object->getName(); + case PackagesPackageTransaction::TYPE_DESCRIPTION: + return $object->getDescription(); + } + + return parent::getCustomTransactionOldValue($object, $xaction); + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PackagesPackageTransaction::TYPE_NAME: + case PackagesPackageTransaction::TYPE_DESCRIPTION: + 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()); + 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: + 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; + } + + 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,166 @@ +getTransactionType()) { + case PackagesVersionTransaction::TYPE_TITLE: + return $object->getTitle(); + case PackagesVersionTransaction::TYPE_DESCRIPTION: + return $object->getProperty('description', ''); + case PackagesVersionTransaction::TYPE_PACKAGE: + return $object->getPackagePHID(); + } + + return parent::getCustomTransactionOldValue($object, $xaction); + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PackagesVersionTransaction::TYPE_TITLE: + case PackagesVersionTransaction::TYPE_DESCRIPTION: // 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->setProperty('description', $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: + 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; + } + + 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 package 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 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $package = $objects[$phid]; + + $id = $package->getID(); + $name = $package->getName(); + + $handle->setName($name); + $handle->setURI("/packages/package/{$id}/"); + } + } + + public function canLoadNamedObject($name) { + return false; + } + + 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 @@ +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 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $versions = $objects[$phid]; + + $id = $versions->getID(); + $name = $versions->getTitle(); + + $handle->setName($name); + $handle->setURI("/packages/version/{$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 PackagesVersionQuery()) + ->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 @@ +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 $versions) { + + if ($this->needProjectPHIDs) { + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($versions, 'getPHID')) + ->withEdgeTypes( + array( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + )); + $edge_query->execute(); + + foreach ($versions as $version) { + $phids = $edge_query->getDestinationPHIDs( + array( + $version->getPHID(), + )); + $version->attachProjectPHIDs($phids); + } + } + + return $versions; + } + + 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 @@ +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 @@ +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); + } + + // I've copied this pattern from HarbormasterBuildStepQuery + protected function willFilterPage(array $page) { + $versions = array(); + + $version_phids = array_filter(mpull($page, 'getVersionPHID')); + if ($version_phids) { + $versions = id(new PhabricatorObjectQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs($version_phids) + ->withTypes(array(PackagesVersionPHIDType::TYPECONST)) + ->setParentQuery($this) + ->execute(); + $versions = mpull($versions, null, 'getPHID'); + } + + foreach ($page as $key => $signature) { + $version_phid = $signature->getVersionPHID(); + if (empty($versions[$version_phid])) { + unset($page[$key]); + continue; + } + $signature->attachVersion($versions[$version_phid]); + } + + return $page; + } + + 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 @@ +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 @@ +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 $versions) { + + if ($this->needProjectPHIDs) { + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($versions, 'getPHID')) + ->withEdgeTypes( + array( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + )); + $edge_query->execute(); + + foreach ($versions as $version) { + $phids = $edge_query->getDestinationPHIDs( + array( + $version->getPHID(), + )); + $version->attachProjectPHIDs($phids); + } + } + + return $versions; + } + + 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 @@ +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 @@ +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', + '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,142 @@ +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,101 @@ +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_SERIALIZATION => array( + 'details' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array( + 'publicKey' => 'text', + 'description' => '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); + } + + public function getVersion() { + return $this->assertAttached($this->version); + } + + public function attachVersion(PackagesVersion $version) { + $this->version = $version; + return $this; + } + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + return $this->getVersion()->getPolicy($capability); + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return $this->getVersion()->hasAutomaticCapability($capability, $viewer); + } + + public function describeAutomaticCapability($capability) { + return pht('A signature has the same policies as its package'); + } + +/* -( 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,154 @@ +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_SERIALIZATION => array( + 'properties' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array( + 'title' => 'text255', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_package' => array( + 'columns' => array('packagePHID'), + ), + ), + ) + parent::getConfiguration(); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID(PackagesVersionPHIDType::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(); + } + + 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, + 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 @@ +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 @@ +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, ),