diff --git a/src/applications/macro/controller/PhabricatorMacroAudioController.php b/src/applications/macro/controller/PhabricatorMacroAudioController.php
index e46b7c78e8..92b98e5564 100644
--- a/src/applications/macro/controller/PhabricatorMacroAudioController.php
+++ b/src/applications/macro/controller/PhabricatorMacroAudioController.php
@@ -1,159 +1,153 @@
 <?php
 
 final class PhabricatorMacroAudioController extends PhabricatorMacroController {
 
-  private $id;
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('id');
 
-  public function willProcessRequest(array $data) {
-    $this->id = idx($data, 'id');
-  }
-
-  public function processRequest() {
     $this->requireApplicationCapability(
       PhabricatorMacroManageCapability::CAPABILITY);
 
-    $request = $this->getRequest();
-    $viewer = $request->getUser();
-
     $macro = id(new PhabricatorMacroQuery())
       ->setViewer($viewer)
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
         ))
-      ->withIDs(array($this->id))
+      ->withIDs(array($id))
       ->executeOne();
 
     if (!$macro) {
       return new Aphront404Response();
     }
 
     $errors = array();
     $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/');
 
     $e_file = null;
     $file = null;
 
     if ($request->isFormPost()) {
       $xactions = array();
 
       if ($request->getBool('behaviorForm')) {
         $xactions[] = id(new PhabricatorMacroTransaction())
           ->setTransactionType(
             PhabricatorMacroTransaction::TYPE_AUDIO_BEHAVIOR)
           ->setNewValue($request->getStr('audioBehavior'));
       } else {
         $file = null;
         if ($request->getFileExists('file')) {
           $file = PhabricatorFile::newFromPHPUpload(
             $_FILES['file'],
             array(
               'name' => $request->getStr('name'),
               'authorPHID' => $viewer->getPHID(),
               'isExplicitUpload' => true,
             ));
         }
 
         if ($file) {
           if (!$file->isAudio()) {
             $errors[] = pht('You must upload audio.');
             $e_file = pht('Invalid');
           } else {
             $xactions[] = id(new PhabricatorMacroTransaction())
               ->setTransactionType(PhabricatorMacroTransaction::TYPE_AUDIO)
               ->setNewValue($file->getPHID());
           }
         } else {
           $errors[] = pht('You must upload an audio file.');
           $e_file = pht('Required');
         }
       }
 
       if (!$errors) {
         id(new PhabricatorMacroEditor())
           ->setActor($viewer)
           ->setContinueOnNoEffect(true)
           ->setContentSourceFromRequest($request)
           ->applyTransactions($macro, $xactions);
 
         return id(new AphrontRedirectResponse())->setURI($view_uri);
       }
     }
 
     $form = id(new AphrontFormView())
       ->addHiddenInput('behaviorForm', 1)
       ->setUser($viewer);
 
     $options = id(new AphrontFormRadioButtonControl())
       ->setLabel(pht('Audio Behavior'))
       ->setName('audioBehavior')
       ->setValue(
         nonempty(
           $macro->getAudioBehavior(),
           PhabricatorFileImageMacro::AUDIO_BEHAVIOR_NONE));
 
     $options->addButton(
       PhabricatorFileImageMacro::AUDIO_BEHAVIOR_NONE,
       pht('Do Not Play'),
       pht('Do not play audio.'));
 
     $options->addButton(
       PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE,
       pht('Play Once'),
       pht('Play audio once, when the viewer looks at the macro.'));
 
     $options->addButton(
       PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP,
       pht('Play Continuously'),
       pht(
         'Play audio continuously, treating the macro as an audio source. '.
         'Best for ambient sounds.'));
 
     $form->appendChild($options);
 
     $form
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue(pht('Save Audio Behavior'))
           ->addCancelButton($view_uri));
 
     $crumbs = $this->buildApplicationCrumbs();
 
     $title = pht('Edit Audio Behavior');
     $crumb = pht('Edit Audio');
 
     $crumbs->addTextCrumb(pht('Macro "%s"', $macro->getName()), $view_uri);
     $crumbs->addTextCrumb($crumb, $request->getRequestURI());
 
     $upload_form = id(new AphrontFormView())
       ->setEncType('multipart/form-data')
       ->setUser($viewer)
       ->appendChild(
         id(new AphrontFormFileControl())
           ->setLabel(pht('Audio File'))
           ->setName('file'))
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue(pht('Upload File')));
 
     $upload = id(new PHUIObjectBoxView())
       ->setHeaderText(pht('Upload New Audio'))
       ->setForm($upload_form);
 
     $form_box = id(new PHUIObjectBoxView())
       ->setHeaderText($title)
       ->setFormErrors($errors)
       ->setForm($form);
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $form_box,
         $upload,
       ),
       array(
         'title' => $title,
       ));
   }
 
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroCommentController.php b/src/applications/macro/controller/PhabricatorMacroCommentController.php
index f438e9b31a..f038140be1 100644
--- a/src/applications/macro/controller/PhabricatorMacroCommentController.php
+++ b/src/applications/macro/controller/PhabricatorMacroCommentController.php
@@ -1,69 +1,63 @@
 <?php
 
 final class PhabricatorMacroCommentController
   extends PhabricatorMacroController {
 
-  private $id;
-
-  public function willProcessRequest(array $data) {
-    $this->id = idx($data, 'id');
-  }
-
-  public function processRequest() {
-    $request = $this->getRequest();
-    $user = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('id');
 
     if (!$request->isFormPost()) {
       return new Aphront400Response();
     }
 
     $macro = id(new PhabricatorMacroQuery())
-      ->setViewer($user)
-      ->withIDs(array($this->id))
+      ->setViewer($viewer)
+      ->withIDs(array($id))
       ->executeOne();
     if (!$macro) {
       return new Aphront404Response();
     }
 
     $is_preview = $request->isPreviewRequest();
     $draft = PhabricatorDraft::buildFromRequest($request);
 
     $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/');
 
     $xactions = array();
     $xactions[] = id(new PhabricatorMacroTransaction())
       ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
       ->attachComment(
         id(new PhabricatorMacroTransactionComment())
           ->setContent($request->getStr('comment')));
 
     $editor = id(new PhabricatorMacroEditor())
-      ->setActor($user)
+      ->setActor($viewer)
       ->setContinueOnNoEffect($request->isContinueRequest())
       ->setContentSourceFromRequest($request)
       ->setIsPreview($is_preview);
 
     try {
       $xactions = $editor->applyTransactions($macro, $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($user)
+        ->setViewer($viewer)
         ->setTransactions($xactions)
         ->setIsPreview($is_preview);
     } else {
       return id(new AphrontRedirectResponse())
         ->setURI($view_uri);
     }
   }
 
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroDisableController.php b/src/applications/macro/controller/PhabricatorMacroDisableController.php
index 0804e119e5..a9868647f0 100644
--- a/src/applications/macro/controller/PhabricatorMacroDisableController.php
+++ b/src/applications/macro/controller/PhabricatorMacroDisableController.php
@@ -1,62 +1,56 @@
 <?php
 
 final class PhabricatorMacroDisableController
   extends PhabricatorMacroController {
 
-  private $id;
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('id');
 
-  public function willProcessRequest(array $data) {
-    $this->id = $data['id'];
-  }
-
-  public function processRequest() {
     $this->requireApplicationCapability(
       PhabricatorMacroManageCapability::CAPABILITY);
 
-    $request = $this->getRequest();
-    $user = $request->getUser();
-
     $macro = id(new PhabricatorMacroQuery())
-      ->setViewer($user)
-      ->withIDs(array($this->id))
+      ->setViewer($viewer)
+      ->withIDs(array($id))
       ->executeOne();
     if (!$macro) {
       return new Aphront404Response();
     }
 
-    $view_uri = $this->getApplicationURI('/view/'.$this->id.'/');
+    $view_uri = $this->getApplicationURI('/view/'.$id.'/');
 
     if ($request->isDialogFormPost() || $macro->getIsDisabled()) {
       $xaction = id(new PhabricatorMacroTransaction())
         ->setTransactionType(PhabricatorMacroTransaction::TYPE_DISABLED)
         ->setNewValue($macro->getIsDisabled() ? 0 : 1);
 
       $editor = id(new PhabricatorMacroEditor())
-        ->setActor($user)
+        ->setActor($viewer)
         ->setContentSourceFromRequest($request);
 
       $xactions = $editor->applyTransactions($macro, array($xaction));
 
       return id(new AphrontRedirectResponse())->setURI($view_uri);
     }
 
     $dialog = new AphrontDialogView();
     $dialog
       ->setUser($request->getUser())
       ->setTitle(pht('Really disable macro?'))
       ->appendChild(
         phutil_tag(
           'p',
           array(),
           pht(
             'Really disable the much-beloved image macro %s? '.
             'It will be sorely missed.',
           $macro->getName())))
-      ->setSubmitURI($this->getApplicationURI('/disable/'.$this->id.'/'))
+      ->setSubmitURI($this->getApplicationURI('/disable/'.$id.'/'))
       ->addSubmitButton(pht('Disable'))
       ->addCancelButton($view_uri);
 
     return id(new AphrontDialogResponse())->setDialog($dialog);
   }
 
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroEditController.php b/src/applications/macro/controller/PhabricatorMacroEditController.php
index f15e5364c2..9e988ef637 100644
--- a/src/applications/macro/controller/PhabricatorMacroEditController.php
+++ b/src/applications/macro/controller/PhabricatorMacroEditController.php
@@ -1,295 +1,289 @@
 <?php
 
 final class PhabricatorMacroEditController extends PhabricatorMacroController {
 
-  private $id;
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('id');
 
-  public function willProcessRequest(array $data) {
-    $this->id = idx($data, 'id');
-  }
-
-  public function processRequest() {
     $this->requireApplicationCapability(
       PhabricatorMacroManageCapability::CAPABILITY);
 
-    $request = $this->getRequest();
-    $user = $request->getUser();
-
-    if ($this->id) {
+    if ($id) {
       $macro = id(new PhabricatorMacroQuery())
-        ->setViewer($user)
-        ->withIDs(array($this->id))
+        ->setViewer($viewer)
+        ->withIDs(array($id))
         ->needFiles(true)
         ->executeOne();
       if (!$macro) {
         return new Aphront404Response();
       }
     } else {
       $macro = new PhabricatorFileImageMacro();
-      $macro->setAuthorPHID($user->getPHID());
+      $macro->setAuthorPHID($viewer->getPHID());
     }
 
     $errors = array();
     $e_name = true;
     $e_file = null;
     $file = null;
 
     if ($request->isFormPost()) {
       $original = clone $macro;
 
       $new_name = null;
       if ($request->getBool('name_form') || !$macro->getID()) {
         $new_name = $request->getStr('name');
 
         $macro->setName($new_name);
 
         if (!strlen($macro->getName())) {
           $errors[] = pht('Macro name is required.');
           $e_name = pht('Required');
         } else if (!preg_match('/^[a-z0-9:_-]{3,}\z/', $macro->getName())) {
           $errors[] = pht(
             'Macro must be at least three characters long and contain only '.
             'lowercase letters, digits, hyphens, colons and underscores.');
           $e_name = pht('Invalid');
         } else {
           $e_name = null;
         }
       }
 
       $uri = $request->getStr('url');
 
       $engine = new PhabricatorDestructionEngine();
 
       $file = null;
       if ($request->getFileExists('file')) {
         $file = PhabricatorFile::newFromPHPUpload(
           $_FILES['file'],
           array(
             'name' => $request->getStr('name'),
-            'authorPHID' => $user->getPHID(),
+            'authorPHID' => $viewer->getPHID(),
             'isExplicitUpload' => true,
             'canCDN' => true,
           ));
       } else if ($uri) {
         try {
           // Rate limit outbound fetches to make this mechanism less useful for
           // scanning networks and ports.
           PhabricatorSystemActionEngine::willTakeAction(
-            array($user->getPHID()),
+            array($viewer->getPHID()),
             new PhabricatorFilesOutboundRequestAction(),
             1);
 
           $file = PhabricatorFile::newFromFileDownload(
             $uri,
             array(
               'name' => $request->getStr('name'),
               'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
               'isExplicitUpload' => true,
               'canCDN' => true,
             ));
 
           if (!$file->isViewableInBrowser()) {
             $mime_type = $file->getMimeType();
             $engine->destroyObject($file);
             $file = null;
             throw new Exception(
               pht(
                 'The URI "%s" does not correspond to a valid image file, got '.
                 'a file with MIME type "%s". You must specify the URI of a '.
                 'valid image file.',
                 $uri,
                 $mime_type));
           } else {
             $file
-              ->setAuthorPHID($user->getPHID())
+              ->setAuthorPHID($viewer->getPHID())
               ->save();
           }
         } catch (HTTPFutureHTTPResponseStatus $status) {
           $errors[] = pht(
             'The URI "%s" could not be loaded, got %s error.',
             $uri,
             $status->getStatusCode());
         } catch (Exception $ex) {
           $errors[] = $ex->getMessage();
         }
       } else if ($request->getStr('phid')) {
         $file = id(new PhabricatorFileQuery())
-          ->setViewer($user)
+          ->setViewer($viewer)
           ->withPHIDs(array($request->getStr('phid')))
           ->executeOne();
       }
 
       if ($file) {
         if (!$file->isViewableInBrowser()) {
           $errors[] = pht('You must upload an image.');
           $e_file = pht('Invalid');
         } else {
           $macro->setFilePHID($file->getPHID());
           $macro->attachFile($file);
           $e_file = null;
         }
       }
 
       if (!$macro->getID() && !$file) {
         $errors[] = pht('You must upload an image to create a macro.');
         $e_file = pht('Required');
       }
 
       if (!$errors) {
         try {
           $xactions = array();
 
           if ($new_name !== null) {
             $xactions[] = id(new PhabricatorMacroTransaction())
               ->setTransactionType(PhabricatorMacroTransaction::TYPE_NAME)
               ->setNewValue($new_name);
           }
 
           if ($file) {
             $xactions[] = id(new PhabricatorMacroTransaction())
               ->setTransactionType(PhabricatorMacroTransaction::TYPE_FILE)
               ->setNewValue($file->getPHID());
           }
 
           $editor = id(new PhabricatorMacroEditor())
-            ->setActor($user)
+            ->setActor($viewer)
             ->setContinueOnNoEffect(true)
             ->setContentSourceFromRequest($request);
 
           $xactions = $editor->applyTransactions($original, $xactions);
 
           $view_uri = $this->getApplicationURI('/view/'.$original->getID().'/');
           return id(new AphrontRedirectResponse())->setURI($view_uri);
         } catch (AphrontDuplicateKeyQueryException $ex) {
           throw $ex;
           $errors[] = pht('Macro name is not unique!');
           $e_name = pht('Duplicate');
         }
       }
     }
 
     $current_file = null;
     if ($macro->getFilePHID()) {
       $current_file = $macro->getFile();
     }
 
     $form = new AphrontFormView();
     $form->addHiddenInput('name_form', 1);
     $form->setUser($request->getUser());
 
     $form
       ->setEncType('multipart/form-data')
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('Name'))
           ->setName('name')
           ->setValue($macro->getName())
           ->setCaption(
             pht('This word or phrase will be replaced with the image.'))
           ->setError($e_name));
 
     if (!$macro->getID()) {
       if ($current_file) {
         $current_file_view = id(new PhabricatorFileLinkView())
           ->setFilePHID($current_file->getPHID())
           ->setFileName($current_file->getName())
           ->setFileViewable(true)
           ->setFileViewURI($current_file->getBestURI())
           ->render();
         $form->addHiddenInput('phid', $current_file->getPHID());
         $form->appendChild(
           id(new AphrontFormMarkupControl())
             ->setLabel(pht('Selected File'))
             ->setValue($current_file_view));
 
         $other_label = pht('Change File');
       } else {
         $other_label = pht('File');
       }
 
       $form->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('URL'))
           ->setName('url')
           ->setValue($request->getStr('url'))
           ->setError($request->getFileExists('file') ? false : $e_file));
 
       $form->appendChild(
         id(new AphrontFormFileControl())
           ->setLabel($other_label)
           ->setName('file')
           ->setError($request->getStr('url') ? false : $e_file));
     }
 
 
     $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/');
 
     if ($macro->getID()) {
       $cancel_uri = $view_uri;
     } else {
       $cancel_uri = $this->getApplicationURI();
     }
 
     $form
       ->appendChild(
         id(new AphrontFormSubmitControl())
           ->setValue(pht('Save Image Macro'))
           ->addCancelButton($cancel_uri));
 
     $crumbs = $this->buildApplicationCrumbs();
 
     if ($macro->getID()) {
       $title = pht('Edit Image Macro');
       $crumb = pht('Edit Macro');
 
       $crumbs->addTextCrumb(pht('Macro "%s"', $macro->getName()), $view_uri);
     } else {
       $title = pht('Create Image Macro');
       $crumb = pht('Create Macro');
     }
 
     $crumbs->addTextCrumb($crumb, $request->getRequestURI());
 
     $upload = null;
     if ($macro->getID()) {
       $upload_form = id(new AphrontFormView())
         ->setEncType('multipart/form-data')
         ->setUser($request->getUser());
 
       $upload_form->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('URL'))
           ->setName('url')
           ->setValue($request->getStr('url')));
 
       $upload_form
         ->appendChild(
           id(new AphrontFormFileControl())
             ->setLabel(pht('File'))
             ->setName('file'))
         ->appendChild(
           id(new AphrontFormSubmitControl())
             ->setValue(pht('Upload File')));
 
       $upload = id(new PHUIObjectBoxView())
         ->setHeaderText(pht('Upload New File'))
         ->setForm($upload_form);
     }
 
     $form_box = id(new PHUIObjectBoxView())
       ->setHeaderText($title)
       ->setFormErrors($errors)
       ->setForm($form);
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $form_box,
         $upload,
       ),
       array(
         'title' => $title,
       ));
   }
 
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroListController.php b/src/applications/macro/controller/PhabricatorMacroListController.php
index 730f8de3b1..3847117275 100644
--- a/src/applications/macro/controller/PhabricatorMacroListController.php
+++ b/src/applications/macro/controller/PhabricatorMacroListController.php
@@ -1,24 +1,20 @@
 <?php
 
 final class PhabricatorMacroListController extends PhabricatorMacroController {
 
-  private $key;
-
   public function shouldAllowPublic() {
     return true;
   }
 
-  public function willProcessRequest(array $data) {
-    $this->key = idx($data, 'key');
-  }
+  public function handleRequest(AphrontRequest $request) {
+    $key = $request->getURIData('key');
 
-  public function processRequest() {
     $controller = id(new PhabricatorApplicationSearchController())
-      ->setQueryKey($this->key)
+      ->setQueryKey($key)
       ->setSearchEngine(new PhabricatorMacroSearchEngine())
       ->setNavigation($this->buildSideNavView());
 
     return $this->delegateToController($controller);
   }
 
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroMemeController.php b/src/applications/macro/controller/PhabricatorMacroMemeController.php
index ff25a8a8a4..03b13f6205 100644
--- a/src/applications/macro/controller/PhabricatorMacroMemeController.php
+++ b/src/applications/macro/controller/PhabricatorMacroMemeController.php
@@ -1,69 +1,68 @@
 <?php
 
 final class PhabricatorMacroMemeController
   extends PhabricatorMacroController {
 
   public function shouldAllowPublic() {
     return true;
   }
 
-  public function processRequest() {
-    $request = $this->getRequest();
+  public function handleRequest(AphrontRequest $request) {
     $macro_name = $request->getStr('macro');
     $upper_text = $request->getStr('uppertext');
     $lower_text = $request->getStr('lowertext');
-    $user = $request->getUser();
+    $viewer = $request->getViewer();
 
-    $uri = self::generateMacro($user, $macro_name,
+    $uri = self::generateMacro($viewer, $macro_name,
       $upper_text, $lower_text);
     if ($uri === false) {
       return new Aphront404Response();
     }
     return id(new AphrontRedirectResponse())
       ->setIsExternal(true)
       ->setURI($uri);
   }
 
-  public static function generateMacro($user, $macro_name, $upper_text,
+  public static function generateMacro($viewer, $macro_name, $upper_text,
       $lower_text) {
     $macro = id(new PhabricatorMacroQuery())
-      ->setViewer($user)
+      ->setViewer($viewer)
       ->withNames(array($macro_name))
       ->needFiles(true)
       ->executeOne();
     if (!$macro) {
       return false;
     }
     $file = $macro->getFile();
 
     $upper_text = strtoupper($upper_text);
     $lower_text = strtoupper($lower_text);
     $mixed_text = md5($upper_text).':'.md5($lower_text);
     $hash = 'meme'.hash('sha256', $mixed_text);
     $xform = id(new PhabricatorTransformedFile())
       ->loadOneWhere('originalphid=%s and transform=%s',
         $file->getPHID(), $hash);
 
     if ($xform) {
       $memefile = id(new PhabricatorFileQuery())
-        ->setViewer($user)
+        ->setViewer($viewer)
         ->withPHIDs(array($xform->getTransformedPHID()))
         ->executeOne();
       if ($memefile) {
         return $memefile->getBestURI();
       }
     }
 
     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
     $transformers = (new PhabricatorImageTransformer());
     $newfile = $transformers
       ->executeMemeTransform($file, $upper_text, $lower_text);
     $xfile = new PhabricatorTransformedFile();
     $xfile->setOriginalPHID($file->getPHID());
     $xfile->setTransformedPHID($newfile->getPHID());
     $xfile->setTransform($hash);
     $xfile->save();
 
     return $newfile->getBestURI();
   }
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php b/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php
index 25a223f5ac..5851cc5ad8 100644
--- a/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php
+++ b/src/applications/macro/controller/PhabricatorMacroMemeDialogController.php
@@ -1,77 +1,76 @@
 <?php
 
 final class PhabricatorMacroMemeDialogController
   extends PhabricatorMacroController {
 
-  public function processRequest() {
-    $request = $this->getRequest();
-    $user = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
 
     $phid = head($request->getArr('macro'));
     $above = $request->getStr('above');
     $below = $request->getStr('below');
 
     $e_macro = true;
     $errors = array();
     if ($request->isDialogFormPost()) {
       if (!$phid) {
         $e_macro = pht('Required');
         $errors[] = pht('Macro name is required.');
       } else {
         $macro = id(new PhabricatorMacroQuery())
-          ->setViewer($user)
+          ->setViewer($viewer)
           ->withPHIDs(array($phid))
           ->executeOne();
         if (!$macro) {
           $e_macro = pht('Invalid');
           $errors[] = pht('No such macro.');
         }
       }
 
       if (!$errors) {
         $options = new PhutilSimpleOptions();
         $data = array(
           'src' => $macro->getName(),
           'above' => $above,
           'below' => $below,
         );
         $string = $options->unparse($data, $escape = '}');
 
         $result = array(
           'text' => "{meme, {$string}}",
         );
         return id(new AphrontAjaxResponse())->setContent($result);
       }
     }
 
     $view = id(new AphrontFormView())
-      ->setUser($user)
+      ->setUser($viewer)
       ->appendControl(
         id(new AphrontFormTokenizerControl())
           ->setLabel(pht('Macro'))
           ->setName('macro')
           ->setLimit(1)
           ->setDatasource(new PhabricatorMacroDatasource())
           ->setError($e_macro))
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('Above'))
           ->setName('above')
           ->setValue($above))
       ->appendChild(
         id(new AphrontFormTextControl())
           ->setLabel(pht('Below'))
           ->setName('below')
           ->setValue($below));
 
     $dialog = id(new AphrontDialogView())
-      ->setUser($user)
+      ->setUser($viewer)
       ->setTitle(pht('Create Meme'))
       ->appendForm($view)
       ->addCancelButton('/')
       ->addSubmitButton(pht('Llama Diorama'));
 
     return id(new AphrontDialogResponse())->setDialog($dialog);
   }
 
 }
diff --git a/src/applications/macro/controller/PhabricatorMacroViewController.php b/src/applications/macro/controller/PhabricatorMacroViewController.php
index a031c7f92b..8915954fc4 100644
--- a/src/applications/macro/controller/PhabricatorMacroViewController.php
+++ b/src/applications/macro/controller/PhabricatorMacroViewController.php
@@ -1,181 +1,175 @@
 <?php
 
 final class PhabricatorMacroViewController
   extends PhabricatorMacroController {
 
-  private $id;
-
-  public function willProcessRequest(array $data) {
-    $this->id = $data['id'];
-  }
-
   public function shouldAllowPublic() {
     return true;
   }
 
-  public function processRequest() {
-    $request = $this->getRequest();
-    $user = $request->getUser();
+  public function handleRequest(AphrontRequest $request) {
+    $viewer = $request->getViewer();
+    $id = $request->getURIData('id');
 
     $macro = id(new PhabricatorMacroQuery())
-      ->setViewer($user)
-      ->withIDs(array($this->id))
+      ->setViewer($viewer)
+      ->withIDs(array($id))
       ->needFiles(true)
       ->executeOne();
     if (!$macro) {
       return new Aphront404Response();
     }
 
     $file = $macro->getFile();
 
     $title_short = pht('Macro "%s"', $macro->getName());
     $title_long  = pht('Image Macro "%s"', $macro->getName());
 
     $actions = $this->buildActionView($macro);
 
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(
       $title_short,
       $this->getApplicationURI('/view/'.$macro->getID().'/'));
 
     $properties = $this->buildPropertyView($macro, $actions);
     if ($file) {
       $file_view = new PHUIPropertyListView();
       $file_view->addImageContent(
         phutil_tag(
           'img',
           array(
             'src'     => $file->getViewURI(),
             'class'   => 'phabricator-image-macro-hero',
           )));
     }
 
     $timeline = $this->buildTransactionTimeline(
       $macro,
       new PhabricatorMacroTransactionQuery());
 
     $header = id(new PHUIHeaderView())
-      ->setUser($user)
+      ->setUser($viewer)
       ->setPolicyObject($macro)
       ->setHeader($title_long);
 
     if (!$macro->getIsDisabled()) {
       $header->setStatus('fa-check', 'bluegrey', pht('Active'));
     } else {
       $header->setStatus('fa-ban', 'red', pht('Archived'));
     }
 
     $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
 
     $comment_header = $is_serious
       ? pht('Add Comment')
       : pht('Grovel in Awe');
 
-    $draft = PhabricatorDraft::newFromUserAndKey($user, $macro->getPHID());
+    $draft = PhabricatorDraft::newFromUserAndKey($viewer, $macro->getPHID());
 
     $add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
-      ->setUser($user)
+      ->setUser($viewer)
       ->setObjectPHID($macro->getPHID())
       ->setDraft($draft)
       ->setHeaderText($comment_header)
       ->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/'))
       ->setSubmitButtonName(pht('Add Comment'));
 
     $object_box = id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->addPropertyList($properties);
 
     if ($file_view) {
       $object_box->addPropertyList($file_view);
     }
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $object_box,
         $timeline,
         $add_comment_form,
       ),
       array(
         'title' => $title_short,
       ));
   }
 
   private function buildActionView(PhabricatorFileImageMacro $macro) {
     $can_manage = $this->hasApplicationCapability(
       PhabricatorMacroManageCapability::CAPABILITY);
 
     $request = $this->getRequest();
     $view = id(new PhabricatorActionListView())
       ->setUser($request->getUser())
       ->setObject($macro)
       ->setObjectURI($request->getRequestURI())
       ->addAction(
         id(new PhabricatorActionView())
         ->setName(pht('Edit Macro'))
         ->setHref($this->getApplicationURI('/edit/'.$macro->getID().'/'))
         ->setDisabled(!$can_manage)
         ->setWorkflow(!$can_manage)
         ->setIcon('fa-pencil'));
 
     $view->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('Edit Audio'))
         ->setHref($this->getApplicationURI('/audio/'.$macro->getID().'/'))
         ->setDisabled(!$can_manage)
         ->setWorkflow(!$can_manage)
         ->setIcon('fa-music'));
 
     if ($macro->getIsDisabled()) {
       $view->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('Activate Macro'))
           ->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/'))
           ->setWorkflow(true)
           ->setDisabled(!$can_manage)
           ->setIcon('fa-check'));
     } else {
       $view->addAction(
         id(new PhabricatorActionView())
           ->setName(pht('Archive Macro'))
           ->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/'))
           ->setWorkflow(true)
           ->setDisabled(!$can_manage)
           ->setIcon('fa-ban'));
     }
 
     return $view;
   }
 
   private function buildPropertyView(
     PhabricatorFileImageMacro $macro,
     PhabricatorActionListView $actions) {
     $viewer = $this->getViewer();
 
     $view = id(new PHUIPropertyListView())
       ->setUser($this->getRequest()->getUser())
       ->setObject($macro)
       ->setActionList($actions);
 
     switch ($macro->getAudioBehavior()) {
       case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE:
         $view->addProperty(pht('Audio Behavior'), pht('Play Once'));
         break;
       case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP:
         $view->addProperty(pht('Audio Behavior'), pht('Loop'));
         break;
     }
 
     $audio_phid = $macro->getAudioPHID();
     if ($audio_phid) {
       $view->addProperty(
         pht('Audio'),
         $viewer->renderHandle($audio_phid));
     }
 
     $view->invokeWillRenderEvent();
 
     return $view;
   }
 
 }