From 36e71a06012bf79b3c563bf1fd830b1c28246fc1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 24 Aug 2012 13:19:30 -0700 Subject: [PATCH] Allow pastes to be edited Summary: Permits the name and langauge of a paste to be edited. This will eventually allow the visibility policy to be edited as well. Test Plan: Edited name/langauge of some pastes. Tried to edit a paste I didn't own, was harshly rebuffed. Reviewers: vrana, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T1690 Differential Revision: https://secure.phabricator.com/D3376 --- scripts/celerity/generate_sprites.php | 1 + .../PhabricatorApplicationPaste.php | 1 + .../PhabricatorPasteEditController.php | 132 ++++++++++++------ .../PhabricatorPasteViewController.php | 17 ++- .../paste/storage/PhabricatorPaste.php | 6 +- src/view/layout/PhabricatorActionView.php | 23 ++- webroot/rsrc/css/autosprite.css | 4 + .../layout/phabricator-action-list-view.css | 9 ++ webroot/rsrc/image/autosprite.png | Bin 84373 -> 84760 bytes 9 files changed, 144 insertions(+), 49 deletions(-) diff --git a/scripts/celerity/generate_sprites.php b/scripts/celerity/generate_sprites.php index 32be7420f9..df40173c85 100755 --- a/scripts/celerity/generate_sprites.php +++ b/scripts/celerity/generate_sprites.php @@ -161,6 +161,7 @@ $action_template = id(new PhutilSprite()) $action_map = array( 'file' => 'icon/page_white_text.png', 'fork' => 'icon/arrow_branch.png', + 'edit' => 'icon/page_white_edit.png', ); foreach ($action_map as $icon => $source) { diff --git a/src/applications/paste/application/PhabricatorApplicationPaste.php b/src/applications/paste/application/PhabricatorApplicationPaste.php index 925db87345..0ca94dd5c4 100644 --- a/src/applications/paste/application/PhabricatorApplicationPaste.php +++ b/src/applications/paste/application/PhabricatorApplicationPaste.php @@ -35,6 +35,7 @@ final class PhabricatorApplicationPaste extends PhabricatorApplication { '/P(?P\d+)' => 'PhabricatorPasteViewController', '/paste/' => array( '' => 'PhabricatorPasteEditController', + 'edit/(?P\d+)/' => 'PhabricatorPasteEditController', 'filter/(?P\w+)/' => 'PhabricatorPasteListController', ), ); diff --git a/src/applications/paste/controller/PhabricatorPasteEditController.php b/src/applications/paste/controller/PhabricatorPasteEditController.php index e9d15ad541..27a00b7f9a 100644 --- a/src/applications/paste/controller/PhabricatorPasteEditController.php +++ b/src/applications/paste/controller/PhabricatorPasteEditController.php @@ -18,26 +18,54 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController { + private $id; + + public function willProcessRequest(array $data) { + $this->id = idx($data, 'id'); + } + + public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); - $paste = new PhabricatorPaste(); - $title = 'Create Paste'; - - $parent_id = $request->getStr('parent'); $parent = null; - if ($parent_id) { - // NOTE: If the Paste is forked from a paste which the user no longer - // has permission to see, we still let them edit it. - $parent = id(new PhabricatorPasteQuery()) - ->setViewer($user) - ->withIDs(array($parent_id)) - ->execute(); - $parent = head($parent); + $parent_id = null; + if (!$this->id) { + $is_create = true; - if ($parent) { - $paste->setParentPHID($parent->getPHID()); + $paste = new PhabricatorPaste(); + + $parent_id = $request->getStr('parent'); + if ($parent_id) { + // NOTE: If the Paste is forked from a paste which the user no longer + // has permission to see, we still let them edit it. + $parent = id(new PhabricatorPasteQuery()) + ->setViewer($user) + ->withIDs(array($parent_id)) + ->execute(); + $parent = head($parent); + + if ($parent) { + $paste->setParentPHID($parent->getPHID()); + } + } + + $paste->setAuthorPHID($user->getPHID()); + } else { + $is_create = false; + + $paste = id(new PhabricatorPasteQuery()) + ->setViewer($user) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->withIDs(array($this->id)) + ->executeOne(); + if (!$paste) { + return new Aphront404Response(); } } @@ -45,33 +73,36 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController { $e_text = true; $errors = array(); if ($request->isFormPost()) { - $text = $request->getStr('text'); - if (!strlen($text)) { - $e_text = 'Required'; - $errors[] = 'The paste may not be blank.'; - } else { - $e_text = null; + + if ($is_create) { + $text = $request->getStr('text'); + if (!strlen($text)) { + $e_text = 'Required'; + $errors[] = 'The paste may not be blank.'; + } else { + $e_text = null; + } } $paste->setTitle($request->getStr('title')); $paste->setLanguage($request->getStr('language')); if (!$errors) { - $paste_file = PhabricatorFile::newFromFileData( - $text, - array( - 'name' => $title, - 'mime-type' => 'text/plain; charset=utf-8', - 'authorPHID' => $user->getPHID(), - )); - $paste->setFilePHID($paste_file->getPHID()); - $paste->setAuthorPHID($user->getPHID()); + if ($is_create) { + $paste_file = PhabricatorFile::newFromFileData( + $text, + array( + 'name' => $paste->getTitle(), + 'mime-type' => 'text/plain; charset=utf-8', + 'authorPHID' => $user->getPHID(), + )); + $paste->setFilePHID($paste_file->getPHID()); + } $paste->save(); - return id(new AphrontRedirectResponse())->setURI($paste->getURI()); } } else { - if ($parent) { + if ($is_create && $parent) { $paste->setTitle('Fork of '.$parent->getFullName()); $paste->setLanguage($parent->getLanguage()); @@ -96,9 +127,6 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController { '' => '(Detect With Wizardly Powers)', ) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices'); - $submit = id(new AphrontFormSubmitControl()) - ->setValue('Create Paste'); - $form ->setUser($user) ->addHiddenInput('parent', $parent_id) @@ -112,15 +140,19 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController { ->setLabel('Language') ->setName('language') ->setValue($paste->getLanguage()) - ->setOptions($langs)) - ->appendChild( - id(new AphrontFormTextAreaControl()) - ->setLabel('Text') - ->setError($e_text) - ->setValue($text) - ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) - ->setCustomClass('PhabricatorMonospaced') - ->setName('text')) + ->setOptions($langs)); + + if ($is_create) { + $form + ->appendChild( + id(new AphrontFormTextAreaControl()) + ->setLabel('Text') + ->setError($e_text) + ->setValue($text) + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) + ->setCustomClass('PhabricatorMonospaced') + ->setName('text')); + } /* TODO: Doesn't have any useful options yet. ->appendChild( @@ -132,13 +164,25 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController { ->setName('policy')) */ + $submit = new AphrontFormSubmitControl(); + + if (!$is_create) { + $submit->addCancelButton($paste->getURI()); + $submit->setValue('Save Paste'); + $title = 'Edit '.$paste->getFullName(); + } else { + $submit->setValue('Create Paste'); + $title = 'Create Paste'; + } + + $form ->appendChild($submit); $nav = $this->buildSideNavView(); $nav->selectFilter('edit'); $nav->appendChild( array( - id(new PhabricatorHeaderView())->setHeader('Create Paste'), + id(new PhabricatorHeaderView())->setHeader($title), $error_view, $form, )); diff --git a/src/applications/paste/controller/PhabricatorPasteViewController.php b/src/applications/paste/controller/PhabricatorPasteViewController.php index ce975f83cf..971840f05c 100644 --- a/src/applications/paste/controller/PhabricatorPasteViewController.php +++ b/src/applications/paste/controller/PhabricatorPasteViewController.php @@ -59,7 +59,7 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController { $fork_phids)); $header = $this->buildHeaderView($paste); - $actions = $this->buildActionView($paste, $file); + $actions = $this->buildActionView($user, $paste, $file); $properties = $this->buildPropertyView($paste, $fork_phids); $source_code = $this->buildSourceCodeView($paste, $file); @@ -89,9 +89,15 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController { } private function buildActionView( + PhabricatorUser $user, PhabricatorPaste $paste, PhabricatorFile $file) { + $can_edit = PhabricatorPolicyFilter::hasCapability( + $user, + $paste, + PhabricatorPolicyCapability::CAN_EDIT); + return id(new PhabricatorActionListView()) ->addAction( id(new PhabricatorActionView()) @@ -102,7 +108,14 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController { id(new PhabricatorActionView()) ->setName(pht('View Raw File')) ->setIcon('file') - ->setHref($file->getBestURI())); + ->setHref($file->getBestURI())) + ->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Paste')) + ->setIcon('edit') + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit) + ->setHref($this->getApplicationURI('/edit/'.$paste->getID().'/'))); } private function buildPropertyView( diff --git a/src/applications/paste/storage/PhabricatorPaste.php b/src/applications/paste/storage/PhabricatorPaste.php index b3b73546e4..9064dac2b8 100644 --- a/src/applications/paste/storage/PhabricatorPaste.php +++ b/src/applications/paste/storage/PhabricatorPaste.php @@ -44,11 +44,15 @@ final class PhabricatorPaste extends PhabricatorPasteDAO public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { - return PhabricatorPolicies::POLICY_USER; + if ($capability == PhabricatorPolicyCapability::CAN_VIEW) { + return PhabricatorPolicies::POLICY_USER; + } + return PhabricatorPolicies::POLICY_NOONE; } public function hasAutomaticCapability($capability, PhabricatorUser $user) { diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php index 778e0d4845..73294baad3 100644 --- a/src/view/layout/PhabricatorActionView.php +++ b/src/view/layout/PhabricatorActionView.php @@ -21,6 +21,8 @@ final class PhabricatorActionView extends AphrontView { private $name; private $icon; private $href; + private $disabled; + private $workflow; public function setHref($href) { $this->href = $href; @@ -37,6 +39,16 @@ final class PhabricatorActionView extends AphrontView { return $this; } + public function setDisabled($disabled) { + $this->disabled = $disabled; + return $this; + } + + public function setWorkflow($workflow) { + $this->workflow = $workflow; + return $this; + } + public function render() { $icon = null; @@ -51,11 +63,12 @@ final class PhabricatorActionView extends AphrontView { } if ($this->href) { - $item = phutil_render_tag( + $item = javelin_render_tag( 'a', array( 'href' => $this->href, 'class' => 'phabricator-action-view-item', + 'sigil' => $this->workflow ? 'workflow' : null, ), phutil_escape_html($this->name)); } else { @@ -67,10 +80,16 @@ final class PhabricatorActionView extends AphrontView { phutil_escape_html($this->name)); } + $classes = array(); + $classes[] = 'phabricator-action-view'; + if ($this->disabled) { + $classes[] = 'phabricator-action-view-disabled'; + } + return phutil_render_tag( 'li', array( - 'class' => 'phabricator-action-view', + 'class' => implode(' ', $classes), ), $icon.$item); } diff --git a/webroot/rsrc/css/autosprite.css b/webroot/rsrc/css/autosprite.css index fa9492afbb..4d62d03452 100644 --- a/webroot/rsrc/css/autosprite.css +++ b/webroot/rsrc/css/autosprite.css @@ -258,3 +258,7 @@ .action-fork { background-position: 0px -2538px; } + +.action-edit { + background-position: 0px -2555px; +} diff --git a/webroot/rsrc/css/layout/phabricator-action-list-view.css b/webroot/rsrc/css/layout/phabricator-action-list-view.css index 37b076dbb8..65fb124fd9 100644 --- a/webroot/rsrc/css/layout/phabricator-action-list-view.css +++ b/webroot/rsrc/css/layout/phabricator-action-list-view.css @@ -52,3 +52,12 @@ color: #ffffff; text-decoration: none; } + +.phabricator-action-view-disabled .phabricator-action-view-item { + color: #888888; +} + +.phabricator-action-view-disabled .phabricator-action-view-item:hover { + background-color: #dfdfdf; + color: #888888; +} diff --git a/webroot/rsrc/image/autosprite.png b/webroot/rsrc/image/autosprite.png index 6730b0097a7fc87e8e5e9874d5b9b7a3d295e139..ec0e480ec15b8ba2c3b71d9f5d50a34788457f63 100644 GIT binary patch delta 6036 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