@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

DRAFT - throw together Phurl skeleton.

Summary: DRAFT - throw together Phurl skeleton.

Test Plan: The idea is that `some/long/url` will become `install/Udet4d` and can be viewed and edited at `install/Udet4d/view` and `install/Udet4d/edit`, respectively?

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: joshuaspence, chad, epriestley, Korvin

Maniphest Tasks: T6049

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

+1463
+10
resources/sql/autopatches/20150721.phurl.1.url.sql
··· 1 + CREATE TABLE {$NAMESPACE}_phurl.phurl_url ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT}, 5 + longURL VARCHAR(2047) NOT NULL COLLATE {$COLLATE_TEXT}, 6 + description VARCHAR(2047) NOT NULL COLLATE {$COLLATE_TEXT}, 7 + viewPolicy VARBINARY(64) NOT NULL, 8 + editPolicy VARBINARY(64) NOT NULL, 9 + spacePHID varbinary(64) DEFAULT NULL 10 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+19
resources/sql/autopatches/20150721.phurl.2.xaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_phurl.phurl_urltransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + authorPHID VARBINARY(64) NOT NULL, 5 + objectPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentPHID VARBINARY(64) DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 11 + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 12 + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 13 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 14 + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+16
resources/sql/autopatches/20150721.phurl.3.xactioncomment.sql
··· 1 + CREATE TABLE {$NAMESPACE}_phurl.phurl_urltransaction_comment ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + transactionPHID VARBINARY(64) DEFAULT NULL, 5 + authorPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentVersion INT UNSIGNED NOT NULL, 9 + content LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 10 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 11 + isDeleted TINYINT(1) NOT NULL, 12 + dateCreated INT UNSIGNED NOT NULL, 13 + dateModified INT UNSIGNED NOT NULL, 14 + UNIQUE KEY `key_phid` (`phid`), 15 + UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`) 16 + ) ENGINE=InnoDB COLLATE {$COLLATE_TEXT}
+3
resources/sql/autopatches/20150721.phurl.4.url.sql
··· 1 + ALTER TABLE {$NAMESPACE}_phurl.phurl_url 2 + ADD dateCreated int unsigned NOT NULL, 3 + ADD dateModified int unsigned NOT NULL;
+16
resources/sql/autopatches/20150721.phurl.5.edge.sql
··· 1 + CREATE TABLE {$NAMESPACE}_phurl.edge ( 2 + src VARBINARY(64) NOT NULL, 3 + type INT UNSIGNED NOT NULL, 4 + dst VARBINARY(64) NOT NULL, 5 + dateCreated INT UNSIGNED NOT NULL, 6 + seq INT UNSIGNED NOT NULL, 7 + dataID INT UNSIGNED, 8 + PRIMARY KEY (src, type, dst), 9 + KEY `src` (src, type, dateCreated, seq), 10 + UNIQUE KEY `key_dst` (dst, type, src) 11 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; 12 + 13 + CREATE TABLE {$NAMESPACE}_phurl.edgedata ( 14 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 15 + data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT} 16 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20150721.phurl.6.alias.sql
··· 1 + ALTER TABLE {$NAMESPACE}_phurl.phurl_url 2 + ADD alias VARCHAR(64) COLLATE {$COLLATE_SORT};
+6
resources/sql/autopatches/20150721.phurl.7.authorphid.sql
··· 1 + ALTER TABLE {$NAMESPACE}_phurl.phurl_url 2 + ADD authorPHID VARBINARY(64) NOT NULL; 3 + 4 + ALTER TABLE {$NAMESPACE}_phurl.phurl_url 5 + CHANGE description description LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, 6 + CHANGE longURL longURL LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
+41
src/__phutil_library_map__.php
··· 2476 2476 'PhabricatorPhrequentConfigOptions' => 'applications/phrequent/config/PhabricatorPhrequentConfigOptions.php', 2477 2477 'PhabricatorPhrictionApplication' => 'applications/phriction/application/PhabricatorPhrictionApplication.php', 2478 2478 'PhabricatorPhrictionConfigOptions' => 'applications/phriction/config/PhabricatorPhrictionConfigOptions.php', 2479 + 'PhabricatorPhurlApplication' => 'applications/phurl/application/PhabricatorPhurlApplication.php', 2480 + 'PhabricatorPhurlController' => 'applications/phurl/controller/PhabricatorPhurlController.php', 2481 + 'PhabricatorPhurlDAO' => 'applications/phurl/storage/PhabricatorPhurlDAO.php', 2482 + 'PhabricatorPhurlSchemaSpec' => 'applications/phurl/storage/PhabricatorPhurlSchemaSpec.php', 2483 + 'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php', 2484 + 'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php', 2485 + 'PhabricatorPhurlURLEditor' => 'applications/phurl/editor/PhabricatorPhurlURLEditor.php', 2486 + 'PhabricatorPhurlURLListController' => 'applications/phurl/controller/PhabricatorPhurlURLListController.php', 2487 + 'PhabricatorPhurlURLPHIDType' => 'applications/phurl/phid/PhabricatorPhurlURLPHIDType.php', 2488 + 'PhabricatorPhurlURLQuery' => 'applications/phurl/query/PhabricatorPhurlURLQuery.php', 2489 + 'PhabricatorPhurlURLSearchEngine' => 'applications/phurl/query/PhabricatorPhurlURLSearchEngine.php', 2490 + 'PhabricatorPhurlURLTransaction' => 'applications/phurl/storage/PhabricatorPhurlURLTransaction.php', 2491 + 'PhabricatorPhurlURLTransactionComment' => 'applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php', 2492 + 'PhabricatorPhurlURLTransactionQuery' => 'applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php', 2493 + 'PhabricatorPhurlURLViewController' => 'applications/phurl/controller/PhabricatorPhurlURLViewController.php', 2479 2494 'PhabricatorPlatformSite' => 'aphront/site/PhabricatorPlatformSite.php', 2480 2495 'PhabricatorPolicies' => 'applications/policy/constants/PhabricatorPolicies.php', 2481 2496 'PhabricatorPolicy' => 'applications/policy/storage/PhabricatorPolicy.php', ··· 6364 6379 'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions', 6365 6380 'PhabricatorPhrictionApplication' => 'PhabricatorApplication', 6366 6381 'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions', 6382 + 'PhabricatorPhurlApplication' => 'PhabricatorApplication', 6383 + 'PhabricatorPhurlController' => 'PhabricatorController', 6384 + 'PhabricatorPhurlDAO' => 'PhabricatorLiskDAO', 6385 + 'PhabricatorPhurlSchemaSpec' => 'PhabricatorConfigSchemaSpec', 6386 + 'PhabricatorPhurlURL' => array( 6387 + 'PhabricatorPhurlDAO', 6388 + 'PhabricatorPolicyInterface', 6389 + 'PhabricatorProjectInterface', 6390 + 'PhabricatorApplicationTransactionInterface', 6391 + 'PhabricatorSubscribableInterface', 6392 + 'PhabricatorTokenReceiverInterface', 6393 + 'PhabricatorDestructibleInterface', 6394 + 'PhabricatorMentionableInterface', 6395 + 'PhabricatorFlaggableInterface', 6396 + 'PhabricatorSpacesInterface', 6397 + ), 6398 + 'PhabricatorPhurlURLEditController' => 'PhabricatorPhurlController', 6399 + 'PhabricatorPhurlURLEditor' => 'PhabricatorApplicationTransactionEditor', 6400 + 'PhabricatorPhurlURLListController' => 'PhabricatorPhurlController', 6401 + 'PhabricatorPhurlURLPHIDType' => 'PhabricatorPHIDType', 6402 + 'PhabricatorPhurlURLQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6403 + 'PhabricatorPhurlURLSearchEngine' => 'PhabricatorApplicationSearchEngine', 6404 + 'PhabricatorPhurlURLTransaction' => 'PhabricatorApplicationTransaction', 6405 + 'PhabricatorPhurlURLTransactionComment' => 'PhabricatorApplicationTransactionComment', 6406 + 'PhabricatorPhurlURLTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6407 + 'PhabricatorPhurlURLViewController' => 'PhabricatorPhurlController', 6367 6408 'PhabricatorPlatformSite' => 'PhabricatorSite', 6368 6409 'PhabricatorPolicies' => 'PhabricatorPolicyConstants', 6369 6410 'PhabricatorPolicy' => array(
+45
src/applications/phurl/application/PhabricatorPhurlApplication.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlApplication extends PhabricatorApplication { 4 + 5 + public function getName() { 6 + return pht('Phurl'); 7 + } 8 + 9 + public function getShortDescription() { 10 + return pht('URL Shortener'); 11 + } 12 + 13 + public function getFlavorText() { 14 + return pht('Shorten your favorite URL.'); 15 + } 16 + 17 + public function getBaseURI() { 18 + return '/phurl/'; 19 + } 20 + 21 + public function getFontIcon() { 22 + return 'fa-compress'; 23 + } 24 + 25 + public function isPrototype() { 26 + return true; 27 + } 28 + 29 + public function getRoutes() { 30 + return array( 31 + '/U(?P<id>[1-9]\d*)' => 'PhabricatorPhurlURLViewController', 32 + '/phurl/' => array( 33 + '(?:query/(?P<queryKey>[^/]+)/)?' 34 + => 'PhabricatorPhurlURLListController', 35 + 'url/' => array( 36 + 'create/' 37 + => 'PhabricatorPhurlURLEditController', 38 + 'edit/(?P<id>[1-9]\d*)/' 39 + => 'PhabricatorPhurlURLEditController', 40 + ), 41 + ), 42 + ); 43 + } 44 + 45 + }
+15
src/applications/phurl/controller/PhabricatorPhurlController.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorPhurlController extends PhabricatorController { 4 + 5 + protected function buildApplicationCrumbs() { 6 + $crumbs = parent::buildApplicationCrumbs(); 7 + $crumbs->addAction( 8 + id(new PHUIListItemView()) 9 + ->setName(pht('Shorten URL')) 10 + ->setHref($this->getApplicationURI().'url/create/') 11 + ->setIcon('fa-plus-square')); 12 + 13 + return $crumbs; 14 + } 15 + }
+240
src/applications/phurl/controller/PhabricatorPhurlURLEditController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLEditController 4 + extends PhabricatorPhurlController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $id = $request->getURIData('id'); 8 + $is_create = !$id; 9 + 10 + $viewer = $request->getViewer(); 11 + $user_phid = $viewer->getPHID(); 12 + $error_name = true; 13 + $error_long_url = true; 14 + $validation_exception = null; 15 + 16 + $next_workflow = $request->getStr('next'); 17 + $uri_query = $request->getStr('query'); 18 + 19 + if ($is_create) { 20 + $url = PhabricatorPhurlURL::initializeNewPhurlURL( 21 + $viewer); 22 + $submit_label = pht('Create'); 23 + $page_title = pht('Shorten URL'); 24 + $subscribers = array(); 25 + $cancel_uri = $this->getApplicationURI(); 26 + } else { 27 + $url = id(new PhabricatorPhurlURLQuery()) 28 + ->setViewer($viewer) 29 + ->withIDs(array($id)) 30 + ->requireCapabilities( 31 + array( 32 + PhabricatorPolicyCapability::CAN_VIEW, 33 + PhabricatorPolicyCapability::CAN_EDIT, 34 + )) 35 + ->executeOne(); 36 + 37 + if (!$url) { 38 + return new Aphront404Response(); 39 + } 40 + 41 + $submit_label = pht('Update'); 42 + $page_title = pht('Update URL'); 43 + 44 + $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( 45 + $url->getPHID()); 46 + 47 + $cancel_uri = '/U'.$url->getID(); 48 + } 49 + 50 + if ($is_create) { 51 + $projects = array(); 52 + } else { 53 + $projects = PhabricatorEdgeQuery::loadDestinationPHIDs( 54 + $url->getPHID(), 55 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 56 + $projects = array_reverse($projects); 57 + } 58 + 59 + $name = $url->getName(); 60 + $long_url = $url->getLongURL(); 61 + $description = $url->getDescription(); 62 + $edit_policy = $url->getEditPolicy(); 63 + $view_policy = $url->getViewPolicy(); 64 + $space = $url->getSpacePHID(); 65 + 66 + if ($request->isFormPost()) { 67 + $xactions = array(); 68 + $name = $request->getStr('name'); 69 + $long_url = $request->getStr('longURL'); 70 + $projects = $request->getArr('projects'); 71 + $description = $request->getStr('description'); 72 + $subscribers = $request->getArr('subscribers'); 73 + $edit_policy = $request->getStr('editPolicy'); 74 + $view_policy = $request->getStr('viewPolicy'); 75 + $space = $request->getStr('spacePHID'); 76 + 77 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 78 + ->setTransactionType( 79 + PhabricatorPhurlURLTransaction::TYPE_NAME) 80 + ->setNewValue($name); 81 + 82 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 83 + ->setTransactionType( 84 + PhabricatorPhurlURLTransaction::TYPE_URL) 85 + ->setNewValue($long_url); 86 + 87 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 88 + ->setTransactionType( 89 + PhabricatorTransactions::TYPE_SUBSCRIBERS) 90 + ->setNewValue(array('=' => array_fuse($subscribers))); 91 + 92 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 93 + ->setTransactionType( 94 + PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION) 95 + ->setNewValue($description); 96 + 97 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 98 + ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 99 + ->setNewValue($view_policy); 100 + 101 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 102 + ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) 103 + ->setNewValue($edit_policy); 104 + 105 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 106 + ->setTransactionType(PhabricatorTransactions::TYPE_SPACE) 107 + ->setNewValue($space); 108 + 109 + $editor = id(new PhabricatorPhurlURLEditor()) 110 + ->setActor($viewer) 111 + ->setContentSourceFromRequest($request) 112 + ->setContinueOnNoEffect(true); 113 + 114 + try { 115 + $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; 116 + $xactions[] = id(new PhabricatorPhurlURLTransaction()) 117 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 118 + ->setMetadataValue('edge:type', $proj_edge_type) 119 + ->setNewValue(array('=' => array_fuse($projects))); 120 + 121 + $xactions = $editor->applyTransactions($url, $xactions); 122 + return id(new AphrontRedirectResponse()) 123 + ->setURI($url->getURI()); 124 + } catch (PhabricatorApplicationTransactionValidationException $ex) { 125 + $validation_exception = $ex; 126 + $error_name = $ex->getShortMessage( 127 + PhabricatorPhurlURLTransaction::TYPE_NAME); 128 + $error_long_url = $ex->getShortMessage( 129 + PhabricatorPhurlURLTransaction::TYPE_URL); 130 + } 131 + } 132 + 133 + $current_policies = id(new PhabricatorPolicyQuery()) 134 + ->setViewer($viewer) 135 + ->setObject($url) 136 + ->execute(); 137 + 138 + $name = id(new AphrontFormTextControl()) 139 + ->setLabel(pht('Name')) 140 + ->setName('name') 141 + ->setValue($name) 142 + ->setError($error_name); 143 + 144 + $long_url = id(new AphrontFormTextControl()) 145 + ->setLabel(pht('URL')) 146 + ->setName('longURL') 147 + ->setValue($long_url) 148 + ->setError($error_long_url); 149 + 150 + $projects = id(new AphrontFormTokenizerControl()) 151 + ->setLabel(pht('Projects')) 152 + ->setName('projects') 153 + ->setValue($projects) 154 + ->setUser($viewer) 155 + ->setDatasource(new PhabricatorProjectDatasource()); 156 + 157 + $description = id(new PhabricatorRemarkupControl()) 158 + ->setLabel(pht('Description')) 159 + ->setName('description') 160 + ->setValue($description) 161 + ->setUser($viewer); 162 + 163 + $view_policies = id(new AphrontFormPolicyControl()) 164 + ->setUser($viewer) 165 + ->setValue($view_policy) 166 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 167 + ->setPolicyObject($url) 168 + ->setPolicies($current_policies) 169 + ->setSpacePHID($space) 170 + ->setName('viewPolicy'); 171 + $edit_policies = id(new AphrontFormPolicyControl()) 172 + ->setUser($viewer) 173 + ->setValue($edit_policy) 174 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 175 + ->setPolicyObject($url) 176 + ->setPolicies($current_policies) 177 + ->setName('editPolicy'); 178 + 179 + $subscribers = id(new AphrontFormTokenizerControl()) 180 + ->setLabel(pht('Subscribers')) 181 + ->setName('subscribers') 182 + ->setValue($subscribers) 183 + ->setUser($viewer) 184 + ->setDatasource(new PhabricatorMetaMTAMailableDatasource()); 185 + 186 + $form = id(new AphrontFormView()) 187 + ->setUser($viewer) 188 + ->appendChild($name) 189 + ->appendChild($long_url) 190 + ->appendControl($view_policies) 191 + ->appendControl($edit_policies) 192 + ->appendControl($subscribers) 193 + ->appendChild($projects) 194 + ->appendChild($description); 195 + 196 + 197 + if ($request->isAjax()) { 198 + return $this->newDialog() 199 + ->setTitle($page_title) 200 + ->setWidth(AphrontDialogView::WIDTH_FULL) 201 + ->appendForm($form) 202 + ->addCancelButton($cancel_uri) 203 + ->addSubmitButton($submit_label); 204 + } 205 + 206 + $submit = id(new AphrontFormSubmitControl()) 207 + ->addCancelButton($cancel_uri) 208 + ->setValue($submit_label); 209 + 210 + $form->appendChild($submit); 211 + 212 + $form_box = id(new PHUIObjectBoxView()) 213 + ->setHeaderText($page_title) 214 + ->setForm($form); 215 + 216 + $crumbs = $this->buildApplicationCrumbs(); 217 + 218 + if (!$is_create) { 219 + $crumbs->addTextCrumb($url->getMonogram(), $url->getURI()); 220 + } else { 221 + $crumbs->addTextCrumb(pht('Create URL')); 222 + } 223 + 224 + $crumbs->addTextCrumb($page_title); 225 + 226 + $object_box = id(new PHUIObjectBoxView()) 227 + ->setHeaderText($page_title) 228 + ->setValidationException($validation_exception) 229 + ->appendChild($form); 230 + 231 + return $this->buildApplicationPage( 232 + array( 233 + $crumbs, 234 + $object_box, 235 + ), 236 + array( 237 + 'title' => $page_title, 238 + )); 239 + } 240 + }
+34
src/applications/phurl/controller/PhabricatorPhurlURLListController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLListController 4 + extends PhabricatorPhurlController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $engine = new PhabricatorPhurlURLSearchEngine(); 12 + $controller = id(new PhabricatorApplicationSearchController()) 13 + ->setQueryKey($request->getURIData('queryKey')) 14 + ->setSearchEngine($engine) 15 + ->setNavigation($this->buildSideNav()); 16 + return $this->delegateToController($controller); 17 + } 18 + 19 + public function buildSideNav() { 20 + $user = $this->getRequest()->getUser(); 21 + 22 + $nav = new AphrontSideNavFilterView(); 23 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 24 + 25 + id(new PhabricatorPhurlURLSearchEngine()) 26 + ->setViewer($user) 27 + ->addNavigationItems($nav->getMenu()); 28 + 29 + $nav->selectFilter(null); 30 + 31 + return $nav; 32 + } 33 + 34 + }
+139
src/applications/phurl/controller/PhabricatorPhurlURLViewController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLViewController 4 + extends PhabricatorPhurlController { 5 + 6 + public function shouldAllowPublic() { 7 + return true; 8 + } 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $request->getViewer(); 12 + $id = $request->getURIData('id'); 13 + 14 + $timeline = null; 15 + 16 + $url = id(new PhabricatorPhurlURLQuery()) 17 + ->setViewer($viewer) 18 + ->withIDs(array($id)) 19 + ->executeOne(); 20 + if (!$url) { 21 + return new Aphront404Response(); 22 + } 23 + 24 + $title = $url->getMonogram(); 25 + $page_title = $title.' '.$url->getName(); 26 + $crumbs = $this->buildApplicationCrumbs(); 27 + $crumbs->addTextCrumb($title, $url->getURI()); 28 + 29 + $timeline = $this->buildTransactionTimeline( 30 + $url, 31 + new PhabricatorPhurlURLTransactionQuery()); 32 + 33 + $header = $this->buildHeaderView($url); 34 + $actions = $this->buildActionView($url); 35 + $properties = $this->buildPropertyView($url); 36 + 37 + $properties->setActionList($actions); 38 + $box = id(new PHUIObjectBoxView()) 39 + ->setHeader($header) 40 + ->addPropertyList($properties); 41 + 42 + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); 43 + $add_comment_header = $is_serious 44 + ? pht('Add Comment') 45 + : pht('More Cowbell'); 46 + $draft = PhabricatorDraft::newFromUserAndKey($viewer, $url->getPHID()); 47 + $comment_uri = $this->getApplicationURI( 48 + '/phurl/comment/'.$url->getID().'/'); 49 + $add_comment_form = id(new PhabricatorApplicationTransactionCommentView()) 50 + ->setUser($viewer) 51 + ->setObjectPHID($url->getPHID()) 52 + ->setDraft($draft) 53 + ->setHeaderText($add_comment_header) 54 + ->setAction($comment_uri) 55 + ->setSubmitButtonName(pht('Add Comment')); 56 + 57 + return $this->buildApplicationPage( 58 + array( 59 + $crumbs, 60 + $box, 61 + $timeline, 62 + $add_comment_form, 63 + ), 64 + array( 65 + 'title' => $page_title, 66 + 'pageObjects' => array($url->getPHID()), 67 + )); 68 + } 69 + 70 + private function buildHeaderView(PhabricatorPhurlURL $url) { 71 + $viewer = $this->getViewer(); 72 + $icon = 'fa-compress'; 73 + $color = 'green'; 74 + $status = pht('Active'); 75 + 76 + $header = id(new PHUIHeaderView()) 77 + ->setUser($viewer) 78 + ->setHeader($url->getName()) 79 + ->setStatus($icon, $color, $status) 80 + ->setPolicyObject($url); 81 + 82 + return $header; 83 + } 84 + 85 + private function buildActionView(PhabricatorPhurlURL $url) { 86 + $viewer = $this->getViewer(); 87 + $id = $url->getID(); 88 + 89 + $actions = id(new PhabricatorActionListView()) 90 + ->setObjectURI($url->getURI()) 91 + ->setUser($viewer) 92 + ->setObject($url); 93 + 94 + $can_edit = PhabricatorPolicyFilter::hasCapability( 95 + $viewer, 96 + $url, 97 + PhabricatorPolicyCapability::CAN_EDIT); 98 + 99 + $actions->addAction( 100 + id(new PhabricatorActionView()) 101 + ->setName(pht('Edit')) 102 + ->setIcon('fa-pencil') 103 + ->setHref($this->getApplicationURI("url/edit/{$id}/")) 104 + ->setDisabled(!$can_edit) 105 + ->setWorkflow(!$can_edit)); 106 + 107 + return $actions; 108 + } 109 + 110 + private function buildPropertyView(PhabricatorPhurlURL $url) { 111 + $viewer = $this->getViewer(); 112 + 113 + $properties = id(new PHUIPropertyListView()) 114 + ->setUser($viewer) 115 + ->setObject($url); 116 + 117 + $properties->addProperty( 118 + pht('Original URL'), 119 + $url->getLongURL()); 120 + 121 + $properties->invokeWillRenderEvent(); 122 + 123 + if (strlen($url->getDescription())) { 124 + $description = PhabricatorMarkupEngine::renderOneObject( 125 + id(new PhabricatorMarkupOneOff())->setContent($url->getDescription()), 126 + 'default', 127 + $viewer); 128 + 129 + $properties->addSectionHeader( 130 + pht('Description'), 131 + PHUIPropertyListView::ICON_SUMMARY); 132 + 133 + $properties->addTextContent($description); 134 + } 135 + 136 + return $properties; 137 + } 138 + 139 + }
+207
src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorPhurlApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('Phurl'); 12 + } 13 + 14 + public function getTransactionTypes() { 15 + $types = parent::getTransactionTypes(); 16 + 17 + $types[] = PhabricatorPhurlURLTransaction::TYPE_NAME; 18 + $types[] = PhabricatorPhurlURLTransaction::TYPE_URL; 19 + $types[] = PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION; 20 + 21 + $types[] = PhabricatorTransactions::TYPE_COMMENT; 22 + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 23 + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 24 + 25 + return $types; 26 + } 27 + 28 + protected function getCustomTransactionOldValue( 29 + PhabricatorLiskDAO $object, 30 + PhabricatorApplicationTransaction $xaction) { 31 + switch ($xaction->getTransactionType()) { 32 + case PhabricatorPhurlURLTransaction::TYPE_NAME: 33 + return $object->getName(); 34 + case PhabricatorPhurlURLTransaction::TYPE_URL: 35 + return $object->getLongURL(); 36 + case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION: 37 + return $object->getDescription(); 38 + } 39 + 40 + return parent::getCustomTransactionOldValue($object, $xaction); 41 + } 42 + 43 + protected function getCustomTransactionNewValue( 44 + PhabricatorLiskDAO $object, 45 + PhabricatorApplicationTransaction $xaction) { 46 + switch ($xaction->getTransactionType()) { 47 + case PhabricatorPhurlURLTransaction::TYPE_NAME: 48 + case PhabricatorPhurlURLTransaction::TYPE_URL: 49 + case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION: 50 + return $xaction->getNewValue(); 51 + } 52 + 53 + return parent::getCustomTransactionNewValue($object, $xaction); 54 + } 55 + 56 + protected function applyCustomInternalTransaction( 57 + PhabricatorLiskDAO $object, 58 + PhabricatorApplicationTransaction $xaction) { 59 + 60 + switch ($xaction->getTransactionType()) { 61 + case PhabricatorPhurlURLTransaction::TYPE_NAME: 62 + $object->setName($xaction->getNewValue()); 63 + return; 64 + case PhabricatorPhurlURLTransaction::TYPE_URL: 65 + $object->setLongURL($xaction->getNewValue()); 66 + return; 67 + case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION: 68 + $object->setDescription($xaction->getNewValue()); 69 + return; 70 + } 71 + 72 + return parent::applyCustomInternalTransaction($object, $xaction); 73 + } 74 + 75 + protected function applyCustomExternalTransaction( 76 + PhabricatorLiskDAO $object, 77 + PhabricatorApplicationTransaction $xaction) { 78 + 79 + switch ($xaction->getTransactionType()) { 80 + case PhabricatorPhurlURLTransaction::TYPE_NAME: 81 + case PhabricatorPhurlURLTransaction::TYPE_URL: 82 + case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION: 83 + return; 84 + } 85 + 86 + return parent::applyCustomExternalTransaction($object, $xaction); 87 + } 88 + 89 + protected function validateTransaction( 90 + PhabricatorLiskDAO $object, 91 + $type, 92 + array $xactions) { 93 + 94 + $errors = parent::validateTransaction($object, $type, $xactions); 95 + 96 + switch ($type) { 97 + case PhabricatorPhurlURLTransaction::TYPE_NAME: 98 + $missing = $this->validateIsEmptyTextField( 99 + $object->getName(), 100 + $xactions); 101 + 102 + if ($missing) { 103 + $error = new PhabricatorApplicationTransactionValidationError( 104 + $type, 105 + pht('Required'), 106 + pht('URL name is required.'), 107 + nonempty(last($xactions), null)); 108 + 109 + $error->setIsMissingFieldError(true); 110 + $errors[] = $error; 111 + } 112 + break; 113 + case PhabricatorPhurlURLTransaction::TYPE_URL: 114 + $missing = $this->validateIsEmptyTextField( 115 + $object->getLongURL(), 116 + $xactions); 117 + 118 + if ($missing) { 119 + $error = new PhabricatorApplicationTransactionValidationError( 120 + $type, 121 + pht('Required'), 122 + pht('URL path is required.'), 123 + nonempty(last($xactions), null)); 124 + 125 + $error->setIsMissingFieldError(true); 126 + $errors[] = $error; 127 + } 128 + break; 129 + } 130 + 131 + return $errors; 132 + } 133 + 134 + protected function shouldPublishFeedStory( 135 + PhabricatorLiskDAO $object, 136 + array $xactions) { 137 + return true; 138 + } 139 + 140 + protected function supportsSearch() { 141 + return true; 142 + } 143 + 144 + protected function shouldSendMail( 145 + PhabricatorLiskDAO $object, 146 + array $xactions) { 147 + return true; 148 + } 149 + 150 + protected function getMailSubjectPrefix() { 151 + return pht('[Phurl]'); 152 + } 153 + 154 + protected function getMailTo(PhabricatorLiskDAO $object) { 155 + $phids = array(); 156 + 157 + if ($object->getPHID()) { 158 + $phids[] = $object->getPHID(); 159 + } 160 + $phids[] = $this->getActingAsPHID(); 161 + $phids = array_unique($phids); 162 + 163 + return $phids; 164 + } 165 + 166 + public function getMailTagsMap() { 167 + return array( 168 + PhabricatorPhurlURLTransaction::MAILTAG_CONTENT => 169 + pht( 170 + "A URL's name or path changes."), 171 + PhabricatorPhurlURLTransaction::MAILTAG_OTHER => 172 + pht('Other event activity not listed above occurs.'), 173 + ); 174 + } 175 + 176 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 177 + $id = $object->getID(); 178 + $name = $object->getName(); 179 + 180 + return id(new PhabricatorMetaMTAMail()) 181 + ->setSubject("U{$id}: {$name}") 182 + ->addHeader('Thread-Topic', "U{$id}: ".$object->getName()); 183 + } 184 + 185 + protected function buildMailBody( 186 + PhabricatorLiskDAO $object, 187 + array $xactions) { 188 + 189 + $description = $object->getDescription(); 190 + $body = parent::buildMailBody($object, $xactions); 191 + 192 + if (strlen($description)) { 193 + $body->addTextSection( 194 + pht('URL DESCRIPTION'), 195 + $object->getDescription()); 196 + } 197 + 198 + $body->addLinkSection( 199 + pht('URL DETAIL'), 200 + PhabricatorEnv::getProductionURI('/U'.$object->getID())); 201 + 202 + 203 + return $body; 204 + } 205 + 206 + 207 + }
+74
src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLPHIDType extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'PHRL'; 6 + 7 + public function getTypeName() { 8 + return pht('URL'); 9 + } 10 + 11 + public function getPHIDTypeApplicationClass() { 12 + return 'PhabricatorPhurlApplication'; 13 + } 14 + 15 + public function newObject() { 16 + return new PhabricatorPhurlURL(); 17 + } 18 + 19 + protected function buildQueryForObjects( 20 + PhabricatorObjectQuery $query, 21 + array $phids) { 22 + 23 + return id(new PhabricatorPhurlURLQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + foreach ($handles as $phid => $handle) { 33 + $url = $objects[$phid]; 34 + 35 + $id = $url->getID(); 36 + $name = $url->getName(); 37 + $full_name = $url->getMonogram().' '.$name; 38 + 39 + $handle 40 + ->setName($name) 41 + ->setFullName($full_name) 42 + ->setURI($url->getURI()); 43 + } 44 + } 45 + 46 + public function canLoadNamedObject($name) { 47 + return preg_match('/^U[1-9]\d*$/i', $name); 48 + } 49 + 50 + public function loadNamedObjects( 51 + PhabricatorObjectQuery $query, 52 + array $names) { 53 + 54 + $id_map = array(); 55 + foreach ($names as $name) { 56 + $id = (int)substr($name, 1); 57 + $id_map[$id][] = $name; 58 + } 59 + 60 + $objects = id(new PhabricatorPhurlURLQuery()) 61 + ->setViewer($query->getViewer()) 62 + ->withIDs(array_keys($id_map)) 63 + ->execute(); 64 + 65 + $results = array(); 66 + foreach ($objects as $id => $object) { 67 + foreach (idx($id_map, $id, array()) as $name) { 68 + $results[$name] = $object; 69 + } 70 + } 71 + 72 + return $results; 73 + } 74 + }
+100
src/applications/phurl/query/PhabricatorPhurlURLQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $names; 9 + private $longURLs; 10 + private $authorPHIDs; 11 + 12 + public function newResultObject() { 13 + return new PhabricatorPhurlURL(); 14 + } 15 + 16 + public function withIDs(array $ids) { 17 + $this->ids = $ids; 18 + return $this; 19 + } 20 + 21 + public function withPHIDs(array $phids) { 22 + $this->phids = $phids; 23 + return $this; 24 + } 25 + 26 + public function withNames(array $names) { 27 + $this->names = $names; 28 + return $this; 29 + } 30 + 31 + public function withLongURLs(array $long_urls) { 32 + $this->longURLs = $long_urls; 33 + return $this; 34 + } 35 + 36 + public function withAuthorPHIDs(array $author_phids) { 37 + $this->authorPHIDs = $author_phids; 38 + return $this; 39 + } 40 + 41 + protected function getPagingValueMap($cursor, array $keys) { 42 + $url = $this->loadCursorObject($cursor); 43 + return array( 44 + 'id' => $url->getID(), 45 + ); 46 + } 47 + 48 + protected function loadPage() { 49 + return $this->loadStandardPage($this->newResultObject()); 50 + } 51 + 52 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 53 + $where = parent::buildWhereClauseParts($conn); 54 + 55 + if ($this->ids !== null) { 56 + $where[] = qsprintf( 57 + $conn, 58 + 'url.id IN (%Ld)', 59 + $this->ids); 60 + } 61 + 62 + if ($this->phids !== null) { 63 + $where[] = qsprintf( 64 + $conn, 65 + 'url.phid IN (%Ls)', 66 + $this->phids); 67 + } 68 + 69 + if ($this->authorPHIDs !== null) { 70 + $where[] = qsprintf( 71 + $conn, 72 + 'url.authorPHID IN (%Ls)', 73 + $this->authorPHIDs); 74 + } 75 + 76 + if ($this->names !== null) { 77 + $where[] = qsprintf( 78 + $conn, 79 + 'url.name IN (%Ls)', 80 + $this->names); 81 + } 82 + 83 + if ($this->longURLs !== null) { 84 + $where[] = qsprintf( 85 + $conn, 86 + 'url.longURL IN (%Ls)', 87 + $this->longURLs); 88 + } 89 + 90 + return $where; 91 + } 92 + 93 + protected function getPrimaryTableAlias() { 94 + return 'url'; 95 + } 96 + 97 + public function getQueryApplicationClass() { 98 + return 'PhabricatorPhurlApplication'; 99 + } 100 + }
+94
src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function getResultTypeDescription() { 7 + return pht('Shortened URLs'); 8 + } 9 + 10 + public function getApplicationClassName() { 11 + return 'PhabricatorPhurlApplication'; 12 + } 13 + 14 + public function newQuery() { 15 + return new PhabricatorPhurlURLQuery(); 16 + } 17 + 18 + protected function shouldShowOrderField() { 19 + return true; 20 + } 21 + 22 + protected function buildCustomSearchFields() { 23 + return array( 24 + id(new PhabricatorSearchDatasourceField()) 25 + ->setLabel(pht('Created By')) 26 + ->setKey('authorPHIDs') 27 + ->setDatasource(new PhabricatorPeopleUserFunctionDatasource()), 28 + ); 29 + } 30 + 31 + protected function buildQueryFromParameters(array $map) { 32 + $query = $this->newQuery(); 33 + 34 + if ($map['authorPHIDs']) { 35 + $query->withAuthorPHIDs($map['authorPHIDs']); 36 + } 37 + 38 + return $query; 39 + } 40 + 41 + protected function getURI($path) { 42 + return '/phurl/'.$path; 43 + } 44 + 45 + protected function getBuiltinQueryNames() { 46 + $names = array( 47 + 'authored' => pht('Authored'), 48 + 'all' => pht('All URLs'), 49 + ); 50 + 51 + return $names; 52 + } 53 + 54 + public function buildSavedQueryFromBuiltin($query_key) { 55 + $query = $this->newSavedQuery(); 56 + $query->setQueryKey($query_key); 57 + $viewer = $this->requireViewer(); 58 + 59 + switch ($query_key) { 60 + case 'authored': 61 + return $query->setParameter('authorPHIDs', array($viewer->getPHID())); 62 + case 'all': 63 + return $query; 64 + } 65 + 66 + return parent::buildSavedQueryFromBuiltin($query_key); 67 + } 68 + 69 + protected function renderResultList( 70 + array $urls, 71 + PhabricatorSavedQuery $query, 72 + array $handles) { 73 + 74 + assert_instances_of($urls, 'PhabricatorPhurlURL'); 75 + $viewer = $this->requireViewer(); 76 + $list = new PHUIObjectItemListView(); 77 + $handles = $viewer->loadHandles(mpull($urls, 'getAuthorPHID')); 78 + 79 + foreach ($urls as $url) { 80 + $item = id(new PHUIObjectItemView()) 81 + ->setUser($viewer) 82 + ->setObject($url) 83 + ->setHeader($viewer->renderHandle($url->getPHID())); 84 + 85 + $list->addItem($item); 86 + } 87 + 88 + $result = new PhabricatorApplicationSearchResultView(); 89 + $result->setObjectList($list); 90 + $result->setNoDataString(pht('No URLs found.')); 91 + 92 + return $result; 93 + } 94 + }
+10
src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new PhabricatorPhurlURLTransaction(); 8 + } 9 + 10 + }
+9
src/applications/phurl/storage/PhabricatorPhurlDAO.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorPhurlDAO extends PhabricatorLiskDAO { 4 + 5 + public function getApplicationName() { 6 + return 'phurl'; 7 + } 8 + 9 + }
+10
src/applications/phurl/storage/PhabricatorPhurlSchemaSpec.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlSchemaSpec 4 + extends PhabricatorConfigSchemaSpec { 5 + 6 + public function buildSchemata() { 7 + $this->buildEdgeSchemata(new PhabricatorPhurlURL()); 8 + } 9 + 10 + }
+171
src/applications/phurl/storage/PhabricatorPhurlURL.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURL extends PhabricatorPhurlDAO 4 + implements PhabricatorPolicyInterface, 5 + PhabricatorProjectInterface, 6 + PhabricatorApplicationTransactionInterface, 7 + PhabricatorSubscribableInterface, 8 + PhabricatorTokenReceiverInterface, 9 + PhabricatorDestructibleInterface, 10 + PhabricatorMentionableInterface, 11 + PhabricatorFlaggableInterface, 12 + PhabricatorSpacesInterface { 13 + 14 + protected $name; 15 + protected $alias; 16 + protected $longURL; 17 + protected $description; 18 + 19 + protected $viewPolicy; 20 + protected $editPolicy; 21 + 22 + protected $authorPHID; 23 + protected $spacePHID; 24 + 25 + const DEFAULT_ICON = 'fa-compress'; 26 + 27 + public static function initializeNewPhurlURL(PhabricatorUser $actor) { 28 + $app = id(new PhabricatorApplicationQuery()) 29 + ->setViewer($actor) 30 + ->withClasses(array('PhabricatorPhurlApplication')) 31 + ->executeOne(); 32 + 33 + return id(new PhabricatorPhurlURL()) 34 + ->setAuthorPHID($actor->getPHID()) 35 + ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) 36 + ->setEditPolicy($actor->getPHID()) 37 + ->setSpacePHID($actor->getDefaultSpacePHID()); 38 + } 39 + 40 + protected function getConfiguration() { 41 + return array( 42 + self::CONFIG_AUX_PHID => true, 43 + self::CONFIG_COLUMN_SCHEMA => array( 44 + 'name' => 'text', 45 + 'alias' => 'sort64?', 46 + 'longURL' => 'text', 47 + 'description' => 'text', 48 + ), 49 + self::CONFIG_KEY_SCHEMA => array( 50 + 'key_instance' => array( 51 + 'columns' => array('alias'), 52 + 'unique' => true, 53 + ), 54 + 'key_author' => array( 55 + 'columns' => array('authorPHID'), 56 + ), 57 + ), 58 + ) + parent::getConfiguration(); 59 + } 60 + 61 + public function generatePHID() { 62 + return PhabricatorPHID::generateNewPHID( 63 + PhabricatorPhurlURLPHIDType::TYPECONST); 64 + } 65 + 66 + public function getMonogram() { 67 + return 'U'.$this->getID(); 68 + } 69 + 70 + public function getURI() { 71 + $uri = '/'.$this->getMonogram(); 72 + return $uri; 73 + } 74 + 75 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 76 + 77 + 78 + public function getCapabilities() { 79 + return array( 80 + PhabricatorPolicyCapability::CAN_VIEW, 81 + PhabricatorPolicyCapability::CAN_EDIT, 82 + ); 83 + } 84 + 85 + public function getPolicy($capability) { 86 + switch ($capability) { 87 + case PhabricatorPolicyCapability::CAN_VIEW: 88 + return $this->getViewPolicy(); 89 + case PhabricatorPolicyCapability::CAN_EDIT: 90 + return $this->getEditPolicy(); 91 + } 92 + } 93 + 94 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 95 + $user_phid = $this->getAuthorPHID(); 96 + if ($user_phid) { 97 + $viewer_phid = $viewer->getPHID(); 98 + if ($viewer_phid == $user_phid) { 99 + return true; 100 + } 101 + } 102 + 103 + return false; 104 + } 105 + 106 + public function describeAutomaticCapability($capability) { 107 + return pht('The owner of a URL can always view and edit it.'); 108 + } 109 + 110 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 111 + 112 + 113 + public function getApplicationTransactionEditor() { 114 + return new PhabricatorPhurlURLEditor(); 115 + } 116 + 117 + public function getApplicationTransactionObject() { 118 + return $this; 119 + } 120 + 121 + public function getApplicationTransactionTemplate() { 122 + return new PhabricatorPhurlURLTransaction(); 123 + } 124 + 125 + public function willRenderTimeline( 126 + PhabricatorApplicationTransactionView $timeline, 127 + AphrontRequest $request) { 128 + 129 + return $timeline; 130 + } 131 + 132 + /* -( PhabricatorSubscribableInterface )----------------------------------- */ 133 + 134 + 135 + public function isAutomaticallySubscribed($phid) { 136 + return ($phid == $this->getAuthorPHID()); 137 + } 138 + 139 + public function shouldShowSubscribersProperty() { 140 + return true; 141 + } 142 + 143 + public function shouldAllowSubscription($phid) { 144 + return true; 145 + } 146 + 147 + /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ 148 + 149 + 150 + public function getUsersToNotifyOfTokenGiven() { 151 + return array($this->getAuthorPHID()); 152 + } 153 + 154 + /* -( PhabricatorDestructibleInterface )----------------------------------- */ 155 + 156 + 157 + public function destroyObjectPermanently( 158 + PhabricatorDestructionEngine $engine) { 159 + 160 + $this->openTransaction(); 161 + $this->delete(); 162 + $this->saveTransaction(); 163 + } 164 + 165 + /* -( PhabricatorSpacesInterface )----------------------------------------- */ 166 + 167 + 168 + public function getSpacePHID() { 169 + return $this->spacePHID; 170 + } 171 + }
+191
src/applications/phurl/storage/PhabricatorPhurlURLTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_NAME = 'phurl.name'; 7 + const TYPE_URL = 'phurl.longurl'; 8 + const TYPE_DESCRIPTION = 'phurl.description'; 9 + 10 + public function getApplicationName() { 11 + return 'phurl'; 12 + } 13 + 14 + public function getApplicationTransactionType() { 15 + return PhabricatorPhurlURLPHIDType::TYPECONST; 16 + } 17 + 18 + public function getApplicationTransactionCommentObject() { 19 + return new PhabricatorPhurlURLTransactionComment(); 20 + } 21 + 22 + public function getRequiredHandlePHIDs() { 23 + $phids = parent::getRequiredHandlePHIDs(); 24 + 25 + switch ($this->getTransactionType()) { 26 + case self::TYPE_NAME: 27 + case self::TYPE_URL: 28 + case self::TYPE_DESCRIPTION: 29 + $phids[] = $this->getObjectPHID(); 30 + break; 31 + } 32 + 33 + return $phids; 34 + } 35 + 36 + public function shouldHide() { 37 + $old = $this->getOldValue(); 38 + switch ($this->getTransactionType()) { 39 + case self::TYPE_DESCRIPTION: 40 + return ($old === null); 41 + } 42 + return parent::shouldHide(); 43 + } 44 + 45 + public function getIcon() { 46 + switch ($this->getTransactionType()) { 47 + case self::TYPE_NAME: 48 + case self::TYPE_URL: 49 + case self::TYPE_DESCRIPTION: 50 + return 'fa-pencil'; 51 + break; 52 + } 53 + return parent::getIcon(); 54 + } 55 + 56 + public function getTitle() { 57 + $author_phid = $this->getAuthorPHID(); 58 + $object_phid = $this->getObjectPHID(); 59 + 60 + $old = $this->getOldValue(); 61 + $new = $this->getNewValue(); 62 + 63 + $type = $this->getTransactionType(); 64 + switch ($type) { 65 + case self::TYPE_NAME: 66 + if ($old === null) { 67 + return pht( 68 + '%s created this URL.', 69 + $this->renderHandleLink($author_phid)); 70 + } else { 71 + return pht( 72 + '%s changed the name of the URL from %s to %s.', 73 + $this->renderHandleLink($author_phid), 74 + $old, 75 + $new); 76 + } 77 + case self::TYPE_URL: 78 + return pht( 79 + '%s changed the destination of the URL from %s to %s.', 80 + $this->renderHandleLink($author_phid), 81 + $old, 82 + $new); 83 + case self::TYPE_DESCRIPTION: 84 + return pht( 85 + "%s updated the URL's description.", 86 + $this->renderHandleLink($author_phid)); 87 + } 88 + return parent::getTitle(); 89 + } 90 + 91 + public function getTitleForFeed() { 92 + $author_phid = $this->getAuthorPHID(); 93 + $object_phid = $this->getObjectPHID(); 94 + 95 + $old = $this->getOldValue(); 96 + $new = $this->getNewValue(); 97 + 98 + $viewer = $this->getViewer(); 99 + 100 + $type = $this->getTransactionType(); 101 + switch ($type) { 102 + case self::TYPE_NAME: 103 + if ($old === null) { 104 + return pht( 105 + '%s created %s.', 106 + $this->renderHandleLink($author_phid), 107 + $this->renderHandleLink($object_phid)); 108 + } else { 109 + return pht( 110 + '%s changed the name of %s from %s to %s.', 111 + $this->renderHandleLink($author_phid), 112 + $this->renderHandleLink($object_phid), 113 + $old, 114 + $new); 115 + } 116 + case self::TYPE_URL: 117 + if ($old === null) { 118 + return pht( 119 + '%s created %s.', 120 + $this->renderHandleLink($author_phid), 121 + $this->renderHandleLink($object_phid)); 122 + } else { 123 + return pht( 124 + '%s changed the destination of %s from %s to %s', 125 + $this->renderHandleLink($author_phid), 126 + $this->renderHandleLink($object_phid), 127 + $old, 128 + $new); 129 + } 130 + case self::TYPE_DESCRIPTION: 131 + return pht( 132 + '%s updated the description of %s.', 133 + $this->renderHandleLink($author_phid), 134 + $this->renderHandleLink($object_phid)); 135 + } 136 + 137 + return parent::getTitleForFeed(); 138 + } 139 + 140 + public function getColor() { 141 + $old = $this->getOldValue(); 142 + $new = $this->getNewValue(); 143 + 144 + switch ($this->getTransactionType()) { 145 + case self::TYPE_NAME: 146 + case self::TYPE_URL: 147 + case self::TYPE_DESCRIPTION: 148 + return PhabricatorTransactions::COLOR_GREEN; 149 + } 150 + 151 + return parent::getColor(); 152 + } 153 + 154 + 155 + public function hasChangeDetails() { 156 + switch ($this->getTransactionType()) { 157 + case self::TYPE_DESCRIPTION: 158 + return ($this->getOldValue() !== null); 159 + } 160 + 161 + return parent::hasChangeDetails(); 162 + } 163 + 164 + public function renderChangeDetails(PhabricatorUser $viewer) { 165 + switch ($this->getTransactionType()) { 166 + case self::TYPE_DESCRIPTION: 167 + $old = $this->getOldValue(); 168 + $new = $this->getNewValue(); 169 + 170 + return $this->renderTextCorpusChangeDetails( 171 + $viewer, 172 + $old, 173 + $new); 174 + } 175 + 176 + return parent::renderChangeDetails($viewer); 177 + } 178 + 179 + public function getMailTags() { 180 + $tags = array(); 181 + switch ($this->getTransactionType()) { 182 + case self::TYPE_NAME: 183 + case self::TYPE_DESCRIPTION: 184 + case self::TYPE_URL: 185 + $tags[] = self::MAILTAG_CONTENT; 186 + break; 187 + } 188 + return $tags; 189 + } 190 + 191 + }
+10
src/applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php
··· 1 + <?php 2 + 3 + final class PhabricatorPhurlURLTransactionComment 4 + extends PhabricatorApplicationTransactionComment { 5 + 6 + public function getApplicationTransactionObject() { 7 + return new PhabricatorPhurlURLTransaction(); 8 + } 9 + 10 + }
+1
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 106 106 'db.almanac' => array(), 107 107 'db.multimeter' => array(), 108 108 'db.spaces' => array(), 109 + 'db.phurl' => array(), 109 110 'db.badges' => array(), 110 111 '0000.legacy.sql' => array( 111 112 'legacy' => 0,