diff --git a/src/controller/class/SemiStructuredObjectTypeViewController.php b/src/controller/class/SemiStructuredObjectTypeViewController.php index c7f8129..d40251c 100644 --- a/src/controller/class/SemiStructuredObjectTypeViewController.php +++ b/src/controller/class/SemiStructuredObjectTypeViewController.php @@ -1,185 +1,186 @@ getViewer(); $id = $request->getURIData('id'); $object_type = id(new SemiStructuredObjectTypeQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->executeOne(); if (!$object_type) { return new Aphront404Response(); } $this->setObjectType($object_type); $crumbs = $this->buildApplicationCrumbs(); $title = $object_type->getName(); $header = $this->buildHeaderView(); $curtain = $this->buildCurtain(); $details = $this->buildDetailsView(); $timeline = $this->buildTransactionTimeline( $object_type, new SemiStructuredObjectTypeTransactionQuery()); $comment_view = id(new SemiStructuredObjectTypeEditEngine()) ->setViewer($viewer) ->buildEditEngineCommentView($object_type); list($instances_header, $instances_view) = $this->buildInstancesView(); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->setMainColumn(array( $timeline, $comment_view, )) ->addPropertySection(pht('Description'), $details) ->addPropertySection($instances_header, $instances_view); $navigation = $this->buildSideNavView('view'); return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->setPageObjectPHIDs(array($object_type->getPHID())) ->setNavigation($navigation) ->appendChild($view); } private function buildDetailsView() { $viewer = $this->getViewer(); $object_type = $this->getObjectType(); $view = id(new PHUIPropertyListView()) ->setUser($viewer); $description = $object_type->getDescription(); if (strlen($description)) { $view->addTextContent( new PHUIRemarkupView($viewer, $description)); } $custom_fields_def = $object_type->getCustomFieldsConfig(); $custom_fields_def = id(new PhutilJSON()) ->encodeFormatted($custom_fields_def); $view->addProperty( pht('Custom Fields'), phutil_tag('pre', array(), $custom_fields_def)); return $view; } private function buildCurtain() { $viewer = $this->getViewer(); $object_type = $this->getObjectType(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $object_type, PhabricatorPolicyCapability::CAN_EDIT); $can_create_instance = true; $id = $object_type->getID(); $edit_uri = $this->getApplicationURI("/editclass/{$id}/"); $create_uri = $this->getApplicationURI("/type/{$id}/new/"); $archive_uri = $this->getApplicationURI("/type/{$id}/archive/"); $curtain = $this->newCurtainView($object_type); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Object Type')) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setHref($edit_uri)); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Create New Instance')) ->setIcon('fa-plus') ->setDisabled(!$can_create_instance) ->setHref($create_uri)); if ($object_type->isArchived()) { $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Activate Object Type')) ->setIcon('fa-check') ->setDisabled(!$can_edit) ->setWorkflow($can_edit) ->setHref($archive_uri)); } else { $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Archive Object Type')) ->setIcon('fa-ban') ->setDisabled(!$can_edit) ->setWorkflow($can_edit) ->setHref($archive_uri)); } return $curtain; } private function buildInstancesView() { $viewer = $this->getViewer(); $object_type = $this->getObjectType(); $instances = id(new SemiStructuredObjectInstanceQuery()) ->setViewer($viewer) ->setLimit(10) ->withClassPHIDs(array($object_type->getPHID())) ->execute(); $content = id(new SemiStructuredObjectInstanceSearchEngine()) ->setViewer($viewer) ->renderResultsDirectly($instances); // $content is PhabricatorApplicationSearchResultView $box = new PHUIObjectBoxView(); if ($content->getObjectList()) { $box->setObjectList($content->getObjectList()); } if ($content->getTable()) { $box->setTable($content->getTable()); } if ($content->getContent()) { $box->appendChild($content->getContent()); } $header = pht('Instances of this type'); $header = (new PHUIHeaderView()) ->setHeader($header) ->addActionLink((new PHUIButtonView()) - ->setText(pht('All Instances')) + ->setText(pht('Instances')) ->setIcon(SemiStructuredDataApplication::ICON_OBJECT_INSTANCE_MANY) ->setHref('./items/') ->setTag('a') ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE)) ->addActionLink((new PHUIButtonView()) + // TODO check policy ->setText(pht('Create New')) ->setIcon('fa-plus') ->setHref('./new/') ->setTag('a') ->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE)); return array($header, $box); } } diff --git a/src/query/SemiStructuredObjectInstanceSearchEngine.php b/src/query/SemiStructuredObjectInstanceSearchEngine.php index 53e5c29..21601f2 100644 --- a/src/query/SemiStructuredObjectInstanceSearchEngine.php +++ b/src/query/SemiStructuredObjectInstanceSearchEngine.php @@ -1,311 +1,320 @@ objectType = $object_type; return $this; } public function getObjectType() { return $this->objectType; } public function getResultTypeDescription() { return pht('Object Instances'); } public function getApplicationClassName() { return 'SemiStructuredDataApplication'; } public function newQuery() { $query = id(new SemiStructuredObjectInstanceQuery()); if ($this->getObjectType()) { $query->withObjectType($this->getObjectType()); } return $query; } protected function buildCustomSearchFields() { $fields = array( ); if ($this->getObjectType()) { $fields[] = id(new SemiStructuredStaticSearchField()) ->setKey('objecttype') ->setLabel(pht('Object Type')) ->setValue($this->getObjectType()->getName()); $fields[] = id(new SemiStructuredObjectTypePHIDSearchField()) ->setKey('classPHIDs') ->setLabel(pht('Object Type PHID')) ->setValue($this->getObjectType()->getPHID()); } return $fields; } protected function getURI($path) { if ($this->getObjectType()) { return "/semistruct/type/{$this->getObjectType()->getID()}/{$path}"; } return "/semistruct/instance/{$path}"; } public function getQueryResultsPageURI($query_key) { $this->loadObjectTypeFromQuery($query_key); return parent::getQueryResultsPageURI($query_key); } public function getCustomizeURI($query_key, $object_phid, $context_phid) { $this->loadObjectTypeFromQuery($query_key); return parent::getCustomizeURI($query_key, $object_phid, $context_phid); } public function isBuiltinQuery($query_key) { $this->loadObjectTypeFromQuery($query_key); $builtins = $this->getBuiltinQueries(); $raw = $this->getRawQueryNames(); return isset($builtins[$query_key]) || isset($raw[$query_key]); } public function getQueryManagementURI() { if ($this->getObjectType()) { return parent::getQueryManagementURI(); } return '/semistruct/'; } protected function getBuiltinQueryNames() { $raw_names = $this->getRawQueryNames(); if (!$this->getObjectType()) { return $raw_names; } $names = array(); $suffix = '-'.$this->getObjectType()->getId(); foreach ($raw_names as $key => $value) { $names[$key.$suffix] = $value; } return $names; } private function getRawQueryNames() { $names = array(); $names['all'] = pht('All Instances'); return $names; } private function loadObjectTypeFromQuery($query_key) { $matches = null; if (preg_match('/^(\w+)-(\d+)$/', $query_key, $matches)) { $type_id = $matches[2]; $query_key = $matches[1]; if (!$this->getObjectType()) { $this->setObjectType( SemiStructuredObjectTypeQuery::loadOneById( $this->requireViewer(), $type_id)); } return $matches[1]; } return $query_key; } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); $query_key = $this->loadObjectTypeFromQuery($query_key); switch ($query_key) { case 'all': return $query; case 'open': return $query->setParameter( 'statuses', array( SemiStructuredObjectType::STATUS_ACTIVE, )); } return parent::buildSavedQueryFromBuiltin($query_key); } public function loadAllNamedQueries() { $named_queries = parent::loadAllNamedQueries(); if (!$this->getObjectType()) { return $named_queries; } $target_class_phid = $this->getObjectType()->getPHID(); $saved_queries = id(new PhabricatorSavedQueryQuery()) ->setViewer($this->requireViewer()) ->withQueryKeys(mpull($named_queries, 'getQueryKey')) ->execute(); $saved_queries = mpull($saved_queries, 'getParameters', 'getQueryKey'); foreach ($named_queries as $key => $named_query) { if ($named_query->getIsBuiltin()) { continue; } $class_phids = idxv($saved_queries, array($key, 'classPHIDs')); if (!$class_phids) { continue; } if (array_search($target_class_phid, $class_phids) === false) { unset($named_queries[$key]); } } return $named_queries; } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); // TODO type id/phid // if ($map['statuses']) { // $query->withStatuses($map['statuses']); // } // if ($map['editable'] !== null) { // $query->withCanEdit($map['editable']); // } return $query; } protected function getRequiredHandlePHIDsForResultList( array $objects, PhabricatorSavedQuery $query) { return array(); } public function renderResultsDirectly(array $items) { return $this->renderResultList( $items, new PhabricatorSavedQuery(), array()); } protected function renderResultList( array $items, PhabricatorSavedQuery $query , array $handles) { $viewer = $this->requireViewer(); if ($items) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($items, 'getPHID')) ->withEdgeTypes( array( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, )); $edge_query->execute(); } $list = id(new PHUIObjectItemListView()) ->setViewer($viewer); foreach ($items as $instance) { $item = id(new PHUIObjectItemView()) ->setViewer($viewer) ->setHeader($instance->getName()) ->setHref($instance->getURI()) ->setObject($instance); $icon = id(new PHUIIconView()) ->setIcon($instance->getIcon()); $item->setImageIcon($icon); $item->setEpoch($instance->getDateModified()); $phid = $instance->getPHID(); $project_phids = $edge_query->getDestinationPHIDs(array($phid)); $project_handles = $viewer->loadHandles($project_phids); $item->addAttribute( id(new PHUIHandleTagListView()) ->setLimit(4) ->setNoDataString(pht('No Tags')) ->setSlim(true) ->setHandles($project_handles)); + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $instance, + PhabricatorPolicyCapability::CAN_EDIT); + + $edit_uri = $this->getApplicationURI( + "/editinstance/{$instance->getId()}/"); + $item->addAction((new PHUIListItemView()) + ->setName(pht('Edit')) + ->setIcon('fa-edit') + ->setDisabled(!$can_edit) + ->setHref($edit_uri)); + $list->addItem($item); } $result = new PhabricatorApplicationSearchResultView(); $result->setObjectList($list); $result->setNoDataString(pht('No objects found.')); return $result; } /* -( Export )------------------------------------------------------------ */ protected function newExportFields() { return array( id(new PhabricatorStringExportField()) ->setKey('name') ->setLabel(pht('Name')), id(new PhabricatorStringExportField()) ->setKey('description') ->setLabel(pht('Description')), id(new PhabricatorStringExportField()) ->setKey('rawData') ->setLabel(pht('Raw Data')), id(new PhabricatorPHIDExportField()) ->setKey('classPHID') ->setLabel(pht('Class PHID')), id(new PhabricatorURIExportField()) ->setKey('uri') ->setLabel(pht('URI')), ); } protected function newExportData(array $instances) { - // $viewer = $this->requireViewer(); - $export = array(); foreach ($instances as $instance) { $export[] = array( 'name' => $instance->getName(), 'uri' => PhabricatorEnv::getProductionURI($instance->getURI()), 'description' => $instance->getDescription(), 'classPHID' => $instance->getClassPHID(), 'rawData' => $instance->getRawData(), ); } - return $export; } - } diff --git a/src/query/SemiStructuredObjectTypeSearchEngine.php b/src/query/SemiStructuredObjectTypeSearchEngine.php index 30b4e5e..ccf111c 100644 --- a/src/query/SemiStructuredObjectTypeSearchEngine.php +++ b/src/query/SemiStructuredObjectTypeSearchEngine.php @@ -1,149 +1,161 @@ setKey('statuses') ->setLabel(pht('Status')) ->setOptions(SemiStructuredObjectType::getStatusNameMap()), id(new PhabricatorSearchCheckboxesField()) ->setKey('editable') ->setLabel(pht('Editable')) ->setOptions(array('editable' => null)), ); } protected function getURI($path) { return '/semistruct/'.$path; } protected function getBuiltinQueryNames() { $names = array(); $names['open'] = pht('Active Types'); $names['all'] = pht('All Types'); return $names; } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); $viewer = $this->requireViewer(); switch ($query_key) { case 'all': return $query; case 'open': return $query->setParameter( 'statuses', array( SemiStructuredObjectType::STATUS_ACTIVE, )); } return parent::buildSavedQueryFromBuiltin($query_key); } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); if ($map['statuses']) { $query->withStatuses($map['statuses']); } if ($map['editable'] !== null) { $query->withCanEdit($map['editable']); } return $query; } protected function getRequiredHandlePHIDsForResultList( array $objects, PhabricatorSavedQuery $query) { return array(); } protected function renderResultList( array $items, PhabricatorSavedQuery $query, array $handles) { $viewer = $this->requireViewer(); if ($items) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($items, 'getPHID')) ->withEdgeTypes( array( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, )); $edge_query->execute(); } $list = id(new PHUIObjectItemListView()) ->setViewer($viewer); foreach ($items as $class) { $item = id(new PHUIObjectItemView()) ->setViewer($viewer) ->setObjectName($class->getObjectName()) ->setHeader($class->getName()) ->setHref($class->getURI()) ->setObject($class); if ($class->isArchived()) { $item->setDisabled(true); $bg_color = 'bg-grey'; } else { $bg_color = 'bg-dark'; } $icon = id(new PHUIIconView()) ->setIcon($class->getIcon()); $item->setImageIcon($icon); - $item->setEpoch($class->getDateModified()); - $phid = $class->getPHID(); $project_phids = $edge_query->getDestinationPHIDs(array($phid)); $project_handles = $viewer->loadHandles($project_phids); $item->addAttribute( id(new PHUIHandleTagListView()) ->setLimit(4) ->setNoDataString(pht('No Tags')) ->setSlim(true) ->setHandles($project_handles)); + $item->setSideColumn( + id(new PHUIButtonView()) + ->setTag('a') + ->setIcon('fa-users') + ->setHref($class->getURI('items/')) + ->setText(pht('Instances')) + ->setColor(PHUIButtonView::GREY)); + + $item->addAction((new PHUIListItemView()) + ->setName(pht('Create New Instance')) + ->setIcon('fa-plus') + // TODO check policy + ->setHref($class->getURI('new/'))); + $list->addItem($item); } $result = new PhabricatorApplicationSearchResultView(); $result->setObjectList($list); $result->setNoDataString(pht('No object types found.')); return $result; } }