public function buildCustomEditFields(PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object)
 {
     $viewer = $engine->getViewer();
     $editor = $object->getApplicationTransactionEditor();
     $types = $editor->getTransactionTypesForObject($object);
     $types = array_fuse($types);
     $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($object)->execute();
     $map = array(PhabricatorTransactions::TYPE_VIEW_POLICY => array('key' => 'policy.view', 'aliases' => array('view'), 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 'label' => pht('View Policy'), 'description' => pht('Controls who can view the object.'), 'edit' => 'view'), PhabricatorTransactions::TYPE_EDIT_POLICY => array('key' => 'policy.edit', 'aliases' => array('edit'), 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 'label' => pht('Edit Policy'), 'description' => pht('Controls who can edit the object.'), 'edit' => 'edit'), PhabricatorTransactions::TYPE_JOIN_POLICY => array('key' => 'policy.join', 'aliases' => array('join'), 'capability' => PhabricatorPolicyCapability::CAN_JOIN, 'label' => pht('Join Policy'), 'description' => pht('Controls who can join the object.'), 'edit' => 'join'));
     $fields = array();
     foreach ($map as $type => $spec) {
         if (empty($types[$type])) {
             continue;
         }
         $capability = $spec['capability'];
         $key = $spec['key'];
         $aliases = $spec['aliases'];
         $label = $spec['label'];
         $description = $spec['description'];
         $edit = $spec['edit'];
         $policy_field = id(new PhabricatorPolicyEditField())->setKey($key)->setLabel($label)->setDescription($description)->setAliases($aliases)->setIsCopyable(true)->setCapability($capability)->setPolicies($policies)->setTransactionType($type)->setEditTypeKey($edit)->setValue($object->getPolicy($capability));
         $fields[] = $policy_field;
         if (!$object instanceof PhabricatorSpacesInterface) {
             if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
                 $type_space = PhabricatorTransactions::TYPE_SPACE;
                 if (isset($types[$type_space])) {
                     $space_field = id(new PhabricatorSpaceEditField())->setKey('spacePHID')->setLabel(pht('Space'))->setEditTypeKey('space')->setDescription(pht('Shifts the object in the Spaces application.'))->setIsCopyable(true)->setIsReorderable(false)->setAliases(array('space', 'policy.space'))->setTransactionType($type_space)->setValue($object->getSpacePHID());
                     $fields[] = $space_field;
                     $policy_field->setSpaceField($space_field);
                 }
             }
         }
     }
     return $fields;
 }
 public function newEditEngineFields(PhabricatorEditEngine $engine, PhabricatorCalendarImport $import)
 {
     $fields = array();
     if ($engine->getIsCreate()) {
         $fields[] = id(new PhabricatorTextEditField())->setKey('uri')->setLabel(pht('URI'))->setDescription(pht('URI to import.'))->setTransactionType(PhabricatorCalendarImportICSURITransaction::TRANSACTIONTYPE)->setConduitDescription(pht('URI to import.'))->setConduitTypeDescription(pht('New URI.'));
     }
     return $fields;
 }
 public function newEditEngineFields(PhabricatorEditEngine $engine, PhabricatorCalendarImport $import)
 {
     $fields = array();
     if ($engine->getIsCreate()) {
         $fields[] = id(new PhabricatorFileEditField())->setKey('icsFilePHID')->setLabel(pht('ICS File'))->setDescription(pht('ICS file to import.'))->setTransactionType(PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE)->setConduitDescription(pht('File PHID to import.'))->setConduitTypeDescription(pht('File PHID.'));
     }
     return $fields;
 }
 private function buildCurtain(ManiphestTask $task, PhabricatorEditEngine $edit_engine)
 {
     $viewer = $this->getViewer();
     $id = $task->getID();
     $phid = $task->getPHID();
     $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $task, PhabricatorPolicyCapability::CAN_EDIT);
     $curtain = $this->newCurtainView($task);
     $curtain->addAction(id(new PhabricatorActionView())->setName(pht('Edit Task'))->setIcon('fa-pencil')->setHref($this->getApplicationURI("/task/edit/{$id}/"))->setDisabled(!$can_edit)->setWorkflow(!$can_edit));
     $edit_config = $edit_engine->loadDefaultEditConfiguration();
     $can_create = (bool) $edit_config;
     $can_reassign = $edit_engine->hasEditAccessToTransaction(ManiphestTransaction::TYPE_OWNER);
     if ($can_create) {
         $form_key = $edit_config->getIdentifier();
         $edit_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))->setQueryParam('parent', $id)->setQueryParam('template', $id)->setQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
         $edit_uri = $this->getApplicationURI($edit_uri);
     } else {
         // TODO: This will usually give us a somewhat-reasonable error page, but
         // could be a bit cleaner.
         $edit_uri = "/task/edit/{$id}/";
         $edit_uri = $this->getApplicationURI($edit_uri);
     }
     $subtask_item = id(new PhabricatorActionView())->setName(pht('Create Subtask'))->setHref($edit_uri)->setIcon('fa-level-down')->setDisabled(!$can_create)->setWorkflow(!$can_create);
     $relationship_list = PhabricatorObjectRelationshipList::newForObject($viewer, $task);
     $submenu_actions = array($subtask_item, ManiphestTaskHasParentRelationship::RELATIONSHIPKEY, ManiphestTaskHasSubtaskRelationship::RELATIONSHIPKEY, ManiphestTaskMergeInRelationship::RELATIONSHIPKEY, ManiphestTaskCloseAsDuplicateRelationship::RELATIONSHIPKEY);
     $task_submenu = $relationship_list->newActionSubmenu($submenu_actions)->setName(pht('Edit Related Tasks...'))->setIcon('fa-anchor');
     $curtain->addAction($task_submenu);
     $relationship_submenu = $relationship_list->newActionMenu();
     if ($relationship_submenu) {
         $curtain->addAction($relationship_submenu);
     }
     $owner_phid = $task->getOwnerPHID();
     $author_phid = $task->getAuthorPHID();
     $handles = $viewer->loadHandles(array($owner_phid, $author_phid));
     if ($owner_phid) {
         $image_uri = $handles[$owner_phid]->getImageURI();
         $image_href = $handles[$owner_phid]->getURI();
         $owner = $viewer->renderHandle($owner_phid)->render();
         $content = phutil_tag('strong', array(), $owner);
         $assigned_to = id(new PHUIHeadThingView())->setImage($image_uri)->setImageHref($image_href)->setContent($content);
     } else {
         $assigned_to = phutil_tag('em', array(), pht('None'));
     }
     $curtain->newPanel()->setHeaderText(pht('Assigned To'))->appendChild($assigned_to);
     $author_uri = $handles[$author_phid]->getImageURI();
     $author_href = $handles[$author_phid]->getURI();
     $author = $viewer->renderHandle($author_phid)->render();
     $content = phutil_tag('strong', array(), $author);
     $date = phabricator_date($task->getDateCreated(), $viewer);
     $content = pht('%s, %s', $content, $date);
     $authored_by = id(new PHUIHeadThingView())->setImage($author_uri)->setImageHref($author_href)->setContent($content);
     $curtain->newPanel()->setHeaderText(pht('Authored By'))->appendChild($authored_by);
     return $curtain;
 }
 public function getQuickMenuItems()
 {
     $viewer = $this->getViewer();
     $engines = PhabricatorEditEngine::getAllEditEngines();
     foreach ($engines as $key => $engine) {
         if (!$engine->hasQuickCreateActions()) {
             unset($engines[$key]);
         }
     }
     if (!$engines) {
         return array();
     }
     $engine_keys = array_keys($engines);
     $configs = id(new PhabricatorEditEngineConfigurationQuery())->setViewer($viewer)->withEngineKeys($engine_keys)->withIsDefault(true)->withIsDisabled(false)->execute();
     $configs = msort($configs, 'getCreateSortKey');
     $configs = mgroup($configs, 'getEngineKey');
     $items = array();
     foreach ($engines as $key => $engine) {
         $engine_configs = idx($configs, $key, array());
         $engine_items = $engine->newQuickCreateActions($engine_configs);
         foreach ($engine_items as $engine_item) {
             $items[] = $engine_item;
         }
     }
     return $items;
 }
 public function buildCustomEditFields(PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object)
 {
     $subscribers_type = PhabricatorTransactions::TYPE_SUBSCRIBERS;
     $object_phid = $object->getPHID();
     if ($object_phid) {
         $sub_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID($object_phid);
     } else {
         $sub_phids = array();
     }
     $subscribers_field = id(new PhabricatorSubscribersEditField())->setKey('subscriberPHIDs')->setLabel(pht('Subscribers'))->setEditTypeKey('subscribers')->setAliases(array('subscriber', 'subscribers'))->setIsCopyable(true)->setUseEdgeTransactions(true)->setCommentActionLabel(pht('Change Subscribers'))->setTransactionType($subscribers_type)->setValue($sub_phids);
     $subscribers_field->setViewer($engine->getViewer());
     $edit_add = $subscribers_field->getConduitEditType('subscribers.add')->setConduitDescription(pht('Add subscribers.'));
     $edit_set = $subscribers_field->getConduitEditType('subscribers.set')->setConduitDescription(pht('Set subscribers, overwriting current value.'));
     $edit_rem = $subscribers_field->getConduitEditType('subscribers.remove')->setConduitDescription(pht('Remove subscribers.'));
     return array($subscribers_field);
 }
 private function buildCurtain(ManiphestTask $task, PhabricatorEditEngine $edit_engine)
 {
     $viewer = $this->getViewer();
     $id = $task->getID();
     $phid = $task->getPHID();
     $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $task, PhabricatorPolicyCapability::CAN_EDIT);
     $curtain = $this->newCurtainView($task);
     $curtain->addAction(id(new PhabricatorActionView())->setName(pht('Edit Task'))->setIcon('fa-pencil')->setHref($this->getApplicationURI("/task/edit/{$id}/"))->setDisabled(!$can_edit)->setWorkflow(!$can_edit));
     $curtain->addAction(id(new PhabricatorActionView())->setName(pht('Merge Duplicates In'))->setHref("/search/attach/{$phid}/TASK/merge/")->setWorkflow(true)->setIcon('fa-compress')->setDisabled(!$can_edit)->setWorkflow(true));
     $edit_config = $edit_engine->loadDefaultEditConfiguration();
     $can_create = (bool) $edit_config;
     $can_reassign = $edit_engine->hasEditAccessToTransaction(ManiphestTransaction::TYPE_OWNER);
     if ($can_create) {
         $form_key = $edit_config->getIdentifier();
         $edit_uri = id(new PhutilURI("/task/edit/form/{$form_key}/"))->setQueryParam('parent', $id)->setQueryParam('template', $id)->setQueryParam('status', ManiphestTaskStatus::getDefaultStatus());
         $edit_uri = $this->getApplicationURI($edit_uri);
     } else {
         // TODO: This will usually give us a somewhat-reasonable error page, but
         // could be a bit cleaner.
         $edit_uri = "/task/edit/{$id}/";
         $edit_uri = $this->getApplicationURI($edit_uri);
     }
     $curtain->addAction(id(new PhabricatorActionView())->setName(pht('Create Subtask'))->setHref($edit_uri)->setIcon('fa-level-down')->setDisabled(!$can_create)->setWorkflow(!$can_create));
     $curtain->addAction(id(new PhabricatorActionView())->setName(pht('Edit Blocking Tasks'))->setHref("/search/attach/{$phid}/TASK/blocks/")->setWorkflow(true)->setIcon('fa-link')->setDisabled(!$can_edit)->setWorkflow(true));
     $owner_phid = $task->getOwnerPHID();
     $author_phid = $task->getAuthorPHID();
     $handles = $viewer->loadHandles(array($owner_phid, $author_phid));
     if ($owner_phid) {
         $image_uri = $handles[$owner_phid]->getImageURI();
         $image_href = $handles[$owner_phid]->getURI();
         $owner = $viewer->renderHandle($owner_phid)->render();
         $content = phutil_tag('strong', array(), $owner);
         $assigned_to = id(new PHUIHeadThingView())->setImage($image_uri)->setImageHref($image_href)->setContent($content);
     } else {
         $assigned_to = phutil_tag('em', array(), pht('None'));
     }
     $curtain->newPanel()->setHeaderText(pht('Assigned To'))->appendChild($assigned_to);
     $author_uri = $handles[$author_phid]->getImageURI();
     $author_href = $handles[$author_phid]->getURI();
     $author = $viewer->renderHandle($author_phid)->render();
     $content = phutil_tag('strong', array(), $author);
     $date = phabricator_date($task->getDateCreated(), $viewer);
     $content = pht('%s, %s', $content, $date);
     $authored_by = id(new PHUIHeadThingView())->setImage($author_uri)->setImageHref($author_href)->setContent($content);
     $curtain->newPanel()->setHeaderText(pht('Authored By'))->appendChild($authored_by);
     return $curtain;
 }
 public function buildCustomEditFields(PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object)
 {
     $viewer = $this->getViewer();
     $field_list = PhabricatorCustomField::getObjectFields($object, PhabricatorCustomField::ROLE_EDIT);
     $field_list->setViewer($viewer);
     if (!$engine->getIsCreate()) {
         $field_list->readFieldsFromStorage($object);
     }
     $results = array();
     foreach ($field_list->getFields() as $field) {
         $edit_fields = $field->getEditEngineFields($engine);
         foreach ($edit_fields as $edit_field) {
             $results[] = $edit_field;
         }
     }
     return $results;
 }
 protected function loadPage()
 {
     $engines = PhabricatorEditEngine::getAllEditEngines();
     if ($this->engineKeys !== null) {
         $engines = array_select_keys($engines, $this->engineKeys);
     }
     return $engines;
 }
 protected function getCommentViewHeaderText($object)
 {
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
     if (!$is_serious) {
         return pht('Eat Paste');
     }
     return parent::getCommentViewHeaderText($object);
 }
 public function applyConfigurationToFields(PhabricatorEditEngine $engine, array $fields)
 {
     $fields = mpull($fields, null, 'getKey');
     $values = $this->getProperty('defaults', array());
     foreach ($fields as $key => $field) {
         if ($engine->getIsCreate()) {
             if (array_key_exists($key, $values)) {
                 $field->readDefaultValueFromConfiguration($values[$key]);
             }
         }
     }
     $fields = $this->reorderFields($fields);
     $head_instructions = $this->getProperty('instructions.head');
     if (strlen($head_instructions)) {
         $fields = array('config.instructions.head' => id(new PhabricatorInstructionsEditField())->setKey('config.instructions.head')->setValue($head_instructions)) + $fields;
     }
     return $fields;
 }
 protected function buildApplicationCrumbs()
 {
     $viewer = $this->getViewer();
     $crumbs = parent::buildApplicationCrumbs();
     $target_key = $this->getEngineKey();
     $target_engine = PhabricatorEditEngine::getByKey($viewer, $target_key);
     id(new PhabricatorEditEngineConfigurationEditEngine())->setTargetEngine($target_engine)->setViewer($viewer)->addActionToCrumbs($crumbs);
     return $crumbs;
 }
 public function buildCustomEditFields(PhabricatorEditEngine $engine, PhabricatorApplicationTransactionInterface $object)
 {
     $edge_type = PhabricatorTransactions::TYPE_EDGE;
     $project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
     $object_phid = $object->getPHID();
     if ($object_phid) {
         $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs($object_phid, $project_edge_type);
         $project_phids = array_reverse($project_phids);
     } else {
         $project_phids = array();
     }
     $projects_field = id(new PhabricatorProjectsEditField())->setKey('projectPHIDs')->setLabel(pht('Projects'))->setEditTypeKey('projects')->setAliases(array('project', 'projects'))->setIsCopyable(true)->setUseEdgeTransactions(true)->setCommentActionLabel(pht('Change Projects'))->setDescription(pht('Select projects for the object.'))->setTransactionType($edge_type)->setMetadataValue('edge:type', $project_edge_type)->setValue($project_phids);
     $projects_field->setViewer($engine->getViewer());
     $edit_add = $projects_field->getConduitEditType('projects.add')->setConduitDescription(pht('Add projects.'));
     $edit_set = $projects_field->getConduitEditType('projects.set')->setConduitDescription(pht('Set projects, overwriting current value.'));
     $edit_rem = $projects_field->getConduitEditType('projects.remove')->setConduitDescription(pht('Remove projects.'));
     return array($projects_field);
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $target_engine_key = $request->getURIData('engineKey');
     $target_engine = PhabricatorEditEngine::getByKey($viewer, $target_engine_key);
     if (!$target_engine) {
         return new Aphront404Response();
     }
     $this->setEngineKey($target_engine->getEngineKey());
     return id(new PhabricatorEditEngineConfigurationEditEngine())->setTargetEngine($target_engine)->setController($this)->buildResponse();
 }
 protected function buildApplicationCrumbs()
 {
     $crumbs = parent::buildApplicationCrumbs();
     $crumbs->addTextCrumb(pht('Edit Engines'), '/transactions/editengine/');
     $engine_key = $this->getEngineKey();
     if ($engine_key !== null) {
         $engine = PhabricatorEditEngine::getByKey($this->getViewer(), $engine_key);
         if ($engine) {
             $crumbs->addTextCrumb($engine->getEngineName(), "/transactions/editengine/{$engine_key}/");
         }
     }
     return $crumbs;
 }
 public function applyConfigurationToFields(PhabricatorEditEngine $engine, array $fields)
 {
     $fields = mpull($fields, null, 'getKey');
     $values = $this->getProperty('defaults', array());
     foreach ($fields as $key => $field) {
         if ($engine->getIsCreate()) {
             if (array_key_exists($key, $values)) {
                 $field->readDefaultValueFromConfiguration($values[$key]);
             }
         }
     }
     $locks = $this->getFieldLocks();
     foreach ($fields as $field) {
         $key = $field->getKey();
         switch (idx($locks, $key)) {
             case self::LOCK_LOCKED:
                 $field->setIsHidden(false);
                 $field->setIsLocked(true);
                 break;
             case self::LOCK_HIDDEN:
                 $field->setIsHidden(true);
                 $field->setIsLocked(false);
                 break;
             case self::LOCK_VISIBLE:
                 $field->setIsHidden(false);
                 $field->setIsLocked(false);
                 break;
             default:
                 // If we don't have an explicit value, don't make any adjustments.
                 break;
         }
     }
     $fields = $this->reorderFields($fields);
     $preamble = $this->getPreamble();
     if (strlen($preamble)) {
         $fields = array('config.preamble' => id(new PhabricatorInstructionsEditField())->setKey('config.preamble')->setIsReorderable(false)->setIsDefaultable(false)->setIsLockable(false)->setValue($preamble)) + $fields;
     }
     return $fields;
 }
 protected function newEditResponse(AphrontRequest $request, $object, array $xactions)
 {
     if ($request->isAjax()) {
         // Reload the task to make sure we pick up the final task state.
         $viewer = $this->getViewer();
         $task = id(new ManiphestTaskQuery())->setViewer($viewer)->withIDs(array($object->getID()))->needSubscriberPHIDs(true)->needProjectPHIDs(true)->executeOne();
         switch ($request->getStr('responseType')) {
             case 'card':
                 return $this->buildCardResponse($task);
             default:
                 return $this->buildListResponse($task);
         }
     }
     return parent::newEditResponse($request, $object, $xactions);
 }
 protected function loadPage()
 {
     // TODO: The logic here is a little flimsy and won't survive pagination.
     // For now, I'm just not bothering with pagination since I believe it will
     // take some time before any install manages to produce a large enough
     // number of edit forms for any particular engine for the lack of UI
     // pagination to become a problem.
     $page = $this->loadStandardPage($this->newResultObject());
     // Now that we've loaded the real results from the database, we're going
     // to load builtins from the edit engines and add them to the list.
     $engines = PhabricatorEditEngine::getAllEditEngines();
     if ($this->engineKeys) {
         $engines = array_select_keys($engines, $this->engineKeys);
     }
     foreach ($engines as $engine) {
         $engine->setViewer($this->getViewer());
     }
     // List all the builtins which have already been saved to the database as
     // real objects.
     $concrete = array();
     foreach ($page as $config) {
         $builtin_key = $config->getBuiltinKey();
         if ($builtin_key !== null) {
             $engine_key = $config->getEngineKey();
             $concrete[$engine_key][$builtin_key] = $config;
         }
     }
     $builtins = array();
     foreach ($engines as $engine_key => $engine) {
         $engine_builtins = $engine->getBuiltinEngineConfigurations();
         foreach ($engine_builtins as $engine_builtin) {
             $builtin_key = $engine_builtin->getBuiltinKey();
             if (isset($concrete[$engine_key][$builtin_key])) {
                 continue;
             } else {
                 $builtins[] = $engine_builtin;
             }
         }
     }
     foreach ($builtins as $builtin) {
         $page[] = $builtin;
     }
     // Now we have to do some extra filtering to make sure everything we're
     // about to return really satisfies the query.
     if ($this->ids !== null) {
         $ids = array_fuse($this->ids);
         foreach ($page as $key => $config) {
             if (empty($ids[$config->getID()])) {
                 unset($page[$key]);
             }
         }
     }
     if ($this->phids !== null) {
         $phids = array_fuse($this->phids);
         foreach ($page as $key => $config) {
             if (empty($phids[$config->getPHID()])) {
                 unset($page[$key]);
             }
         }
     }
     if ($this->builtinKeys !== null) {
         $builtin_keys = array_fuse($this->builtinKeys);
         foreach ($page as $key => $config) {
             if (empty($builtin_keys[$config->getBuiltinKey()])) {
                 unset($page[$key]);
             }
         }
     }
     if ($this->default !== null) {
         foreach ($page as $key => $config) {
             if ($config->getIsDefault() != $this->default) {
                 unset($page[$key]);
             }
         }
     }
     if ($this->disabled !== null) {
         foreach ($page as $key => $config) {
             if ($config->getIsDisabled() != $this->disabled) {
                 unset($page[$key]);
             }
         }
     }
     if ($this->identifiers !== null) {
         $identifiers = array_fuse($this->identifiers);
         foreach ($page as $key => $config) {
             if (isset($identifiers[$config->getBuiltinKey()])) {
                 continue;
             }
             if (isset($identifiers[$config->getID()])) {
                 continue;
             }
             unset($page[$key]);
         }
     }
     return $page;
 }
 protected function newBuiltinEngineConfigurations()
 {
     $configuration = head(parent::newBuiltinEngineConfigurations());
     // TODO: This whole method is clumsy, and the ordering for the custom
     // field is especially clumsy. Maybe try to make this more natural to
     // express.
     $configuration->setFieldOrder(array('parent', 'milestone', 'name', 'std:project:internal:description', 'icon', 'color', 'slugs'));
     return array($configuration);
 }
 protected function getValidationExceptionShortMessage(PhabricatorApplicationTransactionValidationException $ex, PhabricatorEditField $field)
 {
     // Settings fields all have the same transaction type so we need to make
     // sure the transaction is changing the same setting before matching an
     // error to a given field.
     $xaction_type = $field->getTransactionType();
     if ($xaction_type == PhabricatorUserPreferencesTransaction::TYPE_SETTING) {
         $property = PhabricatorUserPreferencesTransaction::PROPERTY_SETTING;
         $field_setting = idx($field->getMetadata(), $property);
         foreach ($ex->getErrors() as $error) {
             if ($error->getType() !== $xaction_type) {
                 continue;
             }
             $xaction = $error->getTransaction();
             if (!$xaction) {
                 continue;
             }
             $xaction_setting = $xaction->getMetadataValue($property);
             if ($xaction_setting != $field_setting) {
                 continue;
             }
             $short_message = $error->getShortMessage();
             if ($short_message !== null) {
                 return $short_message;
             }
         }
         return null;
     }
     return parent::getValidationExceptionShortMessage($ex, $field);
 }