Page MenuHomePhabricator

D15010.diff
No OneTemporary

D15010.diff

diff --git a/resources/sql/autopatches/20160113.propanel.1.storage.sql b/resources/sql/autopatches/20160113.propanel.1.storage.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160113.propanel.1.storage.sql
@@ -0,0 +1,13 @@
+CREATE TABLE {$NAMESPACE}_search.search_profilepanelconfiguration (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ profilePHID VARBINARY(64) NOT NULL,
+ panelKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
+ builtinKey VARCHAR(64) COLLATE {$COLLATE_TEXT},
+ panelOrder INT UNSIGNED,
+ visibility VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
+ panelProperties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ KEY `key_profile` (profilePHID, panelOrder)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20160113.propanel.2.xaction.sql b/resources/sql/autopatches/20160113.propanel.2.xaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160113.propanel.2.xaction.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_search.search_profilepanelconfigurationtransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ objectPHID VARBINARY(64) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ commentPHID VARBINARY(64) DEFAULT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
+ oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ KEY `key_object` (`objectPHID`)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2831,7 +2831,10 @@
'PhabricatorProfilePanelConfiguration' => 'applications/search/storage/PhabricatorProfilePanelConfiguration.php',
'PhabricatorProfilePanelConfigurationQuery' => 'applications/search/query/PhabricatorProfilePanelConfigurationQuery.php',
'PhabricatorProfilePanelConfigurationTransaction' => 'applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php',
+ 'PhabricatorProfilePanelEditEngine' => 'applications/search/editor/PhabricatorProfilePanelEditEngine.php',
+ 'PhabricatorProfilePanelEditor' => 'applications/search/editor/PhabricatorProfilePanelEditor.php',
'PhabricatorProfilePanelEngine' => 'applications/search/engine/PhabricatorProfilePanelEngine.php',
+ 'PhabricatorProfilePanelIconSet' => 'applications/search/profilepanel/PhabricatorProfilePanelIconSet.php',
'PhabricatorProfilePanelInterface' => 'applications/search/interface/PhabricatorProfilePanelInterface.php',
'PhabricatorProfilePanelPHIDType' => 'applications/search/phidtype/PhabricatorProfilePanelPHIDType.php',
'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php',
@@ -2896,6 +2899,7 @@
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php',
'PhabricatorProjectPHIDResolver' => 'applications/phid/resolver/PhabricatorProjectPHIDResolver.php',
+ 'PhabricatorProjectPanelController' => 'applications/project/controller/PhabricatorProjectPanelController.php',
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
'PhabricatorProjectProjectHasMemberEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasMemberEdgeType.php',
'PhabricatorProjectProjectHasObjectEdgeType' => 'applications/project/edge/PhabricatorProjectProjectHasObjectEdgeType.php',
@@ -7188,10 +7192,14 @@
'PhabricatorSearchDAO',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
+ 'PhabricatorApplicationTransactionInterface',
),
'PhabricatorProfilePanelConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorProfilePanelConfigurationTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhabricatorProfilePanelEditEngine' => 'PhabricatorEditEngine',
+ 'PhabricatorProfilePanelEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorProfilePanelEngine' => 'Phobject',
+ 'PhabricatorProfilePanelIconSet' => 'PhabricatorIconSet',
'PhabricatorProfilePanelPHIDType' => 'PhabricatorPHIDType',
'PhabricatorProject' => array(
'PhabricatorProjectDAO',
@@ -7277,6 +7285,7 @@
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectPHIDResolver' => 'PhabricatorPHIDResolver',
+ 'PhabricatorProjectPanelController' => 'PhabricatorProjectController',
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
'PhabricatorProjectProjectHasMemberEdgeType' => 'PhabricatorEdgeType',
'PhabricatorProjectProjectHasObjectEdgeType' => 'PhabricatorEdgeType',
diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php
--- a/src/applications/base/PhabricatorApplication.php
+++ b/src/applications/base/PhabricatorApplication.php
@@ -635,4 +635,18 @@
return $base.'(?:query/(?P<queryKey>[^/]+)/)?';
}
+ protected function getPanelRouting($controller) {
+ $edit_route = $this->getEditRoutePattern();
+
+ return array(
+ '(?P<panelAction>view)/(?P<panelID>[^/]+)/' => $controller,
+ '(?P<panelAction>hide)/(?P<panelID>[^/]+)/' => $controller,
+ '(?P<panelAction>configure)/' => $controller,
+ '(?P<panelAction>edit)/'.$edit_route => $controller,
+ '(?P<panelAction>new)/(?<panelKey>[^/]+)/'.$edit_route => $controller,
+ '(?P<panelAction>builtin)/(?<panelID>[^/]+)/'.$edit_route
+ => $controller,
+ );
+ }
+
}
diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php
--- a/src/applications/project/application/PhabricatorProjectApplication.php
+++ b/src/applications/project/application/PhabricatorProjectApplication.php
@@ -61,6 +61,8 @@
=> 'PhabricatorProjectEditPictureController',
$this->getEditRoutePattern('edit/')
=> 'PhabricatorProjectEditController',
+ '(?P<projectID>[1-9]\d*)/panel/'
+ => $this->getPanelRouting('PhabricatorProjectPanelController'),
'subprojects/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectSubprojectsController',
'milestones/(?P<id>[1-9]\d*)/'
diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php
--- a/src/applications/project/controller/PhabricatorProjectController.php
+++ b/src/applications/project/controller/PhabricatorProjectController.php
@@ -18,7 +18,10 @@
$viewer = $this->getViewer();
$request = $this->getRequest();
- $id = $request->getURIData('id');
+ $id = nonempty(
+ $request->getURIData('projectID'),
+ $request->getURIData('id'));
+
$slug = $request->getURIData('slug');
if ($slug) {
diff --git a/src/applications/project/controller/PhabricatorProjectPanelController.php b/src/applications/project/controller/PhabricatorProjectPanelController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectPanelController.php
@@ -0,0 +1,21 @@
+<?php
+
+final class PhabricatorProjectPanelController
+ extends PhabricatorProjectController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $response = $this->loadProject();
+ if ($response) {
+ return $response;
+ }
+
+ $viewer = $this->getViewer();
+ $project = $this->getProject();
+
+ return id(new PhabricatorProfilePanelEngine())
+ ->setProfileObject($project)
+ ->setController($this)
+ ->buildResponse();
+ }
+
+}
diff --git a/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php b/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php
--- a/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php
+++ b/src/applications/project/profilepanel/PhabricatorProjectDetailsProfilePanel.php
@@ -5,6 +5,36 @@
const PANELKEY = 'project.details';
+ public function getPanelTypeName() {
+ return pht('Project Details');
+ }
+
+ private function getDefaultName() {
+ return pht('Project Details');
+ }
+
+ public function getDisplayName(
+ PhabricatorProfilePanelConfiguration $config) {
+ $name = $config->getPanelProperty('name');
+
+ if (strlen($name)) {
+ return $name;
+ }
+
+ return $this->getDefaultName();
+ }
+
+ public function buildEditEngineFields(
+ PhabricatorProfilePanelConfiguration $config) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setPlaceholder($this->getDefaultName())
+ ->setValue($config->getPanelProperty('name')),
+ );
+ }
+
protected function newNavigationMenuItems(
PhabricatorProfilePanelConfiguration $config) {
diff --git a/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php b/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php
--- a/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php
+++ b/src/applications/project/profilepanel/PhabricatorProjectMembersProfilePanel.php
@@ -5,6 +5,36 @@
const PANELKEY = 'project.members';
+ public function getPanelTypeName() {
+ return pht('Project Members');
+ }
+
+ private function getDefaultName() {
+ return pht('Members');
+ }
+
+ public function getDisplayName(
+ PhabricatorProfilePanelConfiguration $config) {
+ $name = $config->getPanelProperty('name');
+
+ if (strlen($name)) {
+ return $name;
+ }
+
+ return $this->getDefaultName();
+ }
+
+ public function buildEditEngineFields(
+ PhabricatorProfilePanelConfiguration $config) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setPlaceholder($this->getDefaultName())
+ ->setValue($config->getPanelProperty('name')),
+ );
+ }
+
protected function newNavigationMenuItems(
PhabricatorProfilePanelConfiguration $config) {
@@ -12,7 +42,7 @@
$id = $project->getID();
- $name = pht('Members');
+ $name = $this->getDisplayName($config);
$icon = 'fa-group';
$href = "/project/members/{$id}/";
diff --git a/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php b/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php
--- a/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php
+++ b/src/applications/project/profilepanel/PhabricatorProjectWorkboardProfilePanel.php
@@ -5,6 +5,36 @@
const PANELKEY = 'project.workboard';
+ public function getPanelTypeName() {
+ return pht('Project Workboard');
+ }
+
+ private function getDefaultName() {
+ return pht('Workboard');
+ }
+
+ public function getDisplayName(
+ PhabricatorProfilePanelConfiguration $config) {
+ $name = $config->getPanelProperty('name');
+
+ if (strlen($name)) {
+ return $name;
+ }
+
+ return $this->getDefaultName();
+ }
+
+ public function buildEditEngineFields(
+ PhabricatorProfilePanelConfiguration $config) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setPlaceholder($this->getDefaultName())
+ ->setValue($config->getPanelProperty('name')),
+ );
+ }
+
protected function newNavigationMenuItems(
PhabricatorProfilePanelConfiguration $config) {
$viewer = $this->getViewer();
@@ -29,7 +59,7 @@
$id = $project->getID();
$href = "/project/board/{$id}/";
- $name = pht('Workboard');
+ $name = $this->getDisplayName($config);
$item = id(new PHUIListItemView())
->setRenderNameAsTooltip(true)
diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php
--- a/src/applications/project/storage/PhabricatorProject.php
+++ b/src/applications/project/storage/PhabricatorProject.php
@@ -658,36 +658,36 @@
public function getBuiltinProfilePanels() {
$panels = array();
- $panels[] = id(new PhabricatorProfilePanelConfiguration())
+ $panels[] = PhabricatorProfilePanelConfiguration::initializeNewBuiltin()
->setBuiltinKey(self::PANEL_PROFILE)
->setPanelKey(PhabricatorProjectDetailsProfilePanel::PANELKEY);
- $panels[] = id(new PhabricatorProfilePanelConfiguration())
+ $panels[] = PhabricatorProfilePanelConfiguration::initializeNewBuiltin()
->setBuiltinKey(self::PANEL_WORKBOARD)
->setPanelKey(PhabricatorProjectWorkboardProfilePanel::PANELKEY);
// TODO: This is temporary.
- $href = urisprintf(
+ $uri = urisprintf(
'/maniphest/?statuses=open()&projects=%s#R',
$this->getPHID());
- $panels[] = id(new PhabricatorProfilePanelConfiguration())
+ $panels[] = PhabricatorProfilePanelConfiguration::initializeNewBuiltin()
->setBuiltinKey('tasks')
->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY)
- ->setPanelProperty('icon', 'fa-anchor')
+ ->setPanelProperty('icon', 'maniphest')
->setPanelProperty('name', pht('Open Tasks'))
- ->setPanelProperty('href', $href);
+ ->setPanelProperty('uri', $uri);
// TODO: This is temporary.
$id = $this->getID();
- $panels[] = id(new PhabricatorProfilePanelConfiguration())
+ $panels[] = PhabricatorProfilePanelConfiguration::initializeNewBuiltin()
->setBuiltinKey('feed')
->setPanelKey(PhabricatorLinkProfilePanel::PANELKEY)
- ->setPanelProperty('icon', 'fa-newspaper-o')
+ ->setPanelProperty('icon', 'feed')
->setPanelProperty('name', pht('Feed'))
- ->setPanelProperty('href', "/project/feed/{$id}/");
+ ->setPanelProperty('uri', "/project/feed/{$id}/");
- $panels[] = id(new PhabricatorProfilePanelConfiguration())
+ $panels[] = PhabricatorProfilePanelConfiguration::initializeNewBuiltin()
->setBuiltinKey(self::PANEL_MEMBERS)
->setPanelKey(PhabricatorProjectMembersProfilePanel::PANELKEY);
diff --git a/src/applications/search/editor/PhabricatorProfilePanelEditEngine.php b/src/applications/search/editor/PhabricatorProfilePanelEditEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/editor/PhabricatorProfilePanelEditEngine.php
@@ -0,0 +1,136 @@
+<?php
+
+final class PhabricatorProfilePanelEditEngine
+ extends PhabricatorEditEngine {
+
+ const ENGINECONST = 'search.profilepanel';
+
+ private $panelEngine;
+ private $profileObject;
+ private $newPanelConfiguration;
+ private $isBuiltin;
+
+ public function isEngineConfigurable() {
+ return false;
+ }
+
+ public function setPanelEngine(PhabricatorProfilePanelEngine $engine) {
+ $this->panelEngine = $engine;
+ return $this;
+ }
+
+ public function getPanelEngine() {
+ return $this->panelEngine;
+ }
+
+ public function setProfileObject(
+ PhabricatorProfilePanelInterface $profile_object) {
+ $this->profileObject = $profile_object;
+ return $this;
+ }
+
+ public function getProfileObject() {
+ return $this->profileObject;
+ }
+
+ public function setNewPanelConfiguration(
+ PhabricatorProfilePanelConfiguration $configuration) {
+ $this->newPanelConfiguration = $configuration;
+ return $this;
+ }
+
+ public function getNewPanelConfiguration() {
+ return $this->newPanelConfiguration;
+ }
+
+ public function setIsBuiltin($is_builtin) {
+ $this->isBuiltin = $is_builtin;
+ return $this;
+ }
+
+ public function getIsBuiltin() {
+ return $this->isBuiltin;
+ }
+
+ public function getEngineName() {
+ return pht('Profile Panels');
+ }
+
+ public function getSummaryHeader() {
+ return pht('Edit Profile Panel Configurations');
+ }
+
+ public function getSummaryText() {
+ return pht('This engine is used to modify menu items on profiles.');
+ }
+
+ public function getEngineApplicationClass() {
+ return 'PhabricatorSearchApplication';
+ }
+
+ protected function newEditableObject() {
+ if (!$this->newPanelConfiguration) {
+ throw new Exception(
+ pht('Profile panels can not be generated without an object context.'));
+ }
+
+ return clone $this->newPanelConfiguration;
+ }
+
+ protected function newObjectQuery() {
+ return id(new PhabricatorProfilePanelConfigurationQuery());
+ }
+
+ protected function getObjectCreateTitleText($object) {
+ if ($this->getIsBuiltin()) {
+ return pht('Edit Builtin Item');
+ } else {
+ return pht('Create Menu Item');
+ }
+ }
+
+ protected function getObjectCreateButtonText($object) {
+ if ($this->getIsBuiltin()) {
+ return pht('Save Changes');
+ } else {
+ return pht('Create Menu Item');
+ }
+ }
+
+ protected function getObjectEditTitleText($object) {
+ return pht('Edit Menu Item: %s', $object->getDisplayName());
+ }
+
+ protected function getObjectEditShortText($object) {
+ return pht('Edit Menu Item');
+ }
+
+ protected function getObjectCreateShortText() {
+ return pht('Edit Menu Item');
+ }
+
+ protected function getObjectCreateCancelURI($object) {
+ return $this->getPanelEngine()->getConfigureURI();
+ }
+
+ protected function getObjectViewURI($object) {
+ return $this->getPanelEngine()->getConfigureURI();
+ }
+
+ protected function buildCustomEditFields($object) {
+ $panel = $object->getPanel();
+ $fields = $panel->buildEditEngineFields($object);
+
+ $type_property =
+ PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY;
+
+ foreach ($fields as $field) {
+ $field
+ ->setTransactionType($type_property)
+ ->setMetadataValue('property.key', $field->getKey());
+ }
+
+ return $fields;
+ }
+
+}
diff --git a/src/applications/search/editor/PhabricatorProfilePanelEditor.php b/src/applications/search/editor/PhabricatorProfilePanelEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/editor/PhabricatorProfilePanelEditor.php
@@ -0,0 +1,70 @@
+<?php
+
+final class PhabricatorProfilePanelEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorSearchApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Profile Panels');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
+ $key = $xaction->getMetadataValue('property.key');
+ return $object->getPanelProperty($key, null);
+ }
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
+ return $xaction->getNewValue();
+ }
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
+ $key = $xaction->getMetadataValue('property.key');
+ $value = $xaction->getNewValue();
+ $object->setPanelProperty($key, $value);
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorProfilePanelConfigurationTransaction::TYPE_PROPERTY:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+}
diff --git a/src/applications/search/engine/PhabricatorProfilePanelEngine.php b/src/applications/search/engine/PhabricatorProfilePanelEngine.php
--- a/src/applications/search/engine/PhabricatorProfilePanelEngine.php
+++ b/src/applications/search/engine/PhabricatorProfilePanelEngine.php
@@ -5,6 +5,7 @@
private $viewer;
private $profileObject;
private $panels;
+ private $controller;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
@@ -25,6 +26,103 @@
return $this->profileObject;
}
+ public function setController(PhabricatorController $controller) {
+ $this->controller = $controller;
+ return $this;
+ }
+
+ public function getController() {
+ return $this->controller;
+ }
+
+ public function buildResponse() {
+ $controller = $this->getController();
+
+ $viewer = $controller->getViewer();
+ $this->setViewer($viewer);
+
+ $request = $controller->getRequest();
+
+ $panel_action = $request->getURIData('panelAction');
+ $panel_id = $request->getURIData('panelID');
+
+ $panel_list = $this->loadPanels();
+
+ $selected_panel = null;
+ if (strlen($panel_id)) {
+ $panel_id_int = (int)$panel_id;
+ foreach ($panel_list as $panel) {
+ if ($panel_id_int) {
+ if ((int)$panel->getID() === $panel_id) {
+ $selected_panel = $panel;
+ break;
+ }
+ }
+
+ $builtin_key = $panel->getBuiltinKey();
+ if ($builtin_key === (string)$panel_id) {
+ $selected_panel = $panel;
+ break;
+ }
+ }
+ }
+
+ switch ($panel_action) {
+ case 'view':
+ case 'info':
+ case 'hide':
+ case 'builtin':
+ if (!$selected_panel) {
+ return new Aphront404Response();
+ }
+ break;
+ }
+
+ $navigation = $this->buildNavigation();
+ $navigation->selectFilter('panel.configure');
+
+ $crumbs = $controller->buildApplicationCrumbsForEditEngine();
+
+ switch ($panel_action) {
+ case 'view':
+ $content = $this->buildPanelViewContent($selected_panel);
+ break;
+ case 'configure':
+ $content = $this->buildPanelConfigureContent($panel_list);
+ $crumbs->addTextCrumb(pht('Configure Menu'));
+ break;
+ case 'new':
+ $panel_key = $request->getURIData('panelKey');
+ $content = $this->buildPanelNewContent($panel_key);
+ break;
+ case 'builtin':
+ $content = $this->buildPanelBuiltinContent($selected_panel);
+ break;
+ case 'edit':
+ $content = $this->buildPanelEditContent();
+ break;
+ default:
+ throw new Exception(
+ pht(
+ 'Unsupported panel action "%s".',
+ $panel_action));
+ }
+
+ if ($content instanceof AphrontResponse) {
+ return $content;
+ }
+
+ if ($content instanceof AphrontResponseProducerInterface) {
+ return $content;
+ }
+
+ return $controller->newPage()
+ ->setTitle(pht('Profile Stuff'))
+ ->setNavigation($navigation)
+ ->setCrumbs($crumbs)
+ ->appendChild($content);
+ }
+
public function buildNavigation() {
$nav = id(new AphrontSideNavFilterView())
->setIconNav(true)
@@ -60,6 +158,11 @@
}
}
+ $configure_item = $this->newConfigureMenuItem();
+ if ($configure_item) {
+ $nav->addMenuItem($configure_item);
+ }
+
$nav->selectFilter(null);
return $nav;
@@ -75,10 +178,25 @@
private function loadPanels() {
$viewer = $this->getViewer();
+ $object = $this->getProfileObject();
$panels = $this->loadBuiltinProfilePanels();
- // TODO: Load persisted panels.
+ $stored_panels = id(new PhabricatorProfilePanelConfigurationQuery())
+ ->setViewer($viewer)
+ ->withProfilePHIDs(array($object->getPHID()))
+ ->execute();
+
+ // Merge the stored panels into the builtin panels. If a builtin panel has
+ // a stored version, replace the defaults with the stored changes.
+ foreach ($stored_panels as $stored_panel) {
+ $builtin_key = $stored_panel->getBuiltinKey();
+ if ($builtin_key !== null) {
+ $panels[$builtin_key] = $stored_panel;
+ } else {
+ $panels[] = $stored_panel;
+ }
+ }
foreach ($panels as $panel) {
$impl = $panel->getPanel();
@@ -86,6 +204,10 @@
$impl->setViewer($viewer);
}
+ // Normalize keys since callers shouldn't rely on this array being
+ // partially keyed.
+ $panels = array_values($panels);
+
return $panels;
}
@@ -128,6 +250,7 @@
}
$builtin
+ ->setProfilePHID($object->getPHID())
->attachPanel($panel)
->attachProfileObject($object)
->setPanelOrder($order);
@@ -149,4 +272,208 @@
}
}
+ private function newConfigureMenuItem() {
+ if (!PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
+ return null;
+ }
+
+ $viewer = $this->getViewer();
+ $object = $this->getProfileObject();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $object,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ return id(new PHUIListItemView())
+ ->setName('Configure Menu')
+ ->setKey('panel.configure')
+ ->setIcon('fa-gear')
+ ->setHref($this->getPanelURI('configure/'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setRenderNameAsTooltip(true);
+ }
+
+ public function getConfigureURI() {
+ return $this->getPanelURI('configure/');
+ }
+
+ private function getPanelURI($path) {
+ $project = $this->getProfileObject();
+ $id = $project->getID();
+ return "/project/{$id}/panel/{$path}";
+ }
+
+ private function buildPanelConfigureContent(array $panels) {
+ $viewer = $this->getViewer();
+ $object = $this->getProfileObject();
+
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $object,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $list = new PHUIObjectItemListView();
+ foreach ($panels as $panel) {
+ $id = $panel->getID();
+ $builtin_key = $panel->getBuiltinKey();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $panel,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $item = id(new PHUIObjectItemView());
+
+ $name = $panel->getDisplayName();
+ $type = $panel->getPanelTypeName();
+ if (!strlen(trim($name))) {
+ $name = pht('Untitled "%s" Item', $type);
+ }
+
+ $item->setHeader($name);
+ $item->addAttribute($type);
+
+ if ($can_edit) {
+ if ($id) {
+ $item->setHref($this->getPanelURI("edit/{$id}/"));
+ } else {
+ $item->setHref($this->getPanelURI("builtin/{$builtin_key}/"));
+ }
+ }
+
+ $list->addItem($item);
+ }
+
+ $action_view = id(new PhabricatorActionListView())
+ ->setUser($viewer);
+
+ $panel_types = PhabricatorProfilePanel::getAllPanels();
+
+ $action_view->addAction(
+ id(new PhabricatorActionView())
+ ->setLabel(true)
+ ->setName(pht('Add New Menu Item...')));
+
+ foreach ($panel_types as $panel_type) {
+ if (!$panel_type->canAddToObject($object)) {
+ continue;
+ }
+
+ $panel_key = $panel_type->getPanelKey();
+
+ $action_view->addAction(
+ id(new PhabricatorActionView())
+ ->setIcon($panel_type->getPanelTypeIcon())
+ ->setName($panel_type->getPanelTypeName())
+ ->setHref($this->getPanelURI("new/{$panel_key}/")));
+ }
+
+ $action_view->addAction(
+ id(new PhabricatorActionView())
+ ->setLabel(true)
+ ->setName(pht('Documentation')));
+
+ $action_view->addAction(
+ id(new PhabricatorActionView())
+ ->setIcon('fa-book')
+ ->setName(pht('TODO: Write Documentation')));
+
+ $action_button = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setText(pht('Configure Menu'))
+ ->setHref('#')
+ ->setIconFont('fa-gear')
+ ->setDropdownMenu($action_view);
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Profile Menu Items'))
+ ->addActionLink($action_button);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->setObjectList($list);
+
+ return $box;
+ }
+
+ private function buildPanelNewContent($panel_key) {
+ $panel_types = PhabricatorProfilePanel::getAllPanels();
+ $panel_type = idx($panel_types, $panel_key);
+ if (!$panel_type) {
+ return new Aphront404Response();
+ }
+
+ $object = $this->getProfileObject();
+ if (!$panel_type->canAddToObject($object)) {
+ return new Aphront404Response();
+ }
+
+ $configuration =
+ PhabricatorProfilePanelConfiguration::initializeNewPanelConfiguration(
+ $object,
+ $panel_type);
+
+ $viewer = $this->getViewer();
+
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $configuration,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $controller = $this->getController();
+
+ return id(new PhabricatorProfilePanelEditEngine())
+ ->setPanelEngine($this)
+ ->setProfileObject($object)
+ ->setNewPanelConfiguration($configuration)
+ ->setController($controller)
+ ->buildResponse();
+ }
+
+ private function buildPanelEditContent() {
+ $viewer = $this->getViewer();
+ $object = $this->getProfileObject();
+ $controller = $this->getController();
+
+ return id(new PhabricatorProfilePanelEditEngine())
+ ->setPanelEngine($this)
+ ->setProfileObject($object)
+ ->setController($controller)
+ ->buildResponse();
+ }
+
+ private function buildPanelBuiltinContent(
+ PhabricatorProfilePanelConfiguration $configuration) {
+
+ // If this builtin panel has already been persisted, redirect to the
+ // edit page.
+ $id = $configuration->getID();
+ if ($id) {
+ return id(new AphrontRedirectResponse())
+ ->setURI($this->getPanelURI("edit/{$id}/"));
+ }
+
+ // Otherwise, act like we're creating a new panel, we're just starting
+ // with the builtin template.
+ $viewer = $this->getViewer();
+
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $configuration,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $object = $this->getProfileObject();
+ $controller = $this->getController();
+
+ return id(new PhabricatorProfilePanelEditEngine())
+ ->setIsBuiltin(true)
+ ->setPanelEngine($this)
+ ->setProfileObject($object)
+ ->setNewPanelConfiguration($configuration)
+ ->setController($controller)
+ ->buildResponse();
+ }
+
}
diff --git a/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php b/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php
--- a/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php
+++ b/src/applications/search/profilepanel/PhabricatorLinkProfilePanel.php
@@ -5,19 +5,89 @@
const PANELKEY = 'link';
+ public function getPanelTypeIcon() {
+ return 'fa-link';
+ }
+
+ public function getPanelTypeName() {
+ return pht('Link');
+ }
+
+ public function canAddToObject(
+ PhabricatorProfilePanelInterface $object) {
+ return true;
+ }
+
+ public function getDisplayName(
+ PhabricatorProfilePanelConfiguration $config) {
+ return $this->getLinkName($config);
+ }
+
+ public function buildEditEngineFields(
+ PhabricatorProfilePanelConfiguration $config) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setIsRequired(true)
+ ->setValue($this->getLinkName($config)),
+ id(new PhabricatorTextEditField())
+ ->setKey('uri')
+ ->setLabel(pht('URI'))
+ ->setIsRequired(true)
+ ->setValue($this->getLinkURI($config)),
+ id(new PhabricatorIconSetEditField())
+ ->setKey('icon')
+ ->setLabel(pht('Icon'))
+ ->setIconSet(new PhabricatorProfilePanelIconSet())
+ ->setValue($this->getLinkIcon($config)),
+ );
+ }
+
+ private function getLinkName(
+ PhabricatorProfilePanelConfiguration $config) {
+ return $config->getPanelProperty('name');
+ }
+
+ private function getLinkIcon(
+ PhabricatorProfilePanelConfiguration $config) {
+ return $config->getPanelProperty('icon', 'link');
+ }
+
+ private function getLinkURI(
+ PhabricatorProfilePanelConfiguration $config) {
+ return $config->getPanelProperty('uri');
+ }
+
+ private function isValidLinkURI($uri) {
+ return PhabricatorEnv::isValidURIForLink($uri);
+ }
+
protected function newNavigationMenuItems(
PhabricatorProfilePanelConfiguration $config) {
- $icon = $config->getPanelProperty('icon');
- $name = $config->getPanelProperty('name');
- $href = $config->getPanelProperty('href');
+ $icon = $this->getLinkIcon($config);
+ $name = $this->getLinkName($config);
+ $href = $this->getLinkURI($config);
+
+ if (!$this->isValidLinkURI($href)) {
+ $href = '#';
+ }
+
+ $icon_object = id(new PhabricatorProfilePanelIconSet())
+ ->getIcon($icon);
+ if ($icon_object) {
+ $icon_class = $icon_object->getIcon();
+ } else {
+ $icon_class = 'fa-link';
+ }
$item = id(new PHUIListItemView())
->setRenderNameAsTooltip(true)
->setType(PHUIListItemView::TYPE_ICON_NAV)
->setHref($href)
->setName($name)
- ->setIcon($icon);
+ ->setIcon($icon_class);
return array(
$item,
diff --git a/src/applications/search/profilepanel/PhabricatorProfilePanel.php b/src/applications/search/profilepanel/PhabricatorProfilePanel.php
--- a/src/applications/search/profilepanel/PhabricatorProfilePanel.php
+++ b/src/applications/search/profilepanel/PhabricatorProfilePanel.php
@@ -12,6 +12,25 @@
abstract protected function newNavigationMenuItems(
PhabricatorProfilePanelConfiguration $config);
+ public function getPanelTypeIcon() {
+ return null;
+ }
+
+ abstract public function getPanelTypeName();
+
+ abstract public function getDisplayName(
+ PhabricatorProfilePanelConfiguration $config);
+
+ public function buildEditEngineFields(
+ PhabricatorProfilePanelConfiguration $config) {
+ return array();
+ }
+
+ public function canAddToObject(
+ PhabricatorProfilePanelInterface $object) {
+ return false;
+ }
+
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
diff --git a/src/applications/search/profilepanel/PhabricatorProfilePanelIconSet.php b/src/applications/search/profilepanel/PhabricatorProfilePanelIconSet.php
new file mode 100644
--- /dev/null
+++ b/src/applications/search/profilepanel/PhabricatorProfilePanelIconSet.php
@@ -0,0 +1,42 @@
+<?php
+
+final class PhabricatorProfilePanelIconSet
+ extends PhabricatorIconSet {
+
+ const ICONSETKEY = 'profilepanel';
+
+ public function getSelectIconTitleText() {
+ return pht('Choose Item Icon');
+ }
+
+ protected function newIcons() {
+ $list = array(
+ array(
+ 'key' => 'link',
+ 'icon' => 'fa-link',
+ 'name' => pht('Link'),
+ ),
+ array(
+ 'key' => 'maniphest',
+ 'icon' => 'fa-anchor',
+ 'name' => pht('Maniphest'),
+ ),
+ array(
+ 'key' => 'feed',
+ 'icon' => 'fa-newspaper-o',
+ 'name' => pht('Feed'),
+ ),
+ );
+
+ $icons = array();
+ foreach ($list as $spec) {
+ $icons[] = id(new PhabricatorIconSetIcon())
+ ->setKey($spec['key'])
+ ->setIcon($spec['icon'])
+ ->setLabel($spec['name']);
+ }
+
+ return $icons;
+ }
+
+}
diff --git a/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php b/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php
--- a/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php
+++ b/src/applications/search/query/PhabricatorProfilePanelConfigurationQuery.php
@@ -5,7 +5,7 @@
private $ids;
private $phids;
- private $profileObjectPHIDs;
+ private $profilePHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -17,8 +17,8 @@
return $this;
}
- public function withProfileObjectPHIDs(array $phids) {
- $this->profileObjectPHIDs = $phids;
+ public function withProfilePHIDs(array $phids) {
+ $this->profilePHIDs = $phids;
return $this;
}
@@ -47,16 +47,54 @@
$this->phids);
}
- if ($this->profileObjectPHIDs !== null) {
+ if ($this->profilePHIDs !== null) {
$where[] = qsprintf(
$conn,
- 'profileObjectPHID IN (%Ls)',
- $this->profileObjectPHIDs);
+ 'profilePHID IN (%Ls)',
+ $this->profilePHIDs);
}
return $where;
}
+ protected function willFilterPage(array $page) {
+ $panels = PhabricatorProfilePanel::getAllPanels();
+ foreach ($page as $key => $panel) {
+ $panel_type = idx($panels, $panel->getPanelKey());
+ if (!$panel_type) {
+ $this->didRejectResult($panel);
+ unset($page[$key]);
+ continue;
+ }
+ $panel->attachPanel($panel_type);
+ }
+
+ if (!$page) {
+ return array();
+ }
+
+ $profile_phids = mpull($page, 'getProfilePHID');
+
+ $profiles = id(new PhabricatorObjectQuery())
+ ->setViewer($this->getViewer())
+ ->setParentQuery($this)
+ ->withPHIDs($profile_phids)
+ ->execute();
+ $profiles = mpull($profiles, null, 'getPHID');
+
+ foreach ($page as $key => $panel) {
+ $profile = idx($profiles, $panel->getProfilePHID());
+ if (!$profile) {
+ $this->didRejectResult($panel);
+ unset($page[$key]);
+ continue;
+ }
+ $panel->attachProfileObject($profile);
+ }
+
+ return $page;
+ }
+
public function getQueryApplicationClass() {
return 'PhabricatorSearchApplication';
}
diff --git a/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php b/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php
--- a/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php
+++ b/src/applications/search/storage/PhabricatorProfilePanelConfiguration.php
@@ -4,26 +4,34 @@
extends PhabricatorSearchDAO
implements
PhabricatorPolicyInterface,
- PhabricatorExtendedPolicyInterface {
+ PhabricatorExtendedPolicyInterface,
+ PhabricatorApplicationTransactionInterface {
protected $profilePHID;
protected $panelKey;
protected $builtinKey;
protected $panelOrder;
- protected $isDisabled;
+ protected $visibility;
protected $panelProperties = array();
private $profileObject = self::ATTACHABLE;
private $panel = self::ATTACHABLE;
+ const VISIBILITY_VISIBLE = 'visible';
+ const VISIBILITY_DISABLED = 'disabled';
+
+ public static function initializeNewBuiltin() {
+ return id(new self())
+ ->setVisibility(self::VISIBILITY_VISIBLE);
+ }
+
public static function initializeNewPanelConfiguration(
PhabricatorProfilePanelInterface $profile_object,
PhabricatorProfilePanel $panel) {
- return id(new self())
+ return self::initializeNewBuiltin()
->setProfilePHID($profile_object->getPHID())
->setPanelKey($panel->getPanelKey())
- ->setIsDisabled(0)
->attachPanel($panel)
->attachProfileObject($profile_object);
}
@@ -36,9 +44,9 @@
),
self::CONFIG_COLUMN_SCHEMA => array(
'panelKey' => 'text64',
- 'builtinKey' => 'text64',
- 'panelOrder' => 'uint32',
- 'isDisabled' => 'bool',
+ 'builtinKey' => 'text64?',
+ 'panelOrder' => 'uint32?',
+ 'visibility' => 'text32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_profile' => array(
@@ -48,6 +56,11 @@
) + parent::getConfiguration();
}
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ PhabricatorProfilePanelPHIDType::TYPECONST);
+ }
+
public function attachPanel(PhabricatorProfilePanel $panel) {
$this->panel = $panel;
return $this;
@@ -67,10 +80,6 @@
return $this->assertAttached($this->profileObject);
}
- public function buildNavigationMenuItems() {
- return $this->getPanel()->buildNavigationMenuItems($this);
- }
-
public function setPanelProperty($key, $value) {
$this->panelProperties[$key] = $value;
return $this;
@@ -80,6 +89,18 @@
return idx($this->panelProperties, $key, $default);
}
+ public function buildNavigationMenuItems() {
+ return $this->getPanel()->buildNavigationMenuItems($this);
+ }
+
+ public function getPanelTypeName() {
+ return $this->getPanel()->getPanelTypeName();
+ }
+
+ public function getDisplayName() {
+ return $this->getPanel()->getDisplayName($this);
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
@@ -121,4 +142,27 @@
);
}
+
+/* -( PhabricatorApplicationTransactionInterface )------------------------- */
+
+
+ public function getApplicationTransactionEditor() {
+ return new PhabricatorProfilePanelEditor();
+ }
+
+ public function getApplicationTransactionObject() {
+ return $this;
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PhabricatorProfilePanelConfigurationTransaction();
+ }
+
+ public function willRenderTimeline(
+ PhabricatorApplicationTransactionView $timeline,
+ AphrontRequest $request) {
+
+ return $timeline;
+ }
+
}
diff --git a/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php b/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php
--- a/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php
+++ b/src/applications/search/storage/PhabricatorProfilePanelConfigurationTransaction.php
@@ -3,6 +3,8 @@
final class PhabricatorProfilePanelConfigurationTransaction
extends PhabricatorApplicationTransaction {
+ const TYPE_PROPERTY = 'profilepanel.property';
+
public function getApplicationName() {
return 'search';
}
diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php
--- a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php
+++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php
@@ -16,6 +16,10 @@
$engine = PhabricatorEditEngine::getByKey($viewer, $engine_key)
->setViewer($viewer);
+ if (!$engine->isEngineConfigurable()) {
+ return new Aphront404Response();
+ }
+
$items = array();
$items[] = id(new PHUIListItemView())
->setType(PHUIListItemView::TYPE_LABEL)
diff --git a/src/applications/transactions/controller/PhabricatorEditEngineController.php b/src/applications/transactions/controller/PhabricatorEditEngineController.php
--- a/src/applications/transactions/controller/PhabricatorEditEngineController.php
+++ b/src/applications/transactions/controller/PhabricatorEditEngineController.php
@@ -72,6 +72,10 @@
$engine = $config->getEngine();
}
+ if (!$engine->isEngineConfigurable()) {
+ return null;
+ }
+
return $config;
}
diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php
--- a/src/applications/transactions/editengine/PhabricatorEditEngine.php
+++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php
@@ -57,6 +57,10 @@
return $this;
}
+ public function isEngineConfigurable() {
+ return true;
+ }
+
/* -( Managing Fields )---------------------------------------------------- */
@@ -1005,8 +1009,11 @@
}
$header = id(new PHUIHeaderView())
- ->setHeader($header_text)
- ->addActionLink($action_button);
+ ->setHeader($header_text);
+
+ if ($action_button) {
+ $header->addActionLink($action_button);
+ }
$crumbs = $this->buildCrumbs($object, $final = true);
@@ -1066,6 +1073,10 @@
}
private function buildEditFormActionButton($object) {
+ if (!$this->isEngineConfigurable()) {
+ return null;
+ }
+
$viewer = $this->getViewer();
$action_view = id(new PhabricatorActionListView())
diff --git a/src/applications/transactions/editfield/PhabricatorTextEditField.php b/src/applications/transactions/editfield/PhabricatorTextEditField.php
--- a/src/applications/transactions/editfield/PhabricatorTextEditField.php
+++ b/src/applications/transactions/editfield/PhabricatorTextEditField.php
@@ -3,8 +3,26 @@
final class PhabricatorTextEditField
extends PhabricatorEditField {
+ private $placeholder;
+
+ public function setPlaceholder($placeholder) {
+ $this->placeholder = $placeholder;
+ return $this;
+ }
+
+ public function getPlaceholder() {
+ return $this->placeholder;
+ }
+
protected function newControl() {
- return new AphrontFormTextControl();
+ $control = new AphrontFormTextControl();
+
+ $placeholder = $this->getPlaceholder();
+ if (strlen($placeholder)) {
+ $control->setPlaceholder($placeholder);
+ }
+
+ return $control;
}
protected function newConduitParameterType() {
diff --git a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php
--- a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php
+++ b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php
@@ -62,6 +62,10 @@
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($engines as $engine) {
+ if (!$engine->isEngineConfigurable()) {
+ continue;
+ }
+
$engine_key = $engine->getEngineKey();
$query_uri = "/transactions/editengine/{$engine_key}/";
diff --git a/src/docs/user/field/conduit_changes.diviner b/src/docs/user/field/conduit_changes.diviner
--- a/src/docs/user/field/conduit_changes.diviner
+++ b/src/docs/user/field/conduit_changes.diviner
@@ -39,7 +39,7 @@
made, and {nav Deprecated Call Logs} to find deprecated calls by all users.
You can also search for calls by specific users. For example, it may be useful
-to serach for any bot accounts you run to make sure they aren't calling
+to search for any bot accounts you run to make sure they aren't calling
outdated APIs.
The most common cause of calls to deprecated methods is users running very

File Metadata

Mime Type
text/plain
Expires
Fri, Oct 25, 10:05 AM (3 w, 3 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/ko/sw/q5ulzxcx3tpgxy6k
Default Alt Text
D15010.diff (46 KB)

Event Timeline