diff --git a/resources/sql/autopatches/20161005.cal.02.export.sql b/resources/sql/autopatches/20161005.cal.02.export.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20161005.cal.02.export.sql @@ -0,0 +1,14 @@ +CREATE TABLE {$NAMESPACE}_calendar.calendar_export ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + name LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + authorPHID VARBINARY(64) NOT NULL, + policyMode VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, + queryKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, + secretKey BINARY(20) NOT NULL, + isDisabled BOOL NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + KEY `key_author` (authorPHID), + UNIQUE KEY `key_secret` (secretKey) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20161005.cal.03.exportxaction.sql b/resources/sql/autopatches/20161005.cal.03.exportxaction.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20161005.cal.03.exportxaction.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_calendar.calendar_exporttransaction ( + 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/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2075,6 +2075,20 @@ 'PhabricatorCalendarEventTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarEventTransactionType.php', 'PhabricatorCalendarEventUntilDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventUntilDateTransaction.php', 'PhabricatorCalendarEventViewController' => 'applications/calendar/controller/PhabricatorCalendarEventViewController.php', + 'PhabricatorCalendarExport' => 'applications/calendar/storage/PhabricatorCalendarExport.php', + 'PhabricatorCalendarExportDisableTransaction' => 'applications/calendar/xaction/PhabricatorCalendarExportDisableTransaction.php', + 'PhabricatorCalendarExportEditController' => 'applications/calendar/controller/PhabricatorCalendarExportEditController.php', + 'PhabricatorCalendarExportEditEngine' => 'applications/calendar/editor/PhabricatorCalendarExportEditEngine.php', + 'PhabricatorCalendarExportEditor' => 'applications/calendar/editor/PhabricatorCalendarExportEditor.php', + 'PhabricatorCalendarExportListController' => 'applications/calendar/controller/PhabricatorCalendarExportListController.php', + 'PhabricatorCalendarExportModeTransaction' => 'applications/calendar/xaction/PhabricatorCalendarExportModeTransaction.php', + 'PhabricatorCalendarExportNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarExportNameTransaction.php', + 'PhabricatorCalendarExportPHIDType' => 'applications/calendar/phid/PhabricatorCalendarExportPHIDType.php', + 'PhabricatorCalendarExportQuery' => 'applications/calendar/query/PhabricatorCalendarExportQuery.php', + 'PhabricatorCalendarExportQueryKeyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarExportQueryKeyTransaction.php', + 'PhabricatorCalendarExportSearchEngine' => 'applications/calendar/query/PhabricatorCalendarExportSearchEngine.php', + 'PhabricatorCalendarExportTransaction' => 'applications/calendar/storage/PhabricatorCalendarExportTransaction.php', + 'PhabricatorCalendarExportTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarExportTransactionType.php', 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', 'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php', @@ -6825,6 +6839,25 @@ 'PhabricatorCalendarEventTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorCalendarEventUntilDateTransaction' => 'PhabricatorCalendarEventDateTransaction', 'PhabricatorCalendarEventViewController' => 'PhabricatorCalendarController', + 'PhabricatorCalendarExport' => array( + 'PhabricatorCalendarDAO', + 'PhabricatorPolicyInterface', + 'PhabricatorApplicationTransactionInterface', + 'PhabricatorDestructibleInterface', + ), + 'PhabricatorCalendarExportDisableTransaction' => 'PhabricatorCalendarExportTransactionType', + 'PhabricatorCalendarExportEditController' => 'PhabricatorCalendarController', + 'PhabricatorCalendarExportEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorCalendarExportEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorCalendarExportListController' => 'PhabricatorCalendarController', + 'PhabricatorCalendarExportModeTransaction' => 'PhabricatorCalendarExportTransactionType', + 'PhabricatorCalendarExportNameTransaction' => 'PhabricatorCalendarExportTransactionType', + 'PhabricatorCalendarExportPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorCalendarExportQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorCalendarExportQueryKeyTransaction' => 'PhabricatorCalendarExportTransactionType', + 'PhabricatorCalendarExportSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorCalendarExportTransaction' => 'PhabricatorModularTransaction', + 'PhabricatorCalendarExportTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', 'PhabricatorCalendarIconSet' => 'PhabricatorIconSet', diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -62,6 +62,16 @@ 'export/(?P[1-9]\d*)/(?P[^/]*)' => 'PhabricatorCalendarEventExportController', ), + 'export/' => array( + $this->getQueryRoutePattern() + => 'PhabricatorCalendarExportListController', + $this->getEditRoutePattern('edit/') + => 'PhabricatorCalendarExportEditController', + '(?P[1-9]\d*)/' + => 'PhabricatorCalendarExportViewController', + 'ics/(?P[^/]+)/(?P[^/]*)' + => 'PhabricatorCalendarExportICSController', + ), ), ); } diff --git a/src/applications/calendar/controller/PhabricatorCalendarExportEditController.php b/src/applications/calendar/controller/PhabricatorCalendarExportEditController.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/controller/PhabricatorCalendarExportEditController.php @@ -0,0 +1,12 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/calendar/controller/PhabricatorCalendarExportListController.php b/src/applications/calendar/controller/PhabricatorCalendarExportListController.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/controller/PhabricatorCalendarExportListController.php @@ -0,0 +1,27 @@ +setController($this) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $doc_name = 'Calendar User Guide: Exporting Events'; + $doc_href = PhabricatorEnv::getDoclink($doc_name); + + $crumbs->addAction( + id(new PHUIListItemView()) + ->setName(pht('Guide: Exporting Events')) + ->setIcon('fa-book') + ->setHref($doc_href)); + + return $crumbs; + } + +} diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditEngine.php @@ -0,0 +1,96 @@ +getViewer()); + } + + protected function newObjectQuery() { + return new PhabricatorCalendarExportQuery(); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create New Export'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Export: %s', $object->getName()); + } + + protected function getObjectEditShortText($object) { + return $object->getMonogram(); + } + + protected function getObjectCreateShortText() { + return pht('Create Export'); + } + + protected function getObjectName() { + return pht('Export'); + } + + protected function getObjectViewURI($object) { + return $object->getURI(); + } + + protected function getEditorURI() { + return $this->getApplication()->getApplicationURI('export/edit/'); + } + + protected function buildCustomEditFields($object) { + $viewer = $this->getViewer(); + + $fields = array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setDescription(pht('Name of the export.')) + ->setIsRequired(true) + ->setTransactionType( + PhabricatorCalendarExportNameTransaction::TRANSACTIONTYPE) + ->setConduitDescription(pht('Rename the export.')) + ->setConduitTypeDescription(pht('New export name.')) + ->setValue($object->getName()), + id(new PhabricatorBoolEditField()) + ->setKey('disabled') + ->setOptions(pht('Active'), pht('Disabled')) + ->setLabel(pht('Disabled')) + ->setDescription(pht('Disable the export.')) + ->setTransactionType( + PhabricatorCalendarExportDisableTransaction::TRANSACTIONTYPE) + ->setIsConduitOnly(true) + ->setConduitDescription(pht('Disable or restore the export.')) + ->setConduitTypeDescription(pht('True to cancel the export.')) + ->setValue($object->getIsDisabled()), + ); + + return $fields; + } + + +} diff --git a/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php b/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/editor/PhabricatorCalendarExportEditor.php @@ -0,0 +1,14 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $export = $objects[$phid]; + + $id = $export->getID(); + $name = $export->getName(); + $uri = $export->getURI(); + + $handle + ->setName($name) + ->setFullName(pht('Calendar Export %s: %s', $id, $name)) + ->setURI($uri); + + if ($export->getIsDisabled()) { + $handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED); + } + } + } + +} diff --git a/src/applications/calendar/query/PhabricatorCalendarExportQuery.php b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/query/PhabricatorCalendarExportQuery.php @@ -0,0 +1,81 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withAuthorPHIDs(array $phids) { + $this->authorPHIDs = $phids; + return $this; + } + + public function withIsDisabled($is_disabled) { + $this->isDisabled = $is_disabled; + return $this; + } + + public function newResultObject() { + return new PhabricatorCalendarExport(); + } + + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'export.id IN (%Ld)', + $this->ids); + } + + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'export.phid IN (%Ls)', + $this->phids); + } + + if ($this->authorPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'export.authorPHID IN (%Ls)', + $this->authorPHIDs); + } + + if ($this->isDisabled !== null) { + $where[] = qsprintf( + $conn, + 'export.isDisabled = %d', + (int)$this->isDisabled); + } + + return $where; + } + + protected function getPrimaryTableAlias() { + return 'export'; + } + + public function getQueryApplicationClass() { + return 'PhabricatorCalendarApplication'; + } + +} diff --git a/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/query/PhabricatorCalendarExportSearchEngine.php @@ -0,0 +1,109 @@ +requireViewer(); + + return id(new PhabricatorCalendarExportQuery()) + ->withAuthorPHIDs(array($viewer->getPHID())); + } + + protected function buildCustomSearchFields() { + return array(); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + + return $query; + } + + protected function getURI($path) { + return '/calendar/export/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Exports'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $exports, + PhabricatorSavedQuery $query, + array $handles) { + + assert_instances_of($exports, 'PhabricatorCalendarExport'); + $viewer = $this->requireViewer(); + + $list = new PHUIObjectItemListView(); + foreach ($exports as $export) { + $item = id(new PHUIObjectItemView()) + ->setViewer($viewer) + ->setHeader($export->getName()) + ->setHref($export->getURI()); + + if ($export->getIsDisabled()) { + $item->setDisabled(true); + } + + $list->addItem($item); + } + + $result = new PhabricatorApplicationSearchResultView(); + $result->setObjectList($list); + $result->setNoDataString(pht('No exports found.')); + + return $result; + } + + protected function getNewUserBody() { + $doc_name = 'Calendar User Guide: Exporting Events'; + $doc_href = PhabricatorEnv::getDoclink($doc_name); + + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon('fa-book white') + ->setText($doc_name) + ->setHref($doc_href) + ->setColor(PHUIButtonView::GREEN); + + $icon = $this->getApplication()->getIcon(); + $app_name = $this->getApplication()->getName(); + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-download') + ->setTitle(pht('No Exports Configured')) + ->setDescription( + pht( + 'You have not set up any events for export from Calendar yet. '. + 'See the documentation for instructions on how to get started.')) + ->addAction($create_button); + + return $view; + } + +} diff --git a/src/applications/calendar/storage/PhabricatorCalendarExport.php b/src/applications/calendar/storage/PhabricatorCalendarExport.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/storage/PhabricatorCalendarExport.php @@ -0,0 +1,144 @@ +setAuthorPHID($actor->getPHID()) + ->setPolicyMode(self::MODE_PRIVATE) + ->setIsDisabled(0); + } + + protected function getConfiguration() { + return array( + self::CONFIG_AUX_PHID => true, + self::CONFIG_COLUMN_SCHEMA => array( + 'name' => 'text', + 'policyMode' => 'text64', + 'queryKey' => 'text64', + 'secretKey' => 'bytes20', + 'isDisabled' => 'bool', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_author' => array( + 'columns' => array('authorPHID'), + ), + 'key_secret' => array( + 'columns' => array('secretKey'), + 'unique' => true, + ), + ), + ) + parent::getConfiguration(); + } + + public function getPHIDType() { + return PhabricatorCalendarExportPHIDType::TYPECONST; + } + + public function save() { + if (!$this->getSecretKey()) { + $this->setSecretKey(Filesystem::readRandomCharacters(20)); + } + + return parent::save(); + } + + public function getURI() { + $id = $this->getID(); + return "/calendar/export/{$id}/"; + } + + private static function getPolicyModeMap() { + return array( + self::MODE_PUBLIC => array( + 'name' => pht('Public'), + ), + self::MODE_PRIVATE => array( + 'name' => pht('Private'), + ), + ); + } + + private static function getPolicyModeSpec($const) { + return idx(self::getPolicyModeMap(), $const, array()); + } + + public static function getPolicyModeName($const) { + $map = self::getPolicyModeSpec($const); + return idx($map, 'name', $const); + } + + public static function getPolicyModes() { + return array_keys(self::getPolicyModeMap()); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + return $this->getAuthorPHID(); + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhabricatorCalendarExportEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new PhabricatorCalendarExportTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + + return $timeline; + } + + +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + $this->delete(); + } + +} diff --git a/src/applications/calendar/storage/PhabricatorCalendarExportTransaction.php b/src/applications/calendar/storage/PhabricatorCalendarExportTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/storage/PhabricatorCalendarExportTransaction.php @@ -0,0 +1,18 @@ +getIsDisabled(); + } + + public function applyInternalEffects($object, $value) { + $object->setIsDisabled((int)$value); + } + + public function getTitle() { + if ($this->getNewValue()) { + return pht( + '%s disabled this export.', + $this->renderAuthor()); + } else { + return pht( + '%s enabled this export.', + $this->renderAuthor()); + } + } + +} diff --git a/src/applications/calendar/xaction/PhabricatorCalendarExportModeTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarExportModeTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/xaction/PhabricatorCalendarExportModeTransaction.php @@ -0,0 +1,54 @@ +getPolicyMode(); + } + + public function applyInternalEffects($object, $value) { + $object->setPolicyMode($value); + } + + public function getTitle() { + $old_value = $this->getOldValue(); + $new_value = $this->getNewValue(); + + $old_name = PhabricatorCalendarExport::getPolicyModeName($old_value); + $new_name = PhabricatorCalendarExport::getPolicyModeName($new_value); + + return pht( + '%s changed the policy mode for this export from %s to %s.', + $this->renderAuthor(), + $this->renderValue($old_name), + $this->renderValue($new_name)); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $valid = PhabricatorCalendarExport::getPolicyModes(); + $valid = array_fuse($valid); + + foreach ($xactions as $xaction) { + $value = $xaction->getNewValue(); + + if (isset($valid[$value])) { + continue; + } + + $errors[] = $this->newInvalidError( + pht( + 'Mode "%s" is not a valid policy mode. Valid modes are: %s.', + $value, + implode(', ', $valid)), + $xaction); + } + + return $errors; + } + +} diff --git a/src/applications/calendar/xaction/PhabricatorCalendarExportNameTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarExportNameTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/xaction/PhabricatorCalendarExportNameTransaction.php @@ -0,0 +1,35 @@ +getName(); + } + + public function applyInternalEffects($object, $value) { + $object->setName($value); + } + + public function getTitle() { + return pht( + '%s renamed this export from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + if ($this->isEmptyTextTransaction($object->getName(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Calendar exports must have a name.')); + } + + return $errors; + } + +} diff --git a/src/applications/calendar/xaction/PhabricatorCalendarExportQueryKeyTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarExportQueryKeyTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/xaction/PhabricatorCalendarExportQueryKeyTransaction.php @@ -0,0 +1,51 @@ +getQueryKey(); + } + + public function applyInternalEffects($object, $value) { + $object->setQueryKey($value); + } + + public function getTitle() { + return pht( + '%s changed the query for this export.', + $this->renderAuthor()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + foreach ($xactions as $xaction) { + $value = $xaction->getNewValue(); + + $query = id(new PhabricatorSavedQueryQuery()) + ->withEngineClassNames(array('PhabricatorCalendarEventSearchEngine')) + ->withQueryKeys(array($value)) + ->executeOne(); + if ($query) { + continue; + } + + $errors[] = $this->newInvalidError( + pht( + 'Query key "%s" does not identify a valid event query.', + $value), + $xaction); + } + + if ($this->isEmptyTextTransaction($object->getQueryKey(), $xactions)) { + $errors[] = $this->newRequiredError( + pht('Calendar exports must have a query key.')); + } + + return $errors; + } + +} diff --git a/src/applications/calendar/xaction/PhabricatorCalendarExportTransactionType.php b/src/applications/calendar/xaction/PhabricatorCalendarExportTransactionType.php new file mode 100644 --- /dev/null +++ b/src/applications/calendar/xaction/PhabricatorCalendarExportTransactionType.php @@ -0,0 +1,4 @@ +addAction($create_button); - return $view; + return $view; } } diff --git a/src/docs/user/userguide/calendar_exports.diviner b/src/docs/user/userguide/calendar_exports.diviner new file mode 100644 --- /dev/null +++ b/src/docs/user/userguide/calendar_exports.diviner @@ -0,0 +1,12 @@ +@title Calendar User Guide: Exporting Events +@group userguide + +Exporting events to other calendars. + +Overview +======== + +IMPORTANT: Calendar is a prototype application. See +@{article:User Guide: Prototype Applications}. + +Coming soon! diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -1339,11 +1339,12 @@ * @task hook */ public function generatePHID() { - throw new Exception( - pht( - 'To use %s, you need to overload %s to perform PHID generation.', - 'CONFIG_AUX_PHID', - 'generatePHID()')); + $type = $this->getPHIDType(); + return PhabricatorPHID::generateNewPHID($type); + } + + public function getPHIDType() { + throw new PhutilMethodNotImplementedException(); }