diff --git a/src/controller/instance/SemiStructuredObjectInstanceController.php b/src/controller/instance/SemiStructuredObjectInstanceController.php index 69b14ff..66c39b2 100644 --- a/src/controller/instance/SemiStructuredObjectInstanceController.php +++ b/src/controller/instance/SemiStructuredObjectInstanceController.php @@ -1,94 +1,72 @@ object = $object; return $this; } public function getObject() { return $this->object; } - // TODO ??? - public function buildApplicationMenu() { - return $this->buildSideNavView()->getMenu(); - } - - protected function buildHeaderView() { $viewer = $this->getViewer(); $object = $this->getObject(); $object_type = $object->getClass(); if ($object_type->isArchived()) { $status_icon = 'fa-ban'; $status_color = 'dark'; } else { $status_icon = 'fa-check'; $status_color = 'bluegrey'; } $status_name = idx( SemiStructuredObjectType::getStatusNameMap(), $object_type->getStatus()); return id(new PHUIHeaderView()) ->setHeader( pht( '%s: %d %s', $object_type->getName(), $object->getID(), $object->getName())) ->setUser($viewer) ->setPolicyObject($object_type) ->setStatus($status_icon, $status_color, $status_name) ->setHeaderIcon(SemiStructuredDataApplication::ICON_OBJECT_TYPE); } protected function buildApplicationCrumbs() { $object = $this->getObject(); $object_type = $object->getClass(); + $can_create = true; + $crumbs = parent::buildApplicationCrumbs(); $crumbs->addTextCrumb($object_type->getName(), $object_type->getURI()); $crumbs->addTextCrumb( pht('%d %s', $object->getID(), $object->getName()), $object->getURI()); $crumbs->setBorder(true); - return $crumbs; - } - protected function buildSideNavView($filter = null) { - $viewer = $this->getViewer(); - $object = $this->getObject(); - $object_type = $object->getClass(); - $id = $object->getID(); - - $nav = id(new AphrontSideNavFilterView()) - ->setBaseURI(new PhutilURI($this->getApplicationURI())); - - $nav->addLabel(pht('Instance of %s', $object_type->getName())); - // TODO figure out menu - $nav->addFilter( - 'view', - pht('View Object Type'), - $object_type->getURI(), - SemiStructuredDataApplication::ICON_OBJECT_TYPE); - - $nav->addFilter( - 'instances', - pht('View Instances'), - $this->getApplicationURI("/type/{$id}/items/"), - 'fa-group'); + $crumbs->addAction( + id(new PHUIListItemView()) + ->setName(pht('Create %s', $object_type->getName())) + ->setHref($this->getApplicationURI("type/{$object_type->getID()}/new/")) + ->setIcon('fa-plus-square') + ->setWorkflow(!$can_create) + ->setDisabled(!$can_create)); - $nav->selectFilter($filter); - - return $nav; + return $crumbs; } + } diff --git a/src/controller/instance/SemiStructuredObjectInstanceViewController.php b/src/controller/instance/SemiStructuredObjectInstanceViewController.php index b9cb67b..e6fbb14 100644 --- a/src/controller/instance/SemiStructuredObjectInstanceViewController.php +++ b/src/controller/instance/SemiStructuredObjectInstanceViewController.php @@ -1,114 +1,111 @@ getViewer(); $id = $request->getURIData('id'); $object = id(new SemiStructuredObjectInstanceQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->executeOne(); if (!$object) { return new Aphront404Response(); } $this->setObject($object); $object_type = $object->getClass(); $crumbs = $this->buildApplicationCrumbs(); $title = pht( '%s: %d %s', $object_type->getName(), $object->getID(), $object->getName()); $header = $this->buildHeaderView(); $curtain = $this->buildCurtain(); $details = $this->buildDetailsView(); $timeline = $this->buildTransactionTimeline( $object, new SemiStructuredObjectInstanceTransactionQuery()); $comment_view = id(new SemiStructuredObjectInstanceEditEngine()) ->setViewer($viewer) ->buildEditEngineCommentView($object); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->setMainColumn(array( $timeline, $comment_view, )) ->addPropertySection(pht('Details'), $details); - $navigation = $this->buildSideNavView('view'); - return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->setPageObjectPHIDs(array($object->getPHID())) - ->setNavigation($navigation) ->appendChild($view); } private function buildDetailsView() { $viewer = $this->getViewer(); $object = $this->getObject(); $view = id(new PHUIPropertyListView()) ->setUser($viewer); $description = $object->getDescription(); if (strlen($description)) { $view->addTextContent( new PHUIRemarkupView($viewer, $description)); } $field_list = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_VIEW); $field_list->appendFieldsToPropertyList($object, $viewer, $view); $content = $object->getRawData(); $content = id(new PhutilJSON()) ->encodeFormatted(json_decode($content)); $view->addProperty( pht('Structured content'), phutil_tag('pre', array(), $content)); return $view; } private function buildCurtain() { $viewer = $this->getViewer(); $object = $this->getObject(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $object, PhabricatorPolicyCapability::CAN_EDIT); $id = $object->getID(); $edit_uri = $this->getApplicationURI("/editinstance/{$id}/"); $curtain = $this->newCurtainView($object); $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Edit Instance')) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setHref($edit_uri)); return $curtain; } } diff --git a/src/editor/SemiStructuredObjectInstanceEditEngine.php b/src/editor/SemiStructuredObjectInstanceEditEngine.php index 2a67fdd..4d83184 100644 --- a/src/editor/SemiStructuredObjectInstanceEditEngine.php +++ b/src/editor/SemiStructuredObjectInstanceEditEngine.php @@ -1,149 +1,168 @@ objectType = $object_type; return $this; } protected function newEditableObject() { $viewer = $this->getViewer(); return SemiStructuredObjectInstance::initializeNewObjectInstance( $viewer, $this->objectType); } protected function newObjectQuery() { return new SemiStructuredObjectInstanceQuery(); } protected function getObjectCreateTitleText($object) { return pht('Create Object Instance'); } protected function getObjectCreateButtonText($object) { return pht('Create Object Instance'); } protected function getObjectCreateCancelURI($object) { // TODO if objjec has link to class - go to that class return '/semistructured/'; } protected function getEditorURI() { return $this->getApplication()->getApplicationURI('editinstance/'); } public function getCreateURI($form_key) { if ($this->objectType) { return $this->getApplication()->getApplicationURI( "type/{$this->objectType->getID()}/new/"); } return null; } protected function getObjectEditTitleText($object) { return pht('Edit Object: %d', $object->getID()); } protected function getObjectEditShortText($object) { return pht('Edit Object'); } protected function getObjectCreateShortText() { return pht('Create Object Instance'); } protected function getObjectName() { return pht('Object Instance'); } protected function getObjectViewURI($object) { return $object->getURI(); } protected function buildCustomEditFields($object) { $fields = array( id(new PhabricatorTextEditField()) ->setKey('classPHID') ->setLabel(pht('Object Type')) ->setDescription(pht('Type of the object.')) ->setConduitDescription(pht('Set type of object.')) ->setIsHidden(true) ->setTransactionType( SemiStructuredObjectInstanceClassTransaction::TRANSACTIONTYPE) ->setIsRequired(true) ->setValue($object->getClassPHID()), id(new PhabricatorTextEditField()) ->setKey('name') ->setLabel(pht('Name')) ->setDescription(pht('Name of the object.')) ->setConduitDescription(pht('Rename the object.')) ->setConduitTypeDescription(pht('New object name.')) ->setTransactionType( SemiStructuredObjectInstanceNameTransaction::TRANSACTIONTYPE) ->setValue($object->getName()), id(new PhabricatorRemarkupEditField()) ->setKey('description') ->setLabel(pht('Free text')) ->setDescription(pht('Object instance free text (Remarkup).')) ->setConduitTypeDescription(pht('New object free type.')) ->setTransactionType( SemiStructuredObjectInstanceDescriptionTransaction::TRANSACTIONTYPE) ->setValue($object->getDescription()), id(new PhabricatorTextAreaEditField()) ->setKey('rawdata') ->setLabel(pht('Structured content')) ->setDescription(pht('Object structured data (JSON).')) ->setConduitTypeDescription( pht('Object body (JSON, but as a String!).')) ->setMonospaced(true) ->setIsRequired(true) ->setTransactionType( SemiStructuredObjectInstanceRawDataTransaction::TRANSACTIONTYPE) ->setValue($object->getRawData()), ); if ($this->objectType) { array_unshift( $fields, (new PhabricatorStaticEditField()) ->setKey('objecttype') ->setLabel(pht('Object Type')) ->setValue($this->objectType->getName())); } return $fields; } + + // Put the raw-data field at the bottom of the form + protected function willConfigureFields($object, array $fields) { + $config = $this->getEditEngineConfiguration(); + + $order = array_keys($fields); + $json_index = array_search('rawdata', $order); + + if ($json_index !== false) { + unset($order[$json_index]); + $order[] = 'rawdata'; + + $config->setFieldOrder($order); + } + + return $fields; + } + + } diff --git a/src/query/SemiStructuredObjectInstanceSearchEngine.php b/src/query/SemiStructuredObjectInstanceSearchEngine.php index dbb0b4a..38d6371 100644 --- a/src/query/SemiStructuredObjectInstanceSearchEngine.php +++ b/src/query/SemiStructuredObjectInstanceSearchEngine.php @@ -1,208 +1,207 @@ 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()); } return $fields; } protected function getURI($path) { return "/semistruct/type/{$this->getObjectType()->getID()}/{$path}"; } protected function getBuiltinQueryNames() { $names = array(); $names['all'] = pht('All Instances'); 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(); // 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) - ->setObjectName($instance->getName()) ->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)); $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; } }