1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42:41 +01:00

Some owners read workflows.

This commit is contained in:
epriestley 2011-04-03 19:20:47 -07:00
parent 65e1386753
commit 5038ab850c
25 changed files with 1026 additions and 28 deletions

View file

@ -117,7 +117,7 @@ celerity_register_resource_map(array(
), ),
'aphront-tokenizer-control-css' => 'aphront-tokenizer-control-css' =>
array( array(
'uri' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css', 'uri' => '/res/190349be/rsrc/css/aphront/tokenizer.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(
@ -297,6 +297,15 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/css/application/objectselector/object-selector.css', 'disk' => '/rsrc/css/application/objectselector/object-selector.css',
), ),
'owners-path-editor-css' =>
array(
'uri' => '/res/f40dc6b1/rsrc/css/application/owners/owners-path-editor.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/owners/owners-path-editor.css',
),
'phabricator-profile-css' => 'phabricator-profile-css' =>
array( array(
'uri' => '/res/259ad37f/rsrc/css/application/people/profile.css', 'uri' => '/res/259ad37f/rsrc/css/application/people/profile.css',
@ -503,16 +512,28 @@ celerity_register_resource_map(array(
), ),
'herald-rule-editor' => 'herald-rule-editor' =>
array( array(
'uri' => '/res/8b5e9d5e/rsrc/js/application/herald/HeraldRuleEditor.js', 'uri' => '/res/ec8e2110/rsrc/js/application/herald/HeraldRuleEditor.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
0 => 'multirow-row-manager', 0 => 'multirow-row-manager',
1 => 'javelin-lib-dev', 1 => 'javelin-lib-dev',
2 => 'javelin-typeahead-dev', 2 => 'javelin-typeahead-dev',
3 => 'path-typeahead',
), ),
'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js', 'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js',
), ),
'path-typeahead' =>
array(
'uri' => '/res/42fb76c3/rsrc/js/application/herald/PathTypeahead.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
1 => 'javelin-typeahead-dev',
),
'disk' => '/rsrc/js/application/herald/PathTypeahead.js',
),
'javelin-behavior-maniphest-transaction-controls' => 'javelin-behavior-maniphest-transaction-controls' =>
array( array(
'uri' => '/res/fc6a8722/rsrc/js/application/maniphest/behavior-transaction-controls.js', 'uri' => '/res/fc6a8722/rsrc/js/application/maniphest/behavior-transaction-controls.js',
@ -523,6 +544,30 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js', 'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js',
), ),
'javelin-behavior-owners-path-editor' =>
array(
'uri' => '/res/7568aa22/rsrc/js/application/owners/owners-path-editor.js',
'type' => 'js',
'requires' =>
array(
0 => 'owners-path-editor',
1 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/owners/owners-path-editor.js',
),
'owners-path-editor' =>
array(
'uri' => '/res/b01c1ca9/rsrc/js/application/owners/OwnersPathEditor.js',
'type' => 'js',
'requires' =>
array(
0 => 'multirow-row-manager',
1 => 'javelin-lib-dev',
2 => 'javelin-typeahead-dev',
3 => 'path-typeahead',
),
'disk' => '/rsrc/js/application/owners/OwnersPathEditor.js',
),
'javelin-magical-init' => 'javelin-magical-init' =>
array( array(
'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js', 'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js',
@ -598,7 +643,7 @@ celerity_register_resource_map(array(
), array ( ), array (
'packages' => 'packages' =>
array ( array (
'ce1b9ed3' => 'e3ec35d7' =>
array ( array (
'name' => 'core.pkg.css', 'name' => 'core.pkg.css',
'symbols' => 'symbols' =>
@ -618,7 +663,7 @@ celerity_register_resource_map(array(
12 => 'phabricator-remarkup-css', 12 => 'phabricator-remarkup-css',
13 => 'syntax-highlighting-css', 13 => 'syntax-highlighting-css',
), ),
'uri' => '/res/pkg/ce1b9ed3/core.pkg.css', 'uri' => '/res/pkg/e3ec35d7/core.pkg.css',
'type' => 'css', 'type' => 'css',
), ),
'76f3c1f8' => '76f3c1f8' =>
@ -665,20 +710,20 @@ celerity_register_resource_map(array(
), ),
'reverse' => 'reverse' =>
array ( array (
'phabricator-core-css' => 'ce1b9ed3', 'phabricator-core-css' => 'e3ec35d7',
'phabricator-core-buttons-css' => 'ce1b9ed3', 'phabricator-core-buttons-css' => 'e3ec35d7',
'phabricator-standard-page-view' => 'ce1b9ed3', 'phabricator-standard-page-view' => 'e3ec35d7',
'aphront-dialog-view-css' => 'ce1b9ed3', 'aphront-dialog-view-css' => 'e3ec35d7',
'aphront-form-view-css' => 'ce1b9ed3', 'aphront-form-view-css' => 'e3ec35d7',
'aphront-panel-view-css' => 'ce1b9ed3', 'aphront-panel-view-css' => 'e3ec35d7',
'aphront-side-nav-view-css' => 'ce1b9ed3', 'aphront-side-nav-view-css' => 'e3ec35d7',
'aphront-table-view-css' => 'ce1b9ed3', 'aphront-table-view-css' => 'e3ec35d7',
'aphront-crumbs-view-css' => 'ce1b9ed3', 'aphront-crumbs-view-css' => 'e3ec35d7',
'aphront-tokenizer-control-css' => 'ce1b9ed3', 'aphront-tokenizer-control-css' => 'e3ec35d7',
'aphront-typeahead-control-css' => 'ce1b9ed3', 'aphront-typeahead-control-css' => 'e3ec35d7',
'phabricator-directory-css' => 'ce1b9ed3', 'phabricator-directory-css' => 'e3ec35d7',
'phabricator-remarkup-css' => 'ce1b9ed3', 'phabricator-remarkup-css' => 'e3ec35d7',
'syntax-highlighting-css' => 'ce1b9ed3', 'syntax-highlighting-css' => 'e3ec35d7',
'differential-core-view-css' => '76f3c1f8', 'differential-core-view-css' => '76f3c1f8',
'differential-changeset-view-css' => '76f3c1f8', 'differential-changeset-view-css' => '76f3c1f8',
'differential-revision-detail-css' => '76f3c1f8', 'differential-revision-detail-css' => '76f3c1f8',

View file

@ -61,6 +61,7 @@ phutil_register_library_map(array(
'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontSideNavView' => 'view/layout/sidenav',
'AphrontTableView' => 'view/control/table', 'AphrontTableView' => 'view/control/table',
'AphrontTokenizerTemplateView' => 'view/control/tokenizer', 'AphrontTokenizerTemplateView' => 'view/control/tokenizer',
'AphrontTypeaheadTemplateView' => 'view/control/typeahead',
'AphrontURIMapper' => 'aphront/mapper', 'AphrontURIMapper' => 'aphront/mapper',
'AphrontView' => 'view/base', 'AphrontView' => 'view/base',
'AphrontWebpageResponse' => 'aphront/response/webpage', 'AphrontWebpageResponse' => 'aphront/response/webpage',
@ -180,6 +181,8 @@ phutil_register_library_map(array(
'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base', 'DiffusionLastModifiedQuery' => 'applications/diffusion/query/lastmodified/base',
'DiffusionPathChange' => 'applications/diffusion/data/pathchange', 'DiffusionPathChange' => 'applications/diffusion/data/pathchange',
'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base', 'DiffusionPathChangeQuery' => 'applications/diffusion/query/pathchange/base',
'DiffusionPathCompleteController' => 'applications/diffusion/controller/pathcomplete',
'DiffusionPathValidateController' => 'applications/diffusion/controller/pathvalidate',
'DiffusionRepositoryController' => 'applications/diffusion/controller/repository', 'DiffusionRepositoryController' => 'applications/diffusion/controller/repository',
'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath', 'DiffusionRepositoryPath' => 'applications/diffusion/data/repositorypath',
'DiffusionRequest' => 'applications/diffusion/request/base', 'DiffusionRequest' => 'applications/diffusion/request/base',
@ -487,6 +490,7 @@ phutil_register_library_map(array(
'AphrontSideNavView' => 'AphrontView', 'AphrontSideNavView' => 'AphrontView',
'AphrontTableView' => 'AphrontView', 'AphrontTableView' => 'AphrontView',
'AphrontTokenizerTemplateView' => 'AphrontView', 'AphrontTokenizerTemplateView' => 'AphrontView',
'AphrontTypeaheadTemplateView' => 'AphrontView',
'AphrontWebpageResponse' => 'AphrontResponse', 'AphrontWebpageResponse' => 'AphrontResponse',
'CelerityResourceController' => 'AphrontController', 'CelerityResourceController' => 'AphrontController',
'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod', 'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod',
@ -566,6 +570,8 @@ phutil_register_library_map(array(
'DiffusionHistoryTableView' => 'DiffusionView', 'DiffusionHistoryTableView' => 'DiffusionView',
'DiffusionHomeController' => 'DiffusionController', 'DiffusionHomeController' => 'DiffusionController',
'DiffusionLastModifiedController' => 'DiffusionController', 'DiffusionLastModifiedController' => 'DiffusionController',
'DiffusionPathCompleteController' => 'DiffusionController',
'DiffusionPathValidateController' => 'DiffusionController',
'DiffusionRepositoryController' => 'DiffusionController', 'DiffusionRepositoryController' => 'DiffusionController',
'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery', 'DiffusionSvnBrowseQuery' => 'DiffusionBrowseQuery',
'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery', 'DiffusionSvnDiffQuery' => 'DiffusionDiffQuery',

View file

@ -218,6 +218,12 @@ class AphrontDefaultApplicationConfiguration
'$' '$'
=> 'DiffusionLastModifiedController', => 'DiffusionLastModifiedController',
), ),
'services/' => array(
'path/' => array(
'complete/$' => 'DiffusionPathCompleteController',
'validate/$' => 'DiffusionPathValidateController',
),
),
), ),
'/daemon/' => array( '/daemon/' => array(
@ -255,6 +261,7 @@ class AphrontDefaultApplicationConfiguration
'$' => 'PhabricatorOwnersListController', '$' => 'PhabricatorOwnersListController',
'view/(?P<view>[^/]+)/$' => 'PhabricatorOwnersListController', 'view/(?P<view>[^/]+)/$' => 'PhabricatorOwnersListController',
'package/(?P<id>\d+)/$' => 'PhabricatorOwnersDetailController', 'package/(?P<id>\d+)/$' => 'PhabricatorOwnersDetailController',
'new/$' => 'PhabricatorOwnersDetailController',
), ),
); );

View file

@ -0,0 +1,68 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class DiffusionPathCompleteController extends DiffusionController {
public function willProcessRequest(array $data) {
// Don't build a DiffusionRequest.
}
public function processRequest() {
$request = $this->getRequest();
$repository_phid = $request->getStr('repositoryPHID');
$repository = id(new PhabricatorRepository())->loadOneWhere(
'phid = %s',
$repository_phid);
if (!$repository) {
return new Aphront400Response();
}
$query_path = $request->getStr('q');
$query_path = ltrim($query_path, '/');
if (preg_match('@/$@', $query_path)) {
$query_dir = $query_path;
} else {
$query_dir = dirname($query_path);
if ($query_dir == '.') {
$query_dir = '';
}
}
$drequest = DiffusionRequest::newFromAphrontRequestDictionary(
array(
'callsign' => $repository->getCallsign(),
'path' => $query_dir,
'nobranch' => true,
));
$browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
$paths = $browse_query->loadPaths();
$output = array();
foreach ($paths as $path) {
$full_path = $query_dir.$path->getPath();
if ($path->getFileType() == DifferentialChangeType::FILE_DIRECTORY) {
$full_path .= '/';
}
$output[] = array('/'.$full_path, null, substr(md5($full_path), 0, 7));
}
return id(new AphrontAjaxResponse())->setContent($output);
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/400');
phutil_require_module('phabricator', 'aphront/response/ajax');
phutil_require_module('phabricator', 'applications/differential/constants/changetype');
phutil_require_module('phabricator', 'applications/diffusion/controller/base');
phutil_require_module('phabricator', 'applications/diffusion/query/browse/base');
phutil_require_module('phabricator', 'applications/diffusion/request/base');
phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phutil', 'utils');
phutil_require_source('DiffusionPathCompleteController.php');

View file

@ -0,0 +1,80 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class DiffusionPathValidateController extends DiffusionController {
public function willProcessRequest(array $data) {
// Don't build a DiffusionRequest.
}
public function processRequest() {
$request = $this->getRequest();
$repository_phid = $request->getStr('repositoryPHID');
$repository = id(new PhabricatorRepository())->loadOneWhere(
'phid = %s',
$repository_phid);
if (!$repository) {
return new Aphront400Response();
}
$path = $request->getStr('path');
$path = ltrim($path, '/');
$drequest = DiffusionRequest::newFromAphrontRequestDictionary(
array(
'callsign' => $repository->getCallsign(),
'path' => $path,
'nobranch' => true,
));
$browse_query = DiffusionBrowseQuery::newFromDiffusionRequest($drequest);
$browse_query->needValidityOnly(true);
$valid = $browse_query->loadPaths();
if (!$valid) {
switch ($browse_query->getReasonForEmptyResultSet()) {
case DiffusionBrowseQuery::REASON_IS_FILE:
$valid = true;
break;
case DiffusionBrowseQuery::REASON_IS_EMPTY:
$valid = true;
break;
}
}
$output = array(
'valid' => (bool)$valid,
);
if (!$valid) {
$branch = $drequest->getBranch();
if ($branch) {
$message = 'Not found in '.$branch;
} else {
$message = 'Not found at HEAD';
}
} else {
$message = 'OK';
}
$output['message'] = $message;
return id(new AphrontAjaxResponse())->setContent($output);
}
}

View file

@ -0,0 +1,19 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/400');
phutil_require_module('phabricator', 'aphront/response/ajax');
phutil_require_module('phabricator', 'applications/diffusion/controller/base');
phutil_require_module('phabricator', 'applications/diffusion/query/browse/base');
phutil_require_module('phabricator', 'applications/diffusion/request/base');
phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phutil', 'utils');
phutil_require_source('DiffusionPathValidateController.php');

View file

@ -23,6 +23,7 @@ abstract class DiffusionBrowseQuery {
protected $reason; protected $reason;
protected $existedAtCommit; protected $existedAtCommit;
protected $deletedAtCommit; protected $deletedAtCommit;
protected $validityOnly;
const REASON_IS_FILE = 'is-file'; const REASON_IS_FILE = 'is-file';
const REASON_IS_DELETED = 'is-deleted'; const REASON_IS_DELETED = 'is-deleted';
@ -79,5 +80,14 @@ abstract class DiffusionBrowseQuery {
return $this->executeQuery(); return $this->executeQuery();
} }
final public function shouldOnlyTestValidity() {
return $this->validityOnly;
}
final public function needValidityOnly($need_validity_only) {
$this->validityOnly = $need_validity_only;
return $this;
}
abstract protected function executeQuery(); abstract protected function executeQuery();
} }

View file

@ -63,6 +63,10 @@ final class DiffusionGitBrowseQuery extends DiffusionBrowseQuery {
return array(); return array();
} }
if ($this->shouldOnlyTestValidity()) {
return true;
}
list($stdout) = execx( list($stdout) = execx(
"(cd %s && git ls-tree -l %s:%s)", "(cd %s && git ls-tree -l %s:%s)",
$local_path, $local_path,

View file

@ -103,6 +103,10 @@ final class DiffusionSvnBrowseQuery extends DiffusionBrowseQuery {
return array(); return array();
} }
if ($this->shouldOnlyTestValidity()) {
return true;
}
$sql = array(); $sql = array();
foreach ($index as $row) { foreach ($index as $row) {
$sql[] = '('.(int)$row['pathID'].', '.(int)$row['maxCommit'].')'; $sql[] = '('.(int)$row['pathID'].', '.(int)$row['maxCommit'].')';

View file

@ -67,12 +67,12 @@ class DiffusionRequest {
$object->commit = idx($data, 'commit'); $object->commit = idx($data, 'commit');
$object->path = idx($data, 'path'); $object->path = idx($data, 'path');
$object->initializeFromAphrontRequestDictionary(); $object->initializeFromAphrontRequestDictionary($data);
return $object; return $object;
} }
protected function initializeFromAphrontRequestDictionary() { protected function initializeFromAphrontRequestDictionary(array $data) {
} }

View file

@ -18,14 +18,16 @@
class DiffusionGitRequest extends DiffusionRequest { class DiffusionGitRequest extends DiffusionRequest {
protected function initializeFromAphrontRequestDictionary() { protected function initializeFromAphrontRequestDictionary(array $data) {
parent::initializeFromAphrontRequestDictionary(); parent::initializeFromAphrontRequestDictionary($data);
$path = $this->path; $path = $this->path;
$parts = explode('/', $path); $parts = explode('/', $path);
$branch = array_shift($parts); if (empty($data['nobranch'])) {
$this->branch = $this->decodeBranchName($branch); $branch = array_shift($parts);
$this->branch = $this->decodeBranchName($branch);
}
foreach ($parts as $key => $part) { foreach ($parts as $key => $part) {
// Prevent any hyjinx since we're ultimately shipping this to the // Prevent any hyjinx since we're ultimately shipping this to the

View file

@ -18,11 +18,135 @@
class PhabricatorOwnersDetailController extends PhabricatorOwnersController { class PhabricatorOwnersDetailController extends PhabricatorOwnersController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() { public function processRequest() {
return $this->buildStandardPageResponse( $request = $this->getRequest();
'quack', $user = $request->getUser();
if ($this->id) {
$package = id(new PhabricatorOwnersPackage())->load($this->id);
if (!$package) {
return new Aphront404Response();
}
} else {
$package = new PhabricatorOwnersPackage();
$package->setPrimaryOwnerPHID($user->getPHID());
}
$e_name = true;
$e_primary = true;
$token_primary_owner = array();
$token_all_owners = array();
$title = $package->getID() ? 'Edit Package' : 'New Package';
$repos = id(new PhabricatorRepository())->loadAll();
$default_paths = array();
foreach ($repos as $repo) {
$default_path = $repo->getDetail('default-owners-path');
if ($default_path) {
$default_paths[$repo->getPHID()] = $default_path;
}
}
$repos = mpull($repos, 'getCallsign', 'getPHID');
$template = new AphrontTypeaheadTemplateView();
$template = $template->render();
Javelin::initBehavior(
'owners-path-editor',
array( array(
'title' => 'detail', 'root' => 'path-editor',
'table' => 'paths',
'add_button' => 'addpath',
'repositories' => $repos,
'input_template' => $template,
'path_refs' => array(),
'completeURI' => '/diffusion/services/path/complete/',
'validateURI' => '/diffusion/services/path/validate/',
'repositoryDefaultPaths' => $default_paths,
));
require_celerity_resource('owners-path-editor-css');
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($package->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setLabel('Primary Owner')
->setName('primary')
->setLimit(1)
->setValue($token_primary_owner)
->setError($e_primary))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setLabel('Owners')
->setName('owners')
->setValue($token_all_owners)
->setError($e_primary))
->appendChild(
'<h1>Paths</h1>'.
'<div class="aphront-form-inset" id="path-editor">'.
'<div style="float: right;">'.
javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'addpath',
'mustcapture' => true,
),
'Add New Path').
'</div>'.
'<p>Specify the files and directories which comprise this '.
'package.</p>'.
'<div style="clear: both;"></div>'.
javelin_render_tag(
'table',
array(
'class' => 'owners-path-editor-table',
'sigil' => 'paths',
),
'').
'</div>')
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Description')
->setName('description')
->setValue($package->getDescription()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save Package'));
$panel = new AphrontPanelView();
$panel->setHeader($title);
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => $title,
)); ));
} }

View file

@ -6,7 +6,18 @@
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'applications/owners/controller/base'); phutil_require_module('phabricator', 'applications/owners/controller/base');
phutil_require_module('phabricator', 'applications/owners/storage/package');
phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/control/typeahead');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOwnersDetailController.php'); phutil_require_source('PhabricatorOwnersDetailController.php');

View file

@ -26,6 +26,9 @@ class PhabricatorOwnersListController extends PhabricatorOwnersController {
public function processRequest() { public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$views = array( $views = array(
'owned' => 'Owned Packages', 'owned' => 'Owned Packages',
'all' => 'All Packages', 'all' => 'All Packages',
@ -57,7 +60,7 @@ class PhabricatorOwnersListController extends PhabricatorOwnersController {
switch ($this->view) { switch ($this->view) {
case 'search': case 'search':
$content = 'search goes here'; $content = $this->renderPackageTable(array(), 'Search Results');
break; break;
case 'owned': case 'owned':
$content = $this->renderOwnedView(); $content = $this->renderOwnedView();
@ -67,6 +70,54 @@ class PhabricatorOwnersListController extends PhabricatorOwnersController {
break; break;
} }
$filter = new AphrontListFilterView();
$filter->addButton(
phutil_render_tag(
'a',
array(
'href' => '/owners/new/',
'class' => 'green button',
),
'Create New Package'));
$owners_search_value = array();
if ($request->getArr('owner')) {
$phids = $request->getArr('owner');
$phid = reset($phids);
$handles = id(new PhabricatorObjectHandleData(array($phid)))
->loadHandles();
$owners_search_value = array(
$phid => $handles[$phid]->getFullName(),
);
}
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/owners/view/search/')
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel('Name')
->setValue($request->getStr('name')))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setLimit(1)
->setName('owner')
->setLabel('Owner')
->setValue($owners_search_value))
->appendChild(
id(new AphrontFormTextControl())
->setName('path')
->setLabel('Path')
->setValue($request->getStr('path')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Search for Packages'));
$filter->appendChild($form);
$nav->appendChild($filter);
$nav->appendChild($content); $nav->appendChild($content);
return $this->buildStandardPageResponse( return $this->buildStandardPageResponse(

View file

@ -7,6 +7,16 @@
phutil_require_module('phabricator', 'applications/owners/controller/base'); phutil_require_module('phabricator', 'applications/owners/controller/base');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/listfilter');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenav');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorOwnersListController.php'); phutil_require_source('PhabricatorOwnersListController.php');

View file

@ -234,6 +234,12 @@ class PhabricatorRepositoryEditController
$request->getStr('default-branch')); $request->getStr('default-branch'));
} }
$repository->setDetail(
'default-owners-path',
$request->getStr(
'default-owners-path',
'/'));
$repository->setDetail( $repository->setDetail(
'detail-parser', 'detail-parser',
$request->getStr( $request->getStr(
@ -358,6 +364,17 @@ class PhabricatorRepositoryEditController
'Default <strong>remote</strong> branch to show in Diffusion.')); 'Default <strong>remote</strong> branch to show in Diffusion.'));
} }
$form
->appendChild(
id(new AphrontFormTextControl())
->setName('default-owners-path')
->setLabel('Default Owners Path')
->setValue(
$repository->getDetail(
'default-owners-path',
'/'))
->setCaption('Default path in Owners tool.'));
$parsers = id(new PhutilSymbolLoader()) $parsers = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorRepositoryCommitMessageDetailParser') ->setAncestorClass('PhabricatorRepositoryCommitMessageDetailParser')
->selectSymbolsWithoutLoading(); ->selectSymbolsWithoutLoading();

View file

@ -0,0 +1,81 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class AphrontTypeaheadTemplateView extends AphrontView {
private $value;
private $name;
private $id;
public function setID($id) {
$this->id = $id;
return $this;
}
public function setValue(array $value) {
$this->value = $value;
return $this;
}
public function getValue() {
return $this->value;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function render() {
require_celerity_resource('aphront-typeahead-control-css');
$id = $this->id;
$name = $this->getName();
$values = nonempty($this->getValue(), array());
$tokens = array();
foreach ($values as $key => $value) {
$tokens[] = $this->renderToken($key, $value);
}
$input = javelin_render_tag(
'input',
array(
'name' => $name,
'class' => 'jx-typeahead-input',
'sigil' => 'typeahead',
'type' => 'text',
'value' => $this->value,
'autocomplete' => 'off',
));
return javelin_render_tag(
'div',
array(
'id' => $id,
'sigil' => 'typeahead-hardpoint',
'class' => 'jx-typeahead-hardpoint',
),
$input.
'<div style="clear: both;"></div>');
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('AphrontTypeaheadTemplateView.php');

View file

@ -14,6 +14,8 @@ div.jx-tokenizer-container {
background: #fff; background: #fff;
border: 1px solid #96A6C5; border: 1px solid #96A6C5;
position: relative; position: relative;
width: 100%;
padding: 0 2px;
} }
var.jx-tokenizer-metrics { var.jx-tokenizer-metrics {

View file

@ -0,0 +1,37 @@
/**
* @provides owners-path-editor-css
*/
.owners-path-editor-table {
margin: 10px;
}
.owners-path-editor-table td {
padding: 2px 4px;
}
.owners-path-editor-table select {
width: 150px;
}
.owners-path-editor-table input {
width: 550px;
}
.owners-path-editor-table div.error-display {
width: 200px;
padding: 4px 12px 0;
}
.owners-path-editor-table div.validating {
color: #666666;
}
.owners-path-editor-table div.invalid {
color: #aa0000;
}
.owners-path-editor-table div.valid {
color: #00aa00;
font-weight: bold;
}

View file

@ -2,6 +2,7 @@
* @requires multirow-row-manager * @requires multirow-row-manager
* javelin-lib-dev * javelin-lib-dev
* javelin-typeahead-dev * javelin-typeahead-dev
* path-typeahead
* @provides herald-rule-editor * @provides herald-rule-editor
* @javelin * @javelin
*/ */

View file

@ -0,0 +1,202 @@
/**
* @requires javelin-lib-dev
* javelin-typeahead-dev
* @provides path-typeahead
* @javelin
*/
JX.install('PathTypeahead', {
construct : function(config) {
this._repositorySelect = config.repo_select;
this._hardpoint = config.hardpoint;
this._input = config.path_input;
this._completeURI = config.completeURI;
this._validateURI = config.validateURI;
this._errorDisplay = config.error_display;
/*
* Default values to preload the typeahead with, for extremely common
* cases.
*/
this._textInputValues = config.repositoryDefaultPaths;
this._initializeDatasource();
this._initializeTypeahead(this._input);
},
members : {
/*
* DOM <select> elem for choosing the repository of a path.
*/
_repositorySelect : null,
/*
* DOM parent div "hardpoint" to be passed to the JX.Typeahead.
*/
_hardpoint : null,
/*
* DOM element to display errors.
*/
_errorDisplay : null,
/*
* URI to query for typeahead results, to be passed to the
* TypeaheadOnDemandSource.
*/
_completeURI : null,
/*
* Underlying JX.TypeaheadOnDemandSource instance
*/
_datasource : null,
/*
* Underlying JX.Typeahead instance
*/
_typeahead : null,
/*
* Underlying input
*/
_input : null,
/*
* Whenever the user changes the typeahead value, we track the change
* here, keyed by the selected repository ID. That way, we can restore
* typed values if they change the repository choice and then change back.
*/
_textInputValues : null,
/*
* Configurable endpoint for server-side path validation
*/
_validateURI : null,
/*
* Keep the validation AJAX request so we don't send several.
*/
_validationInflight : null,
/*
* Installs path-specific behaviors and then starts the underlying
* typeahead.
*/
start : function() {
if (this._typeahead.getValue()) {
this._textInputValues[this._repositorySelect.value] =
this._typeahead.getValue();
}
this._typeahead.listen(
'change',
JX.bind(this, function(value) {
this._textInputValues[this._repositorySelect.value] = value;
this._validate();
}));
this._typeahead.listen(
'choose',
JX.bind(this, function() {
JX.defer(
JX.bind(this._typeahead, this._typeahead.refresh));
}));
var repo_set_input = JX.bind(this, this._onrepochange);
this._typeahead.listen('start', repo_set_input);
JX.DOM.listen(
this._repositorySelect,
'change',
null,
repo_set_input);
this._typeahead.start();
this._validate();
},
_onrepochange : function() {
this._setPathInputBasedOnRepository(
this._typeahead,
this._textInputValues);
this._datasource.setAuxiliaryData(
{repositoryPHID : this._repositorySelect.value}
);
},
_setPathInputBasedOnRepository : function(typeahead, lookup) {
if (lookup[this._repositorySelect.value]) {
typeahead.setValue(lookup[this._repositorySelect.value]);
} else {
typeahead.setValue('/');
}
},
_initializeDatasource : function() {
this._datasource = new JX.TypeaheadOnDemandSource(this._completeURI);
this._datasource.setNormalizer(this._datasourceNormalizer);
this._datasource.setQueryDelay(40);
},
/*
* Construct and initialize the Typeahead.
* Must be called after initializing the datasource.
*/
_initializeTypeahead : function(path_input) {
this._typeahead = new JX.Typeahead(this._hardpoint, path_input);
this._datasource.setMaximumResultCount(15);
this._typeahead.setDatasource(this._datasource);
},
_datasourceNormalizer : function(str) {
return ('' + str).replace(/[\/]+/g, '\/');
},
_validate : function() {
var input = this._input;
var repo_id = this._repositorySelect.value;
var input_value = input.value;
var error_display = this._errorDisplay;
if (!input_value.length) {
input.value = '/';
input_value = '/';
}
if (this._validationInflight) {
this._validationInflight.abort();
this._validationInflight = null;
}
var validation_request = new JX.Request(
this._validateURI,
function(payload) {
// Don't change validation display state if the input has been
// changed since we started validation
if (input.value === input_value) {
if (payload.valid) {
JX.DOM.alterClass(error_display, 'invalid', false);
JX.DOM.alterClass(error_display, 'valid', true);
} else {
JX.DOM.alterClass(error_display, 'invalid', true);
JX.DOM.alterClass(error_display, 'valid', false);
}
JX.DOM.setContent(error_display, payload.message);
}
});
validation_request.listen('finally', function() {
JX.DOM.alterClass(error_display, 'validating', false);
this._validationInflight = null;
});
validation_request.setData(
{
repositoryPHID : repo_id,
path : input_value
});
this._validationInflight = validation_request;
validation_request.setTimeout(750);
validation_request.send();
}
}
});

View file

@ -0,0 +1,171 @@
/**
* @requires multirow-row-manager
* javelin-lib-dev
* javelin-typeahead-dev
* path-typeahead
* @provides owners-path-editor
* @javelin
*/
JX.install('OwnersPathEditor', {
construct : function(config) {
var root = JX.$(config.root);
this._rowManager = new JX.MultirowRowManager(
JX.DOM.find(root, 'table', config.table));
JX.DOM.listen(
JX.DOM.find(root, 'a', config.add_button),
'click',
null,
JX.bind(this, this._onaddpath));
this._count = 0;
this._repositories = config.repositories;
this._inputTemplate = config.input_template;
this._completeURI = config.completeURI;
this._validateURI = config.validateURI;
this._repositoryDefaultPaths = config.repositoryDefaultPaths;
this._initializePaths(config.pathRefs);
},
members : {
/*
* MultirowRowManager for controlling add/remove behavior
*/
_rowManager : null,
/*
* Array of objects with 'name' and 'repo_id' keys for
* selecting the repository of a path.
*/
_repositories : null,
/*
* How many rows have been created, for form name generation.
*/
_count : 0,
/*
* URL for the typeahead datasource.
*/
_completeURI : null,
/*
* URL for path validation requests.
*/
_validateURI : null,
/*
* Template typeahead markup to be copied per row.
*/
_inputTemplate : null,
/*
* Most packages will be in one repository, so remember whenever
* the user chooses a repository, and use that repository as the
* default for future rows.
*/
_lastRepositoryChoice : null,
_repositoryDefaultPaths : null,
/*
* Initialize with 0 or more rows.
* Adds one initial row if none are given.
*/
_initializePaths : function(path_refs) {
for (var k in path_refs) {
this.addPath(path_refs[k]);
}
if (!JX.keys(path_refs).length) {
this.addPath();
}
},
/*
* Build a row.
*/
addPath : function(path_ref) {
// Smart default repository. See _lastRepositoryChoice.
if (path_ref) {
this._lastRepositoryChoice = path_ref.repositoryPHID;
}
path_ref = path_ref || {};
var selected_repository = path_ref.repositoryPHID ||
this._lastRepositoryChoice;
var options = this._buildRepositoryOptions(selected_repository);
var attrs = {
name : "repo[" + this._count + "]"
};
var repo_select = JX.$N('select', attrs, options);
JX.DOM.listen(repo_select, 'change', null, JX.bind(this, function(e) {
this._lastRepositoryChoice = repo_select.value;
}));
var repo_cell = JX.$N('td', {}, repo_select);
var typeahead_cell = JX.$N(
'td',
JX.HTML(this._inputTemplate));
// Text input for path.
var path_input = JX.DOM.find(typeahead_cell, 'input');
JX.copy(
path_input,
{
value : path_ref.path || "",
name : "path[" + this._count + "]",
});
// The Typeahead requires a display div called hardpoint.
var hardpoint = JX.DOM.find(
typeahead_cell,
'div',
'typeahead-hardpoint');
var error_display = JX.$N(
'div',
{
className : "error-display validating"
},
'Validating...');
var error_display_cell = JX.$N('td', {}, error_display);
var row = this._rowManager.addRow(
[repo_cell, typeahead_cell, error_display_cell]);
new JX.PathTypeahead({
repositoryDefaultPaths : this._repositoryDefaultPaths,
repo_select : repo_select,
path_input : path_input,
hardpoint : hardpoint,
error_display : error_display,
completeURI : this._completeURI,
validateURI : this._validateURI}).start();
this._count++;
return row;
},
_onaddpath : function(e) {
e.kill();
this.addPath();
},
/**
* Helper to build the options for the repository choice dropdown.
*/
_buildRepositoryOptions : function(selected) {
var repos = this._repositories;
var result = [];
for (var k in repos) {
var attr = {
value : k,
selected : (selected == k)
};
result.push(JX.$N('option', attr, repos[k]));
}
return result;
}
}
});

View file

@ -0,0 +1,10 @@
/**
* @requires owners-path-editor
* javelin-lib-dev
* @provides javelin-behavior-owners-path-editor
* @javelin
*/
JX.behavior('owners-path-editor', function(config) {
new JX.OwnersPathEditor(config);
});