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 @@ -3599,11 +3599,15 @@ 'PhabricatorTimezoneSetupCheck' => 'applications/config/check/PhabricatorTimezoneSetupCheck.php', 'PhabricatorTitleGlyphsSetting' => 'applications/settings/setting/PhabricatorTitleGlyphsSetting.php', 'PhabricatorToken' => 'applications/tokens/storage/PhabricatorToken.php', + 'PhabricatorTokenArchiveController' => 'applications/tokens/controller/PhabricatorTokenArchiveController.php', 'PhabricatorTokenController' => 'applications/tokens/controller/PhabricatorTokenController.php', 'PhabricatorTokenCount' => 'applications/tokens/storage/PhabricatorTokenCount.php', 'PhabricatorTokenCountQuery' => 'applications/tokens/query/PhabricatorTokenCountQuery.php', 'PhabricatorTokenDAO' => 'applications/tokens/storage/PhabricatorTokenDAO.php', 'PhabricatorTokenDestructionEngineExtension' => 'applications/tokens/engineextension/PhabricatorTokenDestructionEngineExtension.php', + 'PhabricatorTokenEditController' => 'applications/tokens/controller/PhabricatorTokenEditController.php', + 'PhabricatorTokenEditEngine' => 'applications/tokens/editor/PhabricatorTokenEditEngine.php', + 'PhabricatorTokenEditor' => 'applications/tokens/editor/PhabricatorTokenEditor.php', 'PhabricatorTokenGiveController' => 'applications/tokens/controller/PhabricatorTokenGiveController.php', 'PhabricatorTokenGiven' => 'applications/tokens/storage/PhabricatorTokenGiven.php', 'PhabricatorTokenGivenController' => 'applications/tokens/controller/PhabricatorTokenGivenController.php', @@ -3611,15 +3615,26 @@ 'PhabricatorTokenGivenFeedStory' => 'applications/tokens/feed/PhabricatorTokenGivenFeedStory.php', 'PhabricatorTokenGivenQuery' => 'applications/tokens/query/PhabricatorTokenGivenQuery.php', 'PhabricatorTokenLeaderController' => 'applications/tokens/controller/PhabricatorTokenLeaderController.php', + 'PhabricatorTokenListController' => 'applications/tokens/controller/PhabricatorTokenListController.php', 'PhabricatorTokenQuery' => 'applications/tokens/query/PhabricatorTokenQuery.php', 'PhabricatorTokenReceiverInterface' => 'applications/tokens/interface/PhabricatorTokenReceiverInterface.php', 'PhabricatorTokenReceiverQuery' => 'applications/tokens/query/PhabricatorTokenReceiverQuery.php', - 'PhabricatorTokenTokenPHIDType' => 'applications/tokens/phid/PhabricatorTokenTokenPHIDType.php', + 'PhabricatorTokenSearchEngine' => 'applications/tokens/query/PhabricatorTokenSearchEngine.php', 'PhabricatorTokenUIEventListener' => 'applications/tokens/event/PhabricatorTokenUIEventListener.php', + 'PhabricatorTokenViewController' => 'applications/tokens/controller/PhabricatorTokenViewController.php', 'PhabricatorTokenizerEditField' => 'applications/transactions/editfield/PhabricatorTokenizerEditField.php', 'PhabricatorTokensApplication' => 'applications/tokens/application/PhabricatorTokensApplication.php', + 'PhabricatorTokensCreateCapability' => 'applications/tokens/capability/PhabricatorTokensCreateCapability.php', 'PhabricatorTokensCurtainExtension' => 'applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php', + 'PhabricatorTokensDefaultEditCapability' => 'applications/tokens/capability/PhabricatorTokensDefaultEditCapability.php', + 'PhabricatorTokensMailReceiver' => 'applications/tokens/mail/PhabricatorTokensMailReceiver.php', + 'PhabricatorTokensReplyHandler' => 'applications/tokens/mail/PhabricatorTokensReplyHandler.php', 'PhabricatorTokensSettingsPanel' => 'applications/settings/panel/PhabricatorTokensSettingsPanel.php', + 'PhabricatorTokensToken' => 'applications/tokens/storage/PhabricatorTokensToken.php', + 'PhabricatorTokensTokenPHIDType' => 'applications/tokens/phid/PhabricatorTokensTokenPHIDType.php', + 'PhabricatorTokensTransaction' => 'applications/tokens/storage/PhabricatorTokensTransaction.php', + 'PhabricatorTokensTransactionComment' => 'applications/tokens/storage/PhabricatorTokensTransactionComment.php', + 'PhabricatorTokensTransactionQuery' => 'applications/tokens/query/PhabricatorTokensTransactionQuery.php', 'PhabricatorTooltipUIExample' => 'applications/uiexample/examples/PhabricatorTooltipUIExample.php', 'PhabricatorTransactionChange' => 'applications/transactions/data/PhabricatorTransactionChange.php', 'PhabricatorTransactionRemarkupChange' => 'applications/transactions/data/PhabricatorTransactionRemarkupChange.php', @@ -8440,11 +8455,15 @@ 'PhabricatorTokenDAO', 'PhabricatorPolicyInterface', ), + 'PhabricatorTokenArchiveController' => 'PhabricatorTokenController', 'PhabricatorTokenController' => 'PhabricatorController', 'PhabricatorTokenCount' => 'PhabricatorTokenDAO', 'PhabricatorTokenCountQuery' => 'PhabricatorOffsetPagedQuery', 'PhabricatorTokenDAO' => 'PhabricatorLiskDAO', 'PhabricatorTokenDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', + 'PhabricatorTokenEditController' => 'PhabricatorTokenController', + 'PhabricatorTokenEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorTokenEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorTokenGiveController' => 'PhabricatorTokenController', 'PhabricatorTokenGiven' => array( 'PhabricatorTokenDAO', @@ -8455,14 +8474,31 @@ 'PhabricatorTokenGivenFeedStory' => 'PhabricatorFeedStory', 'PhabricatorTokenGivenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorTokenLeaderController' => 'PhabricatorTokenController', + 'PhabricatorTokenListController' => 'PhabricatorTokenController', 'PhabricatorTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorTokenReceiverQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', - 'PhabricatorTokenTokenPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorTokenSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorTokenUIEventListener' => 'PhabricatorEventListener', + 'PhabricatorTokenViewController' => 'PhabricatorTokenController', 'PhabricatorTokenizerEditField' => 'PhabricatorPHIDListEditField', 'PhabricatorTokensApplication' => 'PhabricatorApplication', + 'PhabricatorTokensCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorTokensCurtainExtension' => 'PHUICurtainExtension', + 'PhabricatorTokensDefaultEditCapability' => 'PhabricatorPolicyCapability', + 'PhabricatorTokensMailReceiver' => 'PhabricatorObjectMailReceiver', + 'PhabricatorTokensReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhabricatorTokensSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorTokensToken' => array( + 'PhameDAO', + 'PhabricatorPolicyInterface', + 'PhabricatorDestructibleInterface', + 'PhabricatorApplicationTransactionInterface', + 'PhabricatorConduitResultInterface', + ), + 'PhabricatorTokensTokenPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorTokensTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorTokensTransactionComment' => 'PhabricatorApplicationTransactionComment', + 'PhabricatorTokensTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorTooltipUIExample' => 'PhabricatorUIExample', 'PhabricatorTransactionChange' => 'Phobject', 'PhabricatorTransactionRemarkupChange' => 'PhabricatorTransactionChange', diff --git a/src/applications/tokens/application/PhabricatorTokensApplication.php b/src/applications/tokens/application/PhabricatorTokensApplication.php --- a/src/applications/tokens/application/PhabricatorTokensApplication.php +++ b/src/applications/tokens/application/PhabricatorTokensApplication.php @@ -7,7 +7,7 @@ } public function getBaseURI() { - return '/token/'; + return '/tokens/'; } public function getIcon() { @@ -28,11 +28,33 @@ public function getRoutes() { return array( - '/token/' => array( - '' => 'PhabricatorTokenGivenController', - 'given/' => 'PhabricatorTokenGivenController', - 'give/(?[^/]+)/' => 'PhabricatorTokenGiveController', - 'leaders/' => 'PhabricatorTokenLeaderController', + '/tokens/' => array( + '(?:query/(?P[^/]+)/)?' + => 'PhabricatorTokenListController', + 'given/' => + 'PhabricatorTokenGivenController', + 'give/(?[^/]+)/' => + 'PhabricatorTokenGiveController', + 'leaders/' => + 'PhabricatorTokenLeaderController', + 'view/(?:(?P\d+)/)?' + => 'PhabricatorTokenViewController', + $this->getEditRoutePattern('edit/') + => 'PhabaricatorTokenEditController', + ), + ); + } + + protected function getCustomCapabilities() { + return array( + PhabricatorTokensCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_ADMIN, + 'caption' => pht('Default create policy for tokens.'), + ), + PhabricatorTokensDefaultEditCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_ADMIN, + 'caption' => pht('Default edit policy for tokens.'), + 'template' => PhabricatorTokensTokenPHIDType::TYPECONST, ), ); } diff --git a/src/applications/tokens/capability/PhabricatorTokensCreateCapability.php b/src/applications/tokens/capability/PhabricatorTokensCreateCapability.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/capability/PhabricatorTokensCreateCapability.php @@ -0,0 +1,16 @@ +getViewer(); + $id = $request->getURIData('id'); + + $token = id(new PhabricatorTokenQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$token) { + return new Aphront404Response(); + } + + $view_uri = $this->getApplicationURI('view/'.$token->getID().'/'); + + if ($request->isFormPost()) { + if ($token->isArchived()) { + $new_status = PhabricatorTokensToken::STATUS_ACTIVE; + } else { + $new_status = PhabricatorTokensToken::STATUS_ARCHIVED; + } + + $xactions = array(); + + $xactions[] = id(new PhabricatorTokensTransaction()) + ->setTransactionType(PhabricatorTokensTransaction::TYPE_STATUS) + ->setNewValue($new_status); + + id(new PhabricatorTokenEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($token, $xactions); + + return id(new AphrontRedirectResponse())->setURI($view_uri); + } + + if ($token->isArchived()) { + $title = pht('Activate Token'); + $body = pht('This token will immediately be available for '. + 'general tomfoolery.'); + $button = pht('Activate Token'); + } else { + $title = pht('Archive Token'); + $body = pht( + 'Really? You want to just "get rid" of this token? Kinda harsh.'); + $button = pht('Archive Token'); + } + + return $this->newDialog() + ->setTitle($title) + ->appendChild($body) + ->addCancelButton($view_uri) + ->addSubmitButton($button); + } + +} diff --git a/src/applications/tokens/controller/PhabricatorTokenController.php b/src/applications/tokens/controller/PhabricatorTokenController.php --- a/src/applications/tokens/controller/PhabricatorTokenController.php +++ b/src/applications/tokens/controller/PhabricatorTokenController.php @@ -1,25 +1,4 @@ setBaseURI(new PhutilURI($this->getApplicationURI())); - - $nav->addLabel(pht('Tokens')); - $nav->addFilter('given/', pht('Tokens Given')); - $nav->addFilter('leaders/', pht('Leader Board')); - - return $nav; - } - - public function buildApplicationMenu() { - return $this->buildSideNav()->getMenu(); - } - - protected function buildApplicationCrumbs() { - $crumbs = parent::buildApplicationCrumbs(); - return $crumbs; - } - -} diff --git a/src/applications/tokens/controller/PhabricatorTokenEditController.php b/src/applications/tokens/controller/PhabricatorTokenEditController.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/controller/PhabricatorTokenEditController.php @@ -0,0 +1,11 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/tokens/controller/PhabricatorTokenListController.php b/src/applications/tokens/controller/PhabricatorTokenListController.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/controller/PhabricatorTokenListController.php @@ -0,0 +1,26 @@ +setController($this) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + id(new PhabricatorTokenEditEngine()) + ->setViewer($this->getViewer()) + ->addActionToCrumbs($crumbs); + + return $crumbs; + } + +} diff --git a/src/applications/tokens/controller/PhabricatorTokenViewController.php b/src/applications/tokens/controller/PhabricatorTokenViewController.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/controller/PhabricatorTokenViewController.php @@ -0,0 +1,124 @@ +getViewer(); + $id = $request->getURIData('id'); + + $token = id(new PhabricatorTokenQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$token) { + return new Aphront404Response(); + } + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb($token->getName()); + $crumbs->setBorder(true); + $title = $token->getName(); + + if ($token->isArchived()) { + $status_icon = 'fa-ban'; + $status_color = 'dark'; + } else { + $status_icon = 'fa-check'; + $status_color = 'bluegrey'; + } + $status_name = idx( + PhabricatorTokensToken::getStatusNameMap(), + $token->getStatus()); + + $header = id(new PHUIHeaderView()) + ->setHeader($token->getName()) + ->setUser($viewer) + ->setPolicyObject($token) + ->setStatus($status_icon, $status_color, $status_name) + ->setHeaderIcon('fa-trophy'); + + $curtain = $this->buildCurtain($token); + $details = $this->buildDetailsView($token); + + $timeline = $this->buildTransactionTimeline( + $token, + new PhabricatorTokensTransactionQuery()); + + $comment_view = id(new PhabricatorTokenEditEngine()) + ->setViewer($viewer) + ->buildEditEngineCommentView($token); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn(array( + $timeline, + $comment_view, + )) + ->addPropertySection(pht('Description'), $details); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($token->getPHID())) + ->appendChild($view); + } + + private function buildDetailsView( + PhabricatorBadgesBadge $token) { + $viewer = $this->getViewer(); + + $view = id(new PHUIPropertyListView()) + ->setUser($viewer); + + return $view; + } + + private function buildCurtain(PhabricatorTokensToken $token) { + $viewer = $this->getViewer(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $token, + PhabricatorPolicyCapability::CAN_EDIT); + + $id = $token->getID(); + $edit_uri = $this->getApplicationURI("/edit/{$id}/"); + $archive_uri = $this->getApplicationURI("/archive/{$id}/"); + + $curtain = $this->newCurtainView($token); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Token')) + ->setIcon('fa-pencil') + ->setDisabled(!$can_edit) + ->setHref($edit_uri)); + + if ($token->isArchived()) { + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Activate Token')) + ->setIcon('fa-check') + ->setDisabled(!$can_edit) + ->setWorkflow($can_edit) + ->setHref($archive_uri)); + } else { + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Archive Token')) + ->setIcon('fa-ban') + ->setDisabled(!$can_edit) + ->setWorkflow($can_edit) + ->setHref($archive_uri)); + } + + return $curtain; + } + +} diff --git a/src/applications/tokens/editor/PhabricatorTokenEditEngine.php b/src/applications/tokens/editor/PhabricatorTokenEditEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/editor/PhabricatorTokenEditEngine.php @@ -0,0 +1,90 @@ +getViewer()); + } + + protected function newObjectQuery() { + return id(new PhabricatorTokenQuery()); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create New Token'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit %s', $object->getName()); + } + + protected function getObjectEditShortText($object) { + return $object->getName(); + } + + protected function getObjectCreateShortText() { + return pht('Create Token'); + } + + protected function getObjectName() { + return pht('Token'); + } + + protected function getObjectCreateCancelURI($object) { + return $this->getApplication()->getApplicationURI('token/'); + } + + protected function getEditorURI() { + return $this->getApplication()->getApplicationURI('token/edit/'); + } + + protected function getObjectViewURI($object) { + return $object->getManageURI(); + } + + protected function getCreateNewObjectPolicy() { + return $this->getApplication()->getPolicy( + PhabricatorTokensCreateCapability::CAPABILITY); + } + + protected function buildCustomEditFields($object) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setDescription(pht('Token name.')) + ->setConduitDescription(pht('Retitle the token.')) + ->setConduitTypeDescription(pht('New token name.')) + ->setTransactionType(PhabricatorTokensTransaction::TYPE_NAME) + ->setValue($object->getName()), + id(new PhabricatorTextEditField()) + ->setKey('flavor') + ->setLabel(pht('Flavor')) + ->setDescription(pht('Token flavor text.')) + ->setConduitDescription(pht('Change the token flavor.')) + ->setConduitTypeDescription(pht('New token flavor.')) + ->setTransactionType(PhabricatorTokensTransaction::TYPE_FLAVOR) + ->setValue($object->getFlavor()), + ); + } + +} diff --git a/src/applications/tokens/editor/PhabricatorTokenEditor.php b/src/applications/tokens/editor/PhabricatorTokenEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/editor/PhabricatorTokenEditor.php @@ -0,0 +1,188 @@ +getTransactionType()) { + case PhabricatorTokensTransaction::TYPE_NAME: + return $object->getName(); + case PhabricatorTokensTransaction::TYPE_FLAVOR: + return $object->getFlavor(); + case PhabricatorTokensTransaction::TYPE_STATUS: + return $object->getStatus(); + case PhabricatorTokensTransaction::TYPE_IMAGE: + return $object->getImage(); + } + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorTokensTransaction::TYPE_NAME: + case PhabricatorTokensTransaction::TYPE_FLAVOR: + case PhabricatorTokensTransaction::TYPE_STATUS: + case PhabricatorTokensTransaction::TYPE_IMAGE: + return $xaction->getNewValue(); + } + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorTokensTransaction::TYPE_NAME: + return $object->setName($xaction->getNewValue()); + case PhabricatorTokensTransaction::TYPE_FLAVOR: + return $object->setFlavor($xaction->getNewValue()); + case PhabricatorTokensTransaction::TYPE_STATUS: + return $object->setStatus($xaction->getNewValue()); + case PhabricatorTokensTransaction::TYPE_IMAGE: + return $object->setImage($xaction->getNewValue()); + } + + return parent::applyCustomInternalTransaction($object, $xaction); + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorTokensTransaction::TYPE_NAME: + case PhabricatorTokensTransaction::TYPE_FLAVOR: + case PhabricatorTokensTransaction::TYPE_STATUS: + case PhabricatorTokensTransaction::TYPE_IMAGE: + return; + } + + return parent::applyCustomExternalTransaction($object, $xaction); + } + + protected function validateTransaction( + PhabricatorLiskDAO $object, + $type, + array $xactions) { + + $errors = parent::validateTransaction($object, $type, $xactions); + + switch ($type) { + case PhabricatorTokensTransaction::TYPE_NAME: + $missing = $this->validateIsEmptyTextField( + $object->getName(), + $xactions); + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Name is required.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } + break; + } + return $errors; + } + + protected function shouldSendMail( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function shouldPublishFeedStory( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function getMailTo(PhabricatorLiskDAO $object) { + $phids = array(); + $phids[] = $this->requireActor()->getPHID(); + $phids[] = $object->getCreatorPHID(); + + return $phids; + } + + protected function buildMailTemplate(PhabricatorLiskDAO $object) { + $phid = $object->getPHID(); + $name = $object->getName(); + + return id(new PhabricatorMetaMTAMail()) + ->setSubject($name) + ->addHeader('Thread-Topic', $phid); + } + + protected function buildReplyHandler(PhabricatorLiskDAO $object) { + return id(new PhabricatorTokensReplyHandler()) + ->setMailReceiver($object); + } + + protected function buildMailBody( + PhabricatorLiskDAO $object, + array $xactions) { + + $body = parent::buildMailBody($object, $xactions); + + $body->addLinkSection( + pht('TOKEN DETAIL'), + PhabricatorEnv::getProductionURI($object->getViewURI())); + + return $body; + } + + public function getMailTagsMap() { + return array( + PhabricatorTokensTransaction::MAILTAG_DETAILS => + pht("A token's details change."), + PhabricatorTokensTransaction::MAILTAG_SUBSCRIBERS => + pht("A token's subscribers change."), + PhabricatorTokensTransaction::MAILTAG_OTHER => + pht('Other token activity not listed above occurs.'), + ); + } + + protected function getMailSubjectPrefix() { + return '[Tokens]'; + } + + protected function supportsSearch() { + return false; + } + + protected function shouldApplyHeraldRules( + PhabricatorLiskDAO $object, + array $xactions) { + return false; + } + +} diff --git a/src/applications/tokens/mail/PhabricatorTokensMailReceiver.php b/src/applications/tokens/mail/PhabricatorTokensMailReceiver.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/mail/PhabricatorTokensMailReceiver.php @@ -0,0 +1,28 @@ +setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + } + + protected function getTransactionReplyHandler() { + return new PhabricatorTokensReplyHandler(); + } + +} diff --git a/src/applications/tokens/mail/PhabricatorTokensReplyHandler.php b/src/applications/tokens/mail/PhabricatorTokensReplyHandler.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/mail/PhabricatorTokensReplyHandler.php @@ -0,0 +1,16 @@ +ids = $ids; + return $this; + } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } + public function withStatuses(array $statuses) { + $this->statuses = $statuses; + return $this; + } + protected function loadPage() { $tokens = $this->getBuiltinTokens(); @@ -45,7 +59,7 @@ array('misc-4', pht('The World Burns')), ); - $type = PhabricatorTokenTokenPHIDType::TYPECONST; + $type = PhabricatorTokensTokenPHIDType::TYPECONST; $tokens = array(); foreach ($specs as $id => $spec) { diff --git a/src/applications/tokens/query/PhabricatorTokenSearchEngine.php b/src/applications/tokens/query/PhabricatorTokenSearchEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/query/PhabricatorTokenSearchEngine.php @@ -0,0 +1,131 @@ +setKey('statuses') + ->setLabel(pht('Status')) + ->setOptions( + id(new PhabricatorTokensToken()) + ->getStatusNameMap()), + ); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + if ($map['statuses']) { + $query->withStatuses($map['statuses']); + } + + return $query; + } + + protected function getURI($path) { + return '/tokens/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array(); + + $names['open'] = pht('Active Tokens'); + $names['all'] = pht('All Tokens'); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + case 'open': + return $query->setParameter( + 'statuses', + array( + PhabricatorBadgesBadge::STATUS_ACTIVE, + )); + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function getRequiredHandlePHIDsForResultList( + array $tokens, + PhabricatorSavedQuery $query) { + + $phids = array(); + + return $phids; + } + + protected function renderResultList( + array $tokens, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($tokens, 'PhabricatorTokensToken'); + + $viewer = $this->requireViewer(); + + $list = id(new PHUIObjectItemListView()); + foreach ($tokens as $token) { + + $item = id(new PHUIObjectItemView()) + ->setHeader($token->getName()) + ->setHref('/tokens/view/'.$token->getID().'/') + ->addAttribute($token->getFlavor()); + + if ($token->isArchived()) { + $item->setDisabled(true); + $item->addIcon('fa-ban', pht('Archived')); + } + + $list->addItem($item); + } + + $result = new PhabricatorApplicationSearchResultView(); + $result->setObjectList($list); + $result->setNoDataString(pht('No tokens found.')); + + return $result; + + } + + protected function getNewUserBody() { + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Create a Token')) + ->setHref('/tokens/edit/') + ->setColor(PHUIButtonView::GREEN); + + $icon = $this->getApplication()->getIcon(); + $app_name = $this->getApplication()->getName(); + $view = id(new PHUIBigInfoView()) + ->setIcon($icon) + ->setTitle(pht('Welcome to %s', $app_name)) + ->setDescription( + pht('Tokens let you award and distinguish objects and comments '. + 'throughout your instance.')) + ->addAction($create_button); + + return $view; + } + +} diff --git a/src/applications/tokens/query/PhabricatorTokensTransactionQuery.php b/src/applications/tokens/query/PhabricatorTokensTransactionQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/query/PhabricatorTokensTransactionQuery.php @@ -0,0 +1,10 @@ + true, + self::CONFIG_SERIALIZATION => array( + 'configData' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array( + 'name' => 'text64', + 'flavor' => 'text128', + 'status' => 'text32', + 'tokenImagePHID' => 'phid?', + 'viewPolicy' => 'policy?', + 'editPolicy' => 'policy?', + 'mailKey' => 'bytes20', + ), + ) + parent::getConfiguration(); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorTokensTokenPHIDType::TYPECONST); + } + + public static function initializeNewToken(PhabricatorUser $actor) { + $app = id(new PhabricatorApplicationQuery()) + ->setViewer($actor) + ->withClasses(array('PhabricatorTokensApplication')) + ->executeOne(); + + $view_policy = PhabricatorPolicies::getMostOpenPolicy(); + + $edit_policy = + $app->getPolicy(PhabricatorBadgesDefaultEditCapability::CAPABILITY); + + $token = id(new self()) + ->setCreatorPHID($actor->getPHID()) + ->setStatus(self::STATUS_ACTIVE) + ->setViewPolicy($view_policy) + ->setEditPolicy($edit_policy); + return $token; + } + + public function isArchived() { + return ($this->getStatus() == self::STATUS_ARCHIVED); + } + + public static function getStatusNameMap() { + return array( + self::STATUS_ACTIVE => pht('Active'), + self::STATUS_ARCHIVED => pht('Archived'), + ); + } + + public function getTokenImageURI() { + return $this->getTokenImageFile()->getBestURI(); + } + + public function attachTokenImageFile(PhabricatorFile $file) { + $this->tokenImageFile = $file; + return $this; + } + + public function getTokenImageFile() { + return $this->assertAttached($this->tokenImageFile); + } + + public function save() { + if (!$this->getMailKey()) { + $this->setMailKey(Filesystem::readRandomCharacters(20)); + } + return parent::save(); + } + + +/* -( PhabricatorPolicyInterface Implementation )-------------------------- */ + + + 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 $user) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return true; + break; + } + return false; + } + + + public function describeAutomaticCapability($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return pht( + 'Users can always see Tokens.'); + } + + return null; + } + +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + + $this->openTransaction(); + + $tokens = id(new self()) + ->loadAllWhere('tokenPHID = %s', $this->getPHID()); + foreach ($tokens as $token) { + $token->delete(); + } + $this->delete(); + + $this->saveTransaction(); + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhabricatorTokenEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new PhabricatorTokensTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + return $timeline; + } + + +/* -( PhabricatorSubscribableInterface Implementation )-------------------- */ + + + public function isAutomaticallySubscribed($phid) { + return ($this->creatorPHID == $phid); + } + + +/* -( PhabricatorConduitResultInterface )---------------------------------- */ + + + public function getFieldSpecificationsForConduit() { + return array( + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('name') + ->setType('string') + ->setDescription(pht('The name of the token.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('flavor') + ->setType('string') + ->setDescription(pht('Token flavor.')), + id(new PhabricatorConduitSearchFieldSpecification()) + ->setKey('status') + ->setType('string') + ->setDescription(pht('Archived or active status.')), + ); + } + + public function getFieldValuesForConduit() { + return array( + 'name' => $this->getName(), + 'flavor' => $this->getFlavor(), + 'status' => $this->getStatus(), + ); + } + + public function getConduitSearchAttachments() { + return array(); + } + + +} diff --git a/src/applications/tokens/storage/PhabricatorTokensTransaction.php b/src/applications/tokens/storage/PhabricatorTokensTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/storage/PhabricatorTokensTransaction.php @@ -0,0 +1,154 @@ +getAuthorPHID(); + $object_phid = $this->getObjectPHID(); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $type = $this->getTransactionType(); + switch ($type) { + case self::TYPE_NAME: + if ($old === null) { + return pht( + '%s created this token.', + $this->renderHandleLink($author_phid)); + } else { + return pht( + '%s renamed this token from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } + break; + case self::TYPE_FLAVOR: + if ($old === null) { + return pht( + '%s set the flavor text for this token.', + $this->renderHandleLink($author_phid)); + } else { + return pht( + '%s updated the flavor text for this token.', + $this->renderHandleLink($author_phid)); + } + break; + case self::TYPE_STATUS: + switch ($new) { + case PhabricatorTokensToken::STATUS_ACTIVE: + return pht( + '%s activated this token.', + $this->renderHandleLink($author_phid)); + case PhabricatorTokensToken::STATUS_ARCHIVED: + return pht( + '%s archived this token.', + $this->renderHandleLink($author_phid)); + } + break; + case self::TYPE_IMAGE: + return pht( + '%s updated the image for this token.', + $this->renderHandleLink($author_phid)); + break; + } + + return parent::getTitle(); + } + + public function getTitleForFeed() { + $author_phid = $this->getAuthorPHID(); + $object_phid = $this->getObjectPHID(); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $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; + case self::TYPE_FLAVOR: + return pht( + '%s updated the flavor text for %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + case self::TYPE_IMAGE: + return pht( + '%s updated the image for %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + case self::TYPE_STATUS: + switch ($new) { + case PhabricatorTokensToken::STATUS_ACTIVE: + return pht( + '%s activated %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + case PhabricatorTokensToken::STATUS_ARCHIVED: + return pht( + '%s archived %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; + } + + return parent::getTitleForFeed(); + } + + public function getMailTags() { + $tags = parent::getMailTags(); + + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_COMMENT: + $tags[] = self::MAILTAG_COMMENT; + break; + case self::TYPE_NAME: + case self::TYPE_FLAVOR: + case self::TYPE_IMAGE: + $tags[] = self::MAILTAG_DETAILS; + break; + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + return $tags; + } + +} diff --git a/src/applications/tokens/storage/PhabricatorTokensTransactionComment.php b/src/applications/tokens/storage/PhabricatorTokensTransactionComment.php new file mode 100644 --- /dev/null +++ b/src/applications/tokens/storage/PhabricatorTokensTransactionComment.php @@ -0,0 +1,10 @@ +