mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Edit recurring ghost events, not recurring event.
Summary: Closes T8358, Edit recurring ghost events, not recurring event. Test Plan: Create recurring event, original event should not be editable, but ghost events should be editable. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T8358 Differential Revision: https://secure.phabricator.com/D13075
This commit is contained in:
parent
9708f68e58
commit
65d9c0ea24
5 changed files with 122 additions and 7 deletions
|
@ -53,7 +53,7 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
|
|||
'event/' => array(
|
||||
'create/'
|
||||
=> 'PhabricatorCalendarEventEditController',
|
||||
'edit/(?P<id>[1-9]\d*)/'
|
||||
'edit/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
|
||||
=> 'PhabricatorCalendarEventEditController',
|
||||
'drag/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarEventDragController',
|
||||
|
|
|
@ -86,6 +86,52 @@ final class PhabricatorCalendarEventEditController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if ($request->getURIData('sequence')) {
|
||||
$index = $request->getURIData('sequence');
|
||||
|
||||
$result = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withInstanceSequencePairs(
|
||||
array(
|
||||
array(
|
||||
$event->getPHID(),
|
||||
$index,
|
||||
),
|
||||
))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
|
||||
if ($result) {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/calendar/event/edit/'.$result->getID().'/');
|
||||
}
|
||||
|
||||
$invitees = $event->getInvitees();
|
||||
|
||||
$new_ghost = $event->generateNthGhost($index, $viewer);
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$new_ghost
|
||||
->setID(null)
|
||||
->setPHID(null)
|
||||
->removeViewerTimezone($viewer)
|
||||
->save();
|
||||
$ghost_invitees = array();
|
||||
foreach ($invitees as $invitee) {
|
||||
$ghost_invitee = clone $invitee;
|
||||
$ghost_invitee
|
||||
->setID(null)
|
||||
->setEventPHID($new_ghost->getPHID())
|
||||
->save();
|
||||
}
|
||||
unset($unguarded);
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/calendar/event/edit/'.$new_ghost->getID().'/');
|
||||
}
|
||||
|
||||
$end_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$event->getDateTo());
|
||||
|
|
|
@ -135,7 +135,19 @@ final class PhabricatorCalendarEventViewController
|
|||
$event,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
if (!$event->getIsGhostEvent()) {
|
||||
if (($event->getIsRecurring() && $event->getIsGhostEvent())) {
|
||||
$index = $event->getSequenceIndex();
|
||||
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit This Instance'))
|
||||
->setIcon('fa-pencil')
|
||||
->setHref($this->getApplicationURI("event/edit/{$id}/{$index}/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit));
|
||||
}
|
||||
|
||||
if (!$event->getIsRecurring() && !$event->getIsGhostEvent()) {
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Event'))
|
||||
|
@ -219,7 +231,7 @@ final class PhabricatorCalendarEventViewController
|
|||
$properties->addProperty(
|
||||
pht('Recurs'),
|
||||
ucwords(idx($event->getRecurrenceFrequency(), 'rule')));
|
||||
if ($event->getIsGhostEvent()) {
|
||||
if ($event->getInstanceOfEventPHID()) {
|
||||
$properties->addProperty(
|
||||
pht('Recurrence of Event'),
|
||||
$viewer->renderHandle($event->getInstanceOfEventPHID()));
|
||||
|
|
|
@ -10,6 +10,8 @@ final class PhabricatorCalendarEventQuery
|
|||
private $inviteePHIDs;
|
||||
private $creatorPHIDs;
|
||||
private $isCancelled;
|
||||
private $instanceSequencePairs;
|
||||
|
||||
|
||||
private $generateGhosts = false;
|
||||
|
||||
|
@ -49,6 +51,11 @@ final class PhabricatorCalendarEventQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withInstanceSequencePairs(array $pairs) {
|
||||
$this->instanceSequencePairs = $pairs;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getDefaultOrderVector() {
|
||||
return array('start', 'id');
|
||||
}
|
||||
|
@ -98,12 +105,15 @@ final class PhabricatorCalendarEventQuery
|
|||
return $events;
|
||||
}
|
||||
|
||||
$map = array();
|
||||
$instance_sequence_pairs = array();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$sequence_start = 0;
|
||||
$instance_count = null;
|
||||
$duration = $event->getDateTo() - $event->getDateFrom();
|
||||
|
||||
if ($event->getIsRecurring()) {
|
||||
if ($event->getIsRecurring() && !$event->getInstanceOfEventPHID()) {
|
||||
$frequency = $event->getFrequencyUnit();
|
||||
$modify_key = '+1 '.$frequency;
|
||||
|
||||
|
@ -147,7 +157,37 @@ final class PhabricatorCalendarEventQuery
|
|||
|
||||
$max_sequence = $sequence_start + $instance_count;
|
||||
for ($index = $sequence_start; $index < $max_sequence; $index++) {
|
||||
$instance_sequence_pairs[] = array($event->getPHID(), $index);
|
||||
$events[] = $event->generateNthGhost($index, $viewer);
|
||||
|
||||
$key = last_key($events);
|
||||
$map[$event->getPHID()][$index] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($instance_sequence_pairs) > 0) {
|
||||
$sub_query = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->setParentQuery($this)
|
||||
->withInstanceSequencePairs($instance_sequence_pairs)
|
||||
->execute();
|
||||
|
||||
foreach ($sub_query as $edited_ghost) {
|
||||
$indexes = idx($map, $edited_ghost->getInstanceOfEventPHID());
|
||||
$key = idx($indexes, $edited_ghost->getSequenceIndex());
|
||||
$events[$key] = $edited_ghost;
|
||||
}
|
||||
|
||||
$id_map = array();
|
||||
foreach ($events as $key => $event) {
|
||||
if ($event->getIsGhostEvent()) {
|
||||
continue;
|
||||
}
|
||||
if (isset($id_map[$event->getID()])) {
|
||||
unset($events[$key]);
|
||||
} else {
|
||||
$id_map[$event->getID()] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,6 +260,22 @@ final class PhabricatorCalendarEventQuery
|
|||
(int)$this->isCancelled);
|
||||
}
|
||||
|
||||
if ($this->instanceSequencePairs !== null) {
|
||||
$sql = array();
|
||||
|
||||
foreach ($this->instanceSequencePairs as $pair) {
|
||||
$sql[] = qsprintf(
|
||||
$conn_r,
|
||||
'(event.instanceOfEventPHID = %s AND event.sequenceIndex = %d)',
|
||||
$pair[0],
|
||||
$pair[1]);
|
||||
}
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'%Q',
|
||||
implode(' OR ', $sql));
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
|
|
|
@ -203,6 +203,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
'userPHID_dateFrom' => array(
|
||||
'columns' => array('userPHID', 'dateTo'),
|
||||
),
|
||||
'key_instance' => array(
|
||||
'columns' => array('instanceOfEventPHID', 'sequenceIndex'),
|
||||
'unique' => true,
|
||||
),
|
||||
),
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'recurrenceFrequency' => self::SERIALIZATION_JSON,
|
||||
|
@ -391,9 +395,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
// The owner of a task can always view and edit it.
|
||||
$user_phid = $this->getUserPHID();
|
||||
if ($this->isGhostEvent) {
|
||||
return false;
|
||||
}
|
||||
if ($user_phid) {
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
if ($viewer_phid == $user_phid) {
|
||||
|
|
Loading…
Reference in a new issue