mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Implement policies in Phragment
Summary: This implements support for enforcing and setting policies in Phragment. Test Plan: Set policies and ensured they were enforced successfully. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley CC: Korvin, epriestley, aran Maniphest Tasks: T4205 Differential Revision: https://secure.phabricator.com/D7751
This commit is contained in:
parent
7f45824984
commit
86ec4d6021
23 changed files with 350 additions and 47 deletions
15
resources/sql/patches/20131211.phragmentedges.sql
Normal file
15
resources/sql/patches/20131211.phragmentedges.sql
Normal file
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE {$NAMESPACE}_phragment.edge (
|
||||
src VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
type VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
dst VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
seq INT UNSIGNED NOT NULL,
|
||||
dataID INT UNSIGNED,
|
||||
PRIMARY KEY (src, type, dst),
|
||||
KEY (src, type, dateCreated, seq)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_phragment.edgedata (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
data LONGTEXT NOT NULL COLLATE utf8_bin
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
|
@ -2180,6 +2180,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
|
||||
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
|
||||
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
|
||||
'PhragmentCapabilityCanCreate' => 'applications/phragment/capability/PhragmentCapabilityCanCreate.php',
|
||||
'PhragmentController' => 'applications/phragment/controller/PhragmentController.php',
|
||||
'PhragmentCreateController' => 'applications/phragment/controller/PhragmentCreateController.php',
|
||||
'PhragmentDAO' => 'applications/phragment/storage/PhragmentDAO.php',
|
||||
|
@ -2193,6 +2194,7 @@ phutil_register_library_map(array(
|
|||
'PhragmentPHIDTypeSnapshot' => 'applications/phragment/phid/PhragmentPHIDTypeSnapshot.php',
|
||||
'PhragmentPatchController' => 'applications/phragment/controller/PhragmentPatchController.php',
|
||||
'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php',
|
||||
'PhragmentPolicyController' => 'applications/phragment/controller/PhragmentPolicyController.php',
|
||||
'PhragmentRevertController' => 'applications/phragment/controller/PhragmentRevertController.php',
|
||||
'PhragmentSnapshot' => 'applications/phragment/storage/PhragmentSnapshot.php',
|
||||
'PhragmentSnapshotChild' => 'applications/phragment/storage/PhragmentSnapshotChild.php',
|
||||
|
@ -4790,6 +4792,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhragmentBrowseController' => 'PhragmentController',
|
||||
'PhragmentCapabilityCanCreate' => 'PhabricatorPolicyCapability',
|
||||
'PhragmentController' => 'PhabricatorController',
|
||||
'PhragmentCreateController' => 'PhragmentController',
|
||||
'PhragmentDAO' => 'PhabricatorLiskDAO',
|
||||
|
@ -4811,6 +4814,7 @@ phutil_register_library_map(array(
|
|||
'PhragmentPHIDTypeSnapshot' => 'PhabricatorPHIDType',
|
||||
'PhragmentPatchController' => 'PhragmentController',
|
||||
'PhragmentPatchUtil' => 'Phobject',
|
||||
'PhragmentPolicyController' => 'PhragmentController',
|
||||
'PhragmentRevertController' => 'PhragmentController',
|
||||
'PhragmentSnapshot' =>
|
||||
array(
|
||||
|
|
|
@ -61,6 +61,7 @@ final class PhabricatorApplicationFiles extends PhabricatorApplication {
|
|||
'xform/(?P<transform>[^/]+)/(?P<phid>[^/]+)/(?P<key>[^/]+)/'
|
||||
=> 'PhabricatorFileTransformController',
|
||||
'uploaddialog/' => 'PhabricatorFileUploadDialogController',
|
||||
'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -66,12 +66,12 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
|
|||
if ($is_viewable && !$force_download) {
|
||||
$response->setMimeType($file->getViewableMimeType());
|
||||
} else {
|
||||
if (!$request->isHTTPPost()) {
|
||||
// NOTE: Require POST to download files. We'd rather go full-bore and
|
||||
// do a real CSRF check, but can't currently authenticate users on the
|
||||
// file domain. This should blunt any attacks based on iframes, script
|
||||
// tags, applet tags, etc., at least. Send the user to the "info" page
|
||||
// if they're using some other method.
|
||||
if (!$request->isHTTPPost() && !$alt_domain) {
|
||||
// NOTE: Require POST to download files from the primary domain. We'd
|
||||
// rather go full-bore and do a real CSRF check, but can't currently
|
||||
// authenticate users on the file domain. This should blunt any
|
||||
// attacks based on iframes, script tags, applet tags, etc., at least.
|
||||
// Send the user to the "info" page if they're using some other method.
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI(PhabricatorEnv::getProductionURI($file->getBestURI()));
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ final class PhabricatorApplicationPhragment extends PhabricatorApplication {
|
|||
'browse/(?P<dblob>.*)' => 'PhragmentBrowseController',
|
||||
'create/(?P<dblob>.*)' => 'PhragmentCreateController',
|
||||
'update/(?P<dblob>.*)' => 'PhragmentUpdateController',
|
||||
'policy/(?P<dblob>.*)' => 'PhragmentPolicyController',
|
||||
'history/(?P<dblob>.*)' => 'PhragmentHistoryController',
|
||||
'zip/(?P<dblob>.*)' => 'PhragmentZIPController',
|
||||
'zip@(?P<snapshot>[^/]+)/(?P<dblob>.*)' => 'PhragmentZIPController',
|
||||
|
@ -56,5 +57,12 @@ final class PhabricatorApplicationPhragment extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
PhragmentCapabilityCanCreate::CAPABILITY => array(
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
final class PhragmentCapabilityCanCreate
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'phragment.create';
|
||||
|
||||
public function getCapabilityKey() {
|
||||
return self::CAPABILITY;
|
||||
}
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Create Fragments');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to create fragments.');
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,10 @@ final class PhragmentBrowseController extends PhragmentController {
|
|||
|
||||
private $dblob;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->dblob = idx($data, "dblob", "");
|
||||
}
|
||||
|
@ -24,11 +28,14 @@ final class PhragmentBrowseController extends PhragmentController {
|
|||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Fragment'))
|
||||
->setHref($this->getApplicationURI('/create/'.$path))
|
||||
->setIcon('create'));
|
||||
if ($this->hasApplicationCapability(
|
||||
PhragmentCapabilityCanCreate::CAPABILITY)) {
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Fragment'))
|
||||
->setHref($this->getApplicationURI('/create/'.$path))
|
||||
->setIcon('create'));
|
||||
}
|
||||
|
||||
$current_box = $this->createCurrentFragmentView($current, false);
|
||||
|
||||
|
@ -79,6 +86,7 @@ final class PhragmentBrowseController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$current_box,
|
||||
$list),
|
||||
array(
|
||||
|
|
|
@ -84,7 +84,7 @@ abstract class PhragmentController extends PhabricatorController {
|
|||
->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
|
||||
->executeOne();
|
||||
if ($file !== null) {
|
||||
$file_uri = $file->getBestURI();
|
||||
$file_uri = $file->getDownloadURI();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,13 @@ abstract class PhragmentController extends PhabricatorController {
|
|||
->setPolicyObject($fragment)
|
||||
->setUser($viewer);
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$fragment,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$zip_uri = $this->getApplicationURI("zip/".$fragment->getPath());
|
||||
|
||||
$actions = id(new PhabricatorActionListView())
|
||||
->setUser($viewer)
|
||||
->setObject($fragment)
|
||||
|
@ -100,30 +107,39 @@ abstract class PhragmentController extends PhabricatorController {
|
|||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Download Fragment'))
|
||||
->setHref($file_uri)
|
||||
->setDisabled($file === null)
|
||||
->setHref($this->isCorrectlyConfigured() ? $file_uri : null)
|
||||
->setDisabled($file === null || !$this->isCorrectlyConfigured())
|
||||
->setIcon('download'));
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Download Contents as ZIP'))
|
||||
->setHref($this->getApplicationURI("zip/".$fragment->getPath()))
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setHref($this->isCorrectlyConfigured() ? $zip_uri : null)
|
||||
->setDisabled(!$this->isCorrectlyConfigured())
|
||||
->setIcon('zip'));
|
||||
if (!$fragment->isDirectory()) {
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Update Fragment'))
|
||||
->setHref($this->getApplicationURI("update/".$fragment->getPath()))
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setIcon('edit'));
|
||||
} else {
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Convert to File'))
|
||||
->setHref($this->getApplicationURI("update/".$fragment->getPath()))
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setIcon('edit'));
|
||||
}
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Set Fragment Policies'))
|
||||
->setHref($this->getApplicationURI("policy/".$fragment->getPath()))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setIcon('edit'));
|
||||
if ($is_history_view) {
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
|
@ -142,7 +158,8 @@ abstract class PhragmentController extends PhabricatorController {
|
|||
->setName(pht('Create Snapshot'))
|
||||
->setHref($this->getApplicationURI(
|
||||
"snapshot/create/".$fragment->getPath()))
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit)
|
||||
->setIcon('snapshot'));
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
|
@ -150,7 +167,7 @@ abstract class PhragmentController extends PhabricatorController {
|
|||
->setHref($this->getApplicationURI(
|
||||
"snapshot/promote/latest/".$fragment->getPath()))
|
||||
->setWorkflow(true)
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setDisabled(!$can_edit)
|
||||
->setIcon('promote'));
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
|
@ -188,4 +205,33 @@ abstract class PhragmentController extends PhabricatorController {
|
|||
->addPropertyList($properties);
|
||||
}
|
||||
|
||||
function renderConfigurationWarningIfRequired() {
|
||||
$alt = PhabricatorEnv::getEnvConfig("security.alternate-file-domain");
|
||||
if ($alt === null) {
|
||||
return id(new AphrontErrorView())
|
||||
->setTitle(pht('security.alternate-file-domain must be configured!'))
|
||||
->setSeverity(AphrontErrorView::SEVERITY_ERROR)
|
||||
->appendChild(phutil_tag('p', array(), pht(
|
||||
'Because Phragment generates files (such as ZIP archives and '.
|
||||
'patches) as they are requested, it requires that you configure '.
|
||||
'the `security.alterate-file-domain` option. This option on it\'s '.
|
||||
'own will also provide additional security when serving files '.
|
||||
'across Phabricator.')));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* We use this to disable the download links if the alternate domain is
|
||||
* not configured correctly. Although the download links will mostly work
|
||||
* for logged in users without an alternate domain, the behaviour is
|
||||
* reasonably non-consistent and will deny public users, even if policies
|
||||
* are configured otherwise (because the Files app does not support showing
|
||||
* the info page to viewers who are not logged in).
|
||||
*/
|
||||
function isCorrectlyConfigured() {
|
||||
$alt = PhabricatorEnv::getEnvConfig("security.alternate-file-domain");
|
||||
return $alt !== null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ final class PhragmentCreateController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$box),
|
||||
array(
|
||||
'title' => pht('Create Fragment'),
|
||||
|
|
|
@ -4,6 +4,10 @@ final class PhragmentHistoryController extends PhragmentController {
|
|||
|
||||
private $dblob;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->dblob = idx($data, "dblob", "");
|
||||
}
|
||||
|
@ -21,11 +25,14 @@ final class PhragmentHistoryController extends PhragmentController {
|
|||
$path = $current->getPath();
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Fragment'))
|
||||
->setHref($this->getApplicationURI('/create/'.$path))
|
||||
->setIcon('create'));
|
||||
if ($this->hasApplicationCapability(
|
||||
PhragmentCapabilityCanCreate::CAPABILITY)) {
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Fragment'))
|
||||
->setHref($this->getApplicationURI('/create/'.$path))
|
||||
->setIcon('create'));
|
||||
}
|
||||
|
||||
$current_box = $this->createCurrentFragmentView($current, true);
|
||||
|
||||
|
@ -44,6 +51,11 @@ final class PhragmentHistoryController extends PhragmentController {
|
|||
->execute();
|
||||
$files = mpull($files, null, 'getPHID');
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$current,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$first = true;
|
||||
foreach ($versions as $version) {
|
||||
$item = id(new PHUIObjectItemView());
|
||||
|
@ -58,7 +70,7 @@ final class PhragmentHistoryController extends PhragmentController {
|
|||
$item->addAttribute('Deletion');
|
||||
}
|
||||
|
||||
if (!$first) {
|
||||
if (!$first && $can_edit) {
|
||||
$item->addAction(id(new PHUIListItemView())
|
||||
->setIcon('undo')
|
||||
->setRenderNameAsTooltip(true)
|
||||
|
@ -71,13 +83,15 @@ final class PhragmentHistoryController extends PhragmentController {
|
|||
$disabled = !isset($files[$version->getFilePHID()]);
|
||||
$action = id(new PHUIListItemView())
|
||||
->setIcon('download')
|
||||
->setDisabled($disabled)
|
||||
->setDisabled($disabled || !$this->isCorrectlyConfigured())
|
||||
->setRenderNameAsTooltip(true)
|
||||
->setName(pht("Download"));
|
||||
if (!$disabled) {
|
||||
$action->setHref($files[$version->getFilePHID()]->getBestURI());
|
||||
if (!$disabled && $this->isCorrectlyConfigured()) {
|
||||
$action->setHref($files[$version->getFilePHID()]
|
||||
->getDownloadURI($version->getURI()));
|
||||
}
|
||||
$item->addAction($action);
|
||||
|
||||
$list->addItem($item);
|
||||
|
||||
$first = false;
|
||||
|
@ -86,6 +100,7 @@ final class PhragmentHistoryController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$current_box,
|
||||
$list),
|
||||
array(
|
||||
|
|
|
@ -5,6 +5,10 @@ final class PhragmentPatchController extends PhragmentController {
|
|||
private $aid;
|
||||
private $bid;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->aid = idx($data, "aid", 0);
|
||||
$this->bid = idx($data, "bid", 0);
|
||||
|
@ -61,7 +65,9 @@ final class PhragmentPatchController extends PhragmentController {
|
|||
$patch = PhragmentPatchUtil::calculatePatch($file_a, $file_b);
|
||||
|
||||
if ($patch === null) {
|
||||
throw new Exception("Unable to compute patch!");
|
||||
// There are no differences between the two files, so we output
|
||||
// an empty patch.
|
||||
$patch = '';
|
||||
}
|
||||
|
||||
$a_sequence = 'x';
|
||||
|
@ -74,6 +80,11 @@ final class PhragmentPatchController extends PhragmentController {
|
|||
$a_sequence.'.'.
|
||||
$version_b->getSequence().'.patch';
|
||||
|
||||
$return = $version_b->getURI();
|
||||
if ($request->getExists('return')) {
|
||||
$return = $request->getStr('return');
|
||||
}
|
||||
|
||||
$result = PhabricatorFile::buildFromFileDataOrHash(
|
||||
$patch,
|
||||
array(
|
||||
|
@ -81,8 +92,13 @@ final class PhragmentPatchController extends PhragmentController {
|
|||
'mime-type' => 'text/plain',
|
||||
'ttl' => time() + 60 * 60 * 24,
|
||||
));
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$result->attachToObject($viewer, $version_b->getFragmentPHID());
|
||||
unset($unguarded);
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($result->getBestURI());
|
||||
->setURI($result->getDownloadURI($return));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
final class PhragmentPolicyController extends PhragmentController {
|
||||
|
||||
private $dblob;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->dblob = idx($data, "dblob", "");
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$parents = $this->loadParentFragments($this->dblob);
|
||||
if ($parents === null) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$fragment = idx($parents, count($parents) - 1, null);
|
||||
|
||||
$error_view = null;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$errors = array();
|
||||
|
||||
$v_view_policy = $request->getStr('viewPolicy');
|
||||
$v_edit_policy = $request->getStr('editPolicy');
|
||||
$v_replace_children = $request->getBool('replacePoliciesOnChildren');
|
||||
|
||||
$fragment->setViewPolicy($v_view_policy);
|
||||
$fragment->setEditPolicy($v_edit_policy);
|
||||
|
||||
$fragment->save();
|
||||
|
||||
if ($v_replace_children) {
|
||||
// If you can edit a fragment, you can forcibly set the policies
|
||||
// on child fragments, regardless of whether you can see them or not.
|
||||
$children = id(new PhragmentFragmentQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withLeadingPath($fragment->getPath().'/')
|
||||
->execute();
|
||||
$children_phids = mpull($children, 'getPHID');
|
||||
|
||||
$fragment->openTransaction();
|
||||
foreach ($children as $child) {
|
||||
$child->setViewPolicy($v_view_policy);
|
||||
$child->setEditPolicy($v_edit_policy);
|
||||
$child->save();
|
||||
}
|
||||
$fragment->saveTransaction();
|
||||
}
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/phragment/browse/'.$fragment->getPath());
|
||||
}
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($fragment)
|
||||
->execute();
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('viewPolicy')
|
||||
->setPolicyObject($fragment)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new AphrontFormPolicyControl())
|
||||
->setName('editPolicy')
|
||||
->setPolicyObject($fragment)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicies($policies))
|
||||
->appendChild(
|
||||
id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'replacePoliciesOnChildren',
|
||||
'true',
|
||||
pht(
|
||||
'Replace policies on child fragments with '.
|
||||
'the policies above.')))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Save Fragment Policies'))
|
||||
->addCancelButton(
|
||||
$this->getApplicationURI('browse/'.$fragment->getPath())));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
|
||||
$crumbs->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName(pht('Edit Fragment Policies')));
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Edit Fragment Policies: %s', $fragment->getPath()))
|
||||
->setValidationException(null)
|
||||
->setForm($form);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$box),
|
||||
array(
|
||||
'title' => pht('Edit Fragment Policies'),
|
||||
'device' => true));
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,11 @@ final class PhragmentSnapshotCreateController extends PhragmentController {
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
$viewer,
|
||||
$fragment,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$children = id(new PhragmentFragmentQuery())
|
||||
->setViewer($viewer)
|
||||
->needLatestVersion(true)
|
||||
|
@ -161,6 +166,7 @@ final class PhragmentSnapshotCreateController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$box),
|
||||
array(
|
||||
'title' => pht('Create Fragment'),
|
||||
|
|
|
@ -14,6 +14,9 @@ final class PhragmentSnapshotDeleteController extends PhragmentController {
|
|||
|
||||
$snapshot = id(new PhragmentSnapshotQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT))
|
||||
->withIDs(array($this->id))
|
||||
->executeOne();
|
||||
if ($snapshot === null) {
|
||||
|
|
|
@ -23,6 +23,9 @@ final class PhragmentSnapshotPromoteController extends PhragmentController {
|
|||
if ($this->dblob !== null) {
|
||||
$this->targetFragment = id(new PhragmentFragmentQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT))
|
||||
->withPaths(array($this->dblob))
|
||||
->executeOne();
|
||||
if ($this->targetFragment === null) {
|
||||
|
@ -40,6 +43,9 @@ final class PhragmentSnapshotPromoteController extends PhragmentController {
|
|||
if ($this->id !== null) {
|
||||
$this->targetSnapshot = id(new PhragmentSnapshotQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT))
|
||||
->withIDs(array($this->id))
|
||||
->executeOne();
|
||||
if ($this->targetSnapshot === null) {
|
||||
|
@ -141,7 +147,13 @@ final class PhragmentSnapshotPromoteController extends PhragmentController {
|
|||
}
|
||||
$snapshot->saveTransaction();
|
||||
|
||||
return id(new AphrontRedirectResponse());
|
||||
if ($this->id === null) {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($this->targetFragment->getURI());
|
||||
} else {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($this->targetSnapshot->getURI());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createDialog();
|
||||
|
|
|
@ -4,6 +4,10 @@ final class PhragmentSnapshotViewController extends PhragmentController {
|
|||
|
||||
private $id;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = idx($data, "id", "");
|
||||
}
|
||||
|
@ -72,6 +76,7 @@ final class PhragmentSnapshotViewController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$box,
|
||||
$list),
|
||||
array(
|
||||
|
@ -100,6 +105,11 @@ final class PhragmentSnapshotViewController extends PhragmentController {
|
|||
"zip@".$snapshot->getName().
|
||||
"/".$snapshot->getPrimaryFragment()->getPath());
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$snapshot,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$actions = id(new PhabricatorActionListView())
|
||||
->setUser($viewer)
|
||||
->setObject($snapshot)
|
||||
|
@ -107,24 +117,24 @@ final class PhragmentSnapshotViewController extends PhragmentController {
|
|||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Download Snapshot as ZIP'))
|
||||
->setHref($zip_uri)
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setHref($this->isCorrectlyConfigured() ? $zip_uri : null)
|
||||
->setDisabled(!$this->isCorrectlyConfigured())
|
||||
->setIcon('zip'));
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Delete Snapshot'))
|
||||
->setHref($this->getApplicationURI(
|
||||
"snapshot/delete/".$snapshot->getID()."/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setIcon('delete'));
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Promote Another Snapshot to Here'))
|
||||
->setHref($this->getApplicationURI(
|
||||
"snapshot/promote/".$snapshot->getID()."/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(false) // TODO: Policy
|
||||
->setIcon('promote'));
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
|
|
|
@ -74,6 +74,7 @@ final class PhragmentUpdateController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$box),
|
||||
array(
|
||||
'title' => pht('Update Fragment'),
|
||||
|
|
|
@ -4,6 +4,10 @@ final class PhragmentVersionController extends PhragmentController {
|
|||
|
||||
private $id;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = idx($data, "id", 0);
|
||||
}
|
||||
|
@ -41,7 +45,7 @@ final class PhragmentVersionController extends PhragmentController {
|
|||
->withPHIDs(array($version->getFilePHID()))
|
||||
->executeOne();
|
||||
if ($file !== null) {
|
||||
$file_uri = $file->getBestURI();
|
||||
$file_uri = $file->getDownloadURI();
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
|
@ -59,8 +63,8 @@ final class PhragmentVersionController extends PhragmentController {
|
|||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Download Version'))
|
||||
->setHref($file_uri)
|
||||
->setDisabled($file === null)
|
||||
->setDisabled($file === null || !$this->isCorrectlyConfigured())
|
||||
->setHref($this->isCorrectlyConfigured() ? $file_uri : null)
|
||||
->setIcon('download'));
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
|
@ -78,6 +82,7 @@ final class PhragmentVersionController extends PhragmentController {
|
|||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
$this->renderConfigurationWarningIfRequired(),
|
||||
$box,
|
||||
$this->renderPatchFromPreviousVersion($version, $file),
|
||||
$this->renderPreviousVersionList($version)),
|
||||
|
@ -155,11 +160,13 @@ final class PhragmentVersionController extends PhragmentController {
|
|||
$item->addAttribute(phabricator_datetime(
|
||||
$previous_version->getDateCreated(),
|
||||
$viewer));
|
||||
$patch_uri = $this->getApplicationURI(
|
||||
'patch/'.$previous_version->getID().'/'.$version->getID());
|
||||
$item->addAction(id(new PHUIListItemView())
|
||||
->setIcon('patch')
|
||||
->setName(pht("Get Patch"))
|
||||
->setHref($this->getApplicationURI(
|
||||
'patch/'.$previous_version->getID().'/'.$version->getID())));
|
||||
->setHref($this->isCorrectlyConfigured() ? $patch_uri : null)
|
||||
->setDisabled(!$this->isCorrectlyConfigured()));
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@ final class PhragmentZIPController extends PhragmentController {
|
|||
|
||||
private $snapshotCache;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->dblob = idx($data, "dblob", "");
|
||||
$this->snapshot = idx($data, "snapshot", null);
|
||||
|
@ -87,7 +91,9 @@ final class PhragmentZIPController extends PhragmentController {
|
|||
}
|
||||
|
||||
foreach ($mappings as $path => $file) {
|
||||
$zip->addFromString($path, $file->loadFileData());
|
||||
if ($file !== null) {
|
||||
$zip->addFromString($path, $file->loadFileData());
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
|
||||
|
@ -103,8 +109,18 @@ final class PhragmentZIPController extends PhragmentController {
|
|||
'name' => $zip_name,
|
||||
'ttl' => time() + 60 * 60 * 24,
|
||||
));
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$file->attachToObject($viewer, $fragment->getPHID());
|
||||
unset($unguarded);
|
||||
|
||||
$return = $fragment->getURI();
|
||||
if ($request->getExists('return')) {
|
||||
$return = $request->getStr('return');
|
||||
}
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($file->getBestURI());
|
||||
->setURI($file->getDownloadURI($return));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -118,6 +118,8 @@ final class PhragmentFragment extends PhragmentDAO
|
|||
$this->setLatestVersionPHID($version->getPHID());
|
||||
$this->save();
|
||||
$this->saveTransaction();
|
||||
|
||||
$file->attachToObject($viewer, $version->getPHID());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,9 +48,7 @@ final class PhragmentSnapshot extends PhragmentDAO
|
|||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW
|
||||
);
|
||||
return $this->getPrimaryFragment()->getCapabilities();
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
|
|
|
@ -1828,6 +1828,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20131208.phragmentsnapshot.sql'),
|
||||
),
|
||||
'20131211.phragmentedges.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20131211.phragmentedges.sql'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ final class PhabricatorActionView extends AphrontView {
|
|||
* viewing.
|
||||
*/
|
||||
public function getHref() {
|
||||
if ($this->workflow || $this->renderAsForm) {
|
||||
if (($this->workflow || $this->renderAsForm) && !$this->download) {
|
||||
if (!$this->user || !$this->user->isLoggedIn()) {
|
||||
return id(new PhutilURI('/auth/start/'))
|
||||
->setQueryParam('next', (string)$this->getObjectURI());
|
||||
|
|
Loading…
Reference in a new issue