Page MenuHomePhabricator

D7751.id17536.diff

D7751.id17536.diff

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/b5b40799/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
@@ -1406,6 +1406,7 @@
'PhabricatorFileDAO' => 'applications/files/storage/PhabricatorFileDAO.php',
'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php',
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
+ 'PhabricatorFileDialogController' => 'applications/files/controller/PhabricatorFileDialogController.php',
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
'PhabricatorFileEditor' => 'applications/files/editor/PhabricatorFileEditor.php',
'PhabricatorFileImageMacro' => 'applications/macro/storage/PhabricatorFileImageMacro.php',
@@ -2176,6 +2177,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 +2191,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',
@@ -3914,6 +3917,7 @@
'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
'PhabricatorFileDataController' => 'PhabricatorFileController',
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
+ 'PhabricatorFileDialogController' => 'PhabricatorFileController',
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
'PhabricatorFileEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorFileImageMacro' =>
@@ -4782,6 +4786,7 @@
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',
+ 'PhragmentCapabilityCanCreate' => 'PhabricatorPolicyCapability',
'PhragmentController' => 'PhabricatorController',
'PhragmentCreateController' => 'PhragmentController',
'PhragmentDAO' => 'PhabricatorLiskDAO',
@@ -4803,6 +4808,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/PhabricatorFileDialogController.php b/src/applications/files/controller/PhabricatorFileDialogController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/files/controller/PhabricatorFileDialogController.php
@@ -0,0 +1,61 @@
+<?php
+
+final class PhabricatorFileDialogController extends PhabricatorFileController {
+
+ private $phid;
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function willProcessRequest(array $data) {
+ $this->phid = $data['phid'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($user)
+ ->withPHIDs(array($this->phid))
+ ->executeOne();
+
+ if (!$file) {
+ return new Aphront404Response();
+ }
+
+ $return = $request->getStr('return');
+
+ // FIXME: There is probably some functionality in Phabricator to sanity
+ // check URIs already?
+ if (strlen($return) === 0 || ($return[0] !== '/' &&
+ substr($return, 0, 7) !== 'http://' &&
+ substr($return, 0, 8) !== 'https://')) {
+ throw new Exception("Invalid or unsafe return URI!");
+ }
+
+ $dialog_id = celerity_generate_unique_node_id();
+
+ Javelin::initBehavior(
+ 'files-download-redirect',
+ array(
+ 'dialogID' => $dialog_id,
+ 'return' => $return,
+ ));
+
+ $dialog = id(new AphrontDialogView())
+ ->setFormID($dialog_id)
+ ->setTitle(pht('Download: %s', $file->getName()))
+ ->setUser($request->getUser())
+ ->addSubmitButton(pht('Download'))
+ ->addCancelButton($return, pht('Back'))
+ ->setSubmitURI($file->getDownloadURI())
+ ->appendParagraph(pht("File Type: %s", $file->getMimeType()))
+ ->appendParagraph(pht(
+ "File Size: %s",
+ phabricator_format_bytes($file->getByteSize())));
+ return id(new AphrontDialogResponse())->setDialog($dialog);
+ }
+
+}
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -512,6 +512,12 @@
return (string) $uri;
}
+ public function getDownloadDialogURI($return) {
+ $uri = id(new PhutilURI('/file/download/'.$this->getPHID().'/'))
+ ->setQueryParam('return', $return);
+ return (string) $uri;
+ }
+
public function getProfileThumbURI() {
$path = '/file/xform/thumb-profile/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
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);
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
@@ -79,20 +79,29 @@
$file = null;
$file_uri = null;
if (!$fragment->isDirectory()) {
- $file = id(new PhabricatorFileQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
- ->executeOne();
- if ($file !== null) {
- $file_uri = $file->getBestURI();
+ try {
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
+ ->executeOne();
+ if ($file !== null) {
+ $file_uri = $file->getDownloadURI();
+ }
+ } catch (PhabricatorPolicyException $ex) {
+ // The download link won't be usable.
}
}
$header = id(new PHUIHeaderView())
->setHeader($fragment->getName())
->setPolicyObject($fragment)
->setUser($viewer);
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $fragment,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($fragment)
@@ -102,28 +111,39 @@
->setName(pht('Download Fragment'))
->setHref($file_uri)
->setDisabled($file === null)
+ ->setRenderAsForm(true)
+ ->setDownload(true)
+ ->setAnonymous(true)
->setIcon('download'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Contents as ZIP'))
->setHref($this->getApplicationURI("zip/".$fragment->getPath()))
- ->setDisabled(false) // TODO: Policy
->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 +162,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())
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)
@@ -68,16 +80,22 @@
"revert/".$version->getID()."/".$current->getPath())));
}
- $disabled = !isset($files[$version->getFilePHID()]);
- $action = id(new PHUIListItemView())
- ->setIcon('download')
- ->setDisabled($disabled)
- ->setRenderNameAsTooltip(true)
- ->setName(pht("Download"));
- if (!$disabled) {
- $action->setHref($files[$version->getFilePHID()]->getBestURI());
+ // We can't display the download links here for unauthenticated users
+ // as they won't be able to see the info page. If they click through
+ // to the version they can download it from there anyway.
+ if (!$request->getUser()) {
+ $disabled = !isset($files[$version->getFilePHID()]);
+ $action = id(new PHUIListItemView())
+ ->setIcon('download')
+ ->setDisabled($disabled)
+ ->setRenderNameAsTooltip(true)
+ ->setName(pht("Download"));
+ if (!$disabled) {
+ $action->setHref($files[$version->getFilePHID()]->getBestURI());
+ }
+ $item->addAction($action);
}
- $item->addAction($action);
+
$list->addItem($item);
$first = false;
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,28 @@
$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->getPHID());
+ if ($version_a !== null) {
+ $result->attachToObject($viewer, $version_a->getPHID());
+ }
+ unset($unguarded);
+
return id(new AphrontRedirectResponse())
- ->setURI($result->getBestURI());
+ ->setURI($result->getDownloadDialogURI($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,109 @@
+<?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,
+ $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)
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
@@ -20,6 +20,11 @@
return new Aphront404Response();
}
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $snapshot,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
if ($request->isDialogFormPost()) {
$fragment_uri = $snapshot->getPrimaryFragment()->getURI();
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", "");
}
@@ -100,31 +104,35 @@
"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
->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/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())
@@ -61,6 +65,9 @@
->setName(pht('Download Version'))
->setHref($file_uri)
->setDisabled($file === null)
+ ->setRenderAsForm(true)
+ ->setDownload(true)
+ ->setAnonymous(true)
->setIcon('download'));
$properties = id(new PHUIPropertyListView())
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->getDownloadDialogURI($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
@@ -11,6 +11,7 @@
private $renderAsForm;
private $download;
private $objectURI;
+ private $anonymous;
public function setObjectURI($object_uri) {
$this->objectURI = $object_uri;
@@ -40,7 +41,7 @@
* viewing.
*/
public function getHref() {
- if ($this->workflow || $this->renderAsForm) {
+ if (($this->workflow || $this->renderAsForm) && !$this->anonymous) {
if (!$this->user || !$this->user->isLoggedIn()) {
return id(new PhutilURI('/auth/start/'))
->setQueryParam('next', (string)$this->getObjectURI());
@@ -75,6 +76,11 @@
return $this;
}
+ public function setAnonymous($anonymous) {
+ $this->anonymous = $anonymous;
+ return $this;
+ }
+
public function setRenderAsForm($form) {
$this->renderAsForm = $form;
return $this;
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; }, 100);
+ });
+
+});
+

File Metadata

Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/fb/6d/mkrzvopidqymtvrv
Default Alt Text
D7751.id17536.diff (33 KB)

Event Timeline