Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13955407
D13681.id33073.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
52 KB
Referenced Files
None
Subscribers
None
D13681.id33073.diff
View Options
diff --git a/resources/sql/autopatches/20150721.phurl.edge.1.sql b/resources/sql/autopatches/20150721.phurl.edge.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.edge.1.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.url.1.sql b/resources/sql/autopatches/20150721.phurl.url.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.url.1.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.url.2.sql b/resources/sql/autopatches/20150721.phurl.url.2.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.url.2.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.xaction.1.sql b/resources/sql/autopatches/20150721.phurl.xaction.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.xaction.1.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.xactioncomment.1.sql b/resources/sql/autopatches/20150721.phurl.xactioncomment.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150721.phurl.xactioncomment.1.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/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2442,6 +2442,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',
@@ -6280,6 +6295,33 @@
'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPhrictionApplication' => 'PhabricatorApplication',
'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions',
+ 'PhabricatorPhurlApplication' => 'PhabricatorApplication',
+ 'PhabricatorPhurlController' => 'PhabricatorController',
+ 'PhabricatorPhurlDAO' => 'PhabricatorLiskDAO',
+ 'PhabricatorPhurlSchemaSpec' => 'PhabricatorConfigSchemaSpec',
+ 'PhabricatorPhurlURL' => array(
+ 'PhabricatorPhurlDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorProjectInterface',
+ 'PhabricatorMarkupInterface',
+ '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,67 @@
+<?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 getTitleGlyph() {
+ // // Unicode has a calendar character but it's in some distant code plane,
+ // // use "keyboard" since it looks vaguely similar.
+ // return "\xE2\x8C\xA8";
+ // }
+
+ 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(
+ 'view/'
+ => 'PhabricatorPhurlURLViewController',
+ 'create/'
+ => 'PhabricatorPhurlURLEditController',
+ 'edit/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
+ => 'PhabricatorPhurlURLEditController',
+ ),
+ ),
+ );
+ }
+
+ // public function getMailCommandObjects() {
+ // return array(
+ // 'event' => array(
+ // 'name' => pht('Email Commands: Events'),
+ // 'header' => pht('Interacting with Calendar Events'),
+ // 'object' => new PhabricatorCalendarEvent(),
+ // 'summary' => pht(
+ // 'This page documents the commands you can use to interact with '.
+ // 'events in Calendar. These commands work when creating new tasks '.
+ // 'via email and when replying to existing tasks.'),
+ // ),
+ // );
+ // }
+
+}
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,24 @@
+<?php
+
+abstract class PhabricatorPhurlController extends PhabricatorController {
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ // $actions = id(new PhabricatorActionListView())
+ // ->setUser($this->getViewer())
+ // ->addAction(
+ // id(new PhabricatorActionView())
+ // ->setName(pht('Shorten URL'))
+ // ->setHref('/phurl/url/create/'));
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Shorten URL'))
+ ->setHref($this->getApplicationURI().'url/create/')
+ ->setIcon('fa-plus-square'));
+ // ->setDropdownMenu($actions));
+
+ 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,247 @@
+<?php
+
+final class PhabricatorPhurlURLEditController
+ extends PhabricatorPhurlController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function isCreate() {
+ return !$this->id;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $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 ($this->isCreate()) {
+ $url = PhabricatorPhurlURL::initializeNewPhurlURL(
+ $viewer);
+ $submit_label = pht('Create');
+ $page_title = pht('Shorten URL');
+ $redirect = 'created';
+ $subscribers = array();
+ $cancel_uri = $this->getApplicationURI();
+ } else {
+ $url = id(new PhabricatorPhurlURLQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->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().'/view';
+ }
+
+ if ($this->isCreate()) {
+ $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($request->getStr('viewPolicy'));
+
+ $xactions[] = id(new PhabricatorPhurlURLTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
+ ->setNewValue($request->getStr('editPolicy'));
+
+ $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());
+ } 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())
+ ->addHiddenInput('next', $next_workflow)
+ ->addHiddenInput('query', $uri_query)
+ ->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 (!$this->isCreate()) {
+ $crumbs->addTextCrumb('U'.$url->getId(), '/U'.$url->getId());
+ }
+
+ $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,147 @@
+<?php
+
+final class PhabricatorPhurlURLViewController
+ extends PhabricatorPhurlController {
+
+ private $id;
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function willProcessRequest(array $data) {
+ $this->id = $data['id'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $timeline = null;
+
+ $url = id(new PhabricatorPhurlURLQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->executeOne();
+ if (!$url) {
+ return new Aphront404Response();
+ }
+
+ $title = 'U'.$url->getID();
+ $page_title = $title.' '.$url->getName();
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($title, '/U'.$url->getID());
+
+ $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->getRequest()->getUser();
+ $id = $url->getID();
+
+ $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->getRequest()->getUser();
+ $id = $url->getID();
+
+ $actions = id(new PhabricatorActionListView())
+ ->setObjectURI($this->getApplicationURI('phurl/'.$id.'/'))
+ ->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("phurl/edit/{$id}/"))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit));
+
+ return $actions;
+ }
+
+ private function buildPropertyView(PhabricatorPhurlURL $url) {
+ $viewer = $this->getRequest()->getUser();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer)
+ ->setObject($url);
+
+ // $properties->invokeWillRenderEvent();
+
+ $properties->addProperty(
+ pht('Original URL'),
+ $url->getLongURL());
+
+ 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,234 @@
+<?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:
+ var_dump($xaction->getNewValue());
+ $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 didApplyInternalEffects(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+
+ return $xactions;
+ }
+
+ protected function applyFinalEffects(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return $xactions;
+ }
+
+
+ protected function validateAllTransactions(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ $errors = array();
+ return $errors;
+ }
+
+ 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 buildReplyHandler(PhabricatorLiskDAO $object) {
+ // return id(new PhabricatorCalendarReplyHandler())
+ // ->setMailReceiver($object);
+ // }
+
+ 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,73 @@
+<?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();
+
+ $handle
+ ->setName($name)
+ ->setFullName(pht('U%d: %s', $id, $name))
+ ->setURI('/U'.$id.'/view');
+ }
+ }
+
+ 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,125 @@
+<?php
+
+final class PhabricatorPhurlURLQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $names;
+ private $longURLs;
+
+ 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 getOrderableColumns() {
+ return array(
+ 'start' => array(
+ 'table' => $this->getPrimaryTableAlias(),
+ 'column' => 'id',
+ 'reverse' => true,
+ 'type' => 'int',
+ 'unique' => false,
+ ),
+ ) + parent::getOrderableColumns();
+ }
+
+ protected function getPagingValueMap($cursor, array $keys) {
+ $url = $this->loadCursorObject($cursor);
+ return array(
+ 'id' => $url->getID(),
+ );
+ }
+
+ protected function loadPage() {
+ $table = new PhabricatorPhurlURL();
+ $conn_r = $table->establishConnection('r');
+ $viewer = $this->getViewer();
+
+ $data = queryfx_all(
+ $conn_r,
+ 'SELECT url.* FROM %T url %Q %Q %Q %Q %Q',
+ $table->getTableName(),
+ $this->buildJoinClause($conn_r),
+ $this->buildWhereClause($conn_r),
+ $this->buildGroupClause($conn_r),
+ $this->buildOrderClause($conn_r),
+ $this->buildLimitClause($conn_r));
+
+ return $table->loadAllFromArray($data);
+ }
+
+ protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
+ $where = array();
+
+ if ($this->ids) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'url.id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'url.phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->names) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'url.name IN (%Ls)',
+ $this->names);
+ }
+
+ if ($this->longURLs) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'url.longURL IN (%Ls)',
+ $this->longURLs);
+ }
+
+ $where[] = $this->buildPagingClause($conn_r);
+
+ return $this->formatWhereClause($where);
+ }
+
+ protected function getPrimaryTableAlias() {
+ return 'url';
+ }
+
+ protected function getApplicationSearchObjectPHIDColumn() {
+ return 'url.phid';
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorPhurlApplication';
+ }
+
+
+ protected function willFilterPage(array $urls) {
+ return $urls;
+ }
+
+}
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,110 @@
+<?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 false;
+ }
+
+ protected function buildCustomSearchFields() {
+ return array(
+ id(new PhabricatorSearchDatasourceField())
+ ->setLabel(pht('Created By'))
+ ->setKey('creatorPHIDs')
+ ->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
+ );
+ }
+
+ protected function buildQueryFromParameters(array $map) {
+ $query = $this->newQuery();
+ $viewer = $this->requireViewer();
+
+ if ($map['creatorPHIDs']) {
+ $query->withPHIDs($map['creatorPHIDs']);
+ }
+
+ 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('creatorPHIDs', array($viewer->getPHID()));
+ case 'all':
+ return $query;
+ }
+
+ return parent::buildSavedQueryFromBuiltin($query_key);
+ }
+
+ protected function getRequiredHandlePHIDsForResultList(
+ array $objects,
+ PhabricatorSavedQuery $query) {
+ $phids = array();
+ foreach ($objects as $url) {
+ $phids[$url->getUserPHID()] = 1;
+ }
+ return array_keys($phids);
+ }
+
+ protected function renderResultList(
+ array $urls,
+ PhabricatorSavedQuery $query,
+ array $handles) {
+
+ assert_instances_of($urls, 'PhabricatorPhurlURL');
+ $viewer = $this->requireViewer();
+ $list = new PHUIObjectItemListView();
+
+ foreach ($urls as $url) {
+ $creator_handle = $handles[$url->getUserPHID()];
+
+ $item = id(new PHUIObjectItemView())
+ ->setUser($viewer)
+ ->setObject($url)
+ ->setHeader($viewer->renderHandle($url->getPHID())->render());
+
+ $list->addItem($item);
+ }
+
+ $result = new PhabricatorApplicationSearchResultView();
+ $result->setObjectList($list);
+ $result->setNoDataString(pht('No URLs found.'));
+
+ return $result;
+ }
+
+ public function getPageSize(PhabricatorSavedQuery $saved) {
+ return $saved->getParameter('limit', 100);
+ }
+}
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,230 @@
+<?php
+
+final class PhabricatorPhurlURL extends PhabricatorPhurlDAO
+ implements PhabricatorPolicyInterface,
+ PhabricatorProjectInterface,
+ PhabricatorMarkupInterface,
+ PhabricatorApplicationTransactionInterface,
+ PhabricatorSubscribableInterface,
+ PhabricatorTokenReceiverInterface,
+ PhabricatorDestructibleInterface,
+ PhabricatorMentionableInterface,
+ PhabricatorFlaggableInterface,
+ PhabricatorSpacesInterface {
+
+ protected $name;
+ protected $longURL;
+ protected $description;
+
+ protected $viewPolicy;
+ protected $editPolicy;
+
+ 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())
+ ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
+ ->setEditPolicy($actor->getPHID())
+ ->setSpacePHID($actor->getDefaultSpacePHID());
+ }
+
+ public function save() {
+ // if (!$this->mailKey) {
+ // $this->mailKey = Filesystem::readRandomCharacters(20);
+ // }
+
+ return parent::save();
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text',
+ 'longURL' => 'text',
+ 'description' => 'text',
+ 'icon' => 'text32',
+ 'mailKey' => 'bytes20',
+ ),
+ // self::CONFIG_KEY_SCHEMA => array(
+ // 'userPHID_dateFrom' => array(
+ // 'columns' => array('userPHID', 'dateTo'),
+ // ),
+ // 'key_instance' => array(
+ // 'columns' => array('instanceOfEventPHID', 'sequenceIndex'),
+ // 'unique' => true,
+ // ),
+ // ),
+ // self::CONFIG_SERIALIZATION => array(
+ // 'recurrenceFrequency' => self::SERIALIZATION_JSON,
+ // ),
+ ) + 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;
+ }
+
+/* -( Markup Interface )--------------------------------------------------- */
+
+
+ /**
+ * @task markup
+ */
+ public function getMarkupFieldKey($field) {
+ $hash = PhabricatorHash::digest($this->getMarkupText($field));
+ $id = $this->getID();
+ return "phurl:U{$id}:{$field}:{$hash}";
+ }
+
+
+ /**
+ * @task markup
+ */
+ public function getMarkupText($field) {
+ return $this->getDescription();
+ }
+
+
+ /**
+ * @task markup
+ */
+ public function newMarkupEngine($field) {
+ return PhabricatorMarkupEngine::newCalendarMarkupEngine();
+ }
+
+
+ /**
+ * @task markup
+ */
+ public function didMarkupText(
+ $field,
+ $output,
+ PhutilMarkupEngine $engine) {
+ return $output;
+ }
+
+
+ /**
+ * @task markup
+ */
+ public function shouldUseMarkupCache($field) {
+ return (bool)$this->getID();
+ }
+
+/* -( 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->getPHID();
+ 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 an event can always view and edit it,
+ and invitees can always view it, except if the event is an
+ instance of a recurring event.');
+ }
+
+/* -( 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->getPHID());
+ }
+
+ public function shouldShowSubscribersProperty() {
+ return true;
+ }
+
+ public function shouldAllowSubscription($phid) {
+ return true;
+ }
+
+/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
+
+
+ public function getUsersToNotifyOfTokenGiven() {
+ return array($this->getUserPHID());
+ }
+
+/* -( 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,201 @@
+<?php
+
+final class PhabricatorPhurlURLTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'phurl.name';
+ const TYPE_URL = 'phurl.longurl';
+ const TYPE_DESCRIPTION = 'phurl.description';
+
+ // const MAILTAG_RESCHEDULE = 'calendar-reschedule';
+ // const MAILTAG_CONTENT = 'calendar-content';
+ // const MAILTAG_OTHER = 'calendar-other';
+
+ 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:
+ if ($old === null) {
+ return pht(
+ '%s created this URL',
+ $this->renderHandleLink($author_phid));
+ } else {
+ 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(),
'0000.legacy.sql' => array(
'legacy' => 0,
),
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Oct 15 2024, 1:18 AM (4 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6710768
Default Alt Text
D13681.id33073.diff (52 KB)
Attached To
Mode
D13681: DRAFT - throw together Phurl skeleton.
Attached
Detach File
Event Timeline
Log In to Comment