public function processRequest()
 {
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $project = id(new PhabricatorProjectQuery())->setViewer($viewer)->withIDs(array($this->id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
     if (!$project) {
         return new Aphront404Response();
     }
     $edit_uri = $this->getApplicationURI('edit/' . $project->getID() . '/');
     require_celerity_resource('project-icon-css');
     Javelin::initBehavior('phabricator-tooltips');
     $project_icons = PhabricatorProjectIcon::getIconMap();
     if ($request->isFormPost()) {
         $v_icon = $request->getStr('icon');
         return id(new AphrontAjaxResponse())->setContent(array('value' => $v_icon, 'display' => PhabricatorProjectIcon::renderIconForChooser($v_icon)));
     }
     $ii = 0;
     $buttons = array();
     foreach ($project_icons as $icon => $label) {
         $view = id(new PHUIIconView())->setIconFont($icon);
         $aural = javelin_tag('span', array('aural' => true), pht('Choose "%s" Icon', $label));
         if ($icon == $project->getIcon()) {
             $class_extra = ' selected';
         } else {
             $class_extra = null;
         }
         $buttons[] = javelin_tag('button', array('class' => 'icon-button' . $class_extra, 'name' => 'icon', 'value' => $icon, 'type' => 'submit', 'sigil' => 'has-tooltip', 'meta' => array('tip' => $label)), array($aural, $view));
         if (++$ii % 4 == 0) {
             $buttons[] = phutil_tag('br');
         }
     }
     $buttons = phutil_tag('div', array('class' => 'icon-grid'), $buttons);
     return $this->newDialog()->setTitle(pht('Choose Project Icon'))->appendChild($buttons)->addCancelButton($edit_uri);
 }
 protected function buildProjectInfoDictionaries(array $projects)
 {
     assert_instances_of($projects, 'PhabricatorProject');
     if (!$projects) {
         return array();
     }
     $result = array();
     foreach ($projects as $project) {
         $member_phids = $project->getMemberPHIDs();
         $member_phids = array_values($member_phids);
         $project_slugs = $project->getSlugs();
         $project_slugs = array_values(mpull($project_slugs, 'getSlug'));
         $project_icon = PhabricatorProjectIcon::getAPIName($project->getIcon());
         $result[$project->getPHID()] = array('id' => $project->getID(), 'phid' => $project->getPHID(), 'name' => $project->getName(), 'profileImagePHID' => $project->getProfileImagePHID(), 'icon' => $project_icon, 'color' => $project->getColor(), 'members' => $member_phids, 'slugs' => $project_slugs, 'dateCreated' => $project->getDateCreated(), 'dateModified' => $project->getDateModified());
     }
     return $result;
 }
 public function buildSearchForm(AphrontFormView $form, PhabricatorSavedQuery $saved)
 {
     $phids = $saved->getParameter('memberPHIDs', array());
     $member_handles = id(new PhabricatorHandleQuery())->setViewer($this->requireViewer())->withPHIDs($phids)->execute();
     $status = $saved->getParameter('status');
     $name_match = $saved->getParameter('name');
     $icons = array_fuse($saved->getParameter('icons', array()));
     $colors = array_fuse($saved->getParameter('colors', array()));
     $icon_control = id(new AphrontFormCheckboxControl())->setLabel(pht('Icons'));
     foreach (PhabricatorProjectIcon::getIconMap() as $icon => $name) {
         $image = id(new PHUIIconView())->setIconFont($icon);
         $icon_control->addCheckbox('icons[]', $icon, array($image, ' ', $name), isset($icons[$icon]));
     }
     $color_control = id(new AphrontFormCheckboxControl())->setLabel(pht('Colors'));
     foreach (PhabricatorProjectIcon::getColorMap() as $color => $name) {
         $tag = id(new PHUITagView())->setType(PHUITagView::TYPE_SHADE)->setShade($color)->setName($name);
         $color_control->addCheckbox('colors[]', $color, $tag, isset($colors[$color]));
     }
     $form->appendChild(id(new AphrontFormTextControl())->setName('name')->setLabel(pht('Name'))->setValue($name_match))->appendChild(id(new AphrontFormTokenizerControl())->setDatasource(new PhabricatorPeopleDatasource())->setName('members')->setLabel(pht('Members'))->setValue($member_handles))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Status'))->setName('status')->setOptions($this->getStatusOptions())->setValue($status))->appendChild($icon_control)->appendChild($color_control);
     $this->appendCustomFieldsToForm($form, $saved);
 }
 public function getTitleForFeed()
 {
     $author_phid = $this->getAuthorPHID();
     $object_phid = $this->getObjectPHID();
     $author_handle = $this->renderHandleLink($author_phid);
     $object_handle = $this->renderHandleLink($object_phid);
     $old = $this->getOldValue();
     $new = $this->getNewValue();
     switch ($this->getTransactionType()) {
         case self::TYPE_NAME:
             if ($old === null) {
                 return pht('%s created %s.', $author_handle, $object_handle);
             } else {
                 return pht('%s renamed %s from "%s" to "%s".', $author_handle, $object_handle, $old, $new);
             }
         case self::TYPE_STATUS:
             if ($old == 0) {
                 return pht('%s archived %s.', $author_handle, $object_handle);
             } else {
                 return pht('%s activated %s.', $author_handle, $object_handle);
             }
         case self::TYPE_IMAGE:
             // TODO: Some day, it would be nice to show the images.
             if (!$old) {
                 return pht('%s set the image for %s to %s.', $author_handle, $object_handle, $this->renderHandleLink($new));
             } else {
                 if (!$new) {
                     return pht('%s removed the image for %s.', $author_handle, $object_handle);
                 } else {
                     return pht('%s updated the image for %s from %s to %s.', $author_handle, $object_handle, $this->renderHandleLink($old), $this->renderHandleLink($new));
                 }
             }
         case self::TYPE_ICON:
             return pht('%s set the icon for %s to %s.', $author_handle, $object_handle, PhabricatorProjectIcon::getLabel($new));
         case self::TYPE_COLOR:
             return pht('%s set the color for %s to %s.', $author_handle, $object_handle, PHUITagView::getShadeName($new));
         case self::TYPE_LOCKED:
             if ($new) {
                 return pht('%s locked %s membership.', $author_handle, $object_handle);
             } else {
                 return pht('%s unlocked %s membership.', $author_handle, $object_handle);
             }
         case self::TYPE_SLUGS:
             $add = array_diff($new, $old);
             $rem = array_diff($old, $new);
             if ($add && $rem) {
                 return pht('%s changed %s hashtag(s), added %d: %s; removed %d: %s.', $author_handle, $object_handle, count($add), $this->renderSlugList($add), count($rem), $this->renderSlugList($rem));
             } else {
                 if ($add) {
                     return pht('%s added %d %s hashtag(s): %s.', $author_handle, count($add), $object_handle, $this->renderSlugList($add));
                 } else {
                     if ($rem) {
                         return pht('%s removed %d %s hashtag(s): %s.', $author_handle, count($rem), $object_handle, $this->renderSlugList($rem));
                     }
                 }
             }
     }
     return parent::getTitleForFeed();
 }
 private function getColorOptions()
 {
     $options = array();
     foreach (PhabricatorProjectIcon::getColorMap() as $color => $name) {
         $options[$color] = array(id(new PHUITagView())->setType(PHUITagView::TYPE_SHADE)->setShade($color)->setName($name), ' ', $name);
     }
     return $options;
 }
 public static function renderIconForChooser($icon)
 {
     $project_icons = PhabricatorProjectIcon::getIconMap();
     return phutil_tag('span', array(), array(id(new PHUIIconView())->setIconFont($icon), ' ', idx($project_icons, $icon, pht('Unknown Icon'))));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $viewer = $request->getUser();
     if ($this->id) {
         $id = $request->getURIData('id');
         $is_new = false;
         $project = id(new PhabricatorProjectQuery())->setViewer($viewer)->withIDs(array($this->id))->needSlugs(true)->needImages(true)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
         if (!$project) {
             return new Aphront404Response();
         }
     } else {
         $is_new = true;
         $this->requireApplicationCapability(ProjectCreateProjectsCapability::CAPABILITY);
         $project = PhabricatorProject::initializeNewProject($viewer);
     }
     $field_list = PhabricatorCustomField::getObjectFields($project, PhabricatorCustomField::ROLE_EDIT);
     $field_list->setViewer($viewer)->readFieldsFromStorage($project);
     $e_name = true;
     $e_slugs = false;
     $e_edit = null;
     $v_name = $project->getName();
     $project_slugs = $project->getSlugs();
     $project_slugs = mpull($project_slugs, 'getSlug', 'getSlug');
     $v_primary_slug = $project->getPrimarySlug();
     unset($project_slugs[$v_primary_slug]);
     $v_slugs = $project_slugs;
     $v_color = $project->getColor();
     $v_icon = $project->getIcon();
     $v_locked = $project->getIsMembershipLocked();
     $validation_exception = null;
     if ($request->isFormPost()) {
         $e_name = null;
         $e_slugs = null;
         $v_name = $request->getStr('name');
         $v_slugs = $request->getStrList('slugs');
         $v_view = $request->getStr('can_view');
         $v_edit = $request->getStr('can_edit');
         $v_join = $request->getStr('can_join');
         $v_color = $request->getStr('color');
         $v_icon = $request->getStr('icon');
         $v_locked = $request->getInt('is_membership_locked', 0);
         $type_name = PhabricatorProjectTransaction::TYPE_NAME;
         $type_slugs = PhabricatorProjectTransaction::TYPE_SLUGS;
         $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
         $type_icon = PhabricatorProjectTransaction::TYPE_ICON;
         $type_color = PhabricatorProjectTransaction::TYPE_COLOR;
         $type_locked = PhabricatorProjectTransaction::TYPE_LOCKED;
         $xactions = array();
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_name)->setNewValue($v_name);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_slugs)->setNewValue($v_slugs);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($v_view);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_edit)->setNewValue($v_edit);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY)->setNewValue($v_join);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_icon)->setNewValue($v_icon);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_color)->setNewValue($v_color);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_locked)->setNewValue($v_locked);
         $xactions = array_merge($xactions, $field_list->buildFieldTransactionsFromRequest(new PhabricatorProjectTransaction(), $request));
         $editor = id(new PhabricatorProjectTransactionEditor())->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true);
         if ($is_new) {
             $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', PhabricatorProjectProjectHasMemberEdgeType::EDGECONST)->setNewValue(array('+' => array($viewer->getPHID() => $viewer->getPHID())));
         }
         try {
             $editor->applyTransactions($project, $xactions);
             if ($request->isAjax()) {
                 return id(new AphrontAjaxResponse())->setContent(array('phid' => $project->getPHID(), 'name' => $project->getName()));
             }
             $redirect_uri = $this->getApplicationURI('profile/' . $project->getID() . '/');
             return id(new AphrontRedirectResponse())->setURI($redirect_uri);
         } catch (PhabricatorApplicationTransactionValidationException $ex) {
             $validation_exception = $ex;
             $e_name = $ex->getShortMessage($type_name);
             $e_slugs = $ex->getShortMessage($type_slugs);
             $e_edit = $ex->getShortMessage($type_edit);
             $project->setViewPolicy($v_view);
             $project->setEditPolicy($v_edit);
             $project->setJoinPolicy($v_join);
         }
     }
     if ($is_new) {
         $header_name = pht('Create a New Project');
         $title = pht('Create Project');
     } else {
         $header_name = pht('Edit Project');
         $title = pht('Edit Project');
     }
     $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($project)->execute();
     $v_slugs = implode(', ', $v_slugs);
     $form = id(new AphrontFormView())->setUser($viewer)->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Name'))->setName('name')->setValue($v_name)->setError($e_name));
     $field_list->appendFieldsToForm($form);
     $shades = PhabricatorProjectIcon::getColorMap();
     if ($is_new) {
         $icon_uri = $this->getApplicationURI('icon/');
     } else {
         $icon_uri = $this->getApplicationURI('icon/' . $project->getID() . '/');
     }
     $icon_display = PhabricatorProjectIcon::renderIconForChooser($v_icon);
     list($can_lock, $lock_message) = $this->explainApplicationCapability(ProjectCanLockProjectsCapability::CAPABILITY, pht('You can update the Lock Project setting.'), pht('You can not update the Lock Project setting.'));
     $form->appendChild(id(new AphrontFormChooseButtonControl())->setLabel(pht('Icon'))->setName('icon')->setDisplayValue($icon_display)->setButtonText(pht('Choose Icon...'))->setChooseURI($icon_uri)->setValue($v_icon))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Color'))->setName('color')->setValue($v_color)->setOptions($shades));
     if (!$is_new) {
         $form->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Primary Hashtag'))->setCaption(pht('The primary hashtag is derived from the name.'))->setValue($v_primary_slug));
     }
     $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Additional Hashtags'))->setCaption(pht('Specify a comma-separated list of additional hashtags.'))->setName('slugs')->setValue($v_slugs)->setError($e_slugs))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('can_view')->setPolicyObject($project)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_VIEW))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('can_edit')->setPolicyObject($project)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setError($e_edit))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('can_join')->setCaption(pht('Users who can edit a project can always join a project.'))->setPolicyObject($project)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_JOIN))->appendChild(id(new AphrontFormCheckboxControl())->setLabel(pht('Lock Project'))->setDisabled(!$can_lock)->addCheckbox('is_membership_locked', 1, pht('Prevent members from leaving this project.'), $v_locked)->setCaption($lock_message));
     if ($request->isAjax()) {
         $errors = array();
         if ($validation_exception) {
             $errors = mpull($ex->getErrors(), 'getMessage');
         }
         $dialog = id(new AphrontDialogView())->setUser($viewer)->setWidth(AphrontDialogView::WIDTH_FULL)->setTitle($header_name)->setErrors($errors)->appendForm($form)->addSubmitButton($title)->addCancelButton('/project/');
         return id(new AphrontDialogResponse())->setDialog($dialog);
     }
     $form->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($this->getApplicationURI())->setValue(pht('Save')));
     $form_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setValidationException($validation_exception)->setForm($form);
     if (!$is_new) {
         $nav = $this->buildIconNavView($project);
         $nav->selectFilter("details/{$id}/");
         $nav->appendChild($form_box);
     } else {
         $nav = array($form_box);
     }
     return $this->buildApplicationPage($nav, array('title' => $title));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $viewer = $request->getUser();
     $project = id(new PhabricatorProjectQuery())->setViewer($viewer)->withIDs(array($this->id))->needSlugs(true)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
     if (!$project) {
         return new Aphront404Response();
     }
     $field_list = PhabricatorCustomField::getObjectFields($project, PhabricatorCustomField::ROLE_EDIT);
     $field_list->setViewer($viewer)->readFieldsFromStorage($project);
     $view_uri = $this->getApplicationURI('view/' . $project->getID() . '/');
     $edit_uri = $this->getApplicationURI('edit/' . $project->getID() . '/');
     $e_name = true;
     $e_slugs = false;
     $e_edit = null;
     $v_name = $project->getName();
     $project_slugs = $project->getSlugs();
     $project_slugs = mpull($project_slugs, 'getSlug', 'getSlug');
     $v_primary_slug = $project->getPrimarySlug();
     unset($project_slugs[$v_primary_slug]);
     $v_slugs = $project_slugs;
     $v_color = $project->getColor();
     $v_icon = $project->getIcon();
     $validation_exception = null;
     if ($request->isFormPost()) {
         $e_name = null;
         $e_slugs = null;
         $v_name = $request->getStr('name');
         $v_slugs = $request->getStrList('slugs');
         $v_view = $request->getStr('can_view');
         $v_edit = $request->getStr('can_edit');
         $v_join = $request->getStr('can_join');
         $v_color = $request->getStr('color');
         $v_icon = $request->getStr('icon');
         $xactions = $field_list->buildFieldTransactionsFromRequest(new PhabricatorProjectTransaction(), $request);
         $type_name = PhabricatorProjectTransaction::TYPE_NAME;
         $type_slugs = PhabricatorProjectTransaction::TYPE_SLUGS;
         $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
         $type_icon = PhabricatorProjectTransaction::TYPE_ICON;
         $type_color = PhabricatorProjectTransaction::TYPE_COLOR;
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_name)->setNewValue($v_name);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_slugs)->setNewValue($v_slugs);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($v_view);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_edit)->setNewValue($v_edit);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY)->setNewValue($v_join);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_icon)->setNewValue($v_icon);
         $xactions[] = id(new PhabricatorProjectTransaction())->setTransactionType($type_color)->setNewValue($v_color);
         $editor = id(new PhabricatorProjectTransactionEditor())->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true);
         try {
             $editor->applyTransactions($project, $xactions);
             return id(new AphrontRedirectResponse())->setURI($edit_uri);
         } catch (PhabricatorApplicationTransactionValidationException $ex) {
             $validation_exception = $ex;
             $e_name = $ex->getShortMessage($type_name);
             $e_slugs = $ex->getShortMessage($type_slugs);
             $e_edit = $ex->getShortMessage($type_edit);
             $project->setViewPolicy($v_view);
             $project->setEditPolicy($v_edit);
             $project->setJoinPolicy($v_join);
         }
     }
     $header_name = pht('Edit Project');
     $title = pht('Edit Project');
     $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($project)->execute();
     $v_slugs = implode(', ', $v_slugs);
     $form = new AphrontFormView();
     $form->setUser($viewer)->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Name'))->setName('name')->setValue($v_name)->setError($e_name));
     $field_list->appendFieldsToForm($form);
     $shades = PhabricatorProjectIcon::getColorMap();
     $icon_uri = $this->getApplicationURI('icon/' . $project->getID() . '/');
     $icon_display = PhabricatorProjectIcon::renderIconForChooser($v_icon);
     $form->appendChild(id(new AphrontFormChooseButtonControl())->setLabel(pht('Icon'))->setName('icon')->setDisplayValue($icon_display)->setButtonText(pht('Choose Icon...'))->setChooseURI($icon_uri)->setValue($v_icon))->appendChild(id(new AphrontFormSelectControl())->setLabel(pht('Color'))->setName('color')->setValue($v_color)->setOptions($shades))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Primary Hashtag'))->setCaption(pht('The primary hashtag is derived from the name.'))->setValue($v_primary_slug))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Additional Hashtags'))->setCaption(pht('Specify a comma-separated list of additional hashtags.'))->setName('slugs')->setValue($v_slugs)->setError($e_slugs))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('can_view')->setPolicyObject($project)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_VIEW))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('can_edit')->setPolicyObject($project)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setError($e_edit))->appendChild(id(new AphrontFormPolicyControl())->setUser($viewer)->setName('can_join')->setCaption(pht('Users who can edit a project can always join a project.'))->setPolicyObject($project)->setPolicies($policies)->setCapability(PhabricatorPolicyCapability::CAN_JOIN))->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($edit_uri)->setValue(pht('Save')));
     $form_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setValidationException($validation_exception)->setForm($form);
     $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView())->addTextCrumb($project->getName(), $view_uri)->addTextCrumb(pht('Edit'), $edit_uri)->addTextCrumb(pht('Details'));
     return $this->buildApplicationPage(array($crumbs, $form_box), array('title' => $title));
 }
 public function getTitle()
 {
     $old = $this->getOldValue();
     $new = $this->getNewValue();
     $author_handle = $this->renderHandleLink($this->getAuthorPHID());
     switch ($this->getTransactionType()) {
         case PhabricatorProjectTransaction::TYPE_NAME:
             if ($old === null) {
                 return pht('%s created this project.', $author_handle);
             } else {
                 return pht('%s renamed this project from "%s" to "%s".', $author_handle, $old, $new);
             }
         case PhabricatorProjectTransaction::TYPE_STATUS:
             if ($old == 0) {
                 return pht('%s closed this project.', $author_handle);
             } else {
                 return pht('%s reopened this project.', $author_handle);
             }
         case PhabricatorProjectTransaction::TYPE_IMAGE:
             // TODO: Some day, it would be nice to show the images.
             if (!$old) {
                 return pht('%s set this project\'s image to %s.', $author_handle, $this->renderHandleLink($new));
             } else {
                 if (!$new) {
                     return pht('%s removed this project\'s image.', $author_handle);
                 } else {
                     return pht('%s updated this project\'s image from %s to %s.', $author_handle, $this->renderHandleLink($old), $this->renderHandleLink($new));
                 }
             }
         case PhabricatorProjectTransaction::TYPE_ICON:
             return pht('%s set this project\'s icon to %s.', $author_handle, PhabricatorProjectIcon::getLabel($new));
         case PhabricatorProjectTransaction::TYPE_COLOR:
             return pht('%s set this project\'s color to %s.', $author_handle, PHUITagView::getShadeName($new));
         case PhabricatorProjectTransaction::TYPE_SLUGS:
             $add = array_diff($new, $old);
             $rem = array_diff($old, $new);
             if ($add && $rem) {
                 return pht('%s changed project hashtag(s), added %d: %s; removed %d: %s', $author_handle, count($add), $this->renderSlugList($add), count($rem), $this->renderSlugList($rem));
             } else {
                 if ($add) {
                     return pht('%s added %d project hashtag(s): %s', $author_handle, count($add), $this->renderSlugList($add));
                 } else {
                     if ($rem) {
                         return pht('%s removed %d project hashtag(s): %s', $author_handle, count($rem), $this->renderSlugList($rem));
                     }
                 }
             }
         case PhabricatorProjectTransaction::TYPE_MEMBERS:
             $add = array_diff($new, $old);
             $rem = array_diff($old, $new);
             if ($add && $rem) {
                 return pht('%s changed project member(s), added %d: %s; removed %d: %s', $author_handle, count($add), $this->renderHandleList($add), count($rem), $this->renderHandleList($rem));
             } else {
                 if ($add) {
                     if (count($add) == 1 && head($add) == $this->getAuthorPHID()) {
                         return pht('%s joined this project.', $author_handle);
                     } else {
                         return pht('%s added %d project member(s): %s', $author_handle, count($add), $this->renderHandleList($add));
                     }
                 } else {
                     if ($rem) {
                         if (count($rem) == 1 && head($rem) == $this->getAuthorPHID()) {
                             return pht('%s left this project.', $author_handle);
                         } else {
                             return pht('%s removed %d project member(s): %s', $author_handle, count($rem), $this->renderHandleList($rem));
                         }
                     }
                 }
             }
     }
     return parent::getTitle();
 }