mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 03:02:43 +01:00
(stable) Promote 2016 Week 31
This commit is contained in:
commit
139e93916f
203 changed files with 7734 additions and 3581 deletions
|
@ -7,10 +7,10 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '4e7e9bde',
|
||||
'core.pkg.js' => '1bcca0f3',
|
||||
'core.pkg.css' => '8b87d014',
|
||||
'core.pkg.js' => '13c7e56a',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '3e81ae60',
|
||||
'differential.pkg.css' => '3fb7f532',
|
||||
'differential.pkg.js' => '634399e9',
|
||||
'diffusion.pkg.css' => '91c5d3a6',
|
||||
'diffusion.pkg.js' => '84c8f8fd',
|
||||
|
@ -25,7 +25,7 @@ return array(
|
|||
'rsrc/css/aphront/notification.css' => '3f6c89c9',
|
||||
'rsrc/css/aphront/panel-view.css' => '8427b78d',
|
||||
'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758',
|
||||
'rsrc/css/aphront/table-view.css' => '8df59783',
|
||||
'rsrc/css/aphront/table-view.css' => '832656fd',
|
||||
'rsrc/css/aphront/tokenizer.css' => '056da01b',
|
||||
'rsrc/css/aphront/tooltip.css' => '1a07aea8',
|
||||
'rsrc/css/aphront/typeahead-browse.css' => '8904346a',
|
||||
|
@ -57,7 +57,7 @@ return array(
|
|||
'rsrc/css/application/dashboard/dashboard.css' => 'bc6f2127',
|
||||
'rsrc/css/application/diff/inline-comment-summary.css' => '51efda3a',
|
||||
'rsrc/css/application/differential/add-comment.css' => 'c47f8c40',
|
||||
'rsrc/css/application/differential/changeset-view.css' => '37792573',
|
||||
'rsrc/css/application/differential/changeset-view.css' => '9ef7d354',
|
||||
'rsrc/css/application/differential/core.css' => '5b7b8ff4',
|
||||
'rsrc/css/application/differential/phui-inline-comment.css' => '5953c28e',
|
||||
'rsrc/css/application/differential/revision-comment.css' => '14b8565a',
|
||||
|
@ -115,10 +115,10 @@ return array(
|
|||
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
|
||||
'rsrc/css/layout/phabricator-side-menu-view.css' => 'dd849797',
|
||||
'rsrc/css/layout/phabricator-source-code-view.css' => 'cbeef983',
|
||||
'rsrc/css/phui/calendar/phui-calendar-day.css' => 'd1cf6f93',
|
||||
'rsrc/css/phui/calendar/phui-calendar-list.css' => '56e6381a',
|
||||
'rsrc/css/phui/calendar/phui-calendar-month.css' => '476be7e0',
|
||||
'rsrc/css/phui/calendar/phui-calendar.css' => 'ccabe893',
|
||||
'rsrc/css/phui/calendar/phui-calendar-day.css' => '572b1893',
|
||||
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'fcc9fb41',
|
||||
'rsrc/css/phui/calendar/phui-calendar-month.css' => '8e10e92c',
|
||||
'rsrc/css/phui/calendar/phui-calendar.css' => 'daadaf39',
|
||||
'rsrc/css/phui/phui-action-list.css' => 'c5eba19d',
|
||||
'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
|
||||
'rsrc/css/phui/phui-badge.css' => '3baef8db',
|
||||
|
@ -126,14 +126,14 @@ return array(
|
|||
'rsrc/css/phui/phui-box.css' => '5c8387cf',
|
||||
'rsrc/css/phui/phui-button.css' => '4a5fbe3d',
|
||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
|
||||
'rsrc/css/phui/phui-crumbs-view.css' => 'b4fa5755',
|
||||
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
|
||||
'rsrc/css/phui/phui-document-pro.css' => 'a3730b94',
|
||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||
'rsrc/css/phui/phui-document.css' => '715aedfb',
|
||||
'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
|
||||
'rsrc/css/phui/phui-fontkit.css' => '9cda225e',
|
||||
'rsrc/css/phui/phui-form-view.css' => '6a51768e',
|
||||
'rsrc/css/phui/phui-form-view.css' => 'fab0a10f',
|
||||
'rsrc/css/phui/phui-form.css' => 'aac1d51d',
|
||||
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
|
||||
'rsrc/css/phui/phui-header-view.css' => '4c7dd8f5',
|
||||
|
@ -156,7 +156,7 @@ return array(
|
|||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
|
||||
'rsrc/css/phui/phui-two-column-view.css' => '5afdf637',
|
||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
|
||||
'rsrc/css/phui/workboards/phui-workcard.css' => '0c62d7c5',
|
||||
|
@ -362,8 +362,9 @@ return array(
|
|||
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
|
||||
'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => 'edd1ba66',
|
||||
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
|
||||
'rsrc/js/application/calendar/behavior-day-view.js' => '5c46cff2',
|
||||
'rsrc/js/application/calendar/behavior-event-all-day.js' => '38dcf3c8',
|
||||
'rsrc/js/application/calendar/behavior-day-view.js' => '4b3c4443',
|
||||
'rsrc/js/application/calendar/behavior-event-all-day.js' => '937bb700',
|
||||
'rsrc/js/application/calendar/behavior-month-view.js' => 'fe33e256',
|
||||
'rsrc/js/application/calendar/behavior-recurring-edit.js' => '5f1c4d5f',
|
||||
'rsrc/js/application/config/behavior-reorder-fields.js' => 'b6993408',
|
||||
'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2',
|
||||
|
@ -439,7 +440,7 @@ return array(
|
|||
'rsrc/js/application/transactions/behavior-comment-actions.js' => '06460e71',
|
||||
'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
|
||||
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
|
||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '94c65b72',
|
||||
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6',
|
||||
'rsrc/js/application/transactions/behavior-transaction-list.js' => '13c739ea',
|
||||
'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '635de1ec',
|
||||
|
@ -536,7 +537,7 @@ return array(
|
|||
'aphront-list-filter-view-css' => '5d6f0526',
|
||||
'aphront-multi-column-view-css' => 'fd18389d',
|
||||
'aphront-panel-view-css' => '8427b78d',
|
||||
'aphront-table-view-css' => '8df59783',
|
||||
'aphront-table-view-css' => '832656fd',
|
||||
'aphront-tokenizer-control-css' => '056da01b',
|
||||
'aphront-tooltip-css' => '1a07aea8',
|
||||
'aphront-typeahead-control-css' => 'd4f16145',
|
||||
|
@ -555,7 +556,7 @@ return array(
|
|||
'conpherence-update-css' => 'faf6be09',
|
||||
'conpherence-widget-pane-css' => '775eaaba',
|
||||
'd3' => 'a11a5ff2',
|
||||
'differential-changeset-view-css' => '37792573',
|
||||
'differential-changeset-view-css' => '9ef7d354',
|
||||
'differential-core-view-css' => '5b7b8ff4',
|
||||
'differential-inline-comment-editor' => '64a5550f',
|
||||
'differential-revision-add-comment-css' => 'c47f8c40',
|
||||
|
@ -590,6 +591,7 @@ return array(
|
|||
'javelin-behavior-audit-preview' => 'd835b03a',
|
||||
'javelin-behavior-badge-view' => '8ff5e24c',
|
||||
'javelin-behavior-bulk-job-reload' => 'edf8a145',
|
||||
'javelin-behavior-calendar-month-view' => 'fe33e256',
|
||||
'javelin-behavior-choose-control' => '327a00d1',
|
||||
'javelin-behavior-comment-actions' => '06460e71',
|
||||
'javelin-behavior-config-reorder-fields' => 'b6993408',
|
||||
|
@ -603,7 +605,7 @@ return array(
|
|||
'javelin-behavior-dashboard-move-panels' => '019f36c4',
|
||||
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
|
||||
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
|
||||
'javelin-behavior-day-view' => '5c46cff2',
|
||||
'javelin-behavior-day-view' => '4b3c4443',
|
||||
'javelin-behavior-desktop-notifications-control' => 'edd1ba66',
|
||||
'javelin-behavior-detect-timezone' => '4c193c96',
|
||||
'javelin-behavior-device' => 'bb1dd507',
|
||||
|
@ -629,7 +631,7 @@ return array(
|
|||
'javelin-behavior-editengine-reorder-configs' => 'd7a74243',
|
||||
'javelin-behavior-editengine-reorder-fields' => 'b59e1e96',
|
||||
'javelin-behavior-error-log' => '6882e80a',
|
||||
'javelin-behavior-event-all-day' => '38dcf3c8',
|
||||
'javelin-behavior-event-all-day' => '937bb700',
|
||||
'javelin-behavior-fancy-datepicker' => '568931f3',
|
||||
'javelin-behavior-global-drag-and-drop' => 'c8e57404',
|
||||
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
|
||||
|
@ -663,7 +665,7 @@ return array(
|
|||
'javelin-behavior-phabricator-remarkup-assist' => '116cf19b',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
'javelin-behavior-phabricator-search-typeahead' => '06c32383',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => 'dbbf48b6',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => '94c65b72',
|
||||
'javelin-behavior-phabricator-tooltips' => '42fcb747',
|
||||
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
|
||||
'javelin-behavior-phabricator-transaction-list' => '13c739ea',
|
||||
|
@ -825,12 +827,12 @@ return array(
|
|||
'phui-big-info-view-css' => 'bd903741',
|
||||
'phui-box-css' => '5c8387cf',
|
||||
'phui-button-css' => '4a5fbe3d',
|
||||
'phui-calendar-css' => 'ccabe893',
|
||||
'phui-calendar-day-css' => 'd1cf6f93',
|
||||
'phui-calendar-list-css' => '56e6381a',
|
||||
'phui-calendar-month-css' => '476be7e0',
|
||||
'phui-calendar-css' => 'daadaf39',
|
||||
'phui-calendar-day-css' => '572b1893',
|
||||
'phui-calendar-list-css' => 'fcc9fb41',
|
||||
'phui-calendar-month-css' => '8e10e92c',
|
||||
'phui-chart-css' => '6bf6f78e',
|
||||
'phui-crumbs-view-css' => '6b813619',
|
||||
'phui-crumbs-view-css' => 'b4fa5755',
|
||||
'phui-curtain-view-css' => '7148ae25',
|
||||
'phui-document-summary-view-css' => '9ca48bdf',
|
||||
'phui-document-view-css' => '715aedfb',
|
||||
|
@ -839,7 +841,7 @@ return array(
|
|||
'phui-font-icon-base-css' => '6449bce8',
|
||||
'phui-fontkit-css' => '9cda225e',
|
||||
'phui-form-css' => 'aac1d51d',
|
||||
'phui-form-view-css' => '6a51768e',
|
||||
'phui-form-view-css' => 'fab0a10f',
|
||||
'phui-head-thing-view-css' => 'fd311e5f',
|
||||
'phui-header-view-css' => '4c7dd8f5',
|
||||
'phui-hovercard' => '1bd28176',
|
||||
|
@ -864,7 +866,7 @@ return array(
|
|||
'phui-tag-view-css' => '6bbd83e2',
|
||||
'phui-theme-css' => '027ba77e',
|
||||
'phui-timeline-view-css' => 'bc523970',
|
||||
'phui-two-column-view-css' => '9fb86c85',
|
||||
'phui-two-column-view-css' => '5afdf637',
|
||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
||||
'phui-workboard-view-css' => 'e6d89647',
|
||||
'phui-workcard-view-css' => '0c62d7c5',
|
||||
|
@ -1143,9 +1145,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-workflow',
|
||||
),
|
||||
37792573 => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'3ab51e2c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-behavior-device',
|
||||
|
@ -1236,6 +1235,9 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'4b3c4443' => array(
|
||||
'phuix-icon-view',
|
||||
),
|
||||
'4b700e9e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1655,6 +1657,12 @@ return array(
|
|||
'javelin-resource',
|
||||
'javelin-routable',
|
||||
),
|
||||
'94c65b72' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'988040b4' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1672,6 +1680,9 @@ return array(
|
|||
'phabricator-phtize',
|
||||
'changeset-view-manager',
|
||||
),
|
||||
'9ef7d354' => array(
|
||||
'phui-inline-comment-view-css',
|
||||
),
|
||||
'9f36c42d' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -2014,12 +2025,6 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-shaped-request',
|
||||
),
|
||||
'dbbf48b6' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'de2e896f' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -17,7 +17,7 @@ foreach ($iterator as $event) {
|
|||
// later patch. See T8209.
|
||||
$user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($event->getUserPHID()))
|
||||
->withPHIDs(array($event->getHostPHID()))
|
||||
->executeOne();
|
||||
|
||||
if ($user) {
|
||||
|
|
2
resources/sql/autopatches/20160707.calendar.01.stub.sql
Normal file
2
resources/sql/autopatches/20160707.calendar.01.stub.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD isStub BOOL NOT NULL;
|
2
resources/sql/autopatches/20160711.files.01.builtin.sql
Normal file
2
resources/sql/autopatches/20160711.files.01.builtin.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_file.file
|
||||
ADD builtinKey VARCHAR(64) COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_file.file
|
||||
ADD UNIQUE KEY `key_builtin` (builtinKey);
|
2
resources/sql/autopatches/20160713.event.01.host.sql
Normal file
2
resources/sql/autopatches/20160713.event.01.host.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
CHANGE userPHID hostPHID VARBINARY(64) NOT NULL;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD allDayDateFrom INT UNSIGNED NOT NULL;
|
2
resources/sql/autopatches/20160715.event.02.alldayto.sql
Normal file
2
resources/sql/autopatches/20160715.event.02.alldayto.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD allDayDateTo INT UNSIGNED NOT NULL;
|
52
resources/sql/autopatches/20160715.event.03.allday.php
Normal file
52
resources/sql/autopatches/20160715.event.03.allday.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
$table = new PhabricatorCalendarEvent();
|
||||
$conn = $table->establishConnection('w');
|
||||
|
||||
// Previously, "All Day" events were stored with a start and end date set to
|
||||
// the earliest possible start and end seconds for the corresponding days. We
|
||||
// now store all day events with their "date" epochs as UTC, separate from
|
||||
// individual event times.
|
||||
$zone_min = new DateTimeZone('Pacific/Midway');
|
||||
$zone_max = new DateTimeZone('Pacific/Kiritimati');
|
||||
$zone_utc = new DateTimeZone('UTC');
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $event) {
|
||||
// If this event has already migrated, skip it.
|
||||
if ($event->getAllDayDateFrom()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$is_all_day = $event->getIsAllDay();
|
||||
|
||||
$epoch_min = $event->getDateFrom();
|
||||
$epoch_max = $event->getDateTo();
|
||||
|
||||
$date_min = new DateTime('@'.$epoch_min);
|
||||
$date_max = new DateTime('@'.$epoch_max);
|
||||
|
||||
if ($is_all_day) {
|
||||
$date_min->setTimeZone($zone_min);
|
||||
$date_min->modify('+2 days');
|
||||
$date_max->setTimeZone($zone_max);
|
||||
$date_max->modify('-2 days');
|
||||
} else {
|
||||
$date_min->setTimeZone($zone_utc);
|
||||
$date_max->setTimeZone($zone_utc);
|
||||
}
|
||||
|
||||
$string_min = $date_min->format('Y-m-d');
|
||||
$string_max = $date_max->format('Y-m-d 23:59:00');
|
||||
|
||||
$allday_min = id(new DateTime($string_min, $zone_utc))->format('U');
|
||||
$allday_max = id(new DateTime($string_max, $zone_utc))->format('U');
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET allDayDateFrom = %d, allDayDateTo = %d
|
||||
WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$allday_min,
|
||||
$allday_max,
|
||||
$event->getID());
|
||||
}
|
37
resources/sql/autopatches/20160720.calendar.invitetxn.php
Normal file
37
resources/sql/autopatches/20160720.calendar.invitetxn.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
$table = new PhabricatorCalendarEventTransaction();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
echo pht(
|
||||
"Restructuring calendar invite transactions...\n");
|
||||
|
||||
foreach (new LiskMigrationIterator($table) as $txn) {
|
||||
$type = PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE;
|
||||
if ($txn->getTransactionType() != $type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$old_value = array_keys($txn->getOldValue());
|
||||
|
||||
$orig_new = $txn->getNewValue();
|
||||
$status_uninvited = 'uninvited';
|
||||
foreach ($orig_new as $key => $status) {
|
||||
if ($status == $status_uninvited) {
|
||||
unset($orig_new[$key]);
|
||||
}
|
||||
}
|
||||
$new_value = array_keys($orig_new);
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'UPDATE %T SET '.
|
||||
'oldValue = %s, newValue = %s'.
|
||||
'WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
phutil_json_encode($old_value),
|
||||
phutil_json_encode($new_value),
|
||||
$txn->getID());
|
||||
}
|
||||
|
||||
echo pht('Done.')."\n";
|
11
resources/sql/autopatches/20160721.pack.01.pub.sql
Normal file
11
resources/sql/autopatches/20160721.pack.01.pub.sql
Normal file
|
@ -0,0 +1,11 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_publisher (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
name VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
publisherKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_SORT},
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
UNIQUE KEY `key_publisher` (publisherKey)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
19
resources/sql/autopatches/20160721.pack.02.pubxaction.sql
Normal file
19
resources/sql/autopatches/20160721.pack.02.pubxaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_publishertransaction (
|
||||
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};
|
16
resources/sql/autopatches/20160721.pack.03.edge.sql
Normal file
16
resources/sql/autopatches/20160721.pack.03.edge.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.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}_packages.edgedata (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
13
resources/sql/autopatches/20160721.pack.04.pkg.sql
Normal file
13
resources/sql/autopatches/20160721.pack.04.pkg.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_package (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
name VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
publisherPHID VARBINARY(64) NOT NULL,
|
||||
packageKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_SORT},
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
UNIQUE KEY `key_package` (publisherPHID, packageKey)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
19
resources/sql/autopatches/20160721.pack.05.pkgxaction.sql
Normal file
19
resources/sql/autopatches/20160721.pack.05.pkgxaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_packagetransaction (
|
||||
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};
|
10
resources/sql/autopatches/20160721.pack.06.version.sql
Normal file
10
resources/sql/autopatches/20160721.pack.06.version.sql
Normal file
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_version (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
name VARCHAR(64) NOT NULL COLLATE {$COLLATE_SORT},
|
||||
packagePHID VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
UNIQUE KEY `key_package` (packagePHID, name)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_versiontransaction (
|
||||
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};
|
7
resources/sql/autopatches/20160722.pack.01.pubngrams.sql
Normal file
7
resources/sql/autopatches/20160722.pack.01.pubngrams.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_publishername_ngrams (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
objectID INT UNSIGNED NOT NULL,
|
||||
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
KEY `key_object` (objectID),
|
||||
KEY `key_ngram` (ngram, objectID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
7
resources/sql/autopatches/20160722.pack.02.pkgngrams.sql
Normal file
7
resources/sql/autopatches/20160722.pack.02.pkgngrams.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_packagename_ngrams (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
objectID INT UNSIGNED NOT NULL,
|
||||
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
KEY `key_object` (objectID),
|
||||
KEY `key_ngram` (ngram, objectID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE {$NAMESPACE}_packages.packages_versionname_ngrams (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
objectID INT UNSIGNED NOT NULL,
|
||||
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
KEY `key_object` (objectID),
|
||||
KEY `key_ngram` (ngram, objectID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -1626,8 +1626,7 @@ phutil_register_library_map(array(
|
|||
'PHUIFormIconSetControl' => 'view/form/control/PHUIFormIconSetControl.php',
|
||||
'PHUIFormInsetView' => 'view/form/PHUIFormInsetView.php',
|
||||
'PHUIFormLayoutView' => 'view/form/PHUIFormLayoutView.php',
|
||||
'PHUIFormMultiSubmitControl' => 'view/form/control/PHUIFormMultiSubmitControl.php',
|
||||
'PHUIFormPageView' => 'view/form/PHUIFormPageView.php',
|
||||
'PHUIFormNumberControl' => 'view/form/control/PHUIFormNumberControl.php',
|
||||
'PHUIHandleListView' => 'applications/phid/view/PHUIHandleListView.php',
|
||||
'PHUIHandleTagListView' => 'applications/phid/view/PHUIHandleTagListView.php',
|
||||
'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
|
||||
|
@ -1653,7 +1652,6 @@ phutil_register_library_map(array(
|
|||
'PHUIObjectItemListExample' => 'applications/uiexample/examples/PHUIObjectItemListExample.php',
|
||||
'PHUIObjectItemListView' => 'view/phui/PHUIObjectItemListView.php',
|
||||
'PHUIObjectItemView' => 'view/phui/PHUIObjectItemView.php',
|
||||
'PHUIPagedFormView' => 'view/form/PHUIPagedFormView.php',
|
||||
'PHUIPagerView' => 'view/phui/PHUIPagerView.php',
|
||||
'PHUIPinboardItemView' => 'view/phui/PHUIPinboardItemView.php',
|
||||
'PHUIPinboardView' => 'view/phui/PHUIPinboardView.php',
|
||||
|
@ -2020,25 +2018,48 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarController' => 'applications/calendar/controller/PhabricatorCalendarController.php',
|
||||
'PhabricatorCalendarDAO' => 'applications/calendar/storage/PhabricatorCalendarDAO.php',
|
||||
'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php',
|
||||
'PhabricatorCalendarEventAcceptTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventAcceptTransaction.php',
|
||||
'PhabricatorCalendarEventAllDayTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventAllDayTransaction.php',
|
||||
'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php',
|
||||
'PhabricatorCalendarEventCommentController' => 'applications/calendar/controller/PhabricatorCalendarEventCommentController.php',
|
||||
'PhabricatorCalendarEventCancelTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventCancelTransaction.php',
|
||||
'PhabricatorCalendarEventDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventDateTransaction.php',
|
||||
'PhabricatorCalendarEventDeclineTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventDeclineTransaction.php',
|
||||
'PhabricatorCalendarEventDefaultEditCapability' => 'applications/calendar/capability/PhabricatorCalendarEventDefaultEditCapability.php',
|
||||
'PhabricatorCalendarEventDefaultViewCapability' => 'applications/calendar/capability/PhabricatorCalendarEventDefaultViewCapability.php',
|
||||
'PhabricatorCalendarEventDescriptionTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventDescriptionTransaction.php',
|
||||
'PhabricatorCalendarEventDragController' => 'applications/calendar/controller/PhabricatorCalendarEventDragController.php',
|
||||
'PhabricatorCalendarEventEditConduitAPIMethod' => 'applications/calendar/conduit/PhabricatorCalendarEventEditConduitAPIMethod.php',
|
||||
'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php',
|
||||
'PhabricatorCalendarEventEditEngine' => 'applications/calendar/editor/PhabricatorCalendarEventEditEngine.php',
|
||||
'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php',
|
||||
'PhabricatorCalendarEventEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventEmailCommand.php',
|
||||
'PhabricatorCalendarEventEndDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventEndDateTransaction.php',
|
||||
'PhabricatorCalendarEventFrequencyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventFrequencyTransaction.php',
|
||||
'PhabricatorCalendarEventFulltextEngine' => 'applications/calendar/search/PhabricatorCalendarEventFulltextEngine.php',
|
||||
'PhabricatorCalendarEventHostPolicyRule' => 'applications/calendar/policyrule/PhabricatorCalendarEventHostPolicyRule.php',
|
||||
'PhabricatorCalendarEventHostTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventHostTransaction.php',
|
||||
'PhabricatorCalendarEventIconTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventIconTransaction.php',
|
||||
'PhabricatorCalendarEventInviteTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php',
|
||||
'PhabricatorCalendarEventInvitee' => 'applications/calendar/storage/PhabricatorCalendarEventInvitee.php',
|
||||
'PhabricatorCalendarEventInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php',
|
||||
'PhabricatorCalendarEventInviteesPolicyRule' => 'applications/calendar/policyrule/PhabricatorCalendarEventInviteesPolicyRule.php',
|
||||
'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php',
|
||||
'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php',
|
||||
'PhabricatorCalendarEventMailReceiver' => 'applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php',
|
||||
'PhabricatorCalendarEventNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventNameTransaction.php',
|
||||
'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php',
|
||||
'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php',
|
||||
'PhabricatorCalendarEventRSVPEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventRSVPEmailCommand.php',
|
||||
'PhabricatorCalendarEventRecurringTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventRecurringTransaction.php',
|
||||
'PhabricatorCalendarEventReplyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventReplyTransaction.php',
|
||||
'PhabricatorCalendarEventSearchConduitAPIMethod' => 'applications/calendar/conduit/PhabricatorCalendarEventSearchConduitAPIMethod.php',
|
||||
'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php',
|
||||
'PhabricatorCalendarEventStartDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventStartDateTransaction.php',
|
||||
'PhabricatorCalendarEventTransaction' => 'applications/calendar/storage/PhabricatorCalendarEventTransaction.php',
|
||||
'PhabricatorCalendarEventTransactionComment' => 'applications/calendar/storage/PhabricatorCalendarEventTransactionComment.php',
|
||||
'PhabricatorCalendarEventTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php',
|
||||
'PhabricatorCalendarEventTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarEventTransactionType.php',
|
||||
'PhabricatorCalendarEventUntilDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventUntilDateTransaction.php',
|
||||
'PhabricatorCalendarEventViewController' => 'applications/calendar/controller/PhabricatorCalendarEventViewController.php',
|
||||
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
|
||||
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
|
||||
|
@ -2951,7 +2972,80 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPHPASTApplication' => 'applications/phpast/application/PhabricatorPHPASTApplication.php',
|
||||
'PhabricatorPHPConfigSetupCheck' => 'applications/config/check/PhabricatorPHPConfigSetupCheck.php',
|
||||
'PhabricatorPHPMailerConfigOptions' => 'applications/config/option/PhabricatorPHPMailerConfigOptions.php',
|
||||
'PhabricatorPagedFormUIExample' => 'applications/uiexample/examples/PhabricatorPagedFormUIExample.php',
|
||||
'PhabricatorPackagesApplication' => 'applications/packages/application/PhabricatorPackagesApplication.php',
|
||||
'PhabricatorPackagesController' => 'applications/packages/controller/PhabricatorPackagesController.php',
|
||||
'PhabricatorPackagesCreatePublisherCapability' => 'applications/packages/capability/PhabricatorPackagesCreatePublisherCapability.php',
|
||||
'PhabricatorPackagesDAO' => 'applications/packages/storage/PhabricatorPackagesDAO.php',
|
||||
'PhabricatorPackagesEditEngine' => 'applications/packages/editor/PhabricatorPackagesEditEngine.php',
|
||||
'PhabricatorPackagesEditor' => 'applications/packages/editor/PhabricatorPackagesEditor.php',
|
||||
'PhabricatorPackagesNgrams' => 'applications/packages/storage/PhabricatorPackagesNgrams.php',
|
||||
'PhabricatorPackagesPackage' => 'applications/packages/storage/PhabricatorPackagesPackage.php',
|
||||
'PhabricatorPackagesPackageController' => 'applications/packages/controller/PhabricatorPackagesPackageController.php',
|
||||
'PhabricatorPackagesPackageDatasource' => 'applications/packages/typeahead/PhabricatorPackagesPackageDatasource.php',
|
||||
'PhabricatorPackagesPackageDefaultEditCapability' => 'applications/packages/capability/PhabricatorPackagesPackageDefaultEditCapability.php',
|
||||
'PhabricatorPackagesPackageDefaultViewCapability' => 'applications/packages/capability/PhabricatorPackagesPackageDefaultViewCapability.php',
|
||||
'PhabricatorPackagesPackageEditConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPackageEditConduitAPIMethod.php',
|
||||
'PhabricatorPackagesPackageEditController' => 'applications/packages/controller/PhabricatorPackagesPackageEditController.php',
|
||||
'PhabricatorPackagesPackageEditEngine' => 'applications/packages/editor/PhabricatorPackagesPackageEditEngine.php',
|
||||
'PhabricatorPackagesPackageEditor' => 'applications/packages/editor/PhabricatorPackagesPackageEditor.php',
|
||||
'PhabricatorPackagesPackageKeyTransaction' => 'applications/packages/xaction/package/PhabricatorPackagesPackageKeyTransaction.php',
|
||||
'PhabricatorPackagesPackageListController' => 'applications/packages/controller/PhabricatorPackagesPackageListController.php',
|
||||
'PhabricatorPackagesPackageListView' => 'applications/packages/view/PhabricatorPackagesPackageListView.php',
|
||||
'PhabricatorPackagesPackageNameNgrams' => 'applications/packages/storage/PhabricatorPackagesPackageNameNgrams.php',
|
||||
'PhabricatorPackagesPackageNameTransaction' => 'applications/packages/xaction/package/PhabricatorPackagesPackageNameTransaction.php',
|
||||
'PhabricatorPackagesPackagePHIDType' => 'applications/packages/phid/PhabricatorPackagesPackagePHIDType.php',
|
||||
'PhabricatorPackagesPackagePublisherTransaction' => 'applications/packages/xaction/package/PhabricatorPackagesPackagePublisherTransaction.php',
|
||||
'PhabricatorPackagesPackageQuery' => 'applications/packages/query/PhabricatorPackagesPackageQuery.php',
|
||||
'PhabricatorPackagesPackageSearchConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPackageSearchConduitAPIMethod.php',
|
||||
'PhabricatorPackagesPackageSearchEngine' => 'applications/packages/query/PhabricatorPackagesPackageSearchEngine.php',
|
||||
'PhabricatorPackagesPackageTransaction' => 'applications/packages/storage/PhabricatorPackagesPackageTransaction.php',
|
||||
'PhabricatorPackagesPackageTransactionQuery' => 'applications/packages/query/PhabricatorPackagesPackageTransactionQuery.php',
|
||||
'PhabricatorPackagesPackageTransactionType' => 'applications/packages/xaction/package/PhabricatorPackagesPackageTransactionType.php',
|
||||
'PhabricatorPackagesPackageViewController' => 'applications/packages/controller/PhabricatorPackagesPackageViewController.php',
|
||||
'PhabricatorPackagesPublisher' => 'applications/packages/storage/PhabricatorPackagesPublisher.php',
|
||||
'PhabricatorPackagesPublisherController' => 'applications/packages/controller/PhabricatorPackagesPublisherController.php',
|
||||
'PhabricatorPackagesPublisherDatasource' => 'applications/packages/typeahead/PhabricatorPackagesPublisherDatasource.php',
|
||||
'PhabricatorPackagesPublisherDefaultEditCapability' => 'applications/packages/capability/PhabricatorPackagesPublisherDefaultEditCapability.php',
|
||||
'PhabricatorPackagesPublisherEditConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPublisherEditConduitAPIMethod.php',
|
||||
'PhabricatorPackagesPublisherEditController' => 'applications/packages/controller/PhabricatorPackagesPublisherEditController.php',
|
||||
'PhabricatorPackagesPublisherEditEngine' => 'applications/packages/editor/PhabricatorPackagesPublisherEditEngine.php',
|
||||
'PhabricatorPackagesPublisherEditor' => 'applications/packages/editor/PhabricatorPackagesPublisherEditor.php',
|
||||
'PhabricatorPackagesPublisherKeyTransaction' => 'applications/packages/xaction/publisher/PhabricatorPackagesPublisherKeyTransaction.php',
|
||||
'PhabricatorPackagesPublisherListController' => 'applications/packages/controller/PhabricatorPackagesPublisherListController.php',
|
||||
'PhabricatorPackagesPublisherListView' => 'applications/packages/view/PhabricatorPackagesPublisherListView.php',
|
||||
'PhabricatorPackagesPublisherNameNgrams' => 'applications/packages/storage/PhabricatorPackagesPublisherNameNgrams.php',
|
||||
'PhabricatorPackagesPublisherNameTransaction' => 'applications/packages/xaction/publisher/PhabricatorPackagesPublisherNameTransaction.php',
|
||||
'PhabricatorPackagesPublisherPHIDType' => 'applications/packages/phid/PhabricatorPackagesPublisherPHIDType.php',
|
||||
'PhabricatorPackagesPublisherQuery' => 'applications/packages/query/PhabricatorPackagesPublisherQuery.php',
|
||||
'PhabricatorPackagesPublisherSearchConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesPublisherSearchConduitAPIMethod.php',
|
||||
'PhabricatorPackagesPublisherSearchEngine' => 'applications/packages/query/PhabricatorPackagesPublisherSearchEngine.php',
|
||||
'PhabricatorPackagesPublisherTransaction' => 'applications/packages/storage/PhabricatorPackagesPublisherTransaction.php',
|
||||
'PhabricatorPackagesPublisherTransactionQuery' => 'applications/packages/query/PhabricatorPackagesPublisherTransactionQuery.php',
|
||||
'PhabricatorPackagesPublisherTransactionType' => 'applications/packages/xaction/publisher/PhabricatorPackagesPublisherTransactionType.php',
|
||||
'PhabricatorPackagesPublisherViewController' => 'applications/packages/controller/PhabricatorPackagesPublisherViewController.php',
|
||||
'PhabricatorPackagesQuery' => 'applications/packages/query/PhabricatorPackagesQuery.php',
|
||||
'PhabricatorPackagesSchemaSpec' => 'applications/packages/storage/PhabricatorPackagesSchemaSpec.php',
|
||||
'PhabricatorPackagesTransactionType' => 'applications/packages/xaction/PhabricatorPackagesTransactionType.php',
|
||||
'PhabricatorPackagesVersion' => 'applications/packages/storage/PhabricatorPackagesVersion.php',
|
||||
'PhabricatorPackagesVersionController' => 'applications/packages/controller/PhabricatorPackagesVersionController.php',
|
||||
'PhabricatorPackagesVersionEditConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesVersionEditConduitAPIMethod.php',
|
||||
'PhabricatorPackagesVersionEditController' => 'applications/packages/controller/PhabricatorPackagesVersionEditController.php',
|
||||
'PhabricatorPackagesVersionEditEngine' => 'applications/packages/editor/PhabricatorPackagesVersionEditEngine.php',
|
||||
'PhabricatorPackagesVersionEditor' => 'applications/packages/editor/PhabricatorPackagesVersionEditor.php',
|
||||
'PhabricatorPackagesVersionListController' => 'applications/packages/controller/PhabricatorPackagesVersionListController.php',
|
||||
'PhabricatorPackagesVersionListView' => 'applications/packages/view/PhabricatorPackagesVersionListView.php',
|
||||
'PhabricatorPackagesVersionNameNgrams' => 'applications/packages/storage/PhabricatorPackagesVersionNameNgrams.php',
|
||||
'PhabricatorPackagesVersionNameTransaction' => 'applications/packages/xaction/version/PhabricatorPackagesVersionNameTransaction.php',
|
||||
'PhabricatorPackagesVersionPHIDType' => 'applications/packages/phid/PhabricatorPackagesVersionPHIDType.php',
|
||||
'PhabricatorPackagesVersionPackageTransaction' => 'applications/packages/xaction/version/PhabricatorPackagesVersionPackageTransaction.php',
|
||||
'PhabricatorPackagesVersionQuery' => 'applications/packages/query/PhabricatorPackagesVersionQuery.php',
|
||||
'PhabricatorPackagesVersionSearchConduitAPIMethod' => 'applications/packages/conduit/PhabricatorPackagesVersionSearchConduitAPIMethod.php',
|
||||
'PhabricatorPackagesVersionSearchEngine' => 'applications/packages/query/PhabricatorPackagesVersionSearchEngine.php',
|
||||
'PhabricatorPackagesVersionTransaction' => 'applications/packages/storage/PhabricatorPackagesVersionTransaction.php',
|
||||
'PhabricatorPackagesVersionTransactionQuery' => 'applications/packages/query/PhabricatorPackagesVersionTransactionQuery.php',
|
||||
'PhabricatorPackagesVersionTransactionType' => 'applications/packages/xaction/version/PhabricatorPackagesVersionTransactionType.php',
|
||||
'PhabricatorPackagesVersionViewController' => 'applications/packages/controller/PhabricatorPackagesVersionViewController.php',
|
||||
'PhabricatorPackagesView' => 'applications/packages/view/PhabricatorPackagesView.php',
|
||||
'PhabricatorPagerUIExample' => 'applications/uiexample/examples/PhabricatorPagerUIExample.php',
|
||||
'PhabricatorPassphraseApplication' => 'applications/passphrase/application/PhabricatorPassphraseApplication.php',
|
||||
'PhabricatorPasswordAuthProvider' => 'applications/auth/provider/PhabricatorPasswordAuthProvider.php',
|
||||
|
@ -2992,7 +3086,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
|
||||
'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
|
||||
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
|
||||
'PhabricatorPeopleCalendarController' => 'applications/people/controller/PhabricatorPeopleCalendarController.php',
|
||||
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
|
||||
'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php',
|
||||
'PhabricatorPeopleDatasource' => 'applications/people/typeahead/PhabricatorPeopleDatasource.php',
|
||||
|
@ -6167,8 +6260,7 @@ phutil_register_library_map(array(
|
|||
'PHUIFormIconSetControl' => 'AphrontFormControl',
|
||||
'PHUIFormInsetView' => 'AphrontView',
|
||||
'PHUIFormLayoutView' => 'AphrontView',
|
||||
'PHUIFormMultiSubmitControl' => 'AphrontFormControl',
|
||||
'PHUIFormPageView' => 'AphrontView',
|
||||
'PHUIFormNumberControl' => 'AphrontFormControl',
|
||||
'PHUIHandleListView' => 'AphrontTagView',
|
||||
'PHUIHandleTagListView' => 'AphrontTagView',
|
||||
'PHUIHandleView' => 'AphrontView',
|
||||
|
@ -6194,7 +6286,6 @@ phutil_register_library_map(array(
|
|||
'PHUIObjectItemListExample' => 'PhabricatorUIExample',
|
||||
'PHUIObjectItemListView' => 'AphrontTagView',
|
||||
'PHUIObjectItemView' => 'AphrontTagView',
|
||||
'PHUIPagedFormView' => 'AphrontView',
|
||||
'PHUIPagerView' => 'AphrontView',
|
||||
'PHUIPinboardItemView' => 'AphrontView',
|
||||
'PHUIPinboardView' => 'AphrontView',
|
||||
|
@ -6628,29 +6719,53 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorSpacesInterface',
|
||||
'PhabricatorFulltextInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
),
|
||||
'PhabricatorCalendarEventAcceptTransaction' => 'PhabricatorCalendarEventReplyTransaction',
|
||||
'PhabricatorCalendarEventAllDayTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventCancelTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventDateTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventDeclineTransaction' => 'PhabricatorCalendarEventReplyTransaction',
|
||||
'PhabricatorCalendarEventDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorCalendarEventDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorCalendarEventDescriptionTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorCalendarEventEmailCommand' => 'MetaMTAEmailTransactionCommand',
|
||||
'PhabricatorCalendarEventEndDateTransaction' => 'PhabricatorCalendarEventDateTransaction',
|
||||
'PhabricatorCalendarEventFrequencyTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'PhabricatorCalendarEventHostPolicyRule' => 'PhabricatorPolicyRule',
|
||||
'PhabricatorCalendarEventHostTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventIconTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventInviteTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventInvitee' => array(
|
||||
'PhabricatorCalendarDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorCalendarEventInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorCalendarEventInviteesPolicyRule' => 'PhabricatorPolicyRule',
|
||||
'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'PhabricatorCalendarEventNameTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorCalendarEventRSVPEmailCommand' => 'PhabricatorCalendarEventEmailCommand',
|
||||
'PhabricatorCalendarEventRecurringTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventReplyTransaction' => 'PhabricatorCalendarEventTransactionType',
|
||||
'PhabricatorCalendarEventSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorCalendarEventTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorCalendarEventStartDateTransaction' => 'PhabricatorCalendarEventDateTransaction',
|
||||
'PhabricatorCalendarEventTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorCalendarEventTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'PhabricatorCalendarEventTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorCalendarEventTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorCalendarEventUntilDateTransaction' => 'PhabricatorCalendarEventDateTransaction',
|
||||
'PhabricatorCalendarEventViewController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
|
||||
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -7684,7 +7799,108 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPHPASTApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPHPConfigSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorPHPMailerConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPagedFormUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorPackagesApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPackagesController' => 'PhabricatorController',
|
||||
'PhabricatorPackagesCreatePublisherCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorPackagesDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorPackagesEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorPackagesEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorPackagesNgrams' => 'PhabricatorSearchNgrams',
|
||||
'PhabricatorPackagesPackage' => array(
|
||||
'PhabricatorPackagesDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
),
|
||||
'PhabricatorPackagesPackageController' => 'PhabricatorPackagesController',
|
||||
'PhabricatorPackagesPackageDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorPackagesPackageDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorPackagesPackageDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorPackagesPackageEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'PhabricatorPackagesPackageEditController' => 'PhabricatorPackagesPackageController',
|
||||
'PhabricatorPackagesPackageEditEngine' => 'PhabricatorPackagesEditEngine',
|
||||
'PhabricatorPackagesPackageEditor' => 'PhabricatorPackagesEditor',
|
||||
'PhabricatorPackagesPackageKeyTransaction' => 'PhabricatorPackagesPackageTransactionType',
|
||||
'PhabricatorPackagesPackageListController' => 'PhabricatorPackagesPackageController',
|
||||
'PhabricatorPackagesPackageListView' => 'PhabricatorPackagesView',
|
||||
'PhabricatorPackagesPackageNameNgrams' => 'PhabricatorPackagesNgrams',
|
||||
'PhabricatorPackagesPackageNameTransaction' => 'PhabricatorPackagesPackageTransactionType',
|
||||
'PhabricatorPackagesPackagePHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorPackagesPackagePublisherTransaction' => 'PhabricatorPackagesPackageTransactionType',
|
||||
'PhabricatorPackagesPackageQuery' => 'PhabricatorPackagesQuery',
|
||||
'PhabricatorPackagesPackageSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'PhabricatorPackagesPackageSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorPackagesPackageTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorPackagesPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorPackagesPackageTransactionType' => 'PhabricatorPackagesTransactionType',
|
||||
'PhabricatorPackagesPackageViewController' => 'PhabricatorPackagesPackageController',
|
||||
'PhabricatorPackagesPublisher' => array(
|
||||
'PhabricatorPackagesDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
),
|
||||
'PhabricatorPackagesPublisherController' => 'PhabricatorPackagesController',
|
||||
'PhabricatorPackagesPublisherDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorPackagesPublisherDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorPackagesPublisherEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'PhabricatorPackagesPublisherEditController' => 'PhabricatorPackagesPublisherController',
|
||||
'PhabricatorPackagesPublisherEditEngine' => 'PhabricatorPackagesEditEngine',
|
||||
'PhabricatorPackagesPublisherEditor' => 'PhabricatorPackagesEditor',
|
||||
'PhabricatorPackagesPublisherKeyTransaction' => 'PhabricatorPackagesPublisherTransactionType',
|
||||
'PhabricatorPackagesPublisherListController' => 'PhabricatorPackagesPublisherController',
|
||||
'PhabricatorPackagesPublisherListView' => 'PhabricatorPackagesView',
|
||||
'PhabricatorPackagesPublisherNameNgrams' => 'PhabricatorPackagesNgrams',
|
||||
'PhabricatorPackagesPublisherNameTransaction' => 'PhabricatorPackagesPublisherTransactionType',
|
||||
'PhabricatorPackagesPublisherPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorPackagesPublisherQuery' => 'PhabricatorPackagesQuery',
|
||||
'PhabricatorPackagesPublisherSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'PhabricatorPackagesPublisherSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorPackagesPublisherTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorPackagesPublisherTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorPackagesPublisherTransactionType' => 'PhabricatorPackagesTransactionType',
|
||||
'PhabricatorPackagesPublisherViewController' => 'PhabricatorPackagesPublisherController',
|
||||
'PhabricatorPackagesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorPackagesSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorPackagesTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorPackagesVersion' => array(
|
||||
'PhabricatorPackagesDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
),
|
||||
'PhabricatorPackagesVersionController' => 'PhabricatorPackagesController',
|
||||
'PhabricatorPackagesVersionEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||
'PhabricatorPackagesVersionEditController' => 'PhabricatorPackagesVersionController',
|
||||
'PhabricatorPackagesVersionEditEngine' => 'PhabricatorPackagesEditEngine',
|
||||
'PhabricatorPackagesVersionEditor' => 'PhabricatorPackagesEditor',
|
||||
'PhabricatorPackagesVersionListController' => 'PhabricatorPackagesVersionController',
|
||||
'PhabricatorPackagesVersionListView' => 'PhabricatorPackagesView',
|
||||
'PhabricatorPackagesVersionNameNgrams' => 'PhabricatorPackagesNgrams',
|
||||
'PhabricatorPackagesVersionNameTransaction' => 'PhabricatorPackagesVersionTransactionType',
|
||||
'PhabricatorPackagesVersionPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorPackagesVersionPackageTransaction' => 'PhabricatorPackagesVersionTransactionType',
|
||||
'PhabricatorPackagesVersionQuery' => 'PhabricatorPackagesQuery',
|
||||
'PhabricatorPackagesVersionSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'PhabricatorPackagesVersionSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorPackagesVersionTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorPackagesVersionTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorPackagesVersionTransactionType' => 'PhabricatorPackagesTransactionType',
|
||||
'PhabricatorPackagesVersionViewController' => 'PhabricatorPackagesVersionController',
|
||||
'PhabricatorPackagesView' => 'AphrontView',
|
||||
'PhabricatorPagerUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorPassphraseApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPasswordAuthProvider' => 'PhabricatorAuthProvider',
|
||||
|
@ -7737,7 +7953,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleCalendarController' => 'PhabricatorPeopleProfileController',
|
||||
'PhabricatorPeopleController' => 'PhabricatorController',
|
||||
'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
|
|
|
@ -3,13 +3,30 @@
|
|||
final class AphrontEpochHTTPParameterType
|
||||
extends AphrontHTTPParameterType {
|
||||
|
||||
private $allowNull;
|
||||
|
||||
public function setAllowNull($allow_null) {
|
||||
$this->allowNull = $allow_null;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowNull() {
|
||||
return $this->allowNull;
|
||||
}
|
||||
|
||||
protected function getParameterExists(AphrontRequest $request, $key) {
|
||||
return $request->getExists($key) ||
|
||||
$request->getExists($key.'_d');
|
||||
}
|
||||
|
||||
protected function getParameterValue(AphrontRequest $request, $key) {
|
||||
return AphrontFormDateControlValue::newFromRequest($request, $key);
|
||||
$value = AphrontFormDateControlValue::newFromRequest($request, $key);
|
||||
|
||||
if ($this->getAllowNull()) {
|
||||
$value->setOptional(true);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getParameterTypeName() {
|
||||
|
|
|
@ -132,7 +132,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
|||
'the authenticator correctly:'));
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
id(new PHUIFormNumberControl())
|
||||
->setLabel(pht('TOTP Code'))
|
||||
->setName('totpcode')
|
||||
->setValue($code)
|
||||
|
@ -151,7 +151,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
|||
}
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
id(new PHUIFormNumberControl())
|
||||
->setName($this->getParameterName($config, 'totpcode'))
|
||||
->setLabel(pht('App Code'))
|
||||
->setCaption(pht('Factor Name: %s', $config->getFactorName()))
|
||||
|
|
|
@ -40,25 +40,21 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
|
|||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/E(?P<id>[1-9]\d*)(?:/(?P<sequence>\d+))?'
|
||||
'/E(?P<id>[1-9]\d*)(?:/(?P<sequence>\d+)/)?'
|
||||
=> 'PhabricatorCalendarEventViewController',
|
||||
'/calendar/' => array(
|
||||
'(?:query/(?P<queryKey>[^/]+)/(?:(?P<year>\d+)/'.
|
||||
'(?P<month>\d+)/)?(?:(?P<day>\d+)/)?)?'
|
||||
=> 'PhabricatorCalendarEventListController',
|
||||
'event/' => array(
|
||||
'create/'
|
||||
=> 'PhabricatorCalendarEventEditController',
|
||||
'edit/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'PhabricatorCalendarEventEditController',
|
||||
'drag/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarEventDragController',
|
||||
'cancel/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
|
||||
'cancel/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarEventCancelController',
|
||||
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorCalendarEventJoinController',
|
||||
'comment/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
|
||||
=> 'PhabricatorCalendarEventCommentController',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -87,4 +83,19 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
|
|||
);
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
PhabricatorCalendarEventDefaultViewCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default view policy for newly created events.'),
|
||||
'template' => PhabricatorCalendarEventPHIDType::TYPECONST,
|
||||
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
|
||||
),
|
||||
PhabricatorCalendarEventDefaultEditCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default edit policy for newly created events.'),
|
||||
'template' => PhabricatorCalendarEventPHIDType::TYPECONST,
|
||||
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventDefaultEditCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'calendar.event.default.edit';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Edit Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventDefaultViewCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'calendar.event.default.view';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default View Policy');
|
||||
}
|
||||
|
||||
public function shouldAllowPublicPolicySetting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -44,32 +44,24 @@ final class PhabricatorCalendarEventRSVPEmailCommand
|
|||
PhabricatorMetaMTAReceivedMail $mail,
|
||||
$command,
|
||||
array $argv) {
|
||||
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
|
||||
$status_declined = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
|
||||
$xactions = array();
|
||||
|
||||
$target = phutil_utf8_strtolower(implode(' ', $argv));
|
||||
$rsvp = null;
|
||||
|
||||
$yes_values = $this->getYesValues();
|
||||
$no_values = $this->getNoValues();
|
||||
|
||||
if (in_array($target, $yes_values)) {
|
||||
$rsvp = $status_attending;
|
||||
$rsvp = PhabricatorCalendarEventAcceptTransaction::TRANSACTIONTYPE;
|
||||
} else if (in_array($target, $no_values)) {
|
||||
$rsvp = $status_declined;
|
||||
$rsvp = PhabricatorCalendarEventDeclineTransaction::TRANSACTIONTYPE;
|
||||
} else {
|
||||
$rsvp = null;
|
||||
}
|
||||
|
||||
if ($rsvp === null) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = $object->getApplicationTransactionTemplate()
|
||||
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE)
|
||||
->setNewValue(array($viewer->getPHID() => $rsvp));
|
||||
|
||||
->setTransactionType($rsvp)
|
||||
->setNewValue(true);
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventEditConduitAPIMethod
|
||||
extends PhabricatorEditEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'calendar.event.edit';
|
||||
}
|
||||
|
||||
public function newEditEngine() {
|
||||
return new PhabricatorCalendarEventEditEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht(
|
||||
'Apply transactions to create a new event or edit an existing one.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'calendar.event.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new PhabricatorCalendarEventSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Read information about events.');
|
||||
}
|
||||
|
||||
}
|
|
@ -2,77 +2,4 @@
|
|||
|
||||
abstract class PhabricatorCalendarController extends PhabricatorController {
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$actions = id(new PhabricatorActionListView())
|
||||
->setUser($this->getViewer())
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Create Event'))
|
||||
->setHref('/calendar/event/create/'))
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Create Public Event'))
|
||||
->setHref('/calendar/event/create/?mode=public'))
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Create Recurring Event'))
|
||||
->setHref('/calendar/event/create/?mode=recurring'));
|
||||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Event'))
|
||||
->setHref($this->getApplicationURI().'event/create/')
|
||||
->setIcon('fa-plus-square')
|
||||
->setDropdownMenu($actions));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
protected function getEventAtIndexForGhostPHID($viewer, $phid, $index) {
|
||||
$result = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withInstanceSequencePairs(
|
||||
array(
|
||||
array(
|
||||
$phid,
|
||||
$index,
|
||||
),
|
||||
))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function createEventFromGhost($viewer, $event, $index) {
|
||||
$invitees = $event->getInvitees();
|
||||
|
||||
$new_ghost = $event->generateNthGhost($index, $viewer);
|
||||
$new_ghost->attachParentEvent($event);
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$new_ghost
|
||||
->setID(null)
|
||||
->setPHID(null)
|
||||
->removeViewerTimezone($viewer)
|
||||
->setViewPolicy($event->getViewPolicy())
|
||||
->setEditPolicy($event->getEditPolicy())
|
||||
->save();
|
||||
$ghost_invitees = array();
|
||||
foreach ($invitees as $invitee) {
|
||||
$ghost_invitee = clone $invitee;
|
||||
$ghost_invitee
|
||||
->setID(null)
|
||||
->setEventPHID($new_ghost->getPHID())
|
||||
->save();
|
||||
}
|
||||
unset($unguarded);
|
||||
return $new_ghost;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ final class PhabricatorCalendarEventCancelController
|
|||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
$sequence = $request->getURIData('sequence');
|
||||
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
|
@ -17,45 +16,29 @@ final class PhabricatorCalendarEventCancelController
|
|||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
|
||||
if ($sequence) {
|
||||
$parent_event = $event;
|
||||
$event = $parent_event->generateNthGhost($sequence, $viewer);
|
||||
$event->attachParentEvent($parent_event);
|
||||
}
|
||||
|
||||
if (!$event) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if (!$sequence) {
|
||||
$cancel_uri = '/E'.$event->getID();
|
||||
} else {
|
||||
$cancel_uri = '/E'.$event->getID().'/'.$sequence;
|
||||
}
|
||||
$cancel_uri = $event->getURI();
|
||||
|
||||
$is_parent = $event->isParentEvent();
|
||||
$is_child = $event->isChildEvent();
|
||||
$is_cancelled = $event->getIsCancelled();
|
||||
$is_parent_cancelled = $event->getIsParentCancelled();
|
||||
$is_parent = $event->getIsRecurrenceParent();
|
||||
|
||||
if ($is_child) {
|
||||
$is_parent_cancelled = $event->getParentEvent()->getIsCancelled();
|
||||
} else {
|
||||
$is_parent_cancelled = false;
|
||||
}
|
||||
|
||||
$validation_exception = null;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
if ($is_cancelled && $sequence) {
|
||||
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
|
||||
} else if ($sequence) {
|
||||
$event = $this->createEventFromGhost(
|
||||
$viewer,
|
||||
$event,
|
||||
$sequence);
|
||||
$event->applyViewerTimezone($viewer);
|
||||
}
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$xaction = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_CANCEL)
|
||||
PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(!$is_cancelled);
|
||||
|
||||
$editor = id(new PhabricatorCalendarEventEditor())
|
||||
|
@ -73,43 +56,47 @@ final class PhabricatorCalendarEventCancelController
|
|||
}
|
||||
|
||||
if ($is_cancelled) {
|
||||
if ($sequence || $is_parent_cancelled) {
|
||||
if ($is_parent_cancelled) {
|
||||
$title = pht('Cannot Reinstate Instance');
|
||||
$paragraph = pht(
|
||||
'Cannot reinstate an instance of a cancelled recurring event.');
|
||||
$cancel = pht('Cancel');
|
||||
'You cannot reinstate an instance of a cancelled recurring event.');
|
||||
$cancel = pht('Back');
|
||||
$submit = null;
|
||||
} else if ($is_parent) {
|
||||
$title = pht('Reinstate Recurrence');
|
||||
} else if ($is_child) {
|
||||
$title = pht('Reinstate Instance');
|
||||
$paragraph = pht(
|
||||
'Reinstate all instances of this recurrence
|
||||
that have not been individually cancelled?');
|
||||
$cancel = pht("Don't Reinstate Recurrence");
|
||||
$submit = pht('Reinstate Recurrence');
|
||||
'Reinstate this instance of this recurring event?');
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Reinstate Instance');
|
||||
} else if ($is_parent) {
|
||||
$title = pht('Reinstate Recurring Event');
|
||||
$paragraph = pht(
|
||||
'Reinstate all instances of this recurring event which have not '.
|
||||
'been individually cancelled?');
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Reinstate Recurring Event');
|
||||
} else {
|
||||
$title = pht('Reinstate Event');
|
||||
$paragraph = pht('Reinstate this event?');
|
||||
$cancel = pht("Don't Reinstate Event");
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Reinstate Event');
|
||||
}
|
||||
} else {
|
||||
if ($sequence) {
|
||||
if ($is_child) {
|
||||
$title = pht('Cancel Instance');
|
||||
$paragraph = pht(
|
||||
'Cancel just this instance of a recurring event.');
|
||||
$cancel = pht("Don't Cancel Instance");
|
||||
$paragraph = pht('Cancel this instance of this recurring event?');
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Cancel Instance');
|
||||
} else if ($is_parent) {
|
||||
$title = pht('Cancel Recurrence');
|
||||
$paragraph = pht(
|
||||
'Cancel the entire series of recurring events?');
|
||||
$cancel = pht("Don't Cancel Recurrence");
|
||||
$submit = pht('Cancel Recurrence');
|
||||
$title = pht('Cancel Recurrin Event');
|
||||
$paragraph = pht('Cancel this entire series of recurring events?');
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Cancel Recurring Event');
|
||||
} else {
|
||||
$title = pht('Cancel Event');
|
||||
$paragraph = pht(
|
||||
'You can always reinstate the event later.');
|
||||
$cancel = pht("Don't Cancel Event");
|
||||
'Cancel this event? You can always reinstate the event later.');
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Cancel Event');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventCommentController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
if (!$request->isFormPost()) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
|
||||
$is_preview = $request->isPreviewRequest();
|
||||
$draft = PhabricatorDraft::buildFromRequest($request);
|
||||
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$event) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$index = $request->getURIData('sequence');
|
||||
if ($index && !$is_preview) {
|
||||
$result = $this->getEventAtIndexForGhostPHID(
|
||||
$viewer,
|
||||
$event->getPHID(),
|
||||
$index);
|
||||
|
||||
if ($result) {
|
||||
$event = $result;
|
||||
} else {
|
||||
$event = $this->createEventFromGhost(
|
||||
$viewer,
|
||||
$event,
|
||||
$index);
|
||||
$event->applyViewerTimezone($viewer);
|
||||
}
|
||||
}
|
||||
|
||||
$view_uri = '/'.$event->getMonogram();
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
->attachComment(
|
||||
id(new PhabricatorCalendarEventTransactionComment())
|
||||
->setContent($request->getStr('comment')));
|
||||
|
||||
$editor = id(new PhabricatorCalendarEventEditor())
|
||||
->setActor($viewer)
|
||||
->setContinueOnNoEffect($request->isContinueRequest())
|
||||
->setContentSourceFromRequest($request)
|
||||
->setIsPreview($is_preview);
|
||||
|
||||
try {
|
||||
$xactions = $editor->applyTransactions($event, $xactions);
|
||||
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
||||
->setCancelURI($view_uri)
|
||||
->setException($ex);
|
||||
}
|
||||
|
||||
if ($draft) {
|
||||
$draft->replaceOrDelete();
|
||||
}
|
||||
|
||||
if ($request->isAjax() && $is_preview) {
|
||||
return id(new PhabricatorApplicationTransactionResponse())
|
||||
->setViewer($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setIsPreview($is_preview);
|
||||
} else {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($view_uri);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@ final class PhabricatorCalendarEventDragController
|
|||
|
||||
$xactions = array();
|
||||
|
||||
$duration = $event->getDateTo() - $event->getDateFrom();
|
||||
$duration = $event->getDuration();
|
||||
|
||||
$start = $request->getInt('start');
|
||||
$start_value = id(AphrontFormDateControlValue::newFromEpoch(
|
||||
|
@ -41,16 +41,16 @@ final class PhabricatorCalendarEventDragController
|
|||
$viewer,
|
||||
$end));
|
||||
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_START_DATE)
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($start_value);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_END_DATE)
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($end_value);
|
||||
|
||||
|
||||
$editor = id(new PhabricatorCalendarEventEditor())
|
||||
->setActor($viewer)
|
||||
->setContinueOnMissingFields(true)
|
||||
|
|
|
@ -3,633 +3,10 @@
|
|||
final class PhabricatorCalendarEventEditController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
private $id;
|
||||
|
||||
public function isCreate() {
|
||||
return !$this->id;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$user_phid = $viewer->getPHID();
|
||||
$this->id = $request->getURIData('id');
|
||||
|
||||
$error_name = true;
|
||||
$error_recurrence_end_date = null;
|
||||
$error_start_date = true;
|
||||
$error_end_date = true;
|
||||
$validation_exception = null;
|
||||
|
||||
$is_recurring_id = celerity_generate_unique_node_id();
|
||||
$recurrence_end_date_id = celerity_generate_unique_node_id();
|
||||
$frequency_id = celerity_generate_unique_node_id();
|
||||
$all_day_id = celerity_generate_unique_node_id();
|
||||
$start_date_id = celerity_generate_unique_node_id();
|
||||
$end_date_id = celerity_generate_unique_node_id();
|
||||
|
||||
$next_workflow = $request->getStr('next');
|
||||
$uri_query = $request->getStr('query');
|
||||
|
||||
if ($this->isCreate()) {
|
||||
$mode = $request->getStr('mode');
|
||||
$event = PhabricatorCalendarEvent::initializeNewCalendarEvent(
|
||||
$viewer,
|
||||
$mode);
|
||||
|
||||
$create_start_year = $request->getInt('year');
|
||||
$create_start_month = $request->getInt('month');
|
||||
$create_start_day = $request->getInt('day');
|
||||
$create_start_time = $request->getStr('time');
|
||||
|
||||
if ($create_start_year) {
|
||||
$start = AphrontFormDateControlValue::newFromParts(
|
||||
$viewer,
|
||||
$create_start_year,
|
||||
$create_start_month,
|
||||
$create_start_day,
|
||||
$create_start_time);
|
||||
if (!$start->isValid()) {
|
||||
return new Aphront400Response();
|
||||
}
|
||||
$start_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$start->getEpoch());
|
||||
|
||||
$end = clone $start_value->getDateTime();
|
||||
$end->modify('+1 hour');
|
||||
$end_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$end->format('U'));
|
||||
|
||||
} else {
|
||||
list($start_value, $end_value) = $this->getDefaultTimeValues($viewer);
|
||||
}
|
||||
|
||||
$recurrence_end_date_value = clone $end_value;
|
||||
$recurrence_end_date_value->setOptional(true);
|
||||
|
||||
$submit_label = pht('Create');
|
||||
$title = pht('Create Event');
|
||||
$header_icon = 'fa-plus-square';
|
||||
$redirect = 'created';
|
||||
$subscribers = array();
|
||||
$invitees = array($user_phid);
|
||||
$cancel_uri = $this->getApplicationURI();
|
||||
} else {
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
|
||||
if (!$event) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if ($request->getURIData('sequence')) {
|
||||
$index = $request->getURIData('sequence');
|
||||
|
||||
$result = $this->getEventAtIndexForGhostPHID(
|
||||
$viewer,
|
||||
$event->getPHID(),
|
||||
$index);
|
||||
|
||||
if ($result) {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/calendar/event/edit/'.$result->getID().'/');
|
||||
}
|
||||
|
||||
$event = $this->createEventFromGhost(
|
||||
$viewer,
|
||||
$event,
|
||||
$index);
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/calendar/event/edit/'.$event->getID().'/');
|
||||
}
|
||||
|
||||
$end_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$event->getDateTo());
|
||||
$start_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$event->getDateFrom());
|
||||
$recurrence_end_date_value = id(clone $end_value)
|
||||
->setOptional(true);
|
||||
|
||||
$submit_label = pht('Update');
|
||||
$title = pht('Edit Event: %s', $event->getName());
|
||||
$header_icon = 'fa-pencil';
|
||||
|
||||
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||
$event->getPHID());
|
||||
|
||||
$invitees = array();
|
||||
foreach ($event->getInvitees() as $invitee) {
|
||||
if ($invitee->isUninvited()) {
|
||||
continue;
|
||||
} else {
|
||||
$invitees[] = $invitee->getInviteePHID();
|
||||
}
|
||||
}
|
||||
|
||||
$cancel_uri = '/'.$event->getMonogram();
|
||||
}
|
||||
|
||||
if ($this->isCreate()) {
|
||||
$projects = array();
|
||||
} else {
|
||||
$projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$event->getPHID(),
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
$projects = array_reverse($projects);
|
||||
}
|
||||
|
||||
$name = $event->getName();
|
||||
$description = $event->getDescription();
|
||||
$is_all_day = $event->getIsAllDay();
|
||||
$is_recurring = $event->getIsRecurring();
|
||||
$is_parent = $event->getIsRecurrenceParent();
|
||||
$frequency = idx($event->getRecurrenceFrequency(), 'rule');
|
||||
$icon = $event->getIcon();
|
||||
$edit_policy = $event->getEditPolicy();
|
||||
$view_policy = $event->getViewPolicy();
|
||||
$space = $event->getSpacePHID();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$xactions = array();
|
||||
$name = $request->getStr('name');
|
||||
|
||||
$start_value = AphrontFormDateControlValue::newFromRequest(
|
||||
$request,
|
||||
'start');
|
||||
$end_value = AphrontFormDateControlValue::newFromRequest(
|
||||
$request,
|
||||
'end');
|
||||
$recurrence_end_date_value = AphrontFormDateControlValue::newFromRequest(
|
||||
$request,
|
||||
'recurrenceEndDate');
|
||||
$recurrence_end_date_value->setOptional(true);
|
||||
$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');
|
||||
$is_recurring = $request->getStr('isRecurring') ? 1 : 0;
|
||||
$frequency = $request->getStr('frequency');
|
||||
$is_all_day = $request->getStr('isAllDay');
|
||||
$icon = $request->getStr('icon');
|
||||
|
||||
$invitees = $request->getArr('invitees');
|
||||
$new_invitees = $this->getNewInviteeList($invitees, $event);
|
||||
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
|
||||
if ($this->isCreate()) {
|
||||
$status = idx($new_invitees, $viewer->getPHID());
|
||||
if ($status) {
|
||||
$new_invitees[$viewer->getPHID()] = $status_attending;
|
||||
}
|
||||
}
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_NAME)
|
||||
->setNewValue($name);
|
||||
|
||||
if ($is_recurring && $this->isCreate()) {
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_RECURRING)
|
||||
->setNewValue($is_recurring);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_FREQUENCY)
|
||||
->setNewValue(array('rule' => $frequency));
|
||||
|
||||
if (!$recurrence_end_date_value->isDisabled()) {
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE)
|
||||
->setNewValue($recurrence_end_date_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (($is_recurring && $this->isCreate()) || !$is_parent) {
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_ALL_DAY)
|
||||
->setNewValue($is_all_day);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_ICON)
|
||||
->setNewValue($icon);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_START_DATE)
|
||||
->setNewValue($start_value);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_END_DATE)
|
||||
->setNewValue($end_value);
|
||||
}
|
||||
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorTransactions::TYPE_SUBSCRIBERS)
|
||||
->setNewValue(array('=' => array_fuse($subscribers)));
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_INVITE)
|
||||
->setNewValue($new_invitees);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION)
|
||||
->setNewValue($description);
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
|
||||
->setNewValue($request->getStr('viewPolicy'));
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||
->setNewValue($request->getStr('editPolicy'));
|
||||
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
|
||||
->setNewValue($space);
|
||||
|
||||
$editor = id(new PhabricatorCalendarEventEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
try {
|
||||
$proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
$xactions[] = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $proj_edge_type)
|
||||
->setNewValue(array('=' => array_fuse($projects)));
|
||||
|
||||
$xactions = $editor->applyTransactions($event, $xactions);
|
||||
$response = id(new AphrontRedirectResponse());
|
||||
switch ($next_workflow) {
|
||||
case 'day':
|
||||
if (!$uri_query) {
|
||||
$uri_query = 'month';
|
||||
}
|
||||
$year = $start_value->getDateTime()->format('Y');
|
||||
$month = $start_value->getDateTime()->format('m');
|
||||
$day = $start_value->getDateTime()->format('d');
|
||||
$response->setURI(
|
||||
'/calendar/query/'.$uri_query.'/'.$year.'/'.$month.'/'.$day.'/');
|
||||
break;
|
||||
default:
|
||||
$response->setURI('/E'.$event->getID());
|
||||
break;
|
||||
}
|
||||
return $response;
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
$error_name = $ex->getShortMessage(
|
||||
PhabricatorCalendarEventTransaction::TYPE_NAME);
|
||||
$error_start_date = $ex->getShortMessage(
|
||||
PhabricatorCalendarEventTransaction::TYPE_START_DATE);
|
||||
$error_end_date = $ex->getShortMessage(
|
||||
PhabricatorCalendarEventTransaction::TYPE_END_DATE);
|
||||
$error_recurrence_end_date = $ex->getShortMessage(
|
||||
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE);
|
||||
}
|
||||
}
|
||||
|
||||
$is_recurring_checkbox = null;
|
||||
$recurrence_end_date_control = null;
|
||||
$recurrence_frequency_select = null;
|
||||
|
||||
$all_day_checkbox = null;
|
||||
$start_control = null;
|
||||
$end_control = null;
|
||||
|
||||
$recurring_date_edit_label = null;
|
||||
|
||||
$current_policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($event)
|
||||
->execute();
|
||||
|
||||
$name = id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Name'))
|
||||
->setName('name')
|
||||
->setValue($name)
|
||||
->setError($error_name);
|
||||
|
||||
if ($this->isCreate()) {
|
||||
Javelin::initBehavior('recurring-edit', array(
|
||||
'isRecurring' => $is_recurring_id,
|
||||
'frequency' => $frequency_id,
|
||||
'recurrenceEndDate' => $recurrence_end_date_id,
|
||||
));
|
||||
|
||||
$is_recurring_checkbox = id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'isRecurring',
|
||||
1,
|
||||
pht('Recurring Event'),
|
||||
$is_recurring,
|
||||
$is_recurring_id);
|
||||
|
||||
$recurrence_end_date_control = id(new AphrontFormDateControl())
|
||||
->setUser($viewer)
|
||||
->setName('recurrenceEndDate')
|
||||
->setLabel(pht('Recurrence End Date'))
|
||||
->setError($error_recurrence_end_date)
|
||||
->setValue($recurrence_end_date_value)
|
||||
->setID($recurrence_end_date_id)
|
||||
->setIsTimeDisabled(true)
|
||||
->setIsDisabled($recurrence_end_date_value->isDisabled())
|
||||
->setAllowNull(true);
|
||||
|
||||
$recurrence_frequency_select = id(new AphrontFormSelectControl())
|
||||
->setName('frequency')
|
||||
->setOptions(array(
|
||||
PhabricatorCalendarEvent::FREQUENCY_DAILY => pht('Daily'),
|
||||
PhabricatorCalendarEvent::FREQUENCY_WEEKLY => pht('Weekly'),
|
||||
PhabricatorCalendarEvent::FREQUENCY_MONTHLY => pht('Monthly'),
|
||||
PhabricatorCalendarEvent::FREQUENCY_YEARLY => pht('Yearly'),
|
||||
))
|
||||
->setValue($frequency)
|
||||
->setLabel(pht('Recurring Event Frequency'))
|
||||
->setID($frequency_id)
|
||||
->setDisabled(!$is_recurring);
|
||||
}
|
||||
|
||||
if ($this->isCreate() || (!$is_parent && !$this->isCreate())) {
|
||||
Javelin::initBehavior('event-all-day', array(
|
||||
'allDayID' => $all_day_id,
|
||||
'startDateID' => $start_date_id,
|
||||
'endDateID' => $end_date_id,
|
||||
));
|
||||
|
||||
$all_day_checkbox = id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'isAllDay',
|
||||
1,
|
||||
pht('All Day Event'),
|
||||
$is_all_day,
|
||||
$all_day_id);
|
||||
|
||||
$start_control = id(new AphrontFormDateControl())
|
||||
->setUser($viewer)
|
||||
->setName('start')
|
||||
->setLabel(pht('Start'))
|
||||
->setError($error_start_date)
|
||||
->setValue($start_value)
|
||||
->setID($start_date_id)
|
||||
->setIsTimeDisabled($is_all_day)
|
||||
->setEndDateID($end_date_id);
|
||||
|
||||
$end_control = id(new AphrontFormDateControl())
|
||||
->setUser($viewer)
|
||||
->setName('end')
|
||||
->setLabel(pht('End'))
|
||||
->setError($error_end_date)
|
||||
->setValue($end_value)
|
||||
->setID($end_date_id)
|
||||
->setIsTimeDisabled($is_all_day);
|
||||
} else if ($is_parent) {
|
||||
$recurring_date_edit_label = id(new AphrontFormStaticControl())
|
||||
->setUser($viewer)
|
||||
->setValue(pht('Date and time of recurring event cannot be edited.'));
|
||||
|
||||
if (!$recurrence_end_date_value->isDisabled()) {
|
||||
$disabled_recurrence_end_date_value =
|
||||
$recurrence_end_date_value->getValueAsFormat('M d, Y');
|
||||
$recurrence_end_date_control = id(new AphrontFormStaticControl())
|
||||
->setUser($viewer)
|
||||
->setLabel(pht('Recurrence End Date'))
|
||||
->setValue($disabled_recurrence_end_date_value)
|
||||
->setDisabled(true);
|
||||
}
|
||||
|
||||
$recurrence_frequency_select = id(new AphrontFormSelectControl())
|
||||
->setName('frequency')
|
||||
->setOptions(array(
|
||||
'daily' => pht('Daily'),
|
||||
'weekly' => pht('Weekly'),
|
||||
'monthly' => pht('Monthly'),
|
||||
'yearly' => pht('Yearly'),
|
||||
))
|
||||
->setValue($frequency)
|
||||
->setLabel(pht('Recurring Event Frequency'))
|
||||
->setID($frequency_id)
|
||||
->setDisabled(true);
|
||||
|
||||
$all_day_checkbox = id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'isAllDay',
|
||||
1,
|
||||
pht('All Day Event'),
|
||||
$is_all_day,
|
||||
$all_day_id)
|
||||
->setDisabled(true);
|
||||
|
||||
$start_disabled = $start_value->getValueAsFormat('M d, Y, g:i A');
|
||||
$end_disabled = $end_value->getValueAsFormat('M d, Y, g:i A');
|
||||
|
||||
$start_control = id(new AphrontFormStaticControl())
|
||||
->setUser($viewer)
|
||||
->setLabel(pht('Start'))
|
||||
->setValue($start_disabled)
|
||||
->setDisabled(true);
|
||||
|
||||
$end_control = id(new AphrontFormStaticControl())
|
||||
->setUser($viewer)
|
||||
->setLabel(pht('End'))
|
||||
->setValue($end_disabled);
|
||||
}
|
||||
|
||||
$projects = id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Tags'))
|
||||
->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($event)
|
||||
->setPolicies($current_policies)
|
||||
->setSpacePHID($space)
|
||||
->setName('viewPolicy');
|
||||
$edit_policies = id(new AphrontFormPolicyControl())
|
||||
->setUser($viewer)
|
||||
->setValue($edit_policy)
|
||||
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
|
||||
->setPolicyObject($event)
|
||||
->setPolicies($current_policies)
|
||||
->setName('editPolicy');
|
||||
|
||||
$subscribers = id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Subscribers'))
|
||||
->setName('subscribers')
|
||||
->setValue($subscribers)
|
||||
->setUser($viewer)
|
||||
->setDatasource(new PhabricatorMetaMTAMailableDatasource());
|
||||
|
||||
$invitees = id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Invitees'))
|
||||
->setName('invitees')
|
||||
->setValue($invitees)
|
||||
->setUser($viewer)
|
||||
->setDatasource(new PhabricatorMetaMTAMailableDatasource());
|
||||
|
||||
|
||||
$icon = id(new PHUIFormIconSetControl())
|
||||
->setLabel(pht('Icon'))
|
||||
->setName('icon')
|
||||
->setIconSet(new PhabricatorCalendarIconSet())
|
||||
->setValue($icon);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->addHiddenInput('next', $next_workflow)
|
||||
->addHiddenInput('query', $uri_query)
|
||||
->setUser($viewer)
|
||||
->appendChild($name);
|
||||
|
||||
if ($recurring_date_edit_label) {
|
||||
$form->appendControl($recurring_date_edit_label);
|
||||
}
|
||||
if ($is_recurring_checkbox) {
|
||||
$form->appendChild($is_recurring_checkbox);
|
||||
}
|
||||
if ($recurrence_end_date_control) {
|
||||
$form->appendChild($recurrence_end_date_control);
|
||||
}
|
||||
if ($recurrence_frequency_select) {
|
||||
$form->appendControl($recurrence_frequency_select);
|
||||
}
|
||||
|
||||
$form
|
||||
->appendChild($all_day_checkbox)
|
||||
->appendChild($start_control)
|
||||
->appendChild($end_control)
|
||||
->appendControl($view_policies)
|
||||
->appendControl($edit_policies)
|
||||
->appendControl($subscribers)
|
||||
->appendControl($invitees)
|
||||
->appendChild($projects)
|
||||
->appendChild($description)
|
||||
->appendChild($icon);
|
||||
|
||||
|
||||
if ($request->isAjax()) {
|
||||
return $this->newDialog()
|
||||
->setTitle($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(pht('Event'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setValidationException($validation_exception)
|
||||
->setForm($form);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
|
||||
if (!$this->isCreate()) {
|
||||
$crumbs->addTextCrumb('E'.$event->getId(), '/E'.$event->getId());
|
||||
$crumb_title = pht('Edit Event');
|
||||
} else {
|
||||
$crumb_title = pht('Create Event');
|
||||
}
|
||||
|
||||
$crumbs->addTextCrumb($crumb_title);
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setHeaderIcon($header_icon);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter($form_box);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
|
||||
public function getNewInviteeList(array $phids, $event) {
|
||||
$invitees = $event->getInvitees();
|
||||
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||
$invited_status = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
||||
$uninvited_status = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
|
||||
$phids = array_fuse($phids);
|
||||
|
||||
$new = array();
|
||||
foreach ($phids as $phid) {
|
||||
$old_status = $event->getUserInviteStatus($phid);
|
||||
if ($old_status != $uninvited_status) {
|
||||
continue;
|
||||
}
|
||||
$new[$phid] = $invited_status;
|
||||
}
|
||||
|
||||
foreach ($invitees as $invitee) {
|
||||
$deleted_invitee = !idx($phids, $invitee->getInviteePHID());
|
||||
if ($deleted_invitee) {
|
||||
$new[$invitee->getInviteePHID()] = $uninvited_status;
|
||||
}
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
private function getDefaultTimeValues($viewer) {
|
||||
$start = new DateTime('@'.time());
|
||||
$start->setTimeZone($viewer->getTimeZone());
|
||||
|
||||
$start->setTime($start->format('H'), 0, 0);
|
||||
$start->modify('+1 hour');
|
||||
$end = id(clone $start)->modify('+1 hour');
|
||||
|
||||
$start_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$start->format('U'));
|
||||
$end_value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
$end->format('U'));
|
||||
|
||||
return array($start_value, $end_value);
|
||||
return id(new PhabricatorCalendarEventEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,57 +3,46 @@
|
|||
final class PhabricatorCalendarEventJoinController
|
||||
extends PhabricatorCalendarController {
|
||||
|
||||
const ACTION_ACCEPT = 'accept';
|
||||
const ACTION_DECLINE = 'decline';
|
||||
const ACTION_JOIN = 'join';
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
$action = $request->getURIData('action');
|
||||
|
||||
$request = $this->getRequest();
|
||||
$viewer = $request->getViewer();
|
||||
$declined_status = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
|
||||
$attending_status = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
|
||||
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
|
||||
if (!$event) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$cancel_uri = '/E'.$event->getID();
|
||||
$validation_exception = null;
|
||||
|
||||
$is_attending = $event->getIsUserAttending($viewer->getPHID());
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$new_status = null;
|
||||
$cancel_uri = $event->getURI();
|
||||
|
||||
$action = $request->getURIData('action');
|
||||
switch ($action) {
|
||||
case self::ACTION_ACCEPT:
|
||||
$new_status = $attending_status;
|
||||
case 'accept':
|
||||
$is_join = true;
|
||||
break;
|
||||
case self::ACTION_JOIN:
|
||||
if ($is_attending) {
|
||||
$new_status = $declined_status;
|
||||
} else {
|
||||
$new_status = $attending_status;
|
||||
}
|
||||
case 'decline':
|
||||
$is_join = false;
|
||||
break;
|
||||
case self::ACTION_DECLINE:
|
||||
$new_status = $declined_status;
|
||||
default:
|
||||
$is_join = !$event->getIsUserAttending($viewer->getPHID());
|
||||
break;
|
||||
}
|
||||
|
||||
$new_status = array($viewer->getPHID() => $new_status);
|
||||
$validation_exception = null;
|
||||
if ($request->isFormPost()) {
|
||||
if ($is_join) {
|
||||
$xaction_type =
|
||||
PhabricatorCalendarEventAcceptTransaction::TRANSACTIONTYPE;
|
||||
} else {
|
||||
$xaction_type =
|
||||
PhabricatorCalendarEventDeclineTransaction::TRANSACTIONTYPE;
|
||||
}
|
||||
|
||||
$xaction = id(new PhabricatorCalendarEventTransaction())
|
||||
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE)
|
||||
->setNewValue($new_status);
|
||||
->setTransactionType($xaction_type)
|
||||
->setNewValue(true);
|
||||
|
||||
$editor = id(new PhabricatorCalendarEventEditor())
|
||||
->setActor($viewer)
|
||||
|
@ -69,8 +58,7 @@ final class PhabricatorCalendarEventJoinController
|
|||
}
|
||||
}
|
||||
|
||||
if (($action == self::ACTION_JOIN && !$is_attending)
|
||||
|| $action == self::ACTION_ACCEPT) {
|
||||
if ($is_join) {
|
||||
$title = pht('Join Event');
|
||||
$paragraph = pht('Would you like to join this event?');
|
||||
$submit = pht('Join');
|
||||
|
|
|
@ -19,24 +19,19 @@ final class PhabricatorCalendarEventListController
|
|||
|
||||
$controller = id(new PhabricatorApplicationSearchController())
|
||||
->setQueryKey($request->getURIData('queryKey'))
|
||||
->setSearchEngine($engine)
|
||||
->setNavigation($this->buildSideNav());
|
||||
->setSearchEngine($engine);
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
}
|
||||
|
||||
public function buildSideNav() {
|
||||
$user = $this->getRequest()->getUser();
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
id(new PhabricatorCalendarEventEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
id(new PhabricatorCalendarEventSearchEngine())
|
||||
->setViewer($user)
|
||||
->addNavigationItems($nav->getMenu());
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
return $nav;
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,102 +9,78 @@ final class PhabricatorCalendarEventViewController
|
|||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$id = $request->getURIData('id');
|
||||
$sequence = $request->getURIData('sequence');
|
||||
|
||||
$timeline = null;
|
||||
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
$event = $this->loadEvent();
|
||||
if (!$event) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if ($sequence) {
|
||||
$result = $this->getEventAtIndexForGhostPHID(
|
||||
$viewer,
|
||||
$event->getPHID(),
|
||||
$sequence);
|
||||
|
||||
if ($result) {
|
||||
$parent_event = $event;
|
||||
$event = $result;
|
||||
$event->attachParentEvent($parent_event);
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/E'.$result->getID());
|
||||
} else if ($sequence && $event->getIsRecurring()) {
|
||||
$parent_event = $event;
|
||||
$event = $event->generateNthGhost($sequence, $viewer);
|
||||
$event->attachParentEvent($parent_event);
|
||||
} else if ($sequence) {
|
||||
return new Aphront404Response();
|
||||
// If we looked up or generated a stub event, redirect to that event's
|
||||
// canonical URI.
|
||||
$id = $request->getURIData('id');
|
||||
if ($event->getID() != $id) {
|
||||
$uri = $event->getURI();
|
||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||
}
|
||||
|
||||
$title = $event->getMonogram().' ('.$sequence.')';
|
||||
$page_title = $title.' '.$event->getName();
|
||||
$monogram = $event->getMonogram();
|
||||
$page_title = $monogram.' '.$event->getName();
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($title, '/'.$event->getMonogram().'/'.$sequence);
|
||||
|
||||
$start = new DateTime('@'.$event->getViewerDateFrom());
|
||||
$start->setTimeZone($viewer->getTimeZone());
|
||||
|
||||
} else {
|
||||
$title = 'E'.$event->getID();
|
||||
$page_title = $title.' '.$event->getName();
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb($title);
|
||||
$crumbs->addTextCrumb(
|
||||
$start->format('F Y'),
|
||||
'/calendar/query/month/'.$start->format('Y/m/'));
|
||||
|
||||
$crumbs->addTextCrumb(
|
||||
$start->format('D jS'),
|
||||
'/calendar/query/month/'.$start->format('Y/m/d/'));
|
||||
|
||||
$crumbs->addTextCrumb($monogram);
|
||||
$crumbs->setBorder(true);
|
||||
}
|
||||
|
||||
if (!$event->getIsGhostEvent()) {
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$event,
|
||||
new PhabricatorCalendarEventTransactionQuery());
|
||||
}
|
||||
|
||||
$header = $this->buildHeaderView($event);
|
||||
$subheader = $this->buildSubheaderView($event);
|
||||
$curtain = $this->buildCurtain($event);
|
||||
$details = $this->buildPropertySection($event);
|
||||
$recurring = $this->buildRecurringSection($event);
|
||||
$description = $this->buildDescriptionView($event);
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$add_comment_header = $is_serious
|
||||
? pht('Add Comment')
|
||||
: pht('Add To Plate');
|
||||
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $event->getPHID());
|
||||
if ($sequence) {
|
||||
$comment_uri = $this->getApplicationURI(
|
||||
'/event/comment/'.$event->getID().'/'.$sequence.'/');
|
||||
} else {
|
||||
$comment_uri = $this->getApplicationURI(
|
||||
'/event/comment/'.$event->getID().'/');
|
||||
}
|
||||
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($event->getPHID())
|
||||
->setDraft($draft)
|
||||
->setHeaderText($add_comment_header)
|
||||
->setAction($comment_uri)
|
||||
->setSubmitButtonName(pht('Add Comment'));
|
||||
$comment_view = id(new PhabricatorCalendarEventEditEngine())
|
||||
->setViewer($viewer)
|
||||
->buildEditEngineCommentView($event);
|
||||
|
||||
$timeline->setQuoteRef($monogram);
|
||||
$comment_view->setTransactionTimeline($timeline);
|
||||
|
||||
$details_header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Details'));
|
||||
$recurring_header = $this->buildRecurringHeader($event);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setMainColumn(array(
|
||||
->setSubheader($subheader)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$timeline,
|
||||
$add_comment_form,
|
||||
$comment_view,
|
||||
))
|
||||
->setCurtain($curtain)
|
||||
->addPropertySection(pht('Details'), $details)
|
||||
->addPropertySection($details_header, $details)
|
||||
->addPropertySection($recurring_header, $recurring)
|
||||
->addPropertySection(pht('Description'), $description);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($page_title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($event->getPHID()))
|
||||
->appendChild(
|
||||
array(
|
||||
$view,
|
||||
));
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function buildHeaderView(
|
||||
|
@ -112,47 +88,33 @@ final class PhabricatorCalendarEventViewController
|
|||
$viewer = $this->getViewer();
|
||||
$id = $event->getID();
|
||||
|
||||
$is_cancelled = $event->getIsCancelled();
|
||||
$icon = $is_cancelled ? ('fa-ban') : ('fa-check');
|
||||
$color = $is_cancelled ? ('red') : ('bluegrey');
|
||||
$status = $is_cancelled ? pht('Cancelled') : pht('Active');
|
||||
|
||||
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
|
||||
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
||||
$is_invite_pending = ($invite_status == $status_invited);
|
||||
if ($event->isCancelledEvent()) {
|
||||
$icon = 'fa-ban';
|
||||
$color = 'red';
|
||||
$status = pht('Cancelled');
|
||||
} else {
|
||||
$icon = 'fa-check';
|
||||
$color = 'bluegrey';
|
||||
$status = pht('Active');
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setUser($viewer)
|
||||
->setHeader($event->getName())
|
||||
->setStatus($icon, $color, $status)
|
||||
->setPolicyObject($event)
|
||||
->setHeaderIcon('fa-calendar');
|
||||
->setHeaderIcon($event->getIcon());
|
||||
|
||||
if ($is_invite_pending) {
|
||||
$decline_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-times grey')
|
||||
->setHref($this->getApplicationURI("/event/decline/{$id}/"))
|
||||
->setWorkflow(true)
|
||||
->setText(pht('Decline'));
|
||||
|
||||
$accept_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-check green')
|
||||
->setHref($this->getApplicationURI("/event/accept/{$id}/"))
|
||||
->setWorkflow(true)
|
||||
->setText(pht('Accept'));
|
||||
|
||||
$header->addActionLink($decline_button)
|
||||
->addActionLink($accept_button);
|
||||
foreach ($this->buildRSVPActions($event) as $action) {
|
||||
$header->addActionLink($action);
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
private function buildCurtain(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
$id = $event->getID();
|
||||
$is_cancelled = $event->getIsCancelled();
|
||||
$is_attending = $event->getIsUserAttending($viewer->getPHID());
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
|
@ -160,19 +122,11 @@ final class PhabricatorCalendarEventViewController
|
|||
$event,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$edit_label = false;
|
||||
$edit_uri = false;
|
||||
|
||||
if ($event->getIsGhostEvent()) {
|
||||
$index = $event->getSequenceIndex();
|
||||
$edit_label = pht('Edit This Instance');
|
||||
$edit_uri = "event/edit/{$id}/{$index}/";
|
||||
} else if ($event->getIsRecurrenceException()) {
|
||||
$edit_label = pht('Edit This Instance');
|
||||
$edit_uri = "event/edit/{$id}/";
|
||||
if ($event->isChildEvent()) {
|
||||
$edit_label = pht('Edit This Instance');
|
||||
} else {
|
||||
$edit_label = pht('Edit');
|
||||
$edit_uri = "event/edit/{$id}/";
|
||||
$edit_label = pht('Edit Event');
|
||||
}
|
||||
|
||||
$curtain = $this->newCurtainView($event);
|
||||
|
@ -204,31 +158,24 @@ final class PhabricatorCalendarEventViewController
|
|||
}
|
||||
|
||||
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/");
|
||||
$cancel_disabled = !$can_edit;
|
||||
|
||||
if ($event->getIsGhostEvent()) {
|
||||
$index = $event->getSequenceIndex();
|
||||
$can_reinstate = $event->getIsParentCancelled();
|
||||
|
||||
if ($event->isChildEvent()) {
|
||||
$cancel_label = pht('Cancel This Instance');
|
||||
$reinstate_label = pht('Reinstate This Instance');
|
||||
$cancel_disabled = (!$can_edit || $can_reinstate);
|
||||
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/{$index}/");
|
||||
} else if ($event->getIsRecurrenceException()) {
|
||||
$can_reinstate = $event->getIsParentCancelled();
|
||||
$cancel_label = pht('Cancel This Instance');
|
||||
$reinstate_label = pht('Reinstate This Instance');
|
||||
$cancel_disabled = (!$can_edit || $can_reinstate);
|
||||
} else if ($event->getIsRecurrenceParent()) {
|
||||
|
||||
if ($event->getParentEvent()->getIsCancelled()) {
|
||||
$cancel_disabled = true;
|
||||
}
|
||||
} else if ($event->isParentEvent()) {
|
||||
$cancel_label = pht('Cancel All');
|
||||
$reinstate_label = pht('Reinstate All');
|
||||
$cancel_disabled = !$can_edit;
|
||||
} else {
|
||||
$cancel_label = pht('Cancel Event');
|
||||
$reinstate_label = pht('Reinstate Event');
|
||||
$cancel_disabled = !$can_edit;
|
||||
}
|
||||
|
||||
if ($is_cancelled) {
|
||||
if ($event->isCancelledEvent()) {
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName($reinstate_label)
|
||||
|
@ -256,56 +203,6 @@ final class PhabricatorCalendarEventViewController
|
|||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer);
|
||||
|
||||
if ($event->getIsAllDay()) {
|
||||
$date_start = phabricator_date($event->getDateFrom(), $viewer);
|
||||
$date_end = phabricator_date($event->getDateTo(), $viewer);
|
||||
|
||||
if ($date_start == $date_end) {
|
||||
$properties->addProperty(
|
||||
pht('Time'),
|
||||
phabricator_date($event->getDateFrom(), $viewer));
|
||||
} else {
|
||||
$properties->addProperty(
|
||||
pht('Starts'),
|
||||
phabricator_date($event->getDateFrom(), $viewer));
|
||||
$properties->addProperty(
|
||||
pht('Ends'),
|
||||
phabricator_date($event->getDateTo(), $viewer));
|
||||
}
|
||||
} else {
|
||||
$properties->addProperty(
|
||||
pht('Starts'),
|
||||
phabricator_datetime($event->getDateFrom(), $viewer));
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Ends'),
|
||||
phabricator_datetime($event->getDateTo(), $viewer));
|
||||
}
|
||||
|
||||
if ($event->getIsRecurring()) {
|
||||
$properties->addProperty(
|
||||
pht('Recurs'),
|
||||
ucwords(idx($event->getRecurrenceFrequency(), 'rule')));
|
||||
|
||||
if ($event->getRecurrenceEndDate()) {
|
||||
$properties->addProperty(
|
||||
pht('Recurrence Ends'),
|
||||
phabricator_datetime($event->getRecurrenceEndDate(), $viewer));
|
||||
}
|
||||
|
||||
if ($event->getInstanceOfEventPHID()) {
|
||||
$properties->addProperty(
|
||||
pht('Recurrence of Event'),
|
||||
pht('%s of %s',
|
||||
$event->getSequenceIndex(),
|
||||
$viewer->renderHandle($event->getInstanceOfEventPHID())->render()));
|
||||
}
|
||||
}
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Host'),
|
||||
$viewer->renderHandle($event->getUserPHID()));
|
||||
|
||||
$invitees = $event->getInvitees();
|
||||
foreach ($invitees as $key => $invitee) {
|
||||
if ($invitee->isUninvited()) {
|
||||
|
@ -361,10 +258,120 @@ final class PhabricatorCalendarEventViewController
|
|||
|
||||
$properties->invokeWillRenderEvent();
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Icon'),
|
||||
id(new PhabricatorCalendarIconSet())
|
||||
->getIconLabel($event->getIcon()));
|
||||
return $properties;
|
||||
}
|
||||
|
||||
private function buildRecurringHeader(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if (!$event->getIsRecurring()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Recurring Event'));
|
||||
|
||||
$sequence = $event->getSequenceIndex();
|
||||
if ($event->isParentEvent()) {
|
||||
$parent = $event;
|
||||
} else {
|
||||
$parent = $event->getParentEvent();
|
||||
}
|
||||
|
||||
$next_uri = $parent->getURI().'/'.($sequence + 1);
|
||||
if ($sequence) {
|
||||
if ($sequence > 1) {
|
||||
$previous_uri = $parent->getURI().'/'.($sequence - 1);
|
||||
} else {
|
||||
$previous_uri = $parent->getURI();
|
||||
}
|
||||
$has_previous = true;
|
||||
} else {
|
||||
$has_previous = false;
|
||||
$previous_uri = null;
|
||||
}
|
||||
|
||||
$prev_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-chevron-left')
|
||||
->setHref($previous_uri)
|
||||
->setDisabled(!$has_previous)
|
||||
->setText(pht('Previous'));
|
||||
|
||||
$next_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-chevron-right')
|
||||
->setHref($next_uri)
|
||||
->setText(pht('Next'));
|
||||
|
||||
$header
|
||||
->addActionLink($next_button)
|
||||
->addActionLink($prev_button);
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
private function buildRecurringSection(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if (!$event->getIsRecurring()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$properties = id(new PHUIPropertyListView())
|
||||
->setUser($viewer);
|
||||
|
||||
$is_parent = $event->isParentEvent();
|
||||
if ($is_parent) {
|
||||
$parent_link = null;
|
||||
} else {
|
||||
$parent = $event->getParentEvent();
|
||||
$parent_link = $viewer
|
||||
->renderHandle($parent->getPHID())
|
||||
->render();
|
||||
}
|
||||
|
||||
$rule = $event->getFrequencyRule();
|
||||
switch ($rule) {
|
||||
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
|
||||
if ($is_parent) {
|
||||
$message = pht('This event repeats every day.');
|
||||
} else {
|
||||
$message = pht(
|
||||
'This event is an instance of %s, and repeats every day.',
|
||||
$parent_link);
|
||||
}
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
|
||||
if ($is_parent) {
|
||||
$message = pht('This event repeats every week.');
|
||||
} else {
|
||||
$message = pht(
|
||||
'This event is an instance of %s, and repeats every week.',
|
||||
$parent_link);
|
||||
}
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
|
||||
if ($is_parent) {
|
||||
$message = pht('This event repeats every month.');
|
||||
} else {
|
||||
$message = pht(
|
||||
'This event is an instance of %s, and repeats every month.',
|
||||
$parent_link);
|
||||
}
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
|
||||
if ($is_parent) {
|
||||
$message = pht('This event repeats every year.');
|
||||
} else {
|
||||
$message = pht(
|
||||
'This event is an instance of %s, and repeats every year.',
|
||||
$parent_link);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$properties->addProperty(pht('Event Series'), $message);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -385,4 +392,120 @@ final class PhabricatorCalendarEventViewController
|
|||
return null;
|
||||
}
|
||||
|
||||
private function loadEvent() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
$sequence = $request->getURIData('sequence');
|
||||
|
||||
// We're going to figure out which event you're trying to look at. Most of
|
||||
// the time this is simple, but you may be looking at an instance of a
|
||||
// recurring event which we haven't generated an object for.
|
||||
|
||||
// If you are, we're going to generate a "stub" event so we have a real
|
||||
// ID and PHID to work with, since the rest of the infrastructure relies
|
||||
// on these identifiers existing.
|
||||
|
||||
// Load the event identified by ID first.
|
||||
$event = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->executeOne();
|
||||
if (!$event) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we aren't looking at an instance of this event, this is a completely
|
||||
// normal request and we can just return this event.
|
||||
if (!$sequence) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
// When you view "E123/999", E123 is normally the parent event. However,
|
||||
// you might visit a different instance first instead and then fiddle
|
||||
// with the URI. If the event we're looking at is a child, we are going
|
||||
// to act on the parent instead.
|
||||
if ($event->isChildEvent()) {
|
||||
$event = $event->getParentEvent();
|
||||
}
|
||||
|
||||
// Try to load the instance. If it already exists, we're all done and
|
||||
// can just return it.
|
||||
$instance = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->withInstanceSequencePairs(
|
||||
array(
|
||||
array($event->getPHID(), $sequence),
|
||||
))
|
||||
->executeOne();
|
||||
if ($instance) {
|
||||
return $instance;
|
||||
}
|
||||
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This event instance has not been created yet. Log in to create '.
|
||||
'it.'));
|
||||
}
|
||||
|
||||
$instance = $event->newStub($viewer, $sequence);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
private function buildSubheaderView(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$host_phid = $event->getHostPHID();
|
||||
|
||||
$handles = $viewer->loadHandles(array($host_phid));
|
||||
$handle = $handles[$host_phid];
|
||||
|
||||
$host = $viewer->renderHandle($host_phid);
|
||||
$host = phutil_tag('strong', array(), $host);
|
||||
|
||||
$image_uri = $handles[$host_phid]->getImageURI();
|
||||
$image_href = $handles[$host_phid]->getURI();
|
||||
|
||||
$date = $event->renderEventDate($viewer, true);
|
||||
|
||||
$content = pht('Hosted by %s on %s.', $host, $date);
|
||||
|
||||
return id(new PHUIHeadThingView())
|
||||
->setImage($image_uri)
|
||||
->setImageHref($image_href)
|
||||
->setContent($content);
|
||||
}
|
||||
|
||||
|
||||
private function buildRSVPActions(PhabricatorCalendarEvent $event) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $event->getID();
|
||||
|
||||
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
|
||||
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
||||
$is_invite_pending = ($invite_status == $status_invited);
|
||||
if (!$is_invite_pending) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$decline_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-times grey')
|
||||
->setHref($this->getApplicationURI("/event/decline/{$id}/"))
|
||||
->setWorkflow(true)
|
||||
->setText(pht('Decline'));
|
||||
|
||||
$accept_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-check green')
|
||||
->setHref($this->getApplicationURI("/event/accept/{$id}/"))
|
||||
->setWorkflow(true)
|
||||
->setText(pht('Accept'));
|
||||
|
||||
return array($decline_button, $accept_button);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventEditEngine
|
||||
extends PhabricatorEditEngine {
|
||||
|
||||
const ENGINECONST = 'calendar.event';
|
||||
|
||||
public function getEngineName() {
|
||||
return pht('Calendar Events');
|
||||
}
|
||||
|
||||
public function getSummaryHeader() {
|
||||
return pht('Configure Calendar Event Forms');
|
||||
}
|
||||
|
||||
public function getSummaryText() {
|
||||
return pht('Configure how users create and edit events.');
|
||||
}
|
||||
|
||||
public function getEngineApplicationClass() {
|
||||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
protected function newEditableObject() {
|
||||
return PhabricatorCalendarEvent::initializeNewCalendarEvent(
|
||||
$this->getViewer());
|
||||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
return new PhabricatorCalendarEventQuery();
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
return pht('Create New Event');
|
||||
}
|
||||
|
||||
protected function getObjectEditTitleText($object) {
|
||||
return pht('Edit Event: %s', $object->getName());
|
||||
}
|
||||
|
||||
protected function getObjectEditShortText($object) {
|
||||
return $object->getMonogram();
|
||||
}
|
||||
|
||||
protected function getObjectCreateShortText() {
|
||||
return pht('Create Event');
|
||||
}
|
||||
|
||||
protected function getObjectName() {
|
||||
return pht('Event');
|
||||
}
|
||||
|
||||
protected function getObjectViewURI($object) {
|
||||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return $this->getApplication()->getApplicationURI('event/edit/');
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if ($this->getIsCreate()) {
|
||||
$invitee_phids = array($viewer->getPHID());
|
||||
} else {
|
||||
$invitee_phids = $object->getInviteePHIDsForEdit();
|
||||
}
|
||||
|
||||
$frequency_options = array(
|
||||
PhabricatorCalendarEvent::FREQUENCY_DAILY => pht('Daily'),
|
||||
PhabricatorCalendarEvent::FREQUENCY_WEEKLY => pht('Weekly'),
|
||||
PhabricatorCalendarEvent::FREQUENCY_MONTHLY => pht('Monthly'),
|
||||
PhabricatorCalendarEvent::FREQUENCY_YEARLY => pht('Yearly'),
|
||||
);
|
||||
|
||||
$fields = array(
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
->setLabel(pht('Name'))
|
||||
->setDescription(pht('Name of the event.'))
|
||||
->setIsRequired(true)
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventNameTransaction::TRANSACTIONTYPE)
|
||||
->setConduitDescription(pht('Rename the event.'))
|
||||
->setConduitTypeDescription(pht('New event name.'))
|
||||
->setValue($object->getName()),
|
||||
id(new PhabricatorRemarkupEditField())
|
||||
->setKey('description')
|
||||
->setLabel(pht('Description'))
|
||||
->setDescription(pht('Description of the event.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventDescriptionTransaction::TRANSACTIONTYPE)
|
||||
->setConduitDescription(pht('Update the event description.'))
|
||||
->setConduitTypeDescription(pht('New event description.'))
|
||||
->setValue($object->getDescription()),
|
||||
id(new PhabricatorBoolEditField())
|
||||
->setKey('cancelled')
|
||||
->setOptions(pht('Active'), pht('Cancelled'))
|
||||
->setLabel(pht('Cancelled'))
|
||||
->setDescription(pht('Cancel the event.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE)
|
||||
->setIsConduitOnly(true)
|
||||
->setConduitDescription(pht('Cancel or restore the event.'))
|
||||
->setConduitTypeDescription(pht('True to cancel the event.'))
|
||||
->setValue($object->getIsCancelled()),
|
||||
id(new PhabricatorUsersEditField())
|
||||
->setKey('hostPHID')
|
||||
->setAliases(array('host'))
|
||||
->setLabel(pht('Host'))
|
||||
->setDescription(pht('Host of the event.'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventHostTransaction::TRANSACTIONTYPE)
|
||||
->setIsConduitOnly($this->getIsCreate())
|
||||
->setConduitDescription(pht('Change the host of the event.'))
|
||||
->setConduitTypeDescription(pht('New event host.'))
|
||||
->setSingleValue($object->getHostPHID()),
|
||||
id(new PhabricatorDatasourceEditField())
|
||||
->setKey('inviteePHIDs')
|
||||
->setAliases(array('invite', 'invitee', 'invitees', 'inviteePHID'))
|
||||
->setLabel(pht('Invitees'))
|
||||
->setDatasource(new PhabricatorMetaMTAMailableDatasource())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('Users invited to the event.'))
|
||||
->setConduitDescription(pht('Change invited users.'))
|
||||
->setConduitTypeDescription(pht('New event invitees.'))
|
||||
->setValue($invitee_phids)
|
||||
->setCommentActionLabel(pht('Change Invitees')),
|
||||
);
|
||||
|
||||
if ($this->getIsCreate()) {
|
||||
$fields[] = id(new PhabricatorBoolEditField())
|
||||
->setKey('isRecurring')
|
||||
->setLabel(pht('Recurring'))
|
||||
->setOptions(pht('One-Time Event'), pht('Recurring Event'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventRecurringTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('One time or recurring event.'))
|
||||
->setConduitDescription(pht('Make the event recurring.'))
|
||||
->setConduitTypeDescription(pht('Mark the event as a recurring event.'))
|
||||
->setValue($object->getIsRecurring());
|
||||
|
||||
$fields[] = id(new PhabricatorSelectEditField())
|
||||
->setKey('frequency')
|
||||
->setLabel(pht('Frequency'))
|
||||
->setOptions($frequency_options)
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventFrequencyTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('Recurring event frequency.'))
|
||||
->setConduitDescription(pht('Change the event frequency.'))
|
||||
->setConduitTypeDescription(pht('New event frequency.'))
|
||||
->setValue($object->getFrequencyRule());
|
||||
}
|
||||
|
||||
if ($this->getIsCreate() || $object->getIsRecurring()) {
|
||||
$fields[] = id(new PhabricatorEpochEditField())
|
||||
->setAllowNull(true)
|
||||
->setKey('until')
|
||||
->setLabel(pht('Repeat Until'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventUntilDateTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('Last instance of the event.'))
|
||||
->setConduitDescription(pht('Change when the event repeats until.'))
|
||||
->setConduitTypeDescription(pht('New final event time.'))
|
||||
->setValue($object->getRecurrenceEndDate());
|
||||
}
|
||||
|
||||
$fields[] = id(new PhabricatorBoolEditField())
|
||||
->setKey('isAllDay')
|
||||
->setLabel(pht('All Day'))
|
||||
->setOptions(pht('Normal Event'), pht('All Day Event'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('Marks this as an all day event.'))
|
||||
->setConduitDescription(pht('Make the event an all day event.'))
|
||||
->setConduitTypeDescription(pht('Mark the event as an all day event.'))
|
||||
->setValue($object->getIsAllDay());
|
||||
|
||||
$fields[] = id(new PhabricatorEpochEditField())
|
||||
->setKey('start')
|
||||
->setLabel(pht('Start'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('Start time of the event.'))
|
||||
->setConduitDescription(pht('Change the start time of the event.'))
|
||||
->setConduitTypeDescription(pht('New event start time.'))
|
||||
->setValue($object->getViewerDateFrom());
|
||||
|
||||
$fields[] = id(new PhabricatorEpochEditField())
|
||||
->setKey('end')
|
||||
->setLabel(pht('End'))
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('End time of the event.'))
|
||||
->setConduitDescription(pht('Change the end time of the event.'))
|
||||
->setConduitTypeDescription(pht('New event end time.'))
|
||||
->setValue($object->getViewerDateTo());
|
||||
|
||||
$fields[] = id(new PhabricatorIconSetEditField())
|
||||
->setKey('icon')
|
||||
->setLabel(pht('Icon'))
|
||||
->setIconSet(new PhabricatorCalendarIconSet())
|
||||
->setTransactionType(
|
||||
PhabricatorCalendarEventIconTransaction::TRANSACTIONTYPE)
|
||||
->setDescription(pht('Event icon.'))
|
||||
->setConduitDescription(pht('Change the event icon.'))
|
||||
->setConduitTypeDescription(pht('New event icon.'))
|
||||
->setValue($object->getIcon());
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
protected function willBuildEditForm($object, array $fields) {
|
||||
$all_day_field = idx($fields, 'isAllDay');
|
||||
$start_field = idx($fields, 'start');
|
||||
$end_field = idx($fields, 'end');
|
||||
|
||||
if ($all_day_field) {
|
||||
$is_all_day = $all_day_field->getValueForTransaction();
|
||||
|
||||
$control_ids = array();
|
||||
if ($start_field) {
|
||||
$control_ids[] = $start_field->getControlID();
|
||||
}
|
||||
if ($end_field) {
|
||||
$control_ids[] = $end_field->getControlID();
|
||||
}
|
||||
|
||||
Javelin::initBehavior(
|
||||
'event-all-day',
|
||||
array(
|
||||
'allDayID' => $all_day_field->getControlID(),
|
||||
'controlIDs' => $control_ids,
|
||||
));
|
||||
|
||||
} else {
|
||||
$is_all_day = $object->getIsAllDay();
|
||||
}
|
||||
|
||||
if ($is_all_day) {
|
||||
if ($start_field) {
|
||||
$start_field->setHideTime(true);
|
||||
}
|
||||
|
||||
if ($end_field) {
|
||||
$end_field->setHideTime(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
|
@ -11,24 +11,53 @@ final class PhabricatorCalendarEventEditor
|
|||
return pht('Calendar');
|
||||
}
|
||||
|
||||
protected function shouldApplyInitialEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function applyInitialEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$actor = $this->requireActor();
|
||||
if ($object->getIsStub()) {
|
||||
$this->materializeStub($object);
|
||||
}
|
||||
}
|
||||
|
||||
private function materializeStub(PhabricatorCalendarEvent $event) {
|
||||
if (!$event->getIsStub()) {
|
||||
throw new Exception(
|
||||
pht('Can not materialize an event stub: this event is not a stub.'));
|
||||
}
|
||||
|
||||
$actor = $this->getActor();
|
||||
$event->copyFromParent($actor);
|
||||
$event->setIsStub(0);
|
||||
|
||||
$invitees = $event->getParentEvent()->getInvitees();
|
||||
|
||||
$new_invitees = array();
|
||||
foreach ($invitees as $invitee) {
|
||||
$invitee = id(new PhabricatorCalendarEventInvitee())
|
||||
->setEventPHID($event->getPHID())
|
||||
->setInviteePHID($invitee->getInviteePHID())
|
||||
->setInviterPHID($invitee->getInviterPHID())
|
||||
->setStatus($invitee->getStatus())
|
||||
->save();
|
||||
|
||||
$new_invitees[] = $invitee;
|
||||
}
|
||||
|
||||
$event->save();
|
||||
$event->attachInvitees($new_invitees);
|
||||
}
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_NAME;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_START_DATE;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_CANCEL;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_INVITE;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_ALL_DAY;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_ICON;
|
||||
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_RECURRING;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_FREQUENCY;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT;
|
||||
$types[] = PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX;
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
@ -36,174 +65,28 @@ final class PhabricatorCalendarEventEditor
|
|||
return $types;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
|
||||
return $object->getIsRecurring();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
|
||||
return $object->getRecurrenceFrequency();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
|
||||
return $object->getRecurrenceEndDate();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT:
|
||||
return $object->getInstanceOfEventPHID();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX:
|
||||
return $object->getSequenceIndex();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_NAME:
|
||||
return $object->getName();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
|
||||
return $object->getDateFrom();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
|
||||
return $object->getDateTo();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
|
||||
return $object->getDescription();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
|
||||
return $object->getIsCancelled();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
|
||||
return (int)$object->getIsAllDay();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ICON:
|
||||
return $object->getIcon();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
|
||||
$map = $xaction->getNewValue();
|
||||
$phids = array_keys($map);
|
||||
$invitees = mpull($object->getInvitees(), null, 'getInviteePHID');
|
||||
|
||||
$old = array();
|
||||
foreach ($phids as $phid) {
|
||||
$invitee = idx($invitees, $phid);
|
||||
if ($invitee) {
|
||||
$old[$phid] = $invitee->getStatus();
|
||||
} else {
|
||||
$old[$phid] = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
|
||||
}
|
||||
}
|
||||
return $old;
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_NAME:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ICON:
|
||||
return $xaction->getNewValue();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
|
||||
return (int)$xaction->getNewValue();
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
|
||||
return $xaction->getNewValue()->getEpoch();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionNewValue($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
|
||||
return $object->setIsRecurring($xaction->getNewValue());
|
||||
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
|
||||
return $object->setRecurrenceFrequency($xaction->getNewValue());
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT:
|
||||
return $object->setInstanceOfEventPHID($xaction->getNewValue());
|
||||
case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX:
|
||||
return $object->setSequenceIndex($xaction->getNewValue());
|
||||
case PhabricatorCalendarEventTransaction::TYPE_NAME:
|
||||
$object->setName($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
|
||||
$object->setDateFrom($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
|
||||
$object->setDateTo($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
|
||||
$object->setRecurrenceEndDate($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
|
||||
$object->setDescription($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
|
||||
$object->setIsCancelled((int)$xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
|
||||
$object->setIsAllDay((int)$xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ICON:
|
||||
$object->setIcon($xaction->getNewValue());
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_NAME:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ICON:
|
||||
return;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
|
||||
$map = $xaction->getNewValue();
|
||||
$phids = array_keys($map);
|
||||
$invitees = $object->getInvitees();
|
||||
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||
|
||||
foreach ($phids as $phid) {
|
||||
$invitee = idx($invitees, $phid);
|
||||
if (!$invitee) {
|
||||
$invitee = id(new PhabricatorCalendarEventInvitee())
|
||||
->setEventPHID($object->getPHID())
|
||||
->setInviteePHID($phid)
|
||||
->setInviterPHID($this->getActingAsPHID());
|
||||
$invitees[] = $invitee;
|
||||
}
|
||||
$invitee->setStatus($map[$phid])
|
||||
->save();
|
||||
}
|
||||
$object->attachInvitees($invitees);
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function didApplyInternalEffects(
|
||||
protected function adjustObjectForPolicyChecks(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$object->removeViewerTimezone($this->requireActor());
|
||||
|
||||
return $xactions;
|
||||
$copy = parent::adjustObjectForPolicyChecks($object, $xactions);
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCalendarEventHostTransaction::TRANSACTIONTYPE:
|
||||
$copy->setHostPHID($xaction->getNewValue());
|
||||
break;
|
||||
case PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE:
|
||||
PhabricatorPolicyRule::passTransactionHintToRule(
|
||||
$copy,
|
||||
new PhabricatorCalendarEventInviteesPolicyRule(),
|
||||
array_fuse($xaction->getNewValue()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
|
||||
protected function applyFinalEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
|
@ -216,22 +99,21 @@ final class PhabricatorCalendarEventEditor
|
|||
$invalidate_phids = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ICON:
|
||||
break;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INSTANCE_OF_EVENT:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_SEQUENCE_INDEX:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
|
||||
case PhabricatorCalendarEventUntilDateTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE:
|
||||
// For these kinds of changes, we need to invalidate the availabilty
|
||||
// caches for all attendees.
|
||||
$invalidate_all = true;
|
||||
break;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
|
||||
case PhabricatorCalendarEventAcceptTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventDeclineTransaction::TRANSACTIONTYPE:
|
||||
$acting_phid = $this->getActingAsPHID();
|
||||
$invalidate_phids[$acting_phid] = $acting_phid;
|
||||
break;
|
||||
case PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE:
|
||||
foreach ($xaction->getNewValue() as $phid => $ignored) {
|
||||
$invalidate_phids[$phid] = $phid;
|
||||
}
|
||||
|
@ -247,6 +129,8 @@ final class PhabricatorCalendarEventEditor
|
|||
}
|
||||
|
||||
if ($phids) {
|
||||
$object->applyViewerTimezone($this->getActor());
|
||||
|
||||
$user = new PhabricatorUser();
|
||||
$conn_w = $user->establishConnection('w');
|
||||
queryfx(
|
||||
|
@ -265,14 +149,15 @@ final class PhabricatorCalendarEventEditor
|
|||
protected function validateAllTransactions(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$start_date_xaction =
|
||||
PhabricatorCalendarEventTransaction::TYPE_START_DATE;
|
||||
PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE;
|
||||
$end_date_xaction =
|
||||
PhabricatorCalendarEventTransaction::TYPE_END_DATE;
|
||||
PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE;
|
||||
$is_recurrence_xaction =
|
||||
PhabricatorCalendarEventTransaction::TYPE_RECURRING;
|
||||
PhabricatorCalendarEventRecurringTransaction::TRANSACTIONTYPE;
|
||||
$recurrence_end_xaction =
|
||||
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
|
||||
PhabricatorCalendarEventUntilDateTransaction::TRANSACTIONTYPE;
|
||||
|
||||
$start_date = $object->getDateFrom();
|
||||
$end_date = $object->getDateTo();
|
||||
|
@ -287,25 +172,23 @@ final class PhabricatorCalendarEventEditor
|
|||
} else if ($xaction->getTransactionType() == $end_date_xaction) {
|
||||
$end_date = $xaction->getNewValue()->getEpoch();
|
||||
} else if ($xaction->getTransactionType() == $recurrence_end_xaction) {
|
||||
$recurrence_end = $xaction->getNewValue();
|
||||
$recurrence_end = $xaction->getNewValue()->getEpoch();
|
||||
} else if ($xaction->getTransactionType() == $is_recurrence_xaction) {
|
||||
$is_recurring = $xaction->getNewValue();
|
||||
}
|
||||
}
|
||||
|
||||
if ($start_date > $end_date) {
|
||||
$type = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
$end_date_xaction,
|
||||
pht('Invalid'),
|
||||
pht('End date must be after start date.'),
|
||||
null);
|
||||
}
|
||||
|
||||
if ($recurrence_end && !$is_recurring) {
|
||||
$type =
|
||||
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
$recurrence_end_xaction,
|
||||
pht('Invalid'),
|
||||
pht('Event must be recurring to have a recurrence end date.').
|
||||
null);
|
||||
|
@ -314,49 +197,6 @@ final class PhabricatorCalendarEventEditor
|
|||
return $errors;
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
switch ($type) {
|
||||
case PhabricatorCalendarEventTransaction::TYPE_NAME:
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
$object->getName(),
|
||||
$xactions);
|
||||
|
||||
if ($missing) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('Event name is required.'),
|
||||
nonempty(last($xactions), null));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
|
||||
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
|
||||
foreach ($xactions as $xaction) {
|
||||
$date_value = $xaction->getNewValue();
|
||||
if (!$date_value->isValid()) {
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('Invalid date.'),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
protected function shouldPublishFeedStory(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
@ -380,8 +220,8 @@ final class PhabricatorCalendarEventEditor
|
|||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
$phids = array();
|
||||
|
||||
if ($object->getUserPHID()) {
|
||||
$phids[] = $object->getUserPHID();
|
||||
if ($object->getHostPHID()) {
|
||||
$phids[] = $object->getHostPHID();
|
||||
}
|
||||
$phids[] = $this->getActingAsPHID();
|
||||
|
||||
|
|
|
@ -32,16 +32,16 @@ final class PhabricatorCalendarEventPHIDType extends PhabricatorPHIDType {
|
|||
foreach ($handles as $phid => $handle) {
|
||||
$event = $objects[$phid];
|
||||
|
||||
$id = $event->getID();
|
||||
$monogram = $event->getMonogram();
|
||||
$name = $event->getName();
|
||||
$is_cancelled = $event->getIsCancelled();
|
||||
$uri = $event->getURI();
|
||||
|
||||
$handle
|
||||
->setName($name)
|
||||
->setFullName(pht('E%d: %s', $id, $name))
|
||||
->setURI('/E'.$id);
|
||||
->setFullName(pht('%s: %s', $monogram, $name))
|
||||
->setURI($uri);
|
||||
|
||||
if ($is_cancelled) {
|
||||
if ($event->isCancelledEvent()) {
|
||||
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventHostPolicyRule
|
||||
extends PhabricatorPolicyRule {
|
||||
|
||||
public function getObjectPolicyKey() {
|
||||
return 'calendar.event.host';
|
||||
}
|
||||
|
||||
public function getObjectPolicyName() {
|
||||
return pht('Event Host');
|
||||
}
|
||||
|
||||
public function getPolicyExplanation() {
|
||||
return pht('The host of this event can take this action.');
|
||||
}
|
||||
|
||||
public function getRuleDescription() {
|
||||
return pht('event host');
|
||||
}
|
||||
|
||||
public function canApplyToObject(PhabricatorPolicyInterface $object) {
|
||||
return ($object instanceof PhabricatorCalendarEvent);
|
||||
}
|
||||
|
||||
public function applyRule(
|
||||
PhabricatorUser $viewer,
|
||||
$value,
|
||||
PhabricatorPolicyInterface $object) {
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
if (!$viewer_phid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($object->getHostPHID() == $viewer_phid);
|
||||
}
|
||||
|
||||
public function getValueControlType() {
|
||||
return self::CONTROL_TYPE_NONE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventInviteesPolicyRule
|
||||
extends PhabricatorPolicyRule {
|
||||
|
||||
private $invited = array();
|
||||
private $sourcePHIDs = array();
|
||||
|
||||
public function getObjectPolicyKey() {
|
||||
return 'calendar.event.invitees';
|
||||
}
|
||||
|
||||
public function getObjectPolicyName() {
|
||||
return pht('Event Invitees');
|
||||
}
|
||||
|
||||
public function getPolicyExplanation() {
|
||||
return pht('Users invited to this event can take this action.');
|
||||
}
|
||||
|
||||
public function getRuleDescription() {
|
||||
return pht('event invitees');
|
||||
}
|
||||
|
||||
public function canApplyToObject(PhabricatorPolicyInterface $object) {
|
||||
return ($object instanceof PhabricatorCalendarEvent);
|
||||
}
|
||||
|
||||
public function willApplyRules(
|
||||
PhabricatorUser $viewer,
|
||||
array $values,
|
||||
array $objects) {
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
if (!$viewer_phid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->invited[$viewer_phid])) {
|
||||
$this->invited[$viewer_phid] = array();
|
||||
}
|
||||
|
||||
if (!isset($this->sourcePHIDs[$viewer_phid])) {
|
||||
$source_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$viewer_phid,
|
||||
PhabricatorProjectMemberOfProjectEdgeType::EDGECONST);
|
||||
$source_phids[] = $viewer_phid;
|
||||
$this->sourcePHIDs[$viewer_phid] = $source_phids;
|
||||
}
|
||||
|
||||
foreach ($objects as $key => $object) {
|
||||
$cache = $this->getTransactionHint($object);
|
||||
if ($cache === null) {
|
||||
// We don't have a hint for this object, so we'll deal with it below.
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a hint, so use that as the source of truth.
|
||||
unset($objects[$key]);
|
||||
|
||||
foreach ($this->sourcePHIDs[$viewer_phid] as $source_phid) {
|
||||
if (isset($cache[$source_phid])) {
|
||||
$this->invited[$viewer_phid][$object->getPHID()] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$phids = mpull($objects, 'getPHID');
|
||||
if (!$phids) {
|
||||
return;
|
||||
}
|
||||
|
||||
$invited = id(new PhabricatorCalendarEventInvitee())->loadAllWhere(
|
||||
'eventPHID IN (%Ls)
|
||||
AND inviteePHID IN (%Ls)
|
||||
AND status != %s',
|
||||
$phids,
|
||||
$this->sourcePHIDs[$viewer_phid],
|
||||
PhabricatorCalendarEventInvitee::STATUS_UNINVITED);
|
||||
$invited = mpull($invited, 'getEventPHID');
|
||||
|
||||
$this->invited[$viewer_phid] += array_fill_keys($invited, true);
|
||||
}
|
||||
|
||||
public function applyRule(
|
||||
PhabricatorUser $viewer,
|
||||
$value,
|
||||
PhabricatorPolicyInterface $object) {
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
if (!$viewer_phid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$invited = idx($this->invited, $viewer_phid);
|
||||
return isset($invited[$object->getPHID()]);
|
||||
}
|
||||
|
||||
public function getValueControlType() {
|
||||
return self::CONTROL_TYPE_NONE;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,10 +8,11 @@ final class PhabricatorCalendarEventQuery
|
|||
private $rangeBegin;
|
||||
private $rangeEnd;
|
||||
private $inviteePHIDs;
|
||||
private $creatorPHIDs;
|
||||
private $hostPHIDs;
|
||||
private $isCancelled;
|
||||
private $eventsWithNoParent;
|
||||
private $instanceSequencePairs;
|
||||
private $isStub;
|
||||
|
||||
private $generateGhosts = false;
|
||||
|
||||
|
@ -45,8 +46,8 @@ final class PhabricatorCalendarEventQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withCreatorPHIDs(array $phids) {
|
||||
$this->creatorPHIDs = $phids;
|
||||
public function withHostPHIDs(array $phids) {
|
||||
$this->hostPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -55,6 +56,11 @@ final class PhabricatorCalendarEventQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withIsStub($is_stub) {
|
||||
$this->isStub = $is_stub;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withEventsWithNoParent($events_with_no_parent) {
|
||||
$this->eventsWithNoParent = $events_with_no_parent;
|
||||
return $this;
|
||||
|
@ -69,6 +75,15 @@ final class PhabricatorCalendarEventQuery
|
|||
return array('start', 'id');
|
||||
}
|
||||
|
||||
public function getBuiltinOrders() {
|
||||
return array(
|
||||
'start' => array(
|
||||
'vector' => array('start', 'id'),
|
||||
'name' => pht('Event Start'),
|
||||
),
|
||||
) + parent::getBuiltinOrders();
|
||||
}
|
||||
|
||||
public function getOrderableColumns() {
|
||||
return array(
|
||||
'start' => array(
|
||||
|
@ -84,11 +99,22 @@ final class PhabricatorCalendarEventQuery
|
|||
protected function getPagingValueMap($cursor, array $keys) {
|
||||
$event = $this->loadCursorObject($cursor);
|
||||
return array(
|
||||
'start' => $event->getDateFrom(),
|
||||
'start' => $event->getViewerDateFrom(),
|
||||
'id' => $event->getID(),
|
||||
);
|
||||
}
|
||||
|
||||
protected function shouldLimitResults() {
|
||||
// When generating ghosts, we can't rely on database ordering because
|
||||
// MySQL can't predict the ghost start times. We'll just load all matching
|
||||
// events, then generate results from there.
|
||||
if ($this->generateGhosts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$events = $this->loadStandardPage($this->newResultObject());
|
||||
|
||||
|
@ -101,7 +127,6 @@ final class PhabricatorCalendarEventQuery
|
|||
return $events;
|
||||
}
|
||||
|
||||
$enforced_end = null;
|
||||
$raw_limit = $this->getRawResultLimit();
|
||||
|
||||
if (!$raw_limit && !$this->rangeEnd) {
|
||||
|
@ -115,7 +140,6 @@ final class PhabricatorCalendarEventQuery
|
|||
foreach ($events as $key => $event) {
|
||||
$sequence_start = 0;
|
||||
$sequence_end = null;
|
||||
$duration = $event->getDateTo() - $event->getDateFrom();
|
||||
$end = null;
|
||||
|
||||
$instance_of = $event->getInstanceOfEventPHID();
|
||||
|
@ -126,14 +150,36 @@ final class PhabricatorCalendarEventQuery
|
|||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pull out all of the parents first. We may discard them as we begin
|
||||
// generating ghost events, but we still want to process all of them.
|
||||
$parents = array();
|
||||
foreach ($events as $key => $event) {
|
||||
if ($event->isParentEvent()) {
|
||||
$parents[$key] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've picked out all the parent events, we can immediately
|
||||
// discard anything outside of the time window.
|
||||
$events = $this->getEventsInRange($events);
|
||||
|
||||
$enforced_end = null;
|
||||
foreach ($parents as $key => $event) {
|
||||
$sequence_start = 0;
|
||||
$sequence_end = null;
|
||||
$start = null;
|
||||
|
||||
$duration = $event->getDuration();
|
||||
|
||||
if ($event->getIsRecurring() && $instance_of == null) {
|
||||
$frequency = $event->getFrequencyUnit();
|
||||
$modify_key = '+1 '.$frequency;
|
||||
|
||||
if ($this->rangeBegin && $this->rangeBegin > $event->getDateFrom()) {
|
||||
if (($this->rangeBegin !== null) &&
|
||||
($this->rangeBegin > $event->getViewerDateFrom())) {
|
||||
$max_date = $this->rangeBegin - $duration;
|
||||
$date = $event->getDateFrom();
|
||||
$date = $event->getViewerDateFrom();
|
||||
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
|
||||
|
||||
while ($date < $max_date) {
|
||||
|
@ -145,28 +191,28 @@ final class PhabricatorCalendarEventQuery
|
|||
|
||||
$start = $this->rangeBegin;
|
||||
} else {
|
||||
$start = $event->getDateFrom() - $duration;
|
||||
$start = $event->getViewerDateFrom() - $duration;
|
||||
}
|
||||
|
||||
$date = $start;
|
||||
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
|
||||
|
||||
if (($this->rangeEnd && $event->getRecurrenceEndDate()) &&
|
||||
$this->rangeEnd < $event->getRecurrenceEndDate()) {
|
||||
$end = $this->rangeEnd;
|
||||
} else if ($event->getRecurrenceEndDate()) {
|
||||
$end = $event->getRecurrenceEndDate();
|
||||
} else if ($this->rangeEnd) {
|
||||
$end = $this->rangeEnd;
|
||||
} else if ($enforced_end) {
|
||||
if ($end) {
|
||||
$end = min($end, $enforced_end);
|
||||
} else {
|
||||
$end = $enforced_end;
|
||||
}
|
||||
// Select the minimum end time we need to generate events until.
|
||||
$end_times = array();
|
||||
if ($this->rangeEnd) {
|
||||
$end_times[] = $this->rangeEnd;
|
||||
}
|
||||
|
||||
if ($end) {
|
||||
if ($event->getRecurrenceEndDate()) {
|
||||
$end_times[] = $event->getRecurrenceEndDate();
|
||||
}
|
||||
|
||||
if ($enforced_end) {
|
||||
$end_times[] = $enforced_end;
|
||||
}
|
||||
|
||||
if ($end_times) {
|
||||
$end = min($end_times);
|
||||
$sequence_end = $sequence_start;
|
||||
while ($date < $end) {
|
||||
$sequence_end++;
|
||||
|
@ -181,9 +227,8 @@ final class PhabricatorCalendarEventQuery
|
|||
}
|
||||
|
||||
$sequence_start = max(1, $sequence_start);
|
||||
|
||||
for ($index = $sequence_start; $index < $sequence_end; $index++) {
|
||||
$events[] = $event->generateNthGhost($index, $viewer);
|
||||
$events[] = $event->newGhost($viewer, $index);
|
||||
}
|
||||
|
||||
// NOTE: We're slicing results every time because this makes it cheaper
|
||||
|
@ -192,57 +237,85 @@ final class PhabricatorCalendarEventQuery
|
|||
// because they couldn't possibly ever appear in the result set.
|
||||
|
||||
if ($raw_limit) {
|
||||
if (count($events) >= $raw_limit) {
|
||||
$events = msort($events, 'getDateFrom');
|
||||
if (count($events) > $raw_limit) {
|
||||
$events = msort($events, 'getViewerDateFrom');
|
||||
$events = array_slice($events, 0, $raw_limit, true);
|
||||
$enforced_end = last($events)->getDateFrom();
|
||||
}
|
||||
$enforced_end = last($events)->getViewerDateFrom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$map = array();
|
||||
$instance_sequence_pairs = array();
|
||||
// Now that we're done generating ghost events, we're going to remove any
|
||||
// ghosts that we have concrete events for (or which we can load the
|
||||
// concrete events for). These concrete events are generated when users
|
||||
// edit a ghost, and replace the ghost events.
|
||||
|
||||
foreach ($events as $key => $event) {
|
||||
if ($event->getIsGhostEvent()) {
|
||||
$index = $event->getSequenceIndex();
|
||||
$instance_sequence_pairs[] = array($event->getPHID(), $index);
|
||||
$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) {
|
||||
// First, generate a map of all concrete <parentPHID, sequence> events we
|
||||
// already loaded. We don't need to load these again.
|
||||
$have_pairs = array();
|
||||
foreach ($events as $event) {
|
||||
if ($event->getIsGhostEvent()) {
|
||||
continue;
|
||||
}
|
||||
if (isset($id_map[$event->getID()])) {
|
||||
|
||||
$parent_phid = $event->getInstanceOfEventPHID();
|
||||
$sequence = $event->getSequenceIndex();
|
||||
|
||||
$have_pairs[$parent_phid][$sequence] = true;
|
||||
}
|
||||
|
||||
// Now, generate a map of all <parentPHID, sequence> events we generated
|
||||
// ghosts for. We need to try to load these if we don't already have them.
|
||||
$map = array();
|
||||
$parent_pairs = array();
|
||||
foreach ($events as $key => $event) {
|
||||
if (!$event->getIsGhostEvent()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parent_phid = $event->getInstanceOfEventPHID();
|
||||
$sequence = $event->getSequenceIndex();
|
||||
|
||||
// We already loaded the concrete version of this event, so we can just
|
||||
// throw out the ghost and move on.
|
||||
if (isset($have_pairs[$parent_phid][$sequence])) {
|
||||
unset($events[$key]);
|
||||
} else {
|
||||
$id_map[$event->getID()] = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// We didn't load the concrete version of this event, so we need to
|
||||
// try to load it if it exists.
|
||||
$parent_pairs[] = array($parent_phid, $sequence);
|
||||
$map[$parent_phid][$sequence] = $key;
|
||||
}
|
||||
|
||||
if ($parent_pairs) {
|
||||
$instances = id(new self())
|
||||
->setViewer($viewer)
|
||||
->setParentQuery($this)
|
||||
->withInstanceSequencePairs($parent_pairs)
|
||||
->execute();
|
||||
|
||||
foreach ($instances as $instance) {
|
||||
$parent_phid = $instance->getInstanceOfEventPHID();
|
||||
$sequence = $instance->getSequenceIndex();
|
||||
|
||||
$indexes = idx($map, $parent_phid);
|
||||
$key = idx($indexes, $sequence);
|
||||
|
||||
// Replace the ghost with the corresponding concrete event.
|
||||
$events[$key] = $instance;
|
||||
}
|
||||
}
|
||||
|
||||
$events = msort($events, 'getViewerDateFrom');
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) {
|
||||
$parts = parent::buildJoinClauseParts($conn_r);
|
||||
|
||||
if ($this->inviteePHIDs !== null) {
|
||||
$parts[] = qsprintf(
|
||||
$conn_r,
|
||||
|
@ -251,6 +324,7 @@ final class PhabricatorCalendarEventQuery
|
|||
id(new PhabricatorCalendarEventInvitee())->getTableName(),
|
||||
PhabricatorCalendarEventInvitee::STATUS_UNINVITED);
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
|
@ -271,18 +345,22 @@ final class PhabricatorCalendarEventQuery
|
|||
$this->phids);
|
||||
}
|
||||
|
||||
// NOTE: The date ranges we query for are larger than the requested ranges
|
||||
// because we need to catch all-day events. We'll refine this range later
|
||||
// after adjusting the visible range of events we load.
|
||||
|
||||
if ($this->rangeBegin) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.dateTo >= %d OR event.isRecurring = 1',
|
||||
$this->rangeBegin);
|
||||
$this->rangeBegin - phutil_units('16 hours in seconds'));
|
||||
}
|
||||
|
||||
if ($this->rangeEnd) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.dateFrom <= %d',
|
||||
$this->rangeEnd);
|
||||
$this->rangeEnd + phutil_units('16 hours in seconds'));
|
||||
}
|
||||
|
||||
if ($this->inviteePHIDs !== null) {
|
||||
|
@ -292,11 +370,11 @@ final class PhabricatorCalendarEventQuery
|
|||
$this->inviteePHIDs);
|
||||
}
|
||||
|
||||
if ($this->creatorPHIDs) {
|
||||
if ($this->hostPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.userPHID IN (%Ls)',
|
||||
$this->creatorPHIDs);
|
||||
'event.hostPHID IN (%Ls)',
|
||||
$this->hostPHIDs);
|
||||
}
|
||||
|
||||
if ($this->isCancelled !== null) {
|
||||
|
@ -329,6 +407,13 @@ final class PhabricatorCalendarEventQuery
|
|||
implode(' OR ', $sql));
|
||||
}
|
||||
|
||||
if ($this->isStub !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.isStub = %d',
|
||||
(int)$this->isStub);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
@ -353,23 +438,11 @@ final class PhabricatorCalendarEventQuery
|
|||
|
||||
|
||||
protected function willFilterPage(array $events) {
|
||||
$range_start = $this->rangeBegin;
|
||||
$range_end = $this->rangeEnd;
|
||||
$instance_of_event_phids = array();
|
||||
$recurring_events = array();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
foreach ($events as $key => $event) {
|
||||
$event_start = $event->getDateFrom();
|
||||
$event_end = $event->getDateTo();
|
||||
|
||||
if ($range_start && $event_end < $range_start) {
|
||||
unset($events[$key]);
|
||||
}
|
||||
if ($range_end && $event_start > $range_end) {
|
||||
unset($events[$key]);
|
||||
}
|
||||
}
|
||||
$events = $this->getEventsInRange($events);
|
||||
|
||||
$phids = array();
|
||||
|
||||
|
@ -427,7 +500,27 @@ final class PhabricatorCalendarEventQuery
|
|||
}
|
||||
}
|
||||
|
||||
$events = msort($events, 'getDateFrom');
|
||||
$events = msort($events, 'getViewerDateFrom');
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
private function getEventsInRange(array $events) {
|
||||
$range_start = $this->rangeBegin;
|
||||
$range_end = $this->rangeEnd;
|
||||
|
||||
foreach ($events as $key => $event) {
|
||||
$event_start = $event->getViewerDateFrom();
|
||||
$event_end = $event->getViewerDateTo();
|
||||
|
||||
if ($range_start && $event_end < $range_start) {
|
||||
unset($events[$key]);
|
||||
}
|
||||
|
||||
if ($range_end && $event_start > $range_end) {
|
||||
unset($events[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,9 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Created By'))
|
||||
->setKey('creatorPHIDs')
|
||||
->setLabel(pht('Hosts'))
|
||||
->setKey('hostPHIDs')
|
||||
->setAliases(array('host', 'hostPHID', 'hosts'))
|
||||
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Invited'))
|
||||
|
@ -78,8 +79,8 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
$query = $this->newQuery();
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
if ($map['creatorPHIDs']) {
|
||||
$query->withCreatorPHIDs($map['creatorPHIDs']);
|
||||
if ($map['hostPHIDs']) {
|
||||
$query->withHostPHIDs($map['hostPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['invitedPHIDs']) {
|
||||
|
@ -113,7 +114,15 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
break;
|
||||
}
|
||||
|
||||
return $query->setGenerateGhosts(true);
|
||||
// Generate ghosts (and ignore stub events) if we aren't querying for
|
||||
// specific events.
|
||||
if (!$map['ids'] && !$map['phids']) {
|
||||
$query
|
||||
->withIsStub(false)
|
||||
->setGenerateGhosts(true);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function getQueryDateRange(
|
||||
|
@ -184,10 +193,11 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
}
|
||||
|
||||
if ($upcoming) {
|
||||
$now = PhabricatorTime::getNow();
|
||||
if ($min_range) {
|
||||
$min_range = max(time(), $min_range);
|
||||
$min_range = max($now, $min_range);
|
||||
} else {
|
||||
$min_range = time();
|
||||
$min_range = $now;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,25 +249,15 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
protected function getRequiredHandlePHIDsForResultList(
|
||||
array $objects,
|
||||
PhabricatorSavedQuery $query) {
|
||||
$phids = array();
|
||||
foreach ($objects as $event) {
|
||||
$phids[$event->getUserPHID()] = 1;
|
||||
}
|
||||
return array_keys($phids);
|
||||
}
|
||||
|
||||
protected function renderResultList(
|
||||
array $events,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
|
||||
if ($this->isMonthView($query)) {
|
||||
return $this->buildCalendarView($events, $query, $handles);
|
||||
return $this->buildCalendarMonthView($events, $query);
|
||||
} else if ($this->isDayView($query)) {
|
||||
return $this->buildCalendarDayView($events, $query, $handles);
|
||||
return $this->buildCalendarDayView($events, $query);
|
||||
}
|
||||
|
||||
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
||||
|
@ -265,51 +265,37 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
$list = new PHUIObjectItemListView();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$event_date_info = $this->getEventDateLabel($event);
|
||||
$creator_handle = $handles[$event->getUserPHID()];
|
||||
$attendees = array();
|
||||
|
||||
foreach ($event->getInvitees() as $invitee) {
|
||||
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
|
||||
if ($invitee->getStatus() === $status_attending) {
|
||||
$attendees[] = $invitee->getInviteePHID();
|
||||
}
|
||||
}
|
||||
|
||||
if ($event->getIsGhostEvent()) {
|
||||
$title_text = $event->getMonogram()
|
||||
.' ('
|
||||
.$event->getSequenceIndex()
|
||||
.'): '
|
||||
.$event->getName();
|
||||
$monogram = $event->getParentEvent()->getMonogram();
|
||||
$index = $event->getSequenceIndex();
|
||||
$monogram = "{$monogram}/{$index}";
|
||||
} else {
|
||||
$title_text = $event->getMonogram().': '.$event->getName();
|
||||
$monogram = $event->getMonogram();
|
||||
}
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setUser($viewer)
|
||||
->setObject($event)
|
||||
->setHeader($title_text)
|
||||
->setHref($event->getURI())
|
||||
->addAttribute($event_date_info);
|
||||
->setObjectName($monogram)
|
||||
->setHeader($event->getName())
|
||||
->setHref($event->getURI());
|
||||
|
||||
if ($attendees) {
|
||||
$attending = pht(
|
||||
'Attending: %s',
|
||||
$viewer->renderHandleList($attendees)
|
||||
->setAsInline(1)
|
||||
->render());
|
||||
$item->addAttribute($event->renderEventDate($viewer, false));
|
||||
|
||||
$item->addAttribute($attending);
|
||||
if ($event->isCancelledEvent()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
if (strlen($event->getDuration()) > 0) {
|
||||
$duration = pht(
|
||||
'Duration: %s',
|
||||
$event->getDuration());
|
||||
$status_icon = $event->getDisplayIcon($viewer);
|
||||
$status_color = $event->getDisplayIconColor($viewer);
|
||||
$status_label = $event->getDisplayIconLabel($viewer);
|
||||
|
||||
$item->addIcon('none', $duration);
|
||||
}
|
||||
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
|
||||
|
||||
$host = pht(
|
||||
'Hosted by %s',
|
||||
$viewer->renderHandle($event->getHostPHID()));
|
||||
$item->addByline($host);
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
@ -321,13 +307,13 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
return $result;
|
||||
}
|
||||
|
||||
private function buildCalendarView(
|
||||
array $statuses,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
private function buildCalendarMonthView(
|
||||
array $events,
|
||||
PhabricatorSavedQuery $query) {
|
||||
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
$now = time();
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
list($start_year, $start_month) =
|
||||
$this->getDisplayYearAndMonthAndDay(
|
||||
|
@ -356,40 +342,45 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
|
||||
$month_view->setUser($viewer);
|
||||
|
||||
$phids = mpull($statuses, 'getUserPHID');
|
||||
foreach ($events as $event) {
|
||||
$epoch_min = $event->getViewerDateFrom();
|
||||
$epoch_max = $event->getViewerDateTo();
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
$viewer_is_invited = $status->getIsUserInvited($viewer->getPHID());
|
||||
$event_view = id(new AphrontCalendarEventView())
|
||||
->setHostPHID($event->getHostPHID())
|
||||
->setEpochRange($epoch_min, $epoch_max)
|
||||
->setIsCancelled($event->isCancelledEvent())
|
||||
->setName($event->getName())
|
||||
->setURI($event->getURI())
|
||||
->setIsAllDay($event->getIsAllDay())
|
||||
->setIcon($event->getDisplayIcon($viewer))
|
||||
->setIconColor($event->getDisplayIconColor($viewer));
|
||||
|
||||
$event = new AphrontCalendarEventView();
|
||||
$event->setEpochRange($status->getDateFrom(), $status->getDateTo());
|
||||
$event->setIsAllDay($status->getIsAllDay());
|
||||
$event->setIcon($status->getIcon());
|
||||
|
||||
$name_text = $handles[$status->getUserPHID()]->getName();
|
||||
$status_text = $status->getName();
|
||||
$event->setUserPHID($status->getUserPHID());
|
||||
$event->setDescription(pht('%s (%s)', $name_text, $status_text));
|
||||
$event->setName($status_text);
|
||||
$event->setURI($status->getURI());
|
||||
$event->setViewerIsInvited($viewer_is_invited);
|
||||
$month_view->addEvent($event);
|
||||
$month_view->addEvent($event_view);
|
||||
}
|
||||
|
||||
$month_view->setBrowseURI(
|
||||
$this->getURI('query/'.$query->getQueryKey().'/'));
|
||||
|
||||
// TODO redesign-2015 : Move buttons out of PHUICalendarView?
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
$result->setContent($month_view);
|
||||
$from = $this->getQueryDateFrom($query)->getDateTime();
|
||||
|
||||
return $result;
|
||||
$crumbs = array();
|
||||
$crumbs[] = id(new PHUICrumbView())
|
||||
->setName($from->format('F Y'));
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($from->format('F Y'));
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
->setCrumbs($crumbs)
|
||||
->setHeader($header)
|
||||
->setContent($month_view)
|
||||
->setCollapsed(true);
|
||||
}
|
||||
|
||||
private function buildCalendarDayView(
|
||||
array $statuses,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
array $events,
|
||||
PhabricatorSavedQuery $query) {
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
|
@ -400,8 +391,8 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
$query->getParameter('display'));
|
||||
|
||||
$day_view = id(new PHUICalendarDayView(
|
||||
$this->getQueryDateFrom($query)->getEpoch(),
|
||||
$this->getQueryDateTo($query)->getEpoch(),
|
||||
$this->getQueryDateFrom($query),
|
||||
$this->getQueryDateTo($query),
|
||||
$start_year,
|
||||
$start_month,
|
||||
$start_day))
|
||||
|
@ -409,40 +400,56 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
|
||||
$day_view->setUser($viewer);
|
||||
|
||||
$phids = mpull($statuses, 'getUserPHID');
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
if ($status->getIsCancelled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$viewer_is_invited = $status->getIsUserInvited($viewer->getPHID());
|
||||
$phids = mpull($events, 'getHostPHID');
|
||||
|
||||
foreach ($events as $event) {
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$status,
|
||||
$event,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$event = new AphrontCalendarEventView();
|
||||
$event->setCanEdit($can_edit);
|
||||
$event->setEventID($status->getID());
|
||||
$event->setEpochRange($status->getDateFrom(), $status->getDateTo());
|
||||
$event->setIsAllDay($status->getIsAllDay());
|
||||
$event->setIcon($status->getIcon());
|
||||
$event->setViewerIsInvited($viewer_is_invited);
|
||||
$epoch_min = $event->getViewerDateFrom();
|
||||
$epoch_max = $event->getViewerDateTo();
|
||||
|
||||
$event->setName($status->getName());
|
||||
$event->setURI($status->getURI());
|
||||
$day_view->addEvent($event);
|
||||
$status_icon = $event->getDisplayIcon($viewer);
|
||||
$status_color = $event->getDisplayIconColor($viewer);
|
||||
|
||||
$event_view = id(new AphrontCalendarEventView())
|
||||
->setCanEdit($can_edit)
|
||||
->setEventID($event->getID())
|
||||
->setEpochRange($epoch_min, $epoch_max)
|
||||
->setIsAllDay($event->getIsAllDay())
|
||||
->setIcon($status_icon)
|
||||
->setIconColor($status_color)
|
||||
->setName($event->getName())
|
||||
->setURI($event->getURI())
|
||||
->setIsCancelled($event->isCancelledEvent());
|
||||
|
||||
$day_view->addEvent($event_view);
|
||||
}
|
||||
|
||||
$day_view->setBrowseURI(
|
||||
$this->getURI('query/'.$query->getQueryKey().'/'));
|
||||
$browse_uri = $this->getURI('query/'.$query->getQueryKey().'/');
|
||||
$day_view->setBrowseURI($browse_uri);
|
||||
|
||||
$result = new PhabricatorApplicationSearchResultView();
|
||||
$result->setContent($day_view);
|
||||
$from = $this->getQueryDateFrom($query)->getDateTime();
|
||||
$month_uri = $browse_uri.$from->format('Y/m/');
|
||||
|
||||
return $result;
|
||||
$crumbs = array(
|
||||
id(new PHUICrumbView())
|
||||
->setName($from->format('F Y'))
|
||||
->setHref($month_uri),
|
||||
id(new PHUICrumbView())
|
||||
->setName($from->format('D jS')),
|
||||
);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($from->format('D, F jS'));
|
||||
|
||||
return id(new PhabricatorApplicationSearchResultView())
|
||||
->setCrumbs($crumbs)
|
||||
->setHeader($header)
|
||||
->setContent($day_view)
|
||||
->setCollapsed(true);
|
||||
}
|
||||
|
||||
private function getDisplayYearAndMonthAndDay(
|
||||
|
@ -486,6 +493,20 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
}
|
||||
|
||||
private function getQueryDateFrom(PhabricatorSavedQuery $saved) {
|
||||
if ($this->calendarYear && $this->calendarMonth) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$start_year = $this->calendarYear;
|
||||
$start_month = $this->calendarMonth;
|
||||
$start_day = $this->calendarDay ? $this->calendarDay : 1;
|
||||
|
||||
return AphrontFormDateControlValue::newFromDictionary(
|
||||
$viewer,
|
||||
array(
|
||||
'd' => "{$start_year}-{$start_month}-{$start_day}",
|
||||
));
|
||||
}
|
||||
|
||||
return $this->getQueryDate($saved, 'rangeStart');
|
||||
}
|
||||
|
||||
|
@ -541,40 +562,4 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
return false;
|
||||
}
|
||||
|
||||
private function getEventDateLabel($event) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$from_datetime = PhabricatorTime::getDateTimeFromEpoch(
|
||||
$event->getDateFrom(),
|
||||
$viewer);
|
||||
$to_datetime = PhabricatorTime::getDateTimeFromEpoch(
|
||||
$event->getDateTo(),
|
||||
$viewer);
|
||||
|
||||
$from_date_formatted = $from_datetime->format('Y m d');
|
||||
$to_date_formatted = $to_datetime->format('Y m d');
|
||||
|
||||
if ($event->getIsAllDay()) {
|
||||
if ($from_date_formatted == $to_date_formatted) {
|
||||
return pht(
|
||||
'%s, All Day',
|
||||
phabricator_date($event->getDateFrom(), $viewer));
|
||||
} else {
|
||||
return pht(
|
||||
'%s - %s, All Day',
|
||||
phabricator_date($event->getDateFrom(), $viewer),
|
||||
phabricator_date($event->getDateTo(), $viewer));
|
||||
}
|
||||
} else if ($from_date_formatted == $to_date_formatted) {
|
||||
return pht(
|
||||
'%s - %s',
|
||||
phabricator_datetime($event->getDateFrom(), $viewer),
|
||||
phabricator_time($event->getDateTo(), $viewer));
|
||||
} else {
|
||||
return pht(
|
||||
'%s - %s',
|
||||
phabricator_datetime($event->getDateFrom(), $viewer),
|
||||
phabricator_datetime($event->getDateTo(), $viewer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ final class PhabricatorCalendarEventFulltextEngine
|
|||
|
||||
$document->addRelationship(
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
||||
$event->getUserPHID(),
|
||||
$event->getHostPHID(),
|
||||
PhabricatorPeopleUserPHIDType::TYPECONST,
|
||||
$event->getDateCreated());
|
||||
|
||||
$document->addRelationship(
|
||||
PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
|
||||
$event->getUserPHID(),
|
||||
$event->getHostPHID(),
|
||||
PhabricatorPeopleUserPHIDType::TYPECONST,
|
||||
$event->getDateCreated());
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
implements PhabricatorPolicyInterface,
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorMarkupInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
|
@ -11,17 +12,21 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
PhabricatorMentionableInterface,
|
||||
PhabricatorFlaggableInterface,
|
||||
PhabricatorSpacesInterface,
|
||||
PhabricatorFulltextInterface {
|
||||
PhabricatorFulltextInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
|
||||
protected $name;
|
||||
protected $userPHID;
|
||||
protected $hostPHID;
|
||||
protected $dateFrom;
|
||||
protected $dateTo;
|
||||
protected $allDayDateFrom;
|
||||
protected $allDayDateTo;
|
||||
protected $description;
|
||||
protected $isCancelled;
|
||||
protected $isAllDay;
|
||||
protected $icon;
|
||||
protected $mailKey;
|
||||
protected $isStub;
|
||||
|
||||
protected $isRecurring = 0;
|
||||
protected $recurrenceFrequency = array();
|
||||
|
@ -36,11 +41,11 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
|
||||
protected $spacePHID;
|
||||
|
||||
const DEFAULT_ICON = 'fa-calendar';
|
||||
|
||||
private $parentEvent = self::ATTACHABLE;
|
||||
private $invitees = self::ATTACHABLE;
|
||||
private $appliedViewer;
|
||||
|
||||
private $viewerDateFrom;
|
||||
private $viewerDateTo;
|
||||
|
||||
// Frequency Constants
|
||||
const FREQUENCY_DAILY = 'daily';
|
||||
|
@ -48,109 +53,225 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
const FREQUENCY_MONTHLY = 'monthly';
|
||||
const FREQUENCY_YEARLY = 'yearly';
|
||||
|
||||
public static function initializeNewCalendarEvent(
|
||||
PhabricatorUser $actor,
|
||||
$mode) {
|
||||
public static function initializeNewCalendarEvent(PhabricatorUser $actor) {
|
||||
$app = id(new PhabricatorApplicationQuery())
|
||||
->setViewer($actor)
|
||||
->withClasses(array('PhabricatorCalendarApplication'))
|
||||
->executeOne();
|
||||
|
||||
$view_policy = null;
|
||||
$is_recurring = 0;
|
||||
$view_default = PhabricatorCalendarEventDefaultViewCapability::CAPABILITY;
|
||||
$edit_default = PhabricatorCalendarEventDefaultEditCapability::CAPABILITY;
|
||||
$view_policy = $app->getPolicy($view_default);
|
||||
$edit_policy = $app->getPolicy($edit_default);
|
||||
|
||||
if ($mode == 'public') {
|
||||
$view_policy = PhabricatorPolicies::getMostOpenPolicy();
|
||||
}
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
if ($mode == 'recurring') {
|
||||
$is_recurring = true;
|
||||
}
|
||||
$start = new DateTime('@'.$now);
|
||||
$start->setTimeZone($actor->getTimeZone());
|
||||
|
||||
$start->setTime($start->format('H'), 0, 0);
|
||||
$start->modify('+1 hour');
|
||||
$end = id(clone $start)->modify('+1 hour');
|
||||
|
||||
$epoch_min = $start->format('U');
|
||||
$epoch_max = $end->format('U');
|
||||
|
||||
$now_date = new DateTime('@'.$now);
|
||||
$now_min = id(clone $now_date)->setTime(0, 0)->format('U');
|
||||
$now_max = id(clone $now_date)->setTime(23, 59)->format('U');
|
||||
|
||||
$default_icon = 'fa-calendar';
|
||||
|
||||
return id(new PhabricatorCalendarEvent())
|
||||
->setUserPHID($actor->getPHID())
|
||||
->setHostPHID($actor->getPHID())
|
||||
->setIsCancelled(0)
|
||||
->setIsAllDay(0)
|
||||
->setIsRecurring($is_recurring)
|
||||
->setIcon(self::DEFAULT_ICON)
|
||||
->setIsStub(0)
|
||||
->setIsRecurring(0)
|
||||
->setRecurrenceFrequency(
|
||||
array(
|
||||
'rule' => self::FREQUENCY_WEEKLY,
|
||||
))
|
||||
->setIcon($default_icon)
|
||||
->setViewPolicy($view_policy)
|
||||
->setEditPolicy($actor->getPHID())
|
||||
->setEditPolicy($edit_policy)
|
||||
->setSpacePHID($actor->getDefaultSpacePHID())
|
||||
->attachInvitees(array())
|
||||
->setDateFrom($epoch_min)
|
||||
->setDateTo($epoch_max)
|
||||
->setAllDayDateFrom($now_min)
|
||||
->setAllDayDateTo($now_max)
|
||||
->applyViewerTimezone($actor);
|
||||
}
|
||||
|
||||
private function newChild(PhabricatorUser $actor, $sequence) {
|
||||
if (!$this->isParentEvent()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to generate a new child event for an event which is not '.
|
||||
'a recurring parent event!'));
|
||||
}
|
||||
|
||||
$child = id(new self())
|
||||
->setIsCancelled(0)
|
||||
->setIsStub(0)
|
||||
->setInstanceOfEventPHID($this->getPHID())
|
||||
->setSequenceIndex($sequence)
|
||||
->setIsRecurring(true)
|
||||
->setRecurrenceFrequency($this->getRecurrenceFrequency())
|
||||
->attachParentEvent($this);
|
||||
|
||||
return $child->copyFromParent($actor);
|
||||
}
|
||||
|
||||
protected function readField($field) {
|
||||
static $inherit = array(
|
||||
'hostPHID' => true,
|
||||
'isAllDay' => true,
|
||||
'icon' => true,
|
||||
'spacePHID' => true,
|
||||
'viewPolicy' => true,
|
||||
'editPolicy' => true,
|
||||
'name' => true,
|
||||
'description' => true,
|
||||
);
|
||||
|
||||
// Read these fields from the parent event instead of this event. For
|
||||
// example, we want any changes to the parent event's name to
|
||||
if (isset($inherit[$field])) {
|
||||
if ($this->getIsStub()) {
|
||||
// TODO: This should be unconditional, but the execution order of
|
||||
// CalendarEventQuery and applyViewerTimezone() are currently odd.
|
||||
if ($this->parentEvent !== self::ATTACHABLE) {
|
||||
return $this->getParentEvent()->readField($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::readField($field);
|
||||
}
|
||||
|
||||
|
||||
public function copyFromParent(PhabricatorUser $actor) {
|
||||
if (!$this->isChildEvent()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unable to copy from parent event: this is not a child event.'));
|
||||
}
|
||||
|
||||
$parent = $this->getParentEvent();
|
||||
|
||||
$this
|
||||
->setHostPHID($parent->getHostPHID())
|
||||
->setIsAllDay($parent->getIsAllDay())
|
||||
->setIcon($parent->getIcon())
|
||||
->setSpacePHID($parent->getSpacePHID())
|
||||
->setViewPolicy($parent->getViewPolicy())
|
||||
->setEditPolicy($parent->getEditPolicy())
|
||||
->setName($parent->getName())
|
||||
->setDescription($parent->getDescription());
|
||||
|
||||
$frequency = $parent->getFrequencyUnit();
|
||||
$modify_key = '+'.$this->getSequenceIndex().' '.$frequency;
|
||||
|
||||
$date = $parent->getDateFrom();
|
||||
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor);
|
||||
$date_time->modify($modify_key);
|
||||
$date = $date_time->format('U');
|
||||
|
||||
$duration = $this->getDuration();
|
||||
|
||||
$utc = new DateTimeZone('UTC');
|
||||
|
||||
$allday_from = $parent->getAllDayDateFrom();
|
||||
$allday_date = new DateTime('@'.$allday_from, $utc);
|
||||
$allday_date->setTimeZone($utc);
|
||||
$allday_date->modify($modify_key);
|
||||
|
||||
$allday_min = $allday_date->format('U');
|
||||
$allday_duration = ($parent->getAllDayDateTo() - $allday_from);
|
||||
|
||||
$this
|
||||
->setDateFrom($date)
|
||||
->setDateTo($date + $duration)
|
||||
->setAllDayDateFrom($allday_min)
|
||||
->setAllDayDateTo($allday_min + $allday_duration);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newStub(PhabricatorUser $actor, $sequence) {
|
||||
$stub = $this->newChild($actor, $sequence);
|
||||
|
||||
$stub->setIsStub(1);
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$stub->save();
|
||||
unset($unguarded);
|
||||
|
||||
$stub->applyViewerTimezone($actor);
|
||||
|
||||
return $stub;
|
||||
}
|
||||
|
||||
public function newGhost(PhabricatorUser $actor, $sequence) {
|
||||
$ghost = $this->newChild($actor, $sequence);
|
||||
|
||||
$ghost
|
||||
->setIsGhostEvent(true)
|
||||
->makeEphemeral();
|
||||
|
||||
$ghost->applyViewerTimezone($actor);
|
||||
|
||||
return $ghost;
|
||||
}
|
||||
|
||||
public function getViewerDateFrom() {
|
||||
if ($this->viewerDateFrom === null) {
|
||||
throw new PhutilInvalidStateException('applyViewerTimezone');
|
||||
}
|
||||
|
||||
return $this->viewerDateFrom;
|
||||
}
|
||||
|
||||
public function getViewerDateTo() {
|
||||
if ($this->viewerDateTo === null) {
|
||||
throw new PhutilInvalidStateException('applyViewerTimezone');
|
||||
}
|
||||
|
||||
return $this->viewerDateTo;
|
||||
}
|
||||
|
||||
public function applyViewerTimezone(PhabricatorUser $viewer) {
|
||||
if ($this->appliedViewer) {
|
||||
throw new Exception(pht('Viewer timezone is already applied!'));
|
||||
}
|
||||
|
||||
$this->appliedViewer = $viewer;
|
||||
|
||||
if (!$this->getIsAllDay()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->viewerDateFrom = $this->getDateFrom();
|
||||
$this->viewerDateTo = $this->getDateTo();
|
||||
} else {
|
||||
$zone = $viewer->getTimeZone();
|
||||
|
||||
|
||||
$this->setDateFrom(
|
||||
$this->getDateEpochForTimeZone(
|
||||
$this->getDateFrom(),
|
||||
new DateTimeZone('Pacific/Kiritimati'),
|
||||
$this->viewerDateFrom = $this->getDateEpochForTimezone(
|
||||
$this->getAllDayDateFrom(),
|
||||
new DateTimeZone('UTC'),
|
||||
'Y-m-d',
|
||||
null,
|
||||
$zone));
|
||||
$zone);
|
||||
|
||||
$this->setDateTo(
|
||||
$this->getDateEpochForTimeZone(
|
||||
$this->getDateTo(),
|
||||
new DateTimeZone('Pacific/Midway'),
|
||||
$this->viewerDateTo = $this->getDateEpochForTimezone(
|
||||
$this->getAllDayDateTo(),
|
||||
new DateTimeZone('UTC'),
|
||||
'Y-m-d 23:59:00',
|
||||
'-1 day',
|
||||
$zone));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function removeViewerTimezone(PhabricatorUser $viewer) {
|
||||
if (!$this->appliedViewer) {
|
||||
throw new Exception(pht('Viewer timezone is not applied!'));
|
||||
}
|
||||
|
||||
if ($viewer->getPHID() != $this->appliedViewer->getPHID()) {
|
||||
throw new Exception(pht('Removed viewer must match applied viewer!'));
|
||||
}
|
||||
|
||||
$this->appliedViewer = null;
|
||||
|
||||
if (!$this->getIsAllDay()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$zone = $viewer->getTimeZone();
|
||||
|
||||
$this->setDateFrom(
|
||||
$this->getDateEpochForTimeZone(
|
||||
$this->getDateFrom(),
|
||||
$zone,
|
||||
'Y-m-d',
|
||||
null,
|
||||
new DateTimeZone('Pacific/Kiritimati')));
|
||||
|
||||
$this->setDateTo(
|
||||
$this->getDateEpochForTimeZone(
|
||||
$this->getDateTo(),
|
||||
$zone,
|
||||
'Y-m-d',
|
||||
'+1 day',
|
||||
new DateTimeZone('Pacific/Midway')));
|
||||
$zone);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function getDateEpochForTimeZone(
|
||||
public function getDuration() {
|
||||
return $this->getDateTo() - $this->getDateFrom();
|
||||
}
|
||||
|
||||
public function getDateEpochForTimezone(
|
||||
$epoch,
|
||||
$src_zone,
|
||||
$format,
|
||||
|
@ -169,12 +290,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
|
||||
public function save() {
|
||||
if ($this->appliedViewer) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Can not save event with viewer timezone still applied!'));
|
||||
}
|
||||
|
||||
if (!$this->mailKey) {
|
||||
$this->mailKey = Filesystem::readRandomCharacters(20);
|
||||
}
|
||||
|
@ -186,13 +301,13 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
* Get the event start epoch for evaluating invitee availability.
|
||||
*
|
||||
* When assessing availability, we pretend events start earlier than they
|
||||
* really. This allows us to mark users away for the entire duration of a
|
||||
* really do. This allows us to mark users away for the entire duration of a
|
||||
* series of back-to-back meetings, even if they don't strictly overlap.
|
||||
*
|
||||
* @return int Event start date for availability caches.
|
||||
*/
|
||||
public function getDateFromForCache() {
|
||||
return ($this->getDateFrom() - phutil_units('15 minutes in seconds'));
|
||||
return ($this->getViewerDateFrom() - phutil_units('15 minutes in seconds'));
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
|
@ -202,6 +317,8 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
'name' => 'text',
|
||||
'dateFrom' => 'epoch',
|
||||
'dateTo' => 'epoch',
|
||||
'allDayDateFrom' => 'epoch',
|
||||
'allDayDateTo' => 'epoch',
|
||||
'description' => 'text',
|
||||
'isCancelled' => 'bool',
|
||||
'isAllDay' => 'bool',
|
||||
|
@ -211,10 +328,11 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
'recurrenceEndDate' => 'epoch?',
|
||||
'instanceOfEventPHID' => 'phid?',
|
||||
'sequenceIndex' => 'uint32?',
|
||||
'isStub' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'userPHID_dateFrom' => array(
|
||||
'columns' => array('userPHID', 'dateTo'),
|
||||
'key_date' => array(
|
||||
'columns' => array('dateFrom', 'dateTo'),
|
||||
),
|
||||
'key_instance' => array(
|
||||
'columns' => array('instanceOfEventPHID', 'sequenceIndex'),
|
||||
|
@ -245,6 +363,19 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getInviteePHIDsForEdit() {
|
||||
$invitees = array();
|
||||
|
||||
foreach ($this->getInvitees() as $invitee) {
|
||||
if ($invitee->isUninvited()) {
|
||||
continue;
|
||||
}
|
||||
$invitees[] = $invitee->getInviteePHID();
|
||||
}
|
||||
|
||||
return $invitees;
|
||||
}
|
||||
|
||||
public function getUserInviteStatus($phid) {
|
||||
$invitees = $this->getInvitees();
|
||||
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||
|
@ -285,40 +416,12 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function generateNthGhost(
|
||||
$sequence_index,
|
||||
PhabricatorUser $actor) {
|
||||
|
||||
$frequency = $this->getFrequencyUnit();
|
||||
$modify_key = '+'.$sequence_index.' '.$frequency;
|
||||
|
||||
$instance_of = ($this->getPHID()) ?
|
||||
$this->getPHID() : $this->instanceOfEventPHID;
|
||||
|
||||
$date = $this->dateFrom;
|
||||
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor);
|
||||
$date_time->modify($modify_key);
|
||||
$date = $date_time->format('U');
|
||||
|
||||
$duration = $this->dateTo - $this->dateFrom;
|
||||
|
||||
$edit_policy = PhabricatorPolicies::POLICY_NOONE;
|
||||
|
||||
$ghost_event = id(clone $this)
|
||||
->setIsGhostEvent(true)
|
||||
->setDateFrom($date)
|
||||
->setDateTo($date + $duration)
|
||||
->setIsRecurring(true)
|
||||
->setRecurrenceFrequency($this->recurrenceFrequency)
|
||||
->setInstanceOfEventPHID($instance_of)
|
||||
->setSequenceIndex($sequence_index)
|
||||
->setEditPolicy($edit_policy);
|
||||
|
||||
return $ghost_event;
|
||||
public function getFrequencyRule() {
|
||||
return idx($this->recurrenceFrequency, 'rule');
|
||||
}
|
||||
|
||||
public function getFrequencyUnit() {
|
||||
$frequency = idx($this->recurrenceFrequency, 'rule');
|
||||
$frequency = $this->getFrequencyRule();
|
||||
|
||||
switch ($frequency) {
|
||||
case 'daily':
|
||||
|
@ -335,11 +438,13 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
|
||||
public function getURI() {
|
||||
$uri = '/'.$this->getMonogram();
|
||||
if ($this->isGhostEvent) {
|
||||
$uri = $uri.'/'.$this->sequenceIndex;
|
||||
if ($this->getIsGhostEvent()) {
|
||||
$base = $this->getParentEvent()->getURI();
|
||||
$sequence = $this->getSequenceIndex();
|
||||
return "{$base}/{$sequence}/";
|
||||
}
|
||||
return $uri;
|
||||
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function getParentEvent() {
|
||||
|
@ -351,63 +456,142 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getIsCancelled() {
|
||||
$instance_of = $this->instanceOfEventPHID;
|
||||
if ($instance_of != null && $this->getIsParentCancelled()) {
|
||||
return true;
|
||||
}
|
||||
return $this->isCancelled;
|
||||
public function isParentEvent() {
|
||||
return ($this->getIsRecurring() && !$this->getInstanceOfEventPHID());
|
||||
}
|
||||
|
||||
public function getIsRecurrenceParent() {
|
||||
if ($this->isRecurring && !$this->instanceOfEventPHID) {
|
||||
public function isChildEvent() {
|
||||
return ($this->instanceOfEventPHID !== null);
|
||||
}
|
||||
|
||||
public function isCancelledEvent() {
|
||||
if ($this->getIsCancelled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isChildEvent()) {
|
||||
if ($this->getParentEvent()->getIsCancelled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getIsRecurrenceException() {
|
||||
if ($this->instanceOfEventPHID && !$this->isGhostEvent) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
public function renderEventDate(
|
||||
PhabricatorUser $viewer,
|
||||
$show_end) {
|
||||
|
||||
if ($show_end) {
|
||||
$min_date = PhabricatorTime::getDateTimeFromEpoch(
|
||||
$this->getViewerDateFrom(),
|
||||
$viewer);
|
||||
|
||||
$max_date = PhabricatorTime::getDateTimeFromEpoch(
|
||||
$this->getViewerDateTo(),
|
||||
$viewer);
|
||||
|
||||
$min_day = $min_date->format('Y m d');
|
||||
$max_day = $max_date->format('Y m d');
|
||||
|
||||
$show_end_date = ($min_day != $max_day);
|
||||
} else {
|
||||
$show_end_date = false;
|
||||
}
|
||||
|
||||
public function getIsParentCancelled() {
|
||||
if ($this->instanceOfEventPHID == null) {
|
||||
return false;
|
||||
}
|
||||
$min_epoch = $this->getViewerDateFrom();
|
||||
$max_epoch = $this->getViewerDateTo();
|
||||
|
||||
$recurring_event = $this->getParentEvent();
|
||||
if ($recurring_event->getIsCancelled()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDuration() {
|
||||
$seconds = $this->dateTo - $this->dateFrom;
|
||||
$minutes = round($seconds / 60, 1);
|
||||
$hours = round($minutes / 60, 3);
|
||||
$days = round($hours / 24, 2);
|
||||
|
||||
$duration = '';
|
||||
|
||||
if ($days >= 1) {
|
||||
if ($this->getIsAllDay()) {
|
||||
if ($show_end_date) {
|
||||
return pht(
|
||||
'%s day(s)',
|
||||
round($days, 1));
|
||||
} else if ($hours >= 1) {
|
||||
'%s - %s, All Day',
|
||||
phabricator_date($min_epoch, $viewer),
|
||||
phabricator_date($max_epoch, $viewer));
|
||||
} else {
|
||||
return pht(
|
||||
'%s hour(s)',
|
||||
round($hours, 1));
|
||||
} else if ($minutes >= 1) {
|
||||
'%s, All Day',
|
||||
phabricator_date($min_epoch, $viewer));
|
||||
}
|
||||
} else if ($show_end_date) {
|
||||
return pht(
|
||||
'%s minute(s)',
|
||||
round($minutes, 0));
|
||||
'%s - %s',
|
||||
phabricator_datetime($min_epoch, $viewer),
|
||||
phabricator_datetime($max_epoch, $viewer));
|
||||
} else if ($show_end) {
|
||||
return pht(
|
||||
'%s - %s',
|
||||
phabricator_datetime($min_epoch, $viewer),
|
||||
phabricator_time($max_epoch, $viewer));
|
||||
} else {
|
||||
return pht(
|
||||
'%s',
|
||||
phabricator_datetime($min_epoch, $viewer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getDisplayIcon(PhabricatorUser $viewer) {
|
||||
if ($this->isCancelledEvent()) {
|
||||
return 'fa-times';
|
||||
}
|
||||
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$status = $this->getUserInviteStatus($viewer->getPHID());
|
||||
switch ($status) {
|
||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||
return 'fa-check-circle';
|
||||
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
||||
return 'fa-user-plus';
|
||||
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
||||
return 'fa-times';
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getIcon();
|
||||
}
|
||||
|
||||
public function getDisplayIconColor(PhabricatorUser $viewer) {
|
||||
if ($this->isCancelledEvent()) {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$status = $this->getUserInviteStatus($viewer->getPHID());
|
||||
switch ($status) {
|
||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||
return 'green';
|
||||
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
||||
return 'green';
|
||||
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
||||
return 'grey';
|
||||
}
|
||||
}
|
||||
|
||||
return 'bluegrey';
|
||||
}
|
||||
|
||||
public function getDisplayIconLabel(PhabricatorUser $viewer) {
|
||||
if ($this->isCancelledEvent()) {
|
||||
return pht('Cancelled');
|
||||
}
|
||||
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$status = $this->getUserInviteStatus($viewer->getPHID());
|
||||
switch ($status) {
|
||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||
return pht('Attending');
|
||||
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
||||
return pht('Invited');
|
||||
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
||||
return pht('Declined');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/* -( Markup Interface )--------------------------------------------------- */
|
||||
|
||||
|
||||
|
@ -475,8 +659,8 @@ 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();
|
||||
// The host of an event can always view and edit it.
|
||||
$user_phid = $this->getHostPHID();
|
||||
if ($user_phid) {
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
if ($viewer_phid == $user_phid) {
|
||||
|
@ -497,11 +681,12 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
|
||||
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.');
|
||||
return pht(
|
||||
'The host of an event can always view and edit it. Users who are '.
|
||||
'invited to an event can always view it.');
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
|
@ -528,14 +713,14 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
|
||||
|
||||
public function isAutomaticallySubscribed($phid) {
|
||||
return ($phid == $this->getUserPHID());
|
||||
return ($phid == $this->getHostPHID());
|
||||
}
|
||||
|
||||
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
|
||||
|
||||
|
||||
public function getUsersToNotifyOfTokenGiven() {
|
||||
return array($this->getUserPHID());
|
||||
return array($this->getHostPHID());
|
||||
}
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
@ -564,4 +749,32 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
return new PhabricatorCalendarEventFulltextEngine();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorConduitResultInterface )---------------------------------- */
|
||||
|
||||
|
||||
public function getFieldSpecificationsForConduit() {
|
||||
return array(
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('name')
|
||||
->setType('string')
|
||||
->setDescription(pht('The name of the event.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('description')
|
||||
->setType('string')
|
||||
->setDescription(pht('The event description.')),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit() {
|
||||
return array(
|
||||
'name' => $this->getName(),
|
||||
'description' => $this->getDescription(),
|
||||
);
|
||||
}
|
||||
|
||||
public function getConduitSearchAttachments() {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,23 +1,7 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventTransaction
|
||||
extends PhabricatorApplicationTransaction {
|
||||
|
||||
const TYPE_NAME = 'calendar.name';
|
||||
const TYPE_START_DATE = 'calendar.startdate';
|
||||
const TYPE_END_DATE = 'calendar.enddate';
|
||||
const TYPE_DESCRIPTION = 'calendar.description';
|
||||
const TYPE_CANCEL = 'calendar.cancel';
|
||||
const TYPE_ALL_DAY = 'calendar.allday';
|
||||
const TYPE_ICON = 'calendar.icon';
|
||||
const TYPE_INVITE = 'calendar.invite';
|
||||
|
||||
const TYPE_RECURRING = 'calendar.recurring';
|
||||
const TYPE_FREQUENCY = 'calendar.frequency';
|
||||
const TYPE_RECURRENCE_END_DATE = 'calendar.recurrenceenddate';
|
||||
|
||||
const TYPE_INSTANCE_OF_EVENT = 'calendar.instanceofevent';
|
||||
const TYPE_SEQUENCE_INDEX = 'calendar.sequenceindex';
|
||||
extends PhabricatorModularTransaction {
|
||||
|
||||
const MAILTAG_RESCHEDULE = 'calendar-reschedule';
|
||||
const MAILTAG_CONTENT = 'calendar-content';
|
||||
|
@ -35,534 +19,21 @@ final class PhabricatorCalendarEventTransaction
|
|||
return new PhabricatorCalendarEventTransactionComment();
|
||||
}
|
||||
|
||||
public function getRequiredHandlePHIDs() {
|
||||
$phids = parent::getRequiredHandlePHIDs();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_NAME:
|
||||
case self::TYPE_START_DATE:
|
||||
case self::TYPE_END_DATE:
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_CANCEL:
|
||||
case self::TYPE_ALL_DAY:
|
||||
case self::TYPE_RECURRING:
|
||||
case self::TYPE_FREQUENCY:
|
||||
case self::TYPE_RECURRENCE_END_DATE:
|
||||
case self::TYPE_INSTANCE_OF_EVENT:
|
||||
case self::TYPE_SEQUENCE_INDEX:
|
||||
$phids[] = $this->getObjectPHID();
|
||||
break;
|
||||
case self::TYPE_INVITE:
|
||||
$new = $this->getNewValue();
|
||||
foreach ($new as $phid => $status) {
|
||||
$phids[] = $phid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
$old = $this->getOldValue();
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_START_DATE:
|
||||
case self::TYPE_END_DATE:
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_CANCEL:
|
||||
case self::TYPE_ALL_DAY:
|
||||
case self::TYPE_INVITE:
|
||||
case self::TYPE_RECURRING:
|
||||
case self::TYPE_FREQUENCY:
|
||||
case self::TYPE_RECURRENCE_END_DATE:
|
||||
case self::TYPE_INSTANCE_OF_EVENT:
|
||||
case self::TYPE_SEQUENCE_INDEX:
|
||||
return ($old === null);
|
||||
}
|
||||
return parent::shouldHide();
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_ICON:
|
||||
return $this->getNewValue();
|
||||
case self::TYPE_NAME:
|
||||
case self::TYPE_START_DATE:
|
||||
case self::TYPE_END_DATE:
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_ALL_DAY:
|
||||
case self::TYPE_CANCEL:
|
||||
case self::TYPE_RECURRING:
|
||||
case self::TYPE_FREQUENCY:
|
||||
case self::TYPE_RECURRENCE_END_DATE:
|
||||
case self::TYPE_INSTANCE_OF_EVENT:
|
||||
case self::TYPE_SEQUENCE_INDEX:
|
||||
return 'fa-pencil';
|
||||
break;
|
||||
case self::TYPE_INVITE:
|
||||
return 'fa-user-plus';
|
||||
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 event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed the name of this event from %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$old,
|
||||
$new);
|
||||
}
|
||||
case self::TYPE_START_DATE:
|
||||
if ($old) {
|
||||
return pht(
|
||||
'%s edited the start date of this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
break;
|
||||
case self::TYPE_END_DATE:
|
||||
if ($old) {
|
||||
return pht(
|
||||
'%s edited the end date of this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
break;
|
||||
case self::TYPE_DESCRIPTION:
|
||||
return pht(
|
||||
"%s updated the event's description.",
|
||||
$this->renderHandleLink($author_phid));
|
||||
case self::TYPE_ALL_DAY:
|
||||
if ($new) {
|
||||
return pht(
|
||||
'%s made this an all day event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s converted this from an all day event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
case self::TYPE_ICON:
|
||||
$set = new PhabricatorCalendarIconSet();
|
||||
return pht(
|
||||
'%s set this event\'s icon to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$set->getIconLabel($new));
|
||||
break;
|
||||
case self::TYPE_CANCEL:
|
||||
if ($new) {
|
||||
return pht(
|
||||
'%s cancelled this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s reinstated this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
case self::TYPE_INVITE:
|
||||
$text = null;
|
||||
|
||||
if (count($old) === 1
|
||||
&& count($new) === 1
|
||||
&& isset($old[$author_phid])) {
|
||||
// user joined/declined/accepted event themself
|
||||
$old_status = $old[$author_phid];
|
||||
$new_status = $new[$author_phid];
|
||||
|
||||
if ($old_status !== $new_status) {
|
||||
switch ($new_status) {
|
||||
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
||||
$text = pht(
|
||||
'%s has joined this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||
$text = pht(
|
||||
'%s is attending this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
||||
case PhabricatorCalendarEventInvitee::STATUS_UNINVITED:
|
||||
$text = pht(
|
||||
'%s has declined this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
default:
|
||||
$text = pht(
|
||||
'%s has changed their status for this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// user changed status for many users
|
||||
$self_joined = null;
|
||||
$self_declined = null;
|
||||
$added = array();
|
||||
$uninvited = array();
|
||||
|
||||
foreach ($new as $phid => $status) {
|
||||
if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED
|
||||
|| $status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING) {
|
||||
// added users
|
||||
$added[] = $phid;
|
||||
} else if (
|
||||
$status == PhabricatorCalendarEventInvitee::STATUS_DECLINED
|
||||
|| $status == PhabricatorCalendarEventInvitee::STATUS_UNINVITED) {
|
||||
$uninvited[] = $phid;
|
||||
}
|
||||
}
|
||||
|
||||
$count_added = count($added);
|
||||
$count_uninvited = count($uninvited);
|
||||
$added_text = null;
|
||||
$uninvited_text = null;
|
||||
|
||||
if ($count_added > 0 && $count_uninvited == 0) {
|
||||
$added_text = $this->renderHandleList($added);
|
||||
$text = pht('%s invited %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$added_text);
|
||||
} else if ($count_added > 0 && $count_uninvited > 0) {
|
||||
$added_text = $this->renderHandleList($added);
|
||||
$uninvited_text = $this->renderHandleList($uninvited);
|
||||
$text = pht('%s invited %s and uninvited %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$added_text,
|
||||
$uninvited_text);
|
||||
} else if ($count_added == 0 && $count_uninvited > 0) {
|
||||
$uninvited_text = $this->renderHandleList($uninvited);
|
||||
$text = pht('%s uninvited %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$uninvited_text);
|
||||
} else {
|
||||
$text = pht('%s updated the invitee list.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
case self::TYPE_RECURRING:
|
||||
$text = pht('%s made this event recurring.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
return $text;
|
||||
case self::TYPE_FREQUENCY:
|
||||
$text = '';
|
||||
switch ($new['rule']) {
|
||||
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
|
||||
$text = pht('%s set this event to repeat daily.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
|
||||
$text = pht('%s set this event to repeat weekly.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
|
||||
$text = pht('%s set this event to repeat monthly.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
|
||||
$text = pht('%s set this event to repeat yearly.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
break;
|
||||
}
|
||||
return $text;
|
||||
case self::TYPE_RECURRENCE_END_DATE:
|
||||
$text = pht('%s has changed the recurrence end date of this event.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
return $text;
|
||||
case self::TYPE_INSTANCE_OF_EVENT:
|
||||
case self::TYPE_SEQUENCE_INDEX:
|
||||
return pht('Recurring event has been updated.');
|
||||
}
|
||||
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_START_DATE:
|
||||
if ($old) {
|
||||
$old = phabricator_datetime($old, $viewer);
|
||||
$new = phabricator_datetime($new, $viewer);
|
||||
return pht(
|
||||
'%s changed the start date of %s from %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$old,
|
||||
$new);
|
||||
}
|
||||
break;
|
||||
case self::TYPE_END_DATE:
|
||||
if ($old) {
|
||||
$old = phabricator_datetime($old, $viewer);
|
||||
$new = phabricator_datetime($new, $viewer);
|
||||
return pht(
|
||||
'%s edited the end date of %s from %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$old,
|
||||
$new);
|
||||
}
|
||||
break;
|
||||
case self::TYPE_DESCRIPTION:
|
||||
return pht(
|
||||
'%s updated the description of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
case self::TYPE_ALL_DAY:
|
||||
if ($new) {
|
||||
return pht(
|
||||
'%s made %s an all day event.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s converted %s from an all day event.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
}
|
||||
case self::TYPE_ICON:
|
||||
$set = new PhabricatorCalendarIconSet();
|
||||
return pht(
|
||||
'%s set the icon for %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$set->getIconLabel($new));
|
||||
case self::TYPE_CANCEL:
|
||||
if ($new) {
|
||||
return pht(
|
||||
'%s cancelled %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s reinstated %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
}
|
||||
case self::TYPE_INVITE:
|
||||
$text = null;
|
||||
|
||||
if (count($old) === 1
|
||||
&& count($new) === 1
|
||||
&& isset($old[$author_phid])) {
|
||||
// user joined/declined/accepted event themself
|
||||
$old_status = $old[$author_phid];
|
||||
$new_status = $new[$author_phid];
|
||||
|
||||
if ($old_status !== $new_status) {
|
||||
switch ($new_status) {
|
||||
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
|
||||
$text = pht(
|
||||
'%s has joined %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
|
||||
$text = pht(
|
||||
'%s is attending %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
|
||||
case PhabricatorCalendarEventInvitee::STATUS_UNINVITED:
|
||||
$text = pht(
|
||||
'%s has declined %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
default:
|
||||
$text = pht(
|
||||
'%s has changed their status of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// user changed status for many users
|
||||
$self_joined = null;
|
||||
$self_declined = null;
|
||||
$added = array();
|
||||
$uninvited = array();
|
||||
|
||||
foreach ($new as $phid => $status) {
|
||||
if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED
|
||||
|| $status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING) {
|
||||
// added users
|
||||
$added[] = $phid;
|
||||
} else if (
|
||||
$status == PhabricatorCalendarEventInvitee::STATUS_DECLINED
|
||||
|| $status == PhabricatorCalendarEventInvitee::STATUS_UNINVITED) {
|
||||
$uninvited[] = $phid;
|
||||
}
|
||||
}
|
||||
|
||||
$count_added = count($added);
|
||||
$count_uninvited = count($uninvited);
|
||||
$added_text = null;
|
||||
$uninvited_text = null;
|
||||
|
||||
if ($count_added > 0 && $count_uninvited == 0) {
|
||||
$added_text = $this->renderHandleList($added);
|
||||
$text = pht('%s invited %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$added_text,
|
||||
$this->renderHandleLink($object_phid));
|
||||
} else if ($count_added > 0 && $count_uninvited > 0) {
|
||||
$added_text = $this->renderHandleList($added);
|
||||
$uninvited_text = $this->renderHandleList($uninvited);
|
||||
$text = pht('%s invited %s and uninvited %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$added_text,
|
||||
$uninvited_text,
|
||||
$this->renderHandleLink($object_phid));
|
||||
} else if ($count_added == 0 && $count_uninvited > 0) {
|
||||
$uninvited_text = $this->renderHandleList($uninvited);
|
||||
$text = pht('%s uninvited %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$uninvited_text,
|
||||
$this->renderHandleLink($object_phid));
|
||||
} else {
|
||||
$text = pht('%s updated the invitee list of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
case self::TYPE_RECURRING:
|
||||
$text = pht('%s made %s a recurring event.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
return $text;
|
||||
case self::TYPE_FREQUENCY:
|
||||
$text = '';
|
||||
switch ($new['rule']) {
|
||||
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
|
||||
$text = pht('%s set %s to repeat daily.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
|
||||
$text = pht('%s set %s to repeat weekly.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
|
||||
$text = pht('%s set %s to repeat monthly.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
|
||||
$text = pht('%s set %s to repeat yearly.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
break;
|
||||
}
|
||||
return $text;
|
||||
case self::TYPE_RECURRENCE_END_DATE:
|
||||
$text = pht('%s set the recurrence end date of %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid),
|
||||
$new);
|
||||
return $text;
|
||||
case self::TYPE_INSTANCE_OF_EVENT:
|
||||
case self::TYPE_SEQUENCE_INDEX:
|
||||
return pht('Recurring event has been updated.');
|
||||
}
|
||||
|
||||
return parent::getTitleForFeed();
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_NAME:
|
||||
case self::TYPE_START_DATE:
|
||||
case self::TYPE_END_DATE:
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_CANCEL:
|
||||
case self::TYPE_INVITE:
|
||||
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 getBaseTransactionClass() {
|
||||
return 'PhabricatorCalendarEventTransactionType';
|
||||
}
|
||||
|
||||
public function getMailTags() {
|
||||
$tags = array();
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_NAME:
|
||||
case self::TYPE_DESCRIPTION:
|
||||
case self::TYPE_INVITE:
|
||||
case self::TYPE_ICON:
|
||||
case PhabricatorCalendarEventNameTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventDescriptionTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE:
|
||||
$tags[] = self::MAILTAG_CONTENT;
|
||||
break;
|
||||
case self::TYPE_START_DATE:
|
||||
case self::TYPE_END_DATE:
|
||||
case self::TYPE_CANCEL:
|
||||
case PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE:
|
||||
case PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE:
|
||||
$tags[] = self::MAILTAG_RESCHEDULE;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
final class AphrontCalendarEventView extends AphrontView {
|
||||
|
||||
private $userPHID;
|
||||
private $hostPHID;
|
||||
private $name;
|
||||
private $epochStart;
|
||||
private $epochEnd;
|
||||
|
@ -12,7 +12,27 @@ final class AphrontCalendarEventView extends AphrontView {
|
|||
private $uri;
|
||||
private $isAllDay;
|
||||
private $icon;
|
||||
private $iconColor;
|
||||
private $canEdit;
|
||||
private $isCancelled;
|
||||
|
||||
public function setIconColor($icon_color) {
|
||||
$this->iconColor = $icon_color;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIconColor() {
|
||||
return $this->iconColor;
|
||||
}
|
||||
|
||||
public function setIsCancelled($is_cancelled) {
|
||||
$this->isCancelled = $is_cancelled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsCancelled() {
|
||||
return $this->isCancelled;
|
||||
}
|
||||
|
||||
public function setURI($uri) {
|
||||
$this->uri = $uri;
|
||||
|
@ -39,13 +59,13 @@ final class AphrontCalendarEventView extends AphrontView {
|
|||
return $this->viewerIsInvited;
|
||||
}
|
||||
|
||||
public function setUserPHID($user_phid) {
|
||||
$this->userPHID = $user_phid;
|
||||
public function setHostPHID($host_phid) {
|
||||
$this->hostPHID = $host_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserPHID() {
|
||||
return $this->userPHID;
|
||||
public function getHostPHID() {
|
||||
return $this->hostPHID;
|
||||
}
|
||||
|
||||
public function setName($name) {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventAcceptTransaction
|
||||
extends PhabricatorCalendarEventReplyTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.accept';
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s is attending this event.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s is attending %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventAllDayTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.allday';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return (int)$object->getIsAllDay();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setIsAllDay($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
if ($this->getNewValue()) {
|
||||
return pht(
|
||||
'%s changed this as an all day event.',
|
||||
$this->renderAuthor());
|
||||
} else {
|
||||
return pht(
|
||||
'%s conveted this from an all day event.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
if ($this->getNewValue()) {
|
||||
return pht(
|
||||
'%s changed %s to an all day event.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
} else {
|
||||
return pht(
|
||||
'%s converted %s from an all day event.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventCancelTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.cancel';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return (int)$object->getIsCancelled();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setIsCancelled($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
if ($this->getNewValue()) {
|
||||
return pht(
|
||||
'%s cancelled this event.',
|
||||
$this->renderAuthor());
|
||||
} else {
|
||||
return pht(
|
||||
'%s reinstated this event.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
if ($this->getNewValue()) {
|
||||
return pht(
|
||||
'%s cancelled %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
} else {
|
||||
return pht(
|
||||
'%s reinstated %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarEventDateTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
abstract protected function getInvalidDateMessage();
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return $value->getEpoch();
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getNewValue()->isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = $this->getInvalidDateMessage();
|
||||
$errors[] = $this->newInvalidError($message, $xaction);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventDeclineTransaction
|
||||
extends PhabricatorCalendarEventReplyTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.decline';
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return PhabricatorCalendarEventInvitee::STATUS_DECLINED;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s declined this event.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s declined %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventDescriptionTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.description';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getDescription();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setDescription($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s updated the event description.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s updated the event description for %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
public function hasChangeDetailView() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function newChangeDetailView() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
return id(new PhabricatorApplicationTransactionTextDiffDetailView())
|
||||
->setViewer($viewer)
|
||||
->setOldText($this->getOldValue())
|
||||
->setNewText($this->getNewValue());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventEndDateTransaction
|
||||
extends PhabricatorCalendarEventDateTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.enddate';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getDateTo();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$actor = $this->getActor();
|
||||
|
||||
$object->setDateTo($value);
|
||||
|
||||
$object->setAllDayDateTo(
|
||||
$object->getDateEpochForTimezone(
|
||||
$value,
|
||||
$actor->getTimeZone(),
|
||||
'Y-m-d 23:59:00',
|
||||
null,
|
||||
new DateTimeZone('UTC')));
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed the end date for this event from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldDate(),
|
||||
$this->renderNewDate());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s changed the end date for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldDate(),
|
||||
$this->renderNewDate());
|
||||
}
|
||||
|
||||
protected function getInvalidDateMessage() {
|
||||
return pht('End date is invalid.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventFrequencyTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.frequency';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getFrequencyRule();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setRecurrenceFrequency(
|
||||
array(
|
||||
'rule' => $value,
|
||||
));
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$frequency = $this->getFrequencyRule($this->getNewValue());
|
||||
switch ($frequency) {
|
||||
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
|
||||
return pht(
|
||||
'%s set this event to repeat daily.',
|
||||
$this->renderAuthor());
|
||||
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
|
||||
return pht(
|
||||
'%s set this event to repeat weekly.',
|
||||
$this->renderAuthor());
|
||||
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
|
||||
return pht(
|
||||
'%s set this event to repeat monthly.',
|
||||
$this->renderAuthor());
|
||||
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
|
||||
return pht(
|
||||
'%s set this event to repeat yearly.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
$frequency = $this->getFrequencyRule($this->getNewValue());
|
||||
switch ($frequency) {
|
||||
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
|
||||
return pht(
|
||||
'%s set %s to repeat daily.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
|
||||
return pht(
|
||||
'%s set %s to repeat weekly.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
|
||||
return pht(
|
||||
'%s set %s to repeat monthly.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
|
||||
return pht(
|
||||
'%s set %s to repeat yearly.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
}
|
||||
|
||||
private function getFrequencyRule($value) {
|
||||
if (is_array($value)) {
|
||||
$value = idx($value, 'rule');
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventHostTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.host';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getHostPHID();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setHostPHID($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed the host of this event from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldHandle(),
|
||||
$this->renderNewHandle());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s changed the host of %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldHandle(),
|
||||
$this->renderNewHandle());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$host_phid = $xaction->getNewValue();
|
||||
if (!$host_phid) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('Event host is required.'));
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withPHIDs(array($host_phid))
|
||||
->executeOne();
|
||||
if (!$user) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Host PHID "%s" is not a valid user PHID.',
|
||||
$host_phid));
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventIconTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.icon';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getIcon();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setIcon($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old = $this->getIconLabel($this->getOldValue());
|
||||
$new = $this->getIconLabel($this->getNewValue());
|
||||
|
||||
return pht(
|
||||
'%s changed the event icon from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderValue($old),
|
||||
$this->renderValue($new));
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
$old = $this->getIconLabel($this->getOldValue());
|
||||
$new = $this->getIconLabel($this->getNewValue());
|
||||
|
||||
return pht(
|
||||
'%s changed the icon for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderValue($old),
|
||||
$this->renderValue($new));
|
||||
}
|
||||
|
||||
private function getIconLabel($icon) {
|
||||
$set = new PhabricatorCalendarIconSet();
|
||||
return $set->getIconLabel($icon);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventInviteTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.invite';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
|
||||
|
||||
$invitees = $object->getInvitees();
|
||||
foreach ($invitees as $key => $invitee) {
|
||||
if ($invitee->getStatus() == $status_uninvited) {
|
||||
unset($invitees[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(mpull($invitees, 'getInviteePHID'));
|
||||
}
|
||||
|
||||
private function generateChangeMap($object, $new_value) {
|
||||
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
|
||||
$status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
|
||||
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
|
||||
|
||||
$old = $this->generateOldValue($object);
|
||||
|
||||
$add = array_diff($new_value, $old);
|
||||
$rem = array_diff($old, $new_value);
|
||||
|
||||
$map = array();
|
||||
foreach ($add as $phid) {
|
||||
$map[$phid] = $status_invited;
|
||||
}
|
||||
foreach ($rem as $phid) {
|
||||
$map[$phid] = $status_uninvited;
|
||||
}
|
||||
|
||||
// If we're creating this event and the actor is inviting themselves,
|
||||
// mark them as attending.
|
||||
if ($this->isNewObject()) {
|
||||
$acting_phid = $this->getActingAsPHID();
|
||||
if (isset($map[$acting_phid])) {
|
||||
$map[$acting_phid] = $status_attending;
|
||||
}
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$map = $this->generateChangeMap($object, $value);
|
||||
|
||||
$invitees = $object->getInvitees();
|
||||
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||
|
||||
foreach ($map as $phid => $status) {
|
||||
$invitee = idx($invitees, $phid);
|
||||
if (!$invitee) {
|
||||
$invitee = id(new PhabricatorCalendarEventInvitee())
|
||||
->setEventPHID($object->getPHID())
|
||||
->setInviteePHID($phid)
|
||||
->setInviterPHID($this->getActingAsPHID());
|
||||
$invitees[] = $invitee;
|
||||
}
|
||||
$invitee->setStatus($status)
|
||||
->save();
|
||||
}
|
||||
|
||||
$object->attachInvitees($invitees);
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$actor = $this->getActor();
|
||||
|
||||
$errors = array();
|
||||
|
||||
$old = $object->getInvitees();
|
||||
$old = mpull($old, null, 'getInviteePHID');
|
||||
foreach ($xactions as $xaction) {
|
||||
$new = $xaction->getNewValue();
|
||||
$new = array_fuse($new);
|
||||
$add = array_diff_key($new, $old);
|
||||
if (!$add) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// In the UI, we only allow you to invite mailable objects, but there
|
||||
// is no definitive marker for "invitable object" today. Just allow
|
||||
// any valid object to be invited.
|
||||
$objects = id(new PhabricatorObjectQuery())
|
||||
->setViewer($actor)
|
||||
->withPHIDs($add)
|
||||
->execute();
|
||||
$objects = mpull($objects, null, 'getPHID');
|
||||
foreach ($add as $phid) {
|
||||
if (isset($objects[$phid])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Invitee "%s" identifies an object that does not exist or '.
|
||||
'which you do not have permission to view.',
|
||||
$phid),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-user-plus';
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
list($add, $rem) = $this->getChanges();
|
||||
|
||||
if ($add && !$rem) {
|
||||
return pht(
|
||||
'%s invited %s attendee(s): %s.',
|
||||
$this->renderAuthor(),
|
||||
phutil_count($add),
|
||||
$this->renderHandleList($add));
|
||||
} else if (!$add && $rem) {
|
||||
return pht(
|
||||
'%s uninvited %s attendee(s): %s.',
|
||||
$this->renderAuthor(),
|
||||
phutil_count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
} else {
|
||||
return pht(
|
||||
'%s invited %s attendee(s): %s; uninvinted %s attendee(s): %s.',
|
||||
$this->renderAuthor(),
|
||||
phutil_count($add),
|
||||
$this->renderHandleList($add),
|
||||
phutil_count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
list($add, $rem) = $this->getChanges();
|
||||
|
||||
if ($add && !$rem) {
|
||||
return pht(
|
||||
'%s invited %s attendee(s) to %s: %s.',
|
||||
$this->renderAuthor(),
|
||||
phutil_count($add),
|
||||
$this->renderObject(),
|
||||
$this->renderHandleList($add));
|
||||
} else if (!$add && $rem) {
|
||||
return pht(
|
||||
'%s uninvited %s attendee(s) to %s: %s.',
|
||||
$this->renderAuthor(),
|
||||
phutil_count($rem),
|
||||
$this->renderObject(),
|
||||
$this->renderHandleList($rem));
|
||||
} else {
|
||||
return pht(
|
||||
'%s updated the invite list for %s, invited %s: %s; '.
|
||||
'uninvinted %s: %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
phutil_count($add),
|
||||
$this->renderHandleList($add),
|
||||
phutil_count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
}
|
||||
}
|
||||
|
||||
private function getChanges() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$add = array_diff($new, $old);
|
||||
$rem = array_diff($old, $new);
|
||||
|
||||
return array(array_fuse($add), array_fuse($rem));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventNameTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.name';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setName($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s renamed this event from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s renamed %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
|
||||
$errors[] = $this->newRequiredError(
|
||||
pht('Events must have a name.'));
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventRecurringTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.recurring';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return (int)$object->getIsRecurring();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
return (int)$value;
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setIsRecurring($value);
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
$old = $object->getIsRecurring();
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($this->isNewObject()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($xaction->getNewValue() == $old) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'An event can only be made recurring when it is created. '.
|
||||
'You can not convert an existing event into a recurring '.
|
||||
'event or vice versa.'),
|
||||
$xaction);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarEventReplyTransaction
|
||||
extends PhabricatorCalendarEventTransactionType {
|
||||
|
||||
public function generateOldValue($object) {
|
||||
$actor_phid = $this->getActingAsPHID();
|
||||
return $object->getUserInviteStatus($actor_phid);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$acting_phid = $this->getActingAsPHID();
|
||||
|
||||
$invitees = $object->getInvitees();
|
||||
$invitees = mpull($invitees, null, 'getInviteePHID');
|
||||
|
||||
$invitee = idx($invitees, $acting_phid);
|
||||
if (!$invitee) {
|
||||
$invitee = id(new PhabricatorCalendarEventInvitee())
|
||||
->setEventPHID($object->getPHID())
|
||||
->setInviteePHID($acting_phid)
|
||||
->setInviterPHID($acting_phid);
|
||||
$invitees[$acting_phid] = $invitee;
|
||||
}
|
||||
|
||||
$invitee
|
||||
->setStatus($value)
|
||||
->save();
|
||||
|
||||
$object->attachInvitees($invitees);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventStartDateTransaction
|
||||
extends PhabricatorCalendarEventDateTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.startdate';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getDateFrom();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$actor = $this->getActor();
|
||||
|
||||
$object->setDateFrom($value);
|
||||
|
||||
$object->setAllDayDateFrom(
|
||||
$object->getDateEpochForTimezone(
|
||||
$value,
|
||||
$actor->getTimeZone(),
|
||||
'Y-m-d',
|
||||
null,
|
||||
new DateTimeZone('UTC')));
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed the start date for this event from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldDate(),
|
||||
$this->renderNewDate());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s changed the start date for %s from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderOldDate(),
|
||||
$this->renderNewDate());
|
||||
}
|
||||
|
||||
protected function getInvalidDateMessage() {
|
||||
return pht('Start date is invalid.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorCalendarEventTransactionType
|
||||
extends PhabricatorModularTransactionType {}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCalendarEventUntilDateTransaction
|
||||
extends PhabricatorCalendarEventDateTransaction {
|
||||
|
||||
const TRANSACTIONTYPE = 'calendar.recurrenceenddate';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getRecurrenceEndDate();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setRecurrenceEndDate($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed this event to repeat until %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewDate());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s changed %s to repeat until %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject(),
|
||||
$this->renderNewDate());
|
||||
}
|
||||
|
||||
protected function getInvalidDateMessage() {
|
||||
return pht('Repeat until date is invalid.');
|
||||
}
|
||||
|
||||
}
|
|
@ -314,6 +314,8 @@ final class ConpherenceThreadQuery
|
|||
|
||||
$events = array();
|
||||
if ($participant_phids) {
|
||||
// TODO: All of this Calendar code is probably extra-broken, but none
|
||||
// of it is currently reachable in the UI.
|
||||
$events = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withInvitedPHIDs($participant_phids)
|
||||
|
|
|
@ -46,7 +46,7 @@ final class DifferentialInlineCommentEditController
|
|||
throw new Exception(
|
||||
pht(
|
||||
'Changeset ID "%s" is part of diff ID "%s", but that diff '.
|
||||
'is attached to reivsion "%s", not revision "%s".',
|
||||
'is attached to revision "%s", not revision "%s".',
|
||||
$changeset_id,
|
||||
$diff->getID(),
|
||||
$diff->getRevisionID(),
|
||||
|
|
|
@ -342,18 +342,16 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
}
|
||||
|
||||
$meta['containerID'] = $detail->getID();
|
||||
$caret = phutil_tag('span', array('class' => 'caret'), '');
|
||||
|
||||
return javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => 'button grey dropdown',
|
||||
'meta' => $meta,
|
||||
'href' => idx($meta, 'detailURI', '#'),
|
||||
'target' => '_blank',
|
||||
'sigil' => 'differential-view-options',
|
||||
),
|
||||
array(pht('View Options'), $caret));
|
||||
return id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setText(pht('View Options'))
|
||||
->setIcon('fa-bars')
|
||||
->setColor(PHUIButtonView::GREY)
|
||||
->setHref(idx($meta, 'detailURI', '#'))
|
||||
->setMetadata($meta)
|
||||
->addSigil('differential-view-options');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ final class PhabricatorFileComposeController
|
|||
|
||||
$data = $composer->loadBuiltinFileData();
|
||||
|
||||
$file = PhabricatorFile::buildFromFileDataOrHash(
|
||||
$file = PhabricatorFile::newFromFileData(
|
||||
$data,
|
||||
array(
|
||||
'name' => $composer->getBuiltinDisplayName(),
|
||||
|
|
|
@ -16,6 +16,7 @@ final class PhabricatorFileQuery
|
|||
private $names;
|
||||
private $isPartial;
|
||||
private $needTransforms;
|
||||
private $builtinKeys;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -47,6 +48,11 @@ final class PhabricatorFileQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withBuiltinKeys(array $keys) {
|
||||
$this->builtinKeys = $keys;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select files which are transformations of some other file. For example,
|
||||
* you can use this query to find previously generated thumbnails of an image
|
||||
|
@ -384,6 +390,13 @@ final class PhabricatorFileQuery
|
|||
(int)$this->isPartial);
|
||||
}
|
||||
|
||||
if ($this->builtinKeys !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'builtinKey IN (%Ls)',
|
||||
$this->builtinKeys);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
protected $contentHash;
|
||||
protected $metadata = array();
|
||||
protected $mailKey;
|
||||
protected $builtinKey;
|
||||
|
||||
protected $storageEngine;
|
||||
protected $storageFormat;
|
||||
|
@ -94,6 +95,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
'isExplicitUpload' => 'bool?',
|
||||
'mailKey' => 'bytes20',
|
||||
'isPartial' => 'bool',
|
||||
'builtinKey' => 'text64?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_phid' => null,
|
||||
|
@ -116,6 +118,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
'key_partial' => array(
|
||||
'columns' => array('authorPHID', 'isPartial'),
|
||||
),
|
||||
'key_builtin' => array(
|
||||
'columns' => array('builtinKey'),
|
||||
'unique' => true,
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
@ -1070,19 +1076,11 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
public static function loadBuiltins(PhabricatorUser $user, array $builtins) {
|
||||
$builtins = mpull($builtins, null, 'getBuiltinFileKey');
|
||||
|
||||
$specs = array();
|
||||
foreach ($builtins as $key => $buitin) {
|
||||
$specs[] = array(
|
||||
'originalPHID' => PhabricatorPHIDConstants::PHID_VOID,
|
||||
'transform' => $key,
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: Anyone is allowed to access builtin files.
|
||||
|
||||
$files = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withTransforms($specs)
|
||||
->withBuiltinKeys(array_keys($builtins))
|
||||
->execute();
|
||||
|
||||
$results = array();
|
||||
|
@ -1109,12 +1107,21 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
);
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
try {
|
||||
$file = self::newFromFileData($data, $params);
|
||||
$xform = id(new PhabricatorTransformedFile())
|
||||
->setOriginalPHID(PhabricatorPHIDConstants::PHID_VOID)
|
||||
->setTransform($key)
|
||||
->setTransformedPHID($file->getPHID())
|
||||
->save();
|
||||
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withBuiltinKeys(array($key))
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Collided mid-air when generating builtin file "%s", but '.
|
||||
'then failed to load the object we collided with.',
|
||||
$key));
|
||||
}
|
||||
}
|
||||
unset($unguarded);
|
||||
|
||||
$file->attachObjectPHIDs(array());
|
||||
|
@ -1289,6 +1296,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
$builtin = idx($params, 'builtin');
|
||||
if ($builtin) {
|
||||
$this->setBuiltinName($builtin);
|
||||
$this->setBuiltinKey($builtin);
|
||||
}
|
||||
|
||||
$profile = idx($params, 'profile');
|
||||
|
|
|
@ -7,20 +7,4 @@ final class PhabricatorFileTransactionComment
|
|||
return new PhabricatorFileTransaction();
|
||||
}
|
||||
|
||||
public function shouldUseMarkupCache($field) {
|
||||
// Only cache submitted comments.
|
||||
return ($this->getTransactionPHID() != null);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
$config = parent::getConfiguration();
|
||||
$config[self::CONFIG_KEY_SCHEMA] = array(
|
||||
'key_draft' => array(
|
||||
'columns' => array('authorPHID', 'transactionPHID'),
|
||||
'unique' => true,
|
||||
),
|
||||
) + $config[self::CONFIG_KEY_SCHEMA];
|
||||
return $config;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -87,13 +87,83 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
->addPropertySection(pht('Description'), $description)
|
||||
->addPropertySection(pht('Details'), $details);
|
||||
|
||||
$graph_limit = 100;
|
||||
$task_graph = id(new ManiphestTaskGraph())
|
||||
->setViewer($viewer)
|
||||
->setSeedPHID($task->getPHID())
|
||||
->setLimit($graph_limit)
|
||||
->loadGraph();
|
||||
if (!$task_graph->isEmpty()) {
|
||||
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
|
||||
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
|
||||
$parent_map = $task_graph->getEdges($parent_type);
|
||||
$subtask_map = $task_graph->getEdges($subtask_type);
|
||||
$parent_list = idx($parent_map, $task->getPHID(), array());
|
||||
$subtask_list = idx($subtask_map, $task->getPHID(), array());
|
||||
$has_parents = (bool)$parent_list;
|
||||
$has_subtasks = (bool)$subtask_list;
|
||||
|
||||
$search_text = pht('Search...');
|
||||
|
||||
// First, get a count of direct parent tasks and subtasks. If there
|
||||
// are too many of these, we just don't draw anything. You can use
|
||||
// the search button to browse tasks with the search UI instead.
|
||||
$direct_count = count($parent_list) + count($subtask_list);
|
||||
|
||||
if ($direct_count > $graph_limit) {
|
||||
$message = pht(
|
||||
'Task graph too large to display (this task is directly connected '.
|
||||
'to more than %s other tasks). Use %s to explore connected tasks.',
|
||||
$graph_limit,
|
||||
phutil_tag('strong', array(), $search_text));
|
||||
$message = phutil_tag('em', array(), $message);
|
||||
$graph_table = id(new PHUIPropertyListView())
|
||||
->addTextContent($message);
|
||||
} else {
|
||||
// If there aren't too many direct tasks, but there are too many total
|
||||
// tasks, we'll only render directly connected tasks.
|
||||
if ($task_graph->isOverLimit()) {
|
||||
$task_graph->setRenderOnlyAdjacentNodes(true);
|
||||
}
|
||||
$graph_table = $task_graph->newGraphTable();
|
||||
$view->addPropertySection(pht('Task Graph'), $graph_table);
|
||||
}
|
||||
|
||||
$parents_uri = urisprintf(
|
||||
'/?subtaskIDs=%d#R',
|
||||
$task->getID());
|
||||
$parents_uri = $this->getApplicationURI($parents_uri);
|
||||
|
||||
$subtasks_uri = urisprintf(
|
||||
'/?parentIDs=%d#R',
|
||||
$task->getID());
|
||||
$subtasks_uri = $this->getApplicationURI($subtasks_uri);
|
||||
|
||||
$dropdown_menu = id(new PhabricatorActionListView())
|
||||
->setViewer($viewer)
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($parents_uri)
|
||||
->setName(pht('Search Parent Tasks'))
|
||||
->setDisabled(!$has_parents)
|
||||
->setIcon('fa-chevron-circle-up'))
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setHref($subtasks_uri)
|
||||
->setName(pht('Search Subtasks'))
|
||||
->setDisabled(!$has_subtasks)
|
||||
->setIcon('fa-chevron-circle-down'));
|
||||
|
||||
$graph_menu = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-search')
|
||||
->setText($search_text)
|
||||
->setDropdownMenu($dropdown_menu);
|
||||
|
||||
$graph_header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Task Graph'))
|
||||
->addActionLink($graph_menu);
|
||||
|
||||
$view->addPropertySection($graph_header, $graph_table);
|
||||
}
|
||||
|
||||
return $this->newPage()
|
||||
|
|
|
@ -20,6 +20,10 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
private $subpriorityMin;
|
||||
private $subpriorityMax;
|
||||
private $bridgedObjectPHIDs;
|
||||
private $hasOpenParents;
|
||||
private $hasOpenSubtasks;
|
||||
private $parentTaskIDs;
|
||||
private $subtaskIDs;
|
||||
|
||||
private $fullTextSearch = '';
|
||||
|
||||
|
@ -51,8 +55,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
|
||||
private $needSubscriberPHIDs;
|
||||
private $needProjectPHIDs;
|
||||
private $blockingTasks;
|
||||
private $blockedTasks;
|
||||
|
||||
public function withAuthors(array $authors) {
|
||||
$this->authorPHIDs = $authors;
|
||||
|
@ -151,32 +153,24 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* True returns tasks that are blocking other tasks only.
|
||||
* False returns tasks that are not blocking other tasks only.
|
||||
* Null returns tasks regardless of blocking status.
|
||||
*/
|
||||
public function withBlockingTasks($mode) {
|
||||
$this->blockingTasks = $mode;
|
||||
public function withOpenSubtasks($value) {
|
||||
$this->hasOpenSubtasks = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function shouldJoinBlockingTasks() {
|
||||
return $this->blockingTasks !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True returns tasks that are blocked by other tasks only.
|
||||
* False returns tasks that are not blocked by other tasks only.
|
||||
* Null returns tasks regardless of blocked by status.
|
||||
*/
|
||||
public function withBlockedTasks($mode) {
|
||||
$this->blockedTasks = $mode;
|
||||
public function withOpenParents($value) {
|
||||
$this->hasOpenParents = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function shouldJoinBlockedTasks() {
|
||||
return $this->blockedTasks !== null;
|
||||
public function withParentTaskIDs(array $ids) {
|
||||
$this->parentTaskIDs = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withSubtaskIDs(array $ids) {
|
||||
$this->subtaskIDs = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withDateCreatedBefore($date_created_before) {
|
||||
|
@ -335,7 +329,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
$where[] = $this->buildStatusWhereClause($conn);
|
||||
$where[] = $this->buildDependenciesWhereClause($conn);
|
||||
$where[] = $this->buildOwnerWhereClause($conn);
|
||||
$where[] = $this->buildFullTextWhereClause($conn);
|
||||
|
||||
|
@ -526,71 +519,64 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$fulltext_results);
|
||||
}
|
||||
|
||||
private function buildDependenciesWhereClause(
|
||||
AphrontDatabaseConnection $conn) {
|
||||
|
||||
if (!$this->shouldJoinBlockedTasks() &&
|
||||
!$this->shouldJoinBlockingTasks()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = array();
|
||||
if ($this->blockingTasks === true) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocking.dst IS NOT NULL AND blockingtask.status IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
} else if ($this->blockingTasks === false) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocking.dst IS NULL OR blockingtask.status NOT IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
}
|
||||
|
||||
if ($this->blockedTasks === true) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocked.dst IS NOT NULL AND blockedtask.status IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
} else if ($this->blockedTasks === false) {
|
||||
$parts[] = qsprintf(
|
||||
$conn,
|
||||
'blocked.dst IS NULL OR blockedtask.status NOT IN (%Ls)',
|
||||
ManiphestTaskStatus::getOpenStatusConstants());
|
||||
}
|
||||
|
||||
return '('.implode(') OR (', $parts).')';
|
||||
}
|
||||
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$open_statuses = ManiphestTaskStatus::getOpenStatusConstants();
|
||||
$edge_table = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
|
||||
$task_table = $this->newResultObject()->getTableName();
|
||||
|
||||
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
|
||||
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
|
||||
|
||||
$joins = array();
|
||||
|
||||
if ($this->shouldJoinBlockingTasks()) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'LEFT JOIN %T blocking ON blocking.src = task.phid '.
|
||||
'AND blocking.type = %d '.
|
||||
'LEFT JOIN %T blockingtask ON blocking.dst = blockingtask.phid',
|
||||
$edge_table,
|
||||
ManiphestTaskDependedOnByTaskEdgeType::EDGECONST,
|
||||
id(new ManiphestTask())->getTableName());
|
||||
if ($this->hasOpenParents !== null) {
|
||||
if ($this->hasOpenParents) {
|
||||
$join_type = 'JOIN';
|
||||
} else {
|
||||
$join_type = 'LEFT JOIN';
|
||||
}
|
||||
if ($this->shouldJoinBlockedTasks()) {
|
||||
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'LEFT JOIN %T blocked ON blocked.src = task.phid '.
|
||||
'AND blocked.type = %d '.
|
||||
'LEFT JOIN %T blockedtask ON blocked.dst = blockedtask.phid',
|
||||
$conn,
|
||||
'%Q %T e_parent
|
||||
ON e_parent.src = task.phid
|
||||
AND e_parent.type = %d
|
||||
%Q %T parent
|
||||
ON e_parent.dst = parent.phid
|
||||
AND parent.status IN (%Ls)',
|
||||
$join_type,
|
||||
$edge_table,
|
||||
ManiphestTaskDependsOnTaskEdgeType::EDGECONST,
|
||||
id(new ManiphestTask())->getTableName());
|
||||
$parent_type,
|
||||
$join_type,
|
||||
$task_table,
|
||||
$open_statuses);
|
||||
}
|
||||
|
||||
if ($this->hasOpenSubtasks !== null) {
|
||||
if ($this->hasOpenSubtasks) {
|
||||
$join_type = 'JOIN';
|
||||
} else {
|
||||
$join_type = 'LEFT JOIN';
|
||||
}
|
||||
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'%Q %T e_subtask
|
||||
ON e_subtask.src = task.phid
|
||||
AND e_subtask.type = %d
|
||||
%Q %T subtask
|
||||
ON e_subtask.dst = subtask.phid
|
||||
AND subtask.status IN (%Ls)',
|
||||
$join_type,
|
||||
$edge_table,
|
||||
$subtask_type,
|
||||
$join_type,
|
||||
$task_table,
|
||||
$open_statuses);
|
||||
}
|
||||
|
||||
if ($this->subscriberPHIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'JOIN %T e_ccs ON e_ccs.src = task.phid '.
|
||||
'AND e_ccs.type = %s '.
|
||||
'AND e_ccs.dst in (%Ls)',
|
||||
|
@ -604,7 +590,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$ignore_group_phids = $this->getIgnoreGroupedProjectPHIDs();
|
||||
if ($ignore_group_phids) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
|
||||
AND projectGroup.type = %d
|
||||
AND projectGroup.dst NOT IN (%Ls)',
|
||||
|
@ -613,29 +599,62 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
$ignore_group_phids);
|
||||
} else {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'LEFT JOIN %T projectGroup ON task.phid = projectGroup.src
|
||||
AND projectGroup.type = %d',
|
||||
$edge_table,
|
||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
|
||||
}
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'LEFT JOIN %T projectGroupName
|
||||
ON projectGroup.dst = projectGroupName.indexedObjectPHID',
|
||||
id(new ManiphestNameIndex())->getTableName());
|
||||
break;
|
||||
}
|
||||
|
||||
$joins[] = parent::buildJoinClauseParts($conn_r);
|
||||
if ($this->parentTaskIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'JOIN %T e_has_parent
|
||||
ON e_has_parent.src = task.phid
|
||||
AND e_has_parent.type = %d
|
||||
JOIN %T has_parent
|
||||
ON e_has_parent.dst = has_parent.phid
|
||||
AND has_parent.id IN (%Ld)',
|
||||
$edge_table,
|
||||
$parent_type,
|
||||
$task_table,
|
||||
$this->parentTaskIDs);
|
||||
}
|
||||
|
||||
if ($this->subtaskIDs !== null) {
|
||||
$joins[] = qsprintf(
|
||||
$conn,
|
||||
'JOIN %T e_has_subtask
|
||||
ON e_has_subtask.src = task.phid
|
||||
AND e_has_subtask.type = %d
|
||||
JOIN %T has_subtask
|
||||
ON e_has_subtask.dst = has_subtask.phid
|
||||
AND has_subtask.id IN (%Ld)',
|
||||
$edge_table,
|
||||
$subtask_type,
|
||||
$task_table,
|
||||
$this->subtaskIDs);
|
||||
}
|
||||
|
||||
$joins[] = parent::buildJoinClauseParts($conn);
|
||||
|
||||
return $joins;
|
||||
}
|
||||
|
||||
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
|
||||
$joined_multiple_rows = $this->shouldJoinBlockingTasks() ||
|
||||
$this->shouldJoinBlockedTasks() ||
|
||||
($this->shouldGroupQueryResultRows());
|
||||
$joined_multiple_rows =
|
||||
($this->hasOpenParents !== null) ||
|
||||
($this->hasOpenSubtasks !== null) ||
|
||||
($this->parentTaskIDs !== null) ||
|
||||
($this->subtaskIDs !== null) ||
|
||||
$this->shouldGroupQueryResultRows();
|
||||
|
||||
$joined_project_name = ($this->groupBy == self::GROUP_PROJECT);
|
||||
|
||||
|
@ -652,6 +671,30 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected function buildHavingClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$having = parent::buildHavingClauseParts($conn);
|
||||
|
||||
if ($this->hasOpenParents !== null) {
|
||||
if (!$this->hasOpenParents) {
|
||||
$having[] = qsprintf(
|
||||
$conn,
|
||||
'COUNT(parent.phid) = 0');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasOpenSubtasks !== null) {
|
||||
if (!$this->hasOpenSubtasks) {
|
||||
$having[] = qsprintf(
|
||||
$conn,
|
||||
'COUNT(subtask.phid) = 0');
|
||||
}
|
||||
}
|
||||
|
||||
return $having;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return project PHIDs which we should ignore when grouping tasks by
|
||||
* project. For example, if a user issues a query like:
|
||||
|
|
|
@ -77,19 +77,29 @@ final class ManiphestTaskSearchEngine
|
|||
->setLabel(pht('Contains Words'))
|
||||
->setKey('fulltext'),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Blocking'))
|
||||
->setKey('blocking')
|
||||
->setLabel(pht('Open Parents'))
|
||||
->setKey('hasParents')
|
||||
->setAliases(array('blocking'))
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Tasks Blocking Other Tasks'),
|
||||
pht('Hide Tasks Blocking Other Tasks')),
|
||||
pht('Show Only Tasks With Open Parents'),
|
||||
pht('Show Only Tasks Without Open Parents')),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Blocked'))
|
||||
->setKey('blocked')
|
||||
->setLabel(pht('Open Subtasks'))
|
||||
->setKey('hasSubtasks')
|
||||
->setAliases(array('blocked'))
|
||||
->setOptions(
|
||||
pht('(Show All)'),
|
||||
pht('Show Only Task Blocked By Other Tasks'),
|
||||
pht('Hide Tasks Blocked By Other Tasks')),
|
||||
pht('Show Only Tasks With Open Subtasks'),
|
||||
pht('Show Only Tasks Without Open Subtasks')),
|
||||
id(new PhabricatorIDsSearchField())
|
||||
->setLabel(pht('Parent IDs'))
|
||||
->setKey('parentIDs')
|
||||
->setAliases(array('parentID')),
|
||||
id(new PhabricatorIDsSearchField())
|
||||
->setLabel(pht('Subtask IDs'))
|
||||
->setKey('subtaskIDs')
|
||||
->setAliases(array('subtaskID')),
|
||||
id(new PhabricatorSearchSelectField())
|
||||
->setLabel(pht('Group By'))
|
||||
->setKey('group')
|
||||
|
@ -121,8 +131,10 @@ final class ManiphestTaskSearchEngine
|
|||
'statuses',
|
||||
'priorities',
|
||||
'fulltext',
|
||||
'blocking',
|
||||
'blocked',
|
||||
'hasParents',
|
||||
'hasSubtasks',
|
||||
'parentIDs',
|
||||
'subtaskIDs',
|
||||
'group',
|
||||
'order',
|
||||
'ids',
|
||||
|
@ -182,18 +194,26 @@ final class ManiphestTaskSearchEngine
|
|||
$query->withDateModifiedBefore($map['modifiedEnd']);
|
||||
}
|
||||
|
||||
if ($map['blocking'] !== null) {
|
||||
$query->withBlockingTasks($map['blocking']);
|
||||
if ($map['hasParents'] !== null) {
|
||||
$query->withOpenParents($map['hasParents']);
|
||||
}
|
||||
|
||||
if ($map['blocked'] !== null) {
|
||||
$query->withBlockedTasks($map['blocked']);
|
||||
if ($map['hasSubtasks'] !== null) {
|
||||
$query->withOpenSubtasks($map['hasSubtasks']);
|
||||
}
|
||||
|
||||
if (strlen($map['fulltext'])) {
|
||||
$query->withFullTextSearch($map['fulltext']);
|
||||
}
|
||||
|
||||
if ($map['parentIDs']) {
|
||||
$query->withParentTaskIDs($map['parentIDs']);
|
||||
}
|
||||
|
||||
if ($map['subtaskIDs']) {
|
||||
$query->withSubtaskIDs($map['subtaskIDs']);
|
||||
}
|
||||
|
||||
$group = idx($map, 'group');
|
||||
$group = idx($this->getGroupValues(), $group);
|
||||
if ($group) {
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesApplication extends PhabricatorApplication {
|
||||
|
||||
public function getName() {
|
||||
return pht('Packages');
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht('Publish Software');
|
||||
}
|
||||
|
||||
public function getFlavorText() {
|
||||
return pht('Applications and Extensions');
|
||||
}
|
||||
|
||||
public function getBaseURI() {
|
||||
return '/packages/';
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-gift';
|
||||
}
|
||||
|
||||
public function isPrototype() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
return array(
|
||||
PhabricatorPackagesCreatePublisherCapability::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
PhabricatorPackagesPublisherDefaultEditCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default edit policy for newly created publishers.'),
|
||||
'template' => PhabricatorPackagesPublisherPHIDType::TYPECONST,
|
||||
'default' => PhabricatorPolicies::POLICY_NOONE,
|
||||
),
|
||||
PhabricatorPackagesPackageDefaultViewCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default edit policy for newly created packages.'),
|
||||
'template' => PhabricatorPackagesPackagePHIDType::TYPECONST,
|
||||
),
|
||||
PhabricatorPackagesPackageDefaultEditCapability::CAPABILITY => array(
|
||||
'caption' => pht('Default view policy for newly created packages.'),
|
||||
'template' => PhabricatorPackagesPackagePHIDType::TYPECONST,
|
||||
'default' => PhabricatorPolicies::POLICY_NOONE,
|
||||
),
|
||||
);
|
||||
}
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/package/' => array(
|
||||
'(?P<publisherKey>[^/]+)/' => array(
|
||||
'' => 'PhabricatorPackagesPublisherViewController',
|
||||
'(?P<packageKey>[^/]+)/' => array(
|
||||
'' => 'PhabricatorPackagesPackageViewController',
|
||||
'(?P<versionKey>[^/]+)/' =>
|
||||
'PhabricatorPackagesVersionViewController',
|
||||
),
|
||||
),
|
||||
),
|
||||
'/packages/' => array(
|
||||
'publisher/' => array(
|
||||
$this->getQueryRoutePattern() =>
|
||||
'PhabricatorPackagesPublisherListController',
|
||||
$this->getEditRoutePattern('edit/') =>
|
||||
'PhabricatorPackagesPublisherEditController',
|
||||
),
|
||||
'package/' => array(
|
||||
$this->getQueryRoutePattern() =>
|
||||
'PhabricatorPackagesPackageListController',
|
||||
$this->getEditRoutePattern('edit/') =>
|
||||
'PhabricatorPackagesPackageEditController',
|
||||
),
|
||||
'version/' => array(
|
||||
$this->getQueryRoutePattern() =>
|
||||
'PhabricatorPackagesVersionListController',
|
||||
$this->getEditRoutePattern('edit/') =>
|
||||
'PhabricatorPackagesVersionEditController',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesCreatePublisherCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'packages.publisher.create';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Can Create Publishers');
|
||||
}
|
||||
|
||||
public function describeCapabilityRejection() {
|
||||
return pht('You do not have permission to create publishers.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageDefaultEditCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'packages.package.default.edit';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Package Edit Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageDefaultViewCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'packages.package.default.view';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Package View Policy');
|
||||
}
|
||||
|
||||
public function shouldAllowPublicPolicySetting() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPublisherDefaultEditCapability
|
||||
extends PhabricatorPolicyCapability {
|
||||
|
||||
const CAPABILITY = 'packages.publisher.default.edit';
|
||||
|
||||
public function getCapabilityName() {
|
||||
return pht('Default Publisher Edit Policy');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageEditConduitAPIMethod
|
||||
extends PhabricatorEditEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'packages.package.edit';
|
||||
}
|
||||
|
||||
public function newEditEngine() {
|
||||
return new PhabricatorPackagesPackageEditEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht(
|
||||
'Apply transactions to create a new package or edit an existing one.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'packages.package.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new PhabricatorPackagesPackageSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Read information about packages.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPublisherEditConduitAPIMethod
|
||||
extends PhabricatorEditEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'packages.publisher.edit';
|
||||
}
|
||||
|
||||
public function newEditEngine() {
|
||||
return new PhabricatorPackagesPublisherEditEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht(
|
||||
'Apply transactions to create a new publisher or edit an existing one.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPublisherSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'packages.publisher.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new PhabricatorPackagesPublisherSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Read information about publishers.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesVersionEditConduitAPIMethod
|
||||
extends PhabricatorEditEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'packages.version.edit';
|
||||
}
|
||||
|
||||
public function newEditEngine() {
|
||||
return new PhabricatorPackagesVersionEditEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht(
|
||||
'Apply transactions to create a new version or edit an existing one.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesVersionSearchConduitAPIMethod
|
||||
extends PhabricatorSearchEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'packages.version.search';
|
||||
}
|
||||
|
||||
public function newSearchEngine() {
|
||||
return new PhabricatorPackagesVersionSearchEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht('Read information about versions.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPackagesController extends PhabricatorController {}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPackagesPackageController
|
||||
extends PhabricatorPackagesController {}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageEditController
|
||||
extends PhabricatorPackagesPackageController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorPackagesPackageEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageListController
|
||||
extends PhabricatorPackagesPackageController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorPackagesPackageSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
id(new PhabricatorPackagesPackageEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPackageViewController
|
||||
extends PhabricatorPackagesPackageController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$publisher_key = $request->getURIData('publisherKey');
|
||||
$package_key = $request->getURIData('packageKey');
|
||||
$full_key = $publisher_key.'/'.$package_key;
|
||||
|
||||
$package = id(new PhabricatorPackagesPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withFullKeys(array($full_key))
|
||||
->executeOne();
|
||||
if (!$package) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$publisher = $package->getPublisher();
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($publisher->getName(), $publisher->getURI())
|
||||
->addTextCrumb($package->getName())
|
||||
->setBorder(true);
|
||||
|
||||
$header = $this->buildHeaderView($package);
|
||||
$curtain = $this->buildCurtain($package);
|
||||
|
||||
$versions_view = $this->buildVersionsView($package);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$package,
|
||||
new PhabricatorPackagesPackageTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$package_view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setCurtain($curtain)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$versions_view,
|
||||
$timeline,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(
|
||||
array(
|
||||
$package->getPHID(),
|
||||
))
|
||||
->appendChild($package_view);
|
||||
}
|
||||
|
||||
|
||||
private function buildHeaderView(PhabricatorPackagesPackage $package) {
|
||||
$viewer = $this->getViewer();
|
||||
$name = $package->getName();
|
||||
|
||||
return id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($name)
|
||||
->setPolicyObject($package)
|
||||
->setHeaderIcon('fa-gift');
|
||||
}
|
||||
|
||||
private function buildCurtain(PhabricatorPackagesPackage $package) {
|
||||
$viewer = $this->getViewer();
|
||||
$curtain = $this->newCurtainView($package);
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$package,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$id = $package->getID();
|
||||
$edit_uri = $this->getApplicationURI("package/edit/{$id}/");
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Package'))
|
||||
->setIcon('fa-pencil')
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($edit_uri));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
private function buildVersionsView(PhabricatorPackagesPackage $package) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$versions = id(new PhabricatorPackagesVersionQuery())
|
||||
->setViewer($viewer)
|
||||
->withPackagePHIDs(array($package->getPHID()))
|
||||
->setLimit(25)
|
||||
->execute();
|
||||
|
||||
$versions_list = id(new PhabricatorPackagesVersionListView())
|
||||
->setViewer($viewer)
|
||||
->setVersions($versions);
|
||||
|
||||
$all_href = urisprintf(
|
||||
'version/?package=%s#R',
|
||||
$package->getPHID());
|
||||
$all_href = $this->getApplicationURI($all_href);
|
||||
|
||||
$view_all = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-search')
|
||||
->setText(pht('View All'))
|
||||
->setHref($all_href);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Versions'))
|
||||
->addActionLink($view_all);
|
||||
|
||||
$versions_view = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setObjectList($versions_list);
|
||||
|
||||
return $versions_view;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPackagesPublisherController
|
||||
extends PhabricatorPackagesController {}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPublisherEditController
|
||||
extends PhabricatorPackagesPublisherController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorPackagesPublisherEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPublisherListController
|
||||
extends PhabricatorPackagesPublisherController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorPackagesPublisherSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
id(new PhabricatorPackagesPublisherEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesPublisherViewController
|
||||
extends PhabricatorPackagesPublisherController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$publisher_key = $request->getURIData('publisherKey');
|
||||
|
||||
$publisher = id(new PhabricatorPackagesPublisherQuery())
|
||||
->setViewer($viewer)
|
||||
->withPublisherKeys(array($publisher_key))
|
||||
->executeOne();
|
||||
if (!$publisher) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb(
|
||||
pht('Publishers'),
|
||||
$this->getApplicationURI('publisher/'))
|
||||
->addTextCrumb($publisher->getName())
|
||||
->setBorder(true);
|
||||
|
||||
$header = $this->buildHeaderView($publisher);
|
||||
$curtain = $this->buildCurtain($publisher);
|
||||
|
||||
$packages_view = $this->buildPackagesView($publisher);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$publisher,
|
||||
new PhabricatorPackagesPublisherTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$publisher_view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setCurtain($curtain)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$packages_view,
|
||||
$timeline,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(
|
||||
array(
|
||||
$publisher->getPHID(),
|
||||
))
|
||||
->appendChild($publisher_view);
|
||||
}
|
||||
|
||||
|
||||
private function buildHeaderView(PhabricatorPackagesPublisher $publisher) {
|
||||
$viewer = $this->getViewer();
|
||||
$name = $publisher->getName();
|
||||
|
||||
return id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($name)
|
||||
->setPolicyObject($publisher)
|
||||
->setHeaderIcon('fa-paw');
|
||||
}
|
||||
|
||||
private function buildCurtain(PhabricatorPackagesPublisher $publisher) {
|
||||
$viewer = $this->getViewer();
|
||||
$curtain = $this->newCurtainView($publisher);
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$publisher,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$id = $publisher->getID();
|
||||
$edit_uri = $this->getApplicationURI("publisher/edit/{$id}/");
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Publisher'))
|
||||
->setIcon('fa-pencil')
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($edit_uri));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
private function buildPackagesView(PhabricatorPackagesPublisher $publisher) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$packages = id(new PhabricatorPackagesPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withPublisherPHIDs(array($publisher->getPHID()))
|
||||
->setLimit(25)
|
||||
->execute();
|
||||
|
||||
$packages_list = id(new PhabricatorPackagesPackageListView())
|
||||
->setViewer($viewer)
|
||||
->setPackages($packages);
|
||||
|
||||
$all_href = urisprintf(
|
||||
'package/?publisher=%s#R',
|
||||
$publisher->getPHID());
|
||||
$all_href = $this->getApplicationURI($all_href);
|
||||
|
||||
$view_all = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setIcon('fa-search')
|
||||
->setText(pht('View All'))
|
||||
->setHref($all_href);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Packages'))
|
||||
->addActionLink($view_all);
|
||||
|
||||
$packages_view = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setObjectList($packages_list);
|
||||
|
||||
return $packages_view;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPackagesVersionController
|
||||
extends PhabricatorPackagesController {}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesVersionEditController
|
||||
extends PhabricatorPackagesVersionController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorPackagesVersionEditEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesVersionListController
|
||||
extends PhabricatorPackagesVersionController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
return id(new PhabricatorPackagesVersionSearchEngine())
|
||||
->setController($this)
|
||||
->buildResponse();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
id(new PhabricatorPackagesVersionEditEngine())
|
||||
->setViewer($this->getViewer())
|
||||
->addActionToCrumbs($crumbs);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPackagesVersionViewController
|
||||
extends PhabricatorPackagesVersionController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$publisher_key = $request->getURIData('publisherKey');
|
||||
$package_key = $request->getURIData('packageKey');
|
||||
$full_key = $publisher_key.'/'.$package_key;
|
||||
$version_key = $request->getURIData('versionKey');
|
||||
|
||||
$version = id(new PhabricatorPackagesVersionQuery())
|
||||
->setViewer($viewer)
|
||||
->withFullKeys(array($full_key))
|
||||
->withNames(array($version_key))
|
||||
->executeOne();
|
||||
if (!$version) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$package = $version->getPackage();
|
||||
$publisher = $package->getPublisher();
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($publisher->getName(), $publisher->getURI())
|
||||
->addTextCrumb($package->getName(), $package->getURI())
|
||||
->addTextCrumb($version->getName())
|
||||
->setBorder(true);
|
||||
|
||||
$header = $this->buildHeaderView($version);
|
||||
$curtain = $this->buildCurtain($version);
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$version,
|
||||
new PhabricatorPackagesVersionTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$version_view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setCurtain($curtain)
|
||||
->setMainColumn($timeline);
|
||||
|
||||
return $this->newPage()
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(
|
||||
array(
|
||||
$version->getPHID(),
|
||||
))
|
||||
->appendChild($version_view);
|
||||
}
|
||||
|
||||
|
||||
private function buildHeaderView(PhabricatorPackagesVersion $version) {
|
||||
$viewer = $this->getViewer();
|
||||
$name = $version->getName();
|
||||
|
||||
return id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($name)
|
||||
->setPolicyObject($version)
|
||||
->setHeaderIcon('fa-tag');
|
||||
}
|
||||
|
||||
private function buildCurtain(PhabricatorPackagesVersion $version) {
|
||||
$viewer = $this->getViewer();
|
||||
$curtain = $this->newCurtainView($version);
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$version,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$id = $version->getID();
|
||||
$edit_uri = $this->getApplicationURI("version/edit/{$id}/");
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit Version'))
|
||||
->setIcon('fa-pencil')
|
||||
->setDisabled(!$can_edit)
|
||||
->setHref($edit_uri));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorPackagesEditEngine
|
||||
extends PhabricatorEditEngine {
|
||||
|
||||
public function isEngineConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getEngineApplicationClass() {
|
||||
return 'PhabricatorPackagesApplication';
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue