Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13964171
D13681.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
48 KB
Referenced Files
None
Subscribers
None
D13681.diff
View Options
diff --git a/resources/sql/autopatches/20150721.phurl.1.url.sql b/resources/sql/autopatches/20150721.phurl.1.url.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.1.url.sql
@@ -0,0 +1,10 @@
+CREATE TABLE {$NAMESPACE}_phurl.phurl_url (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
+ longURL VARCHAR(2047) NOT NULL COLLATE {$COLLATE_TEXT},
+ description VARCHAR(2047) NOT NULL COLLATE {$COLLATE_TEXT},
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ spacePHID varbinary(64) DEFAULT NULL
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20150721.phurl.2.xaction.sql b/resources/sql/autopatches/20150721.phurl.2.xaction.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.2.xaction.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_phurl.phurl_urltransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ objectPHID VARBINARY(64) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ commentPHID VARBINARY(64) DEFAULT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
+ oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ KEY `key_object` (`objectPHID`)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20150721.phurl.3.xactioncomment.sql b/resources/sql/autopatches/20150721.phurl.3.xactioncomment.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.3.xactioncomment.sql
@@ -0,0 +1,16 @@
+CREATE TABLE {$NAMESPACE}_phurl.phurl_urltransaction_comment (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ transactionPHID VARBINARY(64) DEFAULT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ content LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ isDeleted TINYINT(1) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ UNIQUE KEY `key_version` (`transactionPHID`,`commentVersion`)
+) ENGINE=InnoDB COLLATE {$COLLATE_TEXT}
diff --git a/resources/sql/autopatches/20150721.phurl.4.url.sql b/resources/sql/autopatches/20150721.phurl.4.url.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.4.url.sql
@@ -0,0 +1,3 @@
+ALTER TABLE {$NAMESPACE}_phurl.phurl_url
+ ADD dateCreated int unsigned NOT NULL,
+ ADD dateModified int unsigned NOT NULL;
diff --git a/resources/sql/autopatches/20150721.phurl.5.edge.sql b/resources/sql/autopatches/20150721.phurl.5.edge.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.5.edge.sql
@@ -0,0 +1,16 @@
+CREATE TABLE {$NAMESPACE}_phurl.edge (
+ src VARBINARY(64) NOT NULL,
+ type INT UNSIGNED NOT NULL,
+ dst VARBINARY(64) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ seq INT UNSIGNED NOT NULL,
+ dataID INT UNSIGNED,
+ PRIMARY KEY (src, type, dst),
+ KEY `src` (src, type, dateCreated, seq),
+ UNIQUE KEY `key_dst` (dst, type, src)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+
+CREATE TABLE {$NAMESPACE}_phurl.edgedata (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20150721.phurl.6.alias.sql b/resources/sql/autopatches/20150721.phurl.6.alias.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.6.alias.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_phurl.phurl_url
+ ADD alias VARCHAR(64) COLLATE {$COLLATE_SORT};
diff --git a/resources/sql/autopatches/20150721.phurl.7.authorphid.sql b/resources/sql/autopatches/20150721.phurl.7.authorphid.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.7.authorphid.sql
@@ -0,0 +1,6 @@
+ALTER TABLE {$NAMESPACE}_phurl.phurl_url
+ ADD authorPHID VARBINARY(64) NOT NULL;
+
+ALTER TABLE {$NAMESPACE}_phurl.phurl_url
+ CHANGE description description LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ CHANGE longURL longURL LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2476,6 +2476,21 @@
'PhabricatorPhrequentConfigOptions' => 'applications/phrequent/config/PhabricatorPhrequentConfigOptions.php',
'PhabricatorPhrictionApplication' => 'applications/phriction/application/PhabricatorPhrictionApplication.php',
'PhabricatorPhrictionConfigOptions' => 'applications/phriction/config/PhabricatorPhrictionConfigOptions.php',
+ 'PhabricatorPhurlApplication' => 'applications/phurl/application/PhabricatorPhurlApplication.php',
+ 'PhabricatorPhurlController' => 'applications/phurl/controller/PhabricatorPhurlController.php',
+ 'PhabricatorPhurlDAO' => 'applications/phurl/storage/PhabricatorPhurlDAO.php',
+ 'PhabricatorPhurlSchemaSpec' => 'applications/phurl/storage/PhabricatorPhurlSchemaSpec.php',
+ 'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php',
+ 'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php',
+ 'PhabricatorPhurlURLEditor' => 'applications/phurl/editor/PhabricatorPhurlURLEditor.php',
+ 'PhabricatorPhurlURLListController' => 'applications/phurl/controller/PhabricatorPhurlURLListController.php',
+ 'PhabricatorPhurlURLPHIDType' => 'applications/phurl/phid/PhabricatorPhurlURLPHIDType.php',
+ 'PhabricatorPhurlURLQuery' => 'applications/phurl/query/PhabricatorPhurlURLQuery.php',
+ 'PhabricatorPhurlURLSearchEngine' => 'applications/phurl/query/PhabricatorPhurlURLSearchEngine.php',
+ 'PhabricatorPhurlURLTransaction' => 'applications/phurl/storage/PhabricatorPhurlURLTransaction.php',
+ 'PhabricatorPhurlURLTransactionComment' => 'applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php',
+ 'PhabricatorPhurlURLTransactionQuery' => 'applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php',
+ 'PhabricatorPhurlURLViewController' => 'applications/phurl/controller/PhabricatorPhurlURLViewController.php',
'PhabricatorPlatformSite' => 'aphront/site/PhabricatorPlatformSite.php',
'PhabricatorPolicies' => 'applications/policy/constants/PhabricatorPolicies.php',
'PhabricatorPolicy' => 'applications/policy/storage/PhabricatorPolicy.php',
@@ -6364,6 +6379,32 @@
'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPhrictionApplication' => 'PhabricatorApplication',
'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions',
+ 'PhabricatorPhurlApplication' => 'PhabricatorApplication',
+ 'PhabricatorPhurlController' => 'PhabricatorController',
+ 'PhabricatorPhurlDAO' => 'PhabricatorLiskDAO',
+ 'PhabricatorPhurlSchemaSpec' => 'PhabricatorConfigSchemaSpec',
+ 'PhabricatorPhurlURL' => array(
+ 'PhabricatorPhurlDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorProjectInterface',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorSubscribableInterface',
+ 'PhabricatorTokenReceiverInterface',
+ 'PhabricatorDestructibleInterface',
+ 'PhabricatorMentionableInterface',
+ 'PhabricatorFlaggableInterface',
+ 'PhabricatorSpacesInterface',
+ ),
+ 'PhabricatorPhurlURLEditController' => 'PhabricatorPhurlController',
+ 'PhabricatorPhurlURLEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhabricatorPhurlURLListController' => 'PhabricatorPhurlController',
+ 'PhabricatorPhurlURLPHIDType' => 'PhabricatorPHIDType',
+ 'PhabricatorPhurlURLQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorPhurlURLSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'PhabricatorPhurlURLTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhabricatorPhurlURLTransactionComment' => 'PhabricatorApplicationTransactionComment',
+ 'PhabricatorPhurlURLTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhabricatorPhurlURLViewController' => 'PhabricatorPhurlController',
'PhabricatorPlatformSite' => 'PhabricatorSite',
'PhabricatorPolicies' => 'PhabricatorPolicyConstants',
'PhabricatorPolicy' => array(
diff --git a/src/applications/phurl/application/PhabricatorPhurlApplication.php b/src/applications/phurl/application/PhabricatorPhurlApplication.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/application/PhabricatorPhurlApplication.php
@@ -0,0 +1,45 @@
+<?php
+
+final class PhabricatorPhurlApplication extends PhabricatorApplication {
+
+ public function getName() {
+ return pht('Phurl');
+ }
+
+ public function getShortDescription() {
+ return pht('URL Shortener');
+ }
+
+ public function getFlavorText() {
+ return pht('Shorten your favorite URL.');
+ }
+
+ public function getBaseURI() {
+ return '/phurl/';
+ }
+
+ public function getFontIcon() {
+ return 'fa-compress';
+ }
+
+ public function isPrototype() {
+ return true;
+ }
+
+ public function getRoutes() {
+ return array(
+ '/U(?P<id>[1-9]\d*)' => 'PhabricatorPhurlURLViewController',
+ '/phurl/' => array(
+ '(?:query/(?P<queryKey>[^/]+)/)?'
+ => 'PhabricatorPhurlURLListController',
+ 'url/' => array(
+ 'create/'
+ => 'PhabricatorPhurlURLEditController',
+ 'edit/(?P<id>[1-9]\d*)/'
+ => 'PhabricatorPhurlURLEditController',
+ ),
+ ),
+ );
+ }
+
+}
diff --git a/src/applications/phurl/controller/PhabricatorPhurlController.php b/src/applications/phurl/controller/PhabricatorPhurlController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/controller/PhabricatorPhurlController.php
@@ -0,0 +1,15 @@
+<?php
+
+abstract class PhabricatorPhurlController extends PhabricatorController {
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Shorten URL'))
+ ->setHref($this->getApplicationURI().'url/create/')
+ ->setIcon('fa-plus-square'));
+
+ return $crumbs;
+ }
+}
diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php
@@ -0,0 +1,240 @@
+<?php
+
+final class PhabricatorPhurlURLEditController
+ extends PhabricatorPhurlController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $id = $request->getURIData('id');
+ $is_create = !$id;
+
+ $viewer = $request->getViewer();
+ $user_phid = $viewer->getPHID();
+ $error_name = true;
+ $error_long_url = true;
+ $validation_exception = null;
+
+ $next_workflow = $request->getStr('next');
+ $uri_query = $request->getStr('query');
+
+ if ($is_create) {
+ $url = PhabricatorPhurlURL::initializeNewPhurlURL(
+ $viewer);
+ $submit_label = pht('Create');
+ $page_title = pht('Shorten URL');
+ $subscribers = array();
+ $cancel_uri = $this->getApplicationURI();
+ } else {
+ $url = id(new PhabricatorPhurlURLQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+
+ if (!$url) {
+ return new Aphront404Response();
+ }
+
+ $submit_label = pht('Update');
+ $page_title = pht('Update URL');
+
+ $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
+ $url->getPHID());
+
+ $cancel_uri = '/U'.$url->getID();
+ }
+
+ if ($is_create) {
+ $projects = array();
+ } else {
+ $projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $url->getPHID(),
+ PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
+ $projects = array_reverse($projects);
+ }
+
+ $name = $url->getName();
+ $long_url = $url->getLongURL();
+ $description = $url->getDescription();
+ $edit_policy = $url->getEditPolicy();
+ $view_policy = $url->getViewPolicy();
+ $space = $url->getSpacePHID();
+
+ if ($request->isFormPost()) {
+ $xactions = array();
+ $name = $request->getStr('name');
+ $long_url = $request->getStr('longURL');
+ $projects = $request->getArr('projects');
+ $description = $request->getStr('description');
+ $subscribers = $request->getArr('subscribers');
+ $edit_policy = $request->getStr('editPolicy');
+ $view_policy = $request->getStr('viewPolicy');
+ $space = $request->getStr('spacePHID');
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(
+ PhabricatorPhurlURLTransaction::TYPE_NAME)
+ ->setNewValue($name);
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(
+ PhabricatorPhurlURLTransaction::TYPE_URL)
+ ->setNewValue($long_url);
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(
+ PhabricatorTransactions::TYPE_SUBSCRIBERS)
+ ->setNewValue(array('=' => array_fuse($subscribers)));
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(
+ PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION)
+ ->setNewValue($description);
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
+ ->setNewValue($view_policy);
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
+ ->setNewValue($edit_policy);
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
+ ->setNewValue($space);
+
+ $editor = id(new PhabricatorPhurlURLEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ try {
+ $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $proj_edge_type)
+ ->setNewValue(array('=' => array_fuse($projects)));
+
+ $xactions = $editor->applyTransactions($url, $xactions);
+ return id(new AphrontRedirectResponse())
+ ->setURI($url->getURI());
+ } catch (PhabricatorApplicationTransactionValidationException $ex) {
+ $validation_exception = $ex;
+ $error_name = $ex->getShortMessage(
+ PhabricatorPhurlURLTransaction::TYPE_NAME);
+ $error_long_url = $ex->getShortMessage(
+ PhabricatorPhurlURLTransaction::TYPE_URL);
+ }
+ }
+
+ $current_policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($url)
+ ->execute();
+
+ $name = id(new AphrontFormTextControl())
+ ->setLabel(pht('Name'))
+ ->setName('name')
+ ->setValue($name)
+ ->setError($error_name);
+
+ $long_url = id(new AphrontFormTextControl())
+ ->setLabel(pht('URL'))
+ ->setName('longURL')
+ ->setValue($long_url)
+ ->setError($error_long_url);
+
+ $projects = id(new AphrontFormTokenizerControl())
+ ->setLabel(pht('Projects'))
+ ->setName('projects')
+ ->setValue($projects)
+ ->setUser($viewer)
+ ->setDatasource(new PhabricatorProjectDatasource());
+
+ $description = id(new PhabricatorRemarkupControl())
+ ->setLabel(pht('Description'))
+ ->setName('description')
+ ->setValue($description)
+ ->setUser($viewer);
+
+ $view_policies = id(new AphrontFormPolicyControl())
+ ->setUser($viewer)
+ ->setValue($view_policy)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicyObject($url)
+ ->setPolicies($current_policies)
+ ->setSpacePHID($space)
+ ->setName('viewPolicy');
+ $edit_policies = id(new AphrontFormPolicyControl())
+ ->setUser($viewer)
+ ->setValue($edit_policy)
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
+ ->setPolicyObject($url)
+ ->setPolicies($current_policies)
+ ->setName('editPolicy');
+
+ $subscribers = id(new AphrontFormTokenizerControl())
+ ->setLabel(pht('Subscribers'))
+ ->setName('subscribers')
+ ->setValue($subscribers)
+ ->setUser($viewer)
+ ->setDatasource(new PhabricatorMetaMTAMailableDatasource());
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild($name)
+ ->appendChild($long_url)
+ ->appendControl($view_policies)
+ ->appendControl($edit_policies)
+ ->appendControl($subscribers)
+ ->appendChild($projects)
+ ->appendChild($description);
+
+
+ if ($request->isAjax()) {
+ return $this->newDialog()
+ ->setTitle($page_title)
+ ->setWidth(AphrontDialogView::WIDTH_FULL)
+ ->appendForm($form)
+ ->addCancelButton($cancel_uri)
+ ->addSubmitButton($submit_label);
+ }
+
+ $submit = id(new AphrontFormSubmitControl())
+ ->addCancelButton($cancel_uri)
+ ->setValue($submit_label);
+
+ $form->appendChild($submit);
+
+ $form_box = id(new PHUIObjectBoxView())
+ ->setHeaderText($page_title)
+ ->setForm($form);
+
+ $crumbs = $this->buildApplicationCrumbs();
+
+ if (!$is_create) {
+ $crumbs->addTextCrumb($url->getMonogram(), $url->getURI());
+ } else {
+ $crumbs->addTextCrumb(pht('Create URL'));
+ }
+
+ $crumbs->addTextCrumb($page_title);
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeaderText($page_title)
+ ->setValidationException($validation_exception)
+ ->appendChild($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $object_box,
+ ),
+ array(
+ 'title' => $page_title,
+ ));
+ }
+}
diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLListController.php b/src/applications/phurl/controller/PhabricatorPhurlURLListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/controller/PhabricatorPhurlURLListController.php
@@ -0,0 +1,34 @@
+<?php
+
+final class PhabricatorPhurlURLListController
+ extends PhabricatorPhurlController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $engine = new PhabricatorPhurlURLSearchEngine();
+ $controller = id(new PhabricatorApplicationSearchController())
+ ->setQueryKey($request->getURIData('queryKey'))
+ ->setSearchEngine($engine)
+ ->setNavigation($this->buildSideNav());
+ return $this->delegateToController($controller);
+ }
+
+ public function buildSideNav() {
+ $user = $this->getRequest()->getUser();
+
+ $nav = new AphrontSideNavFilterView();
+ $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
+
+ id(new PhabricatorPhurlURLSearchEngine())
+ ->setViewer($user)
+ ->addNavigationItems($nav->getMenu());
+
+ $nav->selectFilter(null);
+
+ return $nav;
+ }
+
+}
diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php
@@ -0,0 +1,139 @@
+<?php
+
+final class PhabricatorPhurlURLViewController
+ extends PhabricatorPhurlController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ $timeline = null;
+
+ $url = id(new PhabricatorPhurlURLQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$url) {
+ return new Aphront404Response();
+ }
+
+ $title = $url->getMonogram();
+ $page_title = $title.' '.$url->getName();
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($title, $url->getURI());
+
+ $timeline = $this->buildTransactionTimeline(
+ $url,
+ new PhabricatorPhurlURLTransactionQuery());
+
+ $header = $this->buildHeaderView($url);
+ $actions = $this->buildActionView($url);
+ $properties = $this->buildPropertyView($url);
+
+ $properties->setActionList($actions);
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+
+ $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
+ $add_comment_header = $is_serious
+ ? pht('Add Comment')
+ : pht('More Cowbell');
+ $draft = PhabricatorDraft::newFromUserAndKey($viewer, $url->getPHID());
+ $comment_uri = $this->getApplicationURI(
+ '/phurl/comment/'.$url->getID().'/');
+ $add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
+ ->setUser($viewer)
+ ->setObjectPHID($url->getPHID())
+ ->setDraft($draft)
+ ->setHeaderText($add_comment_header)
+ ->setAction($comment_uri)
+ ->setSubmitButtonName(pht('Add Comment'));
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ $timeline,
+ $add_comment_form,
+ ),
+ array(
+ 'title' => $page_title,
+ 'pageObjects' => array($url->getPHID()),
+ ));
+ }
+
+ private function buildHeaderView(PhabricatorPhurlURL $url) {
+ $viewer = $this->getViewer();
+ $icon = 'fa-compress';
+ $color = 'green';
+ $status = pht('Active');
+
+ $header = id(new PHUIHeaderView())
+ ->setUser($viewer)
+ ->setHeader($url->getName())
+ ->setStatus($icon, $color, $status)
+ ->setPolicyObject($url);
+
+ return $header;
+ }
+
+ private function buildActionView(PhabricatorPhurlURL $url) {
+ $viewer = $this->getViewer();
+ $id = $url->getID();
+
+ $actions = id(new PhabricatorActionListView())
+ ->setObjectURI($url->getURI())
+ ->setUser($viewer)
+ ->setObject($url);
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $url,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit'))
+ ->setIcon('fa-pencil')
+ ->setHref($this->getApplicationURI("url/edit/{$id}/"))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit));
+
+ return $actions;
+ }
+
+ private function buildPropertyView(PhabricatorPhurlURL $url) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($url);
+
+ $properties->addProperty(
+ pht('Original URL'),
+ $url->getLongURL());
+
+ $properties->invokeWillRenderEvent();
+
+ if (strlen($url->getDescription())) {
+ $description = PhabricatorMarkupEngine::renderOneObject(
+ id(new PhabricatorMarkupOneOff())->setContent($url->getDescription()),
+ 'default',
+ $viewer);
+
+ $properties->addSectionHeader(
+ pht('Description'),
+ PHUIPropertyListView::ICON_SUMMARY);
+
+ $properties->addTextContent($description);
+ }
+
+ return $properties;
+ }
+
+}
diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php
@@ -0,0 +1,207 @@
+<?php
+
+final class PhabricatorPhurlURLEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorPhurlApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Phurl');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PhabricatorPhurlURLTransaction::TYPE_NAME;
+ $types[] = PhabricatorPhurlURLTransaction::TYPE_URL;
+ $types[] = PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION;
+
+ $types[] = PhabricatorTransactions::TYPE_COMMENT;
+ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+ $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorPhurlURLTransaction::TYPE_NAME:
+ return $object->getName();
+ case PhabricatorPhurlURLTransaction::TYPE_URL:
+ return $object->getLongURL();
+ case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION:
+ return $object->getDescription();
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorPhurlURLTransaction::TYPE_NAME:
+ case PhabricatorPhurlURLTransaction::TYPE_URL:
+ case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION:
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorPhurlURLTransaction::TYPE_NAME:
+ $object->setName($xaction->getNewValue());
+ return;
+ case PhabricatorPhurlURLTransaction::TYPE_URL:
+ $object->setLongURL($xaction->getNewValue());
+ return;
+ case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION:
+ $object->setDescription($xaction->getNewValue());
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorPhurlURLTransaction::TYPE_NAME:
+ case PhabricatorPhurlURLTransaction::TYPE_URL:
+ case PhabricatorPhurlURLTransaction::TYPE_DESCRIPTION:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+ protected function validateTransaction(
+ PhabricatorLiskDAO $object,
+ $type,
+ array $xactions) {
+
+ $errors = parent::validateTransaction($object, $type, $xactions);
+
+ switch ($type) {
+ case PhabricatorPhurlURLTransaction::TYPE_NAME:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getName(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('URL name is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ case PhabricatorPhurlURLTransaction::TYPE_URL:
+ $missing = $this->validateIsEmptyTextField(
+ $object->getLongURL(),
+ $xactions);
+
+ if ($missing) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Required'),
+ pht('URL path is required.'),
+ nonempty(last($xactions), null));
+
+ $error->setIsMissingFieldError(true);
+ $errors[] = $error;
+ }
+ break;
+ }
+
+ return $errors;
+ }
+
+ protected function shouldPublishFeedStory(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ protected function supportsSearch() {
+ return true;
+ }
+
+ protected function shouldSendMail(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ protected function getMailSubjectPrefix() {
+ return pht('[Phurl]');
+ }
+
+ protected function getMailTo(PhabricatorLiskDAO $object) {
+ $phids = array();
+
+ if ($object->getPHID()) {
+ $phids[] = $object->getPHID();
+ }
+ $phids[] = $this->getActingAsPHID();
+ $phids = array_unique($phids);
+
+ return $phids;
+ }
+
+ public function getMailTagsMap() {
+ return array(
+ PhabricatorPhurlURLTransaction::MAILTAG_CONTENT =>
+ pht(
+ "A URL's name or path changes."),
+ PhabricatorPhurlURLTransaction::MAILTAG_OTHER =>
+ pht('Other event activity not listed above occurs.'),
+ );
+ }
+
+ protected function buildMailTemplate(PhabricatorLiskDAO $object) {
+ $id = $object->getID();
+ $name = $object->getName();
+
+ return id(new PhabricatorMetaMTAMail())
+ ->setSubject("U{$id}: {$name}")
+ ->addHeader('Thread-Topic', "U{$id}: ".$object->getName());
+ }
+
+ protected function buildMailBody(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+
+ $description = $object->getDescription();
+ $body = parent::buildMailBody($object, $xactions);
+
+ if (strlen($description)) {
+ $body->addTextSection(
+ pht('URL DESCRIPTION'),
+ $object->getDescription());
+ }
+
+ $body->addLinkSection(
+ pht('URL DETAIL'),
+ PhabricatorEnv::getProductionURI('/U'.$object->getID()));
+
+
+ return $body;
+ }
+
+
+}
diff --git a/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php b/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/phid/PhabricatorPhurlURLPHIDType.php
@@ -0,0 +1,74 @@
+<?php
+
+final class PhabricatorPhurlURLPHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'PHRL';
+
+ public function getTypeName() {
+ return pht('URL');
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorPhurlApplication';
+ }
+
+ public function newObject() {
+ return new PhabricatorPhurlURL();
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PhabricatorPhurlURLQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $url = $objects[$phid];
+
+ $id = $url->getID();
+ $name = $url->getName();
+ $full_name = $url->getMonogram().' '.$name;
+
+ $handle
+ ->setName($name)
+ ->setFullName($full_name)
+ ->setURI($url->getURI());
+ }
+ }
+
+ public function canLoadNamedObject($name) {
+ return preg_match('/^U[1-9]\d*$/i', $name);
+ }
+
+ public function loadNamedObjects(
+ PhabricatorObjectQuery $query,
+ array $names) {
+
+ $id_map = array();
+ foreach ($names as $name) {
+ $id = (int)substr($name, 1);
+ $id_map[$id][] = $name;
+ }
+
+ $objects = id(new PhabricatorPhurlURLQuery())
+ ->setViewer($query->getViewer())
+ ->withIDs(array_keys($id_map))
+ ->execute();
+
+ $results = array();
+ foreach ($objects as $id => $object) {
+ foreach (idx($id_map, $id, array()) as $name) {
+ $results[$name] = $object;
+ }
+ }
+
+ return $results;
+ }
+}
diff --git a/src/applications/phurl/query/PhabricatorPhurlURLQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/query/PhabricatorPhurlURLQuery.php
@@ -0,0 +1,100 @@
+<?php
+
+final class PhabricatorPhurlURLQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $names;
+ private $longURLs;
+ private $authorPHIDs;
+
+ public function newResultObject() {
+ return new PhabricatorPhurlURL();
+ }
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withNames(array $names) {
+ $this->names = $names;
+ return $this;
+ }
+
+ public function withLongURLs(array $long_urls) {
+ $this->longURLs = $long_urls;
+ return $this;
+ }
+
+ public function withAuthorPHIDs(array $author_phids) {
+ $this->authorPHIDs = $author_phids;
+ return $this;
+ }
+
+ protected function getPagingValueMap($cursor, array $keys) {
+ $url = $this->loadCursorObject($cursor);
+ return array(
+ 'id' => $url->getID(),
+ );
+ }
+
+ protected function loadPage() {
+ return $this->loadStandardPage($this->newResultObject());
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'url.id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'url.phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->authorPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'url.authorPHID IN (%Ls)',
+ $this->authorPHIDs);
+ }
+
+ if ($this->names !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'url.name IN (%Ls)',
+ $this->names);
+ }
+
+ if ($this->longURLs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'url.longURL IN (%Ls)',
+ $this->longURLs);
+ }
+
+ return $where;
+ }
+
+ protected function getPrimaryTableAlias() {
+ return 'url';
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorPhurlApplication';
+ }
+}
diff --git a/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php b/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/query/PhabricatorPhurlURLSearchEngine.php
@@ -0,0 +1,94 @@
+<?php
+
+final class PhabricatorPhurlURLSearchEngine
+ extends PhabricatorApplicationSearchEngine {
+
+ public function getResultTypeDescription() {
+ return pht('Shortened URLs');
+ }
+
+ public function getApplicationClassName() {
+ return 'PhabricatorPhurlApplication';
+ }
+
+ public function newQuery() {
+ return new PhabricatorPhurlURLQuery();
+ }
+
+ protected function shouldShowOrderField() {
+ return true;
+ }
+
+ protected function buildCustomSearchFields() {
+ return array(
+ id(new PhabricatorSearchDatasourceField())
+ ->setLabel(pht('Created By'))
+ ->setKey('authorPHIDs')
+ ->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
+ );
+ }
+
+ protected function buildQueryFromParameters(array $map) {
+ $query = $this->newQuery();
+
+ if ($map['authorPHIDs']) {
+ $query->withAuthorPHIDs($map['authorPHIDs']);
+ }
+
+ return $query;
+ }
+
+ protected function getURI($path) {
+ return '/phurl/'.$path;
+ }
+
+ protected function getBuiltinQueryNames() {
+ $names = array(
+ 'authored' => pht('Authored'),
+ 'all' => pht('All URLs'),
+ );
+
+ return $names;
+ }
+
+ public function buildSavedQueryFromBuiltin($query_key) {
+ $query = $this->newSavedQuery();
+ $query->setQueryKey($query_key);
+ $viewer = $this->requireViewer();
+
+ switch ($query_key) {
+ case 'authored':
+ return $query->setParameter('authorPHIDs', array($viewer->getPHID()));
+ case 'all':
+ return $query;
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function renderResultList(
+ array $urls,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+
+ assert_instances_of($urls, 'PhabricatorPhurlURL');
+ $viewer = $this->requireViewer();
+ $list = new PHUIObjectItemListView();
+ $handles = $viewer->loadHandles(mpull($urls, 'getAuthorPHID'));
+
+ foreach ($urls as $url) {
+ $item = id(new PHUIObjectItemView())
+ ->setUser($viewer)
+ ->setObject($url)
+ ->setHeader($viewer->renderHandle($url->getPHID()));
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No URLs found.'));
+
+ return $result;
+ }
+}
diff --git a/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php b/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/query/PhabricatorPhurlURLTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorPhurlURLTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhabricatorPhurlURLTransaction();
+ }
+
+}
diff --git a/src/applications/phurl/storage/PhabricatorPhurlDAO.php b/src/applications/phurl/storage/PhabricatorPhurlDAO.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/storage/PhabricatorPhurlDAO.php
@@ -0,0 +1,9 @@
+<?php
+
+abstract class PhabricatorPhurlDAO extends PhabricatorLiskDAO {
+
+ public function getApplicationName() {
+ return 'phurl';
+ }
+
+}
diff --git a/src/applications/phurl/storage/PhabricatorPhurlSchemaSpec.php b/src/applications/phurl/storage/PhabricatorPhurlSchemaSpec.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/storage/PhabricatorPhurlSchemaSpec.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorPhurlSchemaSpec
+ extends PhabricatorConfigSchemaSpec {
+
+ public function buildSchemata() {
+ $this->buildEdgeSchemata(new PhabricatorPhurlURL());
+ }
+
+}
diff --git a/src/applications/phurl/storage/PhabricatorPhurlURL.php b/src/applications/phurl/storage/PhabricatorPhurlURL.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/storage/PhabricatorPhurlURL.php
@@ -0,0 +1,171 @@
+<?php
+
+final class PhabricatorPhurlURL extends PhabricatorPhurlDAO
+ implements PhabricatorPolicyInterface,
+ PhabricatorProjectInterface,
+ PhabricatorApplicationTransactionInterface,
+ PhabricatorSubscribableInterface,
+ PhabricatorTokenReceiverInterface,
+ PhabricatorDestructibleInterface,
+ PhabricatorMentionableInterface,
+ PhabricatorFlaggableInterface,
+ PhabricatorSpacesInterface {
+
+ protected $name;
+ protected $alias;
+ protected $longURL;
+ protected $description;
+
+ protected $viewPolicy;
+ protected $editPolicy;
+
+ protected $authorPHID;
+ protected $spacePHID;
+
+ const DEFAULT_ICON = 'fa-compress';
+
+ public static function initializeNewPhurlURL(PhabricatorUser $actor) {
+ $app = id(new PhabricatorApplicationQuery())
+ ->setViewer($actor)
+ ->withClasses(array('PhabricatorPhurlApplication'))
+ ->executeOne();
+
+ return id(new PhabricatorPhurlURL())
+ ->setAuthorPHID($actor->getPHID())
+ ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
+ ->setEditPolicy($actor->getPHID())
+ ->setSpacePHID($actor->getDefaultSpacePHID());
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text',
+ 'alias' => 'sort64?',
+ 'longURL' => 'text',
+ 'description' => 'text',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_instance' => array(
+ 'columns' => array('alias'),
+ 'unique' => true,
+ ),
+ 'key_author' => array(
+ 'columns' => array('authorPHID'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ PhabricatorPhurlURLPHIDType::TYPECONST);
+ }
+
+ public function getMonogram() {
+ return 'U'.$this->getID();
+ }
+
+ public function getURI() {
+ $uri = '/'.$this->getMonogram();
+ return $uri;
+ }
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ );
+ }
+
+ public function getPolicy($capability) {
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ return $this->getViewPolicy();
+ case PhabricatorPolicyCapability::CAN_EDIT:
+ return $this->getEditPolicy();
+ }
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ $user_phid = $this->getAuthorPHID();
+ if ($user_phid) {
+ $viewer_phid = $viewer->getPHID();
+ if ($viewer_phid == $user_phid) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return pht('The owner of a URL can always view and edit it.');
+ }
+
+/* -( PhabricatorApplicationTransactionInterface )------------------------- */
+
+
+ public function getApplicationTransactionEditor() {
+ return new PhabricatorPhurlURLEditor();
+ }
+
+ public function getApplicationTransactionObject() {
+ return $this;
+ }
+
+ public function getApplicationTransactionTemplate() {
+ return new PhabricatorPhurlURLTransaction();
+ }
+
+ public function willRenderTimeline(
+ PhabricatorApplicationTransactionView $timeline,
+ AphrontRequest $request) {
+
+ return $timeline;
+ }
+
+/* -( PhabricatorSubscribableInterface )----------------------------------- */
+
+
+ public function isAutomaticallySubscribed($phid) {
+ return ($phid == $this->getAuthorPHID());
+ }
+
+ public function shouldShowSubscribersProperty() {
+ return true;
+ }
+
+ public function shouldAllowSubscription($phid) {
+ return true;
+ }
+
+/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
+
+
+ public function getUsersToNotifyOfTokenGiven() {
+ return array($this->getAuthorPHID());
+ }
+
+/* -( PhabricatorDestructibleInterface )----------------------------------- */
+
+
+ public function destroyObjectPermanently(
+ PhabricatorDestructionEngine $engine) {
+
+ $this->openTransaction();
+ $this->delete();
+ $this->saveTransaction();
+ }
+
+/* -( PhabricatorSpacesInterface )----------------------------------------- */
+
+
+ public function getSpacePHID() {
+ return $this->spacePHID;
+ }
+}
diff --git a/src/applications/phurl/storage/PhabricatorPhurlURLTransaction.php b/src/applications/phurl/storage/PhabricatorPhurlURLTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/storage/PhabricatorPhurlURLTransaction.php
@@ -0,0 +1,191 @@
+<?php
+
+final class PhabricatorPhurlURLTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'phurl.name';
+ const TYPE_URL = 'phurl.longurl';
+ const TYPE_DESCRIPTION = 'phurl.description';
+
+ public function getApplicationName() {
+ return 'phurl';
+ }
+
+ public function getApplicationTransactionType() {
+ return PhabricatorPhurlURLPHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return new PhabricatorPhurlURLTransactionComment();
+ }
+
+ public function getRequiredHandlePHIDs() {
+ $phids = parent::getRequiredHandlePHIDs();
+
+ switch ($this->getTransactionType()) {
+ case self::TYPE_NAME:
+ case self::TYPE_URL:
+ case self::TYPE_DESCRIPTION:
+ $phids[] = $this->getObjectPHID();
+ break;
+ }
+
+ return $phids;
+ }
+
+ public function shouldHide() {
+ $old = $this->getOldValue();
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($old === null);
+ }
+ return parent::shouldHide();
+ }
+
+ public function getIcon() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_NAME:
+ case self::TYPE_URL:
+ case self::TYPE_DESCRIPTION:
+ return 'fa-pencil';
+ break;
+ }
+ return parent::getIcon();
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $type = $this->getTransactionType();
+ switch ($type) {
+ case self::TYPE_NAME:
+ if ($old === null) {
+ return pht(
+ '%s created this URL.',
+ $this->renderHandleLink($author_phid));
+ } else {
+ return pht(
+ '%s changed the name of the URL from %s to %s.',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
+ }
+ case self::TYPE_URL:
+ return pht(
+ '%s changed the destination of the URL from %s to %s.',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
+ case self::TYPE_DESCRIPTION:
+ return pht(
+ "%s updated the URL's description.",
+ $this->renderHandleLink($author_phid));
+ }
+ return parent::getTitle();
+ }
+
+ public function getTitleForFeed() {
+ $author_phid = $this->getAuthorPHID();
+ $object_phid = $this->getObjectPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $viewer = $this->getViewer();
+
+ $type = $this->getTransactionType();
+ switch ($type) {
+ case self::TYPE_NAME:
+ if ($old === null) {
+ return pht(
+ '%s created %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ } else {
+ return pht(
+ '%s changed the name of %s from %s to %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid),
+ $old,
+ $new);
+ }
+ case self::TYPE_URL:
+ if ($old === null) {
+ return pht(
+ '%s created %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ } else {
+ return pht(
+ '%s changed the destination of %s from %s to %s',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid),
+ $old,
+ $new);
+ }
+ case self::TYPE_DESCRIPTION:
+ return pht(
+ '%s updated the description of %s.',
+ $this->renderHandleLink($author_phid),
+ $this->renderHandleLink($object_phid));
+ }
+
+ return parent::getTitleForFeed();
+ }
+
+ public function getColor() {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ switch ($this->getTransactionType()) {
+ case self::TYPE_NAME:
+ case self::TYPE_URL:
+ case self::TYPE_DESCRIPTION:
+ return PhabricatorTransactions::COLOR_GREEN;
+ }
+
+ return parent::getColor();
+ }
+
+
+ public function hasChangeDetails() {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ return ($this->getOldValue() !== null);
+ }
+
+ return parent::hasChangeDetails();
+ }
+
+ public function renderChangeDetails(PhabricatorUser $viewer) {
+ switch ($this->getTransactionType()) {
+ case self::TYPE_DESCRIPTION:
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ return $this->renderTextCorpusChangeDetails(
+ $viewer,
+ $old,
+ $new);
+ }
+
+ return parent::renderChangeDetails($viewer);
+ }
+
+ public function getMailTags() {
+ $tags = array();
+ switch ($this->getTransactionType()) {
+ case self::TYPE_NAME:
+ case self::TYPE_DESCRIPTION:
+ case self::TYPE_URL:
+ $tags[] = self::MAILTAG_CONTENT;
+ break;
+ }
+ return $tags;
+ }
+
+}
diff --git a/src/applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php b/src/applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phurl/storage/PhabricatorPhurlURLTransactionComment.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhabricatorPhurlURLTransactionComment
+ extends PhabricatorApplicationTransactionComment {
+
+ public function getApplicationTransactionObject() {
+ return new PhabricatorPhurlURLTransaction();
+ }
+
+}
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -106,6 +106,7 @@
'db.almanac' => array(),
'db.multimeter' => array(),
'db.spaces' => array(),
+ 'db.phurl' => array(),
'db.badges' => array(),
'0000.legacy.sql' => array(
'legacy' => 0,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Oct 16 2024, 7:53 PM (4 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6710700
Default Alt Text
D13681.diff (48 KB)
Attached To
Mode
D13681: DRAFT - throw together Phurl skeleton.
Attached
Detach File
Event Timeline
Log In to Comment