@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Allow macros to have associated audio and audio behaviors

Summary: Ref T3887. Implements storage and editors, but not the actual audio part.

Test Plan: Edited audio, audio behaviors of macros. Transactions and email looked good. Hit error cases.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T3887

Differential Revision: https://secure.phabricator.com/D7159

+325
+8
resources/sql/patches/20130927.audiomacro.sql
··· 1 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 2 + ADD audioPHID VARCHAR(64) COLLATE utf8_bin; 3 + 4 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 5 + ADD audioBehavior VARCHAR(64) NOT NULL COLLATE utf8_bin; 6 + 7 + UPDATE {$NAMESPACE}_file.file_imagemacro 8 + SET audioBehavior = 'audio:none' WHERE audioBehavior = '';
+2
src/__phutil_library_map__.php
··· 1270 1270 'PhabricatorLocalDiskFileStorageEngine' => 'applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php', 1271 1271 'PhabricatorLocalTimeTestCase' => 'view/__tests__/PhabricatorLocalTimeTestCase.php', 1272 1272 'PhabricatorLogoutController' => 'applications/auth/controller/PhabricatorLogoutController.php', 1273 + 'PhabricatorMacroAudioController' => 'applications/macro/controller/PhabricatorMacroAudioController.php', 1273 1274 'PhabricatorMacroCommentController' => 'applications/macro/controller/PhabricatorMacroCommentController.php', 1274 1275 'PhabricatorMacroConfigOptions' => 'applications/macro/config/PhabricatorMacroConfigOptions.php', 1275 1276 'PhabricatorMacroController' => 'applications/macro/controller/PhabricatorMacroController.php', ··· 3416 3417 'PhabricatorLocalDiskFileStorageEngine' => 'PhabricatorFileStorageEngine', 3417 3418 'PhabricatorLocalTimeTestCase' => 'PhabricatorTestCase', 3418 3419 'PhabricatorLogoutController' => 'PhabricatorAuthController', 3420 + 'PhabricatorMacroAudioController' => 'PhabricatorMacroController', 3419 3421 'PhabricatorMacroCommentController' => 'PhabricatorMacroController', 3420 3422 'PhabricatorMacroConfigOptions' => 'PhabricatorApplicationConfigOptions', 3421 3423 'PhabricatorMacroController' => 'PhabricatorController',
+1
src/applications/macro/application/PhabricatorApplicationMacro.php
··· 34 34 'view/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroViewController', 35 35 'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroCommentController', 36 36 'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroEditController', 37 + 'audio/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroAudioController', 37 38 'disable/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroDisableController', 38 39 'meme/' => 'PhabricatorMacroMemeController', 39 40 'meme/create/' => 'PhabricatorMacroMemeDialogController',
+3
src/applications/macro/constants/PhabricatorMacroTransactionType.php
··· 6 6 const TYPE_DISABLED = 'macro:disabled'; 7 7 const TYPE_FILE = 'macro:file'; 8 8 9 + const TYPE_AUDIO = 'macro:audio'; 10 + const TYPE_AUDIO_BEHAVIOR = 'macro:audiobehavior'; 11 + 9 12 }
+173
src/applications/macro/controller/PhabricatorMacroAudioController.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroAudioController 4 + extends PhabricatorMacroController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = idx($data, 'id'); 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $viewer = $request->getUser(); 15 + 16 + $macro = id(new PhabricatorMacroQuery()) 17 + ->setViewer($viewer) 18 + ->requireCapabilities( 19 + array( 20 + PhabricatorPolicyCapability::CAN_VIEW, 21 + PhabricatorPolicyCapability::CAN_EDIT, 22 + )) 23 + ->withIDs(array($this->id)) 24 + ->executeOne(); 25 + 26 + if (!$macro) { 27 + return new Aphront404Response(); 28 + } 29 + 30 + $errors = array(); 31 + $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); 32 + 33 + $e_file = null; 34 + $file = null; 35 + 36 + if ($request->isFormPost()) { 37 + $xactions = array(); 38 + 39 + if ($request->getBool('behaviorForm')) { 40 + $xactions[] = id(new PhabricatorMacroTransaction()) 41 + ->setTransactionType( 42 + PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR) 43 + ->setNewValue($request->getStr('audioBehavior')); 44 + } else { 45 + $file = null; 46 + if ($request->getFileExists('file')) { 47 + $file = PhabricatorFile::newFromPHPUpload( 48 + $_FILES['file'], 49 + array( 50 + 'name' => $request->getStr('name'), 51 + 'authorPHID' => $viewer->getPHID(), 52 + 'isExplicitUpload' => true, 53 + )); 54 + } 55 + 56 + if ($file) { 57 + if (!$file->isAudio()) { 58 + $errors[] = pht('You must upload audio.'); 59 + $e_file = pht('Invalid'); 60 + } else { 61 + $xactions[] = id(new PhabricatorMacroTransaction()) 62 + ->setTransactionType(PhabricatorMacroTransactionType::TYPE_AUDIO) 63 + ->setNewValue($file->getPHID()); 64 + } 65 + } else { 66 + $errors[] = pht('You must upload an audio file.'); 67 + $e_file = pht('Required'); 68 + } 69 + } 70 + 71 + if (!$errors) { 72 + id(new PhabricatorMacroEditor()) 73 + ->setActor($viewer) 74 + ->setContinueOnNoEffect(true) 75 + ->setContentSourceFromRequest($request) 76 + ->applyTransactions($macro, $xactions); 77 + 78 + return id(new AphrontRedirectResponse())->setURI($view_uri); 79 + } 80 + } 81 + 82 + if ($errors) { 83 + $error_view = new AphrontErrorView(); 84 + $error_view->setTitle(pht('Form Errors')); 85 + $error_view->setErrors($errors); 86 + } else { 87 + $error_view = null; 88 + } 89 + 90 + $form = id(new AphrontFormView()) 91 + ->addHiddenInput('behaviorForm', 1) 92 + ->setUser($viewer); 93 + 94 + $options = id(new AphrontFormRadioButtonControl()) 95 + ->setLabel(pht('Audio Behavior')) 96 + ->setName('audioBehavior') 97 + ->setValue( 98 + nonempty( 99 + $macro->getAudioBehavior(), 100 + PhabricatorFileImageMacro::AUDIO_BEHAVIOR_NONE)); 101 + 102 + $options->addButton( 103 + PhabricatorFileImageMacro::AUDIO_BEHAVIOR_NONE, 104 + pht('Do Not Play'), 105 + pht('Do not play audio.')); 106 + 107 + $options->addButton( 108 + PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE, 109 + pht('Play Once'), 110 + pht('Play audio once, when the viewer looks at the macro.')); 111 + 112 + $options->addButton( 113 + PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP, 114 + pht('Play Continuously'), 115 + pht( 116 + 'Play audio continuously, treating the macro as an audio source. '. 117 + 'Best for ambient sounds.')); 118 + 119 + $form->appendChild($options); 120 + 121 + $form 122 + ->appendChild( 123 + id(new AphrontFormSubmitControl()) 124 + ->setValue(pht('Save Audio Behavior')) 125 + ->addCancelButton($view_uri)); 126 + 127 + $crumbs = $this->buildApplicationCrumbs(); 128 + 129 + $title = pht('Edit Audio Behavior'); 130 + $crumb = pht('Edit Audio'); 131 + 132 + $crumbs->addCrumb( 133 + id(new PhabricatorCrumbView()) 134 + ->setHref($view_uri) 135 + ->setName(pht('Macro "%s"', $macro->getName()))); 136 + 137 + $crumbs->addCrumb( 138 + id(new PhabricatorCrumbView()) 139 + ->setHref($request->getRequestURI()) 140 + ->setName($crumb)); 141 + 142 + $upload_form = id(new AphrontFormView()) 143 + ->setEncType('multipart/form-data') 144 + ->setUser($viewer) 145 + ->appendChild( 146 + id(new AphrontFormFileControl()) 147 + ->setLabel(pht('Audio File')) 148 + ->setName('file')) 149 + ->appendChild( 150 + id(new AphrontFormSubmitControl()) 151 + ->setValue(pht('Upload File'))); 152 + 153 + $upload = id(new PHUIObjectBoxView()) 154 + ->setHeaderText(pht('Upload New Audio')) 155 + ->setForm($upload_form); 156 + 157 + $form_box = id(new PHUIObjectBoxView()) 158 + ->setHeaderText($title) 159 + ->setFormError($error_view) 160 + ->setForm($form); 161 + 162 + return $this->buildApplicationPage( 163 + array( 164 + $crumbs, 165 + $form_box, 166 + $upload, 167 + ), 168 + array( 169 + 'title' => $title, 170 + 'device' => true, 171 + )); 172 + } 173 + }
+25
src/applications/macro/controller/PhabricatorMacroViewController.php
··· 119 119 ->setHref($this->getApplicationURI('/edit/'.$macro->getID().'/')) 120 120 ->setIcon('edit')); 121 121 122 + $view->addAction( 123 + id(new PhabricatorActionView()) 124 + ->setName(pht('Edit Audio')) 125 + ->setHref($this->getApplicationURI('/audio/'.$macro->getID().'/')) 126 + ->setIcon('herald')); 127 + 122 128 if ($macro->getIsDisabled()) { 123 129 $view->addAction( 124 130 id(new PhabricatorActionView()) ··· 145 151 $view = id(new PhabricatorPropertyListView()) 146 152 ->setUser($this->getRequest()->getUser()) 147 153 ->setObject($macro); 154 + 155 + switch ($macro->getAudioBehavior()) { 156 + case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE: 157 + $view->addProperty(pht('Audio Behavior'), pht('Play Once')); 158 + break; 159 + case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP: 160 + $view->addProperty(pht('Audio Behavior'), pht('Loop')); 161 + break; 162 + } 163 + 164 + $audio_phid = $macro->getAudioPHID(); 165 + if ($audio_phid) { 166 + $this->loadHandles(array($audio_phid)); 167 + 168 + $view->addProperty( 169 + pht('Audio'), 170 + $this->getHandle($audio_phid)->renderLink()); 171 + } 172 + 148 173 149 174 $view->invokeWillRenderEvent(); 150 175
+16
src/applications/macro/editor/PhabricatorMacroEditor.php
··· 10 10 $types[] = PhabricatorMacroTransactionType::TYPE_NAME; 11 11 $types[] = PhabricatorMacroTransactionType::TYPE_DISABLED; 12 12 $types[] = PhabricatorMacroTransactionType::TYPE_FILE; 13 + $types[] = PhabricatorMacroTransactionType::TYPE_AUDIO; 14 + $types[] = PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR; 13 15 14 16 return $types; 15 17 } ··· 25 27 return $object->getIsDisabled(); 26 28 case PhabricatorMacroTransactionType::TYPE_FILE: 27 29 return $object->getFilePHID(); 30 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 31 + return $object->getAudioPHID(); 32 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 33 + return $object->getAudioBehavior(); 28 34 } 29 35 } 30 36 ··· 36 42 case PhabricatorMacroTransactionType::TYPE_NAME: 37 43 case PhabricatorMacroTransactionType::TYPE_DISABLED: 38 44 case PhabricatorMacroTransactionType::TYPE_FILE: 45 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 46 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 39 47 return $xaction->getNewValue(); 40 48 } 41 49 } ··· 54 62 case PhabricatorMacroTransactionType::TYPE_FILE: 55 63 $object->setFilePHID($xaction->getNewValue()); 56 64 break; 65 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 66 + $object->setAudioPHID($xaction->getNewValue()); 67 + break; 68 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 69 + $object->setAudioBehavior($xaction->getNewValue()); 70 + break; 57 71 } 58 72 } 59 73 ··· 72 86 case PhabricatorMacroTransactionType::TYPE_NAME: 73 87 case PhabricatorMacroTransactionType::TYPE_DISABLED: 74 88 case PhabricatorMacroTransactionType::TYPE_FILE: 89 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 90 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 75 91 return $v; 76 92 } 77 93
+16
src/applications/macro/storage/PhabricatorFileImageMacro.php
··· 11 11 protected $phid; 12 12 protected $name; 13 13 protected $isDisabled = 0; 14 + protected $audioPHID; 15 + protected $audioBehavior = self::AUDIO_BEHAVIOR_NONE; 14 16 15 17 private $file = self::ATTACHABLE; 18 + private $audio = self::ATTACHABLE; 19 + 20 + const AUDIO_BEHAVIOR_NONE = 'audio:none'; 21 + const AUDIO_BEHAVIOR_ONCE = 'audio:once'; 22 + const AUDIO_BEHAVIOR_LOOP = 'audio:loop'; 16 23 17 24 public function attachFile(PhabricatorFile $file) { 18 25 $this->file = $file; ··· 21 28 22 29 public function getFile() { 23 30 return $this->assertAttached($this->file); 31 + } 32 + 33 + public function attachAudio(PhabricatorFile $audio = null) { 34 + $this->audio = $audio; 35 + return $this; 36 + } 37 + 38 + public function getAudio() { 39 + return $this->assertAttached($this->audio); 24 40 } 25 41 26 42 public function getConfiguration() {
+77
src/applications/macro/storage/PhabricatorMacroTransaction.php
··· 27 27 28 28 switch ($this->getTransactionType()) { 29 29 case PhabricatorMacroTransactionType::TYPE_FILE: 30 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 30 31 if ($old !== null) { 31 32 $phids[] = $old; 32 33 } ··· 74 75 $this->renderHandleLink($author_phid)); 75 76 } 76 77 break; 78 + 79 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 80 + if (!$old) { 81 + return pht( 82 + '%s attached audio: %s.', 83 + $this->renderHandleLink($author_phid), 84 + $this->renderHandleLink($new)); 85 + } else { 86 + return pht( 87 + '%s changed the audio for this macro from %s to %s.', 88 + $this->renderHandleLink($author_phid), 89 + $this->renderHandleLink($old), 90 + $this->renderHandleLink($new)); 91 + } 92 + 93 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 94 + switch ($new) { 95 + case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE: 96 + return pht( 97 + '%s set the audio to play once.', 98 + $this->renderHandleLink($author_phid)); 99 + case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP: 100 + return pht( 101 + '%s set the audio to loop.', 102 + $this->renderHandleLink($author_phid)); 103 + default: 104 + return pht( 105 + '%s disabled the audio for this macro.', 106 + $this->renderHandleLink($author_phid)); 107 + } 108 + 77 109 case PhabricatorMacroTransactionType::TYPE_FILE: 78 110 if ($old === null) { 79 111 return pht( ··· 131 163 $this->renderHandleLink($author_phid), 132 164 $this->renderHandleLink($object_phid)); 133 165 } 166 + 167 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 168 + if (!$old) { 169 + return pht( 170 + '%s attached audio to %s: %s.', 171 + $this->renderHandleLink($author_phid), 172 + $this->renderHandleLink($object_phid), 173 + $this->renderHandleLink($new)); 174 + } else { 175 + return pht( 176 + '%s changed the audio for %s from %s to %s.', 177 + $this->renderHandleLink($author_phid), 178 + $this->renderHandleLink($object_phid), 179 + $this->renderHandleLink($old), 180 + $this->renderHandleLink($new)); 181 + } 182 + 183 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 184 + switch ($new) { 185 + case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_ONCE: 186 + return pht( 187 + '%s set the audio for %s to play once.', 188 + $this->renderHandleLink($author_phid), 189 + $this->renderHandleLink($object_phid)); 190 + case PhabricatorFileImageMacro::AUDIO_BEHAVIOR_LOOP: 191 + return pht( 192 + '%s set the audio for %s to loop.', 193 + $this->renderHandleLink($author_phid), 194 + $this->renderHandleLink($object_phid)); 195 + default: 196 + return pht( 197 + '%s disabled the audio for %s.', 198 + $this->renderHandleLink($author_phid), 199 + $this->renderHandleLink($object_phid)); 200 + } 201 + 134 202 } 135 203 136 204 return parent::getTitleForFeed($story); ··· 159 227 } else { 160 228 return pht('Edited Image'); 161 229 } 230 + 231 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 232 + return pht('Audio'); 233 + 234 + case PhabricatorMacroTransactionType::TYPE_AUDIO_BEHAVIOR: 235 + return pht('Audio Behavior'); 236 + 162 237 } 163 238 164 239 return parent::getActionName(); ··· 193 268 } else { 194 269 return 'undo'; 195 270 } 271 + case PhabricatorMacroTransactionType::TYPE_AUDIO: 272 + return 'herald'; 196 273 } 197 274 198 275 return parent::getIcon();
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1644 1644 'type' => 'sql', 1645 1645 'name' => $this->getPatchPath('20130926.dinkeys.sql'), 1646 1646 ), 1647 + '20130927.audiomacro.sql' => array( 1648 + 'type' => 'sql', 1649 + 'name' => $this->getPatchPath('20130927.audiomacro.sql'), 1650 + ), 1647 1651 ); 1648 1652 } 1649 1653 }