Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2893416
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
43 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/docs/manual.md b/docs/manual.md
new file mode 100644
index 0000000..e05bc78
--- /dev/null
+++ b/docs/manual.md
@@ -0,0 +1,38 @@
+# Semi-Structured - manage partly-defined objects
+
+## Types and Instances
+
+TODO: add more instructions.
+
+
+## Planned Features
+
+These are some things I plan to add to the application:
+- bulk extract from json to custom field: a way to define a Custom Field, and
+ then systematically populate its value from the json blob.
+- GUI/Wizard for adding custom field - this should be easier.
+- Have Status field for instances, that allows customized values (per class),
+ and also allows to mark some of the values as Archived.
+- A Custom Field that will allow referring to instances of a different Type.
+ For instance, have a Type for "OS" and a Type for "Software", and allow the
+ Software type to have a field of "Supported OS" with a full selector.
+- Customize Icon for type and/or instance.
+- Allow instances to have their own view/edit Policy.
+
+## Export and Import
+
+There's an Export feature from the Instance Search which will produce full data,
+and there are Conduit methods that allow mass export as well.
+
+For import, currently only available method is to use Conduit and make 2 calls
+per each instance - see `examples/load-xkcd.py` for an example.
+
+
+## Dashboards
+
+There's some strange interaction between Dashboards and Instance queries:
+Instance queries can be added to dashboards, but they can't be //updated//
+directly from dashboards.
+Adding a query from the dashboard's Create Panel button will list all saved
+queries for all instance types; It's easier to find the query in the Item List
+page, and use "Add to Dashboard" from the Use Results drop-down.
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 9e4c604..ef30821 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,168 +1,170 @@
<?php
/**
* This file is automatically generated. Use 'arc liberate' to rebuild it.
*
* @generated
* @phutil-library-version 2
*/
phutil_register_library_map(array(
'__library_version__' => 2,
'class' => array(
'SemiStructuredBaseController' => 'controller/SemiStructuredBaseController.php',
'SemiStructuredDAO' => 'storage/SemiStructuredDAO.php',
'SemiStructuredDataApplication' => 'application/SemiStructuredDataApplication.php',
'SemiStructuredInstanceConfiguredCustomField' => 'customfield/SemiStructuredInstanceConfiguredCustomField.php',
'SemiStructuredInstanceConfiguredCustomFieldStorage' => 'storage/SemiStructuredInstanceConfiguredCustomFieldStorage.php',
'SemiStructuredInstanceCustomField' => 'customfield/SemiStructuredInstanceCustomField.php',
'SemiStructuredInstanceCustomFieldNumericIndex' => 'storage/SemiStructuredInstanceCustomFieldNumericIndex.php',
'SemiStructuredInstanceCustomFieldStringIndex' => 'storage/SemiStructuredInstanceCustomFieldStringIndex.php',
'SemiStructuredInstanceEditConduitAPIMethod' => 'conduit/SemiStructuredInstanceEditConduitAPIMethod.php',
'SemiStructuredInstanceSearchConduitAPIMethod' => 'conduit/SemiStructuredInstanceSearchConduitAPIMethod.php',
'SemiStructuredObjectInstance' => 'storage/SemiStructuredObjectInstance.php',
'SemiStructuredObjectInstanceClassTransaction' => 'xaction/instance/SemiStructuredObjectInstanceClassTransaction.php',
'SemiStructuredObjectInstanceController' => 'controller/instance/SemiStructuredObjectInstanceController.php',
'SemiStructuredObjectInstanceDescriptionTransaction' => 'xaction/instance/SemiStructuredObjectInstanceDescriptionTransaction.php',
'SemiStructuredObjectInstanceEditController' => 'controller/instance/SemiStructuredObjectInstanceEditController.php',
'SemiStructuredObjectInstanceEditEngine' => 'editor/SemiStructuredObjectInstanceEditEngine.php',
'SemiStructuredObjectInstanceFerretEngine' => 'search/SemiStructuredObjectInstanceFerretEngine.php',
'SemiStructuredObjectInstanceFulltextEngine' => 'search/SemiStructuredObjectInstanceFulltextEngine.php',
'SemiStructuredObjectInstanceListController' => 'controller/instance/SemiStructuredObjectInstanceListController.php',
'SemiStructuredObjectInstanceMailReceiver' => 'mail/SemiStructuredObjectInstanceMailReceiver.php',
'SemiStructuredObjectInstanceNameTransaction' => 'xaction/instance/SemiStructuredObjectInstanceNameTransaction.php',
'SemiStructuredObjectInstancePHIDType' => 'phid/SemiStructuredObjectInstancePHIDType.php',
'SemiStructuredObjectInstanceQuery' => 'query/SemiStructuredObjectInstanceQuery.php',
'SemiStructuredObjectInstanceRawDataTransaction' => 'xaction/instance/SemiStructuredObjectInstanceRawDataTransaction.php',
'SemiStructuredObjectInstanceReplyHandler' => 'mail/SemiStructuredObjectInstanceReplyHandler.php',
'SemiStructuredObjectInstanceSearchEngine' => 'query/SemiStructuredObjectInstanceSearchEngine.php',
'SemiStructuredObjectInstanceTransaction' => 'storage/SemiStructuredObjectInstanceTransaction.php',
'SemiStructuredObjectInstanceTransactionComment' => 'storage/SemiStructuredObjectInstanceTransactionComment.php',
'SemiStructuredObjectInstanceTransactionEditor' => 'editor/SemiStructuredObjectInstanceTransactionEditor.php',
'SemiStructuredObjectInstanceTransactionQuery' => 'query/SemiStructuredObjectInstanceTransactionQuery.php',
'SemiStructuredObjectInstanceTransactionType' => 'xaction/instance/SemiStructuredObjectInstanceTransactionType.php',
'SemiStructuredObjectInstanceViewController' => 'controller/instance/SemiStructuredObjectInstanceViewController.php',
'SemiStructuredObjectNewInstanceController' => 'controller/class/SemiStructuredObjectNewInstanceController.php',
'SemiStructuredObjectType' => 'storage/SemiStructuredObjectType.php',
'SemiStructuredObjectTypeArchiveController' => 'controller/class/SemiStructuredObjectTypeArchiveController.php',
'SemiStructuredObjectTypeController' => 'controller/class/SemiStructuredObjectTypeController.php',
'SemiStructuredObjectTypeCustomFieldsTransaction' => 'xaction/class/SemiStructuredObjectTypeCustomFieldsTransaction.php',
'SemiStructuredObjectTypeDescriptionTransaction' => 'xaction/class/SemiStructuredObjectTypeDescriptionTransaction.php',
'SemiStructuredObjectTypeEditConduitAPIMethod' => 'conduit/SemiStructuredObjectTypeEditConduitAPIMethod.php',
'SemiStructuredObjectTypeEditController' => 'controller/class/SemiStructuredObjectTypeEditController.php',
'SemiStructuredObjectTypeEditEngine' => 'editor/SemiStructuredObjectTypeEditEngine.php',
'SemiStructuredObjectTypeFerretEngine' => 'search/SemiStructuredObjectTypeFerretEngine.php',
'SemiStructuredObjectTypeFulltextEngine' => 'search/SemiStructuredObjectTypeFulltextEngine.php',
'SemiStructuredObjectTypeListController' => 'controller/class/SemiStructuredObjectTypeListController.php',
'SemiStructuredObjectTypeMailReceiver' => 'mail/SemiStructuredObjectTypeMailReceiver.php',
'SemiStructuredObjectTypeNameTransaction' => 'xaction/class/SemiStructuredObjectTypeNameTransaction.php',
+ 'SemiStructuredObjectTypePHIDSearchField' => 'search/field/SemiStructuredObjectTypePHIDSearchField.php',
'SemiStructuredObjectTypePHIDType' => 'phid/SemiStructuredObjectTypePHIDType.php',
'SemiStructuredObjectTypeQuery' => 'query/SemiStructuredObjectTypeQuery.php',
'SemiStructuredObjectTypeRemarkupRule' => 'remarkup/SemiStructuredObjectTypeRemarkupRule.php',
'SemiStructuredObjectTypeReplyHandler' => 'mail/SemiStructuredObjectTypeReplyHandler.php',
'SemiStructuredObjectTypeSearchConduitAPIMethod' => 'conduit/SemiStructuredObjectTypeSearchConduitAPIMethod.php',
'SemiStructuredObjectTypeSearchEngine' => 'query/SemiStructuredObjectTypeSearchEngine.php',
'SemiStructuredObjectTypeStatusTransaction' => 'xaction/class/SemiStructuredObjectTypeStatusTransaction.php',
'SemiStructuredObjectTypeTransaction' => 'storage/SemiStructuredObjectTypeTransaction.php',
'SemiStructuredObjectTypeTransactionComment' => 'storage/SemiStructuredObjectTypeTransactionComment.php',
'SemiStructuredObjectTypeTransactionEditor' => 'editor/SemiStructuredObjectTypeTransactionEditor.php',
'SemiStructuredObjectTypeTransactionQuery' => 'query/SemiStructuredObjectTypeTransactionQuery.php',
'SemiStructuredObjectTypeTransactionType' => 'xaction/class/SemiStructuredObjectTypeTransactionType.php',
'SemiStructuredObjectTypeViewController' => 'controller/class/SemiStructuredObjectTypeViewController.php',
'SemiStructuredPatchList' => 'storage/SemiStructuredPatchList.php',
'SemiStructuredSchemaSpec' => 'storage/SemiStructuredSchemaSpec.php',
'SemiStructuredStaticSearchField' => '_to_upstream/SemiStructuredStaticSearchField.php',
),
'function' => array(),
'xmap' => array(
'SemiStructuredBaseController' => 'PhabricatorController',
'SemiStructuredDAO' => 'PhabricatorLiskDAO',
'SemiStructuredDataApplication' => 'PhabricatorApplication',
'SemiStructuredInstanceConfiguredCustomField' => array(
'SemiStructuredInstanceCustomField',
'PhabricatorStandardCustomFieldInterface',
),
'SemiStructuredInstanceConfiguredCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
'SemiStructuredInstanceCustomField' => 'PhabricatorCustomField',
'SemiStructuredInstanceCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
'SemiStructuredInstanceCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
'SemiStructuredInstanceEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'SemiStructuredInstanceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'SemiStructuredObjectInstance' => array(
'SemiStructuredDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorSubscribableInterface',
'PhabricatorFlaggableInterface',
'PhabricatorProjectInterface',
'PhabricatorFerretInterface',
'PhabricatorFulltextInterface',
'PhabricatorMentionableInterface',
'PhabricatorConduitResultInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorDestructibleInterface',
),
'SemiStructuredObjectInstanceClassTransaction' => 'SemiStructuredObjectInstanceTransactionType',
'SemiStructuredObjectInstanceController' => 'SemiStructuredBaseController',
'SemiStructuredObjectInstanceDescriptionTransaction' => 'SemiStructuredObjectInstanceTransactionType',
'SemiStructuredObjectInstanceEditController' => 'SemiStructuredBaseController',
'SemiStructuredObjectInstanceEditEngine' => 'PhabricatorEditEngine',
'SemiStructuredObjectInstanceFerretEngine' => 'PhabricatorFerretEngine',
'SemiStructuredObjectInstanceFulltextEngine' => 'PhabricatorFulltextEngine',
- 'SemiStructuredObjectInstanceListController' => 'SemiStructuredObjectTypeController',
+ 'SemiStructuredObjectInstanceListController' => 'SemiStructuredObjectInstanceController',
'SemiStructuredObjectInstanceMailReceiver' => 'PhabricatorObjectMailReceiver',
'SemiStructuredObjectInstanceNameTransaction' => 'SemiStructuredObjectInstanceTransactionType',
'SemiStructuredObjectInstancePHIDType' => 'PhabricatorPHIDType',
'SemiStructuredObjectInstanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'SemiStructuredObjectInstanceRawDataTransaction' => 'SemiStructuredObjectInstanceTransactionType',
'SemiStructuredObjectInstanceReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'SemiStructuredObjectInstanceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'SemiStructuredObjectInstanceTransaction' => 'PhabricatorModularTransaction',
'SemiStructuredObjectInstanceTransactionComment' => 'PhabricatorApplicationTransactionComment',
'SemiStructuredObjectInstanceTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'SemiStructuredObjectInstanceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'SemiStructuredObjectInstanceTransactionType' => 'PhabricatorModularTransactionType',
'SemiStructuredObjectInstanceViewController' => 'SemiStructuredObjectInstanceController',
'SemiStructuredObjectNewInstanceController' => 'SemiStructuredObjectTypeController',
'SemiStructuredObjectType' => array(
'SemiStructuredDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorMentionableInterface',
'PhabricatorSubscribableInterface',
'PhabricatorProjectInterface',
'PhabricatorConduitResultInterface',
'PhabricatorFlaggableInterface',
'PhabricatorDestructibleInterface',
'PhabricatorFerretInterface',
'PhabricatorFulltextInterface',
),
'SemiStructuredObjectTypeArchiveController' => 'SemiStructuredBaseController',
'SemiStructuredObjectTypeController' => 'SemiStructuredBaseController',
'SemiStructuredObjectTypeCustomFieldsTransaction' => 'SemiStructuredObjectTypeTransactionType',
'SemiStructuredObjectTypeDescriptionTransaction' => 'SemiStructuredObjectTypeTransactionType',
'SemiStructuredObjectTypeEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'SemiStructuredObjectTypeEditController' => 'SemiStructuredBaseController',
'SemiStructuredObjectTypeEditEngine' => 'PhabricatorEditEngine',
'SemiStructuredObjectTypeFerretEngine' => 'PhabricatorFerretEngine',
'SemiStructuredObjectTypeFulltextEngine' => 'PhabricatorFulltextEngine',
'SemiStructuredObjectTypeListController' => 'SemiStructuredBaseController',
'SemiStructuredObjectTypeMailReceiver' => 'PhabricatorObjectMailReceiver',
'SemiStructuredObjectTypeNameTransaction' => 'SemiStructuredObjectTypeTransactionType',
+ 'SemiStructuredObjectTypePHIDSearchField' => 'PhabricatorSearchField',
'SemiStructuredObjectTypePHIDType' => 'PhabricatorPHIDType',
'SemiStructuredObjectTypeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'SemiStructuredObjectTypeRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'SemiStructuredObjectTypeReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'SemiStructuredObjectTypeSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'SemiStructuredObjectTypeSearchEngine' => 'PhabricatorApplicationSearchEngine',
'SemiStructuredObjectTypeStatusTransaction' => 'SemiStructuredObjectTypeTransactionType',
'SemiStructuredObjectTypeTransaction' => 'PhabricatorModularTransaction',
'SemiStructuredObjectTypeTransactionComment' => 'PhabricatorApplicationTransactionComment',
'SemiStructuredObjectTypeTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'SemiStructuredObjectTypeTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'SemiStructuredObjectTypeTransactionType' => 'PhabricatorModularTransactionType',
'SemiStructuredObjectTypeViewController' => 'SemiStructuredObjectTypeController',
'SemiStructuredPatchList' => 'PhabricatorSQLPatchList',
'SemiStructuredSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'SemiStructuredStaticSearchField' => 'PhabricatorSearchField',
),
));
diff --git a/src/application/SemiStructuredDataApplication.php b/src/application/SemiStructuredDataApplication.php
index 20f0932..13a3f95 100644
--- a/src/application/SemiStructuredDataApplication.php
+++ b/src/application/SemiStructuredDataApplication.php
@@ -1,67 +1,72 @@
<?php
final class SemiStructuredDataApplication extends PhabricatorApplication {
const ICON_OBJECT_TYPE = 'fa-camera';
const ICON_OBJECT_INSTANCE = 'fa-dribbble';
const ICON_OBJECT_TYPE_MANY = 'fa-todo';
const ICON_OBJECT_INSTANCE_MANY = 'fa-group';
const ICON_APPLICATION = 'fa-wpforms';
public function getName() {
return pht('Semi-Structured');
}
public function getShortDescription() {
return pht('Work with evolving data sets.');
}
public function getIcon() {
return self::ICON_APPLICATION;
}
public function getBaseURI() {
return '/semistruct/';
}
public function getRemarkupRules() {
// I think I want to use this method to allow classes
// to define user-provided "RemarkupRule" for their instances.
return array(
new SemiStructuredObjectTypeRemarkupRule(),
);
}
public function getRoutes() {
return array(
'/semistruct/' => array(
$this->getQueryRoutePattern() =>
'SemiStructuredObjectTypeListController',
$this->getEditRoutePattern('editclass/') =>
'SemiStructuredObjectTypeEditController',
'type/(?:(?P<id>\d+)/)?' => array(
'' => 'SemiStructuredObjectTypeViewController',
- 'items/' => 'SemiStructuredObjectInstanceListController',
- $this->getQueryRoutePattern() =>
- 'SemiStructuredObjectInstanceListController',
'archive/' => 'SemiStructuredObjectTypeArchiveController',
),
// If the ID field is called `id`, the EditEngine thinks this is
// editing an existing object.
'type/(?:(?P<typeid>\d+)/)?' => array(
'new/' => 'SemiStructuredObjectNewInstanceController',
+ 'items/' => 'SemiStructuredObjectInstanceListController',
+ $this->getQueryRoutePattern() =>
+ 'SemiStructuredObjectInstanceListController',
+ ),
+
+ 'instance/' => array(
+ $this->getQueryRoutePattern() =>
+ 'SemiStructuredObjectInstanceListController',
),
$this->getEditRoutePattern('editinstance/') =>
'SemiStructuredObjectInstanceEditController',
'instance/(?:(?P<id>\d+)/)?' =>
'SemiStructuredObjectInstanceViewController',
),
);
}
}
diff --git a/src/controller/SemiStructuredBaseController.php b/src/controller/SemiStructuredBaseController.php
index d9fd017..c2871e7 100644
--- a/src/controller/SemiStructuredBaseController.php
+++ b/src/controller/SemiStructuredBaseController.php
@@ -1,15 +1,10 @@
<?php
abstract class SemiStructuredBaseController extends PhabricatorController {
- public function buildApplicationMenu() {
- return $this->newApplicationMenu();
- // ->setSearchEngine(new PhabricatorBadgesSearchEngine());
- }
-
public function shouldAllowPublic() {
return true;
}
}
diff --git a/src/controller/class/SemiStructuredObjectTypeController.php b/src/controller/class/SemiStructuredObjectTypeController.php
index 8b206cf..eba01bf 100644
--- a/src/controller/class/SemiStructuredObjectTypeController.php
+++ b/src/controller/class/SemiStructuredObjectTypeController.php
@@ -1,89 +1,87 @@
<?php
abstract class SemiStructuredObjectTypeController
extends SemiStructuredBaseController {
private $objectType;
public function setObjectType(SemiStructuredObjectType $object_type) {
$this->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(SemiStructuredDataApplication::ICON_OBJECT_TYPE);
}
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(),
SemiStructuredDataApplication::ICON_OBJECT_TYPE);
$nav->addFilter(
'instances',
pht('View Instances'),
$this->getApplicationURI("/type/{$id}/items/"),
'fa-group');
$nav->selectFilter($filter);
return $nav;
}
}
diff --git a/src/controller/instance/SemiStructuredObjectInstanceController.php b/src/controller/instance/SemiStructuredObjectInstanceController.php
index 9f45643..eb0eeea 100644
--- a/src/controller/instance/SemiStructuredObjectInstanceController.php
+++ b/src/controller/instance/SemiStructuredObjectInstanceController.php
@@ -1,74 +1,86 @@
<?php
abstract class SemiStructuredObjectInstanceController
extends SemiStructuredBaseController {
private $object;
private $objectType;
public function setObject(SemiStructuredObjectInstance $object) {
$this->object = $object;
+ $this->setObjectType($object->getClass());
return $this;
}
public function getObject() {
return $this->object;
}
+ public function setObjectType(SemiStructuredObjectType $object_type) {
+ $this->objectType = $object_type;
+ return $this;
+ }
+
+ public function getObjectType() {
+ return $this->objectType;
+ }
+
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();
+ $object_type = $this->getObjectType();
$can_create = true;
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(
$object_type->getName(),
$object_type->getURI('items/'));
- $crumbs->addTextCrumb(
- pht('%d %s', $object->getID(), $object->getName()), $object->getURI());
+ if ($object) {
+ $crumbs->addTextCrumb(
+ pht('%d %s', $object->getID(), $object->getName()), $object->getURI());
+ }
$crumbs->setBorder(true);
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create %s', $object_type->getName()))
->setHref($object_type->getURI('new/'))
->setIcon('fa-plus-square')
->setWorkflow(!$can_create)
->setDisabled(!$can_create));
return $crumbs;
}
}
diff --git a/src/controller/instance/SemiStructuredObjectInstanceListController.php b/src/controller/instance/SemiStructuredObjectInstanceListController.php
index 0f5a8a2..51d71af 100644
--- a/src/controller/instance/SemiStructuredObjectInstanceListController.php
+++ b/src/controller/instance/SemiStructuredObjectInstanceListController.php
@@ -1,60 +1,57 @@
<?php
final class SemiStructuredObjectInstanceListController
- extends SemiStructuredObjectTypeController {
+ extends SemiStructuredObjectInstanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- $object_type = id(new SemiStructuredObjectTypeQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->executeOne();
+ $object_type = SemiStructuredObjectTypeQuery::loadOneById(
+ $viewer,
+ $request->getURIData('typeid'));
if (!$object_type) {
return new Aphront404Response();
}
$this->setObjectType($object_type);
$query_key = $request->getURIData('queryKey');
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($query_key)
->setSearchEngine(
id(new SemiStructuredObjectInstanceSearchEngine())
->setObjectType($object_type))
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView($filter = null) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new SemiStructuredObjectInstanceSearchEngine())
->setObjectType($this->getObjectType())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter($filter);
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$object_type = $this->getObjectType();
id(new SemiStructuredObjectInstanceEditEngine())
->setObjectType($object_type)
->setViewer($this->getViewer())
->addActionToCrumbs($crumbs);
return $crumbs;
}
}
diff --git a/src/query/SemiStructuredObjectInstanceSearchEngine.php b/src/query/SemiStructuredObjectInstanceSearchEngine.php
index 38d6371..53e5c29 100644
--- a/src/query/SemiStructuredObjectInstanceSearchEngine.php
+++ b/src/query/SemiStructuredObjectInstanceSearchEngine.php
@@ -1,207 +1,311 @@
<?php
final class SemiStructuredObjectInstanceSearchEngine
extends PhabricatorApplicationSearchEngine {
private $objectType;
public function setObjectType(SemiStructuredObjectType $object_type) {
$this->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) {
- return "/semistruct/type/{$this->getObjectType()->getID()}/{$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);
- $viewer = $this->requireViewer();
+
+ $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));
$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/SemiStructuredObjectTypeQuery.php b/src/query/SemiStructuredObjectTypeQuery.php
index 9103282..48c1e55 100644
--- a/src/query/SemiStructuredObjectTypeQuery.php
+++ b/src/query/SemiStructuredObjectTypeQuery.php
@@ -1,84 +1,107 @@
<?php
final class SemiStructuredObjectTypeQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $statuses;
private $canEdit;
public function withIDs(array $ids) {
$this->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();
}
+ // There might already be a similar solution in the Phorge code base.
+ public static function loadOneById(PhabricatorUser $viewer, $id) {
+ $viewer_key = $viewer->getCacheFragment();
+ $cache = PhabricatorCaches::getRequestCache();
+ $cache_key = self::class.'ById';
+
+ $collection = $cache->getKey($cache_key, array());
+ $value = idxv($collection, array($viewer_key, $id));
+
+ if ($value) {
+ return $value;
+ }
+
+ $value = (new self())
+ ->setViewer($viewer)
+ ->withIds(array($id))
+ ->executeOne();
+
+ $collection[$viewer_key][$id] = $value;
+ $cache->setKey($cache_key, $collection);
+ return $value;
+ }
+
protected function didFilterPage(array $items) {
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/search/field/SemiStructuredObjectTypePHIDSearchField.php b/src/search/field/SemiStructuredObjectTypePHIDSearchField.php
new file mode 100644
index 0000000..89fda6e
--- /dev/null
+++ b/src/search/field/SemiStructuredObjectTypePHIDSearchField.php
@@ -0,0 +1,41 @@
+<?php
+
+final class SemiStructuredObjectTypePHIDSearchField
+ extends PhabricatorSearchField {
+
+ private $value;
+
+ public function setValue(string $value) {
+ $this->value = $value;
+ return $this;
+ }
+
+ public function readValueFromRequest(AphrontRequest $request) {
+ $id = $request->getURIData('typeid');
+ if ($id === null) {
+ return null;
+ }
+
+ $object_type = SemiStructuredObjectTypeQuery::loadOneById(
+ $this->getViewer(),
+ $id);
+ return array($object_type->getPHID());
+ }
+
+
+ protected function getValueFromRequest(AphrontRequest $request, $key) {
+ throw new Exception('Did not expect call');
+ }
+
+
+ protected function getValueForControl() {
+ return $this->value;
+ }
+
+ protected function newControl() {
+ return id(new AphrontFormMarkupControl())
+ ->setValue($this->value);
+ }
+
+
+}
diff --git a/src/storage/SemiStructuredObjectType.php b/src/storage/SemiStructuredObjectType.php
index 296f30c..f060e03 100644
--- a/src/storage/SemiStructuredObjectType.php
+++ b/src/storage/SemiStructuredObjectType.php
@@ -1,204 +1,202 @@
<?php
final class SemiStructuredObjectType extends SemiStructuredDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface,
// PhabricatorMarkupInterface, ?
PhabricatorMentionableInterface,
PhabricatorSubscribableInterface,
PhabricatorProjectInterface,
PhabricatorConduitResultInterface,
PhabricatorFlaggableInterface,
PhabricatorDestructibleInterface,
PhabricatorFerretInterface,
PhabricatorFulltextInterface {
protected $viewPolicy;
protected $editPolicy;
protected $name;
protected $status;
protected $description;
protected $customFieldsConfig = array();
-// icon/logo
-
-
const STATUS_ACTIVE = 'active';
const STATUS_ARCHIVED = 'archived';
public static function initializeNewObjectType(PhabricatorUser $actor) {
return id(new self())
->setName('')
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setEditPolicy($actor->getPHID())
->setStatus(self::STATUS_ACTIVE);
}
public function getCustomFieldsConfig() {
if (!$this->customFieldsConfig) {
return array();
}
return $this->customFieldsConfig;
}
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_SERIALIZATION => array(
'customFieldsConfig' => self::SERIALIZATION_JSON,
),
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($path = '') {
return urisprintf('/semistruct/type/%d/%s', $this->getID(), $path);
}
public function getObjectName() {
return pht('Object Type %d', $this->getID());
}
public function getIcon() {
// TODO copy the feature from Projects, where you can put a custom image
// or select from a list.
return SemiStructuredDataApplication::ICON_OBJECT_TYPE;
}
/* -( 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;
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the object type.')),
(new PhabricatorConduitSearchFieldSpecification())
->setKey('description')
->setType('remarkup')
->setDescription(pht('The description.')),
(new PhabricatorConduitSearchFieldSpecification())
->setKey('status')
->setType('string')
->setDescription(pht('Status of this object data.')),
(new PhabricatorConduitSearchFieldSpecification())
->setKey('customFieldsConfig')
->setType('map<string, wild>')
->setDescription(pht('Custom Fields definition')),
);
}
public function getFieldValuesForConduit() {
return array(
'name' => $this->getName(),
'description' => array(
'raw' => $this->getDescription(),
),
'status' => $this->getStatus(),
'customFieldsConfig' => $this->getCustomFieldsConfig(),
);
}
public function getConduitSearchAttachments() {
return array();
}
/* -( PhabricatorFerretInterface )----------------------------------------- */
public function newFerretEngine() {
return new SemiStructuredObjectTypeFerretEngine();
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new SemiStructuredObjectTypeFulltextEngine();
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
+ // TODO destroy all instances....
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return false;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 18:21 (2 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1108595
Default Alt Text
(43 KB)
Attached To
Mode
R6 Semi Structured
Attached
Detach File
Event Timeline
Log In to Comment