mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-04 20:52:43 +01:00
(stable) Promote 2019 Week 32
This commit is contained in:
commit
26f9ba4684
36 changed files with 929 additions and 155 deletions
|
@ -9,10 +9,10 @@ return array(
|
|||
'names' => array(
|
||||
'conpherence.pkg.css' => '3c8a0668',
|
||||
'conpherence.pkg.js' => '020aebcf',
|
||||
'core.pkg.css' => 'af983028',
|
||||
'core.pkg.css' => '5a4a5010',
|
||||
'core.pkg.js' => '73a06a9f',
|
||||
'differential.pkg.css' => '8d8360fb',
|
||||
'differential.pkg.js' => '67e02996',
|
||||
'differential.pkg.js' => '0b037a4f',
|
||||
'diffusion.pkg.css' => '42c75c37',
|
||||
'diffusion.pkg.js' => 'a98c0bf7',
|
||||
'maniphest.pkg.css' => '35995d6d',
|
||||
|
@ -24,7 +24,7 @@ return array(
|
|||
'rsrc/audio/basic/ting.mp3' => 'a6b6540e',
|
||||
'rsrc/css/aphront/aphront-bars.css' => '4a327b4a',
|
||||
'rsrc/css/aphront/dark-console.css' => '7f06cda2',
|
||||
'rsrc/css/aphront/dialog-view.css' => 'b70c70df',
|
||||
'rsrc/css/aphront/dialog-view.css' => '874f5c06',
|
||||
'rsrc/css/aphront/list-filter-view.css' => 'feb64255',
|
||||
'rsrc/css/aphront/multi-column.css' => 'fbc00ba3',
|
||||
'rsrc/css/aphront/notification.css' => '30240bd2',
|
||||
|
@ -484,7 +484,7 @@ return array(
|
|||
'rsrc/js/core/behavior-line-linker.js' => 'e15c8b1f',
|
||||
'rsrc/js/core/behavior-linked-container.js' => '74446546',
|
||||
'rsrc/js/core/behavior-more.js' => '506aa3f4',
|
||||
'rsrc/js/core/behavior-object-selector.js' => 'a4af0b4a',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '98ef467f',
|
||||
'rsrc/js/core/behavior-oncopy.js' => 'ff7b3f22',
|
||||
'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949',
|
||||
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f',
|
||||
|
@ -530,7 +530,7 @@ return array(
|
|||
'almanac-css' => '2e050f4f',
|
||||
'aphront-bars' => '4a327b4a',
|
||||
'aphront-dark-console-css' => '7f06cda2',
|
||||
'aphront-dialog-view-css' => 'b70c70df',
|
||||
'aphront-dialog-view-css' => '874f5c06',
|
||||
'aphront-list-filter-view-css' => 'feb64255',
|
||||
'aphront-multi-column-view-css' => 'fbc00ba3',
|
||||
'aphront-panel-view-css' => '46923d46',
|
||||
|
@ -645,7 +645,7 @@ return array(
|
|||
'javelin-behavior-phabricator-line-linker' => 'e15c8b1f',
|
||||
'javelin-behavior-phabricator-nav' => 'f166c949',
|
||||
'javelin-behavior-phabricator-notification-example' => '29819b75',
|
||||
'javelin-behavior-phabricator-object-selector' => 'a4af0b4a',
|
||||
'javelin-behavior-phabricator-object-selector' => '98ef467f',
|
||||
'javelin-behavior-phabricator-oncopy' => 'ff7b3f22',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '2f80333f',
|
||||
'javelin-behavior-phabricator-reveal-content' => 'b105a3a6',
|
||||
|
@ -1730,6 +1730,12 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-router',
|
||||
),
|
||||
'98ef467f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'9aae2b66' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1790,12 +1796,6 @@ return array(
|
|||
'phui-button-css',
|
||||
'phui-button-simple-css',
|
||||
),
|
||||
'a4af0b4a' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'a5257c4e' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -3878,7 +3878,21 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersPathsSearchEngineAttachment' => 'applications/owners/engineextension/PhabricatorOwnersPathsSearchEngineAttachment.php',
|
||||
'PhabricatorOwnersSchemaSpec' => 'applications/owners/storage/PhabricatorOwnersSchemaSpec.php',
|
||||
'PhabricatorOwnersSearchField' => 'applications/owners/searchfield/PhabricatorOwnersSearchField.php',
|
||||
'PhabricatorPDFCatalogObject' => 'applications/phortune/pdf/PhabricatorPDFCatalogObject.php',
|
||||
'PhabricatorPDFContentsObject' => 'applications/phortune/pdf/PhabricatorPDFContentsObject.php',
|
||||
'PhabricatorPDFDocumentEngine' => 'applications/files/document/PhabricatorPDFDocumentEngine.php',
|
||||
'PhabricatorPDFFontObject' => 'applications/phortune/pdf/PhabricatorPDFFontObject.php',
|
||||
'PhabricatorPDFFragment' => 'applications/phortune/pdf/PhabricatorPDFFragment.php',
|
||||
'PhabricatorPDFFragmentOffset' => 'applications/phortune/pdf/PhabricatorPDFFragmentOffset.php',
|
||||
'PhabricatorPDFGenerator' => 'applications/phortune/pdf/PhabricatorPDFGenerator.php',
|
||||
'PhabricatorPDFHeadFragment' => 'applications/phortune/pdf/PhabricatorPDFHeadFragment.php',
|
||||
'PhabricatorPDFInfoObject' => 'applications/phortune/pdf/PhabricatorPDFInfoObject.php',
|
||||
'PhabricatorPDFIterator' => 'applications/phortune/pdf/PhabricatorPDFIterator.php',
|
||||
'PhabricatorPDFObject' => 'applications/phortune/pdf/PhabricatorPDFObject.php',
|
||||
'PhabricatorPDFPageObject' => 'applications/phortune/pdf/PhabricatorPDFPageObject.php',
|
||||
'PhabricatorPDFPagesObject' => 'applications/phortune/pdf/PhabricatorPDFPagesObject.php',
|
||||
'PhabricatorPDFResourcesObject' => 'applications/phortune/pdf/PhabricatorPDFResourcesObject.php',
|
||||
'PhabricatorPDFTailFragment' => 'applications/phortune/pdf/PhabricatorPDFTailFragment.php',
|
||||
'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php',
|
||||
'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php',
|
||||
'PhabricatorPHIDConstants' => 'applications/phid/PhabricatorPHIDConstants.php',
|
||||
|
@ -10101,7 +10115,24 @@ phutil_register_library_map(array(
|
|||
'PhabricatorOwnersPathsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorOwnersSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorOwnersSearchField' => 'PhabricatorSearchTokenizerField',
|
||||
'PhabricatorPDFCatalogObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFContentsObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFDocumentEngine' => 'PhabricatorDocumentEngine',
|
||||
'PhabricatorPDFFontObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFFragment' => 'Phobject',
|
||||
'PhabricatorPDFFragmentOffset' => 'Phobject',
|
||||
'PhabricatorPDFGenerator' => 'Phobject',
|
||||
'PhabricatorPDFHeadFragment' => 'PhabricatorPDFFragment',
|
||||
'PhabricatorPDFInfoObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFIterator' => array(
|
||||
'Phobject',
|
||||
'Iterator',
|
||||
),
|
||||
'PhabricatorPDFObject' => 'PhabricatorPDFFragment',
|
||||
'PhabricatorPDFPageObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFPagesObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFResourcesObject' => 'PhabricatorPDFObject',
|
||||
'PhabricatorPDFTailFragment' => 'PhabricatorPDFFragment',
|
||||
'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPHID' => 'Phobject',
|
||||
'PhabricatorPHIDConstants' => 'Phobject',
|
||||
|
|
|
@ -194,6 +194,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
|||
$control = id(new PHUIFormNumberControl())
|
||||
->setName($name)
|
||||
->setDisableAutocomplete(true)
|
||||
->setAutofocus(true)
|
||||
->setValue($value)
|
||||
->setError($error);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,24 @@
|
|||
final class ConduitEpochParameterType
|
||||
extends ConduitParameterType {
|
||||
|
||||
private $allowNull;
|
||||
|
||||
public function setAllowNull($allow_null) {
|
||||
$this->allowNull = $allow_null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowNull() {
|
||||
return $this->allowNull;
|
||||
}
|
||||
|
||||
protected function getParameterValue(array $request, $key, $strict) {
|
||||
$value = parent::getParameterValue($request, $key, $strict);
|
||||
|
||||
if ($this->allowNull && ($value === null)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$value = $this->parseIntValue($request, $key, $value, $strict);
|
||||
|
||||
if ($value <= 0) {
|
||||
|
|
|
@ -17,32 +17,31 @@ final class DiffusionRepositoryEditDeleteController
|
|||
->setRepository($repository)
|
||||
->getPanelURI();
|
||||
|
||||
$dialog = new AphrontDialogView();
|
||||
$text_1 = pht(
|
||||
'If you really want to delete the repository, run this command from '.
|
||||
'the command line:');
|
||||
$command = csprintf(
|
||||
'phabricator/ $ ./bin/remove destroy %R',
|
||||
$repository->getMonogram());
|
||||
$text_2 = pht(
|
||||
'Repositories touch many objects and as such deletes are '.
|
||||
'prohibitively expensive to run from the web UI.');
|
||||
$body = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phabricator-remarkup',
|
||||
),
|
||||
array(
|
||||
phutil_tag('p', array(), $text_1),
|
||||
phutil_tag('p', array(),
|
||||
phutil_tag('tt', array(), $command)),
|
||||
phutil_tag('p', array(), $text_2),
|
||||
));
|
||||
$doc_uri = PhabricatorEnv::getDoclink(
|
||||
'Permanently Destroying Data');
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Really want to delete the repository?'))
|
||||
->appendChild($body)
|
||||
->addCancelButton($panel_uri, pht('Okay'));
|
||||
->setTitle(pht('Delete Repository'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'To permanently destroy this repository, run this command from '.
|
||||
'the command line:'))
|
||||
->appendCommand(
|
||||
csprintf(
|
||||
'phabricator/ $ ./bin/remove destroy %R',
|
||||
$repository->getMonogram()))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Repositories can not be permanently destroyed from the web '.
|
||||
'interface. See %s in the documentation for more information.',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $doc_uri,
|
||||
'target' => '_blank',
|
||||
),
|
||||
pht('Permanently Destroying Data'))))
|
||||
->addCancelButton($panel_uri, pht('Close'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -155,8 +155,6 @@ final class DiffusionRepositoryBasicsManagementPanel
|
|||
->setName(pht('Delete Repository'))
|
||||
->setHref($delete_uri)
|
||||
->setIcon('fa-times')
|
||||
->setColor(PhabricatorActionView::RED)
|
||||
->setDisabled(true)
|
||||
->setWorkflow(true));
|
||||
|
||||
return $this->newCurtainView()
|
||||
|
|
|
@ -47,7 +47,7 @@ final class DiffusionPatternSearchView extends DiffusionView {
|
|||
$offset = $match[1];
|
||||
if ($cursor != $offset) {
|
||||
$output[] = array(
|
||||
'text' => substr($string, $cursor, $offset),
|
||||
'text' => substr($string, $cursor, ($offset - $cursor)),
|
||||
'highlight' => false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -451,15 +451,20 @@ You can choose the default priority for newly created tasks with
|
|||
EOTEXT
|
||||
));
|
||||
|
||||
$fields_description = $this->deformat(pht(<<<EOTEXT
|
||||
List of custom fields for Maniphest tasks.
|
||||
|
||||
For details on adding custom fields to Maniphest, see [[ %s | %s ]] in the
|
||||
documentation.
|
||||
EOTEXT
|
||||
,
|
||||
PhabricatorEnv::getDoclink('Configuring Custom Fields'),
|
||||
pht('Configuring Custom Fields')));
|
||||
|
||||
return array(
|
||||
$this->newOption('maniphest.custom-field-definitions', 'wild', array())
|
||||
->setSummary(pht('Custom Maniphest fields.'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'Array of custom fields for Maniphest tasks. For details on '.
|
||||
'adding custom fields to Maniphest, see "Configuring Custom '.
|
||||
'Fields" in the documentation.'))
|
||||
->setDescription($fields_description)
|
||||
->addExample($fields_json, pht('Valid setting')),
|
||||
$this->newOption('maniphest.fields', $custom_field_type, $default_fields)
|
||||
->setCustomData(id(new ManiphestTask())->getCustomFieldBaseClass())
|
||||
|
|
|
@ -17,58 +17,35 @@ final class PhabricatorPeopleDeleteController
|
|||
|
||||
$manage_uri = $this->getApplicationURI("manage/{$id}/");
|
||||
|
||||
if ($user->getPHID() == $viewer->getPHID()) {
|
||||
return $this->buildDeleteSelfResponse($manage_uri);
|
||||
}
|
||||
|
||||
$str1 = pht(
|
||||
'Be careful when deleting users! This will permanently and '.
|
||||
'irreversibly destroy this user account.');
|
||||
|
||||
$str2 = pht(
|
||||
'If this user interacted with anything, it is generally better to '.
|
||||
'disable them, not delete them. If you delete them, it will no longer '.
|
||||
'be possible to (for example) search for objects they created, and you '.
|
||||
'will lose other information about their history. Disabling them '.
|
||||
'instead will prevent them from logging in, but will not destroy any of '.
|
||||
'their data.');
|
||||
|
||||
$str3 = pht(
|
||||
'It is generally safe to delete newly created users (and test users and '.
|
||||
'so on), but less safe to delete established users. If possible, '.
|
||||
'disable them instead.');
|
||||
|
||||
$str4 = pht('To permanently destroy this user, run this command:');
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendRemarkupInstructions(
|
||||
csprintf(
|
||||
" phabricator/ $ ./bin/remove destroy %R\n",
|
||||
'@'.$user->getUsername()));
|
||||
$doc_uri = PhabricatorEnv::getDoclink(
|
||||
'Permanently Destroying Data');
|
||||
|
||||
return $this->newDialog()
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->setTitle(pht('Permanently Delete User'))
|
||||
->setShortTitle(pht('Delete User'))
|
||||
->appendParagraph($str1)
|
||||
->appendParagraph($str2)
|
||||
->appendParagraph($str3)
|
||||
->appendParagraph($str4)
|
||||
->appendChild($form->buildLayoutView())
|
||||
->setTitle(pht('Delete User'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'To permanently destroy this user, run this command from the '.
|
||||
'command line:'))
|
||||
->appendCommand(
|
||||
csprintf(
|
||||
'phabricator/ $ ./bin/remove destroy %R',
|
||||
$user->getMonogram()))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Unless you have a very good reason to delete this user, consider '.
|
||||
'disabling them instead.'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Users can not be permanently destroyed from the web interface. '.
|
||||
'See %s in the documentation for more information.',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $doc_uri,
|
||||
'target' => '_blank',
|
||||
),
|
||||
pht('Permanently Destroying Data'))))
|
||||
->addCancelButton($manage_uri, pht('Close'));
|
||||
}
|
||||
|
||||
private function buildDeleteSelfResponse($cancel_uri) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('You Shall Journey No Farther'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'As you stare into the gaping maw of the abyss, something '.
|
||||
'holds you back.'))
|
||||
->appendParagraph(pht('You can not delete your own account.'))
|
||||
->addCancelButton($cancel_uri, pht('Turn Back'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -34,20 +34,6 @@ final class PhabricatorPeopleProfileBadgesController
|
|||
$user,
|
||||
PhabricatorPeopleProfileMenuEngine::ITEM_BADGES);
|
||||
|
||||
// Best option?
|
||||
$badges = id(new PhabricatorBadgesQuery())
|
||||
->setViewer($viewer)
|
||||
->withStatuses(array(
|
||||
PhabricatorBadgesBadge::STATUS_ACTIVE,
|
||||
))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->setLimit(1)
|
||||
->execute();
|
||||
|
||||
$button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-plus')
|
||||
|
@ -55,17 +41,16 @@ final class PhabricatorPeopleProfileBadgesController
|
|||
->setWorkflow(true)
|
||||
->setHref('/badges/award/'.$user->getID().'/');
|
||||
|
||||
if ($badges) {
|
||||
$header->addActionLink($button);
|
||||
}
|
||||
$header->addActionLink($button);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->addClass('project-view-home')
|
||||
->addClass('project-view-people-home')
|
||||
->setFooter(array(
|
||||
$this->buildBadgesView($user)
|
||||
));
|
||||
->setFooter(
|
||||
array(
|
||||
$badges,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
|
|
|
@ -188,8 +188,8 @@ final class PhortuneCartEditor
|
|||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
$phids = array();
|
||||
|
||||
// Reload the cart to pull merchant and account information, in case we
|
||||
// just created the object.
|
||||
// Reload the cart to pull account information, in case we just created the
|
||||
// object.
|
||||
$cart = id(new PhortuneCartQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withPHIDs(array($object->getPHID()))
|
||||
|
@ -199,10 +199,6 @@ final class PhortuneCartEditor
|
|||
$phids[] = $account_member;
|
||||
}
|
||||
|
||||
foreach ($cart->getMerchant()->getMemberPHIDs() as $merchant_member) {
|
||||
$phids[] = $merchant_member;
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFCatalogObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $pagesObject;
|
||||
|
||||
public function setPagesObject(PhabricatorPDFPagesObject $pages_object) {
|
||||
$this->pagesObject = $this->newChildObject($pages_object);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPagesObject() {
|
||||
return $this->pagesObject;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Catalog');
|
||||
|
||||
$pages_object = $this->getPagesObject();
|
||||
if ($pages_object) {
|
||||
$this->writeLine('/Pages %d 0 R', $pages_object->getObjectIndex());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFContentsObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $rawContent;
|
||||
|
||||
public function setRawContent($raw_content) {
|
||||
$this->rawContent = $raw_content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRawContent() {
|
||||
return $this->rawContent;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$data = $this->getRawContent();
|
||||
|
||||
$stream_length = $this->newStream($data);
|
||||
|
||||
$this->writeLine('/Filter /FlateDecode /Length %d', $stream_length);
|
||||
}
|
||||
|
||||
}
|
14
src/applications/phortune/pdf/PhabricatorPDFFontObject.php
Normal file
14
src/applications/phortune/pdf/PhabricatorPDFFontObject.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFFontObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Font');
|
||||
|
||||
$this->writeLine('/BaseFont /Helvetica-Bold');
|
||||
$this->writeLine('/Subtype /Type1');
|
||||
$this->writeLine('/Encoding /WinAnsiEncoding');
|
||||
}
|
||||
|
||||
}
|
38
src/applications/phortune/pdf/PhabricatorPDFFragment.php
Normal file
38
src/applications/phortune/pdf/PhabricatorPDFFragment.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPDFFragment
|
||||
extends Phobject {
|
||||
|
||||
private $rope;
|
||||
|
||||
public function getAsBytes() {
|
||||
$this->rope = new PhutilRope();
|
||||
|
||||
$this->writeFragment();
|
||||
|
||||
$rope = $this->rope;
|
||||
$this->rope = null;
|
||||
|
||||
return $rope->getAsString();
|
||||
}
|
||||
|
||||
public function hasRefTableEntry() {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract protected function writeFragment();
|
||||
|
||||
final protected function writeLine($pattern) {
|
||||
$pattern = $pattern."\n";
|
||||
|
||||
$argv = func_get_args();
|
||||
$argv[0] = $pattern;
|
||||
|
||||
$line = call_user_func_array('sprintf', $argv);
|
||||
|
||||
$this->rope->append($line);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFFragmentOffset
|
||||
extends Phobject {
|
||||
|
||||
private $fragment;
|
||||
private $offset;
|
||||
|
||||
public function setFragment(PhabricatorPDFFragment $fragment) {
|
||||
$this->fragment = $fragment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFragment() {
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
public function setOffset($offset) {
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOffset() {
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
}
|
59
src/applications/phortune/pdf/PhabricatorPDFGenerator.php
Normal file
59
src/applications/phortune/pdf/PhabricatorPDFGenerator.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFGenerator
|
||||
extends Phobject {
|
||||
|
||||
private $objects = array();
|
||||
private $hasIterator = false;
|
||||
|
||||
private $infoObject;
|
||||
private $catalogObject;
|
||||
|
||||
public function addObject(PhabricatorPDFObject $object) {
|
||||
if ($this->hasIterator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This generator has already emitted an iterator. You can not '.
|
||||
'modify the PDF document after you begin writing it.'));
|
||||
}
|
||||
|
||||
$this->objects[] = $object;
|
||||
$index = count($this->objects);
|
||||
|
||||
$object->setGenerator($this, $index);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObjects() {
|
||||
return $this->objects;
|
||||
}
|
||||
|
||||
public function newIterator() {
|
||||
$this->hasIterator = true;
|
||||
return id(new PhabricatorPDFIterator())
|
||||
->setGenerator($this);
|
||||
}
|
||||
|
||||
public function setInfoObject(PhabricatorPDFInfoObject $info_object) {
|
||||
$this->addObject($info_object);
|
||||
$this->infoObject = $info_object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInfoObject() {
|
||||
return $this->infoObject;
|
||||
}
|
||||
|
||||
public function setCatalogObject(
|
||||
PhabricatorPDFCatalogObject $catalog_object) {
|
||||
$this->addObject($catalog_object);
|
||||
$this->catalogObject = $catalog_object;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCatalogObject() {
|
||||
return $this->catalogObject;
|
||||
}
|
||||
|
||||
}
|
10
src/applications/phortune/pdf/PhabricatorPDFHeadFragment.php
Normal file
10
src/applications/phortune/pdf/PhabricatorPDFHeadFragment.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFHeadFragment
|
||||
extends PhabricatorPDFFragment {
|
||||
|
||||
protected function writeFragment() {
|
||||
$this->writeLine('%s', '%PDF-1.3');
|
||||
}
|
||||
|
||||
}
|
11
src/applications/phortune/pdf/PhabricatorPDFInfoObject.php
Normal file
11
src/applications/phortune/pdf/PhabricatorPDFInfoObject.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFInfoObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
final protected function writeObject() {
|
||||
$this->writeLine('/Producer (Phabricator 20190801)');
|
||||
$this->writeLine('/CreationDate (D:%s)', date('YmdHis'));
|
||||
}
|
||||
|
||||
}
|
103
src/applications/phortune/pdf/PhabricatorPDFIterator.php
Normal file
103
src/applications/phortune/pdf/PhabricatorPDFIterator.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFIterator
|
||||
extends Phobject
|
||||
implements Iterator {
|
||||
|
||||
private $generator;
|
||||
private $hasRewound;
|
||||
|
||||
private $fragments;
|
||||
private $fragmentKey;
|
||||
private $fragmentBytes;
|
||||
private $fragmentOffsets = array();
|
||||
private $byteLength;
|
||||
|
||||
public function setGenerator(PhabricatorPDFGenerator $generator) {
|
||||
if ($this->generator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This iterator already has a generator. You can not modify the '.
|
||||
'generator for a given iterator.'));
|
||||
}
|
||||
|
||||
$this->generator = $generator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGenerator() {
|
||||
if (!$this->generator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This PDF iterator has no associated PDF generator.'));
|
||||
}
|
||||
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
public function getFragmentOffsets() {
|
||||
return $this->fragmentOffsets;
|
||||
}
|
||||
|
||||
public function current() {
|
||||
return $this->fragmentBytes;
|
||||
}
|
||||
|
||||
public function key() {
|
||||
return $this->framgentKey;
|
||||
}
|
||||
|
||||
public function next() {
|
||||
$this->fragmentKey++;
|
||||
|
||||
if (!$this->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fragment = $this->fragments[$this->fragmentKey];
|
||||
|
||||
$this->fragmentOffsets[] = id(new PhabricatorPDFFragmentOffset())
|
||||
->setFragment($fragment)
|
||||
->setOffset($this->byteLength);
|
||||
|
||||
$bytes = $fragment->getAsBytes();
|
||||
|
||||
$this->fragmentBytes = $bytes;
|
||||
$this->byteLength += strlen($bytes);
|
||||
}
|
||||
|
||||
public function rewind() {
|
||||
if ($this->hasRewound) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'PDF iterators may not be rewound. Create a new iterator to emit '.
|
||||
'another PDF.'));
|
||||
}
|
||||
|
||||
$generator = $this->getGenerator();
|
||||
$objects = $generator->getObjects();
|
||||
|
||||
$this->fragments = array();
|
||||
$this->fragments[] = new PhabricatorPDFHeadFragment();
|
||||
|
||||
foreach ($objects as $object) {
|
||||
$this->fragments[] = $object;
|
||||
}
|
||||
|
||||
$this->fragments[] = id(new PhabricatorPDFTailFragment())
|
||||
->setIterator($this);
|
||||
|
||||
$this->hasRewound = true;
|
||||
|
||||
$this->fragmentKey = -1;
|
||||
$this->byteLength = 0;
|
||||
|
||||
$this->next();
|
||||
}
|
||||
|
||||
public function valid() {
|
||||
return isset($this->fragments[$this->fragmentKey]);
|
||||
}
|
||||
|
||||
}
|
95
src/applications/phortune/pdf/PhabricatorPDFObject.php
Normal file
95
src/applications/phortune/pdf/PhabricatorPDFObject.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPDFObject
|
||||
extends PhabricatorPDFFragment {
|
||||
|
||||
private $generator;
|
||||
private $objectIndex;
|
||||
private $children = array();
|
||||
private $streams = array();
|
||||
|
||||
final public function hasRefTableEntry() {
|
||||
return true;
|
||||
}
|
||||
|
||||
final protected function writeFragment() {
|
||||
$this->writeLine('%d 0 obj', $this->getObjectIndex());
|
||||
$this->writeLine('<<');
|
||||
$this->writeObject();
|
||||
$this->writeLine('>>');
|
||||
|
||||
$streams = $this->streams;
|
||||
$this->streams = array();
|
||||
foreach ($streams as $stream) {
|
||||
$this->writeLine('stream');
|
||||
$this->writeLine('%s', $stream);
|
||||
$this->writeLine('endstream');
|
||||
}
|
||||
|
||||
$this->writeLine('endobj');
|
||||
}
|
||||
|
||||
final public function setGenerator(
|
||||
PhabricatorPDFGenerator $generator,
|
||||
$index) {
|
||||
|
||||
if ($this->getGenerator()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This PDF object is already registered with a PDF generator. You '.
|
||||
'can not register an object with more than one generator.'));
|
||||
}
|
||||
|
||||
$this->generator = $generator;
|
||||
$this->objectIndex = $index;
|
||||
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$generator->addObject($child);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getGenerator() {
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
final public function getObjectIndex() {
|
||||
if (!$this->objectIndex) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Trying to get index for object ("%s") which has not been '.
|
||||
'registered with a generator.',
|
||||
get_class($this)));
|
||||
}
|
||||
|
||||
return $this->objectIndex;
|
||||
}
|
||||
|
||||
final protected function newChildObject(PhabricatorPDFObject $object) {
|
||||
if ($this->generator) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Trying to add a new PDF Object child after already registering '.
|
||||
'the object with a generator.'));
|
||||
}
|
||||
|
||||
$this->children[] = $object;
|
||||
return $object;
|
||||
}
|
||||
|
||||
private function getChildren() {
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
abstract protected function writeObject();
|
||||
|
||||
final protected function newStream($raw_data) {
|
||||
$stream_data = gzcompress($raw_data);
|
||||
|
||||
$this->streams[] = $stream_data;
|
||||
|
||||
return strlen($stream_data);
|
||||
}
|
||||
|
||||
}
|
48
src/applications/phortune/pdf/PhabricatorPDFPageObject.php
Normal file
48
src/applications/phortune/pdf/PhabricatorPDFPageObject.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFPageObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $pagesObject;
|
||||
private $contentsObject;
|
||||
private $resourcesObject;
|
||||
|
||||
public function setPagesObject(PhabricatorPDFPagesObject $pages) {
|
||||
$this->pagesObject = $pages;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setContentsObject(PhabricatorPDFContentsObject $contents) {
|
||||
$this->contentsObject = $this->newChildObject($contents);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setResourcesObject(PhabricatorPDFResourcesObject $resources) {
|
||||
$this->resourcesObject = $this->newChildObject($resources);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Page');
|
||||
|
||||
$pages_object = $this->pagesObject;
|
||||
$contents_object = $this->contentsObject;
|
||||
$resources_object = $this->resourcesObject;
|
||||
|
||||
if ($pages_object) {
|
||||
$pages_index = $pages_object->getObjectIndex();
|
||||
$this->writeLine('/Parent %d 0 R', $pages_index);
|
||||
}
|
||||
|
||||
if ($contents_object) {
|
||||
$contents_index = $contents_object->getObjectIndex();
|
||||
$this->writeLine('/Contents %d 0 R', $contents_index);
|
||||
}
|
||||
|
||||
if ($resources_object) {
|
||||
$resources_index = $resources_object->getObjectIndex();
|
||||
$this->writeLine('/Resources %d 0 R', $resources_index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
38
src/applications/phortune/pdf/PhabricatorPDFPagesObject.php
Normal file
38
src/applications/phortune/pdf/PhabricatorPDFPagesObject.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFPagesObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $pageObjects = array();
|
||||
|
||||
public function addPageObject(PhabricatorPDFPageObject $page) {
|
||||
$page->setPagesObject($this);
|
||||
$this->pageObjects[] = $this->newChildObject($page);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPageObjects() {
|
||||
return $this->pageObjects;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/Type /Pages');
|
||||
|
||||
$page_objects = $this->getPageObjects();
|
||||
|
||||
$this->writeLine('/Count %d', count($page_objects));
|
||||
$this->writeLine('/MediaBox [%d %d %0.2f %0.2f]', 0, 0, 595.28, 841.89);
|
||||
|
||||
if ($page_objects) {
|
||||
$kids = array();
|
||||
foreach ($page_objects as $page_object) {
|
||||
$kids[] = sprintf(
|
||||
'%d 0 R',
|
||||
$page_object->getObjectIndex());
|
||||
}
|
||||
|
||||
$this->writeLine('/Kids [%s]', implode(' ', $kids));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFResourcesObject
|
||||
extends PhabricatorPDFObject {
|
||||
|
||||
private $fontObjects = array();
|
||||
|
||||
public function addFontObject(PhabricatorPDFFontObject $font) {
|
||||
$this->fontObjects[] = $this->newChildObject($font);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFontObjects() {
|
||||
return $this->fontObjects;
|
||||
}
|
||||
|
||||
protected function writeObject() {
|
||||
$this->writeLine('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
|
||||
|
||||
$fonts = $this->getFontObjects();
|
||||
foreach ($fonts as $font) {
|
||||
$this->writeLine('/Font <<');
|
||||
$this->writeLine('/F%d %d 0 R', 1, $font->getObjectIndex());
|
||||
$this->writeLine('>>');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
72
src/applications/phortune/pdf/PhabricatorPDFTailFragment.php
Normal file
72
src/applications/phortune/pdf/PhabricatorPDFTailFragment.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPDFTailFragment
|
||||
extends PhabricatorPDFFragment {
|
||||
|
||||
private $iterator;
|
||||
|
||||
public function setIterator(PhabricatorPDFIterator $iterator) {
|
||||
$this->iterator = $iterator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIterator() {
|
||||
return $this->iterator;
|
||||
}
|
||||
|
||||
protected function writeFragment() {
|
||||
$iterator = $this->getIterator();
|
||||
$generator = $iterator->getGenerator();
|
||||
$objects = $generator->getObjects();
|
||||
|
||||
$xref_offset = null;
|
||||
|
||||
$this->writeLine('xref');
|
||||
$this->writeLine('0 %d', count($objects) + 1);
|
||||
$this->writeLine('%010d %05d f ', 0, 0xFFFF);
|
||||
|
||||
$offset_map = array();
|
||||
|
||||
$fragment_offsets = $iterator->getFragmentOffsets();
|
||||
foreach ($fragment_offsets as $fragment_offset) {
|
||||
$fragment = $fragment_offset->getFragment();
|
||||
$offset = $fragment_offset->getOffset();
|
||||
|
||||
if ($fragment === $this) {
|
||||
$xref_offset = $offset;
|
||||
}
|
||||
|
||||
if (!$fragment->hasRefTableEntry()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$offset_map[$fragment->getObjectIndex()] = $offset;
|
||||
}
|
||||
|
||||
ksort($offset_map);
|
||||
|
||||
foreach ($offset_map as $offset) {
|
||||
$this->writeLine('%010d %05d n ', $offset, 0);
|
||||
}
|
||||
|
||||
$this->writeLine('trailer');
|
||||
$this->writeLine('<<');
|
||||
$this->writeLine('/Size %d', count($objects) + 1);
|
||||
|
||||
$info_object = $generator->getInfoObject();
|
||||
if ($info_object) {
|
||||
$this->writeLine('/Info %d 0 R', $info_object->getObjectIndex());
|
||||
}
|
||||
|
||||
$catalog_object = $generator->getCatalogObject();
|
||||
if ($catalog_object) {
|
||||
$this->writeLine('/Root %d 0 R', $catalog_object->getObjectIndex());
|
||||
}
|
||||
|
||||
$this->writeLine('>>');
|
||||
$this->writeLine('startxref');
|
||||
$this->writeLine('%d', $xref_offset);
|
||||
$this->writeLine('%s', '%%EOF');
|
||||
}
|
||||
|
||||
}
|
|
@ -107,7 +107,16 @@ final class PhabricatorProjectColumnBulkMoveController
|
|||
->executeLayout();
|
||||
|
||||
$dst_columns = $layout_engine->getColumns($dst_project->getPHID());
|
||||
$dst_columns = mpull($columns, null, 'getPHID');
|
||||
$dst_columns = mpull($dst_columns, null, 'getPHID');
|
||||
|
||||
// Prevent moves to milestones or subprojects by selecting their
|
||||
// columns, since the implications aren't obvious and this doesn't
|
||||
// work the same way as normal column moves.
|
||||
foreach ($dst_columns as $key => $dst_column) {
|
||||
if ($dst_column->getProxyPHID()) {
|
||||
unset($dst_columns[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$has_column = false;
|
||||
$dst_column = null;
|
||||
|
@ -210,12 +219,40 @@ final class PhabricatorProjectColumnBulkMoveController
|
|||
->setValue($dst_project->getDisplayName()));
|
||||
}
|
||||
|
||||
$column_options = array(
|
||||
'visible' => array(),
|
||||
'hidden' => array(),
|
||||
);
|
||||
|
||||
$any_hidden = false;
|
||||
foreach ($dst_columns as $column) {
|
||||
if (!$column->isHidden()) {
|
||||
$group = 'visible';
|
||||
} else {
|
||||
$group = 'hidden';
|
||||
}
|
||||
|
||||
$phid = $column->getPHID();
|
||||
$display_name = $column->getDisplayName();
|
||||
|
||||
$column_options[$group][$phid] = $display_name;
|
||||
}
|
||||
|
||||
if ($column_options['hidden']) {
|
||||
$column_options = array(
|
||||
pht('Visible Columns') => $column_options['visible'],
|
||||
pht('Hidden Columns') => $column_options['hidden'],
|
||||
);
|
||||
} else {
|
||||
$column_options = $column_options['visible'];
|
||||
}
|
||||
|
||||
$form->appendControl(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('dstColumnPHID')
|
||||
->setLabel(pht('Move to Column'))
|
||||
->setValue($dst_column_phid)
|
||||
->setOptions(mpull($dst_columns, 'getDisplayName', 'getPHID')));
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('dstColumnPHID')
|
||||
->setLabel(pht('Move to Column'))
|
||||
->setValue($dst_column_phid)
|
||||
->setOptions($column_options));
|
||||
|
||||
$submit = pht('Move Tasks');
|
||||
|
||||
|
|
|
@ -78,14 +78,29 @@ final class PhabricatorProjectProfileController
|
|||
$project,
|
||||
PhabricatorProject::ITEM_PROFILE);
|
||||
|
||||
$stories = id(new PhabricatorFeedQuery())
|
||||
$query = id(new PhabricatorFeedQuery())
|
||||
->setViewer($viewer)
|
||||
->withFilterPHIDs(
|
||||
array(
|
||||
$project->getPHID(),
|
||||
))
|
||||
->withFilterPHIDs(array($project->getPHID()))
|
||||
->setLimit(50)
|
||||
->execute();
|
||||
->setReturnPartialResultsOnOverheat(true);
|
||||
|
||||
$stories = $query->execute();
|
||||
|
||||
$overheated_view = null;
|
||||
$is_overheated = $query->getIsOverheated();
|
||||
if ($is_overheated) {
|
||||
$overheated_message =
|
||||
PhabricatorApplicationSearchController::newOverheatedError(
|
||||
(bool)$stories);
|
||||
|
||||
$overheated_view = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
|
||||
->setTitle(pht('Query Overheated'))
|
||||
->setErrors(
|
||||
array(
|
||||
$overheated_message,
|
||||
));
|
||||
}
|
||||
|
||||
$view_all = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
|
@ -103,7 +118,11 @@ final class PhabricatorProjectProfileController
|
|||
$feed = id(new PHUIObjectBoxView())
|
||||
->setHeader($feed_header)
|
||||
->addClass('project-view-feed')
|
||||
->appendChild($feed);
|
||||
->appendChild(
|
||||
array(
|
||||
$overheated_view,
|
||||
$feed,
|
||||
));
|
||||
|
||||
require_celerity_resource('project-view-css');
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ final class PhabricatorBoardLayoutEngine extends Phobject {
|
|||
|
||||
private $viewer;
|
||||
private $boardPHIDs;
|
||||
private $objectPHIDs;
|
||||
private $objectPHIDs = array();
|
||||
private $boards;
|
||||
private $columnMap = array();
|
||||
private $objectColumnMap = array();
|
||||
|
|
|
@ -37,7 +37,8 @@ final class PhabricatorEpochEditField
|
|||
}
|
||||
|
||||
protected function newConduitParameterType() {
|
||||
return new ConduitEpochParameterType();
|
||||
return id(new ConduitEpochParameterType())
|
||||
->setAllowNull($this->getAllowNull());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -121,6 +121,10 @@ When defining custom fields using a configuration option like
|
|||
supported in text, int and remarkup fields (optional).
|
||||
- **copy**: If true, this field's value will be copied when an object is
|
||||
created using another object as a template.
|
||||
- **limit**: For control types which use a tokenizer control to let the user
|
||||
select a list of values, this limits how many values can be selected. For
|
||||
example, a "users" field with a limit of "1" will behave like the "Owner"
|
||||
field in Maniphest and only allow selection of a single user.
|
||||
|
||||
The `strings` value supports different strings per control type. They are:
|
||||
|
||||
|
|
92
src/docs/user/field/permanently_destroying_data.diviner
Normal file
92
src/docs/user/field/permanently_destroying_data.diviner
Normal file
|
@ -0,0 +1,92 @@
|
|||
@title Permanently Destroying Data
|
||||
@group fieldmanual
|
||||
|
||||
How to permanently destroy data and manage leaked secrets.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Phabricator intentionally makes it difficult to permanently destroy data, but
|
||||
provides a command-line tool for destroying objects if you're certain that
|
||||
you want to destroy something.
|
||||
|
||||
**Disable vs Destroy**: Most kinds of objects can be disabled, deactivated,
|
||||
closed, or archived. These operations place them in inactive states and
|
||||
preserve their transaction history.
|
||||
|
||||
(NOTE) Disabling (rather than destroying) objects is strongly recommended
|
||||
unless you have a very good reason to want to permanently destroy an object.
|
||||
|
||||
|
||||
Destroying Data
|
||||
===============
|
||||
|
||||
To permanently destroy an object, run this command from the command line:
|
||||
|
||||
```
|
||||
phabricator/ $ ./bin/remove destroy <object>
|
||||
```
|
||||
|
||||
The `<object>` may be an object monogram or PHID. For instance, you can use
|
||||
`@alice` to destroy a particular user, or `T123` to destroy a particular task.
|
||||
|
||||
(IMPORTANT) This operation is permanent and can not be undone.
|
||||
|
||||
|
||||
CLI Access Required
|
||||
===================
|
||||
|
||||
In almost all cases, Phabricator requires operational access from the CLI to
|
||||
permanently destroy data. One major reason for this requirement is that it
|
||||
limits the reach of an attacker who compromises a privileged account.
|
||||
|
||||
The web UI is generally append-only and actions generally leave an audit
|
||||
trail, usually in the transaction log. Thus, an attacker who compromises an
|
||||
account but only gains access to the web UI usually can not do much permanent
|
||||
damage and usually can not hide their actions or cover their tracks.
|
||||
|
||||
Another reason that destroying data is hard is simply that it's permanent and
|
||||
can not be undone, so there's no way to recover from mistakes.
|
||||
|
||||
|
||||
Leaked Secrets
|
||||
==============
|
||||
|
||||
Sometimes you may want to destroy an object because it has leaked a secret,
|
||||
like an API key or another credential. For example, an engineer might
|
||||
accidentally send a change for review which includes a sensitive private key.
|
||||
|
||||
No Phabricator command can rewind time, and once data is written to Phabricator
|
||||
the cat is often out of the bag: it has often been transmitted to external
|
||||
systems which Phabricator can not interact with via email, webhooks, API calls,
|
||||
repository mirroring, CDN caching, and so on. You can try to clean up the mess,
|
||||
but you're generally already too late.
|
||||
|
||||
The `bin/remove destroy` command will make a reasonable attempt to completely
|
||||
destroy objects, but this is just an attempt. It can not unsend email or uncall
|
||||
the API, and no command can rewind time and undo a leak.
|
||||
|
||||
**Revoking Credentials**: If Phabricator credentials were accidentally
|
||||
disclosed, you can revoke them so they no longer function. See
|
||||
@{article:Revoking Credentials} for more information.
|
||||
|
||||
|
||||
Preventing Leaks
|
||||
================
|
||||
|
||||
Because time can not be rewound, it is best to prevent sensitive data from
|
||||
leaking in the first place. Phabricator supports some technical measures that
|
||||
can make it more difficult to accidentally disclose secrets:
|
||||
|
||||
**Differential Diff Herald Rules**: You can write "Differential Diff" rules
|
||||
in Herald that reject diffs before they are written to disk by using the
|
||||
"Block diff with message" action.
|
||||
|
||||
These rules can reject diffs based on affected file names or file content.
|
||||
This is a coarse tool, but rejecting diffs which contain strings like
|
||||
`BEGIN RSA PRIVATE KEY` may make it more difficult to accidentally disclose
|
||||
certain secrets.
|
||||
|
||||
**Commit Content Herald Rules**: For hosted repositories, you can write
|
||||
"Commit Hook: Commit Content" rules in Herald which reject pushes that contain
|
||||
commit which match certain rules (like file name or file content rules).
|
|
@ -169,8 +169,8 @@ start working normally.
|
|||
Basics: Delete Repository
|
||||
=========================
|
||||
|
||||
Repositories can not be deleted from the web UI, so this option is always
|
||||
disabled. Clicking it gives you information about how to delete a repository.
|
||||
Repositories can not be deleted from the web UI, so this option only gives you
|
||||
information about how to delete a repository.
|
||||
|
||||
Repositories can only be deleted from the command line, with `bin/remove`:
|
||||
|
||||
|
@ -178,9 +178,8 @@ Repositories can only be deleted from the command line, with `bin/remove`:
|
|||
$ ./bin/remove destroy <repository>
|
||||
```
|
||||
|
||||
WARNING: This command will issue you a dire warning about the severity of the
|
||||
action you are taking. Heed this warning. You are **strongly discouraged** from
|
||||
destroying repositories. Instead, deactivate them.
|
||||
This command will permanently destroy the repository. For more information
|
||||
about destroying things, see @{article:Permanently Destroying Data}.
|
||||
|
||||
|
||||
Policies
|
||||
|
|
|
@ -226,20 +226,14 @@ final class PhabricatorStandardCustomFieldDate
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public function shouldAppearInConduitTransactions() {
|
||||
// TODO: Dates are complicated and we don't yet support handling them from
|
||||
// Conduit.
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function newConduitSearchParameterType() {
|
||||
// TODO: Build a new "pair<epoch|null, epoch|null>" type or similar.
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function newConduitEditParameterType() {
|
||||
return new ConduitEpochParameterType();
|
||||
return id(new ConduitEpochParameterType())
|
||||
->setAllowNull(!$this->getRequired());
|
||||
}
|
||||
|
||||
protected function newExportFieldType() {
|
||||
|
|
|
@ -161,15 +161,36 @@ final class AphrontDialogView
|
|||
}
|
||||
|
||||
public function appendParagraph($paragraph) {
|
||||
return $this->appendChild(
|
||||
phutil_tag(
|
||||
'p',
|
||||
array(
|
||||
'class' => 'aphront-dialog-view-paragraph',
|
||||
),
|
||||
$paragraph));
|
||||
return $this->appendParagraphTag($paragraph);
|
||||
}
|
||||
|
||||
public function appendCommand($command) {
|
||||
$command_tag = phutil_tag('tt', array(), $command);
|
||||
return $this->appendParagraphTag(
|
||||
$command_tag,
|
||||
'aphront-dialog-view-command');
|
||||
}
|
||||
|
||||
private function appendParagraphTag($content, $classes = null) {
|
||||
if ($classes) {
|
||||
$classes = (array)$classes;
|
||||
} else {
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
array_unshift($classes, 'aphront-dialog-view-paragraph');
|
||||
|
||||
$paragraph_tag = phutil_tag(
|
||||
'p',
|
||||
array(
|
||||
'class' => implode(' ', $classes),
|
||||
),
|
||||
$content);
|
||||
|
||||
return $this->appendChild($paragraph_tag);
|
||||
}
|
||||
|
||||
|
||||
public function appendList(array $items) {
|
||||
$listitems = array();
|
||||
foreach ($items as $item) {
|
||||
|
|
|
@ -158,6 +158,11 @@
|
|||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.aphront-dialog-view-command {
|
||||
padding: 8px 16px;
|
||||
background: {$greybackground};
|
||||
}
|
||||
|
||||
.device-desktop .aphront-dialog-flush .phui-oi-list-view {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
|
@ -132,7 +132,7 @@ JX.behavior('phabricator-object-selector', function(config) {
|
|||
|
||||
var select_object_link = JX.$N(
|
||||
'a',
|
||||
{href: h.uri, sigil: 'object-attacher'},
|
||||
{href: '#', sigil: 'object-attacher'},
|
||||
h.name);
|
||||
|
||||
var select_object_button = JX.$N(
|
||||
|
|
Loading…
Reference in a new issue