From f8b085c574aeb24f59ea58ddf378283126b646b3 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 7 Nov 2015 06:52:42 -0800 Subject: [PATCH 01/49] Add a transaction for PhamePost visibility Summary: Adds ability to set visibility when authoring a Post. New default is "Visible". If you write a post and save it as a Draft, and later click publish, a feed story and mail will go out. Test Plan: Write a new Post, see feed story and get email. Write a new Draft, get nothing. Click Publish, see story and email. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9360 Differential Revision: https://secure.phabricator.com/D14429 --- src/__phutil_library_map__.php | 2 + .../PhameCreatePostConduitAPIMethod.php | 2 +- .../PhameQueryPostsConduitAPIMethod.php | 4 +- .../phame/constants/PhameConstants.php | 23 ++++++++ .../blog/PhameBlogFeedController.php | 2 +- .../post/PhamePostEditController.php | 31 ++++++---- .../post/PhamePostPublishController.php | 20 ++++++- .../post/PhamePostUnpublishController.php | 20 ++++++- .../phame/editor/PhamePostEditor.php | 12 ++++ .../phame/query/PhamePostSearchEngine.php | 10 ++-- src/applications/phame/storage/PhamePost.php | 15 +---- .../phame/storage/PhamePostTransaction.php | 56 ++++++++++++++++--- 12 files changed, 150 insertions(+), 47 deletions(-) create mode 100644 src/applications/phame/constants/PhameConstants.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index aaacc2cde4..fa57f18743 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3262,6 +3262,7 @@ phutil_register_library_map(array( 'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php', 'PhameCelerityResources' => 'applications/phame/celerity/PhameCelerityResources.php', 'PhameConduitAPIMethod' => 'applications/phame/conduit/PhameConduitAPIMethod.php', + 'PhameConstants' => 'applications/phame/constants/PhameConstants.php', 'PhameController' => 'applications/phame/controller/PhameController.php', 'PhameCreatePostConduitAPIMethod' => 'applications/phame/conduit/PhameCreatePostConduitAPIMethod.php', 'PhameDAO' => 'applications/phame/storage/PhameDAO.php', @@ -7525,6 +7526,7 @@ phutil_register_library_map(array( 'PhameBlogViewController' => 'PhameBlogController', 'PhameCelerityResources' => 'CelerityResources', 'PhameConduitAPIMethod' => 'ConduitAPIMethod', + 'PhameConstants' => 'Phobject', 'PhameController' => 'PhabricatorController', 'PhameCreatePostConduitAPIMethod' => 'PhameConduitAPIMethod', 'PhameDAO' => 'PhabricatorLiskDAO', diff --git a/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php b/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php index bdee6e2829..425bf17353 100644 --- a/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php @@ -85,7 +85,7 @@ final class PhameCreatePostConduitAPIMethod extends PhameConduitAPIMethod { $is_draft = $request->getValue('isDraft', false); if (!$is_draft) { $post->setDatePublished(time()); - $post->setVisibility(PhamePost::VISIBILITY_PUBLISHED); + $post->setVisibility(PhameConstants::VISIBILITY_PUBLISHED); } $post->setTitle($title); $phame_title = $request->getValue( diff --git a/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php b/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php index 06c20c7720..0ecb4d22eb 100644 --- a/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php @@ -65,9 +65,9 @@ final class PhameQueryPostsConduitAPIMethod extends PhameConduitAPIMethod { $published = $request->getValue('published', null); if ($published === true) { - $query->withVisibility(PhamePost::VISIBILITY_PUBLISHED); + $query->withVisibility(PhameConstants::VISIBILITY_PUBLISHED); } else if ($published === false) { - $query->withVisibility(PhamePost::VISIBILITY_DRAFT); + $query->withVisibility(PhameConstants::VISIBILITY_DRAFT); } $published_after = $request->getValue('publishedAfter', null); diff --git a/src/applications/phame/constants/PhameConstants.php b/src/applications/phame/constants/PhameConstants.php new file mode 100644 index 0000000000..c9444411f0 --- /dev/null +++ b/src/applications/phame/constants/PhameConstants.php @@ -0,0 +1,23 @@ + pht('Published'), + self::VISIBILITY_DRAFT => pht('Draft'), + ); + } + + public static function getPhamePostStatusName($status) { + $map = array( + self::VISIBILITY_PUBLISHED => pht('Published'), + self::VISIBILITY_DRAFT => pht('Draft'), + ); + return idx($map, $status, pht('Unknown')); + } + +} diff --git a/src/applications/phame/controller/blog/PhameBlogFeedController.php b/src/applications/phame/controller/blog/PhameBlogFeedController.php index 0941555ed2..b74465ef5e 100644 --- a/src/applications/phame/controller/blog/PhameBlogFeedController.php +++ b/src/applications/phame/controller/blog/PhameBlogFeedController.php @@ -21,7 +21,7 @@ final class PhameBlogFeedController extends PhameBlogController { $posts = id(new PhamePostQuery()) ->setViewer($viewer) ->withBlogPHIDs(array($blog->getPHID())) - ->withVisibility(PhamePost::VISIBILITY_PUBLISHED) + ->withVisibility(PhameConstants::VISIBILITY_PUBLISHED) ->execute(); $blog_uri = PhabricatorEnv::getProductionURI( diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index 3de92906a8..44368b3207 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -45,25 +45,27 @@ final class PhamePostEditController extends PhamePostController { $post = PhamePost::initializePost($viewer, $blog); $cancel_uri = $this->getApplicationURI('/blog/view/'.$blog->getID().'/'); - $submit_button = pht('Save Draft'); - $page_title = pht('Create Post'); + $submit_button = pht('Create Post'); + $page_title = pht('Create Post'); } - $title = $post->getTitle(); - $phame_title = $post->getPhameTitle(); - $body = $post->getBody(); + $title = $post->getTitle(); + $phame_title = $post->getPhameTitle(); + $body = $post->getBody(); $comments_widget = $post->getCommentsWidget(); + $visibility = $post->getVisibility(); $e_title = true; $e_phame_title = true; $validation_exception = null; if ($request->isFormPost()) { - $title = $request->getStr('title'); - $phame_title = $request->getStr('phame_title'); - $phame_title = PhabricatorSlug::normalize($phame_title); - $body = $request->getStr('body'); + $title = $request->getStr('title'); + $phame_title = $request->getStr('phame_title'); + $phame_title = PhabricatorSlug::normalize($phame_title); + $body = $request->getStr('body'); $comments_widget = $request->getStr('comments_widget'); - $v_projects = $request->getArr('projects'); + $v_projects = $request->getArr('projects'); + $visibility = $request->getInt('visibility'); $xactions = array( id(new PhamePostTransaction()) @@ -75,6 +77,9 @@ final class PhamePostEditController extends PhamePostController { id(new PhamePostTransaction()) ->setTransactionType(PhamePostTransaction::TYPE_BODY) ->setNewValue($body), + id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) + ->setNewValue($visibility), id(new PhamePostTransaction()) ->setTransactionType(PhamePostTransaction::TYPE_COMMENTS_WIDGET) ->setNewValue($comments_widget), @@ -134,6 +139,12 @@ final class PhamePostEditController extends PhamePostController { 'with underscores for spaces. '. 'Formatting is enforced.')) ->setError($e_phame_title)) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Visibility')) + ->setName('visibility') + ->setvalue($visibility) + ->setOptions(PhameConstants::getPhamePostStatusMap())) ->appendChild( id(new PhabricatorRemarkupControl()) ->setLabel(pht('Body')) diff --git a/src/applications/phame/controller/post/PhamePostPublishController.php b/src/applications/phame/controller/post/PhamePostPublishController.php index 6ca8ff5d54..0d612d193e 100644 --- a/src/applications/phame/controller/post/PhamePostPublishController.php +++ b/src/applications/phame/controller/post/PhamePostPublishController.php @@ -21,9 +21,23 @@ final class PhamePostPublishController extends PhamePostController { $view_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/'); if ($request->isFormPost()) { - $post->setVisibility(PhamePost::VISIBILITY_PUBLISHED); - $post->setDatePublished(time()); - $post->save(); + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_TITLE) + ->setNewValue($post->getTitle()); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_PHAME_TITLE) + ->setNewValue($post->getPhameTitle()); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) + ->setNewValue(PhameConstants::VISIBILITY_PUBLISHED); + + id(new PhamePostEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($post, $xactions); return id(new AphrontRedirectResponse())->setURI($view_uri); } diff --git a/src/applications/phame/controller/post/PhamePostUnpublishController.php b/src/applications/phame/controller/post/PhamePostUnpublishController.php index 80a320344d..8e95cfe75b 100644 --- a/src/applications/phame/controller/post/PhamePostUnpublishController.php +++ b/src/applications/phame/controller/post/PhamePostUnpublishController.php @@ -19,9 +19,23 @@ final class PhamePostUnpublishController extends PhamePostController { } if ($request->isFormPost()) { - $post->setVisibility(PhamePost::VISIBILITY_DRAFT); - $post->setDatePublished(0); - $post->save(); + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_TITLE) + ->setNewValue($post->getTitle()); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_PHAME_TITLE) + ->setNewValue($post->getPhameTitle()); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) + ->setNewValue(PhameConstants::VISIBILITY_DRAFT); + + id(new PhamePostEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($post, $xactions); return id(new AphrontRedirectResponse()) ->setURI($this->getApplicationURI('/post/view/'.$post->getID().'/')); diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index ab4b1a1465..475b0c67c3 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -17,6 +17,7 @@ final class PhamePostEditor $types[] = PhamePostTransaction::TYPE_TITLE; $types[] = PhamePostTransaction::TYPE_PHAME_TITLE; $types[] = PhamePostTransaction::TYPE_BODY; + $types[] = PhamePostTransaction::TYPE_VISIBILITY; $types[] = PhamePostTransaction::TYPE_COMMENTS_WIDGET; return $types; @@ -33,6 +34,8 @@ final class PhamePostEditor return $object->getPhameTitle(); case PhamePostTransaction::TYPE_BODY: return $object->getBody(); + case PhamePostTransaction::TYPE_VISIBILITY: + return $object->getVisibility(); case PhamePostTransaction::TYPE_COMMENTS_WIDGET: return $object->getCommentsWidget(); } @@ -46,6 +49,7 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_TITLE: case PhamePostTransaction::TYPE_PHAME_TITLE: case PhamePostTransaction::TYPE_BODY: + case PhamePostTransaction::TYPE_VISIBILITY: case PhamePostTransaction::TYPE_COMMENTS_WIDGET: return $xaction->getNewValue(); } @@ -62,6 +66,13 @@ final class PhamePostEditor return $object->setPhameTitle($xaction->getNewValue()); case PhamePostTransaction::TYPE_BODY: return $object->setBody($xaction->getNewValue()); + case PhamePostTransaction::TYPE_VISIBILITY: + if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) { + $object->setDatePublished(time()); + } else { + $object->setDatePublished(0); + } + return $object->setVisibility($xaction->getNewValue()); case PhamePostTransaction::TYPE_COMMENTS_WIDGET: return $object->setCommentsWidget($xaction->getNewValue()); } @@ -77,6 +88,7 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_TITLE: case PhamePostTransaction::TYPE_PHAME_TITLE: case PhamePostTransaction::TYPE_BODY: + case PhamePostTransaction::TYPE_VISIBILITY: case PhamePostTransaction::TYPE_COMMENTS_WIDGET: return; } diff --git a/src/applications/phame/query/PhamePostSearchEngine.php b/src/applications/phame/query/PhamePostSearchEngine.php index d832b08192..84fec54df0 100644 --- a/src/applications/phame/query/PhamePostSearchEngine.php +++ b/src/applications/phame/query/PhamePostSearchEngine.php @@ -32,8 +32,8 @@ final class PhamePostSearchEngine ->setLabel(pht('Visibility')) ->setOptions(array( '' => pht('All'), - PhamePost::VISIBILITY_PUBLISHED => pht('Live'), - PhamePost::VISIBILITY_DRAFT => pht('Draft'), + PhameConstants::VISIBILITY_PUBLISHED => pht('Published'), + PhameConstants::VISIBILITY_DRAFT => pht('Draft'), )), ); } @@ -45,7 +45,7 @@ final class PhamePostSearchEngine protected function getBuiltinQueryNames() { $names = array( 'all' => pht('All Posts'), - 'live' => pht('Live Posts'), + 'live' => pht('Published Posts'), 'draft' => pht('Draft Posts'), ); return $names; @@ -60,10 +60,10 @@ final class PhamePostSearchEngine return $query; case 'live': return $query->setParameter( - 'visibility', PhamePost::VISIBILITY_PUBLISHED); + 'visibility', PhameConstants::VISIBILITY_PUBLISHED); case 'draft': return $query->setParameter( - 'visibility', PhamePost::VISIBILITY_DRAFT); + 'visibility', PhameConstants::VISIBILITY_DRAFT); } return parent::buildSavedQueryFromBuiltin($query_key); diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index eecb27b566..ef39d20544 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -13,9 +13,6 @@ final class PhamePost extends PhameDAO const MARKUP_FIELD_BODY = 'markup:body'; const MARKUP_FIELD_SUMMARY = 'markup:summary'; - const VISIBILITY_DRAFT = 0; - const VISIBILITY_PUBLISHED = 1; - protected $bloggerPHID; protected $title; protected $phameTitle; @@ -37,7 +34,7 @@ final class PhamePost extends PhameDAO ->setBlogPHID($blog->getPHID()) ->setBlog($blog) ->setDatePublished(0) - ->setVisibility(self::VISIBILITY_DRAFT); + ->setVisibility(PhameConstants::VISIBILITY_PUBLISHED); return $post; } @@ -66,7 +63,7 @@ final class PhamePost extends PhameDAO } public function isDraft() { - return $this->getVisibility() == self::VISIBILITY_DRAFT; + return $this->getVisibility() == PhameConstants::VISIBILITY_DRAFT; } public function getHumanName() { @@ -165,14 +162,6 @@ final class PhamePost extends PhameDAO ); } - public static function getVisibilityOptionsForSelect() { - return array( - self::VISIBILITY_DRAFT => pht('Draft: visible only to me.'), - self::VISIBILITY_PUBLISHED => pht( - 'Published: visible to the whole world.'), - ); - } - public function getCommentsWidgetOptionsForSelect() { $current = $this->getCommentsWidget(); $options = array(); diff --git a/src/applications/phame/storage/PhamePostTransaction.php b/src/applications/phame/storage/PhamePostTransaction.php index 38910c9845..f1b2d22583 100644 --- a/src/applications/phame/storage/PhamePostTransaction.php +++ b/src/applications/phame/storage/PhamePostTransaction.php @@ -6,6 +6,7 @@ final class PhamePostTransaction const TYPE_TITLE = 'phame.post.title'; const TYPE_PHAME_TITLE = 'phame.post.phame.title'; const TYPE_BODY = 'phame.post.body'; + const TYPE_VISIBILITY = 'phame.post.visibility'; const TYPE_COMMENTS_WIDGET = 'phame.post.comments.widget'; const MAILTAG_CONTENT = 'phame-post-content'; @@ -54,6 +55,7 @@ final class PhamePostTransaction break; case self::TYPE_PHAME_TITLE: case self::TYPE_BODY: + case self::TYPE_VISIBILITY: case self::TYPE_COMMENTS_WIDGET: return 'fa-pencil'; break; @@ -108,6 +110,17 @@ final class PhamePostTransaction '%s updated the blog post.', $this->renderHandleLink($author_phid)); break; + case self::TYPE_VISIBILITY: + if ($new == PhameConstants::VISIBILITY_DRAFT) { + return pht( + '%s marked this post as a draft.', + $this->renderHandleLink($author_phid)); + } else { + return pht( + '%s published this post.', + $this->renderHandleLink($author_phid)); + } + break; case self::TYPE_PHAME_TITLE: return pht( '%s updated the post\'s Phame title to "%s".', @@ -153,6 +166,19 @@ final class PhamePostTransaction $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; + case self::TYPE_VISIBILITY: + if ($new == PhameConstants::VISIBILITY_DRAFT) { + return pht( + '%s marked %s as a draft.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } else { + return pht( + '%s published %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; case self::TYPE_PHAME_TITLE: return pht( '%s updated the Phame title for %s.', @@ -171,19 +197,32 @@ final class PhamePostTransaction } public function getBodyForFeed(PhabricatorFeedStory $story) { - $new = $this->getNewValue(); - - $body = null; - + $text = null; switch ($this->getTransactionType()) { case self::TYPE_TITLE: + if ($this->getOldValue() === null) { + $post = $story->getPrimaryObject(); + $text = $post->getBody(); + } + break; + case self::TYPE_VISIBILITY: + if ($this->getNewValue() == PhameConstants::VISIBILITY_PUBLISHED) { + $post = $story->getPrimaryObject(); + $text = $post->getBody(); + } + break; case self::TYPE_BODY: - return phutil_escape_html_newlines( - id(new PhutilUTF8StringTruncator()) - ->setMaximumGlyphs(128) - ->truncateString($new)); + $text = $this->getNewValue(); break; } + + if (strlen($text)) { + return phutil_escape_html_newlines( + id(new PhutilUTF8StringTruncator()) + ->setMaximumGlyphs(128) + ->truncateString($text)); + } + return parent::getBodyForFeed($story); } @@ -201,7 +240,6 @@ final class PhamePostTransaction return parent::getColor(); } - public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_BODY: From 28b8c8e212d4a6f1ee44942ce104dbfc04651179 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Sun, 5 Jul 2015 10:24:51 -0700 Subject: [PATCH 02/49] HTML emails for Calendar event description changes should respect remarkup rules Summary: Ref T7964, HTML emails for Calendar event description changes should respect remarkup rules Test Plan: Create event and edit description, check that email has a correctly formatted remarkup description section. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley, Korvin Maniphest Tasks: T7964 Differential Revision: https://secure.phabricator.com/D13554 --- .../editor/PhabricatorCalendarEventEditor.php | 4 ++-- .../view/PhabricatorMetaMTAMailBody.php | 21 +++++++++---------- .../phortune/editor/PhortuneCartEditor.php | 2 +- ...habricatorApplicationTransactionEditor.php | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php index d01946b370..dd5b361f71 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -436,9 +436,9 @@ final class PhabricatorCalendarEventEditor $body = parent::buildMailBody($object, $xactions); if (strlen($description)) { - $body->addTextSection( + $body->addRemarkupSection( pht('EVENT DESCRIPTION'), - $object->getDescription()); + $description); } $body->addLinkSection( diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php index 18d827f4c0..743082f47f 100644 --- a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php +++ b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php @@ -43,16 +43,16 @@ final class PhabricatorMetaMTAMailBody extends Phobject { return $this; } - public function addRemarkupSection($text) { + public function addRemarkupSection($header, $text) { try { $engine = PhabricatorMarkupEngine::newMarkupEngine(array()); $engine->setConfig('viewer', $this->getViewer()); $engine->setMode(PhutilRemarkupEngine::MODE_TEXT); $styled_text = $engine->markupText($text); - $this->sections[] = $styled_text; + $this->addPlaintextSection($header, $styled_text); } catch (Exception $ex) { phlog($ex); - $this->sections[] = $text; + $this->addTextSection($header, $text); } try { @@ -63,14 +63,10 @@ final class PhabricatorMetaMTAMailBody extends Phobject { 'uri.base', PhabricatorEnv::getProductionURI('/')); $html = $mail_engine->markupText($text); - $this->htmlSections[] = $html; + $this->addHTMLSection($header, $html); } catch (Exception $ex) { phlog($ex); - $this->htmlSections[] = phutil_escape_html_newlines( - phutil_tag( - 'div', - array(), - $text)); + $this->addHTMLSection($header, $text); } return $this; @@ -121,12 +117,16 @@ final class PhabricatorMetaMTAMailBody extends Phobject { } public function addHTMLSection($header, $html_fragment) { + if ($header !== null) { + $header = phutil_tag('strong', array(), $header); + } + $this->htmlSections[] = array( phutil_tag( 'div', array(), array( - phutil_tag('strong', array(), $header), + $header, phutil_tag('div', array(), $html_fragment), )), ); @@ -212,5 +212,4 @@ final class PhabricatorMetaMTAMailBody extends Phobject { private function indent($text) { return rtrim(" ".str_replace("\n", "\n ", $text)); } - } diff --git a/src/applications/phortune/editor/PhortuneCartEditor.php b/src/applications/phortune/editor/PhortuneCartEditor.php index 02f9b729de..5196e12429 100644 --- a/src/applications/phortune/editor/PhortuneCartEditor.php +++ b/src/applications/phortune/editor/PhortuneCartEditor.php @@ -145,7 +145,7 @@ final class PhortuneCartEditor "%s", $issues); - $body->addRemarkupSection($overview); + $body->addRemarkupSection(null, $overview); $body->addLinkSection( pht('PAY NOW'), diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 7d05d5fda4..a83ea2ba22 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -2668,7 +2668,7 @@ abstract class PhabricatorApplicationTransactionEditor $body->addRawSection(implode("\n", $headers)); foreach ($comments as $comment) { - $body->addRemarkupSection($comment); + $body->addRemarkupSection(null, $comment); } } From 6dda67702a74ab876ad44f3c1135d3eb4f2c28c8 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Thu, 25 Jun 2015 14:10:19 -0700 Subject: [PATCH 03/49] Starting the Calendar user guide Summary: Ref T7951, Starting the Calendar user guide Test Plan: Go to {nav Diviner > Phabricator User Docs > Calendar User Guide}, read about how fabulous the Calendar application is. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: epriestley, Korvin Maniphest Tasks: T7951 Differential Revision: https://secure.phabricator.com/D13496 --- .../PhabricatorCalendarApplication.php | 9 ++ src/docs/user/userguide/calendar.diviner | 83 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/docs/user/userguide/calendar.diviner diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index b5bdfaa1eb..0197ec8f02 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -68,6 +68,15 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication { ); } + public function getHelpDocumentationArticles(PhabricatorUser $viewer) { + return array( + array( + 'name' => pht('Calendar User Guide'), + 'href' => PhabricatorEnv::getDoclink('Calendar User Guide'), + ), + ); + } + public function getQuickCreateItems(PhabricatorUser $viewer) { $items = array(); diff --git a/src/docs/user/userguide/calendar.diviner b/src/docs/user/userguide/calendar.diviner new file mode 100644 index 0000000000..e6f9bd4b2c --- /dev/null +++ b/src/docs/user/userguide/calendar.diviner @@ -0,0 +1,83 @@ +@title Calendar User Guide +@group userguide + +Guide to the Calendar application. + +Overview +======== + +IMPORTANT: Calendar is a prototype application. See +@{article:User Guide: Prototype Applications}. + +The Calendar application is a tool that allows users to schedule group events +and share personal plans. + +There are several kinds of events you can create: + +- Regular events such as a one-time meeting or a personal appointment. +- All day events such as a company-wide holiday or a vacation. +- Recurring events, which can be regular or all day, such as a weekly 1-1 or + a birthday. + + +Editing Events +============== + +All fields of basic and all day events can be edited after the event has been +created. + +Every instance of a recurring event has an index that maintains its place in +the sequence order. Before an instance of a recurring event is edited, it is +considered a ghost event, or a placeholder. This means that there is no +database entry for that instance. Rather, when querying for events, if a +recurring series of events overlaps with the query range, instance +placeholders of that recurring event are generated and are displayed for +that range. If a placeholder instance of a recurring event is edited, a real +entry in the database is created and all changes are saved. When that +instance falls within a query range, the real instance event replaces the +old placeholder instance. + +To prevent disordering of the recurring sequence of events, parent recurring +events do not allow editing of date-related fields like recurrence frequency +and recurrence start and end dates. If all instances of the recurring event +need to be rescheduled, users are encouraged to cancel a recurring event and +create a new recurring event with the revised date and time. + + +Cancelling Events +================= + +Cancelling basic events will hide that event from most of the builtin Calendar +queries, unless the query specifies to display cancelled events. + +There are two ways to cancel an instance of a recurring event. + +- Cancel an instance of a recurring event. +- Cancel the entire series of a recurring event. + +Cancelling a placeholder instance of a recurring event will create a real +cancelled event that will replace the placeholder instance. Consequently, +the cancellation status of that instance of the recurring event will +persist if the parent event is cancelled and subsequently reinstated. + +When an entire series of a recurring event is cancelled, all the placeholder +and real instances are also cancelled. An entire series can similarly be +reinstated, but it is currently not possible to reinstate an instance of a +cancelled recurring event series. To reinstate that instance, the entire +series must be reinstated. If an instance of a recurring event has been +cancelled, then the entire recurring event series is also cancelled, +reinstating the series will not reinstate the previously cancelled instances +of that event. + + +Commenting On Recurring Events +============================== + +If a placeholder instance of a recurring event has not been converted to a +real instance of the series as a result of editing or cancelling, commenting on +that placeholder instance does not currently save a draft for that instance +only. The draft is saved for the recurring event parent, so the parent +recurring event and all placeholder instances will show that draft. When a +comment is actually added to a placeholder instance, the instance is converted +to a recurrence exception, and the comment will only appear on that instance +of the recurring event. From 97d74db98b8c00863d7cdf5f4f1b559d56134f8e Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 7 Nov 2015 08:41:08 -0800 Subject: [PATCH 04/49] Add PhamePost body content to emails Summary: Sends out the body of the post along with the details. Test Plan: Write a new post, see body in email. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9360 Differential Revision: https://secure.phabricator.com/D14431 --- src/applications/phame/editor/PhamePostEditor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 475b0c67c3..4cf0a0c60d 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -209,6 +209,7 @@ final class PhamePostEditor $body = parent::buildMailBody($object, $xactions); + $body->addRemarkupSection(null, $object->getBody()); $body->addLinkSection( pht('POST DETAIL'), PhabricatorEnv::getProductionURI($object->getViewURI())); From 3dd2e1fc6d6f3cde2a8917f8f288047f62baf181 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Sun, 8 Nov 2015 03:25:21 +0000 Subject: [PATCH 05/49] Scuttle Workboards if Maniphest is not installed Summary: fix T9718. Test Plan: view project page when maniphest is and isn't. Look for Workboards. Reviewers: #blessed_reviewers, chad Reviewed By: #blessed_reviewers, chad Subscribers: epriestley Maniphest Tasks: T9718 Differential Revision: https://secure.phabricator.com/D14438 --- .../project/controller/PhabricatorProjectController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php index a6901a7222..ac406412ad 100644 --- a/src/applications/project/controller/PhabricatorProjectController.php +++ b/src/applications/project/controller/PhabricatorProjectController.php @@ -70,11 +70,11 @@ abstract class PhabricatorProjectController extends PhabricatorController { $nav->setIconNav(true); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->addIcon("profile/{$id}/", $name, null, $picture); - $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon); $class = 'PhabricatorManiphestApplication'; if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { $phid = $project->getPHID(); + $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon); $query_uri = urisprintf( '/maniphest/?statuses=open()&projects=%s#R', $phid); From 80327a550ab1da0476cdbcb7765a512e4bb46e38 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 7 Nov 2015 18:24:06 -0800 Subject: [PATCH 06/49] Fix PropertyList background color in PHUIDocumentView Summary: Overzealous nuking. Test Plan: Test Phriction, Phame, Diviner. All show property lists correctly. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14439 --- resources/celerity/map.php | 4 ++-- webroot/rsrc/css/phui/phui-document.css | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 91b57e9f5d..8f8209b309 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -127,7 +127,7 @@ return array( 'rsrc/css/phui/phui-button.css' => '16020a60', 'rsrc/css/phui/phui-crumbs-view.css' => 'd842f867', 'rsrc/css/phui/phui-document-pro.css' => '4f2b42e3', - 'rsrc/css/phui/phui-document.css' => '9fa715d2', + 'rsrc/css/phui/phui-document.css' => 'f841ad0a', 'rsrc/css/phui/phui-feed-story.css' => 'b7b26d23', 'rsrc/css/phui/phui-fontkit.css' => 'c9d63950', 'rsrc/css/phui/phui-form-view.css' => '621b21c5', @@ -780,7 +780,7 @@ return array( 'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-month-css' => '476be7e0', 'phui-crumbs-view-css' => 'd842f867', - 'phui-document-view-css' => '9fa715d2', + 'phui-document-view-css' => 'f841ad0a', 'phui-document-view-pro-css' => '4f2b42e3', 'phui-feed-story-css' => 'b7b26d23', 'phui-font-icon-base-css' => 'ecbbb4c2', diff --git a/webroot/rsrc/css/phui/phui-document.css b/webroot/rsrc/css/phui/phui-document.css index c13d99930c..eaf90dc468 100644 --- a/webroot/rsrc/css/phui/phui-document.css +++ b/webroot/rsrc/css/phui/phui-document.css @@ -89,7 +89,8 @@ } .phui-document-content .phui-property-list-container { - border-color: {$thinblueborder}; + border-bottom: 1px solid {$thinblueborder}; + background-color: {$lightgreybackground}; } .legalpad .phui-document-content .phui-property-list-view { From 37df419266d452961b641cdaf02f273be2198a3a Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 7 Nov 2015 11:57:34 -0800 Subject: [PATCH 07/49] Add Can Create Policy Capability to Phame Blogs Summary: Larger (open) installs may want to restrict Blog to formal entities, like with Phriction. Test Plan: Set policy to administrators, have notchad try to create a blog. See error. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14433 --- resources/celerity/map.php | 6 +++--- src/__phutil_library_map__.php | 2 ++ .../application/PhabricatorPhameApplication.php | 9 +++++++++ .../capability/PhameBlogCreateCapability.php | 16 ++++++++++++++++ .../phame/controller/PhameController.php | 13 ++++++++++--- .../controller/blog/PhameBlogEditController.php | 3 +++ webroot/rsrc/css/phui/phui-crumbs-view.css | 3 ++- 7 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 src/applications/phame/capability/PhameBlogCreateCapability.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 8f8209b309..1ca4df907d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => 'e94665e4', + 'core.pkg.css' => 'e4f1ea81', 'core.pkg.js' => '47dc9ebb', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -125,7 +125,7 @@ return array( 'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-box.css' => 'a5bb366d', 'rsrc/css/phui/phui-button.css' => '16020a60', - 'rsrc/css/phui/phui-crumbs-view.css' => 'd842f867', + 'rsrc/css/phui/phui-crumbs-view.css' => '414406b5', 'rsrc/css/phui/phui-document-pro.css' => '4f2b42e3', 'rsrc/css/phui/phui-document.css' => 'f841ad0a', 'rsrc/css/phui/phui-feed-story.css' => 'b7b26d23', @@ -779,7 +779,7 @@ return array( 'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-month-css' => '476be7e0', - 'phui-crumbs-view-css' => 'd842f867', + 'phui-crumbs-view-css' => '414406b5', 'phui-document-view-css' => 'f841ad0a', 'phui-document-view-pro-css' => '4f2b42e3', 'phui-feed-story-css' => 'b7b26d23', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fa57f18743..65fab9c53b 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3248,6 +3248,7 @@ phutil_register_library_map(array( 'PhameBasicTemplateBlogSkin' => 'applications/phame/skins/PhameBasicTemplateBlogSkin.php', 'PhameBlog' => 'applications/phame/storage/PhameBlog.php', 'PhameBlogController' => 'applications/phame/controller/blog/PhameBlogController.php', + 'PhameBlogCreateCapability' => 'applications/phame/capability/PhameBlogCreateCapability.php', 'PhameBlogDeleteController' => 'applications/phame/controller/blog/PhameBlogDeleteController.php', 'PhameBlogEditController' => 'applications/phame/controller/blog/PhameBlogEditController.php', 'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php', @@ -7512,6 +7513,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionInterface', ), 'PhameBlogController' => 'PhameController', + 'PhameBlogCreateCapability' => 'PhabricatorPolicyCapability', 'PhameBlogDeleteController' => 'PhameBlogController', 'PhameBlogEditController' => 'PhameBlogController', 'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor', diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php index ec98087adf..f4636d758c 100644 --- a/src/applications/phame/application/PhabricatorPhameApplication.php +++ b/src/applications/phame/application/PhabricatorPhameApplication.php @@ -102,4 +102,13 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { ); } + protected function getCustomCapabilities() { + return array( + PhameBlogCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for blogs.'), + ), + ); + } + } diff --git a/src/applications/phame/capability/PhameBlogCreateCapability.php b/src/applications/phame/capability/PhameBlogCreateCapability.php new file mode 100644 index 0000000000..e86f2327ab --- /dev/null +++ b/src/applications/phame/capability/PhameBlogCreateCapability.php @@ -0,0 +1,16 @@ +hasApplicationCapability( + PhameBlogCreateCapability::CAPABILITY); + $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('New Blog')) - ->setHref($this->getApplicationURI('/blog/new')) - ->setIcon('fa-plus-square')); + ->setHref($this->getApplicationURI('/blog/new/')) + ->setIcon('fa-plus-square') + ->setDisabled(!$can_create) + ->setWorkflow(!$can_create)); + $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('New Post')) - ->setHref($this->getApplicationURI('/post/new')) + ->setHref($this->getApplicationURI('/post/new/')) ->setIcon('fa-pencil')); return $crumbs; } diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index 24c5681583..659f33ae3d 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -30,6 +30,9 @@ final class PhameBlogEditController $v_projects = array_reverse($v_projects); } else { + $this->requireApplicationCapability( + PhameBlogCreateCapability::CAPABILITY); + $blog = PhameBlog::initializeNewBlog($viewer); $submit_button = pht('Create Blog'); diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index 2f38e0ca47..71113dadc9 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -25,7 +25,8 @@ padding-right: 0; } -.phui-crumbs-view a.phui-crumbs-action-disabled { +.phui-crumbs-view a.phui-crumbs-action-disabled, +.phui-crumbs-view a.phui-crumbs-action-disabled .phui-icon-view { color: {$lightgreytext}; } From 152ddf57092e3c73d9dcc60f57cdedb1625b679d Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Nov 2015 05:36:42 -0800 Subject: [PATCH 08/49] Use unicode mode when tokenizing strings like user realnames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Fixes T9732. We currently tokenize strings (like user realnames) in the default non-unicode mode, which can cause patterns like `\s` to work incorrectly. Use `/u` to use unicode-aware tokenization instead. Test Plan: The behavior of "\s" depends upon environmental settings like LC_ALL. With LC_ALL set to "C", `\xA0` is not considered a whitespace character. With LC_ALL set to "en_US", it is: ``` $ php -r 'setlocale(LC_ALL, "C"); echo count(preg_split("/\s/", "\xE5\xBF\xA0")) . "\n";' 1 $ php -r 'setlocale(LC_ALL, "en_US"); echo count(preg_split("/\s/", "\xE5\xBF\xA0")) . "\n";' 2 ``` To reproduce the original issue, I added an explicit: ``` setlocale(LC_ALL, "en_US"); ``` ...call before the `preg_split()` call. This caused "忠" to be improperly split. I then added "/u", and observed proper tokenization. Reviewers: chad Reviewed By: chad Subscribers: qiu8310 Maniphest Tasks: T9732 Differential Revision: https://secure.phabricator.com/D14441 --- .../typeahead/datasource/PhabricatorTypeaheadDatasource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php index 1514f46d5b..66d8357341 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -107,7 +107,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { return array(); } - $tokens = preg_split('/\s+|[-\[\]]/', $string); + $tokens = preg_split('/\s+|[-\[\]]/u', $string); return array_unique($tokens); } From c86a514f8478d0e04116ae232210aca0f84782ec Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 7 Nov 2015 11:29:20 -0800 Subject: [PATCH 09/49] Add Subscribers to Phame Blogs / Posts Summary: Fixes T9051, adds ability to edit blogs and posts and manually add subscribers. Also fixed bug granting tokens to posts. Test Plan: Create a new blog, subcribe chad and notchad. Write a post, both are notified. Award token for hard work. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9051 Differential Revision: https://secure.phabricator.com/D14432 --- .../blog/PhameBlogEditController.php | 40 +++++++++++++------ .../post/PhamePostEditController.php | 15 +++++++ .../post/PhamePostPublishController.php | 6 --- .../post/PhamePostUnpublishController.php | 6 --- .../phame/editor/PhameBlogEditor.php | 4 ++ .../phame/editor/PhamePostEditor.php | 8 +++- 6 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index 659f33ae3d..b59e5e01a2 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -28,6 +28,8 @@ final class PhameBlogEditController $blog->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $v_projects = array_reverse($v_projects); + $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID( + $blog->getPHID()); } else { $this->requireApplicationCapability( @@ -39,28 +41,30 @@ final class PhameBlogEditController $page_title = pht('Create Blog'); $cancel_uri = $this->getApplicationURI(); $v_projects = array(); + $v_cc = array(); } - $name = $blog->getName(); - $description = $blog->getDescription(); + $name = $blog->getName(); + $description = $blog->getDescription(); $custom_domain = $blog->getDomain(); - $skin = $blog->getSkin(); - $can_view = $blog->getViewPolicy(); - $can_edit = $blog->getEditPolicy(); - $can_join = $blog->getJoinPolicy(); + $skin = $blog->getSkin(); + $can_view = $blog->getViewPolicy(); + $can_edit = $blog->getEditPolicy(); + $can_join = $blog->getJoinPolicy(); $e_name = true; $e_custom_domain = null; $e_view_policy = null; $validation_exception = null; if ($request->isFormPost()) { - $name = $request->getStr('name'); - $description = $request->getStr('description'); + $name = $request->getStr('name'); + $description = $request->getStr('description'); $custom_domain = nonempty($request->getStr('custom_domain'), null); - $skin = $request->getStr('skin'); - $can_view = $request->getStr('can_view'); - $can_edit = $request->getStr('can_edit'); - $can_join = $request->getStr('can_join'); - $v_projects = $request->getArr('projects'); + $skin = $request->getStr('skin'); + $can_view = $request->getStr('can_view'); + $can_edit = $request->getStr('can_edit'); + $can_join = $request->getStr('can_join'); + $v_projects = $request->getArr('projects'); + $v_cc = $request->getArr('cc'); $xactions = array( id(new PhameBlogTransaction()) @@ -84,6 +88,9 @@ final class PhameBlogEditController id(new PhameBlogTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY) ->setNewValue($can_join), + id(new PhameBlogTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) + ->setNewValue(array('=' => $v_cc)), ); $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; @@ -139,6 +146,13 @@ final class PhameBlogEditController ->setID('blog-description') ->setUser($viewer) ->setDisableMacros(true)) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Subscribers')) + ->setName('cc') + ->setValue($v_cc) + ->setUser($viewer) + ->setDatasource(new PhabricatorMetaMTAMailableDatasource())) ->appendChild( id(new AphrontFormPolicyControl()) ->setUser($viewer) diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index 44368b3207..99e6df390e 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -27,6 +27,8 @@ final class PhamePostEditController extends PhamePostController { $post->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $v_projects = array_reverse($v_projects); + $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID( + $post->getPHID()); } else { $blog = id(new PhameBlogQuery()) ->setViewer($viewer) @@ -41,6 +43,7 @@ final class PhamePostEditController extends PhamePostController { return new Aphront404Response(); } $v_projects = array(); + $v_cc = array(); $post = PhamePost::initializePost($viewer, $blog); $cancel_uri = $this->getApplicationURI('/blog/view/'.$blog->getID().'/'); @@ -65,6 +68,7 @@ final class PhamePostEditController extends PhamePostController { $body = $request->getStr('body'); $comments_widget = $request->getStr('comments_widget'); $v_projects = $request->getArr('projects'); + $v_cc = $request->getArr('cc'); $visibility = $request->getInt('visibility'); $xactions = array( @@ -83,6 +87,10 @@ final class PhamePostEditController extends PhamePostController { id(new PhamePostTransaction()) ->setTransactionType(PhamePostTransaction::TYPE_COMMENTS_WIDGET) ->setNewValue($comments_widget), + id(new PhamePostTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) + ->setNewValue(array('=' => $v_cc)), + ); $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; @@ -154,6 +162,13 @@ final class PhamePostEditController extends PhamePostController { ->setID('post-body') ->setUser($viewer) ->setDisableMacros(true)) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Subscribers')) + ->setName('cc') + ->setValue($v_cc) + ->setUser($viewer) + ->setDatasource(new PhabricatorMetaMTAMailableDatasource())) ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Projects')) diff --git a/src/applications/phame/controller/post/PhamePostPublishController.php b/src/applications/phame/controller/post/PhamePostPublishController.php index 0d612d193e..9b593b1313 100644 --- a/src/applications/phame/controller/post/PhamePostPublishController.php +++ b/src/applications/phame/controller/post/PhamePostPublishController.php @@ -22,12 +22,6 @@ final class PhamePostPublishController extends PhamePostController { if ($request->isFormPost()) { $xactions = array(); - $xactions[] = id(new PhamePostTransaction()) - ->setTransactionType(PhamePostTransaction::TYPE_TITLE) - ->setNewValue($post->getTitle()); - $xactions[] = id(new PhamePostTransaction()) - ->setTransactionType(PhamePostTransaction::TYPE_PHAME_TITLE) - ->setNewValue($post->getPhameTitle()); $xactions[] = id(new PhamePostTransaction()) ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) ->setNewValue(PhameConstants::VISIBILITY_PUBLISHED); diff --git a/src/applications/phame/controller/post/PhamePostUnpublishController.php b/src/applications/phame/controller/post/PhamePostUnpublishController.php index 8e95cfe75b..ca09ef8879 100644 --- a/src/applications/phame/controller/post/PhamePostUnpublishController.php +++ b/src/applications/phame/controller/post/PhamePostUnpublishController.php @@ -20,12 +20,6 @@ final class PhamePostUnpublishController extends PhamePostController { if ($request->isFormPost()) { $xactions = array(); - $xactions[] = id(new PhamePostTransaction()) - ->setTransactionType(PhamePostTransaction::TYPE_TITLE) - ->setNewValue($post->getTitle()); - $xactions[] = id(new PhamePostTransaction()) - ->setTransactionType(PhamePostTransaction::TYPE_PHAME_TITLE) - ->setNewValue($post->getPhameTitle()); $xactions[] = id(new PhamePostTransaction()) ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) ->setNewValue(PhameConstants::VISIBILITY_DRAFT); diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index d665d92541..a44bec422b 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -94,6 +94,7 @@ final class PhameBlogEditor $errors = parent::validateTransaction($object, $type, $xactions); + switch ($type) { case PhameBlogTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( @@ -112,6 +113,9 @@ final class PhameBlogEditor } break; case PhameBlogTransaction::TYPE_DOMAIN: + if (!$xactions) { + continue; + } $custom_domain = last($xactions)->getNewValue(); if (empty($custom_domain)) { continue; diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 4cf0a0c60d..0009ccaea5 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -121,6 +121,9 @@ final class PhamePostEditor } break; case PhamePostTransaction::TYPE_PHAME_TITLE: + if (!$xactions) { + continue; + } $missing = $this->validateIsEmptyTextField( $object->getPhameTitle(), $xactions); @@ -183,8 +186,11 @@ final class PhamePostEditor $blog_phid = $object->getBlogPHID(); if ($blog_phid) { - $phids[] = PhabricatorSubscribersQuery::loadSubscribersForPHID( + $cc_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( $blog_phid); + foreach ($cc_phids as $cc) { + $phids[] = $cc; + } } return $phids; } From 2b41ed01c69221c99214a5c003685635dadc166d Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 8 Nov 2015 05:21:54 -0800 Subject: [PATCH 10/49] Fix no-op transaction error on `paste.create` Conduit API method Summary: Fixes T9735. I changed how the TYPE_LANGUAGE transction works a little but that accidentally tripped an error condition in `paste.create`. - Don't bail on no-effect transactions to `paste.create` (like not setting a language). - When a transaction type has no tailored UI message, make it easier to figure out which transaction is problematic. Test Plan: Ran `arc paste ...` locally. Got an error before the patch, clean paste creation afterward. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9735 Differential Revision: https://secure.phabricator.com/D14440 --- .../paste/conduit/PasteCreateConduitAPIMethod.php | 1 + .../storage/PhabricatorApplicationTransaction.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php index d5ce021f13..2dae0132d4 100644 --- a/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteCreateConduitAPIMethod.php @@ -60,6 +60,7 @@ final class PasteCreateConduitAPIMethod extends PasteConduitAPIMethod { $editor = id(new PhabricatorPasteEditor()) ->setActor($viewer) + ->setContinueOnNoEffect(true) ->setContentSourceFromConduitRequest($request); $xactions = $editor->applyTransactions($paste, $xactions); diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index a61de01978..9eb1609931 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -626,7 +626,9 @@ abstract class PhabricatorApplicationTransaction return pht('Edges already exist; transaction has no effect.'); } - return pht('Transaction has no effect.'); + return pht( + 'Transaction (of type "%s") has no effect.', + $this->getTransactionType()); } public function getTitle() { From c3ecea9788c1fdc2656b485e84ed933d08ae64d5 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sun, 8 Nov 2015 07:50:01 -0800 Subject: [PATCH 11/49] Add mail support to PhameBlog Summary: Add some mailkeys, allow feed stories to be published. Test Plan: New Blog, Edit Blog Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14434 --- .../20151107.phame.blog.mailkey.1.sql | 2 + .../20151107.phame.blog.mailkey.2.php | 18 ++++++ src/__phutil_library_map__.php | 4 ++ .../phame/editor/PhameBlogEditor.php | 55 ++++++++++++++++++- .../phame/mail/PhameBlogReplyHandler.php | 21 +++++++ .../phame/query/PhameBlogTransactionQuery.php | 10 ++++ src/applications/phame/storage/PhameBlog.php | 14 +++++ .../phame/storage/PhameBlogTransaction.php | 26 ++++++++- 8 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql create mode 100644 resources/sql/autopatches/20151107.phame.blog.mailkey.2.php create mode 100644 src/applications/phame/mail/PhameBlogReplyHandler.php create mode 100644 src/applications/phame/query/PhameBlogTransactionQuery.php diff --git a/resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql b/resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql new file mode 100644 index 0000000000..764f50766c --- /dev/null +++ b/resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_blog + ADD mailKey binary(20) NOT NULL; diff --git a/resources/sql/autopatches/20151107.phame.blog.mailkey.2.php b/resources/sql/autopatches/20151107.phame.blog.mailkey.2.php new file mode 100644 index 0000000000..c6f316aa47 --- /dev/null +++ b/resources/sql/autopatches/20151107.phame.blog.mailkey.2.php @@ -0,0 +1,18 @@ +establishConnection('w'); +$iterator = new LiskMigrationIterator($table); +foreach ($iterator as $blog) { + $id = $blog->getID(); + + echo pht('Adding mail key for Blog %d...', $id); + echo "\n"; + + queryfx( + $conn_w, + 'UPDATE %T SET mailKey = %s WHERE id = %d', + $table->getTableName(), + Filesystem::readRandomCharacters(20), + $id); +} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 65fab9c53b..b9b654a572 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3256,10 +3256,12 @@ phutil_register_library_map(array( 'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php', 'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php', 'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php', + 'PhameBlogReplyHandler' => 'applications/phame/mail/PhameBlogReplyHandler.php', 'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php', 'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php', 'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php', 'PhameBlogTransaction' => 'applications/phame/storage/PhameBlogTransaction.php', + 'PhameBlogTransactionQuery' => 'applications/phame/query/PhameBlogTransactionQuery.php', 'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php', 'PhameCelerityResources' => 'applications/phame/celerity/PhameCelerityResources.php', 'PhameConduitAPIMethod' => 'applications/phame/conduit/PhameConduitAPIMethod.php', @@ -7521,10 +7523,12 @@ phutil_register_library_map(array( 'PhameBlogListController' => 'PhameBlogController', 'PhameBlogLiveController' => 'PhameBlogController', 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhameBlogReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhameBlogSite' => 'PhameSite', 'PhameBlogSkin' => 'PhabricatorController', 'PhameBlogTransaction' => 'PhabricatorApplicationTransaction', + 'PhameBlogTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhameBlogViewController' => 'PhameBlogController', 'PhameCelerityResources' => 'CelerityResources', 'PhameConduitAPIMethod' => 'ConduitAPIMethod', diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index a44bec422b..7c793aff63 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -162,15 +162,66 @@ final class PhameBlogEditor protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { - return false; + return true; } protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { - return false; + return true; } + protected function getMailTo(PhabricatorLiskDAO $object) { + $phids = array(); + $phids[] = $this->requireActor()->getPHID(); + $phids[] = $object->getCreatorPHID(); + + return $phids; + } + + protected function buildMailTemplate(PhabricatorLiskDAO $object) { + $phid = $object->getPHID(); + $name = $object->getName(); + + return id(new PhabricatorMetaMTAMail()) + ->setSubject($name) + ->addHeader('Thread-Topic', $phid); + } + + protected function buildReplyHandler(PhabricatorLiskDAO $object) { + return id(new PhameBlogReplyHandler()) + ->setMailReceiver($object); + } + + protected function buildMailBody( + PhabricatorLiskDAO $object, + array $xactions) { + + $body = parent::buildMailBody($object, $xactions); + + $body->addLinkSection( + pht('BLOG DETAIL'), + PhabricatorEnv::getProductionURI($object->getViewURI())); + + return $body; + } + + public function getMailTagsMap() { + return array( + PhameBlogTransaction::MAILTAG_DETAILS => + pht("A blog's details change."), + PhameBlogTransaction::MAILTAG_SUBSCRIBERS => + pht("A blog's subscribers change."), + PhameBlogTransaction::MAILTAG_OTHER => + pht('Other blog activity not listed above occurs.'), + ); + } + + protected function getMailSubjectPrefix() { + return '[Phame]'; + } + + protected function supportsSearch() { return false; } diff --git a/src/applications/phame/mail/PhameBlogReplyHandler.php b/src/applications/phame/mail/PhameBlogReplyHandler.php new file mode 100644 index 0000000000..a6e245709e --- /dev/null +++ b/src/applications/phame/mail/PhameBlogReplyHandler.php @@ -0,0 +1,21 @@ + 'text64', 'description' => 'text', 'domain' => 'text128?', + 'mailKey' => 'bytes20', // T6203/NULLABILITY // These policies should always be non-null. @@ -55,6 +57,13 @@ final class PhameBlog extends PhameDAO ) + parent::getConfiguration(); } + public function save() { + if (!$this->getMailKey()) { + $this->setMailKey(Filesystem::readRandomCharacters(20)); + } + return parent::save(); + } + public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPhameBlogPHIDType::TYPECONST); @@ -214,6 +223,11 @@ final class PhameBlog extends PhameDAO return $base; } + public function getViewURI() { + $uri = '/phame/blog/view/'.$this->getID().'/'; + return PhabricatorEnv::getProductionURI($uri); + } + /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ diff --git a/src/applications/phame/storage/PhameBlogTransaction.php b/src/applications/phame/storage/PhameBlogTransaction.php index f1e558e240..0954e72a37 100644 --- a/src/applications/phame/storage/PhameBlogTransaction.php +++ b/src/applications/phame/storage/PhameBlogTransaction.php @@ -8,6 +8,10 @@ final class PhameBlogTransaction const TYPE_DOMAIN = 'phame.blog.domain'; const TYPE_SKIN = 'phame.blog.skin'; + const MAILTAG_DETAILS = 'phame-blog-details'; + const MAILTAG_SUBSCRIBERS = 'phame-blog-subscribers'; + const MAILTAG_OTHER = 'phame-blog-other'; + public function getApplicationName() { return 'phame'; } @@ -44,6 +48,26 @@ final class PhameBlogTransaction return parent::getIcon(); } + public function getMailTags() { + $tags = parent::getMailTags(); + + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_SUBSCRIBERS: + $tags[] = self::MAILTAG_SUBSCRIBERS; + break; + case self::TYPE_NAME: + case self::TYPE_DESCRIPTION: + case self::TYPE_DOMAIN: + case self::TYPE_SKIN: + $tags[] = self::MAILTAG_DETAILS; + break; + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + return $tags; + } + public function getTitle() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); @@ -53,7 +77,7 @@ final class PhameBlogTransaction $type = $this->getTransactionType(); switch ($type) { - case self:TYPE_NAME: + case self::TYPE_NAME: if ($old === null) { return pht( '%s created this blog.', From a2f909f0bd0a195e06730e955e4d5a29bb3ad61b Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Sun, 8 Nov 2015 21:54:47 +1100 Subject: [PATCH 12/49] Improve XHPAST handling of syntax errors Summary: Currently, a bunch of developers are using #xhpast for writing custom linter rules. As such, we end up with a fair few `XHPASTSyntaxErrorException` in our PHP error logs. I think that throwing an exception is not quite correct in this case because it is somewhat expected that invalid PHP may be entered. Instead, catch the exception and show the user a helpful message. Test Plan: This doesn't quite work yet... the stream and tree views render as blank but the exceptions still propogate to the error logs. Mostly, I'm not sure how the exception should be rendered for display. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14028 --- .../autopatches/20151108.xhpast.stderr.sql | 5 +++++ .../PhabricatorXHPASTViewController.php | 1 - .../PhabricatorXHPASTViewFrameController.php | 2 +- ...habricatorXHPASTViewFramesetController.php | 22 +++++++++---------- .../PhabricatorXHPASTViewPanelController.php | 8 +++---- .../PhabricatorXHPASTViewRunController.php | 20 ++++++++++------- .../PhabricatorXHPASTViewStreamController.php | 12 +++++++--- .../PhabricatorXHPASTViewTreeController.php | 13 +++++++---- .../PhabricatorXHPASTViewParseTree.php | 5 ++++- 9 files changed, 54 insertions(+), 34 deletions(-) create mode 100644 resources/sql/autopatches/20151108.xhpast.stderr.sql diff --git a/resources/sql/autopatches/20151108.xhpast.stderr.sql b/resources/sql/autopatches/20151108.xhpast.stderr.sql new file mode 100644 index 0000000000..1721505658 --- /dev/null +++ b/resources/sql/autopatches/20151108.xhpast.stderr.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_xhpastview.xhpastview_parsetree + ADD returnCode INT NOT NULL AFTER input; + +ALTER TABLE {$NAMESPACE}_xhpastview.xhpastview_parsetree + ADD stderr longtext NOT NULL AFTER stdout; diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewController.php index 7d3ccf8187..2ef34fb36f 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewController.php @@ -3,7 +3,6 @@ abstract class PhabricatorXHPASTViewController extends PhabricatorController { public function buildStandardPageResponse($view, array $data) { - $page = $this->buildStandardPageView(); $page->setApplicationName('XHPASTView'); diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php index 2c5a43687c..89f5b8fd7d 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php @@ -14,7 +14,7 @@ final class PhabricatorXHPASTViewFrameController phutil_tag( 'iframe', array( - 'src' => '/xhpast/frameset/'.$id.'/', + 'src' => "/xhpast/frameset/{$id}/", 'frameborder' => '0', 'style' => 'width: 100%; height: 800px;', '', diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php index de446b5e44..6f186fb3f8 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php @@ -10,17 +10,15 @@ final class PhabricatorXHPASTViewFramesetController public function handleRequest(AphrontRequest $request) { $id = $request->getURIData('id'); - $response = new AphrontWebpageResponse(); - $response->setFrameable(true); - $response->setContent(phutil_tag( - 'frameset', - array('cols' => '33%, 34%, 33%'), - array( - phutil_tag('frame', array('src' => "/xhpast/input/{$id}/")), - phutil_tag('frame', array('src' => "/xhpast/tree/{$id}/")), - phutil_tag('frame', array('src' => "/xhpast/stream/{$id}/")), - ))); - - return $response; + return id(new AphrontWebpageResponse()) + ->setFrameable(true) + ->setContent(phutil_tag( + 'frameset', + array('cols' => '33%, 34%, 33%'), + array( + phutil_tag('frame', array('src' => "/xhpast/input/{$id}/")), + phutil_tag('frame', array('src' => "/xhpast/tree/{$id}/")), + phutil_tag('frame', array('src' => "/xhpast/stream/{$id}/")), + ))); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php index 8f824dc03f..7238b36381 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php @@ -14,6 +14,7 @@ abstract class PhabricatorXHPASTViewPanelController $this->id = $data['id']; $this->storageTree = id(new PhabricatorXHPASTViewParseTree()) ->load($this->id); + if (!$this->storageTree) { throw new Exception(pht('No such AST!')); } @@ -65,10 +66,9 @@ li span { '', $content); - $response = new AphrontWebpageResponse(); - $response->setFrameable(true); - $response->setContent($content); - return $response; + return id(new AphrontWebpageResponse()) + ->setFrameable(true) + ->setContent($content); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php index dd9cf85433..dc2224d291 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php @@ -13,17 +13,21 @@ final class PhabricatorXHPASTViewRunController $resolved = $future->resolve(); // This is just to let it throw exceptions if stuff is broken. - $parse_tree = XHPASTTree::newFromDataAndResolvedExecFuture( - $source, - $resolved); + try { + XHPASTTree::newFromDataAndResolvedExecFuture($source, $resolved); + } catch (XHPASTSyntaxErrorException $ex) { + // This is possibly expected. + } list($err, $stdout, $stderr) = $resolved; - $storage_tree = new PhabricatorXHPASTViewParseTree(); - $storage_tree->setInput($source); - $storage_tree->setStdout($stdout); - $storage_tree->setAuthorPHID($viewer->getPHID()); - $storage_tree->save(); + $storage_tree = id(new PhabricatorXHPASTViewParseTree()) + ->setInput($source) + ->setReturnCode($err) + ->setStdout($stdout) + ->setStderr($stderr) + ->setAuthorPHID($viewer->getPHID()) + ->save(); return id(new AphrontRedirectResponse()) ->setURI('/xhpast/view/'.$storage_tree->getID().'/'); diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php index 3fe1046f10..f5cedf225a 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php @@ -6,11 +6,17 @@ final class PhabricatorXHPASTViewStreamController public function handleRequest(AphrontRequest $request) { $storage = $this->getStorageTree(); $input = $storage->getInput(); + $err = $storage->getReturnCode(); $stdout = $storage->getStdout(); + $stderr = $storage->getStderr(); - $tree = XHPASTTree::newFromDataAndResolvedExecFuture( - $input, - array(0, $stdout, '')); + try { + $tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $input, + array($err, $stdout, $stderr)); + } catch (XHPASTSyntaxErrorException $ex) { + return $this->buildXHPASTViewPanelResponse($ex->getMessage()); + } $tokens = array(); foreach ($tree->getRawTokenStream() as $id => $token) { diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php index 1b4eec6441..c15bdc2928 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php @@ -10,18 +10,23 @@ final class PhabricatorXHPASTViewTreeController public function handleRequest(AphrontRequest $request) { $storage = $this->getStorageTree(); $input = $storage->getInput(); + $err = $storage->getReturnCode(); $stdout = $storage->getStdout(); + $stderr = $storage->getStderr(); - $tree = XHPASTTree::newFromDataAndResolvedExecFuture( - $input, - array(0, $stdout, '')); + try { + $tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $input, + array($err, $stdout, $stderr)); + } catch (XHPASTSyntaxErrorException $ex) { + return $this->buildXHPASTViewPanelResponse($ex->getMessage()); + } $tree = phutil_tag('ul', array(), $this->buildTree($tree->getRootNode())); return $this->buildXHPASTViewPanelResponse($tree); } protected function buildTree($root) { - try { $name = $root->getTypeName(); $title = $root->getDescription(); diff --git a/src/applications/phpast/storage/PhabricatorXHPASTViewParseTree.php b/src/applications/phpast/storage/PhabricatorXHPASTViewParseTree.php index fa9adb62a2..d4432af496 100644 --- a/src/applications/phpast/storage/PhabricatorXHPASTViewParseTree.php +++ b/src/applications/phpast/storage/PhabricatorXHPASTViewParseTree.php @@ -3,16 +3,19 @@ final class PhabricatorXHPASTViewParseTree extends PhabricatorXHPASTViewDAO { protected $authorPHID; - protected $input; + protected $returnCode; protected $stdout; + protected $stderr; protected function getConfiguration() { return array( self::CONFIG_COLUMN_SCHEMA => array( 'authorPHID' => 'phid?', 'input' => 'text', + 'returnCode' => 'sint32', 'stdout' => 'text', + 'stderr' => 'text', ), ) + parent::getConfiguration(); } From df23d893f7caaa8d2b358422ce8bab35bff5de3c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 9 Nov 2015 08:52:44 -0800 Subject: [PATCH 13/49] Remove Join Policy from Phame Summary: Drops Join Policy, uses Edit Policy where needed. Allows anyone with Blog Edit permissions to post and edit any post on that blog. Fixes T5371 Test Plan: Draft Post as chad, see post, log in with notchad, edit that post and publish it. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T5371 Differential Revision: https://secure.phabricator.com/D14444 --- .../20151108.phame.blog.joinpolicy.sql | 2 ++ .../PhameCreatePostConduitAPIMethod.php | 3 ++- .../blog/PhameBlogEditController.php | 13 ----------- .../blog/PhameBlogViewController.php | 13 ++--------- .../post/PhamePostEditController.php | 2 +- .../post/PhamePostNewController.php | 6 ++--- .../post/PhamePostViewController.php | 2 ++ .../phame/editor/PhameBlogEditor.php | 1 - src/applications/phame/storage/PhameBlog.php | 23 ++----------------- src/applications/phame/storage/PhamePost.php | 13 +++++++---- 10 files changed, 22 insertions(+), 56 deletions(-) create mode 100644 resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql diff --git a/resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql b/resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql new file mode 100644 index 0000000000..54aea48bbf --- /dev/null +++ b/resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_blog + DROP joinPolicy; diff --git a/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php b/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php index 425bf17353..1f569d3a3b 100644 --- a/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameCreatePostConduitAPIMethod.php @@ -73,7 +73,8 @@ final class PhameCreatePostConduitAPIMethod extends PhameConduitAPIMethod { ->withPHIDs(array($blog_phid)) ->requireCapabilities( array( - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index b59e5e01a2..0c2bb324dc 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -49,7 +49,6 @@ final class PhameBlogEditController $skin = $blog->getSkin(); $can_view = $blog->getViewPolicy(); $can_edit = $blog->getEditPolicy(); - $can_join = $blog->getJoinPolicy(); $e_name = true; $e_custom_domain = null; @@ -62,7 +61,6 @@ final class PhameBlogEditController $skin = $request->getStr('skin'); $can_view = $request->getStr('can_view'); $can_edit = $request->getStr('can_edit'); - $can_join = $request->getStr('can_join'); $v_projects = $request->getArr('projects'); $v_cc = $request->getArr('cc'); @@ -85,9 +83,6 @@ final class PhameBlogEditController id(new PhameBlogTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setNewValue($can_edit), - id(new PhameBlogTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY) - ->setNewValue($can_join), id(new PhameBlogTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) ->setNewValue(array('=' => $v_cc)), @@ -170,14 +165,6 @@ final class PhameBlogEditController ->setPolicies($policies) ->setValue($can_edit) ->setName('can_edit')) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setUser($viewer) - ->setCapability(PhabricatorPolicyCapability::CAN_JOIN) - ->setPolicyObject($blog) - ->setPolicies($policies) - ->setValue($can_join) - ->setName('can_join')) ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Projects')) diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 03dfc8c1c6..249bd5506a 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -100,10 +100,6 @@ final class PhameBlogViewController extends PhameBlogController { pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); - $properties->addProperty( - pht('Joinable By'), - $descriptions[PhabricatorPolicyCapability::CAN_JOIN]); - $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer) ->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION) @@ -136,18 +132,13 @@ final class PhameBlogViewController extends PhameBlogController { $blog, PhabricatorPolicyCapability::CAN_EDIT); - $can_join = PhabricatorPolicyFilter::hasCapability( - $viewer, - $blog, - PhabricatorPolicyCapability::CAN_JOIN); - $actions->addAction( id(new PhabricatorActionView()) ->setIcon('fa-plus') ->setHref($this->getApplicationURI('post/edit/?blog='.$blog->getID())) ->setName(pht('Write Post')) - ->setDisabled(!$can_join) - ->setWorkflow(!$can_join)); + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); $actions->addAction( id(new PhabricatorActionView()) diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index 99e6df390e..55ce9e176f 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -36,7 +36,7 @@ final class PhamePostEditController extends PhamePostController { ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$blog) { diff --git a/src/applications/phame/controller/post/PhamePostNewController.php b/src/applications/phame/controller/post/PhamePostNewController.php index 7098401749..8dd2e0d3c7 100644 --- a/src/applications/phame/controller/post/PhamePostNewController.php +++ b/src/applications/phame/controller/post/PhamePostNewController.php @@ -30,7 +30,7 @@ final class PhamePostNewController extends PhamePostController { ->withIDs(array($request->getInt('blog'))) ->requireCapabilities( array( - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); @@ -52,7 +52,7 @@ final class PhamePostNewController extends PhamePostController { ->setViewer($viewer) ->requireCapabilities( array( - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_EDIT, )) ->execute(); @@ -65,7 +65,7 @@ final class PhamePostNewController extends PhamePostController { $notification = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NODATA) ->appendChild( - pht('You do not have permission to join any blogs. Create a blog '. + pht('You do not have permission to post to any blogs. Create a blog '. 'first, then you can post to it.')); } else { diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 3afdf5401d..ca48af1036 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -123,6 +123,7 @@ final class PhamePostViewController extends PhamePostController { id(new PhabricatorActionView()) ->setIcon('fa-eye') ->setHref($this->getApplicationURI('post/publish/'.$id.'/')) + ->setDisabled(!$can_edit) ->setName(pht('Preview / Publish'))); } else { $actions->addAction( @@ -130,6 +131,7 @@ final class PhamePostViewController extends PhamePostController { ->setIcon('fa-eye-slash') ->setHref($this->getApplicationURI('post/unpublish/'.$id.'/')) ->setName(pht('Unpublish')) + ->setDisabled(!$can_edit) ->setWorkflow(true)); } diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index 7c793aff63..712c4ddd5b 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -20,7 +20,6 @@ final class PhameBlogEditor $types[] = PhameBlogTransaction::TYPE_SKIN; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; - $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; return $types; } diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index fa9828e5f2..3082592a93 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -20,7 +20,6 @@ final class PhameBlog extends PhameDAO protected $creatorPHID; protected $viewPolicy; protected $editPolicy; - protected $joinPolicy; protected $mailKey; private static $requestBlog; @@ -39,7 +38,6 @@ final class PhameBlog extends PhameDAO // T6203/NULLABILITY // These policies should always be non-null. - 'joinPolicy' => 'policy?', 'editPolicy' => 'policy?', 'viewPolicy' => 'policy?', ), @@ -73,8 +71,7 @@ final class PhameBlog extends PhameDAO $blog = id(new PhameBlog()) ->setCreatorPHID($actor->getPHID()) ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER) - ->setJoinPolicy(PhabricatorPolicies::POLICY_USER); + ->setEditPolicy(PhabricatorPolicies::POLICY_USER); return $blog; } @@ -236,7 +233,6 @@ final class PhameBlog extends PhameDAO return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, - PhabricatorPolicyCapability::CAN_JOIN, ); } @@ -247,14 +243,11 @@ final class PhameBlog extends PhameDAO return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); - case PhabricatorPolicyCapability::CAN_JOIN: - return $this->getJoinPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { $can_edit = PhabricatorPolicyCapability::CAN_EDIT; - $can_join = PhabricatorPolicyCapability::CAN_JOIN; switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: @@ -262,15 +255,6 @@ final class PhameBlog extends PhameDAO if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { return true; } - if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_join)) { - return true; - } - break; - case PhabricatorPolicyCapability::CAN_JOIN: - // Users who can edit a blog can always post to it. - if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { - return true; - } break; } @@ -282,10 +266,7 @@ final class PhameBlog extends PhameDAO switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return pht( - 'Users who can edit or post on a blog can always view it.'); - case PhabricatorPolicyCapability::CAN_JOIN: - return pht( - 'Users who can edit a blog can always post on it.'); + 'Users who can edit a blog can always view it.'); } return null; diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index ef39d20544..76681f2f57 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -198,18 +198,21 @@ final class PhamePost extends PhameDAO case PhabricatorPolicyCapability::CAN_VIEW: if (!$this->isDraft() && $this->getBlog()) { return $this->getBlog()->getViewPolicy(); - } else { - return PhabricatorPolicies::POLICY_NOONE; + } else if ($this->getBlog()) { + return $this->getBlog()->getEditPolicy(); } break; case PhabricatorPolicyCapability::CAN_EDIT: - return PhabricatorPolicies::POLICY_NOONE; + if ($this->getBlog()) { + return $this->getBlog()->getEditPolicy(); + } else { + return PhabricatorPolicies::POLICY_NOONE; + } } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { - // A blog post's author can always view it, and is the only user allowed - // to edit it. + // A blog post's author can always view it. switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: From d8111f828f4d3c488abd1d25896dc682977203f2 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Mon, 9 Nov 2015 10:09:19 -0800 Subject: [PATCH 14/49] Allow a domain other than the install domain to serve as a short Phurl domain Summary: Ref T8995, config option for Phurl short domain to share shortened URL's Test Plan: - Configure Phurl short domain to something like "zz.us" - Navigate to `zz.us`; get 404 - Navigate to `zz.us/u/3` or `zz.us/u/alias` where `U3` is an existing Phurl; redirect to correct destination Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T8995 Differential Revision: https://secure.phabricator.com/D14447 --- src/__phutil_library_map__.php | 8 ++++ src/aphront/site/PhabricatorShortSite.php | 44 +++++++++++++++++++ .../option/PhabricatorPhurlConfigOptions.php | 35 +++++++++++++++ .../PhabricatorPhurlApplication.php | 7 +++ .../PhabricatorPhurlShortURLController.php | 19 ++++++++ ...bricatorPhurlShortURLDefaultController.php | 13 ++++++ .../PhabricatorPhurlURLAccessController.php | 5 ++- 7 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/aphront/site/PhabricatorShortSite.php create mode 100644 src/applications/config/option/PhabricatorPhurlConfigOptions.php create mode 100644 src/applications/phurl/controller/PhabricatorPhurlShortURLController.php create mode 100644 src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b9b654a572..a5b33bc14d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2641,11 +2641,14 @@ phutil_register_library_map(array( 'PhabricatorPhrictionApplication' => 'applications/phriction/application/PhabricatorPhrictionApplication.php', 'PhabricatorPhrictionConfigOptions' => 'applications/phriction/config/PhabricatorPhrictionConfigOptions.php', 'PhabricatorPhurlApplication' => 'applications/phurl/application/PhabricatorPhurlApplication.php', + 'PhabricatorPhurlConfigOptions' => 'applications/config/option/PhabricatorPhurlConfigOptions.php', 'PhabricatorPhurlController' => 'applications/phurl/controller/PhabricatorPhurlController.php', 'PhabricatorPhurlDAO' => 'applications/phurl/storage/PhabricatorPhurlDAO.php', 'PhabricatorPhurlLinkRemarkupRule' => 'applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php', 'PhabricatorPhurlRemarkupRule' => 'applications/phurl/remarkup/PhabricatorPhurlRemarkupRule.php', 'PhabricatorPhurlSchemaSpec' => 'applications/phurl/storage/PhabricatorPhurlSchemaSpec.php', + 'PhabricatorPhurlShortURLController' => 'applications/phurl/controller/PhabricatorPhurlShortURLController.php', + 'PhabricatorPhurlShortURLDefaultController' => 'applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php', 'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php', 'PhabricatorPhurlURLAccessController' => 'applications/phurl/controller/PhabricatorPhurlURLAccessController.php', 'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php', @@ -2954,6 +2957,7 @@ phutil_register_library_map(array( 'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php', 'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php', 'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php', + 'PhabricatorShortSite' => 'aphront/site/PhabricatorShortSite.php', 'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php', 'PhabricatorSite' => 'aphront/site/PhabricatorSite.php', 'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php', @@ -6774,11 +6778,14 @@ phutil_register_library_map(array( 'PhabricatorPhrictionApplication' => 'PhabricatorApplication', 'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPhurlApplication' => 'PhabricatorApplication', + 'PhabricatorPhurlConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPhurlController' => 'PhabricatorController', 'PhabricatorPhurlDAO' => 'PhabricatorLiskDAO', 'PhabricatorPhurlLinkRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorPhurlRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'PhabricatorPhurlSchemaSpec' => 'PhabricatorConfigSchemaSpec', + 'PhabricatorPhurlShortURLController' => 'PhabricatorPhurlController', + 'PhabricatorPhurlShortURLDefaultController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURL' => array( 'PhabricatorPhurlDAO', 'PhabricatorPolicyInterface', @@ -7167,6 +7174,7 @@ phutil_register_library_map(array( 'PhabricatorSetupIssue' => 'Phobject', 'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample', 'PhabricatorSetupIssueView' => 'AphrontView', + 'PhabricatorShortSite' => 'PhabricatorSite', 'PhabricatorSimpleEditType' => 'PhabricatorEditType', 'PhabricatorSite' => 'AphrontSite', 'PhabricatorSlowvoteApplication' => 'PhabricatorApplication', diff --git a/src/aphront/site/PhabricatorShortSite.php b/src/aphront/site/PhabricatorShortSite.php new file mode 100644 index 0000000000..d4c36aecbe --- /dev/null +++ b/src/aphront/site/PhabricatorShortSite.php @@ -0,0 +1,44 @@ +getHost(); + + $uri = PhabricatorEnv::getEnvConfig('phurl.short-uri'); + if (!strlen($uri)) { + return null; + } + + $phurl_installed = PhabricatorApplication::isClassInstalled( + 'PhabricatorPhurlApplication'); + if (!$phurl_installed) { + return false; + } + + if ($this->isHostMatch($host, array($uri))) { + return new PhabricatorShortSite(); + } + + return null; + } + + public function getRoutingMaps() { + $app = PhabricatorApplication::getByClass('PhabricatorPhurlApplication'); + + $maps = array(); + $maps[] = $this->newRoutingMap() + ->setApplication($app) + ->setRoutes($app->getShortRoutes()); + return $maps; + } + +} diff --git a/src/applications/config/option/PhabricatorPhurlConfigOptions.php b/src/applications/config/option/PhabricatorPhurlConfigOptions.php new file mode 100644 index 0000000000..378724a4b5 --- /dev/null +++ b/src/applications/config/option/PhabricatorPhurlConfigOptions.php @@ -0,0 +1,35 @@ +newOption('phurl.short-uri', 'string', null) + ->setLocked(true) + ->setSummary(pht('URI that Phurl will use to shorten URLs.')) + ->setDescription( + pht( + 'Set the URI that Phurl will use to share shortened URLs.')) + ->addExample( + 'https://some-very-short-domain.museum/', + pht('Valid Setting')), + ); + } +} diff --git a/src/applications/phurl/application/PhabricatorPhurlApplication.php b/src/applications/phurl/application/PhabricatorPhurlApplication.php index 7b01a3f4c5..015ed75fe7 100644 --- a/src/applications/phurl/application/PhabricatorPhurlApplication.php +++ b/src/applications/phurl/application/PhabricatorPhurlApplication.php @@ -51,4 +51,11 @@ final class PhabricatorPhurlApplication extends PhabricatorApplication { ); } + public function getShortRoutes() { + return array( + '/u/(?P[^/]+)' => 'PhabricatorPhurlShortURLController', + '.*' => 'PhabricatorPhurlShortURLDefaultController', + ); + } + } diff --git a/src/applications/phurl/controller/PhabricatorPhurlShortURLController.php b/src/applications/phurl/controller/PhabricatorPhurlShortURLController.php new file mode 100644 index 0000000000..b4f97d6813 --- /dev/null +++ b/src/applications/phurl/controller/PhabricatorPhurlShortURLController.php @@ -0,0 +1,19 @@ +getViewer(); + $append = $request->getURIData('append'); + $main_domain_uri = PhabricatorEnv::getProductionURI('/u/'.$append); + + return id(new AphrontRedirectResponse()) + ->setIsExternal(true) + ->setURI($main_domain_uri); + } +} diff --git a/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php b/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php new file mode 100644 index 0000000000..7d48f56e24 --- /dev/null +++ b/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php @@ -0,0 +1,13 @@ +getViewer(); $id = $request->getURIData('id'); @@ -32,5 +36,4 @@ final class PhabricatorPhurlURLAccessController return id(new AphrontRedirectResponse())->setURI('/'.$url->getMonogram()); } } - } From ada7d45a00d9fd212a4c66d4b27701f44b61f97c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 9 Nov 2015 11:43:27 -0800 Subject: [PATCH 15/49] Remove comment plugins from Phame Summary: Cleaning up house, may revisit in a v2. Removes ability to set Disqus or Facebook comments as comment system on Phame Posts. Test Plan: Create blog, create post, edit blog, view live pages. Reviewers: epriestley Reviewed By: epriestley Subscribers: btrahan, Korvin Maniphest Tasks: T9746 Differential Revision: https://secure.phabricator.com/D14448 --- src/__phutil_library_map__.php | 2 - .../option/PhabricatorDisqusConfigOptions.php | 35 ------ .../post/PhamePostEditController.php | 11 -- .../phame/editor/PhamePostEditor.php | 7 -- src/applications/phame/storage/PhamePost.php | 31 ----- .../phame/storage/PhamePostTransaction.php | 15 --- src/applications/phame/view/PhamePostView.php | 111 ------------------ 7 files changed, 212 deletions(-) delete mode 100644 src/applications/config/option/PhabricatorDisqusConfigOptions.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a5b33bc14d..a74f3e4633 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2095,7 +2095,6 @@ phutil_register_library_map(array( 'PhabricatorDisabledUserController' => 'applications/auth/controller/PhabricatorDisabledUserController.php', 'PhabricatorDisplayPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php', 'PhabricatorDisqusAuthProvider' => 'applications/auth/provider/PhabricatorDisqusAuthProvider.php', - 'PhabricatorDisqusConfigOptions' => 'applications/config/option/PhabricatorDisqusConfigOptions.php', 'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php', 'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', 'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php', @@ -6155,7 +6154,6 @@ phutil_register_library_map(array( 'PhabricatorDisabledUserController' => 'PhabricatorAuthController', 'PhabricatorDisplayPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorDisqusAuthProvider' => 'PhabricatorOAuth2AuthProvider', - 'PhabricatorDisqusConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorDivinerApplication' => 'PhabricatorApplication', 'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication', 'PhabricatorDraft' => 'PhabricatorDraftDAO', diff --git a/src/applications/config/option/PhabricatorDisqusConfigOptions.php b/src/applications/config/option/PhabricatorDisqusConfigOptions.php deleted file mode 100644 index 9bc6e13e39..0000000000 --- a/src/applications/config/option/PhabricatorDisqusConfigOptions.php +++ /dev/null @@ -1,35 +0,0 @@ -newOption('disqus.shortname', 'string', null) - ->setSummary(pht('Shortname for Disqus comment widget.')) - ->setDescription( - pht( - "Website shortname to use for Disqus comment widget in Phame. ". - "For more information, see:\n\n". - "[[http://docs.disqus.com/help/4/ | Disqus Quick Start Guide]]\n". - "[[http://docs.disqus.com/help/68/ | Information on Shortnames]]")), - ); - } - -} diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index 55ce9e176f..c768e3c3a0 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -55,7 +55,6 @@ final class PhamePostEditController extends PhamePostController { $title = $post->getTitle(); $phame_title = $post->getPhameTitle(); $body = $post->getBody(); - $comments_widget = $post->getCommentsWidget(); $visibility = $post->getVisibility(); $e_title = true; @@ -66,7 +65,6 @@ final class PhamePostEditController extends PhamePostController { $phame_title = $request->getStr('phame_title'); $phame_title = PhabricatorSlug::normalize($phame_title); $body = $request->getStr('body'); - $comments_widget = $request->getStr('comments_widget'); $v_projects = $request->getArr('projects'); $v_cc = $request->getArr('cc'); $visibility = $request->getInt('visibility'); @@ -84,9 +82,6 @@ final class PhamePostEditController extends PhamePostController { id(new PhamePostTransaction()) ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) ->setNewValue($visibility), - id(new PhamePostTransaction()) - ->setTransactionType(PhamePostTransaction::TYPE_COMMENTS_WIDGET) - ->setNewValue($comments_widget), id(new PhamePostTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) ->setNewValue(array('=' => $v_cc)), @@ -175,12 +170,6 @@ final class PhamePostEditController extends PhamePostController { ->setName('projects') ->setValue($v_projects) ->setDatasource(new PhabricatorProjectDatasource())) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Comments Widget')) - ->setName('comments_widget') - ->setvalue($comments_widget) - ->setOptions($post->getCommentsWidgetOptionsForSelect())) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($cancel_uri) diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 0009ccaea5..a824f84bd1 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -18,7 +18,6 @@ final class PhamePostEditor $types[] = PhamePostTransaction::TYPE_PHAME_TITLE; $types[] = PhamePostTransaction::TYPE_BODY; $types[] = PhamePostTransaction::TYPE_VISIBILITY; - $types[] = PhamePostTransaction::TYPE_COMMENTS_WIDGET; return $types; } @@ -36,8 +35,6 @@ final class PhamePostEditor return $object->getBody(); case PhamePostTransaction::TYPE_VISIBILITY: return $object->getVisibility(); - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: - return $object->getCommentsWidget(); } } @@ -50,7 +47,6 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_PHAME_TITLE: case PhamePostTransaction::TYPE_BODY: case PhamePostTransaction::TYPE_VISIBILITY: - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: return $xaction->getNewValue(); } } @@ -73,8 +69,6 @@ final class PhamePostEditor $object->setDatePublished(0); } return $object->setVisibility($xaction->getNewValue()); - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: - return $object->setCommentsWidget($xaction->getNewValue()); } return parent::applyCustomInternalTransaction($object, $xaction); @@ -89,7 +83,6 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_PHAME_TITLE: case PhamePostTransaction::TYPE_BODY: case PhamePostTransaction::TYPE_VISIBILITY: - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: return; } diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index 76681f2f57..da464a4a46 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -76,20 +76,6 @@ final class PhamePost extends PhameDAO return $name; } - public function setCommentsWidget($widget) { - $config_data = $this->getConfigData(); - $config_data['comments_widget'] = $widget; - return $this; - } - - public function getCommentsWidget() { - $config_data = $this->getConfigData(); - if (empty($config_data)) { - return 'none'; - } - return idx($config_data, 'comments_widget', 'none'); - } - protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, @@ -162,23 +148,6 @@ final class PhamePost extends PhameDAO ); } - public function getCommentsWidgetOptionsForSelect() { - $current = $this->getCommentsWidget(); - $options = array(); - - if ($current == 'facebook' || - PhabricatorFacebookAuthProvider::getFacebookApplicationID()) { - $options['facebook'] = pht('Facebook'); - } - if ($current == 'disqus' || - PhabricatorEnv::getEnvConfig('disqus.shortname')) { - $options['disqus'] = pht('Disqus'); - } - $options['none'] = pht('None'); - - return $options; - } - /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ diff --git a/src/applications/phame/storage/PhamePostTransaction.php b/src/applications/phame/storage/PhamePostTransaction.php index f1b2d22583..a22690d724 100644 --- a/src/applications/phame/storage/PhamePostTransaction.php +++ b/src/applications/phame/storage/PhamePostTransaction.php @@ -7,7 +7,6 @@ final class PhamePostTransaction const TYPE_PHAME_TITLE = 'phame.post.phame.title'; const TYPE_BODY = 'phame.post.body'; const TYPE_VISIBILITY = 'phame.post.visibility'; - const TYPE_COMMENTS_WIDGET = 'phame.post.comments.widget'; const MAILTAG_CONTENT = 'phame-post-content'; const MAILTAG_COMMENT = 'phame-post-comment'; @@ -56,7 +55,6 @@ final class PhamePostTransaction case self::TYPE_PHAME_TITLE: case self::TYPE_BODY: case self::TYPE_VISIBILITY: - case self::TYPE_COMMENTS_WIDGET: return 'fa-pencil'; break; } @@ -67,7 +65,6 @@ final class PhamePostTransaction $tags = parent::getMailTags(); switch ($this->getTransactionType()) { - case self::TYPE_COMMENTS_WIDGET: case PhabricatorTransactions::TYPE_COMMENT: $tags[] = self::MAILTAG_COMMENT; break; @@ -127,12 +124,6 @@ final class PhamePostTransaction $this->renderHandleLink($author_phid), rtrim($new, '/')); break; - case self::TYPE_COMMENTS_WIDGET: - return pht( - '%s updated the post\'s comment widget to "%s".', - $this->renderHandleLink($author_phid), - $new); - break; } return parent::getTitle(); @@ -185,12 +176,6 @@ final class PhamePostTransaction $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; - case self::TYPE_COMMENTS_WIDGET: - return pht( - '%s updated the comments widget for %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - break; } return parent::getTitleForFeed(); diff --git a/src/applications/phame/view/PhamePostView.php b/src/applications/phame/view/PhamePostView.php index 9fef145e02..589e1bb838 100644 --- a/src/applications/phame/view/PhamePostView.php +++ b/src/applications/phame/view/PhamePostView.php @@ -101,24 +101,6 @@ final class PhamePostView extends AphrontView { $this->getSummary()); } - public function renderComments() { - $post = $this->getPost(); - - switch ($post->getCommentsWidget()) { - case 'facebook': - $comments = $this->renderFacebookComments(); - break; - case 'disqus': - $comments = $this->renderDisqusComments(); - break; - case 'none': - default: - $comments = null; - break; - } - return $comments; - } - public function render() { return phutil_tag( 'div', @@ -129,7 +111,6 @@ final class PhamePostView extends AphrontView { $this->renderTitle(), $this->renderDatePublished(), $this->renderBody(), - $this->renderComments(), )); } @@ -146,96 +127,4 @@ final class PhamePostView extends AphrontView { )); } - private function renderFacebookComments() { - $fb_id = PhabricatorFacebookAuthProvider::getFacebookApplicationID(); - if (!$fb_id) { - return null; - } - - $fb_root = phutil_tag('div', - array( - 'id' => 'fb-root', - ), - ''); - - $c_uri = '//connect.facebook.net/en_US/all.js#xfbml=1&appId='.$fb_id; - $fb_js = CelerityStaticResourceResponse::renderInlineScript( - jsprintf( - '(function(d, s, id) {'. - ' var js, fjs = d.getElementsByTagName(s)[0];'. - ' if (d.getElementById(id)) return;'. - ' js = d.createElement(s); js.id = id;'. - ' js.src = %s;'. - ' fjs.parentNode.insertBefore(js, fjs);'. - '}(document, \'script\', \'facebook-jssdk\'));', - $c_uri)); - - - $uri = $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()); - - require_celerity_resource('phame-css'); - $fb_comments = phutil_tag('div', - array( - 'class' => 'fb-comments', - 'data-href' => $uri, - 'data-num-posts' => 5, - ), - ''); - - return phutil_tag( - 'div', - array( - 'class' => 'phame-comments-facebook', - ), - array( - $fb_root, - $fb_js, - $fb_comments, - )); - } - - private function renderDisqusComments() { - $disqus_shortname = PhabricatorEnv::getEnvConfig('disqus.shortname'); - if (!$disqus_shortname) { - return null; - } - - $post = $this->getPost(); - - $disqus_thread = phutil_tag('div', - array( - 'id' => 'disqus_thread', - )); - - // protip - try some var disqus_developer = 1; action to test locally - $disqus_js = CelerityStaticResourceResponse::renderInlineScript( - jsprintf( - ' var disqus_shortname = %s;'. - ' var disqus_identifier = %s;'. - ' var disqus_url = %s;'. - ' var disqus_title = %s;'. - '(function() {'. - ' var dsq = document.createElement("script");'. - ' dsq.type = "text/javascript";'. - ' dsq.async = true;'. - ' dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";'. - '(document.getElementsByTagName("head")[0] ||'. - ' document.getElementsByTagName("body")[0]).appendChild(dsq);'. - '})();', - $disqus_shortname, - $post->getPHID(), - $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()), - $post->getTitle())); - - return phutil_tag( - 'div', - array( - 'class' => 'phame-comments-disqus', - ), - array( - $disqus_thread, - $disqus_js, - )); - } - } From 7fd6704fb5c789be51757df3b4d8c43ce0fbc995 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 9 Nov 2015 22:18:27 +0000 Subject: [PATCH 16/49] Add a crumb to blog on Phame posts Summary: Crumbies Test Plan: View post, see blog link, click on crumb, see blog Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14449 --- .../phame/controller/blog/PhameBlogViewController.php | 7 +++++-- .../phame/controller/post/PhamePostViewController.php | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 249bd5506a..2f20702f6e 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -39,8 +39,11 @@ final class PhameBlogViewController extends PhameBlogController { ->appendChild($post_list); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('Blogs'), $this->getApplicationURI('blog/')); - $crumbs->addTextCrumb($blog->getName(), $this->getApplicationURI()); + $crumbs->addTextCrumb( + pht('Blogs'), + $this->getApplicationURI('blog/')); + $crumbs->addTextCrumb( + $blog->getName()); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index ca48af1036..8b85ada2aa 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -14,7 +14,12 @@ final class PhamePostViewController extends PhamePostController { return new Aphront404Response(); } + $blog = $post->getBlog(); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb( + $blog->getName(), + $this->getApplicationURI('blog/view/'.$blog->getID().'/')); $crumbs->addTextCrumb( $post->getTitle(), $this->getApplicationURI('post/view/'.$post->getID().'/')); From c589af51e8b353436539c2df30a9e3f5bdda6e25 Mon Sep 17 00:00:00 2001 From: David Fisher Date: Fri, 6 Nov 2015 14:30:32 -0800 Subject: [PATCH 17/49] add "update" mode to Diffusion coverage Conduit Summary: This diff adds a new mode argument to the Diffusion Conduit API with two options: - "overwrite": the default, maintains the current behavior of deleting all coverage in the specified branch before uploading the new coverage - "update": does not delete old coverage, but will overwrite previous coverage information if it's for the same file and commit `DiffusionRequest::loadCoverage` already loads a file's coverage from the latest available commit, so uploading coverage for different files in different commits with "update" will result in seeing the latest uploaded coverage in Diffusion. Test Plan: manual local verification Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14428 --- .../20151109.repository.coverage.1.sql | 8 +++++ ...iffusionUpdateCoverageConduitAPIMethod.php | 33 +++++++++++++++---- .../PhabricatorRepositorySchemaSpec.php | 1 + 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 resources/sql/autopatches/20151109.repository.coverage.1.sql diff --git a/resources/sql/autopatches/20151109.repository.coverage.1.sql b/resources/sql/autopatches/20151109.repository.coverage.1.sql new file mode 100644 index 0000000000..cebea0177c --- /dev/null +++ b/resources/sql/autopatches/20151109.repository.coverage.1.sql @@ -0,0 +1,8 @@ +USE {$NAMESPACE}_repository; +DELETE x FROM repository_coverage x +LEFT JOIN repository_coverage y + ON x.branchID = y.branchID + AND x.commitID = y.commitID + AND x.pathID = y.pathID + AND y.id > x.id + WHERE y.id IS NOT NULL; diff --git a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php index f356da3a10..be0d2c4faa 100644 --- a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php @@ -20,11 +20,17 @@ final class DiffusionUpdateCoverageConduitAPIMethod } protected function defineParamTypes() { + $modes = array( + 'overwrite', + 'update', + ); + return array( 'repositoryPHID' => 'required phid', 'branch' => 'required string', 'commit' => 'required string', 'coverage' => 'required map', + 'mode' => 'optional '.$this->formatStringConstants($modes), ); } @@ -77,16 +83,31 @@ final class DiffusionUpdateCoverageConduitAPIMethod $table_name = 'repository_coverage'; $conn->openTransaction(); - queryfx( - $conn, - 'DELETE FROM %T WHERE branchID = %d', - $table_name, - $branch->getID()); + $mode = $request->getValue('mode'); + switch ($mode) { + case '': + case 'overwrite': + // sets the coverage for the whole branch, deleting all previous + // coverage information + queryfx( + $conn, + 'DELETE FROM %T WHERE branchID = %d', + $table_name, + $branch->getID()); + break; + case 'update': + // sets the coverage for the provided files on the specified commit + break; + default: + $conn->killTransaction(); + throw new Exception(pht('Invalid mode "%s".', $mode)); + } foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q', + 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q'. + ' ON DUPLICATE KEY UPDATE coverage=VALUES(coverage)', $table_name, $chunk); } diff --git a/src/applications/repository/storage/PhabricatorRepositorySchemaSpec.php b/src/applications/repository/storage/PhabricatorRepositorySchemaSpec.php index 074c48c628..8ff2ed0c9b 100644 --- a/src/applications/repository/storage/PhabricatorRepositorySchemaSpec.php +++ b/src/applications/repository/storage/PhabricatorRepositorySchemaSpec.php @@ -37,6 +37,7 @@ final class PhabricatorRepositorySchemaSpec ), 'key_path' => array( 'columns' => array('branchID', 'pathID', 'commitID'), + 'unique' => true, ), )); From b315f61f49769c80bbac89bb7924b87bae55e2b1 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Mon, 9 Nov 2015 21:20:35 -0800 Subject: [PATCH 18/49] Add comments to internal Phame Posts Summary: Adds commenting to Phame Posts, also testing a new "document comment style". Unsure about it but Phame is a prototype so good place to explore. Test Plan: Leave some comments, see some comments, test show/hide. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9746 Differential Revision: https://secure.phabricator.com/D14451 --- resources/celerity/map.php | 4 +- .../20151109.phame.post.comments.1.sql | 18 ++++++ src/__phutil_library_map__.php | 4 ++ .../PhabricatorPhameApplication.php | 1 + .../post/PhamePostCommentController.php | 63 +++++++++++++++++++ .../post/PhamePostViewController.php | 34 ++++++++++ .../phame/editor/PhamePostEditor.php | 1 + .../phame/storage/PhamePostTransaction.php | 4 ++ .../storage/PhamePostTransactionComment.php | 10 +++ webroot/rsrc/css/phui/phui-document-pro.css | 61 ++++++++++++++++++ 10 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 resources/sql/autopatches/20151109.phame.post.comments.1.sql create mode 100644 src/applications/phame/controller/post/PhamePostCommentController.php create mode 100644 src/applications/phame/storage/PhamePostTransactionComment.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 1ca4df907d..c67682e8f7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -126,7 +126,7 @@ return array( 'rsrc/css/phui/phui-box.css' => 'a5bb366d', 'rsrc/css/phui/phui-button.css' => '16020a60', 'rsrc/css/phui/phui-crumbs-view.css' => '414406b5', - 'rsrc/css/phui/phui-document-pro.css' => '4f2b42e3', + 'rsrc/css/phui/phui-document-pro.css' => '7f3009ce', 'rsrc/css/phui/phui-document.css' => 'f841ad0a', 'rsrc/css/phui/phui-feed-story.css' => 'b7b26d23', 'rsrc/css/phui/phui-fontkit.css' => 'c9d63950', @@ -781,7 +781,7 @@ return array( 'phui-calendar-month-css' => '476be7e0', 'phui-crumbs-view-css' => '414406b5', 'phui-document-view-css' => 'f841ad0a', - 'phui-document-view-pro-css' => '4f2b42e3', + 'phui-document-view-pro-css' => '7f3009ce', 'phui-feed-story-css' => 'b7b26d23', 'phui-font-icon-base-css' => 'ecbbb4c2', 'phui-fontkit-css' => 'c9d63950', diff --git a/resources/sql/autopatches/20151109.phame.post.comments.1.sql b/resources/sql/autopatches/20151109.phame.post.comments.1.sql new file mode 100644 index 0000000000..8a36a0d3a7 --- /dev/null +++ b/resources/sql/autopatches/20151109.phame.post.comments.1.sql @@ -0,0 +1,18 @@ +CREATE TABLE {$NAMESPACE}_phame.phame_posttransaction_comment ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + phid VARCHAR(64) NOT NULL, + transactionPHID VARCHAR(64), + authorPHID VARCHAR(64) NOT NULL, + viewPolicy VARCHAR(64) NOT NULL, + editPolicy VARCHAR(64) NOT NULL, + commentVersion INT UNSIGNED NOT NULL, + content LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + contentSource LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + isDeleted BOOL NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + + UNIQUE KEY `key_phid` (phid), + UNIQUE KEY `key_version` (transactionPHID, commentVersion) + +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a74f3e4633..42bb3969e4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3273,6 +3273,7 @@ phutil_register_library_map(array( 'PhameCreatePostConduitAPIMethod' => 'applications/phame/conduit/PhameCreatePostConduitAPIMethod.php', 'PhameDAO' => 'applications/phame/storage/PhameDAO.php', 'PhamePost' => 'applications/phame/storage/PhamePost.php', + 'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php', 'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php', 'PhamePostDeleteController' => 'applications/phame/controller/post/PhamePostDeleteController.php', 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', @@ -3287,6 +3288,7 @@ phutil_register_library_map(array( 'PhamePostReplyHandler' => 'applications/phame/mail/PhamePostReplyHandler.php', 'PhamePostSearchEngine' => 'applications/phame/query/PhamePostSearchEngine.php', 'PhamePostTransaction' => 'applications/phame/storage/PhamePostTransaction.php', + 'PhamePostTransactionComment' => 'applications/phame/storage/PhamePostTransactionComment.php', 'PhamePostTransactionQuery' => 'applications/phame/query/PhamePostTransactionQuery.php', 'PhamePostUnpublishController' => 'applications/phame/controller/post/PhamePostUnpublishController.php', 'PhamePostView' => 'applications/phame/view/PhamePostView.php', @@ -7552,6 +7554,7 @@ phutil_register_library_map(array( 'PhabricatorSubscribableInterface', 'PhabricatorTokenReceiverInterface', ), + 'PhamePostCommentController' => 'PhamePostController', 'PhamePostController' => 'PhameController', 'PhamePostDeleteController' => 'PhamePostController', 'PhamePostEditController' => 'PhamePostController', @@ -7566,6 +7569,7 @@ phutil_register_library_map(array( 'PhamePostReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhamePostSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhamePostTransaction' => 'PhabricatorApplicationTransaction', + 'PhamePostTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhamePostTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhamePostUnpublishController' => 'PhamePostController', 'PhamePostView' => 'AphrontView', diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php index f4636d758c..1a3d3f90f4 100644 --- a/src/applications/phame/application/PhabricatorPhameApplication.php +++ b/src/applications/phame/application/PhabricatorPhameApplication.php @@ -54,6 +54,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { 'framed/(?P\d+)/' => 'PhamePostFramedController', 'new/' => 'PhamePostNewController', 'move/(?P\d+)/' => 'PhamePostNewController', + 'comment/(?P[1-9]\d*)/' => 'PhamePostCommentController', ), 'blog/' => array( '(?:(?Puser|all)/)?' => 'PhameBlogListController', diff --git a/src/applications/phame/controller/post/PhamePostCommentController.php b/src/applications/phame/controller/post/PhamePostCommentController.php new file mode 100644 index 0000000000..54c041ef94 --- /dev/null +++ b/src/applications/phame/controller/post/PhamePostCommentController.php @@ -0,0 +1,63 @@ +getViewer(); + $id = $request->getURIData('id'); + + if (!$request->isFormPost()) { + return new Aphront400Response(); + } + + $post = id(new PhamePostQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$post) { + return new Aphront404Response(); + } + + $is_preview = $request->isPreviewRequest(); + $draft = PhabricatorDraft::buildFromRequest($request); + + $view_uri = $this->getApplicationURI('post/view/'.$post->getID().'/'); + + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->attachComment( + id(new PhamePostTransactionComment()) + ->setContent($request->getStr('comment'))); + + $editor = id(new PhamePostEditor()) + ->setActor($viewer) + ->setContinueOnNoEffect($request->isContinueRequest()) + ->setContentSourceFromRequest($request) + ->setIsPreview($is_preview); + + try { + $xactions = $editor->applyTransactions($post, $xactions); + } catch (PhabricatorApplicationTransactionNoEffectException $ex) { + return id(new PhabricatorApplicationTransactionNoEffectResponse()) + ->setCancelURI($view_uri) + ->setException($ex); + } + + if ($draft) { + $draft->replaceOrDelete(); + } + + if ($request->isAjax() && $is_preview) { + return id(new PhabricatorApplicationTransactionResponse()) + ->setViewer($viewer) + ->setTransactions($xactions) + ->setIsPreview($is_preview); + } else { + return id(new AphrontRedirectResponse()) + ->setURI($view_uri); + } + } + +} diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 8b85ada2aa..1304e906cd 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -81,13 +81,24 @@ final class PhamePostViewController extends PhamePostController { ), $engine->getOutput($post, PhamePost::MARKUP_FIELD_BODY))); + $timeline = $this->buildTransactionTimeline( + $post, + id(new PhamePostTransactionQuery()) + ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); + $timeline = phutil_tag_div('phui-document-view-pro-box', $timeline); + + $add_comment = $this->buildCommentForm($post); + return $this->newPage() ->setTitle($post->getTitle()) ->addClass('pro-white-background') + ->setPageObjectPHIDs(array($post->getPHID())) ->setCrumbs($crumbs) ->appendChild( array( $document, + $timeline, + $add_comment, )); } @@ -197,4 +208,27 @@ final class PhamePostViewController extends PhamePostController { return $properties; } + private function buildCommentForm(PhamePost $post) { + $viewer = $this->getViewer(); + + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); + + $add_comment_header = $is_serious + ? pht('Add Comment') + : pht('Derp Text'); + + $draft = PhabricatorDraft::newFromUserAndKey( + $viewer, $post->getPHID()); + + $box = id(new PhabricatorApplicationTransactionCommentView()) + ->setUser($viewer) + ->setObjectPHID($post->getPHID()) + ->setDraft($draft) + ->setHeaderText($add_comment_header) + ->setAction($this->getApplicationURI('post/comment/'.$post->getID().'/')) + ->setSubmitButtonName(pht('Add Comment')); + + return phutil_tag_div('phui-document-view-pro-box', $box); + } + } diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index a824f84bd1..01a7ca2739 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -18,6 +18,7 @@ final class PhamePostEditor $types[] = PhamePostTransaction::TYPE_PHAME_TITLE; $types[] = PhamePostTransaction::TYPE_BODY; $types[] = PhamePostTransaction::TYPE_VISIBILITY; + $types[] = PhabricatorTransactions::TYPE_COMMENT; return $types; } diff --git a/src/applications/phame/storage/PhamePostTransaction.php b/src/applications/phame/storage/PhamePostTransaction.php index a22690d724..c985d41c25 100644 --- a/src/applications/phame/storage/PhamePostTransaction.php +++ b/src/applications/phame/storage/PhamePostTransaction.php @@ -20,6 +20,10 @@ final class PhamePostTransaction return PhabricatorPhamePostPHIDType::TYPECONST; } + public function getApplicationTransactionCommentObject() { + return new PhamePostTransactionComment(); + } + public function getRemarkupBlocks() { $blocks = parent::getRemarkupBlocks(); diff --git a/src/applications/phame/storage/PhamePostTransactionComment.php b/src/applications/phame/storage/PhamePostTransactionComment.php new file mode 100644 index 0000000000..4a04495d0b --- /dev/null +++ b/src/applications/phame/storage/PhamePostTransactionComment.php @@ -0,0 +1,10 @@ + Date: Mon, 9 Nov 2015 14:33:36 -0800 Subject: [PATCH 19/49] Provide a more informative alternative to 404 on invalid shortened Phurl URL Summary: When accessing an invalid URL on the short Phurl domain, users should see informative message Test Plan: Open URL in the previously configured Phurl short domain such as `https://www.zz.us` and see dialog with message. Open `https://www.zz.us/u/123` for a valid `U123` Phurl and access destination URL. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14450 --- .../PhabricatorPhurlShortURLDefaultController.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php b/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php index 7d48f56e24..8934d4b628 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php @@ -8,6 +8,16 @@ final class PhabricatorPhurlShortURLDefaultController } public function handleRequest(AphrontRequest $request) { - return new Aphront404Response(); + $dialog = $this->newDialog() + ->setTitle(pht('Invalid URL')) + ->appendParagraph( + pht('This domain can only be used to open URLs'. + ' shortened using the Phurl application. The'. + ' URL you are trying to access does not have'. + ' a Phurl URL associated with it.')); + + return id(new AphrontDialogResponse()) + ->setDialog($dialog) + ->setHTTPResponseCode(404); } } From 3747a35476100c0650ec1b203aafe188692b9b78 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 10 Nov 2015 17:41:05 +0000 Subject: [PATCH 20/49] Allow mail replies to Phame Posts Summary: Adds mail reply support to Phame Posts. Test Plan: Comment on a post, get mail. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9746 Differential Revision: https://secure.phabricator.com/D14454 --- src/__phutil_library_map__.php | 2 ++ .../phame/mail/PhamePostMailReceiver.php | 28 +++++++++++++++++++ .../phame/mail/PhamePostReplyHandler.php | 4 --- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/applications/phame/mail/PhamePostMailReceiver.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 42bb3969e4..b0b0c6731f 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3280,6 +3280,7 @@ phutil_register_library_map(array( 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', 'PhamePostFramedController' => 'applications/phame/controller/post/PhamePostFramedController.php', 'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php', + 'PhamePostMailReceiver' => 'applications/phame/mail/PhamePostMailReceiver.php', 'PhamePostNewController' => 'applications/phame/controller/post/PhamePostNewController.php', 'PhamePostNotLiveController' => 'applications/phame/controller/post/PhamePostNotLiveController.php', 'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php', @@ -7561,6 +7562,7 @@ phutil_register_library_map(array( 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor', 'PhamePostFramedController' => 'PhamePostController', 'PhamePostListController' => 'PhamePostController', + 'PhamePostMailReceiver' => 'PhabricatorObjectMailReceiver', 'PhamePostNewController' => 'PhamePostController', 'PhamePostNotLiveController' => 'PhamePostController', 'PhamePostPreviewController' => 'PhamePostController', diff --git a/src/applications/phame/mail/PhamePostMailReceiver.php b/src/applications/phame/mail/PhamePostMailReceiver.php new file mode 100644 index 0000000000..975d64a668 --- /dev/null +++ b/src/applications/phame/mail/PhamePostMailReceiver.php @@ -0,0 +1,28 @@ +setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + } + + protected function getTransactionReplyHandler() { + return new PhamePostReplyHandler(); + } + +} diff --git a/src/applications/phame/mail/PhamePostReplyHandler.php b/src/applications/phame/mail/PhamePostReplyHandler.php index f994763709..5e47edf43c 100644 --- a/src/applications/phame/mail/PhamePostReplyHandler.php +++ b/src/applications/phame/mail/PhamePostReplyHandler.php @@ -14,8 +14,4 @@ final class PhamePostReplyHandler return PhabricatorPhamePostPHIDType::TYPECONST; } - protected function shouldCreateCommentFromMailBody() { - return false; - } - } From 64ad44cffb24169278c0c57990722dd42020da47 Mon Sep 17 00:00:00 2001 From: lkassianik Date: Tue, 10 Nov 2015 10:09:14 -0800 Subject: [PATCH 21/49] Always override `auth.email-domains` when running unit tests Summary: Fixes T9689, Always override `auth.email-domains` when running unit tests Test Plan: - Set `auth.email-domains` - Run `arc unit --everything`. Observe no errors. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T9689 Differential Revision: https://secure.phabricator.com/D14456 --- .../controller/__tests__/PhabricatorAccessControlTestCase.php | 1 - src/infrastructure/testing/PhabricatorTestCase.php | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php b/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php index 560e0c95c4..27e716556e 100644 --- a/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php +++ b/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php @@ -56,7 +56,6 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase { $env->overrideEnvConfig('phabricator.base-uri', 'http://'.$host); $env->overrideEnvConfig('policy.allow-public', false); $env->overrideEnvConfig('auth.require-email-verification', false); - $env->overrideEnvConfig('auth.email-domains', array()); $env->overrideEnvConfig('security.require-multi-factor-auth', false); diff --git a/src/infrastructure/testing/PhabricatorTestCase.php b/src/infrastructure/testing/PhabricatorTestCase.php index 32fe4f3bee..068c2ed557 100644 --- a/src/infrastructure/testing/PhabricatorTestCase.php +++ b/src/infrastructure/testing/PhabricatorTestCase.php @@ -120,6 +120,10 @@ abstract class PhabricatorTestCase extends PhutilTestCase { 'phabricator.base-uri', 'http://phabricator.example.com'); + $this->env->overrideEnvConfig( + 'auth.email-domains', + array()); + // Tests do their own stubbing/voiding for events. $this->env->overrideEnvConfig('phabricator.silent', false); } From b3d3130b718ddeac7605fcc10009921280ee84f3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 10 Nov 2015 09:51:57 -0800 Subject: [PATCH 22/49] Fix a potentially lax hash comparison Summary: Via HackerOne. See D14025. I missed this comparison when making the original change. Test Plan: - Used `cat mail.txt | scripts/mail/mail_handler.php --process-duplicates` to pipe mail in a whole lot of times. - Tried bad hashes, saw rejections. - Tried good hash, saw mail accepted. Reviewers: chad Reviewed By: chad Differential Revision: https://secure.phabricator.com/D14455 --- .../metamta/receiver/PhabricatorObjectMailReceiver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php index 81f8ade37b..52a9a78a19 100644 --- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php @@ -126,7 +126,7 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver { $expect_hash = self::computeMailHash($object->getMailKey(), $check_phid); - if ($expect_hash != $parts['hash']) { + if (!phutil_hashes_are_identical($expect_hash, $parts['hash'])) { throw new PhabricatorMetaMTAReceivedMailProcessingException( MetaMTAReceivedMailStatus::STATUS_HASH_MISMATCH, pht( From 0398097498694f661e27fa4c3eff5f069ae2ec2e Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 4 Nov 2015 12:52:52 -0800 Subject: [PATCH 23/49] Allow ApplicationEditor forms to be reconfigured Summary: Ref T9132. This diff doesn't do anything interesting, it just lays the groundwork for more interesting future diffs. Broadly, the idea here is to let you create multiple views of each edit form. For example, we might create several different "Create Task" forms, like: - "New Bug Report" - "New Feature Request" These would be views of the "Create Task" form, but with various adjustments: - A form might have additional instructions ("how to file a good bug report"). - A form might have prefilled values for some fields (like particular projects, subscribers, or policies). - A form might have some fields locked (so they can not be edited) or hidden. - A form might have a different field order. - A form might have a limited visibility policy, so only some users can access it. This diff adds a new storage object (`EditEngineConfiguration`) to keep track of all those customizations and represent "a form which has been configured to look and work a certain way". This doesn't let these configurations do anything useful/interesting, and you can't access them directly yet, it's just all the boring plumbing to enable more interesting behavior in the future. Test Plan: ApplicationEditor forms now let you manage available forms and edit the current form: {F959025} There's a new (bare bones) list of all available engines: {F959030} And if you jump into an engine, you can see all the forms for it: {F959038} The actual form configurations have standard detail/edit pages. The edit pages are themselves driven by ApplicationEditor, of course, so you can edit the form for editing forms. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9132 Differential Revision: https://secure.phabricator.com/D14453 --- .../20151106.editengine.1.table.sql | 17 ++ .../20151106.editengine.2.xactions.sql | 19 ++ src/__phutil_library_map__.php | 53 +++- .../base/PhabricatorApplication.php | 6 +- .../conduit/PasteEditConduitAPIMethod.php | 2 +- .../editor/PhabricatorPasteEditEngine.php | 10 +- .../PhabricatorTransactionsApplication.php | 14 + ...rEditEngineConfigurationEditController.php | 26 ++ ...rEditEngineConfigurationListController.php | 34 +++ ...rEditEngineConfigurationSaveController.php | 55 ++++ ...rEditEngineConfigurationViewController.php | 125 +++++++++ .../PhabricatorEditEngineController.php | 37 +++ .../PhabricatorEditEngineListController.php | 16 ++ ...itEngine.php => PhabricatorEditEngine.php} | 245 +++++++++++++++++- ...php => PhabricatorEditEngineAPIMethod.php} | 2 +- .../editfield/PhabricatorEditField.php | 28 +- .../PhabricatorInstructionsEditField.php | 10 + ...habricatorApplicationTransactionEditor.php | 12 - ...catorEditEngineConfigurationEditEngine.php | 78 ++++++ ...abricatorEditEngineConfigurationEditor.php | 98 +++++++ ...ricatorEditEngineConfigurationPHIDType.php | 43 +++ ...habricatorEditEngineConfigurationQuery.php | 183 +++++++++++++ ...torEditEngineConfigurationSearchEngine.php | 102 ++++++++ ...ditEngineConfigurationTransactionQuery.php | 10 + .../query/PhabricatorEditEngineQuery.php | 31 +++ .../PhabricatorEditEngineSearchEngine.php | 78 ++++++ .../PhabricatorEditEngineConfiguration.php | 211 +++++++++++++++ ...atorEditEngineConfigurationTransaction.php | 20 ++ ...orApplicationEditHTTPParameterHelpView.php | 2 +- 29 files changed, 1536 insertions(+), 31 deletions(-) create mode 100644 resources/sql/autopatches/20151106.editengine.1.table.sql create mode 100644 resources/sql/autopatches/20151106.editengine.2.xactions.sql create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineController.php create mode 100644 src/applications/transactions/controller/PhabricatorEditEngineListController.php rename src/applications/transactions/editengine/{PhabricatorApplicationEditEngine.php => PhabricatorEditEngine.php} (74%) rename src/applications/transactions/editengine/{PhabricatorApplicationEditEngineAPIMethod.php => PhabricatorEditEngineAPIMethod.php} (98%) create mode 100644 src/applications/transactions/editfield/PhabricatorInstructionsEditField.php create mode 100644 src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php create mode 100644 src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php create mode 100644 src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php create mode 100644 src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php create mode 100644 src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php create mode 100644 src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php create mode 100644 src/applications/transactions/query/PhabricatorEditEngineQuery.php create mode 100644 src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php create mode 100644 src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php create mode 100644 src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php diff --git a/resources/sql/autopatches/20151106.editengine.1.table.sql b/resources/sql/autopatches/20151106.editengine.1.table.sql new file mode 100644 index 0000000000..bda84d9443 --- /dev/null +++ b/resources/sql/autopatches/20151106.editengine.1.table.sql @@ -0,0 +1,17 @@ +CREATE TABLE {$NAMESPACE}_search.search_editengineconfiguration ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + engineKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, + builtinKey VARCHAR(64) COLLATE {$COLLATE_TEXT}, + name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT}, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + isDisabled BOOL NOT NULL DEFAULT 0, + isDefault BOOL NOT NULL DEFAULT 0, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (phid), + UNIQUE KEY `key_engine` (engineKey, builtinKey), + KEY `key_default` (engineKey, isDefault, isDisabled) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151106.editengine.2.xactions.sql b/resources/sql/autopatches/20151106.editengine.2.xactions.sql new file mode 100644 index 0000000000..36a9d7a769 --- /dev/null +++ b/resources/sql/autopatches/20151106.editengine.2.xactions.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_search.search_editengineconfigurationtransaction ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + authorPHID VARBINARY(64) NOT NULL, + objectPHID VARBINARY(64) NOT NULL, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + commentPHID VARBINARY(64) DEFAULT NULL, + commentVersion INT UNSIGNED NOT NULL, + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b0b0c6731f..ad5809a954 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1579,8 +1579,6 @@ phutil_register_library_map(array( 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', - 'PhabricatorApplicationEditEngine' => 'applications/transactions/editengine/PhabricatorApplicationEditEngine.php', - 'PhabricatorApplicationEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php', 'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php', 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', @@ -2110,6 +2108,24 @@ phutil_register_library_map(array( 'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php', 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php', 'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php', + 'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php', + 'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php', + 'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php', + 'PhabricatorEditEngineConfigurationEditController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php', + 'PhabricatorEditEngineConfigurationEditEngine' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php', + 'PhabricatorEditEngineConfigurationEditor' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php', + 'PhabricatorEditEngineConfigurationListController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php', + 'PhabricatorEditEngineConfigurationPHIDType' => 'applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php', + 'PhabricatorEditEngineConfigurationQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php', + 'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php', + 'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php', + 'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php', + 'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php', + 'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php', + 'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php', + 'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php', + 'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php', + 'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php', 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php', 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', @@ -2294,6 +2310,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php', 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', + 'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', 'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php', @@ -5500,7 +5517,7 @@ phutil_register_library_map(array( 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability', - 'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod', + 'PasteEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PasteEmbedView' => 'AphrontView', 'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod', 'PasteMailReceiver' => 'PhabricatorObjectMailReceiver', @@ -5544,8 +5561,6 @@ phutil_register_library_map(array( 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', - 'PhabricatorApplicationEditEngine' => 'Phobject', - 'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView', 'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationLaunchView' => 'AphrontTagView', @@ -6172,6 +6187,31 @@ phutil_register_library_map(array( 'PhabricatorEdgeTestCase' => 'PhabricatorTestCase', 'PhabricatorEdgeType' => 'Phobject', 'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase', + 'PhabricatorEditEngine' => array( + 'Phobject', + 'PhabricatorPolicyInterface', + ), + 'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod', + 'PhabricatorEditEngineConfiguration' => array( + 'PhabricatorSearchDAO', + 'PhabricatorApplicationTransactionInterface', + 'PhabricatorPolicyInterface', + ), + 'PhabricatorEditEngineConfigurationEditController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorEditEngineConfigurationEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorEditEngineConfigurationListController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorEditEngineConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController', + 'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorEditField' => 'Phobject', 'PhabricatorEditType' => 'Phobject', 'PhabricatorEditor' => 'Phobject', @@ -6392,6 +6432,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface', 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 'PhabricatorInlineSummaryView' => 'AphrontView', + 'PhabricatorInstructionsEditField' => 'PhabricatorEditField', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck', @@ -6710,7 +6751,7 @@ phutil_register_library_map(array( 'PhabricatorPasteController' => 'PhabricatorController', 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 'PhabricatorPasteEditController' => 'PhabricatorPasteController', - 'PhabricatorPasteEditEngine' => 'PhabricatorApplicationEditEngine', + 'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine', 'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPasteListController' => 'PhabricatorPasteController', 'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType', diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 080062a2d9..8dce30290d 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -635,8 +635,12 @@ abstract class PhabricatorApplication return array(); } - protected function getEditRoutePattern($base) { + protected function getEditRoutePattern($base = null) { return $base.'(?:(?P[0-9]\d*)/)?(?:(?Pparameters)/)?'; } + protected function getQueryRoutePattern($base = null) { + return $base.'(?:query/(?P[^/]+)/)?'; + } + } diff --git a/src/applications/paste/conduit/PasteEditConduitAPIMethod.php b/src/applications/paste/conduit/PasteEditConduitAPIMethod.php index c02fb940e8..cfbb8de612 100644 --- a/src/applications/paste/conduit/PasteEditConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteEditConduitAPIMethod.php @@ -1,7 +1,7 @@ getViewer()); @@ -24,7 +30,7 @@ final class PhabricatorPasteEditEngine return $object->getMonogram(); } - protected function getObjectCreateShortText($object) { + protected function getObjectCreateShortText() { return pht('Create Paste'); } diff --git a/src/applications/transactions/application/PhabricatorTransactionsApplication.php b/src/applications/transactions/application/PhabricatorTransactionsApplication.php index 1a8792b256..260fa889b4 100644 --- a/src/applications/transactions/application/PhabricatorTransactionsApplication.php +++ b/src/applications/transactions/application/PhabricatorTransactionsApplication.php @@ -33,6 +33,20 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication { => 'PhabricatorApplicationTransactionShowOlderController', '(?Pold|new)/(?[^/]+)/' => 'PhabricatorApplicationTransactionValueController', + 'editengine/' => array( + $this->getQueryRoutePattern() + => 'PhabricatorEditEngineListController', + '(?P[^/]+)/' => array( + $this->getQueryRoutePattern() => + 'PhabricatorEditEngineConfigurationListController', + $this->getEditRoutePattern('edit/') => + 'PhabricatorEditEngineConfigurationEditController', + 'view/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationViewController', + 'save/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationSaveController', + ), + ), ), ); } diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php new file mode 100644 index 0000000000..1227ebc552 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php @@ -0,0 +1,26 @@ +getViewer(); + + $target_engine_key = $request->getURIData('engineKey'); + + $target_engine = PhabricatorEditEngine::getByKey( + $viewer, + $target_engine_key); + if (!$target_engine) { + return new Aphront404Response(); + } + + $this->setEngineKey($target_engine->getEngineKey()); + + return id(new PhabricatorEditEngineConfigurationEditEngine()) + ->setTargetEngine($target_engine) + ->setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php new file mode 100644 index 0000000000..b2c18f13e5 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php @@ -0,0 +1,34 @@ +setEngineKey($request->getURIData('engineKey')); + + return id(new PhabricatorEditEngineConfigurationSearchEngine()) + ->setController($this) + ->setEngineKey($this->getEngineKey()) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $engine_key = $this->getEngineKey(); + $edit_uri = "/transactions/editengine/{$engine_key}/edit/"; + + $crumbs->addAction( + id(new PHUIListItemView()) + ->setName(pht('New Form')) + ->setHref($edit_uri) + ->setIcon('fa-plus-square')); + + return $crumbs; + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php new file mode 100644 index 0000000000..f7ca08b01a --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php @@ -0,0 +1,55 @@ +getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $key = $request->getURIData('key'); + $viewer = $this->getViewer(); + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine_key)) + ->withIdentifiers(array($key)) + ->executeOne(); + if (!$config) { + return id(new Aphront404Response()); + } + + $view_uri = $config->getURI(); + + if ($config->getID()) { + return $this->newDialog() + ->setTitle(pht('Already Editable')) + ->appendParagraph( + pht('This form configuration is already editable.')) + ->addCancelButton($view_uri); + } + + if ($request->isFormPost()) { + $editor = id(new PhabricatorEditEngineConfigurationEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($config, array()); + + return id(new AphrontRedirectResponse()) + ->setURI($config->getURI()); + } + + // TODO: Explain what this means in more detail once the implications are + // more clear, or just link to some docs or something. + + return $this->newDialog() + ->setTitle(pht('Make Builtin Editable')) + ->appendParagraph( + pht('Make this builtin form editable?')) + ->addSubmitButton(pht('Make Editable')) + ->addCancelButton($view_uri); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php new file mode 100644 index 0000000000..f65eed1746 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php @@ -0,0 +1,125 @@ +getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $key = $request->getURIData('key'); + $viewer = $this->getViewer(); + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine_key)) + ->withIdentifiers(array($key)) + ->executeOne(); + if (!$config) { + return id(new Aphront404Response()); + } + + $is_concrete = (bool)$config->getID(); + + $actions = $this->buildActionView($config); + + $properties = $this->buildPropertyView($config) + ->setActionList($actions); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setPolicyObject($config) + ->setHeader(pht('Edit Form: %s', $config->getDisplayName())); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->addPropertyList($properties); + + $crumbs = $this->buildApplicationCrumbs(); + + if ($is_concrete) { + $crumbs->addTextCrumb(pht('Form %d', $config->getID())); + } else { + $crumbs->addTextCrumb(pht('Builtin')); + } + + if ($is_concrete) { + $timeline = $this->buildTransactionTimeline( + $config, + new PhabricatorEditEngineConfigurationTransactionQuery()); + + $timeline->setShouldTerminate(true); + } else { + $timeline = null; + } + + return $this->newPage() + ->setCrumbs($crumbs) + ->appendChild( + array( + $box, + $timeline, + )); + } + + private function buildActionView( + PhabricatorEditEngineConfiguration $config) { + $viewer = $this->getViewer(); + $engine_key = $this->getEngineKey(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $config, + PhabricatorPolicyCapability::CAN_EDIT); + + $view = id(new PhabricatorActionListView()) + ->setUser($viewer); + + $key = $config->getIdentifier(); + + $base_uri = "/transactions/editengine/{$engine_key}"; + + $is_concrete = (bool)$config->getID(); + if (!$is_concrete) { + $save_uri = "{$base_uri}/save/{$key}/"; + + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Make Editable')) + ->setIcon('fa-pencil') + ->setDisabled(!$can_edit) + ->setWorkflow(true) + ->setHref($save_uri)); + + $can_edit = false; + } else { + $edit_uri = "{$base_uri}/edit/{$key}/"; + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Form Configuration')) + ->setIcon('fa-pencil') + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit) + ->setHref($edit_uri)); + } + + return $view; + } + + private function buildPropertyView( + PhabricatorEditEngineConfiguration $config) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setObject($config); + + return $properties; + } + + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineController.php b/src/applications/transactions/controller/PhabricatorEditEngineController.php new file mode 100644 index 0000000000..e920260f37 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineController.php @@ -0,0 +1,37 @@ +engineKey = $engine_key; + return $this; + } + + public function getEngineKey() { + return $this->engineKey; + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $crumbs->addTextCrumb(pht('Edit Engines'), '/transactions/editengine/'); + + $engine_key = $this->getEngineKey(); + if ($engine_key !== null) { + $engine = PhabricatorEditEngine::getByKey( + $this->getViewer(), + $engine_key); + if ($engine) { + $crumbs->addTextCrumb( + $engine->getEngineName(), + "/transactions/editengine/{$engine_key}/"); + } + } + + return $crumbs; + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineListController.php b/src/applications/transactions/controller/PhabricatorEditEngineListController.php new file mode 100644 index 0000000000..3120d6846c --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineListController.php @@ -0,0 +1,16 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php similarity index 74% rename from src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php rename to src/applications/transactions/editengine/PhabricatorEditEngine.php index 86a03b51f4..3fca734330 100644 --- a/src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -4,6 +4,7 @@ /** * @task fields Managing Fields * @task text Display Text + * @task config Edit Engine Configuration * @task uri Managing URIs * @task load Creating and Loading Objects * @task web Responding to Web Requests @@ -11,11 +12,16 @@ * @task http Responding to HTTP Parameter Requests * @task conduit Responding to Conduit Requests */ -abstract class PhabricatorApplicationEditEngine extends Phobject { +abstract class PhabricatorEditEngine + extends Phobject + implements PhabricatorPolicyInterface { + + const EDITENGINECONFIG_DEFAULT = 'default'; private $viewer; private $controller; private $isCreate; + private $editEngineConfiguration; final public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; @@ -36,6 +42,10 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { return $this->controller; } + final public function getEngineKey() { + return $this->getPhobjectClassConstant('ENGINECONST', 64); + } + /* -( Managing Fields )---------------------------------------------------- */ @@ -184,6 +194,9 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } } + $config = $this->getEditEngineConfiguration(); + $fields = $config->applyConfigurationToFields($this, $fields); + foreach ($fields as $field) { $field ->setViewer($viewer) @@ -197,6 +210,12 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { /* -( Display Text )------------------------------------------------------- */ + /** + * @task text + */ + abstract public function getEngineName(); + + /** * @task text */ @@ -212,7 +231,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { /** * @task text */ - abstract protected function getObjectCreateShortText($object); + abstract protected function getObjectCreateShortText(); /** @@ -237,6 +256,121 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } +/* -( Edit Engine Configuration )------------------------------------------ */ + + + protected function supportsEditEngineConfiguration() { + return true; + } + + final protected function getEditEngineConfiguration() { + return $this->editEngineConfiguration; + } + + private function loadEditEngineConfiguration($key) { + if ($key === null) { + $key = self::EDITENGINECONFIG_DEFAULT; + } + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($this->getViewer()) + ->withEngineKeys(array($this->getEngineKey())) + ->withIdentifiers(array($key)) + ->executeOne(); + if (!$config) { + return null; + } + + $this->editEngineConfiguration = $config; + + return $config; + } + + final public function getBuiltinEngineConfigurations() { + $configurations = $this->newBuiltinEngineConfigurations(); + + if (!$configurations) { + throw new Exception( + pht( + 'EditEngine ("%s") returned no builtin engine configurations, but '. + 'an edit engine must have at least one configuration.', + get_class($this))); + } + + assert_instances_of($configurations, 'PhabricatorEditEngineConfiguration'); + + $has_default = false; + foreach ($configurations as $config) { + if ($config->getBuiltinKey() == self::EDITENGINECONFIG_DEFAULT) { + $has_default = true; + } + } + + if (!$has_default) { + $first = head($configurations); + if (!$first->getBuiltinKey()) { + $first->setBuiltinKey(self::EDITENGINECONFIG_DEFAULT); + + if (!strlen($first->getName())) { + $first->setName($this->getObjectCreateShortText()); + } + } else { + throw new Exception( + pht( + 'EditEngine ("%s") returned builtin engine configurations, '. + 'but none are marked as default and the first configuration has '. + 'a different builtin key already. Mark a builtin as default or '. + 'omit the key from the first configuration', + get_class($this))); + } + } + + $builtins = array(); + foreach ($configurations as $key => $config) { + $builtin_key = $config->getBuiltinKey(); + + if ($builtin_key === null) { + throw new Exception( + pht( + 'EditEngine ("%s") returned builtin engine configurations, '. + 'but one (with key "%s") is missing a builtin key. Provide a '. + 'builtin key for each configuration (you can omit it from the '. + 'first configuration in the list to automatically assign the '. + 'default key).', + get_class($this), + $key)); + } + + if (isset($builtins[$builtin_key])) { + throw new Exception( + pht( + 'EditEngine ("%s") returned builtin engine configurations, '. + 'but at least two specify the same builtin key ("%s"). Engines '. + 'must have unique builtin keys.', + get_class($this), + $builtin_key)); + } + + $builtins[$builtin_key] = $config; + } + + + return $builtins; + } + + protected function newBuiltinEngineConfigurations() { + return array( + $this->newConfiguration(), + ); + } + + final protected function newConfiguration() { + return PhabricatorEditEngineConfiguration::initializeNewConfiguration( + $this->getViewer(), + $this); + } + + /* -( Managing URIs )------------------------------------------------------ */ @@ -317,7 +451,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { * @return bool True if a new object is being created. * @task load */ - final protected function getIsCreate() { + final public function getIsCreate() { return $this->isCreate; } @@ -391,6 +525,35 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } + /** + * Verify that an object is appropriate for editing. + * + * @param wild Loaded value. + * @return void + * @task load + */ + private function validateObject($object) { + if (!$object || !is_object($object)) { + throw new Exception( + pht( + 'EditEngine "%s" created or loaded an invalid object: object must '. + 'actually be an object, but is of some other type ("%s").', + get_class($this), + gettype($object))); + } + + if (!($object instanceof PhabricatorApplicationTransactionInterface)) { + throw new Exception( + pht( + 'EditEngine "%s" created or loaded an invalid object: object (of '. + 'class "%s") must implement "%s", but does not.', + get_class($this), + get_class($object), + 'PhabricatorApplicationTransactionInterface')); + } + } + + /* -( Responding to Web Requests )----------------------------------------- */ @@ -399,6 +562,11 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $controller = $this->getController(); $request = $controller->getRequest(); + $config = $this->loadEditEngineConfiguration($request->getURIData('form')); + if (!$config) { + return new Aphront404Response(); + } + $id = $request->getURIData('id'); if ($id) { $this->setIsCreate(false); @@ -411,6 +579,8 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $object = $this->newEditableObject(); } + $this->validateObject($object); + $action = $request->getURIData('editAction'); switch ($action) { case 'parameters': @@ -425,7 +595,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $crumbs = $controller->buildApplicationCrumbsForEditEngine(); if ($this->getIsCreate()) { - $create_text = $this->getObjectCreateShortText($object); + $create_text = $this->getObjectCreateShortText(); if ($final) { $crumbs->addTextCrumb($create_text); } else { @@ -570,6 +740,20 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { private function buildEditFormActions($object) { $actions = array(); + if ($this->supportsEditEngineConfiguration()) { + $engine_key = $this->getEngineKey(); + $config = $this->getEditEngineConfiguration(); + + $actions[] = id(new PhabricatorActionView()) + ->setName(pht('Manage Form Configurations')) + ->setIcon('fa-list-ul') + ->setHref("/transactions/editengine/{$engine_key}/"); + $actions[] = id(new PhabricatorActionView()) + ->setName(pht('Edit Form Configuration')) + ->setIcon('fa-pencil') + ->setHref($config->getURI()); + } + $actions[] = id(new PhabricatorActionView()) ->setName(pht('Show HTTP Parameters')) ->setIcon('fa-crosshairs') @@ -601,7 +785,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $header_text = pht( 'HTTP Parameters: %s', - $this->getObjectCreateShortText($object)); + $this->getObjectCreateShortText()); $header = id(new PHUIHeaderView()) ->setHeader($header_text); @@ -637,6 +821,14 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { final public function buildConduitResponse(ConduitAPIRequest $request) { $viewer = $this->getViewer(); + $config = $this->loadEditEngineConfiguration(null); + if (!$config) { + throw new Exception( + pht( + 'Unable to load configuration for this EditEngine ("%s").', + get_class($this))); + } + $phid = $request->getValue('objectPHID'); if ($phid) { $this->setIsCreate(false); @@ -649,6 +841,8 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $object = $this->newEditableObject(); } + $this->validateObject($object); + $fields = $this->buildEditFields($object); $types = $this->getAllEditTypesFromFields($fields); @@ -772,5 +966,46 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { return $this->getAllEditTypesFromFields($fields); } + final public static function getAllEditEngines() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getEngineKey') + ->execute(); + } + final public static function getByKey(PhabricatorUser $viewer, $key) { + return id(new PhabricatorEditEngineQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($key)) + ->executeOne(); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getPHID() { + return get_class($this); + } + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::getMostOpenPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } } diff --git a/src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php similarity index 98% rename from src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php rename to src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 9fc6495455..dafc56c825 100644 --- a/src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -1,6 +1,6 @@ key = $key; @@ -69,7 +69,18 @@ abstract class PhabricatorEditField extends Phobject { return $this->description; } - abstract protected function newControl(); + public function setIsLocked($is_locked) { + $this->isLocked = $is_locked; + return $this; + } + + public function getIsLocked() { + return $this->isLocked; + } + + protected function newControl() { + throw new PhutilMethodNotImplementedException(); + } protected function renderControl() { $control = $this->newControl(); @@ -85,6 +96,10 @@ abstract class PhabricatorEditField extends Phobject { $control->setLabel($this->getLabel()); } + if ($this->getIsLocked()) { + $control->setDisabled(true); + } + return $control; } @@ -166,6 +181,15 @@ abstract class PhabricatorEditField extends Phobject { return $this; } + public function readDefaultValueFromConfiguration($value) { + $this->value = $this->getDefaultValueFromConfiguration($value); + return $this; + } + + protected function getDefaultValueFromConfiguration($value) { + return $value; + } + protected function getValueFromObject($object) { if ($this->hasValue) { return $this->value; diff --git a/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php b/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php new file mode 100644 index 0000000000..9da1d49ae6 --- /dev/null +++ b/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php @@ -0,0 +1,10 @@ +appendRemarkupInstructions($this->getValue()); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index a83ea2ba22..1f390ed468 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -815,18 +815,6 @@ abstract class PhabricatorApplicationTransactionEditor $xactions = $this->filterTransactions($object, $xactions); - if (!$xactions) { - if ($read_locking) { - $object->endReadLocking(); - $read_locking = false; - } - if ($transaction_open) { - $object->killTransaction(); - $transaction_open = false; - } - return array(); - } - // Now that we've merged, filtered, and combined transactions, check for // required capabilities. foreach ($xactions as $xaction) { diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php new file mode 100644 index 0000000000..17b604b7b2 --- /dev/null +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php @@ -0,0 +1,78 @@ +targetEngine = $target_engine; + return $this; + } + + public function getTargetEngine() { + return $this->targetEngine; + } + + public function getEngineName() { + return pht('Edit Configurations'); + } + + protected function newEditableObject() { + return PhabricatorEditEngineConfiguration::initializeNewConfiguration( + $this->getViewer(), + $this->getTargetEngine()); + } + + protected function newObjectQuery() { + return id(new PhabricatorEditEngineConfigurationQuery()); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create New Form'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Form %d: %s', $object->getID(), $object->getDisplayName()); + } + + protected function getObjectEditShortText($object) { + return pht('Form %d', $object->getID()); + } + + protected function getObjectCreateShortText() { + return pht('Create Form'); + } + + protected function getObjectViewURI($object) { + $engine_key = $this->getTargetEngine()->getEngineKey(); + $id = $object->getID(); + return "/transactions/editengine/{$engine_key}/view/{$id}/"; + } + + protected function getObjectEditURI($object) { + $engine_key = $this->getTargetEngine()->getEngineKey(); + $id = $object->getID(); + return "/transactions/editengine/{$engine_key}/edit/{$id}/"; + } + + protected function getObjectCreateCancelURI($object) { + $engine_key = $this->getTargetEngine()->getEngineKey(); + return "/transactions/editengine/{$engine_key}/"; + } + + protected function buildCustomEditFields($object) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setDescription(pht('Name of the form.')) + ->setTransactionType( + PhabricatorEditEngineConfigurationTransaction::TYPE_NAME) + ->setValue($object->getName()), + ); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php new file mode 100644 index 0000000000..4736e89efa --- /dev/null +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -0,0 +1,98 @@ +validateIsEmptyTextField( + $object->getName(), + $xactions); + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Form name is required.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } + break; + } + + return $errors; + } + + protected function getCustomTransactionOldValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + return $object->getName(); + } + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + return $xaction->getNewValue(); + } + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + $object->setName($xaction->getNewValue()); + return; + } + + return parent::applyCustomInternalTransaction($object, $xaction); + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + return; + } + + return parent::applyCustomExternalTransaction($object, $xaction); + } + +} diff --git a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php new file mode 100644 index 0000000000..b4ac0ddbfe --- /dev/null +++ b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php @@ -0,0 +1,43 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $config = $objects[$phid]; + + $id = $config->getID(); + $name = $config->getName(); + + $handle->setName($name); + $handle->setURI($config->getURI()); + } + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php new file mode 100644 index 0000000000..b1c573f775 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php @@ -0,0 +1,183 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withEngineKeys(array $engine_keys) { + $this->engineKeys = $engine_keys; + return $this; + } + + public function withBuiltinKeys(array $builtin_keys) { + $this->builtinKeys = $builtin_keys; + return $this; + } + + public function withIdentifiers(array $identifiers) { + $this->identifiers = $identifiers; + return $this; + } + + public function newResultObject() { + return new PhabricatorEditEngineConfiguration(); + } + + protected function loadPage() { + // TODO: The logic here is a little flimsy and won't survive pagination. + // For now, I'm just not bothering with pagination since I believe it will + // take some time before any install manages to produce a large enough + // number of edit forms for any particular engine for the lack of UI + // pagination to become a problem. + + $page = $this->loadStandardPage($this->newResultObject()); + + // Now that we've loaded the real results from the database, we're going + // to load builtins from the edit engines and add them to the list. + + $engines = PhabricatorEditEngine::getAllEditEngines(); + + if ($this->engineKeys) { + $engines = array_select_keys($engines, $this->engineKeys); + } + + foreach ($engines as $engine) { + $engine->setViewer($this->getViewer()); + } + + // List all the builtins which have already been saved to the database as + // real objects. + $concrete = array(); + foreach ($page as $config) { + $builtin_key = $config->getBuiltinKey(); + if ($builtin_key !== null) { + $engine_key = $config->getEngineKey(); + $concrete[$engine_key][$builtin_key] = $config; + } + } + + $builtins = array(); + foreach ($engines as $engine_key => $engine) { + $engine_builtins = $engine->getBuiltinEngineConfigurations(); + foreach ($engine_builtins as $engine_builtin) { + $builtin_key = $engine_builtin->getBuiltinKey(); + if (isset($concrete[$engine_key][$builtin_key])) { + continue; + } else { + $builtins[] = $engine_builtin; + } + } + } + + foreach ($builtins as $builtin) { + $page[] = $builtin; + } + + // Now we have to do some extra filtering to make sure everything we're + // about to return really satisfies the query. + + if ($this->ids !== null) { + $ids = array_fuse($this->ids); + foreach ($page as $key => $config) { + if (empty($ids[$config->getID()])) { + unset($page[$key]); + } + } + } + + if ($this->phids !== null) { + $phids = array_fuse($this->phids); + foreach ($page as $key => $config) { + if (empty($phids[$config->getPHID()])) { + unset($page[$key]); + } + } + } + + if ($this->builtinKeys !== null) { + $builtin_keys = array_fuse($this->builtinKeys); + foreach ($page as $key => $config) { + if (empty($builtin_keys[$config->getBuiltinKey()])) { + unset($page[$key]); + } + } + } + + if ($this->identifiers !== null) { + $identifiers = array_fuse($this->identifiers); + foreach ($page as $key => $config) { + if (isset($identifiers[$config->getBuiltinKey()])) { + continue; + } + if (isset($identifiers[$config->getID()])) { + continue; + } + unset($page[$key]); + } + } + + return $page; + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'phid IN (%Ls)', + $this->phids); + } + + if ($this->engineKeys !== null) { + $where[] = qsprintf( + $conn, + 'engineKey IN (%Ls)', + $this->engineKeys); + } + + if ($this->builtinKeys !== null) { + $where[] = qsprintf( + $conn, + 'builtinKey IN (%Ls)', + $this->builtinKeys); + } + + if ($this->identifiers !== null) { + $where[] = qsprintf( + $conn, + '(id IN (%Ls) OR builtinKey IN (%Ls))', + $this->identifiers, + $this->identifiers); + } + + return $where; + } + + public function getQueryApplicationClass() { + return 'PhabricatorTransactionsApplication'; + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php new file mode 100644 index 0000000000..8d55aeb5ac --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php @@ -0,0 +1,102 @@ +engineKey = $engine_key; + return $this; + } + + public function getEngineKey() { + return $this->engineKey; + } + + public function canUseInPanelContext() { + return false; + } + + public function getResultTypeDescription() { + return pht('Forms'); + } + + public function getApplicationClassName() { + return 'PhabricatorTransactionsApplication'; + } + + public function newQuery() { + return id(new PhabricatorEditEngineConfigurationQuery()) + ->withEngineKeys(array($this->getEngineKey())); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + return $query; + } + + protected function buildCustomSearchFields() { + return array(); + } + + protected function getDefaultFieldOrder() { + return array(); + } + + protected function getURI($path) { + return '/transactions/editengine/'.$this->getEngineKey().'/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Forms'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $configs, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($configs, 'PhabricatorEditEngineConfiguration'); + $viewer = $this->requireViewer(); + $engine_key = $this->getEngineKey(); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer); + foreach ($configs as $config) { + $item = id(new PHUIObjectItemView()) + ->setHeader($config->getDisplayName()); + + $id = $config->getID(); + if ($id) { + $item->setObjectName(pht('Form %d', $id)); + $key = $id; + } else { + $item->setObjectName(pht('Builtin')); + $key = $config->getBuiltinKey(); + } + $item->setHref("/transactions/editengine/{$engine_key}/view/{$key}/"); + + + $list->addItem($item); + } + + return id(new PhabricatorApplicationSearchResultView()) + ->setObjectList($list); + } +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php new file mode 100644 index 0000000000..2a4677944e --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php @@ -0,0 +1,10 @@ +engineKeys = $keys; + return $this; + } + + protected function loadPage() { + $engines = PhabricatorEditEngine::getAllEditEngines(); + + if ($this->engineKeys !== null) { + $engines = array_select_keys($engines, $this->engineKeys); + } + + return $engines; + } + + public function getQueryApplicationClass() { + return 'PhabricatorTransactionsApplication'; + } + + protected function getResultCursor($object) { + return null; + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php new file mode 100644 index 0000000000..7b86d34bd9 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php @@ -0,0 +1,78 @@ +newQuery(); + return $query; + } + + protected function buildCustomSearchFields() { + return array(); + } + + protected function getDefaultFieldOrder() { + return array(); + } + + protected function getURI($path) { + return '/transactions/editengine/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Edit Engines'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $engines, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($engines, 'PhabricatorEditEngine'); + $viewer = $this->requireViewer(); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer); + foreach ($engines as $engine) { + $engine_key = $engine->getEngineKey(); + $query_uri = "/transactions/editengine/{$engine_key}/"; + + $item = id(new PHUIObjectItemView()) + ->setHeader($engine->getEngineName()) + ->setHref($query_uri); + + $list->addItem($item); + } + + return id(new PhabricatorApplicationSearchResultView()) + ->setObjectList($list); + } +} diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php new file mode 100644 index 0000000000..aed81bd8a3 --- /dev/null +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php @@ -0,0 +1,211 @@ +setEngineKey($engine->getEngineKey()) + ->attachEngine($engine) + ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) + ->setEditPolicy($edit_policy); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorEditEngineConfigurationPHIDType::TYPECONST); + } + + protected function getConfiguration() { + return array( + self::CONFIG_AUX_PHID => true, + self::CONFIG_SERIALIZATION => array( + 'properties' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array( + 'engineKey' => 'text64', + 'builtinKey' => 'text64?', + 'name' => 'text255', + 'isDisabled' => 'bool', + 'isDefault' => 'bool', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_engine' => array( + 'columns' => array('engineKey', 'builtinKey'), + 'unique' => true, + ), + 'key_default' => array( + 'columns' => array('engineKey', 'isDefault', 'isDisabled'), + ), + ), + ) + parent::getConfiguration(); + } + + public function getProperty($key, $default = null) { + return idx($this->properties, $key, $default); + } + + public function setProperty($key, $value) { + $this->properties[$key] = $value; + return $this; + } + + public function attachEngine(PhabricatorEditEngine $engine) { + $this->engine = $engine; + return $this; + } + + public function getEngine() { + return $this->assertAttached($this->engine); + } + + public function applyConfigurationToFields( + PhabricatorEditEngine $engine, + array $fields) { + $fields = mpull($fields, null, 'getKey'); + + $values = $this->getProperty('defaults', array()); + foreach ($fields as $key => $field) { + if ($engine->getIsCreate()) { + if (array_key_exists($key, $values)) { + $field->readDefaultValueFromConfiguration($values[$key]); + } + } + } + + $fields = $this->reorderFields($fields); + + $head_instructions = $this->getProperty('instructions.head'); + if (strlen($head_instructions)) { + $fields = array( + 'config.instructions.head' => id(new PhabricatorInstructionsEditField()) + ->setKey('config.instructions.head') + ->setValue($head_instructions), + ) + $fields; + } + + return $fields; + } + + private function reorderFields(array $fields) { + $keys = array(); + $fields = array_select_keys($fields, $keys) + $fields; + + // Now, move locked fields to the bottom. + $head = array(); + $tail = array(); + foreach ($fields as $key => $field) { + if (!$field->getIsLocked()) { + $head[$key] = $field; + } else { + $tail[$key] = $field; + } + } + + return $head + $tail; + } + + public function getURI() { + $engine_key = $this->getEngineKey(); + $key = $this->getIdentifier(); + + return "/transactions/editengine/{$engine_key}/view/{$key}/"; + } + + public function getIdentifier() { + $key = $this->getID(); + if (!$key) { + $key = $this->getBuiltinKey(); + } + return $key; + } + + public function getDisplayName() { + $name = $this->getName(); + if (strlen($name)) { + return $name; + } + + $builtin = $this->getBuiltinKey(); + if ($builtin !== null) { + return pht('Builtin Form "%s"', $builtin); + } + + return pht('Untitled Form'); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return $this->getEditPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhabricatorEditEngineConfigurationEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new PhabricatorEditEngineConfigurationTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + return $timeline; + } + +} diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php new file mode 100644 index 0000000000..cbc7d2b911 --- /dev/null +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php @@ -0,0 +1,20 @@ + Date: Tue, 10 Nov 2015 11:50:19 -0800 Subject: [PATCH 24/49] Make deleting a blog a little easier to recover from Summary: We currently orphan posts when you delete a blog. Fixes some visibility and permission errors when that happens. Also... should allow you to archive posts. Test Plan: Delete a blog, visit a post I made, still can see it. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T9360 Differential Revision: https://secure.phabricator.com/D14457 --- .../controller/post/PhamePostViewController.php | 12 +++++++++--- .../phame/query/PhamePostSearchEngine.php | 11 ++++++++--- src/applications/phame/storage/PhamePost.php | 2 ++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 1304e906cd..6b414a9fe7 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -17,9 +17,15 @@ final class PhamePostViewController extends PhamePostController { $blog = $post->getBlog(); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb( - $blog->getName(), - $this->getApplicationURI('blog/view/'.$blog->getID().'/')); + if ($blog) { + $crumbs->addTextCrumb( + $blog->getName(), + $this->getApplicationURI('blog/view/'.$blog->getID().'/')); + } else { + $crumbs->addTextCrumb( + pht('[No Blog]'), + null); + } $crumbs->addTextCrumb( $post->getTitle(), $this->getApplicationURI('post/view/'.$post->getID().'/')); diff --git a/src/applications/phame/query/PhamePostSearchEngine.php b/src/applications/phame/query/PhamePostSearchEngine.php index 84fec54df0..68c94552bb 100644 --- a/src/applications/phame/query/PhamePostSearchEngine.php +++ b/src/applications/phame/query/PhamePostSearchEngine.php @@ -81,15 +81,20 @@ final class PhamePostSearchEngine foreach ($posts as $post) { $id = $post->getID(); - $blog = $viewer->renderHandle($post->getBlogPHID())->render(); + $blog = $post->getBlog(); + if ($blog) { + $blog_name = $viewer->renderHandle($post->getBlogPHID())->render(); + $blog_name = pht('Blog: %s', $blog_name); + } else { + $blog_name = pht('[No Blog]'); + } $item = id(new PHUIObjectItemView()) ->setUser($viewer) ->setObject($post) ->setHeader($post->getTitle()) ->setStatusIcon('fa-star') ->setHref($this->getApplicationURI("/post/view/{$id}/")) - ->addAttribute( - pht('Blog: %s', $blog)); + ->addAttribute($blog_name); if ($post->isDraft()) { $item->setStatusIcon('fa-star-o grey'); $item->setDisabled(true); diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index da464a4a46..eb04679e1f 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -169,6 +169,8 @@ final class PhamePost extends PhameDAO return $this->getBlog()->getViewPolicy(); } else if ($this->getBlog()) { return $this->getBlog()->getEditPolicy(); + } else { + return PhabricatorPolicies::POLICY_NOONE; } break; case PhabricatorPolicyCapability::CAN_EDIT: From 9f2fc7f93834a142028ee7385d564120b0eaf58a Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 10 Nov 2015 12:57:33 -0800 Subject: [PATCH 25/49] Only send Phame Post body on new creation Summary: Right now we're attaching the body of every Phame post on each comment, at least restrict it to newly created objects only. Test Plan: Write a new post, get full email, leave a comment, get less email. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14459 --- src/applications/phame/editor/PhamePostEditor.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 01a7ca2739..abe7dfe097 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -209,7 +209,10 @@ final class PhamePostEditor $body = parent::buildMailBody($object, $xactions); - $body->addRemarkupSection(null, $object->getBody()); + if ($this->getIsNewObject()) { + $body->addRemarkupSection(null, $object->getBody()); + } + $body->addLinkSection( pht('POST DETAIL'), PhabricatorEnv::getProductionURI($object->getViewURI())); From a07a8aca24623ee8549d0f966e3cf6e9c085748b Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Wed, 11 Nov 2015 08:34:34 +1100 Subject: [PATCH 26/49] Add a daemon overseer module to restart daemons when config changes Summary: Fixes T7053. Depends on D14452. Test Plan: Created a custom daemon which dumps out the config hash (by querying `PhabricatorEnv::calculateEnvironmentHash()`). Ran this daemon with `./bin/phd debug PhabricatorDebugDaemon` and saw the config hash update within 30 seconds. {P1886} Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T7053 Differential Revision: https://secure.phabricator.com/D14458 --- src/__phutil_library_map__.php | 2 + .../PhabricatorDaemonOverseerModule.php | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index ad5809a954..c89cb75f70 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2014,6 +2014,7 @@ phutil_register_library_map(array( 'PhabricatorDaemonManagementStatusWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php', 'PhabricatorDaemonManagementStopWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php', 'PhabricatorDaemonManagementWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementWorkflow.php', + 'PhabricatorDaemonOverseerModule' => 'infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php', 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php', 'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php', 'PhabricatorDaemonTasksTableView' => 'applications/daemon/view/PhabricatorDaemonTasksTableView.php', @@ -6076,6 +6077,7 @@ phutil_register_library_map(array( 'PhabricatorDaemonManagementStatusWorkflow' => 'PhabricatorDaemonManagementWorkflow', 'PhabricatorDaemonManagementStopWorkflow' => 'PhabricatorDaemonManagementWorkflow', 'PhabricatorDaemonManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorDaemonOverseerModule' => 'PhutilDaemonOverseerModule', 'PhabricatorDaemonReference' => 'Phobject', 'PhabricatorDaemonTaskGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorDaemonTasksTableView' => 'AphrontView', diff --git a/src/infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php b/src/infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php new file mode 100644 index 0000000000..e9f34028cd --- /dev/null +++ b/src/infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php @@ -0,0 +1,67 @@ +timestamp = PhabricatorTime::getNow(); + } + + public function shouldReloadDaemons() { + if ($this->timestamp < PhabricatorTime::getNow() - 10) { + return false; + } + + return $this->updateConfigVersion(); + } + + /** + * Calculate a version number for the current Phabricator configuration. + * + * The version number has no real meaning and does not provide any real + * indication of whether a configuration entry has been changed. The config + * version is intended to be a rough indicator that "something has changed", + * which indicates to the overseer that the daemons should be reloaded. + * + * @return int + */ + private function loadConfigVersion() { + $conn_r = id(new PhabricatorConfigEntry())->establishConnection('r'); + return head(queryfx_one( + $conn_r, + 'SELECT MAX(id) FROM %T', + id(new PhabricatorConfigTransaction())->getTableName())); + } + + /** + * Update the configuration version and timestamp. + * + * @return bool True if the daemons should restart, otherwise false. + */ + private function updateConfigVersion() { + $config_version = $this->loadConfigVersion(); + $this->timestamp = PhabricatorTime::getNow(); + + if (!$this->configVersion) { + $this->configVersion = $config_version; + return false; + } + + if ($this->configVersion != $config_version) { + $this->configVersion = $config_version; + return true; + } + + return false; + } + +} From 321c61a853d96ba6c98cfd6812939b9027d91008 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Wed, 11 Nov 2015 08:48:42 +1100 Subject: [PATCH 27/49] Remove daemon envHash and envInfo Summary: Ref T7053. Remove the `envHash` and `envInfo` fields, which are no longer used now that the daemons restart automagically. Depends on D14458. Test Plan: Saw no more setup issues. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: tycho.tatitscheff, epriestley Maniphest Tasks: T7053 Differential Revision: https://secure.phabricator.com/D14446 --- .../autopatches/20151110.daemonenvhash.sql | 5 + .../check/PhabricatorDaemonsSetupCheck.php | 102 ------------------ .../PhabricatorExtraConfigSetupCheck.php | 4 + .../option/PhabricatorPHDConfigOptions.php | 7 -- .../PhabricatorDaemonLogViewController.php | 8 -- .../event/PhabricatorDaemonEventListener.php | 2 - .../daemon/storage/PhabricatorDaemonLog.php | 4 - .../view/PhabricatorDaemonLogListView.php | 12 +-- src/infrastructure/env/PhabricatorEnv.php | 100 ----------------- 9 files changed, 11 insertions(+), 233 deletions(-) create mode 100644 resources/sql/autopatches/20151110.daemonenvhash.sql diff --git a/resources/sql/autopatches/20151110.daemonenvhash.sql b/resources/sql/autopatches/20151110.daemonenvhash.sql new file mode 100644 index 0000000000..bde4b21741 --- /dev/null +++ b/resources/sql/autopatches/20151110.daemonenvhash.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_daemon.daemon_log + DROP COLUMN envHash; + +ALTER TABLE {$NAMESPACE}_daemon.daemon_log + DROP COLUMN envInfo; diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index a89590cc18..0b2bd8614e 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -47,7 +47,6 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { } $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); - $environment_hash = PhabricatorEnv::calculateEnvironmentHash(); $all_daemons = id(new PhabricatorDaemonLogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) @@ -91,107 +90,6 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { ->addCommand('phabricator/ $ ./bin/phd restart'); } } - - if ($daemon->getEnvHash() != $environment_hash) { - $doc_href = PhabricatorEnv::getDocLink( - 'Managing Daemons with phd'); - - $summary = pht( - 'At least one daemon is currently running with different '. - 'configuration than the Phabricator web application.'); - - $list_section = null; - $env_info = $daemon->getEnvInfo(); - if ($env_info) { - $issues = PhabricatorEnv::compareEnvironmentInfo( - PhabricatorEnv::calculateEnvironmentInfo(), - $env_info); - - if ($issues) { - foreach ($issues as $key => $issue) { - $issues[$key] = phutil_tag('li', array(), $issue); - } - - $list_section = array( - pht( - 'The configurations differ in the following %s way(s):', - phutil_count($issues)), - phutil_tag( - 'ul', - array(), - $issues), - ); - } - } - - - $message = pht( - 'At least one daemon is currently running with a different '. - 'configuration (config checksum %s) than the web application '. - '(config checksum %s).'. - "\n\n%s". - 'This usually means that you have just made a configuration change '. - 'from the web UI, but have not yet restarted the daemons. You '. - 'need to restart the daemons after making configuration changes '. - 'so they will pick up the new values: until you do, they will '. - 'continue operating with the old settings.'. - "\n\n". - '(If you plan to make more changes, you can restart the daemons '. - 'once after you finish making all of your changes.)'. - "\n\n". - 'Use %s to restart daemons. You can find a list of running daemons '. - 'in the %s, which will also help you identify which daemon (or '. - 'daemons) have divergent configuration. For more information about '. - 'managing the daemons, see %s in the documentation.'. - "\n\n". - 'This can also happen if you use the %s environmental variable to '. - 'choose a configuration file, but the daemons run with a different '. - 'value than the web application. If restarting the daemons does '. - 'not resolve this issue and you use %s to select configuration, '. - 'check that it is set consistently.'. - "\n\n". - 'A third possible cause is that you run several machines, and '. - 'the %s configuration file differs between them. This file is '. - 'updated when you edit configuration from the CLI with %s. If '. - 'restarting the daemons does not resolve this issue and you '. - 'run multiple machines, check that all machines have identical '. - '%s configuration files.'. - "\n\n". - 'This issue is not severe, but usually indicates that something '. - 'is not configured the way you expect, and may cause the daemons '. - 'to exhibit different behavior than the web application does.', - - phutil_tag('tt', array(), substr($daemon->getEnvHash(), 0, 12)), - phutil_tag('tt', array(), substr($environment_hash, 0, 12)), - $list_section, - phutil_tag('tt', array(), 'bin/phd restart'), - phutil_tag( - 'a', - array( - 'href' => '/daemon/', - 'target' => '_blank', - ), - pht('Daemon Console')), - phutil_tag( - 'a', - array( - 'href' => $doc_href, - 'target' => '_blank', - ), - pht('Managing Daemons with phd')), - phutil_tag('tt', array(), 'PHABRICATOR_ENV'), - phutil_tag('tt', array(), 'PHABRICATOR_ENV'), - phutil_tag('tt', array(), 'phabricator/conf/local/local.json'), - phutil_tag('tt', array(), 'bin/config'), - phutil_tag('tt', array(), 'phabricator/conf/local/local.json')); - - $this->newIssue('daemons.need-restarting') - ->setName(pht('Daemons and Web Have Different Config')) - ->setSummary($summary) - ->setMessage($message) - ->addCommand('phabricator/ $ ./bin/phd restart'); - break; - } } } diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index 92cafb9449..de087096fb 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -292,6 +292,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'gcdaemon.ttl.task-archive' => $gc_reason, 'gcdaemon.ttl.general-cache' => $gc_reason, 'gcdaemon.ttl.conduit-logs' => $gc_reason, + + 'phd.variant-config' => pht( + 'This configuration is no longer relevant because daemons '. + 'restart automatically on configuration changes.'), ); return $ancient_config; diff --git a/src/applications/config/option/PhabricatorPHDConfigOptions.php b/src/applications/config/option/PhabricatorPHDConfigOptions.php index 587194cd4d..12c29da616 100644 --- a/src/applications/config/option/PhabricatorPHDConfigOptions.php +++ b/src/applications/config/option/PhabricatorPHDConfigOptions.php @@ -73,13 +73,6 @@ final class PhabricatorPHDConfigOptions "trace mode. See also '%s'.", 'phd debug', 'phd.verbose')), - $this->newOption('phd.variant-config', 'list', array()) - ->setDescription( - pht( - 'Specify config keys that can safely vary between the web tier '. - 'and the daemons. Primarily, this is a way to suppress the '. - '"Daemons and Web Have Different Config" setup issue on a per '. - 'config key basis.')), $this->newOption('phd.garbage-collection', 'wild', array()) ->setLocked(true) ->setLockedMessage( diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php index 60bf290ae5..32af8f6f13 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php @@ -58,14 +58,6 @@ final class PhabricatorDaemonLogViewController } $header->addTag($tag); - $env_hash = PhabricatorEnv::calculateEnvironmentHash(); - if ($log->getEnvHash() != $env_hash) { - $tag = id(new PHUITagView()) - ->setType(PHUITagView::TYPE_STATE) - ->setBackgroundColor(PHUITagView::COLOR_YELLOW) - ->setName(pht('Stale Config')); - $header->addTag($tag); - } $properties = $this->buildPropertyListView($log); diff --git a/src/applications/daemon/event/PhabricatorDaemonEventListener.php b/src/applications/daemon/event/PhabricatorDaemonEventListener.php index db6d9a2eff..41324cc9c9 100644 --- a/src/applications/daemon/event/PhabricatorDaemonEventListener.php +++ b/src/applications/daemon/event/PhabricatorDaemonEventListener.php @@ -42,8 +42,6 @@ final class PhabricatorDaemonEventListener extends PhabricatorEventListener { ->setHost(php_uname('n')) ->setPID(getmypid()) ->setRunningAsUser($current_user['name']) - ->setEnvHash(PhabricatorEnv::calculateEnvironmentHash()) - ->setEnvInfo(PhabricatorEnv::calculateEnvironmentInfo()) ->setStatus(PhabricatorDaemonLog::STATUS_RUNNING) ->setArgv($event->getValue('argv')) ->setExplicitArgv($event->getValue('explicitArgv')) diff --git a/src/applications/daemon/storage/PhabricatorDaemonLog.php b/src/applications/daemon/storage/PhabricatorDaemonLog.php index 6573ea8e4c..2d005e33e0 100644 --- a/src/applications/daemon/storage/PhabricatorDaemonLog.php +++ b/src/applications/daemon/storage/PhabricatorDaemonLog.php @@ -17,8 +17,6 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO protected $runningAsUser; protected $argv; protected $explicitArgv = array(); - protected $envHash; - protected $envInfo; protected $status; protected function getConfiguration() { @@ -26,14 +24,12 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO self::CONFIG_SERIALIZATION => array( 'argv' => self::SERIALIZATION_JSON, 'explicitArgv' => self::SERIALIZATION_JSON, - 'envInfo' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'daemon' => 'text255', 'host' => 'text255', 'pid' => 'uint32', 'runningAsUser' => 'text255?', - 'envHash' => 'bytes40', 'status' => 'text8', 'daemonID' => 'text64', ), diff --git a/src/applications/daemon/view/PhabricatorDaemonLogListView.php b/src/applications/daemon/view/PhabricatorDaemonLogListView.php index 8be2743bd1..6c96509505 100644 --- a/src/applications/daemon/view/PhabricatorDaemonLogListView.php +++ b/src/applications/daemon/view/PhabricatorDaemonLogListView.php @@ -17,7 +17,6 @@ final class PhabricatorDaemonLogListView extends AphrontView { throw new PhutilInvalidStateException('setUser'); } - $env_hash = PhabricatorEnv::calculateEnvironmentHash(); $list = new PHUIObjectItemListView(); $list->setFlush(true); foreach ($this->daemonLogs as $log) { @@ -33,15 +32,8 @@ final class PhabricatorDaemonLogListView extends AphrontView { $status = $log->getStatus(); switch ($status) { case PhabricatorDaemonLog::STATUS_RUNNING: - if ($env_hash != $log->getEnvHash()) { - $item->setStatusIcon('fa-warning yellow'); - $item->addAttribute(pht( - 'This daemon is running with an out of date configuration and '. - 'should be restarted.')); - } else { - $item->setStatusIcon('fa-rocket green'); - $item->addAttribute(pht('This daemon is running.')); - } + $item->setStatusIcon('fa-rocket green'); + $item->addAttribute(pht('This daemon is running.')); break; case PhabricatorDaemonLog::STATUS_DEAD: $item->setStatusIcon('fa-warning red'); diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index b576e2f83b..694c793e0a 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -277,106 +277,6 @@ final class PhabricatorEnv extends Phobject { return $env; } - public static function calculateEnvironmentHash() { - $keys = self::getKeysForConsistencyCheck(); - - $values = array(); - foreach ($keys as $key) { - $values[$key] = self::getEnvConfigIfExists($key); - } - - return PhabricatorHash::digest(json_encode($values)); - } - - /** - * Returns a summary of non-default configuration settings to allow the - * "daemons and web have different config" setup check to list divergent - * keys. - */ - public static function calculateEnvironmentInfo() { - $keys = self::getKeysForConsistencyCheck(); - - $info = array(); - - $defaults = id(new PhabricatorConfigDefaultSource())->getAllKeys(); - foreach ($keys as $key) { - $current = self::getEnvConfigIfExists($key); - $default = idx($defaults, $key, null); - if ($current !== $default) { - $info[$key] = PhabricatorHash::digestForIndex(json_encode($current)); - } - } - - $keys_hash = array_keys($defaults); - sort($keys_hash); - $keys_hash = implode("\0", $keys_hash); - $keys_hash = PhabricatorHash::digestForIndex($keys_hash); - - return array( - 'version' => 1, - 'keys' => $keys_hash, - 'values' => $info, - ); - } - - - /** - * Compare two environment info summaries to generate a human-readable - * list of discrepancies. - */ - public static function compareEnvironmentInfo(array $u, array $v) { - $issues = array(); - - $uversion = idx($u, 'version'); - $vversion = idx($v, 'version'); - if ($uversion != $vversion) { - $issues[] = pht( - 'The two configurations were generated by different versions '. - 'of Phabricator.'); - - // These may not be comparable, so stop here. - return $issues; - } - - if ($u['keys'] !== $v['keys']) { - $issues[] = pht( - 'The two configurations have different keys. This usually means '. - 'that they are running different versions of Phabricator.'); - } - - $uval = idx($u, 'values', array()); - $vval = idx($v, 'values', array()); - - $all_keys = array_keys($uval + $vval); - - foreach ($all_keys as $key) { - $uv = idx($uval, $key); - $vv = idx($vval, $key); - if ($uv !== $vv) { - if ($uv && $vv) { - $issues[] = pht( - 'The configuration key "%s" is set in both configurations, but '. - 'set to different values.', - $key); - } else { - $issues[] = pht( - 'The configuration key "%s" is set in only one configuration.', - $key); - } - } - } - - return $issues; - } - - private static function getKeysForConsistencyCheck() { - $keys = array_keys(self::getAllConfigKeys()); - sort($keys); - - $skip_keys = self::getEnvConfig('phd.variant-config'); - return array_diff($keys, $skip_keys); - } - /* -( Reading Configuration )---------------------------------------------- */ From bb9b25a7ba799d43ea4dfeafac57014a56c5f554 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Thu, 12 Nov 2015 06:33:38 +1100 Subject: [PATCH 28/49] Remove the `PhortuneNotImplementedException` class Summary: Replace `PhortuneNotImplementedException` with `PhutilMethodNotImplementedException`. Test Plan: N/A Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14445 --- src/__phutil_library_map__.php | 2 -- .../PhortuneNotImplementedException.php | 12 ------------ .../provider/PhortunePaymentProvider.php | 16 ++++++++-------- 3 files changed, 8 insertions(+), 22 deletions(-) delete mode 100644 src/applications/phortune/exception/PhortuneNotImplementedException.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c89cb75f70..a28863b4ac 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3428,7 +3428,6 @@ phutil_register_library_map(array( 'PhortuneMerchantTransactionQuery' => 'applications/phortune/query/PhortuneMerchantTransactionQuery.php', 'PhortuneMerchantViewController' => 'applications/phortune/controller/PhortuneMerchantViewController.php', 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', - 'PhortuneNotImplementedException' => 'applications/phortune/exception/PhortuneNotImplementedException.php', 'PhortuneOrderTableView' => 'applications/phortune/view/PhortuneOrderTableView.php', 'PhortunePayPalPaymentProvider' => 'applications/phortune/provider/PhortunePayPalPaymentProvider.php', 'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php', @@ -7771,7 +7770,6 @@ phutil_register_library_map(array( 'PhortuneMerchantTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhortuneMerchantViewController' => 'PhortuneMerchantController', 'PhortuneMonthYearExpiryControl' => 'AphrontFormControl', - 'PhortuneNotImplementedException' => 'Exception', 'PhortuneOrderTableView' => 'AphrontView', 'PhortunePayPalPaymentProvider' => 'PhortunePaymentProvider', 'PhortunePaymentMethod' => array( diff --git a/src/applications/phortune/exception/PhortuneNotImplementedException.php b/src/applications/phortune/exception/PhortuneNotImplementedException.php deleted file mode 100644 index eac3b3b7a8..0000000000 --- a/src/applications/phortune/exception/PhortuneNotImplementedException.php +++ /dev/null @@ -1,12 +0,0 @@ - Date: Thu, 12 Nov 2015 10:49:39 -0800 Subject: [PATCH 29/49] Fix missing EditEngineConfig on indirect pathway through conduit.query Summary: Fixes T9772. We now need an EditEngineConfiguration to do interesting things with EditEngine, but this public API wasn't properly making sure we have one. Test Plan: Called `conduit.query` from web console. Fatal prior to patch; success afterward. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9772 Differential Revision: https://secure.phabricator.com/D14475 --- .../transactions/editengine/PhabricatorEditEngine.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 3fca734330..3137a7d8bc 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -961,6 +961,11 @@ abstract class PhabricatorEditEngine } public function getAllEditTypes() { + $config = $this->loadEditEngineConfiguration(null); + if (!$config) { + return array(); + } + $object = $this->newEditableObject(); $fields = $this->buildEditFields($object); return $this->getAllEditTypesFromFields($fields); From e8fdf478bbc9d0bfe82031df370fe711e4b03607 Mon Sep 17 00:00:00 2001 From: Aviv Eyal Date: Thu, 12 Nov 2015 19:30:43 +0000 Subject: [PATCH 30/49] JIRA Integration: Link and/or Comment Summary: Current JIRA integration is quite noisy in terms of email, and makes users hunt and peck for the related revisions. Teach it to create an Issue Link on the JIRA side, and allow to disable commenting. Test Plan: comment on revision in each of the 4 settings, check JIRA end for expected result. Reviewers: btrahan, eMxyzptlk, epriestley, #blessed_reviewers, avivey Reviewed By: epriestley, #blessed_reviewers Subscribers: avivey, vhbit, jra3, eMxyzptlk, frenchs, aik099, svemir, rmuslimov, cpa199, waynea, epriestley, Korvin, hach-que Projects: #doorkeeper Maniphest Tasks: T5422 Differential Revision: https://secure.phabricator.com/D9858 --- .../provider/PhabricatorJIRAAuthProvider.php | 48 ++++++++++- .../worker/DoorkeeperJIRAFeedWorker.php | 86 ++++++++++++++++--- 2 files changed, 123 insertions(+), 11 deletions(-) diff --git a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php index c81cbe64aa..8bbbedc3c6 100644 --- a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php @@ -77,6 +77,8 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { const PROPERTY_JIRA_URI = 'oauth1:jira:uri'; const PROPERTY_PUBLIC_KEY = 'oauth1:jira:key:public'; const PROPERTY_PRIVATE_KEY = 'oauth1:jira:key:private'; + const PROPERTY_REPORT_LINK = 'oauth1:jira:report:link'; + const PROPERTY_REPORT_COMMENT = 'oauth1:jira:report:comment'; public function readFormValuesFromProvider() { @@ -100,6 +102,10 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { return array( self::PROPERTY_JIRA_NAME => $name, self::PROPERTY_JIRA_URI => $request->getStr(self::PROPERTY_JIRA_URI), + self::PROPERTY_REPORT_LINK => + $request->getInt(self::PROPERTY_REPORT_LINK, 0), + self::PROPERTY_REPORT_COMMENT => + $request->getInt(self::PROPERTY_REPORT_COMMENT, 0), ); } @@ -175,6 +181,7 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { 'JIRA 5 or earlier.')); $is_setup = $this->isSetup(); + $viewer = $request->getViewer(); $e_required = $request->isFormPost() ? null : true; @@ -249,11 +256,40 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { id(new AphrontFormStaticControl()) ->setLabel(pht('Public Key')) ->setValue($pkey)); + + $form + ->appendRemarkupInstructions( + pht( + '= Integration Options = '."\n". + 'Configure how to record Revisions on JIRA tasks.'."\n\n". + 'Note you\'ll have to restart the daemons for this to take '. + 'effect.')) + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->addCheckbox( + self::PROPERTY_REPORT_LINK, + 1, + new PHUIRemarkupView( + $viewer, + pht( + 'Create **Issue Link** to the Revision, as an "implemented '. + 'in" relationship.')), + $this->shouldCreateJIRALink())) + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->addCheckbox( + self::PROPERTY_REPORT_COMMENT, + 1, + new PHUIRemarkupView( + $viewer, + pht( + '**Post a comment** in the JIRA task, similar to the '. + 'emails Phabricator sends.')), + $this->shouldCreateJIRAComment())); } } - /** * JIRA uses a setup step to generate public/private keys. */ @@ -286,4 +322,14 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { return $adapter->newJIRAFuture($path, $method, $params); } + public function shouldCreateJIRALink() { + $config = $this->getProviderConfig(); + return $config->getProperty(self::PROPERTY_REPORT_LINK, true); + } + + public function shouldCreateJIRAComment() { + $config = $this->getProviderConfig(); + return $config->getProperty(self::PROPERTY_REPORT_COMMENT, true); + } + } diff --git a/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php b/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php index 7c6f15cf04..24631ed1b3 100644 --- a/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php +++ b/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php @@ -40,6 +40,14 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { return; } + $do_anything = ($this->shouldPostComment() || $this->shouldPostLink()); + if (!$do_anything) { + $this->log( + "%s\n", + pht('JIRA integration is configured not to post anything.')); + return; + } + $xobjs = id(new DoorkeeperExternalObjectQuery()) ->setViewer($viewer) ->withPHIDs($jira_issue_phids) @@ -60,7 +68,6 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { return; } - $story_text = $this->renderStoryText(); $xobjs = mgroup($xobjs, 'getApplicationDomain'); foreach ($xobjs as $domain => $xobj_list) { @@ -84,13 +91,16 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { foreach ($xobj_list as $xobj) { foreach ($accounts as $account) { try { - $provider->newJIRAFuture( - $account, - 'rest/api/2/issue/'.$xobj->getObjectID().'/comment', - 'POST', - array( - 'body' => $story_text, - ))->resolveJSON(); + $jira_key = $xobj->getObjectID(); + + if ($this->shouldPostComment()) { + $this->postComment($account, $jira_key); + } + + if ($this->shouldPostLink()) { + $this->postLink($account, $jira_key); + } + break; } catch (HTTPFutureResponseStatus $ex) { phlog($ex); @@ -169,14 +179,70 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { return $try_users; } + private function shouldPostComment() { + return $this->getProvider()->shouldCreateJIRAComment(); + } + + private function shouldPostLink() { + return $this->getProvider()->shouldCreateJIRALink(); + } + + private function postComment($account, $jira_key) { + $provider = $this->getProvider(); + + $provider->newJIRAFuture( + $account, + 'rest/api/2/issue/'.$jira_key.'/comment', + 'POST', + array( + 'body' => $this->renderStoryText(), + ))->resolveJSON(); + } + private function renderStoryText() { $object = $this->getStoryObject(); $publisher = $this->getPublisher(); $text = $publisher->getStoryText($object); - $uri = $publisher->getObjectURI($object); - return $text."\n\n".$uri; + if ($this->shouldPostLink()) { + return $text; + } else { + // include the link in the comment + return $text."\n\n".$publisher->getObjectURI($object); + } } + private function postLink($account, $jira_key) { + $provider = $this->getProvider(); + $object = $this->getStoryObject(); + $publisher = $this->getPublisher(); + $icon_uri = celerity_get_resource_uri('rsrc/favicons/favicon-16x16.png'); + + $provider->newJIRAFuture( + $account, + 'rest/api/2/issue/'.$jira_key.'/remotelink', + 'POST', + + // format documented at http://bit.ly/1K5T0Li + array( + 'globalId' => $object->getPHID(), + 'application' => array( + 'type' => 'com.phacility.phabricator', + 'name' => 'Phabricator', + ), + 'relationship' => 'implemented in', + 'object' => array( + 'url' => $publisher->getObjectURI($object), + 'title' => $publisher->getObjectTitle($object), + 'icon' => array( + 'url16x16' => $icon_uri, + 'title' => 'Phabricator', + ), + 'status' => array( + 'resolved' => $publisher->isObjectClosed($object), + ), + ), + ))->resolveJSON(); + } } From a1737ef9c7ba1a4e904dfe415192bbd239f095ba Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Fri, 13 Nov 2015 07:04:48 +1100 Subject: [PATCH 31/49] Fix a translation Summary: Fixes T9763. Test Plan: Merged tasks, saw translations. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T9763 Differential Revision: https://secure.phabricator.com/D14473 --- src/applications/maniphest/storage/ManiphestTransaction.php | 4 ++-- .../translation/PhabricatorUSEnglishTranslation.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php index 0456673a10..024a292337 100644 --- a/src/applications/maniphest/storage/ManiphestTransaction.php +++ b/src/applications/maniphest/storage/ManiphestTransaction.php @@ -839,9 +839,9 @@ final class ManiphestTransaction case self::TYPE_MERGED_FROM: return pht( - '%s merged %d task(s) %s into %s.', + '%s merged %s task(s) %s into %s.', $this->renderHandleLink($author_phid), - count($new), + phutil_count($new), $this->renderHandleList($new), $this->renderHandleLink($object_phid)); diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php index fd529dc35d..346e37977d 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -178,14 +178,14 @@ final class PhabricatorUSEnglishTranslation ), ), - '%s merged %d task(s): %s.' => array( + '%s merged %s task(s): %s.' => array( array( '%s merged a task: %3$s.', '%s merged tasks: %3$s.', ), ), - '%s merged %d task(s) %s into %s.' => array( + '%s merged %s task(s) %s into %s.' => array( array( '%s merged %3$s into %4$s.', '%s merged tasks %3$s into %4$s.', From feca8fbdece3ca631db09a03450dd969937bd920 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Fri, 13 Nov 2015 07:06:59 +1100 Subject: [PATCH 32/49] Use monograms for Herald URIs Summary: I think `HeraldRule`s are the only objects which have monograms but are not accesible via `/{$monogram}`. This diff changes the `/herald/rule/{$id}` URI to `/{$monogram}`. Test Plan: Clicked a bunch of links in Herald to ensure there were no dead links. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14469 --- .../application/PhabricatorHeraldApplication.php | 2 +- .../herald/controller/HeraldDisableController.php | 2 +- .../herald/controller/HeraldRuleController.php | 4 ++-- .../herald/controller/HeraldRuleViewController.php | 2 +- src/applications/herald/phid/HeraldRulePHIDType.php | 10 +++++----- .../herald/query/HeraldRuleSearchEngine.php | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/applications/herald/application/PhabricatorHeraldApplication.php b/src/applications/herald/application/PhabricatorHeraldApplication.php index e3537d9722..bef94b6ac4 100644 --- a/src/applications/herald/application/PhabricatorHeraldApplication.php +++ b/src/applications/herald/application/PhabricatorHeraldApplication.php @@ -47,10 +47,10 @@ final class PhabricatorHeraldApplication extends PhabricatorApplication { public function getRoutes() { return array( + '/H(?P[1-9]\d*)' => 'HeraldRuleViewController', '/herald/' => array( '(?:query/(?P[^/]+)/)?' => 'HeraldRuleListController', 'new/' => 'HeraldNewController', - 'rule/(?P[1-9]\d*)/' => 'HeraldRuleViewController', 'edit/(?:(?P[1-9]\d*)/)?' => 'HeraldRuleController', 'disable/(?P[1-9]\d*)/(?P\w+)/' => 'HeraldDisableController', diff --git a/src/applications/herald/controller/HeraldDisableController.php b/src/applications/herald/controller/HeraldDisableController.php index 054f30e7d3..bdbefa55ea 100644 --- a/src/applications/herald/controller/HeraldDisableController.php +++ b/src/applications/herald/controller/HeraldDisableController.php @@ -25,7 +25,7 @@ final class HeraldDisableController extends HeraldController { HeraldManageGlobalRulesCapability::CAPABILITY); } - $view_uri = $this->getApplicationURI("rule/{$id}/"); + $view_uri = '/'.$rule->getMonogram(); $is_disable = ($action === 'disable'); diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php index 37a583c7ef..510113b0de 100644 --- a/src/applications/herald/controller/HeraldRuleController.php +++ b/src/applications/herald/controller/HeraldRuleController.php @@ -22,7 +22,7 @@ final class HeraldRuleController extends HeraldController { if (!$rule) { return new Aphront404Response(); } - $cancel_uri = $this->getApplicationURI("rule/{$id}/"); + $cancel_uri = '/'.$rule->getMonogram(); } else { $new_uri = $this->getApplicationURI('new/'); @@ -128,7 +128,7 @@ final class HeraldRuleController extends HeraldController { list($e_name, $errors) = $this->saveRule($adapter, $rule, $request); if (!$errors) { $id = $rule->getID(); - $uri = $this->getApplicationURI("rule/{$id}/"); + $uri = '/'.$rule->getMonogram(); return id(new AphrontRedirectResponse())->setURI($uri); } } diff --git a/src/applications/herald/controller/HeraldRuleViewController.php b/src/applications/herald/controller/HeraldRuleViewController.php index f6ec235a9e..2063ebcc2f 100644 --- a/src/applications/herald/controller/HeraldRuleViewController.php +++ b/src/applications/herald/controller/HeraldRuleViewController.php @@ -67,7 +67,7 @@ final class HeraldRuleViewController extends HeraldController { $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($rule) - ->setObjectURI($this->getApplicationURI("rule/{$id}/")); + ->setObjectURI('/'.$rule->getMonogram()); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, diff --git a/src/applications/herald/phid/HeraldRulePHIDType.php b/src/applications/herald/phid/HeraldRulePHIDType.php index f8d5c25db4..34c6d80c5f 100644 --- a/src/applications/herald/phid/HeraldRulePHIDType.php +++ b/src/applications/herald/phid/HeraldRulePHIDType.php @@ -32,12 +32,12 @@ final class HeraldRulePHIDType extends PhabricatorPHIDType { foreach ($handles as $phid => $handle) { $rule = $objects[$phid]; - $id = $rule->getID(); - $name = $rule->getName(); + $monogram = $rule->getMonogram(); + $name = $rule->getName(); - $handle->setName("H{$id}"); - $handle->setFullName("H{$id} {$name}"); - $handle->setURI("/herald/rule/{$id}/"); + $handle->setName($monogram); + $handle->setFullName("{$monogram} {$name}"); + $handle->setURI("/{$monogram}"); } } diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php index 04b7a5d852..ad4c9a7ad2 100644 --- a/src/applications/herald/query/HeraldRuleSearchEngine.php +++ b/src/applications/herald/query/HeraldRuleSearchEngine.php @@ -173,12 +173,12 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { $list = id(new PHUIObjectItemListView()) ->setUser($viewer); foreach ($rules as $rule) { - $id = $rule->getID(); + $monogram = $rule->getMonogram(); $item = id(new PHUIObjectItemView()) - ->setObjectName("H{$id}") + ->setObjectName($monogram) ->setHeader($rule->getName()) - ->setHref($this->getApplicationURI("rule/{$id}/")); + ->setHref("/{$monogram}"); if ($rule->isPersonalRule()) { $item->addIcon('fa-user', pht('Personal Rule')); From 4e4ab36f0645c685490373d3292789bfa831c5aa Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Fri, 13 Nov 2015 07:09:12 +1100 Subject: [PATCH 33/49] Apply phutil XHPAST linter standard Summary: Depends on D13867. Test Plan: N/A Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14472 --- .arclint | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.arclint b/.arclint index 0077bdf518..1b5b63976a 100644 --- a/.arclint +++ b/.arclint @@ -57,13 +57,6 @@ "type": "phutil-library", "include": "(\\.php$)" }, - "phutil-xhpast": { - "type": "phutil-xhpast", - "include": "(\\.php$)", - "phutil-xhpast.deprecated.functions": { - "phutil_escape_html": "The phutil_escape_html() function is deprecated. Raw strings passed to phutil_tag() or hsprintf() are escaped automatically." - } - }, "spelling": { "type": "spelling" }, @@ -73,15 +66,7 @@ "xhpast": { "type": "xhpast", "include": "(\\.php$)", - "severity": { - "16": "advice", - "34": "error" - }, - "xhpast.blacklisted.function": { - "eval": "The eval() function should be avoided. It is potentially unsafe and makes debugging more difficult." - }, - "xhpast.php-version": "5.2.3", - "xhpast.php-version.windows": "5.3.0" + "standard": "phutil.xhpast" } } } From 50d158a8c4d9180ea0987a8847187567cbf42112 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 13 Nov 2015 16:52:05 +0000 Subject: [PATCH 34/49] Fix publish time on Phame Posts Summary: This logic is inverted. Re-vert it. Test Plan: Write and publish a new post, see publish time. Reviewers: epriestley, joshuaspence Reviewed By: joshuaspence Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14464 --- src/applications/phame/editor/PhamePostEditor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index abe7dfe097..330ce86c19 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -65,9 +65,9 @@ final class PhamePostEditor return $object->setBody($xaction->getNewValue()); case PhamePostTransaction::TYPE_VISIBILITY: if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) { - $object->setDatePublished(time()); - } else { $object->setDatePublished(0); + } else { + $object->setDatePublished(time()); } return $object->setVisibility($xaction->getNewValue()); } From ca0b36c174b436dc686ea6a5c53cfe59948bfeec Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Sat, 14 Nov 2015 21:41:27 +1100 Subject: [PATCH 35/49] Rename XHPAST database Summary: Rename the XHPAST database from `{$NAMESPACE}_xpastview` to `{$NAMESPACE}_xhpast`. Test Plan: Ran `./bin/storage --namespace test upgrade --no-quickstart`. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14442 --- resources/sql/autopatches/20151109.xhpast.db.1.sql | 2 ++ resources/sql/autopatches/20151109.xhpast.db.2.sql | 1 + src/__phutil_library_map__.php | 8 ++++---- .../controller/PhabricatorXHPASTViewPanelController.php | 2 +- .../controller/PhabricatorXHPASTViewRunController.php | 2 +- src/applications/phpast/storage/PhabricatorXHPASTDAO.php | 8 ++++++++ ...STViewParseTree.php => PhabricatorXHPASTParseTree.php} | 2 +- .../phpast/storage/PhabricatorXHPASTViewDAO.php | 8 -------- .../storage/patch/PhabricatorBuiltinPatchList.php | 5 ++++- 9 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 resources/sql/autopatches/20151109.xhpast.db.1.sql create mode 100644 resources/sql/autopatches/20151109.xhpast.db.2.sql create mode 100644 src/applications/phpast/storage/PhabricatorXHPASTDAO.php rename src/applications/phpast/storage/{PhabricatorXHPASTViewParseTree.php => PhabricatorXHPASTParseTree.php} (84%) delete mode 100644 src/applications/phpast/storage/PhabricatorXHPASTViewDAO.php diff --git a/resources/sql/autopatches/20151109.xhpast.db.1.sql b/resources/sql/autopatches/20151109.xhpast.db.1.sql new file mode 100644 index 0000000000..acf8b36297 --- /dev/null +++ b/resources/sql/autopatches/20151109.xhpast.db.1.sql @@ -0,0 +1,2 @@ +RENAME TABLE {$NAMESPACE}_xhpastview.xhpastview_parsetree + TO {$NAMESPACE}_xhpast.xhpast_parsetree; diff --git a/resources/sql/autopatches/20151109.xhpast.db.2.sql b/resources/sql/autopatches/20151109.xhpast.db.2.sql new file mode 100644 index 0000000000..a4a79bdced --- /dev/null +++ b/resources/sql/autopatches/20151109.xhpast.db.2.sql @@ -0,0 +1 @@ +DROP DATABASE {$NAMESPACE}_xhpastview; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index a28863b4ac..0f2e784d60 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3245,13 +3245,13 @@ phutil_register_library_map(array( 'PhabricatorWorkingCopyDiscoveryTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php', 'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php', 'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php', + 'PhabricatorXHPASTDAO' => 'applications/phpast/storage/PhabricatorXHPASTDAO.php', + 'PhabricatorXHPASTParseTree' => 'applications/phpast/storage/PhabricatorXHPASTParseTree.php', 'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php', - 'PhabricatorXHPASTViewDAO' => 'applications/phpast/storage/PhabricatorXHPASTViewDAO.php', 'PhabricatorXHPASTViewFrameController' => 'applications/phpast/controller/PhabricatorXHPASTViewFrameController.php', 'PhabricatorXHPASTViewFramesetController' => 'applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php', 'PhabricatorXHPASTViewInputController' => 'applications/phpast/controller/PhabricatorXHPASTViewInputController.php', 'PhabricatorXHPASTViewPanelController' => 'applications/phpast/controller/PhabricatorXHPASTViewPanelController.php', - 'PhabricatorXHPASTViewParseTree' => 'applications/phpast/storage/PhabricatorXHPASTViewParseTree.php', 'PhabricatorXHPASTViewRunController' => 'applications/phpast/controller/PhabricatorXHPASTViewRunController.php', 'PhabricatorXHPASTViewStreamController' => 'applications/phpast/controller/PhabricatorXHPASTViewStreamController.php', 'PhabricatorXHPASTViewTreeController' => 'applications/phpast/controller/PhabricatorXHPASTViewTreeController.php', @@ -7534,13 +7534,13 @@ phutil_register_library_map(array( 'PhabricatorWorkingCopyDiscoveryTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase', + 'PhabricatorXHPASTDAO' => 'PhabricatorLiskDAO', + 'PhabricatorXHPASTParseTree' => 'PhabricatorXHPASTDAO', 'PhabricatorXHPASTViewController' => 'PhabricatorController', - 'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO', 'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController', - 'PhabricatorXHPASTViewParseTree' => 'PhabricatorXHPASTViewDAO', 'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController', diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php index 7238b36381..af6c1ee761 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php @@ -12,7 +12,7 @@ abstract class PhabricatorXHPASTViewPanelController public function willProcessRequest(array $data) { $this->id = $data['id']; - $this->storageTree = id(new PhabricatorXHPASTViewParseTree()) + $this->storageTree = id(new PhabricatorXHPASTParseTree()) ->load($this->id); if (!$this->storageTree) { diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php index dc2224d291..290666e375 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php @@ -21,7 +21,7 @@ final class PhabricatorXHPASTViewRunController list($err, $stdout, $stderr) = $resolved; - $storage_tree = id(new PhabricatorXHPASTViewParseTree()) + $storage_tree = id(new PhabricatorXHPASTParseTree()) ->setInput($source) ->setReturnCode($err) ->setStdout($stdout) diff --git a/src/applications/phpast/storage/PhabricatorXHPASTDAO.php b/src/applications/phpast/storage/PhabricatorXHPASTDAO.php new file mode 100644 index 0000000000..e22b0ed94f --- /dev/null +++ b/src/applications/phpast/storage/PhabricatorXHPASTDAO.php @@ -0,0 +1,8 @@ + array(), 'db.worker' => array(), - 'db.xhpastview' => array(), + 'db.xhpast' => array(), + 'db.xhpastview' => array( + 'dead' => true, + ), 'db.cache' => array(), 'db.fact' => array(), 'db.ponder' => array(), From 1f1c3f40755edf21795db47b444d91ad06eacf6a Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Sun, 15 Nov 2015 19:50:10 +0000 Subject: [PATCH 36/49] Allow lint messages to be rendered as Remarkup Summary: Some linter messages, such as those produced by `ArcanistPHPCompatibilityXHPASTLinterRule`, contain backticks but are currently rendered as Remarkup literals. I think that it is generally desirable to allow lint messages to be rendered as Remarkup, although we should ideally have a way to render Remarkup for use on the command line (I actually think that this already exists, but I don't think that `arc lint` does this when rendering linter messages). Test Plan: Resubmitted D14481 to my dev install and saw Remarkuped lint messages. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14485 --- .../controller/DifferentialChangesetViewController.php | 1 - .../diff/view/PHUIDiffInlineCommentDetailView.php | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index a73875c8f8..3f08ce4883 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -387,7 +387,6 @@ final class DifferentialChangesetViewController extends DifferentialController { $inlines = array(); foreach ($messages as $message) { $description = $message->getProperty('description'); - $description = '%%%'.$description.'%%%'; $inlines[] = id(clone $template) ->setSyntheticAuthor(pht('Lint: %s', $message->getName())) diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index 99ac7e1194..2c47c744ac 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -227,9 +227,7 @@ final class PHUIDiffInlineCommentDetailView $action_buttons = array(); if ($this->allowReply) { - if (!$is_synthetic) { - // NOTE: No product reason why you can't reply to these, but the reply // mechanism currently sends the inline comment ID to the server, not // file/line information, and synthetic comments don't have an inline @@ -242,7 +240,6 @@ final class PHUIDiffInlineCommentDetailView ->addSigil('differential-inline-reply') ->setMustCapture(true); } - } } @@ -267,11 +264,11 @@ final class PHUIDiffInlineCommentDetailView $links[] = javelin_tag( 'a', array( - 'class' => 'inline-button-divider pml msl', - 'meta' => array( + 'class' => 'inline-button-divider pml msl', + 'meta' => array( 'anchor' => $anchor_name, ), - 'sigil' => 'differential-inline-preview-jump', + 'sigil' => 'differential-inline-preview-jump', ), pht('Not Visible')); From 67b6c532bc54eea579807660c30b53543b059e53 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Mon, 16 Nov 2015 07:14:55 +1100 Subject: [PATCH 37/49] Allow device bindings to be destroyed Summary: Ref T9762. Currently it is not possible to destroy an Alamanac device because any associate bindings cannot be destroyed. Test Plan: Destroyed an Almanac device. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T9762 Differential Revision: https://secure.phabricator.com/D14461 --- src/__phutil_library_map__.php | 1 + src/applications/almanac/storage/AlmanacBinding.php | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0f2e784d60..004a262656 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3755,6 +3755,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldInterface', 'PhabricatorApplicationTransactionInterface', 'AlmanacPropertyInterface', + 'PhabricatorDestructibleInterface', ), 'AlmanacBindingEditController' => 'AlmanacServiceController', 'AlmanacBindingEditor' => 'PhabricatorApplicationTransactionEditor', diff --git a/src/applications/almanac/storage/AlmanacBinding.php b/src/applications/almanac/storage/AlmanacBinding.php index b6c3a35cca..8f56c86c1a 100644 --- a/src/applications/almanac/storage/AlmanacBinding.php +++ b/src/applications/almanac/storage/AlmanacBinding.php @@ -6,7 +6,8 @@ final class AlmanacBinding PhabricatorPolicyInterface, PhabricatorCustomFieldInterface, PhabricatorApplicationTransactionInterface, - AlmanacPropertyInterface { + AlmanacPropertyInterface, + PhabricatorDestructibleInterface { protected $servicePHID; protected $devicePHID; @@ -204,4 +205,14 @@ final class AlmanacBinding return $timeline; } +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + + $this->delete(); + } + + } From 1a84a2fe4bc485fb7aa6e03241684bc506ccb191 Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Sun, 15 Nov 2015 20:58:50 +0000 Subject: [PATCH 38/49] Permanently destroy Almanac properties with the destruction engine Summary: As suggested in D14461. Test Plan: Used `./bin/remove destroy` on an Almanac service with properties attached, saw entries removed from the `phabricator_almanac.almanac_property` table. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14490 --- .../engine/PhabricatorDestructionEngine.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/applications/system/engine/PhabricatorDestructionEngine.php b/src/applications/system/engine/PhabricatorDestructionEngine.php index fd7ba7c30a..607c526c14 100644 --- a/src/applications/system/engine/PhabricatorDestructionEngine.php +++ b/src/applications/system/engine/PhabricatorDestructionEngine.php @@ -92,6 +92,10 @@ final class PhabricatorDestructionEngine extends Phobject { $token->delete(); } } + + if ($object instanceof AlmanacPropertyInterface) { + $this->destroyAlmanacProperties($object_phid); + } } private function destroyEdges($src_phid) { @@ -148,4 +152,15 @@ final class PhabricatorDestructionEngine extends Phobject { $object_phid); } + private function destroyAlmanacProperties($object_phid) { + $table = new AlmanacProperty(); + $conn_w = $table->establishConnection('w'); + + queryfx( + $conn_w, + 'DELETE FROM %T WHERE objectPHID = %s', + $table->getTableName(), + $object_phid); + } + } From 5963c4c9e06d3ead8d1022dcea6f09d17822a3eb Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 16 Nov 2015 08:32:38 -0800 Subject: [PATCH 39/49] Fix history link in Diffusion non-root browse views Summary: Fixes T9798. That task has good repro instructions. In sub-views, we don't link the "History" icon correctly -- we only link it to `history/README` instead of `history/path/to/README`. Add the full path. Also canonicalize the paths in a slightly prettier and more consistenty way. Test Plan: Viewed root and non-root browse tables, saw links show up properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9798 Differential Revision: https://secure.phabricator.com/D14491 --- .../diffusion/view/DiffusionBrowseTableView.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/applications/diffusion/view/DiffusionBrowseTableView.php b/src/applications/diffusion/view/DiffusionBrowseTableView.php index 71cc659897..6354c5dc5e 100644 --- a/src/applications/diffusion/view/DiffusionBrowseTableView.php +++ b/src/applications/diffusion/view/DiffusionBrowseTableView.php @@ -30,8 +30,7 @@ final class DiffusionBrowseTableView extends DiffusionView { $rows = array(); $show_edit = false; foreach ($this->paths as $path) { - - $history_link = $this->linkHistory($path->getPath()); + $full_path = $base_path.$path->getPath(); $dir_slash = null; $file_type = $path->getFileType(); @@ -40,11 +39,13 @@ final class DiffusionBrowseTableView extends DiffusionView { $dir_slash = '/'; $browse_link = phutil_tag('strong', array(), $this->linkBrowse( - $base_path.$path->getPath().$dir_slash, + $full_path.$dir_slash, array( 'type' => $file_type, 'name' => $browse_text, ))); + + $history_path = $full_path.'/'; } else if ($file_type == DifferentialChangeType::FILE_SUBMODULE) { $browse_text = $path->getPath().'/'; $browse_link = phutil_tag('strong', array(), $this->linkBrowse( @@ -55,16 +56,22 @@ final class DiffusionBrowseTableView extends DiffusionView { 'hash' => $path->getHash(), 'external' => $path->getExternalURI(), ))); + + $history_path = $full_path.'/'; } else { $browse_text = $path->getPath(); $browse_link = $this->linkBrowse( - $base_path.$path->getPath(), + $full_path, array( 'type' => $file_type, 'name' => $browse_text, )); + + $history_path = $full_path; } + $history_link = $this->linkHistory($history_path); + $dict = array( 'lint' => celerity_generate_unique_node_id(), 'commit' => celerity_generate_unique_node_id(), @@ -73,7 +80,7 @@ final class DiffusionBrowseTableView extends DiffusionView { 'details' => celerity_generate_unique_node_id(), ); - $need_pull[$base_path.$path->getPath().$dir_slash] = $dict; + $need_pull[$full_path.$dir_slash] = $dict; foreach ($dict as $k => $uniq) { $dict[$k] = phutil_tag('span', array('id' => $uniq), ''); } From 12dd9ec3ff34d0037dacf43ce912b9eaa4a9faff Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 16 Nov 2015 09:24:50 -0800 Subject: [PATCH 40/49] Have EditEngine API methods provide the correct application to Conduit Summary: Fixes T9799. Currently, if you can't see an application like Paste, we fatal when trying to generate a result for `conduit.query`, because the new EditEngine-based `paste.edit` method doesn't "know" that it's a "Paste" method. Straighten this out, and use policies and queries a little more correctly/consistently. Test Plan: - Called `conduit.query` as a user who does not have permission to use Paste. - Before change: fatal. - After change: results, excluding "paste.*" methods. Reviewers: chad Reviewed By: chad Subscribers: cburroughs Maniphest Tasks: T9799 Differential Revision: https://secure.phabricator.com/D14492 --- .../method/ConduitQueryConduitAPIMethod.php | 18 ++++----- .../query/PhabricatorConduitMethodQuery.php | 37 +++++++++++++++++++ .../editor/PhabricatorPasteEditEngine.php | 4 ++ .../editengine/PhabricatorEditEngine.php | 1 + .../PhabricatorEditEngineAPIMethod.php | 6 +++ ...catorEditEngineConfigurationEditEngine.php | 4 ++ 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php index 9a982f49b6..04e8b6d05b 100644 --- a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php @@ -19,20 +19,20 @@ final class ConduitQueryConduitAPIMethod extends ConduitAPIMethod { } protected function execute(ConduitAPIRequest $request) { - $classes = id(new PhutilClassMapQuery()) - ->setAncestorClass('ConduitAPIMethod') + $methods = id(new PhabricatorConduitMethodQuery()) + ->setViewer($request->getUser()) ->execute(); - $names_to_params = array(); - foreach ($classes as $class) { - $names_to_params[$class->getAPIMethodName()] = array( - 'description' => $class->getMethodDescription(), - 'params' => $class->getParamTypes(), - 'return' => $class->getReturnType(), + $map = array(); + foreach ($methods as $method) { + $map[$method->getAPIMethodName()] = array( + 'description' => $method->getMethodDescription(), + 'params' => $method->getParamTypes(), + 'return' => $method->getReturnType(), ); } - return $names_to_params; + return $map; } } diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index e1e7e1914b..cb30bf5e55 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -115,6 +115,43 @@ final class PhabricatorConduitMethodQuery return $methods; } + protected function willFilterPage(array $methods) { + $application_phids = array(); + foreach ($methods as $method) { + $application = $method->getApplication(); + if ($application === null) { + continue; + } + $application_phids[] = $application->getPHID(); + } + + if ($application_phids) { + $applications = id(new PhabricatorApplicationQuery()) + ->setParentQuery($this) + ->setViewer($this->getViewer()) + ->withPHIDs($application_phids) + ->execute(); + $applications = mpull($applications, null, 'getPHID'); + } else { + $applications = array(); + } + + // Remove methods which belong to an application the viewer can not see. + foreach ($methods as $key => $method) { + $application = $method->getApplication(); + if ($application === null) { + continue; + } + + if (empty($applications[$application->getPHID()])) { + $this->didRejectResult($method); + unset($methods[$key]); + } + } + + return $methods; + } + public function getQueryApplicationClass() { return 'PhabricatorConduitApplication'; } diff --git a/src/applications/paste/editor/PhabricatorPasteEditEngine.php b/src/applications/paste/editor/PhabricatorPasteEditEngine.php index 0198507fa0..ce03a7ff06 100644 --- a/src/applications/paste/editor/PhabricatorPasteEditEngine.php +++ b/src/applications/paste/editor/PhabricatorPasteEditEngine.php @@ -9,6 +9,10 @@ final class PhabricatorPasteEditEngine return pht('Pastes'); } + public function getEngineApplicationClass() { + return 'PhabricatorPasteApplication'; + } + protected function newEditableObject() { return PhabricatorPaste::initializeNewPaste($this->getViewer()); } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 3137a7d8bc..13c2b3e017 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -50,6 +50,7 @@ abstract class PhabricatorEditEngine /* -( Managing Fields )---------------------------------------------------- */ + abstract public function getEngineApplicationClass(); abstract protected function buildCustomEditFields($object); final protected function buildEditFields($object) { diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index dafc56c825..cb7662c7ba 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -5,6 +5,12 @@ abstract class PhabricatorEditEngineAPIMethod abstract public function newEditEngine(); + public function getApplication() { + $engine = $this->newEditEngine(); + $class = $engine->getEngineApplicationClass(); + return PhabricatorApplication::getByClass($class); + } + public function getMethodStatus() { return self::METHOD_STATUS_UNSTABLE; } diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php index 17b604b7b2..e47447c351 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php @@ -20,6 +20,10 @@ final class PhabricatorEditEngineConfigurationEditEngine return pht('Edit Configurations'); } + public function getEngineApplicationClass() { + return 'PhabricatorTransactionsApplication'; + } + protected function newEditableObject() { return PhabricatorEditEngineConfiguration::initializeNewConfiguration( $this->getViewer(), From cf2eb0dd5f92b39eb34da6cf6e805d4a2134db1d Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Tue, 17 Nov 2015 06:26:13 +1100 Subject: [PATCH 41/49] Move some files around Summary: Move some `PhabricatorPolicyRule` implementations to a subdirectory of the parent application. Test Plan: N/A Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14478 --- src/__phutil_library_map__.php | 8 ++++---- .../PhabricatorLegalpadSignaturePolicyRule.php | 1 - .../PhabricatorAdministratorsPolicyRule.php | 0 .../policyrule}/PhabricatorUsersPolicyRule.php | 0 .../policy/rule/PhabricatorLunarPhasePolicyRule.php | 12 ++++++------ .../policyrule}/PhabricatorProjectsPolicyRule.php | 3 ++- 6 files changed, 12 insertions(+), 12 deletions(-) rename src/applications/{policy/rule => legalpad/policyrule}/PhabricatorLegalpadSignaturePolicyRule.php (99%) rename src/applications/{policy/rule => people/policyrule}/PhabricatorAdministratorsPolicyRule.php (100%) rename src/applications/{policy/rule => people/policyrule}/PhabricatorUsersPolicyRule.php (100%) rename src/applications/{policy/rule => project/policyrule}/PhabricatorProjectsPolicyRule.php (95%) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 004a262656..bcb6f630cc 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1556,7 +1556,7 @@ phutil_register_library_map(array( 'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php', 'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php', 'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php', - 'PhabricatorAdministratorsPolicyRule' => 'applications/policy/rule/PhabricatorAdministratorsPolicyRule.php', + 'PhabricatorAdministratorsPolicyRule' => 'applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php', 'PhabricatorAjaxRequestExceptionHandler' => 'aphront/handler/PhabricatorAjaxRequestExceptionHandler.php', 'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php', 'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php', @@ -2326,7 +2326,7 @@ phutil_register_library_map(array( 'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php', 'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php', 'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php', - 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php', + 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php', 'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php', 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', 'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php', @@ -2787,7 +2787,7 @@ phutil_register_library_map(array( 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', - 'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php', + 'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php', 'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php', 'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', 'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', @@ -3197,7 +3197,7 @@ phutil_register_library_map(array( 'PhabricatorUserTitleField' => 'applications/people/customfield/PhabricatorUserTitleField.php', 'PhabricatorUserTransaction' => 'applications/people/storage/PhabricatorUserTransaction.php', 'PhabricatorUsersEditField' => 'applications/transactions/editfield/PhabricatorUsersEditField.php', - 'PhabricatorUsersPolicyRule' => 'applications/policy/rule/PhabricatorUsersPolicyRule.php', + 'PhabricatorUsersPolicyRule' => 'applications/people/policyrule/PhabricatorUsersPolicyRule.php', 'PhabricatorUsersSearchField' => 'applications/people/searchfield/PhabricatorUsersSearchField.php', 'PhabricatorVCSResponse' => 'applications/repository/response/PhabricatorVCSResponse.php', 'PhabricatorVeryWowEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorVeryWowEnglishTranslation.php', diff --git a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php b/src/applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php similarity index 99% rename from src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php rename to src/applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php index 3aa7ef8d9c..423b19e510 100644 --- a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php +++ b/src/applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php @@ -66,7 +66,6 @@ final class PhabricatorLegalpadSignaturePolicyRule ->setViewer($viewer) ->withPHIDs($value) ->execute(); - return mpull($handles, 'getFullName', 'getPHID'); } diff --git a/src/applications/policy/rule/PhabricatorAdministratorsPolicyRule.php b/src/applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php similarity index 100% rename from src/applications/policy/rule/PhabricatorAdministratorsPolicyRule.php rename to src/applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php diff --git a/src/applications/policy/rule/PhabricatorUsersPolicyRule.php b/src/applications/people/policyrule/PhabricatorUsersPolicyRule.php similarity index 100% rename from src/applications/policy/rule/PhabricatorUsersPolicyRule.php rename to src/applications/people/policyrule/PhabricatorUsersPolicyRule.php diff --git a/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php b/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php index a3336a7971..c5ea04881b 100644 --- a/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php +++ b/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php @@ -2,8 +2,8 @@ final class PhabricatorLunarPhasePolicyRule extends PhabricatorPolicyRule { - const PHASE_FULL = 'full'; - const PHASE_NEW = 'new'; + const PHASE_FULL = 'full'; + const PHASE_NEW = 'new'; const PHASE_WAXING = 'waxing'; const PHASE_WANING = 'waning'; @@ -27,9 +27,9 @@ final class PhabricatorLunarPhasePolicyRule extends PhabricatorPolicyRule { return $moon->isWaxing(); case 'waning': return $moon->isWaning(); + default: + return false; } - - return false; } public function getValueControlType() { @@ -39,8 +39,8 @@ final class PhabricatorLunarPhasePolicyRule extends PhabricatorPolicyRule { public function getValueControlTemplate() { return array( 'options' => array( - self::PHASE_FULL => pht('is full'), - self::PHASE_NEW => pht('is new'), + self::PHASE_FULL => pht('is full'), + self::PHASE_NEW => pht('is new'), self::PHASE_WAXING => pht('is waxing'), self::PHASE_WANING => pht('is waning'), ), diff --git a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php b/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php similarity index 95% rename from src/applications/policy/rule/PhabricatorProjectsPolicyRule.php rename to src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php index c3fbfd4f7e..1782b62a9d 100644 --- a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php +++ b/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php @@ -1,6 +1,7 @@ Date: Tue, 17 Nov 2015 06:27:12 +1100 Subject: [PATCH 42/49] Make Herald rules subscribable Summary: Fixes T9757. Test Plan: Created a Herald rule and then subscribed to it with a different account. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T9757 Differential Revision: https://secure.phabricator.com/D14468 --- .../sql/autopatches/20151112.herald.edge.sql | 16 +++++++++++++++ src/__phutil_library_map__.php | 1 + .../herald/storage/HeraldRule.php | 20 ++++++++++++++++++- .../herald/storage/HeraldSchemaSpec.php | 1 + 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 resources/sql/autopatches/20151112.herald.edge.sql diff --git a/resources/sql/autopatches/20151112.herald.edge.sql b/resources/sql/autopatches/20151112.herald.edge.sql new file mode 100644 index 0000000000..db71dfbd79 --- /dev/null +++ b/resources/sql/autopatches/20151112.herald.edge.sql @@ -0,0 +1,16 @@ +CREATE TABLE {$NAMESPACE}_herald.edge ( + src VARBINARY(64) NOT NULL, + type INT UNSIGNED NOT NULL, + dst VARBINARY(64) NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + seq INT UNSIGNED NOT NULL, + dataID INT UNSIGNED, + PRIMARY KEY (src, type, dst), + KEY `src` (src, type, dateCreated, seq), + UNIQUE KEY `key_dst` (dst, type, src) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; + +CREATE TABLE {$NAMESPACE}_herald.edgedata ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT} +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bcb6f630cc..83b604c6f6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5073,6 +5073,7 @@ phutil_register_library_map(array( 'PhabricatorFlaggableInterface', 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorSubscribableInterface', ), 'HeraldRuleController' => 'HeraldController', 'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', diff --git a/src/applications/herald/storage/HeraldRule.php b/src/applications/herald/storage/HeraldRule.php index efdfcdece6..51707fcc8a 100644 --- a/src/applications/herald/storage/HeraldRule.php +++ b/src/applications/herald/storage/HeraldRule.php @@ -5,7 +5,8 @@ final class HeraldRule extends HeraldDAO PhabricatorApplicationTransactionInterface, PhabricatorFlaggableInterface, PhabricatorPolicyInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorSubscribableInterface { const TABLE_RULE_APPLIED = 'herald_ruleapplied'; @@ -320,8 +321,25 @@ final class HeraldRule extends HeraldDAO } +/* -( PhabricatorSubscribableInterface )----------------------------------- */ + + + public function isAutomaticallySubscribed($phid) { + return $this->isPersonalRule() && $phid == $this->getAuthorPHID(); + } + + public function shouldShowSubscribersProperty() { + return true; + } + + public function shouldAllowSubscription($phid) { + return true; + } + + /* -( PhabricatorDestructibleInterface )----------------------------------- */ + public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { diff --git a/src/applications/herald/storage/HeraldSchemaSpec.php b/src/applications/herald/storage/HeraldSchemaSpec.php index 33045634d7..1b7b574296 100644 --- a/src/applications/herald/storage/HeraldSchemaSpec.php +++ b/src/applications/herald/storage/HeraldSchemaSpec.php @@ -33,6 +33,7 @@ final class HeraldSchemaSpec extends PhabricatorConfigSchemaSpec { 'unique' => true, ), )); + $this->buildEdgeSchemata(new HeraldRule()); } } From 5aae89babb73b82d14c737ea87104269f4f27e36 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 16 Nov 2015 09:53:01 -0800 Subject: [PATCH 43/49] Fix file PHID extraction in Owners and Differential Summary: Ref T9787. To fix this, I want to change how file PHIDs are extracted slightly: specifically, I'm going to extract them later in the editing process. Before doing this, clean up a couple of bad implementations: - Owners extracts its description as a file PHID. This is an error. - Extract the description as a remarkup block instead. - Add an edge table so stuff like file attachment works properly. - Differential has a no-op extract method. This is presumably just a copy/paste issue from long ago. Test Plan: - Edited a revision in Differential. - Dropped a file into the description of an Owners package. - Before change: this did not attach the file. - After change: the file now attaches properly and shows up as "Attached" in the file details. Reviewers: chad, joshuaspence Reviewed By: joshuaspence Subscribers: joshuaspence Maniphest Tasks: T9787 Differential Revision: https://secure.phabricator.com/D14493 --- .../sql/autopatches/20151116.owners.edge.sql | 16 ++++++++++++++++ src/__phutil_library_map__.php | 2 ++ .../editor/DifferentialTransactionEditor.php | 9 --------- ...PhabricatorOwnersPackageTransactionEditor.php | 12 ------------ .../PhabricatorOwnersPackageTransaction.php | 12 ++++++++++++ .../storage/PhabricatorOwnersSchemaSpec.php | 10 ++++++++++ 6 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 resources/sql/autopatches/20151116.owners.edge.sql create mode 100644 src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php diff --git a/resources/sql/autopatches/20151116.owners.edge.sql b/resources/sql/autopatches/20151116.owners.edge.sql new file mode 100644 index 0000000000..6d100eacd1 --- /dev/null +++ b/resources/sql/autopatches/20151116.owners.edge.sql @@ -0,0 +1,16 @@ +CREATE TABLE {$NAMESPACE}_owners.edge ( + src VARBINARY(64) NOT NULL, + type INT UNSIGNED NOT NULL, + dst VARBINARY(64) NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + seq INT UNSIGNED NOT NULL, + dataID INT UNSIGNED, + PRIMARY KEY (src, type, dst), + KEY `src` (src, type, dateCreated, seq), + UNIQUE KEY `key_dst` (dst, type, src) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; + +CREATE TABLE {$NAMESPACE}_owners.edgedata ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT} +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 83b604c6f6..9e1c1a8742 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2563,6 +2563,7 @@ phutil_register_library_map(array( 'PhabricatorOwnersPackageTransactionQuery' => 'applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php', 'PhabricatorOwnersPath' => 'applications/owners/storage/PhabricatorOwnersPath.php', 'PhabricatorOwnersPathsController' => 'applications/owners/controller/PhabricatorOwnersPathsController.php', + 'PhabricatorOwnersSchemaSpec' => 'applications/owners/storage/PhabricatorOwnersSchemaSpec.php', 'PhabricatorOwnersSearchField' => 'applications/owners/searchfield/PhabricatorOwnersSearchField.php', 'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php', 'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php', @@ -6718,6 +6719,7 @@ phutil_register_library_map(array( 'PhabricatorOwnersPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO', 'PhabricatorOwnersPathsController' => 'PhabricatorOwnersController', + 'PhabricatorOwnersSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorOwnersSearchField' => 'PhabricatorSearchTokenizerField', 'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPHID' => 'Phobject', diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 2a8e907145..fabd2511ba 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -1298,15 +1298,6 @@ final class DifferentialTransactionEditor return true; } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) {} - - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - } - protected function expandCustomRemarkupBlockTransactions( PhabricatorLiskDAO $object, array $xactions, diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index 8feeeb1408..cb989f77cb 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -205,18 +205,6 @@ final class PhabricatorOwnersPackageTransactionEditor return $errors; } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: - return array($xaction->getNewValue()); - } - - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - } - protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { diff --git a/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php b/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php index edb4f5db8f..93ef9d419d 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php @@ -208,4 +208,16 @@ final class PhabricatorOwnersPackageTransaction return parent::renderChangeDetails($viewer); } + public function getRemarkupBlocks() { + $blocks = parent::getRemarkupBlocks(); + + switch ($this->getTransactionType()) { + case self::TYPE_DESCRIPTION: + $blocks[] = $this->getNewValue(); + break; + } + + return $blocks; + } + } diff --git a/src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php b/src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php new file mode 100644 index 0000000000..2aeca83c0a --- /dev/null +++ b/src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php @@ -0,0 +1,10 @@ +buildEdgeSchemata(new PhabricatorOwnersPackage()); + } + +} From 06de605992881269693cb74cf7c0777ecc9d6f64 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 16 Nov 2015 09:53:08 -0800 Subject: [PATCH 44/49] Extract PHIDs from transactions later, fixing Paste extraction/attachment Summary: Fixes T9787. Currently, file PHID extraction logic happens very early, before we normalize/merge/etc the transactions. In D14390, I changed how the CONTENT transaction works: before, callers would pass in a file PHID. Afterward, they just pass in the content. Passing in the content is generaly easier and feels more correct, but inadvertenly broke PHID extraction because converting the content into a file PHID now happened after we extracted the PHID. So we'd extract the entire text of the paste as a "file PHID", which wouldn't work. Instead, extract file PHIDs later. This impacts a couple of other applications (Conpherence, Pholio) which receive an object or have an unusual file-oriented transaction. Test Plan: - Made a new paste, verified the raw file attached to it properly. - Made and updated a mock, verified all the files attached properly. - Updated a Conpherence room image, verified the files attached properly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9787 Differential Revision: https://secure.phabricator.com/D14494 --- .../conpherence/editor/ConpherenceEditor.php | 3 +-- .../pholio/editor/PholioMockEditor.php | 26 ++++++++++++++----- ...habricatorApplicationTransactionEditor.php | 3 +-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index 299b799d3e..7d3aa02d2f 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -623,9 +623,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PICTURE: - return array($xaction->getNewValue()->getPHID()); case ConpherenceTransaction::TYPE_PICTURE_CROP: - return array($xaction->getNewValue()); + return array($xaction->getNewValue()); } return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index 14d3a0af16..31d1602202 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -120,19 +120,31 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { + $images = $this->getNewImages(); + $images = mpull($images, null, 'getPHID'); + switch ($xaction->getTransactionType()) { case PholioTransaction::TYPE_IMAGE_FILE: - $new = $xaction->getNewValue(); - $phids = array(); - foreach ($new as $key => $images) { - $phids[] = mpull($images, 'getFilePHID'); + $file_phids = array(); + foreach ($xaction->getNewValue() as $image_phid) { + $image = idx($images, $image_phid); + if (!$image) { + continue; + } + $file_phids[] = $image->getFilePHID(); } - return array_mergev($phids); + return $file_phids; case PholioTransaction::TYPE_IMAGE_REPLACE: - return array($xaction->getNewValue()->getFilePHID()); + $image_phid = $xaction->getNewValue(); + $image = idx($images, $image_phid); + + if ($image) { + return array($image->getFilePHID()); + } + break; } - return array(); + return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); } diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 1f390ed468..d61b498edd 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -775,8 +775,6 @@ abstract class PhabricatorApplicationTransactionEditor throw new PhabricatorApplicationTransactionValidationException($errors); } - $file_phids = $this->extractFilePHIDs($object, $xactions); - if ($object->getID()) { foreach ($xactions as $xaction) { @@ -822,6 +820,7 @@ abstract class PhabricatorApplicationTransactionEditor } $xactions = $this->sortTransactions($xactions); + $file_phids = $this->extractFilePHIDs($object, $xactions); if ($is_preview) { $this->loadHandles($xactions); From 59c5cd95e7c9f9a7011f4ddd2b9efa37bbc5958a Mon Sep 17 00:00:00 2001 From: lkassianik Date: Fri, 13 Nov 2015 14:36:58 -0800 Subject: [PATCH 45/49] Remarkup links to link to short url instead of long and fix commenting on Phurl's Summary: Ref T6049, remarkup links to use short URLs and make commenting on Phurl's actually work Test Plan: - Create Phurl `U123` - Comment on that Phurl `((123))` Comment should link to `/u/123` Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T6049 Differential Revision: https://secure.phabricator.com/D14477 --- src/__phutil_library_map__.php | 2 + .../option/PhabricatorPhurlConfigOptions.php | 2 +- .../PhabricatorPhurlApplication.php | 2 + .../PhabricatorPhurlURLCommentController.php | 63 +++++++++++++++++++ .../PhabricatorPhurlURLEditController.php | 6 +- .../PhabricatorPhurlURLViewController.php | 2 +- .../editor/PhabricatorPhurlURLEditor.php | 16 ----- .../PhabricatorPhurlLinkRemarkupRule.php | 20 ++++-- .../phurl/storage/PhabricatorPhurlURL.php | 16 +++++ 9 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9e1c1a8742..e28eec6f6a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2669,6 +2669,7 @@ phutil_register_library_map(array( 'PhabricatorPhurlShortURLDefaultController' => 'applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php', 'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php', 'PhabricatorPhurlURLAccessController' => 'applications/phurl/controller/PhabricatorPhurlURLAccessController.php', + 'PhabricatorPhurlURLCommentController' => 'applications/phurl/controller/PhabricatorPhurlURLCommentController.php', 'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php', 'PhabricatorPhurlURLEditor' => 'applications/phurl/editor/PhabricatorPhurlURLEditor.php', 'PhabricatorPhurlURLListController' => 'applications/phurl/controller/PhabricatorPhurlURLListController.php', @@ -6846,6 +6847,7 @@ phutil_register_library_map(array( 'PhabricatorSpacesInterface', ), 'PhabricatorPhurlURLAccessController' => 'PhabricatorPhurlController', + 'PhabricatorPhurlURLCommentController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURLEditController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURLEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPhurlURLListController' => 'PhabricatorPhurlController', diff --git a/src/applications/config/option/PhabricatorPhurlConfigOptions.php b/src/applications/config/option/PhabricatorPhurlConfigOptions.php index 378724a4b5..726540d3dd 100644 --- a/src/applications/config/option/PhabricatorPhurlConfigOptions.php +++ b/src/applications/config/option/PhabricatorPhurlConfigOptions.php @@ -16,7 +16,7 @@ final class PhabricatorPhurlConfigOptions } public function getGroup() { - return 'phurl'; + return 'apps'; } public function getOptions() { diff --git a/src/applications/phurl/application/PhabricatorPhurlApplication.php b/src/applications/phurl/application/PhabricatorPhurlApplication.php index 015ed75fe7..75ac91c8c2 100644 --- a/src/applications/phurl/application/PhabricatorPhurlApplication.php +++ b/src/applications/phurl/application/PhabricatorPhurlApplication.php @@ -46,6 +46,8 @@ final class PhabricatorPhurlApplication extends PhabricatorApplication { => 'PhabricatorPhurlURLEditController', 'edit/(?P[1-9]\d*)/' => 'PhabricatorPhurlURLEditController', + 'comment/(?P[1-9]\d*)/' + => 'PhabricatorPhurlURLCommentController', ), ), ); diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php b/src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php new file mode 100644 index 0000000000..8726a38706 --- /dev/null +++ b/src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php @@ -0,0 +1,63 @@ +isFormPost()) { + return new Aphront400Response(); + } + + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + + $is_preview = $request->isPreviewRequest(); + $draft = PhabricatorDraft::buildFromRequest($request); + + $phurl = id(new PhabricatorPhurlURLQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$phurl) { + return new Aphront404Response(); + } + + $view_uri = '/'.$phurl->getMonogram(); + + $xactions = array(); + $xactions[] = id(new PhabricatorPhurlURLTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->attachComment( + id(new PhabricatorPhurlURLTransactionComment()) + ->setContent($request->getStr('comment'))); + + $editor = id(new PhabricatorPhurlURLEditor()) + ->setActor($viewer) + ->setContinueOnNoEffect($request->isContinueRequest()) + ->setContentSourceFromRequest($request) + ->setIsPreview($is_preview); + + try { + $xactions = $editor->applyTransactions($phurl, $xactions); + } catch (PhabricatorApplicationTransactionNoEffectException $ex) { + return id(new PhabricatorApplicationTransactionNoEffectResponse()) + ->setCancelURI($view_uri) + ->setException($ex); + } + + if ($draft) { + $draft->replaceOrDelete(); + } + + if ($request->isAjax() && $is_preview) { + return id(new PhabricatorApplicationTransactionResponse()) + ->setViewer($viewer) + ->setTransactions($xactions) + ->setIsPreview($is_preview); + } else { + return id(new AphrontRedirectResponse()) + ->setURI($view_uri); + } + } + +} diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php index 5a7cd9686d..a4960d0c03 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php @@ -9,7 +9,6 @@ final class PhabricatorPhurlURLEditController $viewer = $this->getViewer(); $user_phid = $viewer->getPHID(); - $error_name = true; $error_long_url = true; $error_alias = null; $validation_exception = null; @@ -131,8 +130,6 @@ final class PhabricatorPhurlURLEditController ->setURI($url->getURI()); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; - $error_name = $ex->getShortMessage( - PhabricatorPhurlURLTransaction::TYPE_NAME); $error_long_url = $ex->getShortMessage( PhabricatorPhurlURLTransaction::TYPE_URL); $error_alias = $ex->getShortMessage( @@ -148,8 +145,7 @@ final class PhabricatorPhurlURLEditController $name = id(new AphrontFormTextControl()) ->setLabel(pht('Name')) ->setName('name') - ->setValue($name) - ->setError($error_name); + ->setValue($name); $long_url = id(new AphrontFormTextControl()) ->setLabel(pht('URL')) diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php index 7702d67b7c..b0649cc62a 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php @@ -49,7 +49,7 @@ final class PhabricatorPhurlURLViewController : pht('More Cowbell'); $draft = PhabricatorDraft::newFromUserAndKey($viewer, $url->getPHID()); $comment_uri = $this->getApplicationURI( - '/phurl/comment/'.$url->getID().'/'); + '/url/comment/'.$url->getID().'/'); $add_comment_form = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) ->setObjectPHID($url->getPHID()) diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php index e7a3c7394b..ef7f547621 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php @@ -106,22 +106,6 @@ final class PhabricatorPhurlURLEditor $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { - case PhabricatorPhurlURLTransaction::TYPE_NAME: - $missing = $this->validateIsEmptyTextField( - $object->getName(), - $xactions); - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('URL name is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } - break; case PhabricatorPhurlURLTransaction::TYPE_ALIAS: $overdrawn = $this->validateIsTextFieldTooLong( $object->getName(), diff --git a/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php b/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php index bdb6c1959f..aca7c14dc7 100644 --- a/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php +++ b/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php @@ -7,7 +7,7 @@ final class PhabricatorPhurlLinkRemarkupRule extends PhutilRemarkupRule { } public function apply($text) { - // `((U123))` remarkup link to `/u/123` + // `((123))` remarkup link to `/u/123` // `((alias))` remarkup link to `/u/alias` return preg_replace_callback( '/\(\(([^ )]+)\)\)/', @@ -25,8 +25,15 @@ final class PhabricatorPhurlLinkRemarkupRule extends PhutilRemarkupRule { } $ref = $matches[1]; + $monogram = null; + $is_monogram = '/^U(?P[1-9]\d*)/'; - if (ctype_digit($ref)) { + if (preg_match($is_monogram, $ref, $monogram)) { + $phurls = id(new PhabricatorPhurlURLQuery()) + ->setViewer($viewer) + ->withIDs(array($monogram[1])) + ->execute(); + } else if (ctype_digit($ref)) { $phurls = id(new PhabricatorPhurlURLQuery()) ->setViewer($viewer) ->withIDs(array($ref)) @@ -42,16 +49,19 @@ final class PhabricatorPhurlLinkRemarkupRule extends PhutilRemarkupRule { if ($phurl) { if ($text_mode) { - return $phurl->getName().' <'.$phurl->getLongURL().'>'; + return $phurl->getDisplayName(). + ' <'. + $phurl->getRedirectURI(). + '>'; } $link = phutil_tag( 'a', array( - 'href' => $phurl->getLongURL(), + 'href' => $phurl->getRedirectURI(), 'target' => '_blank', ), - $phurl->getName()); + $phurl->getDisplayName()); return $this->getEngine()->storeText($link); } else { diff --git a/src/applications/phurl/storage/PhabricatorPhurlURL.php b/src/applications/phurl/storage/PhabricatorPhurlURL.php index 35c5382d52..cc90b6ce6a 100644 --- a/src/applications/phurl/storage/PhabricatorPhurlURL.php +++ b/src/applications/phurl/storage/PhabricatorPhurlURL.php @@ -79,6 +79,22 @@ final class PhabricatorPhurlURL extends PhabricatorPhurlDAO return isset($allowed_protocols[$uri->getProtocol()]); } + public function getDisplayName() { + if ($this->getName()) { + return $this->getName(); + } else { + return $this->getMonogram(); + } + } + + public function getRedirectURI() { + if (strlen($this->getAlias())) { + return '/u/'.$this->getAlias(); + } else { + return '/u/'.$this->getID(); + } + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ From 2e09a93dc12353647dace14a502bd8b2f828f20e Mon Sep 17 00:00:00 2001 From: epriestley Date: Tue, 17 Nov 2015 16:47:58 -0800 Subject: [PATCH 46/49] Improve efficiency of worker task GC for huge loads Summary: Fixes T9808. An instance imported a very large repository, generating approximately 4 million tasks over the course of a few days. A week later, these tasks started expiring and became candidates for garbage collection. The GC works by deleting 100 rows at at time over and over again. It finds the rows it's going to delete by querying for old rows. Currently, this query generates a `WHERE dateCreated < X ORDER BY id DESC` query. This query can not efficiently execute using a single key, because it relies on `dateCreated` order to find the rows, then on `id` order to sort them. With a table with 4M rows, this is slow. This would still be OK, except that the query has to execute a lot of times since it only deletes 100 rows each time. Particularly, it needs to execute a total of ~40K times. Instead, generate `WHERE dateCreated < X ORDER BY dateCreated DESC, id DESC`. This should have the same effect in general and the GC definitely doesn't care about the difference, but it should be more efficient at large scales. Test Plan: I had to `TRUNCATE` the problem table so I don't have a perfect repro to completely convincingly test this anymore. Both queries behave fine at small scales, which is why we haven't seen this before. I was able to run the newer query in production before I nuked the table and have it complete in a reasonable amount of time, while the old query hung longer than I wanted to wait (several minutes?). The query plan for the new query was also a good one, while the query plan for the old query was terrible. I loaded the daemon console and ran `bin/garbage collect --collector worker.tasks --trace`. I verified the queries looked reasonable and produced reasonable results in production. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9808 Differential Revision: https://secure.phabricator.com/D14505 --- .../query/PhabricatorWorkerArchiveTaskQuery.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php index 7dfafa16cc..b0aa6addd3 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php @@ -41,9 +41,10 @@ final class PhabricatorWorkerArchiveTaskQuery $rows = queryfx_all( $conn_r, - 'SELECT * FROM %T %Q ORDER BY id DESC %Q', + 'SELECT * FROM %T %Q %Q %Q', $task_table->getTableName(), $this->buildWhereClause($conn_r), + $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $task_table->loadAllFromArray($rows); @@ -83,6 +84,18 @@ final class PhabricatorWorkerArchiveTaskQuery return $this->formatWhereClause($where); } + private function buildOrderClause(AphrontDatabaseConnection $conn_r) { + // NOTE: The garbage collector executes this query with a date constraint, + // and the query is inefficient if we don't use the same key for ordering. + // See T9808 for discussion. + + if ($this->dateCreatedBefore) { + return qsprintf($conn_r, 'ORDER BY dateCreated DESC, id DESC'); + } else { + return qsprintf($conn_r, 'ORDER BY id DESC'); + } + } + private function buildLimitClause(AphrontDatabaseConnection $conn_r) { $clause = ''; if ($this->limit) { From a41b85788263e62d361a7e91c19767e807d4a8cd Mon Sep 17 00:00:00 2001 From: lkassianik Date: Tue, 17 Nov 2015 12:01:12 -0800 Subject: [PATCH 47/49] Ref T6049, Phurl object view should always display some sort of header. Summary: Ref T6049, Phurl object view should display Phurl name or Phurl long url as header. Test Plan: - Create Phurl with no name. Header should show long url as header. - Add name to Phurl. Header should be new Phurl name. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Maniphest Tasks: T6049 Differential Revision: https://secure.phabricator.com/D14502 --- .../phurl/controller/PhabricatorPhurlURLViewController.php | 2 +- src/applications/phurl/storage/PhabricatorPhurlURL.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php index b0649cc62a..014d0eefef 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php @@ -79,7 +79,7 @@ final class PhabricatorPhurlURLViewController $header = id(new PHUIHeaderView()) ->setUser($viewer) - ->setHeader($url->getName()) + ->setHeader($url->getDisplayName()) ->setStatus($icon, $color, $status) ->setPolicyObject($url); diff --git a/src/applications/phurl/storage/PhabricatorPhurlURL.php b/src/applications/phurl/storage/PhabricatorPhurlURL.php index cc90b6ce6a..34a143bc90 100644 --- a/src/applications/phurl/storage/PhabricatorPhurlURL.php +++ b/src/applications/phurl/storage/PhabricatorPhurlURL.php @@ -83,7 +83,7 @@ final class PhabricatorPhurlURL extends PhabricatorPhurlDAO if ($this->getName()) { return $this->getName(); } else { - return $this->getMonogram(); + return $this->getLongURL(); } } From d5cb3cd27729857d3d37d91b52fac546803060ff Mon Sep 17 00:00:00 2001 From: cburroughs Date: Thu, 19 Nov 2015 10:20:25 -0500 Subject: [PATCH 48/49] typo in storage message Test Plan: I didn't put any skill points in spelling since I need combat skills to survive in a nuclear wasteland, but spell check says this is better. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D14522 --- .../workflow/PhabricatorStorageManagementWorkflow.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php index 62b2651f70..3203998a40 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php @@ -633,7 +633,7 @@ abstract class PhabricatorStorageManagementWorkflow if ($any_surplus) { $message[] = pht( 'Some of these errors are caused by surplus schemata (extra '. - 'tables or columsn which Phabricator does not expect). These are '. + 'tables or columns which Phabricator does not expect). These are '. 'not serious. For information on resolving these issues, see the '. '"Surplus Schemata" section in the "Managing Storage Adjustments" '. 'article in the documentation.'); From 4d362ddcee4a76beb27c87ff2260e473cbc5182a Mon Sep 17 00:00:00 2001 From: lkassianik Date: Wed, 18 Nov 2015 12:24:09 -0800 Subject: [PATCH 49/49] Ref T6049, Add Phurl URL create capability Summary: Ref T6049, Add Phurl URL create capability Test Plan: - Change {nav Home > Applications > Phurl > Configure} to allow no one to create Phurl URLs - Attempt {nav Phurl > Shorten URL}. Should not be able to create a Phurl. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T6049 Differential Revision: https://secure.phabricator.com/D14510 --- src/__phutil_library_map__.php | 2 ++ .../application/PhabricatorPhurlApplication.php | 8 ++++++++ .../PhabricatorPhurlURLCreateCapability.php | 16 ++++++++++++++++ .../controller/PhabricatorPhurlController.php | 7 ++++++- .../PhabricatorPhurlURLEditController.php | 3 +++ 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e28eec6f6a..73a0fd7393 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2670,6 +2670,7 @@ phutil_register_library_map(array( 'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php', 'PhabricatorPhurlURLAccessController' => 'applications/phurl/controller/PhabricatorPhurlURLAccessController.php', 'PhabricatorPhurlURLCommentController' => 'applications/phurl/controller/PhabricatorPhurlURLCommentController.php', + 'PhabricatorPhurlURLCreateCapability' => 'applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php', 'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php', 'PhabricatorPhurlURLEditor' => 'applications/phurl/editor/PhabricatorPhurlURLEditor.php', 'PhabricatorPhurlURLListController' => 'applications/phurl/controller/PhabricatorPhurlURLListController.php', @@ -6848,6 +6849,7 @@ phutil_register_library_map(array( ), 'PhabricatorPhurlURLAccessController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURLCommentController' => 'PhabricatorPhurlController', + 'PhabricatorPhurlURLCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorPhurlURLEditController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURLEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPhurlURLListController' => 'PhabricatorPhurlController', diff --git a/src/applications/phurl/application/PhabricatorPhurlApplication.php b/src/applications/phurl/application/PhabricatorPhurlApplication.php index 75ac91c8c2..11b12067ea 100644 --- a/src/applications/phurl/application/PhabricatorPhurlApplication.php +++ b/src/applications/phurl/application/PhabricatorPhurlApplication.php @@ -60,4 +60,12 @@ final class PhabricatorPhurlApplication extends PhabricatorApplication { ); } + protected function getCustomCapabilities() { + return array( + PhabricatorPhurlURLCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + ), + ); + } + } diff --git a/src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php b/src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php new file mode 100644 index 0000000000..59590e6c6a --- /dev/null +++ b/src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php @@ -0,0 +1,16 @@ +hasApplicationCapability( + PhabricatorPhurlURLCreateCapability::CAPABILITY); + $crumbs = parent::buildApplicationCrumbs(); $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('Shorten URL')) ->setHref($this->getApplicationURI().'url/create/') - ->setIcon('fa-plus-square')); + ->setIcon('fa-plus-square') + ->setDisabled(!$can_create) + ->setWorkflow(!$can_create)); return $crumbs; } diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php index a4960d0c03..4136c855d8 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php @@ -17,6 +17,9 @@ final class PhabricatorPhurlURLEditController $uri_query = $request->getStr('query'); if ($is_create) { + $this->requireApplicationCapability( + PhabricatorPhurlURLCreateCapability::CAPABILITY); + $url = PhabricatorPhurlURL::initializeNewPhurlURL( $viewer); $submit_label = pht('Create');