1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 19:40:55 +01:00

Prepare for event imports in Calendar

Summary:
Ref T10747. Adds a bunch of stuff so we can keep track of which events we've imported from external sources.

This doesn't do anything yet: you can't actually import anything.

Test Plan:
  - Ran `bin/storage upgrade`.
  - Clicked "Imports", saw an empty wasteland.
  - Created/edited events.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10747

Differential Revision: https://secure.phabricator.com/D16696
This commit is contained in:
epriestley 2016-10-11 11:45:57 -07:00
parent ea6db2ae9b
commit 2ab07ed29b
17 changed files with 596 additions and 4 deletions

View file

@ -0,0 +1,15 @@
CREATE TABLE {$NAMESPACE}_calendar.calendar_import (
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,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
engineType VARCHAR(64) NOT NULL,
parameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
isDisabled BOOL NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
KEY `key_author` (authorPHID)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_calendar.calendar_importtransaction (
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};

View file

@ -0,0 +1,11 @@
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
ADD importAuthorPHID VARBINARY(64);
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
ADD importSourcePHID VARBINARY(64);
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
ADD importUIDIndex BINARY(12);
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
ADD importUID LONGTEXT COLLATE {$COLLATE_TEXT};

View file

@ -2100,6 +2100,14 @@ phutil_register_library_map(array(
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
'PhabricatorCalendarICSWriter' => 'applications/calendar/util/PhabricatorCalendarICSWriter.php',
'PhabricatorCalendarIconSet' => 'applications/calendar/icon/PhabricatorCalendarIconSet.php',
'PhabricatorCalendarImport' => 'applications/calendar/storage/PhabricatorCalendarImport.php',
'PhabricatorCalendarImportEditor' => 'applications/calendar/editor/PhabricatorCalendarImportEditor.php',
'PhabricatorCalendarImportListController' => 'applications/calendar/controller/PhabricatorCalendarImportListController.php',
'PhabricatorCalendarImportPHIDType' => 'applications/calendar/phid/PhabricatorCalendarImportPHIDType.php',
'PhabricatorCalendarImportQuery' => 'applications/calendar/query/PhabricatorCalendarImportQuery.php',
'PhabricatorCalendarImportSearchEngine' => 'applications/calendar/query/PhabricatorCalendarImportSearchEngine.php',
'PhabricatorCalendarImportTransaction' => 'applications/calendar/storage/PhabricatorCalendarImportTransaction.php',
'PhabricatorCalendarImportTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarImportTransactionQuery.php',
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
@ -6789,6 +6797,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarEvent' => array(
'PhabricatorCalendarDAO',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
'PhabricatorProjectInterface',
'PhabricatorMarkupInterface',
'PhabricatorApplicationTransactionInterface',
@ -6879,6 +6888,19 @@ phutil_register_library_map(array(
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
'PhabricatorCalendarICSWriter' => 'Phobject',
'PhabricatorCalendarIconSet' => 'PhabricatorIconSet',
'PhabricatorCalendarImport' => array(
'PhabricatorCalendarDAO',
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorDestructibleInterface',
),
'PhabricatorCalendarImportEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorCalendarImportListController' => 'PhabricatorCalendarController',
'PhabricatorCalendarImportPHIDType' => 'PhabricatorPHIDType',
'PhabricatorCalendarImportQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCalendarImportSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorCalendarImportTransaction' => 'PhabricatorModularTransaction',
'PhabricatorCalendarImportTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',

View file

@ -73,7 +73,16 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
=> 'PhabricatorCalendarExportICSController',
'disable/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarExportDisableController',
),
'import/' => array(
$this->getQueryRoutePattern()
=> 'PhabricatorCalendarImportListController',
$this->getEditRoutePattern('edit/')
=> 'PhabricatorCalendarImportEditController',
'(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarImportViewController',
'disable/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarImportDisableController',
),
),
);

View file

@ -43,6 +43,10 @@ final class PhabricatorCalendarEventListController
->setType(PHUIListItemView::TYPE_LABEL)
->setName(pht('Import/Export'));
$items[] = id(new PHUIListItemView())
->setName('Imports')
->setHref('/calendar/import/');
$items[] = id(new PHUIListItemView())
->setName('Exports')
->setHref('/calendar/export/');

View file

@ -0,0 +1,12 @@
<?php
final class PhabricatorCalendarImportListController
extends PhabricatorCalendarController {
public function handleRequest(AphrontRequest $request) {
return id(new PhabricatorCalendarImportSearchEngine())
->setController($this)
->buildResponse();
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorCalendarImportEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorCalendarApplication';
}
public function getEditorObjectsDescription() {
return pht('Calendar Imports');
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this import.', $author);
}
}

View file

@ -0,0 +1,50 @@
<?php
final class PhabricatorCalendarImportPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'CIMP';
public function getTypeName() {
return pht('Calendar Import');
}
public function newObject() {
return new PhabricatorCalendarImport();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorCalendarApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorCalendarImportQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
foreach ($handles as $phid => $handle) {
$import = $objects[$phid];
$id = $import->getID();
$name = $import->getName();
$uri = $import->getURI();
$handle
->setName($name)
->setFullName(pht('Calendar Import %s: %s', $id, $name))
->setURI($uri);
if ($import->getIsDisabled()) {
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
}
}
}
}

View file

@ -14,6 +14,7 @@ final class PhabricatorCalendarEventQuery
private $instanceSequencePairs;
private $isStub;
private $parentEventPHIDs;
private $importSourcePHIDs;
private $generateGhosts = false;
@ -77,6 +78,11 @@ final class PhabricatorCalendarEventQuery
return $this;
}
public function withImportSourcePHIDs(array $import_phids) {
$this->importSourcePHIDs = $import_phids;
return $this;
}
protected function getDefaultOrderVector() {
return array('start', 'id');
}
@ -411,6 +417,13 @@ final class PhabricatorCalendarEventQuery
$this->parentEventPHIDs);
}
if ($this->importSourcePHIDs !== null) {
$where[] = qsprintf(
$conn,
'event.importSourcePHID IN (%Ls)',
$this->importSourcePHIDs);
}
return $where;
}
@ -441,6 +454,42 @@ final class PhabricatorCalendarEventQuery
$events = $this->getEventsInRange($events);
$import_phids = array();
foreach ($events as $event) {
$import_phid = $event->getImportSourcePHID();
if ($import_phid !== null) {
$import_phids[$import_phid] = $import_phid;
}
}
if ($import_phids) {
$imports = id(new PhabricatorCalendarImportQuery())
->setParentQuery($this)
->setViewer($viewer)
->withPHIDs($import_phids)
->execute();
$sources = mpull($sources, null, 'getPHID');
} else {
$sources = array();
}
foreach ($events as $key => $event) {
$import_phid = $event->getImportSourcePHID();
if ($import_phid === null) {
$event->attachImportSource(null);
continue;
}
$import = idx($imports, $import_phid);
if (!$import) {
unset($events[$key]);
$this->didRejectResult($event);
continue;
}
$event->attachImportSource($import);
}
$phids = array();
foreach ($events as $event) {
@ -561,5 +610,4 @@ final class PhabricatorCalendarEventQuery
return $raw_limit;
}
}

View file

@ -0,0 +1,81 @@
<?php
final class PhabricatorCalendarImportQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $isDisabled;
public function withIDs(array $ids) {
$this->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 PhabricatorCalendarImport();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'import.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'import.phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs !== null) {
$where[] = qsprintf(
$conn,
'import.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->isDisabled !== null) {
$where[] = qsprintf(
$conn,
'import.isDisabled = %d',
(int)$this->isDisabled);
}
return $where;
}
protected function getPrimaryTableAlias() {
return 'import';
}
public function getQueryApplicationClass() {
return 'PhabricatorCalendarApplication';
}
}

View file

@ -0,0 +1,81 @@
<?php
final class PhabricatorCalendarImportSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Calendar Imports');
}
public function getApplicationClassName() {
return 'PhabricatorCalendarApplication';
}
public function newQuery() {
return new PhabricatorCalendarImportQuery();
}
protected function buildCustomSearchFields() {
return array();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
return $query;
}
protected function getURI($path) {
return '/calendar/import/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'all' => pht('All Imports'),
);
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 $imports,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($imports, 'PhabricatorCalendarImport');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
foreach ($imports as $import) {
$item = id(new PHUIObjectItemView())
->setViewer($viewer)
->setObjectName(pht('Import %d', $import->getID()))
->setHeader($import->getName())
->setHref($import->getURI());
if ($import->getIsDisabled()) {
$item->setDisabled(true);
}
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
$result->setNoDataString(pht('No imports found.'));
return $result;
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorCalendarImportTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhabricatorCalendarImportTransaction();
}
}

View file

@ -3,6 +3,7 @@
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
implements
PhabricatorPolicyInterface,
PhabricatorExtendedPolicyInterface,
PhabricatorProjectInterface,
PhabricatorMarkupInterface,
PhabricatorApplicationTransactionInterface,
@ -40,8 +41,14 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
protected $utcInstanceEpoch;
protected $parameters = array();
protected $importAuthorPHID;
protected $importSourcePHID;
protected $importUIDIndex;
protected $importUID;
private $parentEvent = self::ATTACHABLE;
private $invitees = self::ATTACHABLE;
private $importSource = self::ATTACHABLE;
private $viewerTimezone;
@ -90,6 +97,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
->setAllDayDateTo(0)
->setStartDateTime($datetime_start)
->setEndDateTime($datetime_end)
->attachImportSource(null)
->applyViewerTimezone($actor);
}
@ -306,6 +314,14 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
$this->mailKey = Filesystem::readRandomCharacters(20);
}
$import_uid = $this->getImportUID();
if ($import_uid !== null) {
$index = PhabricatorHash::digestForIndex($import_uid);
} else {
$index = null;
}
$this->setImportUIDIndex($index);
$this->updateUTCEpochs();
return parent::save();
@ -344,6 +360,11 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
'utcUntilEpoch' => 'epoch?',
'utcInstanceEpoch' => 'epoch?',
'importAuthorPHID' => 'phid?',
'importSourcePHID' => 'phid?',
'importUIDIndex' => 'bytes12?',
'importUID' => 'text?',
// TODO: DEPRECATED.
'allDayDateFrom' => 'epoch',
'allDayDateTo' => 'epoch',
@ -885,6 +906,17 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
return $set;
}
public function getImportSource() {
return $this->assertAttached($this->importSource);
}
public function attachImportSource(
PhabricatorCalendarImport $import = null) {
$this->importSource = $import;
return $this;
}
/* -( Markup Interface )--------------------------------------------------- */
@ -947,11 +979,19 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
if ($this->getImportSource()) {
return PhabricatorPolicy::POLICY_NOONE;
} else {
return $this->getEditPolicy();
}
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getImportSource()) {
return false;
}
// The host of an event can always view and edit it.
$user_phid = $this->getHostPHID();
if ($user_phid) {
@ -974,12 +1014,40 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
}
public function describeAutomaticCapability($capability) {
if ($this->getImportSource()) {
return pht(
'Events imported from external sources can not be edited in '.
'Phabricator.');
}
return pht(
'The host of an event can always view and edit it. Users who are '.
'invited to an event can always view it.');
}
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
$extended = array();
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
$import_source = $this->getImportSource();
if ($import_source) {
$extended[] = array(
$import_source,
PhabricatorPolicyCapability::CAN_VIEW,
);
}
break;
}
return $extended;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -0,0 +1,126 @@
<?php
final class PhabricatorCalendarImport
extends PhabricatorCalendarDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorDestructibleInterface {
protected $name;
protected $authorPHID;
protected $viewPolicy;
protected $editPolicy;
protected $engineType;
protected $parameters = array();
protected $isDisabled = 0;
private $engine = self::ATTACHABLE;
public static function initializeNewCalendarImport(PhabricatorUser $actor) {
return id(new self())
->setAuthorPHID($actor->getPHID())
->setViewPolicy($actor->getPHID())
->setEditPolicy($actor->getPHID())
->setIsDisabled(0);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'parameters' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text',
'engineType' => 'text64',
'isDisabled' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_author' => array(
'columns' => array('authorPHID'),
),
),
) + parent::getConfiguration();
}
public function getPHIDType() {
return PhabricatorCalendarImportPHIDType::TYPECONST;
}
public function getURI() {
$id = $this->getID();
return "/calendar/import/{$id}/";
}
/* -( 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) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorCalendarImportEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorCalendarImportTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$viewer = $engine->getViewer();
$this->openTransaction();
$events = id(new PhabricatorCalendarEventQuery())
->withImportSourcePHIDs(array($this->getPHID()))
->execute();
foreach ($events as $event) {
$engine->destroyObject($event);
}
$this->delete();
$this->saveTransaction();
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorCalendarImportTransaction
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'calendar';
}
public function getApplicationTransactionType() {
return PhabricatorCalendarImportPHIDType::TYPECONST;
}
public function getBaseTransactionClass() {
return 'PhabricatorCalendarImportTransactionType';
}
}

View file

@ -187,7 +187,7 @@ final class PhabricatorSlowvotePoll extends PhabricatorSlowvoteDAO
return array($this->getAuthorPHID());
}
/* -( PhabricatorDestructableInterface )----------------------------------- */
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {