diff --git a/src/applications/project/controller/PhabricatorProjectBoardController.php b/src/applications/project/controller/PhabricatorProjectBoardController.php index d127a6e921..fac78072e6 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardController.php @@ -1,185 +1,197 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) ->executeOne(); if (!$project) { return new Aphront404Response(); } $columns = id(new PhabricatorProjectColumnQuery()) ->setViewer($viewer) ->withProjectPHIDs(array($project->getPHID())) ->execute(); $columns = mpull($columns, null, 'getSequence'); // If there's no default column, create one now. if (empty($columns[0])) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $column = PhabricatorProjectColumn::initializeNewColumn($viewer) ->setSequence(0) ->setProjectPHID($project->getPHID()) ->save(); $column->attachProject($project); $columns[0] = $column; unset($unguarded); } ksort($columns); $tasks = id(new ManiphestTaskQuery()) ->setViewer($viewer) ->withAllProjects(array($project->getPHID())) ->withStatus(ManiphestTaskQuery::STATUS_OPEN) ->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY) ->execute(); $tasks = mpull($tasks, null, 'getPHID'); + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($tasks, 'getPHID')) + ->withEdgeTypes(array($edge_type)) + ->withDestinationPHIDs(mpull($columns, 'getPHID')); + $edge_query->execute(); + $task_map = array(); $default_phid = $columns[0]->getPHID(); - foreach ($tasks as $task) { - $task_map[$default_phid][] = $task->getPHID(); + $task_phid = $task->getPHID(); + $column_phids = $edge_query->getDestinationPHIDs(array($task_phid)); + + $column_phid = head($column_phids); + $column_phid = nonempty($column_phid, $default_phid); + + $task_map[$column_phid][] = $task_phid; } $board_id = celerity_generate_unique_node_id(); $board = id(new PHUIWorkboardView()) ->setUser($viewer) ->setFluidishLayout(true) ->setID($board_id); $this->initBehavior( 'project-boards', array( 'boardID' => $board_id, 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'), )); foreach ($columns as $column) { $panel = id(new PHUIWorkpanelView()) ->setHeader($column->getDisplayName()) ->setHeaderColor($column->getHeaderColor()) ->setEditURI('edit/'.$column->getID().'/'); $cards = id(new PHUIObjectItemListView()) ->setUser($viewer) ->setCards(true) ->setFlush(true) ->setAllowEmptyList(true) ->addSigil('project-column') ->setMetadata( array( 'columnPHID' => $column->getPHID(), )); $task_phids = idx($task_map, $column->getPHID(), array()); foreach (array_select_keys($tasks, $task_phids) as $task) { $cards->addItem($this->renderTaskCard($task)); } $panel->setCards($cards); if (!$task_phids) { $cards->addClass('project-column-empty'); } $board->addPanel($panel); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb( $project->getName(), $this->getApplicationURI('view/'.$project->getID().'/')); $crumbs->addTextCrumb(pht('Board')); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); $actions = id(new PhabricatorActionListView()) ->setUser($viewer) ->addAction( id(new PhabricatorActionView()) ->setName(pht('Add Column/Milestone/Sprint')) ->setHref($this->getApplicationURI('board/'.$this->id.'/edit/')) ->setIcon('create') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); $plist = id(new PHUIPropertyListView()); // TODO: Need this to get actions to render. $plist->addProperty(pht('Ignore'), pht('This Property')); $plist->setActionList($actions); $header = id(new PHUIObjectBoxView()) ->setHeaderText($project->getName()) ->addPropertyList($plist); $board_box = id(new PHUIBoxView()) ->appendChild($board) ->addMargin(PHUI::MARGIN_LARGE); return $this->buildApplicationPage( array( $crumbs, $header, $board_box, ), array( 'title' => pht('%s Board', $project->getName()), 'device' => true, )); } private function renderTaskCard(ManiphestTask $task) { $request = $this->getRequest(); $viewer = $request->getUser(); $color_map = ManiphestTaskPriority::getColorMap(); $bar_color = idx($color_map, $task->getPriority(), 'grey'); // TODO: Batch this earlier on. $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $task, PhabricatorPolicyCapability::CAN_EDIT); return id(new PHUIObjectItemView()) ->setObjectName('T'.$task->getID()) ->setHeader($task->getTitle()) ->setGrippable($can_edit) ->setHref('/T'.$task->getID()) ->addSigil('project-card') ->setMetadata( array( 'objectPHID' => $task->getPHID(), )) ->addAction( id(new PHUIListItemView()) ->setName(pht('Edit')) ->setIcon('edit') ->setHref('/maniphest/task/edit/'.$task->getID().'/') ->setWorkflow(true)) ->setBarColor($bar_color); } } diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index 73a79b546d..7478a7dc79 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -1,31 +1,120 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); + $column_phid = $request->getStr('columnPHID'); + $object_phid = $request->getStr('objectPHID'); + $after_phid = $request->getStr('afterPHID'); + $project = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->withIDs(array($this->id)) ->executeOne(); if (!$project) { return new Aphront404Response(); } + // NOTE: I'm not requiring EDIT on the object for now, since we require + // EDIT on the project anyway and this relationship is more owned by the + // project than the object. Maybe this is worth revisiting eventually. + + $object = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withPHIDs(array($object_phid)) + ->executeOne(); + + if (!$object) { + return new Aphront404Response(); + } + + $columns = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withProjectPHIDs(array($project->getPHID())) + ->execute(); + + $columns = mpull($columns, null, 'getPHID'); + if (empty($columns[$column_phid])) { + // User is trying to drop this object into a nonexistent column, just kick + // them out. + return new Aphront404Response(); + } + + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; + + $query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(array($object->getPHID())) + ->withEdgeTypes(array($edge_type)) + ->withDestinationPHIDs(array_keys($columns)); + + $query->execute(); + + $edge_phids = $query->getDestinationPHIDs(); + + $this->rewriteEdges( + $object->getPHID(), + $edge_type, + $column_phid, + $edge_phids); + + // TODO: We also need to deal with priorities, so far this only gets stuff + // in the correct column. + return id(new AphrontAjaxResponse())->setContent(array()); } + + private function rewriteEdges($src, $edge_type, $dst, array $edges) { + $viewer = $this->getRequest()->getUser(); + + // NOTE: Normally, we expect only one edge to exist, but this works in a + // general way so it will repair any stray edges. + + $remove = array(); + $edge_missing = true; + foreach ($edges as $phid) { + if ($phid == $dst) { + $edge_missing = false; + } else { + $remove[] = $phid; + } + } + + $add = array(); + if ($edge_missing) { + $add[] = $dst; + } + + if (!$add && !$remove) { + return; + } + + $editor = id(new PhabricatorEdgeEditor()) + ->setActor($viewer) + ->setSuppressEvents(true); + + foreach ($add as $phid) { + $editor->addEdge($src, $edge_type, $phid); + } + foreach ($remove as $phid) { + $editor->removeEdge($src, $edge_type, $phid); + } + + $editor->save(); + } + } diff --git a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php index 81e851625f..8614a38372 100644 --- a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php +++ b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php @@ -1,478 +1,483 @@ self::TYPE_COMMIT_HAS_TASK, self::TYPE_COMMIT_HAS_TASK => self::TYPE_TASK_HAS_COMMIT, self::TYPE_TASK_DEPENDS_ON_TASK => self::TYPE_TASK_DEPENDED_ON_BY_TASK, self::TYPE_TASK_DEPENDED_ON_BY_TASK => self::TYPE_TASK_DEPENDS_ON_TASK, self::TYPE_DREV_DEPENDS_ON_DREV => self::TYPE_DREV_DEPENDED_ON_BY_DREV, self::TYPE_DREV_DEPENDED_ON_BY_DREV => self::TYPE_DREV_DEPENDS_ON_DREV, self::TYPE_BLOG_HAS_POST => self::TYPE_POST_HAS_BLOG, self::TYPE_POST_HAS_BLOG => self::TYPE_BLOG_HAS_POST, self::TYPE_BLOG_HAS_BLOGGER => self::TYPE_BLOGGER_HAS_BLOG, self::TYPE_BLOGGER_HAS_BLOG => self::TYPE_BLOG_HAS_BLOGGER, self::TYPE_TASK_HAS_RELATED_DREV => self::TYPE_DREV_HAS_RELATED_TASK, self::TYPE_DREV_HAS_RELATED_TASK => self::TYPE_TASK_HAS_RELATED_DREV, self::TYPE_PROJ_MEMBER => self::TYPE_MEMBER_OF_PROJ, self::TYPE_MEMBER_OF_PROJ => self::TYPE_PROJ_MEMBER, self::TYPE_COMMIT_HAS_PROJECT => self::TYPE_PROJECT_HAS_COMMIT, self::TYPE_PROJECT_HAS_COMMIT => self::TYPE_COMMIT_HAS_PROJECT, self::TYPE_QUESTION_HAS_VOTING_USER => self::TYPE_VOTING_USER_HAS_QUESTION, self::TYPE_VOTING_USER_HAS_QUESTION => self::TYPE_QUESTION_HAS_VOTING_USER, self::TYPE_ANSWER_HAS_VOTING_USER => self::TYPE_VOTING_USER_HAS_ANSWER, self::TYPE_VOTING_USER_HAS_ANSWER => self::TYPE_ANSWER_HAS_VOTING_USER, self::TYPE_OBJECT_HAS_SUBSCRIBER => self::TYPE_SUBSCRIBED_TO_OBJECT, self::TYPE_SUBSCRIBED_TO_OBJECT => self::TYPE_OBJECT_HAS_SUBSCRIBER, self::TYPE_OBJECT_HAS_UNSUBSCRIBER => self::TYPE_UNSUBSCRIBED_FROM_OBJECT, self::TYPE_UNSUBSCRIBED_FROM_OBJECT => self::TYPE_OBJECT_HAS_UNSUBSCRIBER, self::TYPE_OBJECT_HAS_FILE => self::TYPE_FILE_HAS_OBJECT, self::TYPE_FILE_HAS_OBJECT => self::TYPE_OBJECT_HAS_FILE, self::TYPE_ACCOUNT_HAS_MEMBER => self::TYPE_MEMBER_HAS_ACCOUNT, self::TYPE_MEMBER_HAS_ACCOUNT => self::TYPE_ACCOUNT_HAS_MEMBER, self::TYPE_DREV_HAS_COMMIT => self::TYPE_COMMIT_HAS_DREV, self::TYPE_COMMIT_HAS_DREV => self::TYPE_DREV_HAS_COMMIT, self::TYPE_OBJECT_HAS_CONTRIBUTOR => self::TYPE_SUBSCRIBED_TO_OBJECT, self::TYPE_CONTRIBUTED_TO_OBJECT => self::TYPE_OBJECT_HAS_CONTRIBUTOR, self::TYPE_TASK_HAS_MOCK => self::TYPE_MOCK_HAS_TASK, self::TYPE_MOCK_HAS_TASK => self::TYPE_TASK_HAS_MOCK, self::TYPE_PHOB_HAS_ASANATASK => self::TYPE_ASANATASK_HAS_PHOB, self::TYPE_ASANATASK_HAS_PHOB => self::TYPE_PHOB_HAS_ASANATASK, self::TYPE_PHOB_HAS_ASANASUBTASK => self::TYPE_ASANASUBTASK_HAS_PHOB, self::TYPE_ASANASUBTASK_HAS_PHOB => self::TYPE_PHOB_HAS_ASANASUBTASK, self::TYPE_DREV_HAS_REVIEWER => self::TYPE_REVIEWER_FOR_DREV, self::TYPE_REVIEWER_FOR_DREV => self::TYPE_DREV_HAS_REVIEWER, self::TYPE_PHOB_HAS_JIRAISSUE => self::TYPE_JIRAISSUE_HAS_PHOB, self::TYPE_JIRAISSUE_HAS_PHOB => self::TYPE_PHOB_HAS_JIRAISSUE, self::TYPE_OBJECT_USES_CREDENTIAL => self::TYPE_CREDENTIAL_USED_BY_OBJECT, self::TYPE_CREDENTIAL_USED_BY_OBJECT => self::TYPE_OBJECT_USES_CREDENTIAL, self::TYPE_OBJECT_HAS_PROJECT => self::TYPE_PROJECT_HAS_OBJECT, self::TYPE_PROJECT_HAS_OBJECT => self::TYPE_OBJECT_HAS_PROJECT, + + self::TYPE_OBJECT_HAS_COLUMN => self::TYPE_COLUMN_HAS_OBJECT, + self::TYPE_COLUMN_HAS_OBJECT => self::TYPE_OBJECT_HAS_COLUMN, ); return idx($map, $edge_type); } public static function shouldPreventCycles($edge_type) { static $map = array( self::TYPE_TEST_NO_CYCLE => true, self::TYPE_TASK_DEPENDS_ON_TASK => true, self::TYPE_DREV_DEPENDS_ON_DREV => true, ); return isset($map[$edge_type]); } public static function establishConnection($phid_type, $conn_type) { $map = PhabricatorPHIDType::getAllTypes(); if (isset($map[$phid_type])) { $type = $map[$phid_type]; $object = $type->newObject(); if ($object) { return $object->establishConnection($conn_type); } } static $class_map = array( PhabricatorPHIDConstants::PHID_TYPE_TOBJ => 'HarbormasterObject', PhabricatorPHIDConstants::PHID_TYPE_ACNT => 'PhortuneAccount', PhabricatorPHIDConstants::PHID_TYPE_PRCH => 'PhortunePurchase', PhabricatorPHIDConstants::PHID_TYPE_CHRG => 'PhortuneCharge', PhabricatorPHIDConstants::PHID_TYPE_XOBJ => 'DoorkeeperExternalObject', ); $class = idx($class_map, $phid_type); if (!$class) { throw new Exception( "Edges are not available for objects of type '{$phid_type}'!"); } return newv($class, array())->establishConnection($conn_type); } public static function getEditStringForEdgeType($type) { switch ($type) { case self::TYPE_TASK_HAS_COMMIT: case self::TYPE_PROJECT_HAS_COMMIT: case self::TYPE_DREV_HAS_COMMIT: return '%s edited commit(s), added %d: %s; removed %d: %s.'; case self::TYPE_COMMIT_HAS_TASK: case self::TYPE_TASK_DEPENDS_ON_TASK: case self::TYPE_TASK_DEPENDED_ON_BY_TASK: case self::TYPE_DREV_HAS_RELATED_TASK: case self::TYPE_MOCK_HAS_TASK: return '%s edited task(s), added %d: %s; removed %d: %s.'; case self::TYPE_DREV_DEPENDS_ON_DREV: case self::TYPE_DREV_DEPENDED_ON_BY_DREV: case self::TYPE_TASK_HAS_RELATED_DREV: case self::TYPE_COMMIT_HAS_DREV: case self::TYPE_REVIEWER_FOR_DREV: return '%s edited revision(s), added %d: %s; removed %d: %s.'; case self::TYPE_BLOG_HAS_POST: return '%s edited post(s), added %d: %s; removed %d: %s.'; case self::TYPE_POST_HAS_BLOG: case self::TYPE_BLOGGER_HAS_BLOG: return '%s edited blog(s), added %d: %s; removed %d: %s.'; case self::TYPE_BLOG_HAS_BLOGGER: return '%s edited blogger(s), added %d: %s; removed %d: %s.'; case self::TYPE_PROJ_MEMBER: return '%s edited member(s), added %d: %s; removed %d: %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: case self::TYPE_OBJECT_HAS_PROJECT: return '%s edited project(s), added %d: %s; removed %d: %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: return '%s edited voting user(s), added %d: %s; removed %d: %s.'; case self::TYPE_VOTING_USER_HAS_QUESTION: return '%s edited question(s), added %d: %s; removed %d: %s.'; case self::TYPE_VOTING_USER_HAS_ANSWER: return '%s edited answer(s), added %d: %s; removed %d: %s.'; case self::TYPE_OBJECT_HAS_SUBSCRIBER: return '%s edited subscriber(s), added %d: %s; removed %d: %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: case self::TYPE_PROJECT_HAS_OBJECT: return '%s edited object(s), added %d: %s; removed %d: %s.'; case self::TYPE_OBJECT_HAS_UNSUBSCRIBER: return '%s edited unsubcriber(s), added %d: %s; removed %d: %s.'; case self::TYPE_OBJECT_HAS_FILE: return '%s edited file(s), added %d: %s; removed %d: %s.'; case self::TYPE_ACCOUNT_HAS_MEMBER: return '%s edited member(s), added %d: %s; removed %d: %s.'; case self::TYPE_MEMBER_HAS_ACCOUNT: return '%s edited account(s), added %d: %s; removed %d: %s.'; case self::TYPE_PURCAHSE_HAS_CHARGE: return '%s edited charge(s), added %d: %s; removed %d: %s.'; case self::TYPE_CHARGE_HAS_PURCHASE: return '%s edited purchase(s), added %d: %s; removed %d: %s.'; case self::TYPE_OBJECT_HAS_CONTRIBUTOR: return '%s edited contributor(s), added %d: %s; removed %d: %s.'; case self::TYPE_DREV_HAS_REVIEWER: return '%s edited reviewer(s), added %d: %s; removed %d: %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s edited mock(s), added %d: %s; removed %d: %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: default: return '%s edited object(s), added %d: %s; removed %d: %s.'; } } public static function getAddStringForEdgeType($type) { switch ($type) { case self::TYPE_TASK_HAS_COMMIT: case self::TYPE_PROJECT_HAS_COMMIT: case self::TYPE_DREV_HAS_COMMIT: return '%s added %d commit(s): %s.'; case self::TYPE_TASK_DEPENDS_ON_TASK: return '%s added %d dependencie(s): %s.'; case self::TYPE_TASK_DEPENDED_ON_BY_TASK: return '%s added %d dependent task(s): %s.'; case self::TYPE_COMMIT_HAS_TASK: case self::TYPE_DREV_HAS_RELATED_TASK: case self::TYPE_MOCK_HAS_TASK: return '%s added %d task(s): %s.'; case self::TYPE_DREV_DEPENDS_ON_DREV: case self::TYPE_DREV_DEPENDED_ON_BY_DREV: case self::TYPE_TASK_HAS_RELATED_DREV: case self::TYPE_COMMIT_HAS_DREV: case self::TYPE_REVIEWER_FOR_DREV: return '%s added %d revision(s): %s.'; case self::TYPE_BLOG_HAS_POST: return '%s added %d post(s): %s.'; case self::TYPE_POST_HAS_BLOG: case self::TYPE_BLOGGER_HAS_BLOG: return '%s added %d blog(s): %s.'; case self::TYPE_BLOG_HAS_BLOGGER: return '%s added %d blogger(s): %s.'; case self::TYPE_PROJ_MEMBER: return '%s added %d member(s): %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: case self::TYPE_OBJECT_HAS_PROJECT: return '%s added %d project(s): %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: return '%s added %d voting user(s): %s.'; case self::TYPE_VOTING_USER_HAS_QUESTION: return '%s added %d question(s): %s.'; case self::TYPE_VOTING_USER_HAS_ANSWER: return '%s added %d answer(s): %s.'; case self::TYPE_OBJECT_HAS_SUBSCRIBER: return '%s added %d subscriber(s): %s.'; case self::TYPE_OBJECT_HAS_UNSUBSCRIBER: return '%s added %d unsubcriber(s): %s.'; case self::TYPE_OBJECT_HAS_FILE: return '%s added %d file(s): %s.'; case self::TYPE_ACCOUNT_HAS_MEMBER: return '%s added %d member(s): %s.'; case self::TYPE_MEMBER_HAS_ACCOUNT: return '%s added %d account(s): %s.'; case self::TYPE_PURCAHSE_HAS_CHARGE: return '%s added %d charge(s): %s.'; case self::TYPE_CHARGE_HAS_PURCHASE: return '%s added %d purchase(s): %s.'; case self::TYPE_OBJECT_HAS_CONTRIBUTOR: return '%s added %d contributor(s): %s.'; case self::TYPE_DREV_HAS_REVIEWER: return '%s added %d reviewer(s): %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s added %d mock(s): %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: case self::TYPE_PROJECT_HAS_OBJECT: default: return '%s added %d object(s): %s.'; } } public static function getRemoveStringForEdgeType($type) { switch ($type) { case self::TYPE_TASK_HAS_COMMIT: case self::TYPE_PROJECT_HAS_COMMIT: case self::TYPE_DREV_HAS_COMMIT: return '%s removed %d commit(s): %s.'; case self::TYPE_TASK_DEPENDS_ON_TASK: return '%s removed %d dependencie(s): %s.'; case self::TYPE_TASK_DEPENDED_ON_BY_TASK: return '%s removed %d dependent task(s): %s.'; case self::TYPE_COMMIT_HAS_TASK: case self::TYPE_DREV_HAS_RELATED_TASK: case self::TYPE_MOCK_HAS_TASK: return '%s removed %d task(s): %s.'; case self::TYPE_DREV_DEPENDS_ON_DREV: case self::TYPE_DREV_DEPENDED_ON_BY_DREV: case self::TYPE_TASK_HAS_RELATED_DREV: case self::TYPE_COMMIT_HAS_DREV: case self::TYPE_REVIEWER_FOR_DREV: return '%s removed %d revision(s): %s.'; case self::TYPE_BLOG_HAS_POST: return '%s removed %d post(s): %s.'; case self::TYPE_POST_HAS_BLOG: case self::TYPE_BLOGGER_HAS_BLOG: return '%s removed %d blog(s): %s.'; case self::TYPE_BLOG_HAS_BLOGGER: return '%s removed %d blogger(s): %s.'; case self::TYPE_PROJ_MEMBER: return '%s removed %d member(s): %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: case self::TYPE_OBJECT_HAS_PROJECT: return '%s removed %d project(s): %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: return '%s removed %d voting user(s): %s.'; case self::TYPE_VOTING_USER_HAS_QUESTION: return '%s removed %d question(s): %s.'; case self::TYPE_VOTING_USER_HAS_ANSWER: return '%s removed %d answer(s): %s.'; case self::TYPE_OBJECT_HAS_SUBSCRIBER: return '%s removed %d subscriber(s): %s.'; case self::TYPE_OBJECT_HAS_UNSUBSCRIBER: return '%s removed %d unsubcriber(s): %s.'; case self::TYPE_OBJECT_HAS_FILE: return '%s removed %d file(s): %s.'; case self::TYPE_ACCOUNT_HAS_MEMBER: return '%s removed %d member(s): %s.'; case self::TYPE_MEMBER_HAS_ACCOUNT: return '%s removed %d account(s): %s.'; case self::TYPE_PURCAHSE_HAS_CHARGE: return '%s removed %d charge(s): %s.'; case self::TYPE_CHARGE_HAS_PURCHASE: return '%s removed %d purchase(s): %s.'; case self::TYPE_OBJECT_HAS_CONTRIBUTOR: return '%s removed %d contributor(s): %s.'; case self::TYPE_DREV_HAS_REVIEWER: return '%s removed %d reviewer(s): %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s removed %d mock(s): %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: case self::TYPE_PROJECT_HAS_OBJECT: default: return '%s removed %d object(s): %s.'; } } public static function getFeedStringForEdgeType($type) { switch ($type) { case self::TYPE_TASK_HAS_COMMIT: case self::TYPE_PROJECT_HAS_COMMIT: case self::TYPE_DREV_HAS_COMMIT: return '%s updated commits of %s.'; case self::TYPE_COMMIT_HAS_TASK: case self::TYPE_TASK_DEPENDS_ON_TASK: case self::TYPE_TASK_DEPENDED_ON_BY_TASK: case self::TYPE_DREV_HAS_RELATED_TASK: case self::TYPE_MOCK_HAS_TASK: return '%s updated tasks of %s.'; case self::TYPE_DREV_DEPENDS_ON_DREV: case self::TYPE_DREV_DEPENDED_ON_BY_DREV: case self::TYPE_TASK_HAS_RELATED_DREV: case self::TYPE_COMMIT_HAS_DREV: case self::TYPE_REVIEWER_FOR_DREV: return '%s updated revisions of %s.'; case self::TYPE_BLOG_HAS_POST: return '%s updated posts of %s.'; case self::TYPE_POST_HAS_BLOG: case self::TYPE_BLOGGER_HAS_BLOG: return '%s updated blogs of %s.'; case self::TYPE_BLOG_HAS_BLOGGER: return '%s updated bloggers of %s.'; case self::TYPE_PROJ_MEMBER: return '%s updated members of %s.'; case self::TYPE_MEMBER_OF_PROJ: case self::TYPE_COMMIT_HAS_PROJECT: case self::TYPE_OBJECT_HAS_PROJECT: return '%s updated projects of %s.'; case self::TYPE_QUESTION_HAS_VOTING_USER: case self::TYPE_ANSWER_HAS_VOTING_USER: return '%s updated voting users of %s.'; case self::TYPE_VOTING_USER_HAS_QUESTION: return '%s updated questions of %s.'; case self::TYPE_VOTING_USER_HAS_ANSWER: return '%s updated answers of %s.'; case self::TYPE_OBJECT_HAS_SUBSCRIBER: return '%s updated subscribers of %s.'; case self::TYPE_OBJECT_HAS_UNSUBSCRIBER: return '%s updated unsubcribers of %s.'; case self::TYPE_OBJECT_HAS_FILE: return '%s updated files of %s.'; case self::TYPE_ACCOUNT_HAS_MEMBER: return '%s updated members of %s.'; case self::TYPE_MEMBER_HAS_ACCOUNT: return '%s updated accounts of %s.'; case self::TYPE_PURCAHSE_HAS_CHARGE: return '%s updated charges of %s.'; case self::TYPE_CHARGE_HAS_PURCHASE: return '%s updated purchases of %s.'; case self::TYPE_OBJECT_HAS_CONTRIBUTOR: return '%s updated contributors of %s.'; case self::TYPE_DREV_HAS_REVIEWER: return '%s updated reviewers of %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s updated mocks of %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: case self::TYPE_CONTRIBUTED_TO_OBJECT: case self::TYPE_PROJECT_HAS_OBJECT: default: return '%s updated objects of %s.'; } } }