diff --git a/resources/sql/20230427.semistructured.2.objectinstance.sql b/resources/sql/20230427.semistructured.2.objectinstance.sql index f935943..8d6bbd3 100644 --- a/resources/sql/20230427.semistructured.2.objectinstance.sql +++ b/resources/sql/20230427.semistructured.2.objectinstance.sql @@ -1,16 +1,16 @@ 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, - classPHID VARBINARY(64) NOT NULL, + classID INT UNSIGNED NOT NULL, status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, rawData LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, UNIQUE KEY `key_phid` (phid), - KEY `classPHID` (classPHID) + KEY `classID` (classID) ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; --- CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objectinstancetransaction --- LIKE {$NAMESPACE}_file.file_transaction; +CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objectinstancetransaction + LIKE {$NAMESPACE}_file.file_transaction; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index d6e343f..ccd04bd 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,73 +1,91 @@ 2, 'class' => array( 'SemiStructuredBaseController' => 'applications/semistruct/controller/SemiStructuredBaseController.php', 'SemiStructuredDAO' => 'applications/semistruct/storage/SemiStructuredDAO.php', 'SemiStructuredDataApplication' => 'applications/semistruct/application/SemiStructuredDataApplication.php', 'SemiStructuredObjectInstance' => 'applications/semistruct/storage/SemiStructuredObjectInstance.php', + 'SemiStructuredObjectInstanceClassTransaction' => 'applications/semistruct/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php', + 'SemiStructuredObjectInstanceController' => 'applications/semistruct/controller/instance/SemiStructuredObjectInstanceController.php', + 'SemiStructuredObjectInstanceEditEngine' => 'applications/semistruct/editor/SemiStructuredObjectInstanceEditEngine.php', 'SemiStructuredObjectInstancePHIDType' => 'applications/semistruct/phid/SemiStructuredObjectInstancePHIDType.php', 'SemiStructuredObjectInstanceQuery' => 'applications/semistruct/query/SemiStructuredObjectInstanceQuery.php', + 'SemiStructuredObjectInstanceRawDataTransaction' => 'applications/semistruct/xaction/instance/SemiStructuredObjectInstanceRawDataTransaction.php', + 'SemiStructuredObjectInstanceTransaction' => 'applications/semistruct/storage/SemiStructuredObjectInstanceTransaction.php', + 'SemiStructuredObjectInstanceTransactionEditor' => 'applications/semistruct/editor/SemiStructuredObjectInstanceTransactionEditor.php', + 'SemiStructuredObjectInstanceTransactionType' => 'applications/semistruct/xaction/instance/SemiStructuredObjectInstanceTransactionType.php', + 'SemiStructuredObjectInstanceViewController' => 'applications/semistruct/controller/instance/SemiStructuredObjectInstanceViewController.php', + 'SemiStructuredObjectNewInstanceController' => 'applications/semistruct/controller/class/SemiStructuredObjectNewInstanceController.php', 'SemiStructuredObjectType' => 'applications/semistruct/storage/SemiStructuredObjectType.php', 'SemiStructuredObjectTypeController' => 'applications/semistruct/controller/class/SemiStructuredObjectTypeController.php', 'SemiStructuredObjectTypeDescriptionTransaction' => 'applications/semistruct/xaction/class/SemiStructuredObjectTypeDescriptionTransaction.php', 'SemiStructuredObjectTypeEditController' => 'applications/semistruct/controller/class/SemiStructuredObjectTypeEditController.php', 'SemiStructuredObjectTypeEditEngine' => 'applications/semistruct/editor/SemiStructuredObjectTypeEditEngine.php', 'SemiStructuredObjectTypeFerretEngine' => 'applications/semistruct/engine/SemiStructuredObjectTypeFerretEngine.php', 'SemiStructuredObjectTypeListController' => 'applications/semistruct/controller/class/SemiStructuredObjectTypeListController.php', 'SemiStructuredObjectTypeNameTransaction' => 'applications/semistruct/xaction/class/SemiStructuredObjectTypeNameTransaction.php', 'SemiStructuredObjectTypePHIDType' => 'applications/semistruct/phid/SemiStructuredObjectTypePHIDType.php', 'SemiStructuredObjectTypeQuery' => 'applications/semistruct/query/SemiStructuredObjectTypeQuery.php', 'SemiStructuredObjectTypeSearchEngine' => 'applications/semistruct/query/SemiStructuredObjectTypeSearchEngine.php', 'SemiStructuredObjectTypeTransaction' => 'applications/semistruct/storage/SemiStructuredObjectTypeTransaction.php', 'SemiStructuredObjectTypeTransactionEditor' => 'applications/semistruct/editor/SemiStructuredObjectTypeTransactionEditor.php', 'SemiStructuredObjectTypeTransactionType' => 'applications/semistruct/xaction/class/SemiStructuredObjectTypeTransactionType.php', 'SemiStructuredObjectTypeViewController' => 'applications/semistruct/controller/class/SemiStructuredObjectTypeViewController.php', 'SemiStructuredPatchList' => 'applications/semistruct/storage/SemiStructuredPatchList.php', ), 'function' => array(), 'xmap' => array( 'SemiStructuredBaseController' => 'PhabricatorController', 'SemiStructuredDAO' => 'PhabricatorLiskDAO', 'SemiStructuredDataApplication' => 'PhabricatorApplication', 'SemiStructuredObjectInstance' => array( 'SemiStructuredDAO', 'PhabricatorPolicyInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', ), + 'SemiStructuredObjectInstanceClassTransaction' => 'SemiStructuredObjectInstanceTransactionType', + 'SemiStructuredObjectInstanceController' => 'SemiStructuredBaseController', + 'SemiStructuredObjectInstanceEditEngine' => 'PhabricatorEditEngine', 'SemiStructuredObjectInstancePHIDType' => 'PhabricatorPHIDType', 'SemiStructuredObjectInstanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'SemiStructuredObjectInstanceRawDataTransaction' => 'SemiStructuredObjectInstanceTransactionType', + 'SemiStructuredObjectInstanceTransaction' => 'PhabricatorModularTransaction', + 'SemiStructuredObjectInstanceTransactionEditor' => 'PhabricatorApplicationTransactionEditor', + 'SemiStructuredObjectInstanceTransactionType' => 'PhabricatorModularTransactionType', + 'SemiStructuredObjectInstanceViewController' => 'SemiStructuredObjectInstanceController', + 'SemiStructuredObjectNewInstanceController' => 'SemiStructuredObjectTypeController', 'SemiStructuredObjectType' => array( 'SemiStructuredDAO', 'PhabricatorPolicyInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', 'PhabricatorFerretInterface', ), 'SemiStructuredObjectTypeController' => 'SemiStructuredBaseController', 'SemiStructuredObjectTypeDescriptionTransaction' => 'SemiStructuredObjectTypeTransactionType', 'SemiStructuredObjectTypeEditController' => 'PhabricatorDashboardController', 'SemiStructuredObjectTypeEditEngine' => 'PhabricatorEditEngine', 'SemiStructuredObjectTypeFerretEngine' => 'PhabricatorFerretEngine', 'SemiStructuredObjectTypeListController' => 'SemiStructuredBaseController', 'SemiStructuredObjectTypeNameTransaction' => 'SemiStructuredObjectTypeTransactionType', 'SemiStructuredObjectTypePHIDType' => 'PhabricatorPHIDType', 'SemiStructuredObjectTypeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'SemiStructuredObjectTypeSearchEngine' => 'PhabricatorApplicationSearchEngine', 'SemiStructuredObjectTypeTransaction' => 'PhabricatorModularTransaction', 'SemiStructuredObjectTypeTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'SemiStructuredObjectTypeTransactionType' => 'PhabricatorModularTransactionType', 'SemiStructuredObjectTypeViewController' => 'SemiStructuredObjectTypeController', 'SemiStructuredPatchList' => 'PhabricatorSQLPatchList', ), )); diff --git a/src/application/SemiStructuredDataApplication.php b/src/application/SemiStructuredDataApplication.php index 31f0911..6ec3103 100644 --- a/src/application/SemiStructuredDataApplication.php +++ b/src/application/SemiStructuredDataApplication.php @@ -1,29 +1,38 @@ array( '(?:query/(?P[^/]+)/)?' => 'SemiStructuredObjectTypeListController', $this->getEditRoutePattern('editclass/') => 'SemiStructuredObjectTypeEditController', - 'type/(?:(?P\d+)/)?' => 'SemiStructuredObjectTypeViewController', + 'type/(?:(?P\d+)/)?' => array( + '' => 'SemiStructuredObjectTypeViewController', + 'new/' => 'SemiStructuredObjectNewInstanceController', + ), + + + $this->getEditRoutePattern('editobject/') => + 'SemiStructuredObjectInstanceEditController', + 'instance/(?:(?P\d+)/)?' => + 'SemiStructuredObjectInstanceViewController', ), ); } } diff --git a/src/controller/class/SemiStructuredObjectNewInstanceController.php b/src/controller/class/SemiStructuredObjectNewInstanceController.php new file mode 100644 index 0000000..8e1ba73 --- /dev/null +++ b/src/controller/class/SemiStructuredObjectNewInstanceController.php @@ -0,0 +1,53 @@ +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); + + $object = SemiStructuredObjectInstance::initializeNewObjectInstance( + $viewer, + $object_type); + + return id(new SemiStructuredObjectInstanceEditEngine()) + ->setTargetObject($object) + ->setController($this) + ->buildResponse(); + + + $crumbs = $this->buildApplicationCrumbs(); + $title = $object_type->getName(); + + $header = $this->buildHeaderView(); + // $curtain = $this->buildCurtain(); + + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + // ->setCurtain($curtain) + ->setMainColumn(array( + )) + ->addPropertySection(pht('Description'), "ixi"); + + $navigation = $this->buildSideNavView('view'); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setPageObjectPHIDs(array($object_type->getPHID())) + ->setNavigation($navigation) + ->appendChild($view); + } + } diff --git a/src/controller/class/SemiStructuredObjectTypeViewController.php b/src/controller/class/SemiStructuredObjectTypeViewController.php index d75b288..d89fa34 100644 --- a/src/controller/class/SemiStructuredObjectTypeViewController.php +++ b/src/controller/class/SemiStructuredObjectTypeViewController.php @@ -1,114 +1,126 @@ 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 PhabricatorBadgesTransactionQuery()); /// TODO $timeline = null; $comment_view = id(new SemiStructuredObjectTypeEditEngine()) ->setViewer($viewer) ->buildEditEngineCommentView($object_type); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->setMainColumn(array( + // TODO add instance list here $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)); } 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 { + } else {/* $curtain->addAction( id(new PhabricatorActionView()) ->setName(pht('Archive Object Type')) ->setIcon('fa-ban') ->setDisabled(!$can_edit) ->setWorkflow($can_edit) - ->setHref($archive_uri)); + ->setHref($archive_uri)); */ } return $curtain; } } diff --git a/src/controller/instance/SemiStructuredObjectInstanceController.php b/src/controller/instance/SemiStructuredObjectInstanceController.php new file mode 100644 index 0000000..781049c --- /dev/null +++ b/src/controller/instance/SemiStructuredObjectInstanceController.php @@ -0,0 +1,89 @@ +objectType = $object_type; + return $this; + } + + public function getObjectType() { + return $this->objectType; + } + + // TODO ??? + public function buildApplicationMenu() { + return $this->buildSideNavView()->getMenu(); + } + + + protected function buildHeaderView() { + $viewer = $this->getViewer(); + $object_type = $this->getObjectType(); + + + 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($object_type->getName()) + ->setUser($viewer) + ->setPolicyObject($object_type) + ->setStatus($status_icon, $status_color, $status_name) + ->setHeaderIcon('fa-trophy'); + } + + protected function buildApplicationCrumbs() { + $object_type = $this->getObjectType(); + $uri = $object_type->getURI(); + + $crumbs = parent::buildApplicationCrumbs(); + $crumbs->addTextCrumb($object_type->getName(), $uri); + $crumbs->setBorder(true); + return $crumbs; + } + + protected function buildSideNavView($filter = null) { + $viewer = $this->getViewer(); + $object_type = $this->getObjectType(); + $id = $object_type->getID(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $object_type, + PhabricatorPolicyCapability::CAN_EDIT); + + $nav = id(new AphrontSideNavFilterView()) + ->setBaseURI(new PhutilURI($this->getApplicationURI())); + + $nav->addLabel(pht('Object Type')); + + $nav->addFilter( + 'view', + pht('View Object Type'), + $object_type->getURI(), + 'fa-trophy'); + + $nav->addFilter( + 'instances', + pht('View Instances'), + $this->getApplicationURI("/recipients/{$id}/"), + 'fa-group'); + + $nav->selectFilter($filter); + + return $nav; + } + +} diff --git a/src/controller/class/SemiStructuredObjectTypeViewController.php b/src/controller/instance/SemiStructuredObjectInstanceViewController.php similarity index 94% copy from src/controller/class/SemiStructuredObjectTypeViewController.php copy to src/controller/instance/SemiStructuredObjectInstanceViewController.php index d75b288..5b30eae 100644 --- a/src/controller/class/SemiStructuredObjectTypeViewController.php +++ b/src/controller/instance/SemiStructuredObjectInstanceViewController.php @@ -1,114 +1,114 @@ getViewer(); $id = $request->getURIData('id'); - $object_type = id(new SemiStructuredObjectTypeQuery()) + $object_type = id(new SemiStructuredObjectInstanceQuery()) ->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 PhabricatorBadgesTransactionQuery()); /// TODO $timeline = null; $comment_view = id(new SemiStructuredObjectTypeEditEngine()) ->setViewer($viewer) ->buildEditEngineCommentView($object_type); $view = id(new PHUITwoColumnView()) ->setHeader($header) ->setCurtain($curtain) ->setMainColumn(array( $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)); } return $view; } private function buildCurtain() { $viewer = $this->getViewer(); $object_type = $this->getObjectType(); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $object_type, PhabricatorPolicyCapability::CAN_EDIT); $id = $object_type->getID(); $edit_uri = $this->getApplicationURI("/editclass/{$id}/"); $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)); 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; } } diff --git a/src/editor/SemiStructuredObjectInstanceEditEngine.php b/src/editor/SemiStructuredObjectInstanceEditEngine.php new file mode 100644 index 0000000..44bbf78 --- /dev/null +++ b/src/editor/SemiStructuredObjectInstanceEditEngine.php @@ -0,0 +1,111 @@ +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('editobject/'); + } + + 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( +// TODO is this the best edit field? + id(new PhabricatorIntEditField()) + ->setKey('classID') + ->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()), + id(new PhabricatorTextAreaEditField()) + ->setKey('rawdata') + ->setLabel(pht('Raw data')) + ->setDescription(pht('Object unstructured data (JSON).')) + ->setConduitTypeDescription(pht('Object body (JSON).')) + ->setMonospaced(true) + ->setTransactionType( + SemiStructuredObjectInstanceRawDataTransaction::TRANSACTIONTYPE) + ->setValue($object->getRawData()), + ); + + return $fields; + } + +} diff --git a/src/editor/SemiStructuredObjectInstanceTransactionEditor.php b/src/editor/SemiStructuredObjectInstanceTransactionEditor.php new file mode 100644 index 0000000..172f87a --- /dev/null +++ b/src/editor/SemiStructuredObjectInstanceTransactionEditor.php @@ -0,0 +1,28 @@ +ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } - public function withClassPHIDs(array $classPHIDs) { - $this->classPHIDs = $classPHIDs; + 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() { 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->classPHIDs !== null) { $where[] = qsprintf( $conn, 'instances.classPHID IN (%Ls)', $this->phids); } if ($this->statuses !== null) { $where[] = qsprintf( $conn, 'instances.status IN (%Ls)', $this->statuses); } return $where; } public function getQueryApplicationClass() { return 'SemiStructuredDataApplication'; } protected function getPrimaryTableAlias() { return 'instances'; } } diff --git a/src/storage/SemiStructuredObjectInstance.php b/src/storage/SemiStructuredObjectInstance.php index ec37143..f5e06fc 100644 --- a/src/storage/SemiStructuredObjectInstance.php +++ b/src/storage/SemiStructuredObjectInstance.php @@ -1,111 +1,136 @@ setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) // TODO take policies from class? + // ->setEditPolicy($actor->getPHID()) + ->setStatus(self::STATUS_ACTIVE); + + if ($object_class !== null) { + $object + ->setClassID($object_class->getID()) + ->attachClass($object_class); + } + + return $object; + } + + public function attachClass( + SemiStructuredObjectType $object_class = null) { + $this->class = $object_class; + return $this; + } - return id(new self()) - ->setClassPHID($object_class->getPHID()) - ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) // TODO take policies from class? - ->setEditPolicy($actor->getPHID()) - ->setStatus(self::STATUS_ACTIVE); + public function getClass() { + return $this->assertAttached($this->class); } + public function getURI() { + return urisprintf('/semistruct/instance/%d/', $this->getID()); + } + + public static function getStatusNameMap() { return array( self::STATUS_ACTIVE => pht('Active'), self::STATUS_ARCHIVED => pht('Archived'), ); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', 'status' => 'text32', - 'classPHID' => 'phid', 'rawData' => 'text', ), self::CONFIG_KEY_SCHEMA => array( - 'classPHID' => array( - 'columns' => array('classPHID'), + 'classID' => array( + 'columns' => array('classID'), ), ), ) + parent::getConfiguration(); } public function getPHIDType() { return SemiStructuredObjectInstancePHIDType::TYPECONST; } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { - // return new PhabricatorDashboardPanelTransactionEditor(); TODO + return new SemiStructuredObjectInstanceTransactionEditor(); } public function getApplicationTransactionTemplate() { - // return new PhabricatorDashboardPanelTransaction(); TODO + return new SemiStructuredObjectInstanceTransaction(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { + return; switch ($capability) { + // TODO case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return false; + return true; } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $this->saveTransaction(); } } diff --git a/src/storage/SemiStructuredObjectInstanceTransaction.php b/src/storage/SemiStructuredObjectInstanceTransaction.php new file mode 100644 index 0000000..92fe112 --- /dev/null +++ b/src/storage/SemiStructuredObjectInstanceTransaction.php @@ -0,0 +1,18 @@ +setName('') ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) ->setEditPolicy($actor->getPHID()) ->setStatus(self::STATUS_ACTIVE); } public static function getStatusNameMap() { return array( self::STATUS_ACTIVE => pht('Active'), self::STATUS_ARCHIVED => pht('Archived'), ); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', 'status' => 'text32', 'description' => 'text', ), ) + parent::getConfiguration(); } public function getPHIDType() { return SemiStructuredObjectTypePHIDType::TYPECONST; } public function isArchived() { return ($this->getStatus() == self::STATUS_ARCHIVED); } public function getURI() { return urisprintf('/semistruct/type/%d/', $this->getID()); } public function getObjectName() { return pht('Object Type %d', $this->getID()); } public function getIcon() { // TODO return 'fa-bear'; } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new SemiStructuredObjectTypeTransactionEditor(); } public function getApplicationTransactionTemplate() { return new SemiStructuredObjectTypeTransaction(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, // TODO can-create-instances ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return false; } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $this->saveTransaction(); } /* -( PhabricatorFerretInterface )----------------------------------------- */ public function newFerretEngine() { return new SemiStructuredObjectTypeFerretEngine(); } } diff --git a/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php b/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php new file mode 100644 index 0000000..4e0269f --- /dev/null +++ b/src/xaction/instance/SemiStructuredObjectInstanceClassTransaction.php @@ -0,0 +1,38 @@ +getClassID(); + } + + public function applyInternalEffects($object, $value) { + $object->setClassID($value); + } + + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if (!$xactions) { + if (!$object->getClassID()) { + $errors[] = $this->newInvalidError(pht('ClassID 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.')); + } + } + + return $errors; + } +} diff --git a/src/xaction/instance/SemiStructuredObjectInstanceRawDataTransaction.php b/src/xaction/instance/SemiStructuredObjectInstanceRawDataTransaction.php new file mode 100644 index 0000000..9816db4 --- /dev/null +++ b/src/xaction/instance/SemiStructuredObjectInstanceRawDataTransaction.php @@ -0,0 +1,82 @@ +getRawData(); + } + + public function applyInternalEffects($object, $value) { + $object->setRawData($value); + } + + public function getTitle() { + return pht( + '%s updated the content of the object.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s updated the content for object %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function getMailDiffSectionHeader() { + return pht('CHANGES TO CONTENT OF OBJECT INSTANCE'); + } + + public function hasChangeDetailView() { + return true; + } + + public function newChangeDetailView() { + $viewer = $this->getViewer(); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $json = new PhutilJSON(); + // $old_json = $json->encodeFormatted($old); + // $new_json = $json->encodeFormatted($new); + + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setViewer($viewer) + ->setOldText($old) + ->setNewText($new); + } + + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if (!$xactions) { + return $errors; + } + + $json_parser = new PhutilJSONParser(); + + if ($this->isEmptyTextTransaction($object->getRawData(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Object must have content.')); + } + + foreach ($xactions as $xaction) { + $new_value = $xaction->getNewValue(); + + try { + phutil_json_decode($new_value); + } catch (PhutilJSONParserException $ex) { + $errors[] = $this->newInvalidError( + pht('Object body must be valid json!')); + } + } + + return $errors; + } + +} diff --git a/src/xaction/instance/SemiStructuredObjectInstanceTransactionType.php b/src/xaction/instance/SemiStructuredObjectInstanceTransactionType.php new file mode 100644 index 0000000..15a63e7 --- /dev/null +++ b/src/xaction/instance/SemiStructuredObjectInstanceTransactionType.php @@ -0,0 +1,4 @@ +