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:
parent
65e1386753
commit
5038ab850c
25 changed files with 1026 additions and 28 deletions
|
@ -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',
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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',
|
||||||
),
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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');
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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');
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'].')';
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
if (empty($data['nobranch'])) {
|
||||||
$branch = array_shift($parts);
|
$branch = array_shift($parts);
|
||||||
$this->branch = $this->decodeBranchName($branch);
|
$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
|
||||||
|
|
|
@ -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,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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();
|
||||||
|
|
81
src/view/control/typeahead/AphrontTypeaheadTemplateView.php
Normal file
81
src/view/control/typeahead/AphrontTypeaheadTemplateView.php
Normal 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>');
|
||||||
|
}
|
||||||
|
}
|
16
src/view/control/typeahead/__init__.php
Normal file
16
src/view/control/typeahead/__init__.php
Normal 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');
|
|
@ -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 {
|
||||||
|
|
37
webroot/rsrc/css/application/owners/owners-path-editor.css
Normal file
37
webroot/rsrc/css/application/owners/owners-path-editor.css
Normal 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;
|
||||||
|
}
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
202
webroot/rsrc/js/application/herald/PathTypeahead.js
Normal file
202
webroot/rsrc/js/application/herald/PathTypeahead.js
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
171
webroot/rsrc/js/application/owners/OwnersPathEditor.js
Normal file
171
webroot/rsrc/js/application/owners/OwnersPathEditor.js
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
10
webroot/rsrc/js/application/owners/owners-path-editor.js
Normal file
10
webroot/rsrc/js/application/owners/owners-path-editor.js
Normal 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);
|
||||||
|
});
|
Loading…
Reference in a new issue