Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2894217
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
92 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 1bb27137cc..09e6f6c6de 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,122 +1,138 @@
<?php
/**
* This file is automatically generated. Use 'phutil_mapper.php' to rebuild it.
* @generated
*/
phutil_register_library_map(array(
'class' =>
array(
'Aphront404Response' => 'aphront/response/404',
'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration',
'AphrontController' => 'aphront/controller',
'AphrontDatabaseConnection' => 'storage/connection/base',
'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration',
'AphrontDefaultApplicationController' => 'aphront/default/controller',
'AphrontDialogResponse' => 'aphront/response/dialog',
'AphrontDialogView' => 'view/dialog',
'AphrontErrorView' => 'view/form/error',
'AphrontFormControl' => 'view/form/control/base',
'AphrontFormSelectControl' => 'view/form/control/select',
'AphrontFormSubmitControl' => 'view/form/control/submit',
'AphrontFormTextAreaControl' => 'view/form/control/textarea',
'AphrontFormTextControl' => 'view/form/control/text',
'AphrontFormView' => 'view/form/base',
'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql',
'AphrontNullView' => 'view/null',
'AphrontPageView' => 'view/page/base',
'AphrontPanelView' => 'view/layout/panel',
'AphrontQueryConnectionException' => 'storage/exception/connection',
'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost',
'AphrontQueryCountException' => 'storage/exception/count',
'AphrontQueryException' => 'storage/exception/base',
'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing',
'AphrontQueryParameterException' => 'storage/exception/parameter',
'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
'AphrontRedirectResponse' => 'aphront/response/redirect',
'AphrontRequest' => 'aphront/request',
'AphrontResponse' => 'aphront/response/base',
'AphrontTableView' => 'view/control/table',
'AphrontURIMapper' => 'aphront/mapper',
'AphrontView' => 'view/base',
'AphrontWebpageResponse' => 'aphront/response/webpage',
'DifferentialAction' => 'applications/review/constants/action',
'DifferentialChangeType' => 'applications/review/constants/changetype',
'DifferentialLintStatus' => 'applications/review/constants/lintstatus',
'DifferentialRevisionStatus' => 'applications/review/constants/revisionstatus',
'DifferentialUnitStatus' => 'applications/review/constants/unitstatus',
'LiskDAO' => 'storage/lisk/dao',
'PhabricatorController' => 'applications/base/controller/base',
'PhabricatorDirectoryCategory' => 'applications/directory/storage/category',
'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete',
'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit',
'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist',
'PhabricatorDirectoryController' => 'applications/directory/controller/base',
'PhabricatorDirectoryDAO' => 'applications/directory/storage/base',
'PhabricatorDirectoryItem' => 'applications/directory/storage/item',
'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete',
'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit',
'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist',
'PhabricatorDirectoryMainController' => 'applications/directory/controller/main',
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
+ 'PhabricatorPHID' => 'applications/phid/storage/phid',
+ 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate',
+ 'PhabricatorPHIDController' => 'applications/phid/controller/base',
+ 'PhabricatorPHIDDAO' => 'applications/phid/storage/base',
+ 'PhabricatorPHIDListController' => 'applications/phid/controller/list',
+ 'PhabricatorPHIDType' => 'applications/phid/storage/type',
+ 'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit',
+ 'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist',
'PhabricatorStandardPageView' => 'view/page/standard',
),
'function' =>
array(
'_qsprintf_check_scalar_type' => 'storage/qsprintf',
'_qsprintf_check_type' => 'storage/qsprintf',
'qsprintf' => 'storage/qsprintf',
'queryfx' => 'storage/queryfx',
'queryfx_all' => 'storage/queryfx',
'queryfx_one' => 'storage/queryfx',
'vqsprintf' => 'storage/qsprintf',
'vqueryfx' => 'storage/queryfx',
'xsprintf_query' => 'storage/qsprintf',
),
'requires_class' =>
array(
'Aphront404Response' => 'AphrontResponse',
'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration',
'AphrontDefaultApplicationController' => 'AphrontController',
'AphrontDialogResponse' => 'AphrontResponse',
'AphrontDialogView' => 'AphrontView',
'AphrontErrorView' => 'AphrontView',
'AphrontFormControl' => 'AphrontView',
'AphrontFormSelectControl' => 'AphrontFormControl',
'AphrontFormSubmitControl' => 'AphrontFormControl',
'AphrontFormTextAreaControl' => 'AphrontFormControl',
'AphrontFormTextControl' => 'AphrontFormControl',
'AphrontFormView' => 'AphrontView',
'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection',
'AphrontNullView' => 'AphrontView',
'AphrontPageView' => 'AphrontView',
'AphrontPanelView' => 'AphrontView',
'AphrontQueryConnectionException' => 'AphrontQueryException',
'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException',
'AphrontQueryCountException' => 'AphrontQueryException',
'AphrontQueryObjectMissingException' => 'AphrontQueryException',
'AphrontQueryParameterException' => 'AphrontQueryException',
'AphrontQueryRecoverableException' => 'AphrontQueryException',
'AphrontRedirectResponse' => 'AphrontResponse',
'AphrontTableView' => 'AphrontView',
'AphrontWebpageResponse' => 'AphrontResponse',
'PhabricatorController' => 'AphrontController',
'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryController' => 'PhabricatorController',
'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO',
'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO',
'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController',
'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController',
'PhabricatorLiskDAO' => 'LiskDAO',
+ 'PhabricatorPHID' => 'PhabricatorPHIDDAO',
+ 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
+ 'PhabricatorPHIDController' => 'PhabricatorController',
+ 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO',
+ 'PhabricatorPHIDListController' => 'PhabricatorPHIDController',
+ 'PhabricatorPHIDType' => 'PhabricatorPHIDDAO',
+ 'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController',
+ 'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController',
'PhabricatorStandardPageView' => 'AphrontPageView',
),
'requires_interface' =>
array(
),
));
diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
index f73dda95f0..4765cf56e6 100644
--- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,100 +1,109 @@
<?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.
*/
/**
* @group aphront
*/
class AphrontDefaultApplicationConfiguration
extends AphrontApplicationConfiguration {
public function getApplicationName() {
return 'aphront-default';
}
public function getURIMap() {
return array(
'/repository/' => array(
'$' => 'RepositoryListController',
'new/$' => 'RepositoryEditController',
'edit/(?<id>\d+)/$' => 'RepositoryEditController',
'delete/(?<id>\d+)/$' => 'RepositoryDeleteController',
),
'/' => array(
- '$' => 'AphrontDirectoryMainController',
+ '$' => 'PhabricatorDirectoryMainController',
),
'/directory/' => array(
- 'item/$' => 'AphrontDirectoryItemListController',
- 'item/edit/(?:(?<id>\d+)/)?$' => 'AphrontDirectoryItemEditController',
- 'item/delete/(?<id>\d+)/' => 'AphrontDirectoryItemDeleteController',
+ 'item/$'
+ => 'PhabricatorDirectoryItemListController',
+ 'item/edit/(?:(?<id>\d+)/)?$'
+ => 'PhabricatorDirectoryItemEditController',
+ 'item/delete/(?<id>\d+)/'
+ => 'PhabricatorDirectoryItemDeleteController',
'category/$'
- => 'AphrontDirectoryCategoryListController',
+ => 'PhabricatorDirectoryCategoryListController',
'category/edit/(?:(?<id>\d+)/)?$'
- => 'AphrontDirectoryCategoryEditController',
+ => 'PhabricatorDirectoryCategoryEditController',
'category/delete/(?<id>\d+)/'
- => 'AphrontDirectoryCategoryDeleteController',
+ => 'PhabricatorDirectoryCategoryDeleteController',
+ ),
+ '/phid/' => array(
+ '$' => 'PhabricatorPHIDListController',
+ 'type/$' => 'PhabricatorPHIDTypeListController',
+ 'type/edit/(?:(?<id>\d+)/)?$' => 'PhabricatorPHIDTypeEditController',
+ 'new/$' => 'PhabricatorPHIDAllocateController',
),
'.*' => 'AphrontDefaultApplicationController',
);
}
public function buildRequest() {
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($_GET + $_POST);
return $request;
}
public function handleException(Exception $ex) {
$class = phutil_escape_html(get_class($ex));
$message = phutil_escape_html($ex->getMessage());
$content =
'<div class="aphront-unhandled-exception">'.
'<h1>Unhandled Exception "'.$class.'": '.$message.'</h1>'.
'<code>'.phutil_escape_html((string)$ex).'</code>'.
'</div>';
$view = new PhabricatorStandardPageView();
$view->appendChild($content);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
public function willSendResponse(AphrontResponse $response) {
$request = $this->getRequest();
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax()) {
$view = new PhabricatorStandardPageView();
$view->appendChild(
'<div style="padding: 2em 0;">'.
$response->buildResponseString().
'</div>');
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
}
return $response;
}
}
diff --git a/src/aphront/response/webpage/AphrontWebpageResponse.php b/src/aphront/response/webpage/AphrontWebpageResponse.php
index 40ceb9e2eb..94e976a64c 100644
--- a/src/aphront/response/webpage/AphrontWebpageResponse.php
+++ b/src/aphront/response/webpage/AphrontWebpageResponse.php
@@ -1,35 +1,41 @@
<?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.
*/
/**
* @group aphront
*/
class AphrontWebpageResponse extends AphrontResponse {
private $content;
public function setContent($content) {
$this->content = $content;
return $this;
}
public function buildResponseString() {
return $this->content;
}
+ public function getHeaders() {
+ return array(
+ array('Content-Type', 'text/html; charset=UTF-8'),
+ );
+ }
+
}
diff --git a/src/applications/base/controller/base/__init__.php b/src/applications/base/controller/base/__init__.php
new file mode 100644
index 0000000000..03cfc02dba
--- /dev/null
+++ b/src/applications/base/controller/base/__init__.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/controller');
+phutil_require_module('phabricator', 'aphront/response/webpage');
+phutil_require_module('phabricator', 'view/page/standard');
+
+
+phutil_require_source('PhabricatorController.php');
diff --git a/src/applications/base/storage/lisk/PhabricatorLiskDAO.php b/src/applications/base/storage/lisk/PhabricatorLiskDAO.php
index 069dca62a3..c27248660e 100644
--- a/src/applications/base/storage/lisk/PhabricatorLiskDAO.php
+++ b/src/applications/base/storage/lisk/PhabricatorLiskDAO.php
@@ -1,49 +1,54 @@
<?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.
*/
abstract class PhabricatorLiskDAO extends LiskDAO {
public function establishConnection($mode) {
return new AphrontMySQLDatabaseConnection(
array(
'user' => 'root',
'pass' => '',
'host' => 'localhost',
'database' => 'phabricator_'.$this->getApplicationName(),
));
}
public function getTableName() {
$str = 'phabricator';
$len = strlen($str);
$class = strtolower(get_class($this));
if (!strncmp($class, $str, $len)) {
$class = substr($class, $len);
}
$app = $this->getApplicationName();
if (!strncmp($class, $app, strlen($app))) {
$class = substr($class, strlen($app));
}
- return $app.'_'.$class;
+
+ if (strlen($class)) {
+ return $app.'_'.$class;
+ } else {
+ return $app;
+ }
}
abstract public function getApplicationName();
}
diff --git a/src/applications/directory/storage/item/PhabricatorDirectoryItem.php b/src/applications/directory/storage/item/PhabricatorDirectoryItem.php
index 811bb59a69..6a7ea3c6f8 100644
--- a/src/applications/directory/storage/item/PhabricatorDirectoryItem.php
+++ b/src/applications/directory/storage/item/PhabricatorDirectoryItem.php
@@ -1,36 +1,36 @@
<?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 PhabricatorDirectoryItem extends PhabricatorDirectoryDAO {
protected $name;
protected $description;
protected $href;
protected $categoryID;
protected $sequence;
- protected $imageGUID;
+ protected $imagePHID;
public function getSortKey() {
return sprintf(
'%08d:%08d:%s',
$this->getCategoryID(),
$this->getSequence(),
$this->getName());
}
}
diff --git a/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php b/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php
new file mode 100644
index 0000000000..53f8af3e14
--- /dev/null
+++ b/src/applications/phid/controller/allocate/PhabricatorPHIDAllocateController.php
@@ -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 PhabricatorPHIDAllocateController
+ extends PhabricatorPHIDController {
+
+ public function processRequest() {
+
+ $request = $this->getRequest();
+ if ($request->isFormPost()) {
+ $type = $request->getStr('type');
+ $phid = PhabricatorPHID::generateNewPHID($type);
+
+ return id(new AphrontRedirectResponse())
+ ->setURI('/phid/?phid='.phutil_escape_uri($phid));
+ }
+
+ $types = id(new PhabricatorPHIDType())->loadAll();
+
+ $options = array();
+ foreach ($types as $type) {
+ $options[$type->getType()] = $type->getType().': '.$type->getName();
+ }
+ asort($options);
+
+ $form = new AphrontFormView();
+ $form->setAction('/phid/new/');
+
+ $form
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setLabel('PHID Type')
+ ->setName('type')
+ ->setOptions($options))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue('Allocate')
+ ->addCancelButton('/phid/'));
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Allocate New PHID');
+
+ $panel->appendChild($form);
+ $panel->setWidth(AphrontPanelView::WIDTH_FORM);
+
+ return $this->buildStandardPageResponse(
+ array($panel),
+ array(
+ 'title' => 'Allocate New PHID',
+ ));
+ }
+
+}
diff --git a/src/applications/phid/controller/allocate/__init__.php b/src/applications/phid/controller/allocate/__init__.php
new file mode 100644
index 0000000000..0605962517
--- /dev/null
+++ b/src/applications/phid/controller/allocate/__init__.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/response/redirect');
+phutil_require_module('phabricator', 'applications/phid/controller/base');
+phutil_require_module('phabricator', 'applications/phid/storage/phid');
+phutil_require_module('phabricator', 'applications/phid/storage/type');
+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('PhabricatorPHIDAllocateController.php');
diff --git a/src/applications/phid/controller/base/PhabricatorPHIDController.php b/src/applications/phid/controller/base/PhabricatorPHIDController.php
new file mode 100644
index 0000000000..4c190e9ab7
--- /dev/null
+++ b/src/applications/phid/controller/base/PhabricatorPHIDController.php
@@ -0,0 +1,46 @@
+<?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.
+ */
+
+abstract class PhabricatorPHIDController extends PhabricatorController {
+
+ public function buildStandardPageResponse($view, array $data) {
+ $page = new PhabricatorStandardPageView();
+
+ $page->setApplicationName('PHID');
+ $page->setBaseURI('/phid/');
+ $page->setTitle(idx($data, 'title'));
+ $page->setTabs(
+ array(
+ 'phids' => array(
+ 'href' => '/phid/',
+ 'name' => 'PHIDs',
+ ),
+ 'types' => array(
+ 'href' => '/phid/type/',
+ 'name' => 'PHID Types',
+ ),
+ ),
+ idx($data, 'tab'));
+ $page->setGlyph('#');
+ $page->appendChild($view);
+
+ $response = new AphrontWebpageResponse();
+ return $response->setContent($page->render());
+ }
+
+}
diff --git a/src/applications/phid/controller/base/__init__.php b/src/applications/phid/controller/base/__init__.php
new file mode 100644
index 0000000000..44f0f1b94d
--- /dev/null
+++ b/src/applications/phid/controller/base/__init__.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/response/webpage');
+phutil_require_module('phabricator', 'applications/base/controller/base');
+phutil_require_module('phabricator', 'view/page/standard');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorPHIDController.php');
diff --git a/src/applications/phid/controller/list/PhabricatorPHIDListController.php b/src/applications/phid/controller/list/PhabricatorPHIDListController.php
new file mode 100644
index 0000000000..33803cc4d0
--- /dev/null
+++ b/src/applications/phid/controller/list/PhabricatorPHIDListController.php
@@ -0,0 +1,56 @@
+<?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 PhabricatorPHIDListController
+ extends PhabricatorPHIDController {
+
+ public function processRequest() {
+ $items = id(new PhabricatorPHID())->loadAllWhere(
+ '1 = 1 ORDER BY id DESC limit 100');
+
+ $rows = array();
+ foreach ($items as $item) {
+ $rows[] = array(
+ phutil_escape_html($item->getPHID()),
+ phutil_escape_html($item->getPHIDType()),
+ phutil_escape_html($item->getOwnerPHID()),
+ phutil_escape_html($item->getParentPHID()),
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setHeaders(
+ array(
+ 'PHID',
+ 'Type',
+ 'Owner PHID',
+ 'Parent PHID',
+ ));
+
+ $panel = new AphrontPanelView();
+ $panel->appendChild($table);
+ $panel->setHeader('PHIDs');
+ $panel->setCreateButton('Allocate New PHID', '/phid/new/');
+
+ return $this->buildStandardPageResponse($panel, array(
+ 'title' => 'PHIDs',
+ 'tab' => 'phids',
+ ));
+ }
+
+}
diff --git a/src/applications/phid/controller/list/__init__.php b/src/applications/phid/controller/list/__init__.php
new file mode 100644
index 0000000000..913d4fe3f1
--- /dev/null
+++ b/src/applications/phid/controller/list/__init__.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/phid/controller/base');
+phutil_require_module('phabricator', 'applications/phid/storage/phid');
+phutil_require_module('phabricator', 'view/control/table');
+phutil_require_module('phabricator', 'view/layout/panel');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorPHIDListController.php');
diff --git a/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php b/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php
new file mode 100644
index 0000000000..a82e63c778
--- /dev/null
+++ b/src/applications/phid/controller/typeedit/PhabricatorPHIDTypeEditController.php
@@ -0,0 +1,132 @@
+<?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 PhabricatorPHIDTypeEditController
+ extends PhabricatorPHIDController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+
+ if ($this->id) {
+ $type = id(new PhabricatorPHIDType())->load($this->id);
+ if (!$type) {
+ return new Aphront404Response();
+ }
+ } else {
+ $type = new PhabricatorPHIDType();
+ }
+
+ $e_type = true;
+ $e_name = true;
+ $errors = array();
+
+ $request = $this->getRequest();
+ if ($request->isFormPost()) {
+ $type->setName($request->getStr('name'));
+ if (!$type->getID()) {
+ $type->setType($request->getStr('type'));
+ }
+ $type->setDescription($request->getStr('description'));
+
+ if (!strlen($type->getType())) {
+ $errors[] = 'Type code is required.';
+ $e_type = 'Required';
+ }
+
+ if (!strlen($type->getName())) {
+ $errors[] = 'Type name is required.';
+ $e_name = 'Required';
+ }
+
+ if (!$errors) {
+ $type->save();
+ return id(new AphrontRedirectResponse())
+ ->setURI('/phid/type/');
+ }
+ }
+
+ $error_view = null;
+ if ($errors) {
+ $error_view = id(new AphrontErrorView())
+ ->setTitle('Form Errors')
+ ->setErrors($errors);
+ }
+
+ $form = new AphrontFormView();
+ if ($type->getID()) {
+ $form->setAction('/phid/type/edit/'.$type->getID().'/');
+ } else {
+ $form->setAction('/phid/type/edit/');
+ }
+
+ if ($type->getID()) {
+ $type_immutable = true;
+ } else {
+ $type_immutable = false;
+ }
+
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel('Type')
+ ->setName('type')
+ ->setValue($type->getType())
+ ->setError($e_type)
+ ->setCaption(
+ 'Four character type identifier. This can not be changed once '.
+ 'it is created.')
+ ->setDisabled($type_immutable))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel('Name')
+ ->setName('name')
+ ->setValue($type->getName())
+ ->setError($e_name))
+ ->appendChild(
+ id(new AphrontFormTextAreaControl())
+ ->setLabel('Description')
+ ->setName('description')
+ ->setValue($type->getDescription()))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue('Save')
+ ->addCancelButton('/phid/type/'));
+
+ $panel = new AphrontPanelView();
+ if ($type->getID()) {
+ $panel->setHeader('Edit PHID Type');
+ } else {
+ $panel->setHeader('Create New PHID Type');
+ }
+
+ $panel->appendChild($form);
+ $panel->setWidth(AphrontPanelView::WIDTH_FORM);
+
+ return $this->buildStandardPageResponse(
+ array($error_view, $panel),
+ array(
+ 'title' => 'Edit PHID Type',
+ ));
+ }
+
+}
diff --git a/src/applications/phid/controller/typeedit/__init__.php b/src/applications/phid/controller/typeedit/__init__.php
new file mode 100644
index 0000000000..0805e99ff0
--- /dev/null
+++ b/src/applications/phid/controller/typeedit/__init__.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'aphront/response/404');
+phutil_require_module('phabricator', 'aphront/response/redirect');
+phutil_require_module('phabricator', 'applications/phid/controller/base');
+phutil_require_module('phabricator', 'applications/phid/storage/type');
+phutil_require_module('phabricator', 'view/form/base');
+phutil_require_module('phabricator', 'view/form/control/submit');
+phutil_require_module('phabricator', 'view/form/error');
+phutil_require_module('phabricator', 'view/layout/panel');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorPHIDTypeEditController.php');
diff --git a/src/applications/phid/controller/typelist/PhabricatorPHIDTypeListController.php b/src/applications/phid/controller/typelist/PhabricatorPHIDTypeListController.php
new file mode 100644
index 0000000000..cb961798c0
--- /dev/null
+++ b/src/applications/phid/controller/typelist/PhabricatorPHIDTypeListController.php
@@ -0,0 +1,59 @@
+<?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 PhabricatorPHIDTypeListController
+ extends PhabricatorPHIDController {
+
+ public function processRequest() {
+ $items = id(new PhabricatorPHIDType())->loadAll();
+
+ $rows = array();
+ foreach ($items as $item) {
+ $rows[] = array(
+ $item->getID(),
+ phutil_escape_html($item->getType()),
+ phutil_escape_html($item->getName()),
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setHeaders(
+ array(
+ 'ID',
+ 'Type Code',
+ 'Name',
+ ));
+ $table->setColumnClasses(
+ array(
+ null,
+ null,
+ 'wide',
+ ));
+
+ $panel = new AphrontPanelView();
+ $panel->appendChild($table);
+ $panel->setHeader('PHID Types');
+ $panel->setCreateButton('New Type', '/phid/type/edit/');
+
+ return $this->buildStandardPageResponse($panel, array(
+ 'title' => 'PHID Types',
+ 'tab' => 'types',
+ ));
+ }
+
+}
diff --git a/src/applications/phid/controller/typelist/__init__.php b/src/applications/phid/controller/typelist/__init__.php
new file mode 100644
index 0000000000..81a4d0557d
--- /dev/null
+++ b/src/applications/phid/controller/typelist/__init__.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/phid/controller/base');
+phutil_require_module('phabricator', 'applications/phid/storage/type');
+phutil_require_module('phabricator', 'view/control/table');
+phutil_require_module('phabricator', 'view/layout/panel');
+
+phutil_require_module('phutil', 'markup');
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorPHIDTypeListController.php');
diff --git a/src/aphront/response/webpage/AphrontWebpageResponse.php b/src/applications/phid/storage/base/PhabricatorPHIDDAO.php
similarity index 69%
copy from src/aphront/response/webpage/AphrontWebpageResponse.php
copy to src/applications/phid/storage/base/PhabricatorPHIDDAO.php
index 40ceb9e2eb..a133222722 100644
--- a/src/aphront/response/webpage/AphrontWebpageResponse.php
+++ b/src/applications/phid/storage/base/PhabricatorPHIDDAO.php
@@ -1,35 +1,25 @@
<?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.
*/
-/**
- * @group aphront
- */
-class AphrontWebpageResponse extends AphrontResponse {
-
- private $content;
-
- public function setContent($content) {
- $this->content = $content;
- return $this;
- }
+class PhabricatorPHIDDAO extends PhabricatorLiskDAO {
- public function buildResponseString() {
- return $this->content;
+ public function getApplicationName() {
+ return 'phid';
}
}
diff --git a/src/applications/phid/storage/base/__init__.php b/src/applications/phid/storage/base/__init__.php
new file mode 100644
index 0000000000..5c42a7b15d
--- /dev/null
+++ b/src/applications/phid/storage/base/__init__.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/base/storage/lisk');
+
+
+phutil_require_source('PhabricatorPHIDDAO.php');
diff --git a/src/applications/phid/storage/phid/PhabricatorPHID.php b/src/applications/phid/storage/phid/PhabricatorPHID.php
new file mode 100644
index 0000000000..70f018ad4f
--- /dev/null
+++ b/src/applications/phid/storage/phid/PhabricatorPHID.php
@@ -0,0 +1,56 @@
+<?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 PhabricatorPHID extends PhabricatorPHIDDAO {
+
+ protected $phid;
+ protected $phidType;
+ protected $ownerPHID;
+ protected $parentPHID;
+
+ public static function generateNewPHID($type, array $config = array()) {
+ $owner = idx($config, 'owner');
+ $parent = idx($config, 'parent');
+
+ if (!$type) {
+ throw new Exception("Can not generate PHID with no type.");
+ }
+
+ $urandom = @fopen('/dev/urandom', 'r');
+ if (!$urandom) {
+ throw new Exception("Failed to open /dev/urandom!");
+ }
+ $entropy = fread($urandom, 16);
+ if (strlen($entropy) != 16) {
+ throw new Exception("Failed to read from /dev/urandom!");
+ }
+
+ $uniq = sha1($entropy);
+ $phid = 'PHID-'.$type.'-X-'.$uniq;
+
+ $phid_rec = new PhabricatorPHID();
+ $phid_rec->setPHIDType($type);
+ $phid_rec->setOwnerPHID($owner);
+ $phid_rec->setParentPHID($parent);
+ $phid_rec->setPHID($phid);
+ $phid_rec->save();
+
+ return $phid;
+ }
+
+}
diff --git a/src/applications/phid/storage/phid/__init__.php b/src/applications/phid/storage/phid/__init__.php
new file mode 100644
index 0000000000..5ff7852286
--- /dev/null
+++ b/src/applications/phid/storage/phid/__init__.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/phid/storage/base');
+
+phutil_require_module('phutil', 'utils');
+
+
+phutil_require_source('PhabricatorPHID.php');
diff --git a/src/aphront/response/webpage/AphrontWebpageResponse.php b/src/applications/phid/storage/type/PhabricatorPHIDType.php
similarity index 69%
copy from src/aphront/response/webpage/AphrontWebpageResponse.php
copy to src/applications/phid/storage/type/PhabricatorPHIDType.php
index 40ceb9e2eb..b5c50d512b 100644
--- a/src/aphront/response/webpage/AphrontWebpageResponse.php
+++ b/src/applications/phid/storage/type/PhabricatorPHIDType.php
@@ -1,35 +1,25 @@
<?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.
*/
-/**
- * @group aphront
- */
-class AphrontWebpageResponse extends AphrontResponse {
-
- private $content;
-
- public function setContent($content) {
- $this->content = $content;
- return $this;
- }
+class PhabricatorPHIDType extends PhabricatorPHIDDAO {
- public function buildResponseString() {
- return $this->content;
- }
+ protected $type;
+ protected $name;
+ protected $description;
}
diff --git a/src/applications/phid/storage/type/__init__.php b/src/applications/phid/storage/type/__init__.php
new file mode 100644
index 0000000000..9753ee3309
--- /dev/null
+++ b/src/applications/phid/storage/type/__init__.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/phid/storage/base');
+
+
+phutil_require_source('PhabricatorPHIDType.php');
diff --git a/src/storage/lisk/dao/LiskDAO.php b/src/storage/lisk/dao/LiskDAO.php
index de90777d70..226c89d4fd 100644
--- a/src/storage/lisk/dao/LiskDAO.php
+++ b/src/storage/lisk/dao/LiskDAO.php
@@ -1,1109 +1,1109 @@
<?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.
*/
/**
* Simple object-authoritative data access object that makes it easy to build
* stuff that you need to save to a database. Basically, it means that the
* amount of boilerplate code (and, particularly, boilerplate SQL) you need
* to write is greatly reduced.
*
* Lisk makes it fairly easy to build something quickly and end up with
* reasonably high-quality code when you're done (e.g., getters and setters,
* objects, transactions, reasonably structured OO code). It's also very thin:
* you can break past it and use MySQL and other lower-level tools when you
* need to in those couple of cases where it doesn't handle your workflow
* gracefully.
*
* However, Lisk won't scale past one database and lacks many of the features
* of modern DAOs like Hibernate: for instance, it does not support joins or
* polymorphic storage.
*
* This means that Lisk is well-suited for tools like Differential, but often a
* poor choice elsewhere. And it is strictly unsuitable for many projects.
*
* Lisk's model is object-authoritative: the PHP class definition is the
* master authority for what the object looks like.
*
* =Building New Objects=
*
* To create new Lisk objects, extend @{class:LiskDAO} and implement
* @{method:establishConnection}. It should return an AphrontDatabaseConnection;
* this will tell Lisk where to save your objects.
*
* class Dog extends LiskDAO {
*
* protected $name;
* protected $breed;
*
* public function establishConnection() {
* return $some_connection_object;
* }
* }
*
* Now, you should create your table:
*
* CREATE TABLE dog (
* id int unsigned not null auto_increment primary key,
* name varchar(32) not null,
* breed varchar(32) not null,
* dateCreated int unsigned not null,
* dateModified int unsigned not null
* );
*
* For each property in your class, add a column with the same name to the
* table (see getConfiguration() for information about changing this mapping).
* Additionally, you should create the three columns `id`, `dateCreated` and
* `dateModified`. Lisk will automatically manage these, using them to implement
* autoincrement IDs and timestamps. If you do not want to use these features,
* see getConfiguration() for information on disabling them. At a bare minimum,
* you must normally have an `id` column which is a primary or unique key with a
* numeric type, although you can change its name by overriding getIDKey() or
* disable it entirely by overriding getIDKey() to return null. Note that many
* methods rely on a single-part primary key and will no longer work (they will
* throw) if you disable it.
*
* As you add more properties to your class in the future, remember to add them
* to the database table as well.
*
* Lisk will now automatically handle these operations: getting and setting
* properties, saving objects, loading individual objects, loading groups
* of objects, updating objects, managing IDs, updating timestamps whenever
* an object is created or modified, and some additional specialized
* operations.
*
* = Creating, Retrieving, Updating, and Deleting =
*
* To create and persist a Lisk object, use save():
*
* $dog = id(new Dog())
* ->setName('Sawyer')
* ->setBreed('Pug')
* ->save();
*
* Note that **Lisk automatically builds getters and setters for all of your
* object's properties** via __call(). You can override these by defining
* versions yourself.
*
* Calling save() will persist the object to the database. After calling
* save(), you can call getID() to retrieve the object's ID.
*
* To load objects by ID, use the load() method:
*
* $dog = id(new Dog())->load($id);
*
* This will load the Dog record with ID $id into $dog, or ##null## if no such
* record exists (load() is an instance method rather than a static method
* because PHP does not support late static binding, at least until PHP 5.3).
*
* To update an object, change its properties and save it:
*
* $dog->setBreed('Lab')->save();
*
* To delete an object, call delete():
*
* $dog->delete();
*
* That's Lisk CRUD in a nutshell.
*
* = Queries =
*
* Often, you want to load a bunch of objects, or execute a more specialized
* query. Use loadAllWhere() or loadOneWhere() to do this:
*
* $pugs = $dog->loadAllWhere('breed = %s', 'Pug');
* $sawyer = $dog->loadOneWhere('name = %s', 'Sawyer');
*
* These methods work like @{function:queryfx}, but only take half of a query
* (the part after the WHERE keyword). Lisk will handle the connection, columns,
* and object construction; you are responsible for the rest of it.
* loadAllWhere() returns a list of objects, while loadOneWhere() returns a
* single object (or null).
*
* @task config Configuring Lisk
* @task load Loading Objects
* @task info Examining Objects
* @task save Writing Objects
* @task hook Hooks and Callbacks
* @task util Utilities
*
* @group storage
*/
abstract class LiskDAO {
const CONFIG_OPTIMISTIC_LOCKS = 'enable-locks';
const CONFIG_IDS = 'id-mechanism';
const CONFIG_TIMESTAMPS = 'timestamps';
- const CONFIG_AUX_GUID = 'auxiliary-guid';
+ const CONFIG_AUX_PHID = 'auxiliary-phid';
const CONFIG_SERIALIZATION = 'col-serialization';
const SERIALIZATION_NONE = 'id';
const SERIALIZATION_JSON = 'json';
const SERIALIZATION_PHP = 'php';
const IDS_AUTOINCREMENT = 'ids-auto';
- const IDS_GUID = 'ids-guid';
+ const IDS_PHID = 'ids-phid';
const IDS_MANUAL = 'ids-manual';
/**
* Build an empty object.
*
* @return obj Empty object.
*/
public function __construct() {
$id_key = $this->getIDKey();
if ($id_key) {
$this->$id_key = null;
}
}
abstract protected function establishConnection($mode);
/* -( Configuring Lisk )--------------------------------------------------- */
/**
* Change Lisk behaviors, like optimistic locks and timestamps. If you want
* to change these behaviors, you should override this method in your child
* class and change the options you're interested in. For example:
*
* public function getConfiguration() {
* return array(
* Lisk_DataAccessObject::CONFIG_EXAMPLE => true,
* ) + parent::getConfiguration();
* }
*
* The available options are:
*
* CONFIG_OPTIMISTIC_LOCKS
* Lisk automatically performs optimistic locking on objects, which protects
* you from read-modify-write concurrency problems. Lock failures are
* detected at write time and arise when two users read an object, then both
* save it. In theory, you should detect these failures and accommodate them
* in some sensible way (for instance, by showing the user differences
* between the original record and the copy they are trying to update, and
* prompting them to merge them). In practice, most Lisk tools are quick
* and dirty and don't get to that level of sophistication, but optimistic
* locks can still protect you from yourself sometimes. If you don't want
* to use optimistic locks, you can disable them. The performance cost of
* doing this locking is very very small (optimistic locks were chosen
* because they're simple and cheap, and highly optimized for the case where
* collisions are rare). By default, this option is OFF.
*
* CONFIG_IDS
* Lisk objects need to have a unique identifying ID. The three mechanisms
* available for generating this ID are IDS_AUTOINCREMENT (default, assumes
- * the ID column is an autoincrement primary key), IDS_GUID (to generate a
- * unique GUID for each object) or IDS_MANUAL (you are taking full
+ * the ID column is an autoincrement primary key), IDS_PHID (to generate a
+ * unique PHID for each object) or IDS_MANUAL (you are taking full
* responsibility for ID management).
*
* CONFIG_TIMESTAMPS
* Lisk can automatically handle keeping track of a `dateCreated' and
* `dateModified' column, which it will update when it creates or modifies
* an object. If you don't want to do this, you may disable this option.
* By default, this option is ON.
*
- * CONFIG_AUX_GUID
+ * CONFIG_AUX_PHID
* This option can be enabled by being set to some truthy value. The meaning
- * of this value is defined by your guid generation mechanism. If this option
- * is enabled, a `guid' property will be populated with a unique GUID when an
+ * of this value is defined by your PHID generation mechanism. If this option
+ * is enabled, a `phid' property will be populated with a unique PHID when an
* object is created (or if it is saved and does not currently have one). You
- * need to override generateGUID() and hook it into your GUID generation
+ * need to override generatePHID() and hook it into your PHID generation
* mechanism for this to work. By default, this option is OFF.
*
* CONFIG_SERIALIZATION
* You can optionally provide a column serialization map that will be applied
* to values when they are written to the database. For example:
*
* self::CONFIG_SERIALIZATION => array(
* 'complex' => self::SERIALIZATION_JSON,
* )
*
* This will cause Lisk to JSON-serialize the 'complex' field before it is
* written, and unserialize it when it is read.
*
*
* @return dictionary Map of configuration options to values.
*
* @task config
*/
protected function getConfiguration() {
return array(
self::CONFIG_OPTIMISTIC_LOCKS => false,
self::CONFIG_IDS => self::IDS_AUTOINCREMENT,
self::CONFIG_TIMESTAMPS => true,
);
}
/**
* Determine the setting of a configuration option for this class of objects.
*
* @param const Option name, one of the CONFIG_* constants.
* @return mixed Option value, if configured (null if unavailable).
*
* @task config
*/
public function getConfigOption($option_name) {
static $options = null;
if (!isset($options)) {
$options = $this->getConfiguration();
}
return idx($options, $option_name);
}
/* -( Loading Objects )---------------------------------------------------- */
/**
* Load an object by ID. You need to invoke this as an instance method, not
* a class method, because PHP doesn't have late static binding (until
* PHP 5.3.0). For example:
*
* $dog = id(new Dog())->load($dog_id);
*
* @param int Numeric ID identifying the object to load.
* @return obj|null Identified object, or null if it does not exist.
*
* @task load
*/
public function load($id) {
if (!($id = (int)$id)) {
throw new Exception("Bogus ID provided to load().");
}
return $this->loadOneWhere(
'%C = %d',
$this->getIDKeyForUse(),
$id);
}
/**
* Loads all of the objects, unconditionally.
*
* @return dict Dictionary of all persisted objects of this type, keyed
* on object ID.
*
* @task load
*/
public function loadAll() {
return $this->loadAllWhere('1 = 1');
}
/**
* Load all objects which match a WHERE clause. You provide everything after
* the 'WHERE'; Lisk handles everything up to it. For example:
*
* $old_dogs = id(new Dog())->loadAllWhere('age > %d', 7);
*
* The pattern and arguments are as per queryfx().
*
* @param string queryfx()-style SQL WHERE clause.
* @param ... Zero or more conversions.
* @return dict Dictionary of matching objects, keyed on ID.
*
* @task load
*/
public function loadAllWhere($pattern/*, $arg, $arg, $arg ... */) {
$args = func_get_args();
$data = call_user_func_array(
array($this, 'loadRawDataWhere'),
$args);
return $this->loadAllFromArray($data);
}
/**
* Load a single object identified by a 'WHERE' clause. You provide
* everything after the 'WHERE', and Lisk builds the first half of the
* query. See loadAllWhere(). This method is similar, but returns a single
* result instead of a list.
*
* @param string queryfx()-style SQL WHERE clause.
* @param ... Zero or more conversions.
* @return obj|null Matching object, or null if no object matches.
*
* @task load
*/
public function loadOneWhere($pattern/*, $arg, $arg, $arg ... */) {
$args = func_get_args();
$data = call_user_func_array(
array($this, 'loadRawDataWhere'),
$args);
if (count($data) > 1) {
throw new AphrontQueryCountException(
"More than 1 result from loadOneWhere()!");
}
$data = reset($data);
if (!$data) {
return null;
}
return $this->loadFromArray($data);
}
protected function loadRawDataWhere($pattern/*, $arg, $arg, $arg ... */) {
$connection = $this->getConnection('r');
$lock_clause = '';
if ($connection->isReadLocking()) {
$lock_clause = 'FOR UPDATE';
} else if ($connection->isWriteLocking()) {
$lock_clause = 'LOCK IN SHARE MODE';
}
$args = func_get_args();
$args = array_slice($args, 1);
$pattern = 'SELECT * FROM %T WHERE '.$pattern.' %Q';
array_unshift($args, $this->getTableName());
array_push($args, $lock_clause);
array_unshift($args, $pattern);
return call_user_func_array(
array($connection, 'queryData'),
$args);
}
/**
* Reload an object from the database, discarding any changes to persistent
* properties. If the object uses optimistic locks and you are in a locking
* mode while transactional, this will effectively synchronize the locks.
* This is pretty heady. It is unlikely you need to use this method.
*
* @return this
*
* @task load
*/
public function reload() {
if (!$this->getID()) {
throw new Exception("Unable to reload object that hasn't been loaded!");
}
$use_locks = $this->getConfigOption(self::CONFIG_OPTIMISTIC_LOCKS);
if (!$use_locks) {
$result = $this->loadOneWhere(
'%C = %d',
$this->getIDKeyForUse(),
$this->getID());
} else {
$result = $this->loadOneWhere(
'%C = %d AND %C = %d',
$this->getIDKeyForUse(),
$this->getID(),
'version',
$this->getVersion());
}
if (!$result) {
throw new AphrontQueryObjectMissingException($use_locks);
}
return $this;
}
/**
* Initialize this object's properties from a dictionary. Generally, you
* load single objects with loadOneWhere(), but sometimes it may be more
* convenient to pull data from elsewhere directly (e.g., a complicated
* join via queryData()) and then load from an array representation.
*
* @param dict Dictionary of properties, which should be equivalent to
* selecting a row from the table or calling getProperties().
* @return this
*
* @task load
*/
public function loadFromArray(array $row) {
$map = array();
foreach ($row as $k => $v) {
$map[$k] = $v;
}
$this->willReadData($map);
foreach ($map as $prop => $value) {
$this->$prop = $value;
}
$this->didReadData();
return $this;
}
/**
* Initialize a list of objects from a list of dictionaries. Usually you
* load lists of objects with loadAllWhere(), but sometimes that isn't
* flexible enough. One case is if you need to do joins to select the right
* objects:
*
* function loadAllWithOwner($owner) {
* $data = $this->queryData(
* 'SELECT d.*
* FROM owner o
* JOIN owner_has_dog od ON o.id = od.ownerID
* JOIN dog d ON od.dogID = d.id
* WHERE o.id = %d',
* $owner);
* return $this->loadAllFromArray($data);
* }
*
* This is a lot messier than loadAllWhere(), but more flexible.
*
* @param list List of property dictionaries.
* @return dict List of constructed objects, keyed on ID.
*
* @task load
*/
public function loadAllFromArray(array $rows) {
$result = array();
$id_key = $this->getIDKey();
foreach ($rows as $row) {
$obj = clone $this;
if ($id_key) {
$result[$row[$id_key]] = $obj->loadFromArray($row);
} else {
$result[] = $obj->loadFromArray($row);
}
}
return $result;
}
/* -( Examining Objects )-------------------------------------------------- */
/**
* Retrieve the unique, numerical ID identifying this object. This value
* will be null if the object hasn't been persisted.
*
* @return int Unique numerical ID.
*
* @task info
*/
public function getID() {
$id_key = $this->getIDKeyForUse();
return $this->$id_key;
}
/**
* Retrieve a list of all object properties. Note that some may be
* "transient", which means they should not be persisted to the database.
* Transient properties can be identified by calling
* getTransientProperties().
*
* @return dict Dictionary of normalized (lowercase) to canonical (original
* case) property names.
*
* @task info
*/
protected function getProperties() {
static $properties = null;
if (!isset($properties)) {
$class = new ReflectionClass(get_class($this));
$properties = array();
foreach ($class->getProperties() as $p) {
$properties[strtolower($p->getName())] = $p->getName();
}
$id_key = $this->getIDKey();
if ($id_key) {
if (!isset($properties[strtolower($id_key)])) {
$properties[strtolower($id_key)] = $id_key;
}
}
if ($this->getConfigOption(self::CONFIG_OPTIMISTIC_LOCKS)) {
$properties['version'] = 'version';
}
if ($this->getConfigOption(self::CONFIG_TIMESTAMPS)) {
$properties['datecreated'] = 'dateCreated';
$properties['datemodified'] = 'dateModified';
}
- if (!$this->isGUIDPrimaryID() &&
- $this->getConfigOption(self::CONFIG_AUX_GUID)) {
- $properties['guid'] = 'guid';
+ if (!$this->isPHIDPrimaryID() &&
+ $this->getConfigOption(self::CONFIG_AUX_PHID)) {
+ $properties['phid'] = 'phid';
}
}
return $properties;
}
/**
* Check if a property exists on this object.
*
* @return string|null Canonical property name, or null if the property
* does not exist.
*
* @task info
*/
protected function checkProperty($property) {
static $properties = null;
if (!isset($properties)) {
$properties = $this->getProperties();
}
return idx($properties, strtolower($property));
}
/**
* Get or build the database connection for this object.
*
* @return LiskDatabaseConnection Lisk connection object.
*
* @task info
*/
protected function getConnection($mode) {
if ($mode != 'r' && $mode != 'w') {
throw new Exception("Unknown mode '{$mode}', should be 'r' or 'w'.");
}
// TODO: We don't do anything with the read/write mode right now, but
// should.
if (!isset($this->__connection)) {
$this->__connection = $this->establishConnection($mode);
}
return $this->__connection;
}
/**
* Convert this object into a property dictionary. This dictionary can be
* restored into an object by using loadFromArray() (unless you're using
* legacy features with CONFIG_CONVERT_CAMELCASE, but in that case you should
* just go ahead and die in a fire).
*
* @return dict Dictionary of object properties.
*
* @task info
*/
protected function getPropertyValues() {
$map = array();
foreach ($this->getProperties() as $p) {
// We may receive a warning here for properties we've implicitly added
// through configuration; squelch it.
$map[$p] = @$this->$p;
}
return $map;
}
/**
* Convert this object into a property dictionary containing only properties
* which will be persisted to the database.
*
* @return dict Dictionary of persistent object properties.
*
* @task info
*/
protected function getPersistentPropertyValues() {
$map = $this->getPropertyValues();
foreach ($this->getTransientProperties() as $p) {
unset($map[$p]);
}
return $map;
}
/* -( Writing Objects )---------------------------------------------------- */
/**
* Persist this object to the database. In most cases, this is the only
* method you need to call to do writes. If the object has not yet been
* inserted this will do an insert; if it has, it will do an update.
*
* @return this
*
* @task save
*/
public function save() {
if ($this->shouldInsertWhenSaved()) {
return $this->insert();
} else {
return $this->update();
}
}
/**
* Save this object, forcing the query to use REPLACE regardless of object
* state.
*
* @return this
*
* @task save
*/
public function replace() {
return $this->insertRecordIntoDatabase('REPLACE');
}
/**
* Save this object, forcing the query to use INSERT regardless of object
* state.
*
* @return this
*
* @task save
*/
public function insert() {
return $this->insertRecordIntoDatabase('INSERT');
}
/**
* Save this object, forcing the query to use UPDATE regardless of object
* state.
*
* @return this
*
* @task save
*/
public function update() {
$use_locks = $this->getConfigOption(self::CONFIG_OPTIMISTIC_LOCKS);
$this->willSaveObject();
$data = $this->getPersistentPropertyValues();
$this->willWriteData($data);
$map = array();
foreach ($data as $k => $v) {
if ($use_locks && $k == 'version') {
continue;
}
$map[$k] = $v;
}
$conn = $this->getConnection('w');
foreach ($map as $key => $value) {
$map[$key] = qsprintf($conn, '%C = %ns', $key, $value);
}
$map = implode(', ', $map);
if ($use_locks) {
$conn->query(
'UPDATE %T SET %Q, version = version + 1 WHERE %C = %d AND %C = %d',
$this->getTableName(),
$map,
$this->getIDKeyForUse(),
$this->getID(),
'version',
$this->getVersion());
} else {
$conn->query(
'UPDATE %T SET %Q WHERE %C = %d',
$this->getTableName(),
$map,
$this->getIDKeyForUse(),
$this->getID());
}
if ($conn->getAffectedRows() !== 1) {
throw new AphrontQueryObjectMissingException($use_locks);
}
if ($use_locks) {
$this->setVersion($this->getVersion() + 1);
}
$this->didWriteData();
return $this;
}
/**
* Delete this object, permanently.
*
* @return this
*
* @task save
*/
public function delete() {
$this->willDelete();
$conn = $this->getConnection('w');
$conn->query(
'DELETE FROM %T WHERE %C = %d',
$this->getTableName(),
$this->getIDKeyForUse(),
$this->getID());
$this->didDelete();
return $this;
}
/**
* Internal implementation of INSERT and REPLACE.
*
* @param const Either "INSERT" or "REPLACE", to force the desired mode.
*
* @task save
*/
protected function insertRecordIntoDatabase($mode) {
$this->willSaveObject();
$data = $this->getPersistentPropertyValues();
$id_mechanism = $this->getConfigOption(self::CONFIG_IDS);
switch ($id_mechanism) {
// If we are using autoincrement IDs, let MySQL assign the value for the
// ID column.
case self::IDS_AUTOINCREMENT:
unset($data[$this->getIDKeyForUse()]);
break;
- case self::IDS_GUID:
+ case self::IDS_PHID:
if (empty($data[$this->getIDKeyForUse()])) {
- $guid = $this->generateGUID();
- $this->setID($guid);
- $data[$this->getIDKeyForUse()] = $guid;
+ $phid = $this->generatePHID();
+ $this->setID($phid);
+ $data[$this->getIDKeyForUse()] = $phid;
}
break;
case self::IDS_MANUAL:
break;
default:
throw new Exception('Unknown CONFIG_IDs mechanism!');
}
if ($this->getConfigOption(self::CONFIG_OPTIMISTIC_LOCKS)) {
$data['version'] = 0;
}
$this->willWriteData($data);
$columns = array_keys($data);
foreach ($columns as $k => $property) {
$columns[$k] = $property;
}
$conn = $this->getConnection('w');
$conn->query(
'%Q INTO %T (%LC) VALUES (%Ls)',
$mode,
$this->getTableName(),
$columns,
$data);
// Update the object with the initial Version value
if ($this->getConfigOption(self::CONFIG_OPTIMISTIC_LOCKS)) {
$this->setVersion(0);
}
// Only use the insert id if this table is using auto-increment ids
if ($id_mechanism === self::IDS_AUTOINCREMENT) {
$this->setID($conn->getInsertID());
}
$this->didWriteData();
return $this;
}
/**
* Method used to determine whether to insert or update when saving.
*
* @return bool true if the record should be inserted
*/
protected function shouldInsertWhenSaved() {
$key_type = $this->getConfigOption(self::CONFIG_IDS);
$use_locks = $this->getConfigOption(self::CONFIG_OPTIMISTIC_LOCKS);
if ($key_type == self::IDS_MANUAL) {
if ($use_locks) {
// If we are manually keyed and the object has a version (which means
// that it has been saved to the DB before), do an update, otherwise
// perform an insert.
if ($this->getID() && $this->getVersion() !== null) {
return false;
} else {
return true;
}
} else {
throw new Exception(
'You are not using optimistic locks, but are using manual IDs. You '.
'must override the shouldInsertWhenSaved() method to properly '.
'detect when to insert a new record.');
}
} else {
return !$this->getID();
}
}
/* -( Hooks and Callbacks )------------------------------------------------ */
/**
* Retrieve the database table name. By default, this is the class name.
*
* @return string Table name for object storage.
*
* @task hook
*/
public function getTableName() {
return get_class($this);
}
/**
- * Helper: Whether this class is configured to use GUIDs as the primary ID.
+ * Helper: Whether this class is configured to use PHIDs as the primary ID.
* @task internal
*/
- private function isGUIDPrimaryID() {
- return ($this->getConfigOption(self::CONFIG_IDS) === self::IDS_GUID);
+ private function isPHIDPrimaryID() {
+ return ($this->getConfigOption(self::CONFIG_IDS) === self::IDS_PHID);
}
/**
* Retrieve the primary key column, "id" by default. If you can not
* reasonably name your ID column "id", override this method.
*
* @return string Name of the ID column.
*
* @task hook
*/
public function getIDKey() {
return
- $this->isGUIDPrimaryID() ?
- 'guid' :
+ $this->isPHIDPrimaryID() ?
+ 'phid' :
'id';
}
protected function getIDKeyForUse() {
$id_key = $this->getIDKey();
if (!$id_key) {
throw new Exception(
"This DAO does not have a single-part primary key. The method you ".
"called requires a single-part primary key.");
}
return $id_key;
}
/**
- * Generate a new GUID, used by CONFIG_AUX_GUID and IDS_GUID.
+ * Generate a new PHID, used by CONFIG_AUX_PHID and IDS_PHID.
*
- * @return guid Unique, newly allocated GUID.
+ * @return phid Unique, newly allocated PHID.
*
* @task hook
*/
- protected function generateGUID() {
+ protected function generatePHID() {
throw new Exception(
- "To use CONFIG_AUX_GUID or IDS_GUID, you need to overload ".
- "generateGUID() to perform GUID generation.");
+ "To use CONFIG_AUX_PHID or IDS_PHID, you need to overload ".
+ "generatePHID() to perform PHID generation.");
}
/**
* If your object has properties which you don't want to be persisted to the
* database, you can override this method and specify them.
*
* @return list List of properties which should NOT be persisted.
* Property names should be in normalized (lowercase) form.
* By default, all properties are persistent.
*
* @task hook
*/
protected function getTransientProperties() {
return array();
}
/**
* Hook to apply serialization or validation to data before it is written to
* the database. See also willReadData().
*
* @task hook
*/
protected function willWriteData(array &$data) {
$this->applyLiskDataSerialization($data, false);
}
/**
* Hook to perform actions after data has been written to the database.
*
* @task hook
*/
protected function didWriteData() {}
/**
* Hook to make internal object state changes prior to INSERT, REPLACE or
* UPDATE.
*
* @task hook
*/
protected function willSaveObject() {
$use_timestamps = $this->getConfigOption(self::CONFIG_TIMESTAMPS);
if ($use_timestamps) {
if (!$this->getDateCreated()) {
$this->setDateCreated(time());
}
$this->setDateModified(time());
}
- if (($this->isGUIDPrimaryID() && !$this->getID())) {
- // If GUIDs are the primary ID, the subclass could have overridden the
+ if (($this->isPHIDPrimaryID() && !$this->getID())) {
+ // If PHIDs are the primary ID, the subclass could have overridden the
// name of the ID column.
- $this->setID($this->generateGUID());
- } else if ($this->getConfigOption(self::CONFIG_AUX_GUID) &&
- !$this->getGUID()) {
- // The subclass could still want GUIDs.
- $this->setGUID($this->generateGUID());
+ $this->setID($this->generatePHID());
+ } else if ($this->getConfigOption(self::CONFIG_AUX_PHID) &&
+ !$this->getPHID()) {
+ // The subclass could still want PHIDs.
+ $this->setPHID($this->generatePHID());
}
}
/**
* Hook to apply serialization or validation to data as it is read from the
* database. See also willWriteData().
*
* @task hook
*/
protected function willReadData(array &$data) {
$this->applyLiskDataSerialization($data, $deserialize = true);
}
/**
* Hook to perform an action on data after it is read from the database.
*
* @task hook
*/
protected function didReadData() {}
/**
* Hook to perform an action before the deletion of an object.
*
* @task hook
*/
protected function willDelete() {}
/**
* Hook to perform an action after the deletion of an object.
*
* @task hook
*/
protected function didDelete() {}
/* -( Utilities )---------------------------------------------------------- */
/**
* Applies configured serialization to a dictionary of values.
*
* @task util
*/
protected function applyLiskDataSerialization(array &$data, $deserialize) {
$serialization = $this->getConfigOption(self::CONFIG_SERIALIZATION);
if ($serialization) {
foreach (array_intersect_key($serialization, $data) as $col => $format) {
switch ($format) {
case self::SERIALIZATION_NONE:
break;
case self::SERIALIZATION_PHP:
if ($deserialize) {
$data[$col] = unserialize($data[$col]);
} else {
$data[$col] = serialize($data[$col]);
}
break;
case self::SERIALIZATION_JSON:
if ($deserialize) {
$data[$col] = json_decode($data[$col], true);
} else {
$data[$col] = json_encode($data[$col]);
}
break;
default:
throw new Exception("Unknown serialization format '{$format}'.");
}
}
}
}
/**
* Black magic. Builds implied get*() and set*() for all properties.
*
* @param string Method name.
* @param list Argument vector.
* @return mixed get*() methods return the property value. set*() methods
* return $this.
* @task util
*/
public function __call($method, $args) {
if (!strncmp($method, 'get', 3)) {
$property = substr($method, 3);
if (!($property = $this->checkProperty($property))) {
throw new Exception("Bad getter call: {$method}");
}
if (count($args) !== 0) {
throw new Exception("Getter call should have zero args: {$method}");
}
return @$this->$property;
}
if (!strncmp($method, 'set', 3)) {
$property = substr($method, 3);
$property = $this->checkProperty($property);
if (!$property) {
throw new Exception("Bad setter call: {$method}");
}
if (count($args) !== 1) {
throw new Exception("Setter should have exactly one arg: {$method}");
}
if ($property == 'ID') {
$property = $this->getIDKeyForUse();
}
$this->$property = $args[0];
return $this;
}
throw new Exception("Unable to resolve method: {$method}.");
}
}
diff --git a/src/view/form/control/base/AphrontFormControl.php b/src/view/form/control/base/AphrontFormControl.php
index 9fa982b131..1f078710f8 100755
--- a/src/view/form/control/base/AphrontFormControl.php
+++ b/src/view/form/control/base/AphrontFormControl.php
@@ -1,127 +1,137 @@
<?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.
*/
abstract class AphrontFormControl extends AphrontView {
private $label;
private $caption;
private $error;
private $name;
private $value;
+ private $disabled;
public function setLabel($label) {
$this->label = $label;
return $this;
}
public function getLabel() {
return $this->label;
}
public function setCaption($caption) {
$this->caption = $caption;
return $this;
}
public function getCaption() {
return $this->caption;
}
public function setError($error) {
$this->error = $error;
return $this;
}
public function getError() {
return $this->error;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setValue($value) {
$this->value = $value;
return $this;
}
public function getValue() {
return $this->value;
}
+ public function setDisabled($disabled) {
+ $this->disabled = $disabled;
+ return $this;
+ }
+
+ public function getDisabled() {
+ return $this->disabled;
+ }
+
abstract protected function renderInput();
abstract protected function getCustomControlClass();
final public function render() {
$custom_class = $this->getCustomControlClass();
if (strlen($this->getLabel())) {
$label =
'<label>'.
phutil_escape_html($this->getLabel()).
':'.
'</label>';
} else {
$label = null;
$custom_class .= ' aphront-form-control-nolabel';
}
$input =
'<div class="aphront-form-input">'.
$this->renderInput().
'</div>';
if (strlen($this->getError())) {
$error = $this->getError();
if ($error === true) {
$error = '*';
} else {
$error = "\xC2\xAB ".$error;
}
$error =
'<div class="aphront-form-error">'.
phutil_escape_html($error).
'</div>';
} else {
$error = null;
}
if (strlen($this->getCaption())) {
$caption =
'<div class="aphront-form-caption">'.
phutil_escape_html($this->getCaption()).
'</div>';
} else {
$caption = null;
}
return
'<div class="aphront-form-control '.$custom_class.'">'.
$error.
$label.
$input.
$caption.
'<div style="clear: both;"></div>'.
'</div>';
}
}
diff --git a/src/view/form/control/select/AphrontFormSelectControl.php b/src/view/form/control/select/AphrontFormSelectControl.php
index a15eda8f49..d2ffaad74d 100755
--- a/src/view/form/control/select/AphrontFormSelectControl.php
+++ b/src/view/form/control/select/AphrontFormSelectControl.php
@@ -1,56 +1,57 @@
<?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 AphrontFormSelectControl extends AphrontFormControl {
protected function getCustomControlClass() {
return 'aphront-form-control-select';
}
private $options;
public function setOptions(array $options) {
$this->options = $options;
return $this;
}
public function getOptions() {
return $this->options;
}
protected function renderInput() {
$options = array();
foreach ($this->getOptions() as $value => $label) {
$options[] = phutil_render_tag(
'option',
array(
'selected' => ($value == $this->getValue()) ? 'selected' : null,
'value' => $value,
),
phutil_escape_html($label));
}
return phutil_render_tag(
'select',
array(
- 'name' => $this->getName(),
+ 'name' => $this->getName(),
+ 'disabled' => $this->getDisabled() ? 'disabled' : null,
),
implode("\n", $options));
}
}
diff --git a/src/view/form/control/submit/AphrontFormSubmitControl.php b/src/view/form/control/submit/AphrontFormSubmitControl.php
index 16dd7b9a5e..7742a20ae0 100755
--- a/src/view/form/control/submit/AphrontFormSubmitControl.php
+++ b/src/view/form/control/submit/AphrontFormSubmitControl.php
@@ -1,48 +1,49 @@
<?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 AphrontFormSubmitControl extends AphrontFormControl {
protected $cancelButton;
public function addCancelButton($href, $label = 'Cancel') {
$this->cancelButton = phutil_render_tag(
'a',
array(
'href' => $href,
'class' => 'button grey',
),
phutil_escape_html($label));
return $this;
}
protected function getCustomControlClass() {
return 'aphront-form-control-submit';
}
protected function renderInput() {
return phutil_render_tag(
'button',
array(
- 'name' => '__submit__',
+ 'name' => '__submit__',
+ 'disabled' => $this->getDisabled() ? 'disabled' : null,
),
phutil_escape_html($this->getValue())).
$this->cancelButton;
}
}
diff --git a/src/view/form/control/text/AphrontFormTextControl.php b/src/view/form/control/text/AphrontFormTextControl.php
index f62fd6f463..f8265188af 100755
--- a/src/view/form/control/text/AphrontFormTextControl.php
+++ b/src/view/form/control/text/AphrontFormTextControl.php
@@ -1,35 +1,36 @@
<?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 AphrontFormTextControl extends AphrontFormControl {
protected function getCustomControlClass() {
return 'aphront-form-control-text';
}
protected function renderInput() {
return phutil_render_tag(
'input',
array(
- 'type' => 'text',
- 'name' => $this->getName(),
- 'value' => $this->getValue(),
+ 'type' => 'text',
+ 'name' => $this->getName(),
+ 'value' => $this->getValue(),
+ 'disabled' => $this->getDisabled() ? 'disabled' : null,
));
}
}
diff --git a/src/view/form/control/textarea/AphrontFormTextAreaControl.php b/src/view/form/control/textarea/AphrontFormTextAreaControl.php
index f52cbeb9d6..2668b825b1 100755
--- a/src/view/form/control/textarea/AphrontFormTextAreaControl.php
+++ b/src/view/form/control/textarea/AphrontFormTextAreaControl.php
@@ -1,34 +1,35 @@
<?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 AphrontFormTextAreaControl extends AphrontFormControl {
protected function getCustomControlClass() {
return 'aphront-form-control-textarea';
}
protected function renderInput() {
return phutil_render_tag(
'textarea',
array(
- 'name' => $this->getName(),
+ 'name' => $this->getName(),
+ 'disabled' => $this->getDisabled() ? 'disabled' : null,
),
phutil_escape_html($this->getValue()));
}
}
diff --git a/webroot/rsrc/css/base.css b/webroot/rsrc/css/base.css
index f305a544c2..4e49a892d3 100644
--- a/webroot/rsrc/css/base.css
+++ b/webroot/rsrc/css/base.css
@@ -1,534 +1,536 @@
html {
overflow-y: scroll;
}
body, div, dl, dt, dd, ul, ol, li,
h1, h2, h3, h4, h5, h6,
pre, form, fieldset,
p, blockquote, th, td, button {
margin: 0;
padding: 0;
outline: 0;
border: 0;
}
html {
padding-bottom: 16em;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset, img {
border: 0;
}
address, caption, cite, code, dfn, th, var {
font-style: normal;
font-weight: normal;
}
ol, ul {
list-style: none;
}
caption, th {
text-align: left;
}
td, th {
vertical-align: top;
}
h1, h2, h3, h4, h5, h6 {
font-size: 100%;
font-weight: bold;
}
body {
font: 13px/1.231 'lucida grande', tahoma, verdana, arial, sans-serif;
background: #ACACAC;
direction: ltr;
text-align: left;
unicode-bidi: embed;
*font-size: small;
}
select, input, button, textarea, button {
font: 99% 'lucida grande', tahoma, verdana, arial, clean, sans-serif;
}
table {
font-size: inherit;
font: 100%;
}
h1 {
font-size: 16px;
}
h2 {
font-size: 14px;
}
a {
-moz-outline-style: none;
text-decoration: none;
cursor: pointer;
}
a:visited {
color: #3b5998;
}
a:link {
color: #3b5998;
}
a:hover {
text-decoration: underline;
}
img {
display: block;
}
/******************************************************************************/
/* Buttons */
/******************************************************************************/
button,
a.button,
a.button:visited,
input.inputsubmit {
background: #5e77aa url('/rsrc/image/sprite.png') 0 0 repeat-x;
border: 1px solid #29447e;
border-bottom-color: #1a356e;
color: #fff;
cursor: pointer;
font-weight: bold;
text-align: center;
white-space: nowrap;
display: inline-block;
font-size: 13px;
overflow: visible;
padding: 2px 8px 3px 8px;
line-height: 18px;
vertical-align: baseline;
width: auto;
box-shadow: 0px 1px 0px rgba(0,0,0,.12);
-moz-box-shadow: 0px 1px 0px rgba(0,0,0,.12);
-webkit-box-shadow: 0px 1px 0px rgba(0,0,0,.12);
}
button {
*padding: 2px 4px 1px 8px;
_padding-right: 6px;
}
a.button,
a.button:visited {
*padding: 3px 8px 4px;
}
/* Buttons with images (full size only) */
button.icon,
a.icon,
a.icon:visited {
padding-left: 0;
position: relative;
text-indent: 29px;
}
/* Fix for IE7 within table cells ? */
td button {
*width: 100%;
*padding-right: 8px;
}
button:active,
a.button:active {
background-color: #4f6aa3;
background-position: 0 -100px;
border-bottom-color: #29447e;
}
button.green,
a.green,
a.green:visited {
background-color: #6da952;
background-position: 0 -50px;
border: 1px solid #3b6e22;
border-bottom-color: #2c5a15;
}
button.green:active,
a.green:active {
background-color: #5e9d43;
background-position: 0 -150px;
border-bottom-color: #3b6e22;
}
button.grey,
input.inputaux,
a.grey,
a.grey:visited,
a.button.disabled,
button.disabled {
background-color: #e4e5e5;
background-position: 0 -250px;
border: 1px solid #999;
border-bottom-color: #888;
color: #333;
box-shadow: 0px 1px 0px rgba(0,0,0,.07);
-moz-box-shadow: 0px 1px 0px rgba(0,0,0,.07);
-webkit-box-shadow: 0px 1px 0px rgba(0,0,0,.07);
}
a.disabled,
button.disabled {
filter:alpha(opacity=50);
-moz-opacity:0.5;
-khtml-opacity: 0.5;
opacity: 0.5;
}
button.grey:active,
a.grey:active,
button.grey_active {
background-color: #dddddd;
background-position: 0 -200px;
border-bottom-color: #999;
}
button:active::-moz-focus-inner,
button:focus::-moz-focus-inner {
border-color: #405071;
}
button.green:active::-moz-focus-inner,
button.green:focus::-moz-focus-inner {
border-color: #4c713b;
}
button.grey:active::-moz-focus-inner,
button.grey:focus::-moz-focus-inner {
border-color: #666;
}
a.button:hover {
text-decoration: none;
}
button.small,
a.small,
a.small:visited {
padding: 2px 7px;
height: auto;
font-size: 11px;
line-height: 16px;
}
/******************************************************************************/
/* Aphront */
/******************************************************************************/
.aphront-standard-page {
background: #ffffff;
border-bottom: 1px solid #888888;
font-size: 14px;
-webkit-box-shadow: 0 0 6px #000;
-mox-box-shadow: 0 0 6px #000;
box-shadow: 0 0 6px #000;
}
.aphront-standard-header {
background: #003366;
color: white;
padding: 1em 1em 0.5em 1em;
overflow: hidden;
position: relative;
}
.aphront-standard-header a {
color: white;
}
.aphront-standard-header .aphront-head-tabs {
padding: 0 1em;
font-size: 13px;
font-weight: bold;
}
.aphront-standard-header .aphront-head-tabs a {
border-bottom: 3px solid transparent;
padding: 0.5em 0.75em;
position: relative;
bottom: 2px;
}
.aphront-standard-header .aphront-head-tabs a.aphront-selected-tab {
border-bottom-color: #cccccc;
}
.aphront-standard-header .aphront-head-appname {
padding: 0 1em;
text-transform: uppercase;
}
.aphront-directory-list {
margin: 1em 3% 8em;
}
.aphront-directory-category h1 {
border-bottom: 1px solid #cccccc;
margin-bottom: .5em;
padding-bottom: .1em;
}
.aphront-directory-list h2 {
font-size: 14px;
font-weight: bold;
padding: 0;
margin: 0;
}
.aphront-directory-list p {
color: #444444;
font-size: 12px;
padding: .05em .5em .5em;
}
.aphront-directory-category {
padding: 10px;
width: 300px;
float: left;
}
.aphront-directory-group {
padding: 0 .5em 3em;
}
.aphront-panel-view {
background: #f3f3f3;
border: 1px solid #c0c0c0;
border-width: 1px 0 0;
padding: 1em 2em;
margin: 1em 2em;
}
.aphront-panel-view h1 {
font-size: 14px;
font-weight: bold;
padding: 2px 0 8px;
}
.aphront-panel-view a.create-button {
float: right;
}
.aphront-panel-width-form {
width: 720px;
margin-right: auto;
margin-left: auto;
}
.aphront-table-view {
width: 100%;
border-collapse: collapse;
background: #fdfdfd;
border: 1px solid #003366;
}
.aphront-table-view tr.alt {
background: #efefef;
}
.aphront-table-view th {
font-size: 12px;
font-weight: bold;
padding: 4px 8px;
background: #003366;
color: white;
white-space: nowrap;
}
.aphront-table-view td.header {
padding: 4px 8px;
background: #3b5998;
color: white;
white-space: nowrap;
text-align: right;
}
.aphront-table-view td {
vertical-align: top;
padding: 4px 8px;
font-size: 11px;
white-space: nowrap;
}
.aphront-table-view td.action {
padding-top: 1px;
padding-bottom: 1px;
}
.aphront-table-view td.larger {
font-size: 14px;
}
.aphront-table-view td.pri {
font-weight: bold;
}
.aphront-table-view td.wide {
white-space: normal;
width: 100%;
}
.aphront-table-view td.right {
text-align: right;
}
.aphront-table-view td.mono {
font-family: "Monaco", monospace;
font-size: 10px;
}
.aphront-table-view tr.no-data td {
padding: 1em;
text-align: center;
color: #888888;
font-style: italic;
}
/******************************************************************************/
/* forms */
/******************************************************************************/
.aphront-form-view {
background: #e7e7e7;
border: 1px solid #c4c4c4;
padding: 1em;
}
.aphront-form-view label {
padding-top: 4px;
width: 14%;
float: left;
text-align: right;
font-weight: bold;
font-size: 13px;
color: #666666;
}
.aphront-form-input {
margin-left: 15%;
margin-right: 25%;
width: 60%;
}
.aphront-form-error {
width: 23%;
float: right;
color: #aa0000;
font-weight: bold;
padding-top: 4px;
}
.aphront-form-input input,
.aphront-form-input textarea {
font-size: 12px;
width: 100%;
}
.aphront-form-input textarea {
height: 12em;
}
.aphront-form-control {
padding: 4px;
}
.aphront-form-control-submit button,
.aphront-form-control-submit a.button {
float: right;
margin: 1em 0 0em 2%;
}
.aphront-form-view .aphront-form-caption {
font-size: 11px;
color: #444444;
text-align: right;
clear: both;
margin-right: 25%;
margin-left: 15%;
+ margin-top: 3px;
+ margin-bottom: 6px;
}
.aphront-error-view {
width: 720px;
margin: 1em auto;
border: 1px solid #aa0000;
padding: 1em;
background: #f9b9bc;
}
/******************************************************************************/
/* dialog */
/******************************************************************************/
.aphront-dialog-view {
width: 480px;
padding: 8px;
background: #666;
margin: auto;
}
.aphront-dialog-head {
background: #003366;
border: none;
font-size: 15px;
padding: 5px 12px 6px;
color: #ffffff;
}
.aphront-dialog-body {
background: #ffffff;
padding: 16px 12px;
border: none;
overflow: hidden;
}
.aphront-dialog-tail {
border: none;
background: #ededed;
padding: 0.5em;
text-align: right;
}
.aphront-dialog-tail button,
.aphront-dialog-tail a.button {
float: right;
margin-left: .5em;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 19:35 (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128023
Default Alt Text
(92 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment