diff --git a/resources/sql/20230427.semistructured.2.objectinstance.sql b/resources/sql/20230427.semistructured.2.objectinstance.sql index 0ec54f0..3d03acc 100644 --- a/resources/sql/20230427.semistructured.2.objectinstance.sql +++ b/resources/sql/20230427.semistructured.2.objectinstance.sql @@ -1,20 +1,20 @@ CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objectinstance ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, phid VARBINARY(64) NOT NULL, dateCreated INT UNSIGNED NOT NULL, dateModified INT UNSIGNED NOT NULL, - classID INT UNSIGNED NOT NULL, + classPHID VARBINARY(64) NOT NULL, name VARCHAR(255) NOT NULL COLLATE {$COLLATE_SORT}, description LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, rawData LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, UNIQUE KEY `key_phid` (phid), - KEY `classID` (classID) + KEY `classPHID` (classPHID) ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objectinstancetransaction LIKE {$NAMESPACE}_file.file_transaction; CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objectinstancetransaction_comment LIKE {$NAMESPACE}_file.file_transaction_comment; diff --git a/src/controller/class/SemiStructuredObjectTypeViewController.php b/src/controller/class/SemiStructuredObjectTypeViewController.php index 2688005..3a83db1 100644 --- a/src/controller/class/SemiStructuredObjectTypeViewController.php +++ b/src/controller/class/SemiStructuredObjectTypeViewController.php @@ -1,174 +1,174 @@ 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); $instances_view = $this->buildInstancesView(); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->setMainColumn(array( $instances_view, $timeline, $comment_view, )) ->addPropertySection(pht('Description'), $details); $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("/archive/{$id}/"); $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) - ->withClassIds(array($object_type->getId())) + ->withClassPHIDs(array($object_type->getPHID())) ->execute(); $content = id(new SemiStructuredObjectInstanceSearchEngine()) ->setViewer($viewer) ->renderResultsDirectly($instances); // $content is PhabricatorApplicationSearchResultView $box = new PHUIObjectBoxView(); $interface = 'PhabricatorApplicationSearchResultView'; if ($content->getObjectList()) { $box->setObjectList($content->getObjectList()); } if ($content->getTable()) { $box->setTable($content->getTable()); } if ($content->getContent()) { $box->appendChild($content->getContent()); } $box ->setHeader(pht('Instances of this type')); return $box; } } diff --git a/src/editor/SemiStructuredObjectInstanceEditEngine.php b/src/editor/SemiStructuredObjectInstanceEditEngine.php index af04ea3..e097734 100644 --- a/src/editor/SemiStructuredObjectInstanceEditEngine.php +++ b/src/editor/SemiStructuredObjectInstanceEditEngine.php @@ -1,133 +1,133 @@ 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/'); } 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(id(new PhabricatorStaticEditField()) ->setKey('objecttype') ->setLabel(pht('Object Type')) ->setValue($object->getClass()->getName())), id(new PhabricatorIntEditField()) - ->setKey('classID') + ->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->getClassID()), + ->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).')) ->setMonospaced(true) ->setIsRequired(true) ->setTransactionType( SemiStructuredObjectInstanceRawDataTransaction::TRANSACTIONTYPE) ->setValue($object->getRawData()), ); return $fields; } } diff --git a/src/query/SemiStructuredObjectInstanceQuery.php b/src/query/SemiStructuredObjectInstanceQuery.php index 9dae453..ec43c7b 100644 --- a/src/query/SemiStructuredObjectInstanceQuery.php +++ b/src/query/SemiStructuredObjectInstanceQuery.php @@ -1,120 +1,122 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } - public function withClassIDs(array $class_ids) { - $this->classIDs = $class_ids; + public function withClassPHIDs(array $class_phids) { + $this->classPHIDs = $class_phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withCanEdit($can_edit) { $this->canEdit = $can_edit; return $this; } public function newResultObject() { // TODO maybe if I have classId, I can attach the class directly? return new SemiStructuredObjectInstance(); } protected function didFilterPage(array $items) { $phids = mpull($items, 'getPHID'); if ($this->canEdit) { $items = id(new PhabricatorPolicyFilter()) ->setViewer($this->getViewer()) ->requireCapabilities(array( PhabricatorPolicyCapability::CAN_EDIT, )) ->apply($items); } return $items; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'instances.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'instances.phid IN (%Ls)', $this->phids); } - if ($this->classIDs !== null) { + if ($this->classPHIDs !== null) { $where[] = qsprintf( $conn, - 'instances.classID IN (%Ls)', - $this->classIDs); + 'instances.classPHID IN (%Ls)', + $this->classPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'instances.status IN (%Ls)', $this->statuses); } return $where; } protected function willFilterPage(array $objects) { $classes = id(new SemiStructuredObjectTypeQuery()) ->setViewer($this->getViewer()) - ->withIDs(mpull($objects, 'getClassID')) + ->withPHIDs(mpull($objects, 'getClassPHID')) ->execute(); + $classes = mpull($classes, null, 'getPHID'); + foreach ($objects as $key => $object) { - $class = idx($classes, $object->getClassID()); + $class = idx($classes, $object->getClassPHID()); if (!$class) { unset($objects[$key]); continue; } $object->attachClass($class); } return $objects; } public function getQueryApplicationClass() { return 'SemiStructuredDataApplication'; } protected function getPrimaryTableAlias() { return 'instances'; } } diff --git a/src/query/SemiStructuredObjectTypeQuery.php b/src/query/SemiStructuredObjectTypeQuery.php index 4fade63..9103282 100644 --- a/src/query/SemiStructuredObjectTypeQuery.php +++ b/src/query/SemiStructuredObjectTypeQuery.php @@ -1,87 +1,84 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; } public function withCanEdit($can_edit) { $this->canEdit = $can_edit; return $this; } public function newResultObject() { return new SemiStructuredObjectType(); } protected function didFilterPage(array $items) { - $phids = mpull($items, 'getPHID'); - if ($this->canEdit) { $items = id(new PhabricatorPolicyFilter()) ->setViewer($this->getViewer()) ->requireCapabilities(array( PhabricatorPolicyCapability::CAN_EDIT, )) ->apply($items); } return $items; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); - if ($this->ids !== null) { $where[] = qsprintf( $conn, 'classes.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'classes.phid IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'classes.status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'SemiStructuredDataApplication'; } protected function getPrimaryTableAlias() { return 'classes'; } } diff --git a/src/storage/SemiStructuredObjectInstance.php b/src/storage/SemiStructuredObjectInstance.php index b110d28..a36039e 100644 --- a/src/storage/SemiStructuredObjectInstance.php +++ b/src/storage/SemiStructuredObjectInstance.php @@ -1,171 +1,171 @@ setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) // TODO take policies from class? // ->setEditPolicy($actor->getPHID()) ->setStatus(self::STATUS_ACTIVE); if ($object_class !== null) { $object - ->setClassID($object_class->getID()) + ->setClassPHID($object_class->getPHID()) ->attachClass($object_class); } return $object; } public function attachClass( SemiStructuredObjectType $object_class = null) { $this->class = $object_class; return $this; } public function getClass() { return $this->assertAttached($this->class); } public function hasAttachedClass() { return $this->class !== self::ATTACHABLE; } public function getURI() { return urisprintf('/semistruct/instance/%d/', $this->getID()); } public function getIcon() { // TODO return 'fa-google'; } public static function getStatusNameMap() { return array( // TODO remove this self::STATUS_ACTIVE => pht('Active'), self::STATUS_ARCHIVED => pht('Archived'), ); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'rawData' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', 'description' => 'text', 'status' => 'text32', ), self::CONFIG_KEY_SCHEMA => array( - 'classID' => array( - 'columns' => array('classID'), + 'classPHID' => array( + 'columns' => array('classPHID'), ), ), ) + parent::getConfiguration(); } public function getPHIDType() { return SemiStructuredObjectInstancePHIDType::TYPECONST; } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new SemiStructuredObjectInstanceTransactionEditor(); } public function getApplicationTransactionTemplate() { return new SemiStructuredObjectInstanceTransaction(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { - return; +return ''; // TODO switch ($capability) { // TODO case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return true; } /* -( PhabricatorCustomFieldInterface )------------------------------------ */ public function getCustomFieldSpecificationForRole($role) { return PhabricatorEnv::getEnvConfig('user.fields'); } public function getCustomFieldBaseClass() { return 'SemiStructuredInstanceCustomField'; } public function getCustomFields() { return $this->assertAttached($this->customFields); } public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) { $this->customFields = $fields; return $this; } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $this->saveTransaction(); } } diff --git a/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php b/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php index 4e0269f..5f09bf4 100644 --- a/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php +++ b/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php @@ -1,38 +1,38 @@ getClassID(); + return $object->getClassPHID(); } public function applyInternalEffects($object, $value) { - $object->setClassID($value); + $object->setClassPHID($value); } public function validateTransactions($object, array $xactions) { $errors = array(); if (!$xactions) { - if (!$object->getClassID()) { - $errors[] = $this->newInvalidError(pht('ClassID is required!')); + if (!$object->getClassPHID()) { + $errors[] = $this->newInvalidError(pht('ClassPHID is required!')); } } foreach ($xactions as $xaction) { $new_value = $xaction->getNewValue(); $old_value = $xaction->getOldValue(); if ($old_value !== null && $new_value != $old_value) { $errors[] = $this->newInvalidError( - pht('ClassID cannot be changed.')); + pht('ClassPHID cannot be changed.')); } } return $errors; } }