Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F90151
D7751.id17558.diff
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
34 KB
Referenced Files
None
Subscribers
None
D7751.id17558.diff
View Options
diff --git a/resources/sql/patches/20131211.phragmentedges.sql b/resources/sql/patches/20131211.phragmentedges.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/patches/20131211.phragmentedges.sql
@@ -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;
diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php
--- a/src/__celerity_resource_map__.php
+++ b/src/__celerity_resource_map__.php
@@ -1776,6 +1776,18 @@
),
'disk' => '/rsrc/js/core/behavior-fancy-datepicker.js',
),
+ 'javelin-behavior-files-download-redirect' =>
+ array(
+ 'uri' => '/res/e3513aad/rsrc/js/application/files/behavior-files-download-redirect.js',
+ 'type' => 'js',
+ 'requires' =>
+ array(
+ 0 => 'javelin-behavior',
+ 1 => 'javelin-dom',
+ 2 => 'javelin-workflow',
+ ),
+ 'disk' => '/rsrc/js/application/files/behavior-files-download-redirect.js',
+ ),
'javelin-behavior-global-drag-and-drop' =>
array(
'uri' => '/res/ee8e9c39/rsrc/js/core/behavior-global-drag-and-drop.js',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2176,6 +2176,7 @@
'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',
@@ -2189,6 +2190,7 @@
'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',
@@ -4782,6 +4784,7 @@
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',
+ 'PhragmentCapabilityCanCreate' => 'PhabricatorPolicyCapability',
'PhragmentController' => 'PhabricatorController',
'PhragmentCreateController' => 'PhragmentController',
'PhragmentDAO' => 'PhabricatorLiskDAO',
@@ -4803,6 +4806,7 @@
'PhragmentPHIDTypeSnapshot' => 'PhabricatorPHIDType',
'PhragmentPatchController' => 'PhragmentController',
'PhragmentPatchUtil' => 'Phobject',
+ 'PhragmentPolicyController' => 'PhragmentController',
'PhragmentRevertController' => 'PhragmentController',
'PhragmentSnapshot' =>
array(
diff --git a/src/applications/files/application/PhabricatorApplicationFiles.php b/src/applications/files/application/PhabricatorApplicationFiles.php
--- a/src/applications/files/application/PhabricatorApplicationFiles.php
+++ b/src/applications/files/application/PhabricatorApplicationFiles.php
@@ -61,6 +61,7 @@
'xform/(?P<transform>[^/]+)/(?P<phid>[^/]+)/(?P<key>[^/]+)/'
=> 'PhabricatorFileTransformController',
'uploaddialog/' => 'PhabricatorFileUploadDialogController',
+ 'download/(?P<phid>[^/]+)/' => 'PhabricatorFileDialogController',
),
);
}
diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php
--- a/src/applications/files/controller/PhabricatorFileDataController.php
+++ b/src/applications/files/controller/PhabricatorFileDataController.php
@@ -66,12 +66,12 @@
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()));
}
diff --git a/src/applications/phragment/application/PhabricatorApplicationPhragment.php b/src/applications/phragment/application/PhabricatorApplicationPhragment.php
--- a/src/applications/phragment/application/PhabricatorApplicationPhragment.php
+++ b/src/applications/phragment/application/PhabricatorApplicationPhragment.php
@@ -37,6 +37,7 @@
'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 @@
);
}
+ protected function getCustomCapabilities() {
+ return array(
+ PhragmentCapabilityCanCreate::CAPABILITY => array(
+ ),
+ );
+ }
+
}
diff --git a/src/applications/phragment/capability/PhragmentCapabilityCanCreate.php b/src/applications/phragment/capability/PhragmentCapabilityCanCreate.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phragment/capability/PhragmentCapabilityCanCreate.php
@@ -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.');
+ }
+
+}
diff --git a/src/applications/phragment/controller/PhragmentBrowseController.php b/src/applications/phragment/controller/PhragmentBrowseController.php
--- a/src/applications/phragment/controller/PhragmentBrowseController.php
+++ b/src/applications/phragment/controller/PhragmentBrowseController.php
@@ -4,6 +4,10 @@
private $dblob;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
}
@@ -24,11 +28,14 @@
}
$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 @@
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$current_box,
$list),
array(
diff --git a/src/applications/phragment/controller/PhragmentController.php b/src/applications/phragment/controller/PhragmentController.php
--- a/src/applications/phragment/controller/PhragmentController.php
+++ b/src/applications/phragment/controller/PhragmentController.php
@@ -84,46 +84,62 @@
->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
->executeOne();
if ($file !== null) {
- $file_uri = $file->getBestURI();
+ $file_uri = $file->getDownloadURI();
}
}
$header = id(new PHUIHeaderView())
->setHeader($fragment->getName())
->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)
->setObjectURI($fragment->getURI());
$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,15 +158,16 @@
->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())
->setName(pht('Promote Snapshot to Here'))
->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 @@
->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;
+ }
+
}
diff --git a/src/applications/phragment/controller/PhragmentCreateController.php b/src/applications/phragment/controller/PhragmentCreateController.php
--- a/src/applications/phragment/controller/PhragmentCreateController.php
+++ b/src/applications/phragment/controller/PhragmentCreateController.php
@@ -123,6 +123,7 @@
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$box),
array(
'title' => pht('Create Fragment'),
diff --git a/src/applications/phragment/controller/PhragmentHistoryController.php b/src/applications/phragment/controller/PhragmentHistoryController.php
--- a/src/applications/phragment/controller/PhragmentHistoryController.php
+++ b/src/applications/phragment/controller/PhragmentHistoryController.php
@@ -4,6 +4,10 @@
private $dblob;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
}
@@ -21,11 +25,14 @@
$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 @@
->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 @@
$item->addAttribute('Deletion');
}
- if (!$first) {
+ if (!$first && $can_edit) {
$item->addAction(id(new PHUIListItemView())
->setIcon('undo')
->setRenderNameAsTooltip(true)
@@ -71,21 +83,24 @@
$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;
}
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$current_box,
$list),
array(
diff --git a/src/applications/phragment/controller/PhragmentPatchController.php b/src/applications/phragment/controller/PhragmentPatchController.php
--- a/src/applications/phragment/controller/PhragmentPatchController.php
+++ b/src/applications/phragment/controller/PhragmentPatchController.php
@@ -5,6 +5,10 @@
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 @@
$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,15 +80,25 @@
$a_sequence.'.'.
$version_b->getSequence().'.patch';
+ $return = $version_b->getURI();
+ if ($request->getExists('return')) {
+ $return = $request->getStr('return');
+ }
+
$result = PhabricatorFile::buildFromFileDataOrHash(
$patch,
array(
'name' => $name,
'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));
}
}
diff --git a/src/applications/phragment/controller/PhragmentPolicyController.php b/src/applications/phragment/controller/PhragmentPolicyController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phragment/controller/PhragmentPolicyController.php
@@ -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));
+ }
+
+}
diff --git a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php
@@ -21,6 +21,11 @@
return new Aphront404Response();
}
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $fragment,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$children = id(new PhragmentFragmentQuery())
->setViewer($viewer)
->needLatestVersion(true)
@@ -161,6 +166,7 @@
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$box),
array(
'title' => pht('Create Fragment'),
diff --git a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php
@@ -14,6 +14,9 @@
$snapshot = id(new PhragmentSnapshotQuery())
->setViewer($viewer)
+ ->requireCapabilities(array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT))
->withIDs(array($this->id))
->executeOne();
if ($snapshot === null) {
diff --git a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php
@@ -23,6 +23,9 @@
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 @@
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 @@
}
$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();
diff --git a/src/applications/phragment/controller/PhragmentSnapshotViewController.php b/src/applications/phragment/controller/PhragmentSnapshotViewController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotViewController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotViewController.php
@@ -4,6 +4,10 @@
private $id;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->id = idx($data, "id", "");
}
@@ -72,6 +76,7 @@
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$box,
$list),
array(
@@ -100,31 +105,36 @@
"zip@".$snapshot->getName().
"/".$snapshot->getPrimaryFragment()->getPath());
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $snapshot,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($snapshot)
->setObjectURI($snapshot->getURI());
$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())
diff --git a/src/applications/phragment/controller/PhragmentUpdateController.php b/src/applications/phragment/controller/PhragmentUpdateController.php
--- a/src/applications/phragment/controller/PhragmentUpdateController.php
+++ b/src/applications/phragment/controller/PhragmentUpdateController.php
@@ -74,6 +74,7 @@
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$box),
array(
'title' => pht('Update Fragment'),
diff --git a/src/applications/phragment/controller/PhragmentVersionController.php b/src/applications/phragment/controller/PhragmentVersionController.php
--- a/src/applications/phragment/controller/PhragmentVersionController.php
+++ b/src/applications/phragment/controller/PhragmentVersionController.php
@@ -4,6 +4,10 @@
private $id;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->id = idx($data, "id", 0);
}
@@ -41,7 +45,7 @@
->withPHIDs(array($version->getFilePHID()))
->executeOne();
if ($file !== null) {
- $file_uri = $file->getBestURI();
+ $file_uri = $file->getDownloadURI();
}
$header = id(new PHUIHeaderView())
@@ -59,8 +63,8 @@
$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 @@
return $this->buildApplicationPage(
array(
$crumbs,
+ $this->renderConfigurationWarningIfRequired(),
$box,
$this->renderPatchFromPreviousVersion($version, $file),
$this->renderPreviousVersionList($version)),
@@ -155,11 +160,13 @@
$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);
}
diff --git a/src/applications/phragment/controller/PhragmentZIPController.php b/src/applications/phragment/controller/PhragmentZIPController.php
--- a/src/applications/phragment/controller/PhragmentZIPController.php
+++ b/src/applications/phragment/controller/PhragmentZIPController.php
@@ -7,6 +7,10 @@
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 @@
}
foreach ($mappings as $path => $file) {
- $zip->addFromString($path, $file->loadFileData());
+ if ($file !== null) {
+ $zip->addFromString($path, $file->loadFileData());
+ }
}
$zip->close();
@@ -103,8 +109,18 @@
'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));
}
/**
diff --git a/src/applications/phragment/storage/PhragmentFragment.php b/src/applications/phragment/storage/PhragmentFragment.php
--- a/src/applications/phragment/storage/PhragmentFragment.php
+++ b/src/applications/phragment/storage/PhragmentFragment.php
@@ -118,6 +118,8 @@
$this->setLatestVersionPHID($version->getPHID());
$this->save();
$this->saveTransaction();
+
+ $file->attachToObject($viewer, $version->getPHID());
}
/**
diff --git a/src/applications/phragment/storage/PhragmentSnapshot.php b/src/applications/phragment/storage/PhragmentSnapshot.php
--- a/src/applications/phragment/storage/PhragmentSnapshot.php
+++ b/src/applications/phragment/storage/PhragmentSnapshot.php
@@ -48,9 +48,7 @@
public function getCapabilities() {
- return array(
- PhabricatorPolicyCapability::CAN_VIEW
- );
+ return $this->getPrimaryFragment()->getCapabilities();
}
public function getPolicy($capability) {
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1828,6 +1828,10 @@
'type' => 'sql',
'name' => $this->getPatchPath('20131208.phragmentsnapshot.sql'),
),
+ '20131211.phragmentedges.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131211.phragmentedges.sql'),
+ ),
);
}
}
diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php
--- a/src/view/layout/PhabricatorActionView.php
+++ b/src/view/layout/PhabricatorActionView.php
@@ -40,7 +40,7 @@
* 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());
diff --git a/webroot/rsrc/js/application/files/behavior-files-download-redirect.js b/webroot/rsrc/js/application/files/behavior-files-download-redirect.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/application/files/behavior-files-download-redirect.js
@@ -0,0 +1,19 @@
+/**
+ * @provides javelin-behavior-files-download-redirect
+ * @requires javelin-behavior
+ * javelin-dom
+ * javelin-workflow
+ */
+
+JX.behavior('files-download-redirect', function(config) {
+
+ JX.DOM.listen(
+ JX.$(config.dialogID),
+ 'submit',
+ null,
+ function(e) {
+ window.setTimeout(function() { location.href = config.return; }, 2000);
+ });
+
+});
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/z3/3y/sanbltesi6vhkif3
Default Alt Text
D7751.id17558.diff (34 KB)
Attached To
Mode
D7751: Implement policies in Phragment
Attached
Detach File
Event Timeline
Log In to Comment