diff --git a/resources/sql/20230427.semistructured.1.objecttype.sql b/resources/sql/20230427.semistructured.1.objecttype.sql new file mode 100644 index 0000000..6e9fbc0 --- /dev/null +++ b/resources/sql/20230427.semistructured.1.objecttype.sql @@ -0,0 +1,24 @@ +CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED 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}, + + UNIQUE KEY `key_phid` (phid) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; + +-- CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttypetransaction +-- LIKE {$NAMESPACE}_file.file_transaction; + +CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_ffield + LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_ffield; +CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_fdocument + LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_fdocument; +CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_fngrams + LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_fngrams; +CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_fngrams_common + LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_fngrams_common; diff --git a/resources/sql/20230427.semistructured.1.objectinstance.sql b/resources/sql/20230427.semistructured.2.objectinstance.sql similarity index 78% rename from resources/sql/20230427.semistructured.1.objectinstance.sql rename to resources/sql/20230427.semistructured.2.objectinstance.sql index c3ada15..f935943 100644 --- a/resources/sql/20230427.semistructured.1.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, status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT}, rawData LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, UNIQUE KEY `key_phid` (phid), 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 +-- LIKE {$NAMESPACE}_file.file_transaction; diff --git a/resources/sql/20230427.semistructured.2.objecttype.sql b/resources/sql/20230427.semistructured.2.objecttype.sql deleted file mode 100644 index 232ec94..0000000 --- a/resources/sql/20230427.semistructured.2.objecttype.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype ( - id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, - phid VARBINARY(64) NOT NULL, - dateCreated INT UNSIGNED NOT NULL, - dateModified INT UNSIGNED 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}, - - UNIQUE KEY `key_phid` (phid) -) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; - -CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttypetransaction - LIKE {$NAMESPACE}_file.file_transaction; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0baa48d..872b200 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,44 +1,55 @@ 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', 'SemiStructuredObjectInstancePHIDType' => 'applications/semistruct/phid/SemiStructuredObjectInstancePHIDType.php', + 'SemiStructuredObjectInstanceQuery' => 'applications/semistruct/query/SemiStructuredObjectInstanceQuery.php', 'SemiStructuredObjectType' => 'applications/semistruct/storage/SemiStructuredObjectType.php', + 'SemiStructuredObjectTypeFerretEngine' => 'applications/semistruct/engine/SemiStructuredObjectTypeFerretEngine.php', + 'SemiStructuredObjectTypeListController' => 'applications/semistruct/controller/SemiStructuredObjectTypeListController.php', 'SemiStructuredObjectTypePHIDType' => 'applications/semistruct/phid/SemiStructuredObjectTypePHIDType.php', + 'SemiStructuredObjectTypeQuery' => 'applications/semistruct/query/SemiStructuredObjectTypeQuery.php', + 'SemiStructuredObjectTypeSearchEngine' => 'applications/semistruct/query/SemiStructuredObjectTypeSearchEngine.php', 'SemiStructuredPatchList' => 'applications/semistruct/storage/SemiStructuredPatchList.php', ), 'function' => array(), 'xmap' => array( 'SemiStructuredBaseController' => 'PhabricatorController', 'SemiStructuredDAO' => 'PhabricatorLiskDAO', 'SemiStructuredDataApplication' => 'PhabricatorApplication', 'SemiStructuredObjectInstance' => array( 'SemiStructuredDAO', 'PhabricatorPolicyInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', ), 'SemiStructuredObjectInstancePHIDType' => 'PhabricatorPHIDType', + 'SemiStructuredObjectInstanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'SemiStructuredObjectType' => array( 'SemiStructuredDAO', 'PhabricatorPolicyInterface', 'PhabricatorApplicationTransactionInterface', 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorFerretInterface', ), + 'SemiStructuredObjectTypeFerretEngine' => 'PhabricatorFerretEngine', + 'SemiStructuredObjectTypeListController' => 'SemiStructuredBaseController', 'SemiStructuredObjectTypePHIDType' => 'PhabricatorPHIDType', + 'SemiStructuredObjectTypeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'SemiStructuredObjectTypeSearchEngine' => 'PhabricatorApplicationSearchEngine', 'SemiStructuredPatchList' => 'PhabricatorSQLPatchList', ), )); diff --git a/src/application/SemiStructuredDataApplication.php b/src/application/SemiStructuredDataApplication.php index 873b6b1..4c0022f 100644 --- a/src/application/SemiStructuredDataApplication.php +++ b/src/application/SemiStructuredDataApplication.php @@ -1,23 +1,25 @@ array( + '(?:query/(?P[^/]+)/)?' => + 'SemiStructuredObjectTypeListController', ), ); } } diff --git a/src/controller/SemiStructuredBaseController.php b/src/controller/SemiStructuredBaseController.php index 4ad8018..ab83928 100644 --- a/src/controller/SemiStructuredBaseController.php +++ b/src/controller/SemiStructuredBaseController.php @@ -1,11 +1,16 @@ newApplicationMenu() // ->setSearchEngine(new PhabricatorBadgesSearchEngine()); ; } + public function shouldAllowPublic() { + return true; + } + + } diff --git a/src/controller/SemiStructuredObjectTypeListController.php b/src/controller/SemiStructuredObjectTypeListController.php new file mode 100644 index 0000000..f9d30fa --- /dev/null +++ b/src/controller/SemiStructuredObjectTypeListController.php @@ -0,0 +1,41 @@ +getViewer(); + $query_key = $request->getURIData('queryKey'); + + $controller = id(new PhabricatorApplicationSearchController()) + ->setQueryKey($query_key) + ->setSearchEngine(new SemiStructuredObjectTypeSearchEngine()) + ->setNavigation($this->buildSideNavView()); + return $this->delegateToController($controller); + } + + public function buildSideNavView() { + $user = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + + id(new SemiStructuredObjectTypeSearchEngine()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); + + return $nav; + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + +// id(new SemiStructuredObjectTypeEditEngine()) // TODO + // ->setViewer($this->getViewer()) + // ->addActionToCrumbs($crumbs); + + return $crumbs; + } +} diff --git a/src/engine/SemiStructuredObjectTypeFerretEngine.php b/src/engine/SemiStructuredObjectTypeFerretEngine.php new file mode 100644 index 0000000..20510df --- /dev/null +++ b/src/engine/SemiStructuredObjectTypeFerretEngine.php @@ -0,0 +1,18 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withClassPHIDs(array $classPHIDs) { + $this->classPHIDs = $classPHIDs; + 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/query/SemiStructuredObjectTypeQuery.php b/src/query/SemiStructuredObjectTypeQuery.php new file mode 100644 index 0000000..4fade63 --- /dev/null +++ b/src/query/SemiStructuredObjectTypeQuery.php @@ -0,0 +1,87 @@ +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/query/SemiStructuredObjectTypeSearchEngine.php b/src/query/SemiStructuredObjectTypeSearchEngine.php new file mode 100644 index 0000000..82b09a9 --- /dev/null +++ b/src/query/SemiStructuredObjectTypeSearchEngine.php @@ -0,0 +1,153 @@ +setKey('statuses') + ->setLabel(pht('Status')) + ->setOptions(PhabricatorDashboard::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['all'] = pht('All Types'); + $names['open'] = pht('Active 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 renderResultList( + array $items, + PhabricatorSavedQuery $query, + array $handles) { + + $viewer = $this->requireViewer(); + + $phids = array(); + foreach ($items as $item) { + // $author_phid = $dashboard->getAuthorPHID(); + // if ($author_phid) { + // $phids[] = $author_phid; + // } + } + + $handles = $viewer->loadHandles($phids); + + 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 $item) { + $item = id(new PHUIObjectItemView()) + ->setViewer($viewer) + ->setObjectName($item->getObjectName()) + ->setHeader($item->getName()) + ->setHref($item->getURI()) + ->setObject($item); + + if ($item->isArchived()) { + $item->setDisabled(true); + $bg_color = 'bg-grey'; + } else { + $bg_color = 'bg-dark'; + } + + $icon = id(new PHUIIconView()) + ->setIcon($item->getIcon()) + ->setBackground($bg_color); + $item->setImageIcon($icon); + $item->setEpoch($item->getDateModified()); + + + $phid = $item->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 object types found.')); + + return $result; + } + + +} diff --git a/src/storage/SemiStructuredObjectType.php b/src/storage/SemiStructuredObjectType.php index 356e0ba..fcf849d 100644 --- a/src/storage/SemiStructuredObjectType.php +++ b/src/storage/SemiStructuredObjectType.php @@ -1,99 +1,106 @@ setName('') ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) ->setEditPolicy($actor->getPHID()) // TODO config for deflt ->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(); } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new PhabricatorDashboardPanelTransactionEditor(); } public function getApplicationTransactionTemplate() { return new PhabricatorDashboardPanelTransaction(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } 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(); + } + }