mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-15 17:21:10 +01:00
Merge branch 'master' into redesign-2015
This commit is contained in:
commit
1ccdb941b9
32 changed files with 971 additions and 243 deletions
|
@ -8,7 +8,7 @@
|
|||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '61e69662',
|
||||
'core.pkg.js' => 'f1e8abd7',
|
||||
'core.pkg.js' => 'a590b451',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => 'fe951924',
|
||||
'differential.pkg.js' => 'ebef29b1',
|
||||
|
@ -327,8 +327,9 @@ return array(
|
|||
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
||||
'rsrc/js/application/aphlict/Aphlict.js' => '5359e785',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '031cee25',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'b1a59974',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'fb20ac8d',
|
||||
'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',
|
||||
|
@ -428,7 +429,7 @@ return array(
|
|||
'rsrc/js/core/KeyboardShortcut.js' => '1ae869f2',
|
||||
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
|
||||
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
|
||||
'rsrc/js/core/Notification.js' => '0c6946e7',
|
||||
'rsrc/js/core/Notification.js' => 'ccf1cbf8',
|
||||
'rsrc/js/core/Prefab.js' => '6920d200',
|
||||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '5c93c52c',
|
||||
|
@ -532,7 +533,7 @@ return array(
|
|||
'javelin-aphlict' => '5359e785',
|
||||
'javelin-behavior' => '61cbc29a',
|
||||
'javelin-behavior-aphlict-dropdown' => '031cee25',
|
||||
'javelin-behavior-aphlict-listen' => 'b1a59974',
|
||||
'javelin-behavior-aphlict-listen' => 'fb20ac8d',
|
||||
'javelin-behavior-aphlict-status' => 'ea681761',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
|
||||
|
@ -554,6 +555,7 @@ return array(
|
|||
'javelin-behavior-dashboard-query-panel-select' => '453c5375',
|
||||
'javelin-behavior-dashboard-tab-panel' => 'd4eecc63',
|
||||
'javelin-behavior-day-view' => '5c46cff2',
|
||||
'javelin-behavior-desktop-notifications-control' => 'edd1ba66',
|
||||
'javelin-behavior-device' => 'a205cf28',
|
||||
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
|
||||
'javelin-behavior-differential-comment-jump' => '4fdb476d',
|
||||
|
@ -724,7 +726,7 @@ return array(
|
|||
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
|
||||
'phabricator-main-menu-view' => '3cd48671',
|
||||
'phabricator-nav-view-css' => '0ecd30a1',
|
||||
'phabricator-notification' => '0c6946e7',
|
||||
'phabricator-notification' => 'ccf1cbf8',
|
||||
'phabricator-notification-css' => '9c279160',
|
||||
'phabricator-notification-menu-css' => 'f31c0bde',
|
||||
'phabricator-object-selector-css' => '029a133d',
|
||||
|
@ -903,13 +905,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-router',
|
||||
),
|
||||
'0c6946e7' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-util',
|
||||
'phabricator-notification-css',
|
||||
),
|
||||
'0f764c35' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1650,20 +1645,6 @@ return array(
|
|||
'javelin-util',
|
||||
'phabricator-shaped-request',
|
||||
),
|
||||
'b1a59974' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-aphlict',
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-uri',
|
||||
'javelin-dom',
|
||||
'javelin-json',
|
||||
'javelin-router',
|
||||
'javelin-util',
|
||||
'javelin-leader',
|
||||
'javelin-sound',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'b1f0ccee' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1798,6 +1779,13 @@ return array(
|
|||
'javelin-stratcom',
|
||||
'phabricator-phtize',
|
||||
),
|
||||
'ccf1cbf8' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-util',
|
||||
'phabricator-notification-css',
|
||||
),
|
||||
'cf86d16a' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1942,6 +1930,13 @@ return array(
|
|||
'phabricator-phtize',
|
||||
'javelin-dom',
|
||||
),
|
||||
'edd1ba66' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'eeaa9e5a' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -2017,6 +2012,20 @@ return array(
|
|||
'javelin-vector',
|
||||
'javelin-magical-init',
|
||||
),
|
||||
'fb20ac8d' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-aphlict',
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-uri',
|
||||
'javelin-dom',
|
||||
'javelin-json',
|
||||
'javelin-router',
|
||||
'javelin-util',
|
||||
'javelin-leader',
|
||||
'javelin-sound',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'fbe497e7' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-util',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_metamta.metamta_mail
|
||||
ADD phid VARBINARY(64) NOT NULL AFTER id;
|
22
resources/sql/autopatches/20150622.metamta.2.phid-mig.php
Normal file
22
resources/sql/autopatches/20150622.metamta.2.phid-mig.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
$table = new PhabricatorMetaMTAMail();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
echo pht('Assigning PHIDs to mails...')."\n";
|
||||
foreach (new LiskMigrationIterator($table) as $mail) {
|
||||
$id = $mail->getID();
|
||||
|
||||
echo pht('Updating mail %d...', $id)."\n";
|
||||
if ($mail->getPHID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'UPDATE %T SET phid = %s WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$table->generatePHID(),
|
||||
$id);
|
||||
}
|
||||
echo pht('Done.')."\n";
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_metamta.metamta_mail
|
||||
ADD UNIQUE KEY `key_phid` (phid);
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_metamta.metamta_mail
|
||||
ADD actorPHID VARBINARY(64) AFTER phid;
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
$table = new PhabricatorMetaMTAMail();
|
||||
$conn_w = $table->establishConnection('w');
|
||||
|
||||
echo pht('Assigning actorPHIDs to mails...')."\n";
|
||||
foreach (new LiskMigrationIterator($table) as $mail) {
|
||||
$id = $mail->getID();
|
||||
|
||||
echo pht('Updating mail %d...', $id)."\n";
|
||||
if ($mail->getActorPHID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$actor_phid = $mail->getFrom();
|
||||
if ($actor_phid === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'UPDATE %T SET actorPHID = %s WHERE id = %d',
|
||||
$table->getTableName(),
|
||||
$actor_phid,
|
||||
$id);
|
||||
}
|
||||
echo pht('Done.')."\n";
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_metamta.metamta_mail
|
||||
ADD KEY `key_actorPHID` (actorPHID);
|
|
@ -1792,6 +1792,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorDatabaseSetupCheck' => 'applications/config/check/PhabricatorDatabaseSetupCheck.php',
|
||||
'PhabricatorDateTimeSettingsPanel' => 'applications/settings/panel/PhabricatorDateTimeSettingsPanel.php',
|
||||
'PhabricatorDebugController' => 'applications/system/controller/PhabricatorDebugController.php',
|
||||
'PhabricatorDesktopNotificationsSettingsPanel' => 'applications/settings/panel/PhabricatorDesktopNotificationsSettingsPanel.php',
|
||||
'PhabricatorDestructibleInterface' => 'applications/system/interface/PhabricatorDestructibleInterface.php',
|
||||
'PhabricatorDestructionEngine' => 'applications/system/engine/PhabricatorDestructionEngine.php',
|
||||
'PhabricatorDeveloperConfigOptions' => 'applications/config/option/PhabricatorDeveloperConfigOptions.php',
|
||||
|
@ -2109,6 +2110,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAMail' => 'applications/metamta/storage/PhabricatorMetaMTAMail.php',
|
||||
'PhabricatorMetaMTAMailBody' => 'applications/metamta/view/PhabricatorMetaMTAMailBody.php',
|
||||
'PhabricatorMetaMTAMailBodyTestCase' => 'applications/metamta/view/__tests__/PhabricatorMetaMTAMailBodyTestCase.php',
|
||||
'PhabricatorMetaMTAMailPHIDType' => 'applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php',
|
||||
'PhabricatorMetaMTAMailQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailQuery.php',
|
||||
'PhabricatorMetaMTAMailSection' => 'applications/metamta/view/PhabricatorMetaMTAMailSection.php',
|
||||
'PhabricatorMetaMTAMailTestCase' => 'applications/metamta/storage/__tests__/PhabricatorMetaMTAMailTestCase.php',
|
||||
'PhabricatorMetaMTAMailableDatasource' => 'applications/metamta/typeahead/PhabricatorMetaMTAMailableDatasource.php',
|
||||
|
@ -2136,7 +2139,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorNamedQueryQuery' => 'applications/search/query/PhabricatorNamedQueryQuery.php',
|
||||
'PhabricatorNavigationRemarkupRule' => 'infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php',
|
||||
'PhabricatorNeverTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorNeverTriggerClock.php',
|
||||
'PhabricatorNotificationAdHocFeedStory' => 'applications/notification/feed/PhabricatorNotificationAdHocFeedStory.php',
|
||||
'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php',
|
||||
'PhabricatorNotificationClearController' => 'applications/notification/controller/PhabricatorNotificationClearController.php',
|
||||
'PhabricatorNotificationClient' => 'applications/notification/client/PhabricatorNotificationClient.php',
|
||||
|
@ -2150,6 +2152,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorNotificationStatusController' => 'applications/notification/controller/PhabricatorNotificationStatusController.php',
|
||||
'PhabricatorNotificationStatusView' => 'applications/notification/view/PhabricatorNotificationStatusView.php',
|
||||
'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php',
|
||||
'PhabricatorNotificationTestFeedStory' => 'applications/notification/feed/PhabricatorNotificationTestFeedStory.php',
|
||||
'PhabricatorNotificationUIExample' => 'applications/uiexample/examples/PhabricatorNotificationUIExample.php',
|
||||
'PhabricatorNotificationsApplication' => 'applications/notification/application/PhabricatorNotificationsApplication.php',
|
||||
'PhabricatorNuanceApplication' => 'applications/nuance/application/PhabricatorNuanceApplication.php',
|
||||
|
@ -2550,6 +2553,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchDAO' => 'applications/search/storage/PhabricatorSearchDAO.php',
|
||||
'PhabricatorSearchDatasource' => 'applications/search/typeahead/PhabricatorSearchDatasource.php',
|
||||
'PhabricatorSearchDatasourceField' => 'applications/search/field/PhabricatorSearchDatasourceField.php',
|
||||
'PhabricatorSearchDateControlField' => 'applications/search/field/PhabricatorSearchDateControlField.php',
|
||||
'PhabricatorSearchDateField' => 'applications/search/field/PhabricatorSearchDateField.php',
|
||||
'PhabricatorSearchDeleteController' => 'applications/search/controller/PhabricatorSearchDeleteController.php',
|
||||
'PhabricatorSearchDocument' => 'applications/search/storage/document/PhabricatorSearchDocument.php',
|
||||
|
@ -5090,6 +5094,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarEvent' => array(
|
||||
'PhabricatorCalendarDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorMarkupInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
|
@ -5390,6 +5395,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorDatabaseSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorDateTimeSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorDebugController' => 'PhabricatorController',
|
||||
'PhabricatorDesktopNotificationsSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorDestructionEngine' => 'Phobject',
|
||||
'PhabricatorDeveloperConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorDeveloperPreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
|
@ -5745,9 +5751,14 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAEmailBodyParser' => 'Phobject',
|
||||
'PhabricatorMetaMTAEmailBodyParserTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMetaMTAErrorMailAction' => 'PhabricatorSystemAction',
|
||||
'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO',
|
||||
'PhabricatorMetaMTAMail' => array(
|
||||
'PhabricatorMetaMTADAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorMetaMTAMailBody' => 'Phobject',
|
||||
'PhabricatorMetaMTAMailBodyTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMetaMTAMailPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorMetaMTAMailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorMetaMTAMailSection' => 'Phobject',
|
||||
'PhabricatorMetaMTAMailTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMetaMTAMailableDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
|
@ -5778,7 +5789,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorNamedQueryQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorNavigationRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorNeverTriggerClock' => 'PhabricatorTriggerClock',
|
||||
'PhabricatorNotificationAdHocFeedStory' => 'PhabricatorFeedStory',
|
||||
'PhabricatorNotificationBuilder' => 'Phobject',
|
||||
'PhabricatorNotificationClearController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationClient' => 'Phobject',
|
||||
|
@ -5792,6 +5802,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorNotificationStatusController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationStatusView' => 'AphrontTagView',
|
||||
'PhabricatorNotificationTestController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationTestFeedStory' => 'PhabricatorFeedStory',
|
||||
'PhabricatorNotificationUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorNotificationsApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorNuanceApplication' => 'PhabricatorApplication',
|
||||
|
@ -6288,6 +6299,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSearchDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorSearchDatasourceField' => 'PhabricatorSearchTokenizerField',
|
||||
'PhabricatorSearchDateControlField' => 'PhabricatorSearchField',
|
||||
'PhabricatorSearchDateField' => 'PhabricatorSearchField',
|
||||
'PhabricatorSearchDeleteController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorSearchDocument' => 'PhabricatorSearchDAO',
|
||||
|
|
|
@ -140,6 +140,15 @@ final class PhabricatorCalendarEventEditController
|
|||
$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();
|
||||
|
@ -167,6 +176,7 @@ final class PhabricatorCalendarEventEditController
|
|||
$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');
|
||||
|
@ -262,6 +272,12 @@ final class PhabricatorCalendarEventEditController
|
|||
->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) {
|
||||
|
@ -437,6 +453,13 @@ final class PhabricatorCalendarEventEditController
|
|||
->setValue($end_disabled);
|
||||
}
|
||||
|
||||
$projects = id(new AphrontFormTokenizerControl())
|
||||
->setLabel(pht('Projects'))
|
||||
->setName('projects')
|
||||
->setValue($projects)
|
||||
->setUser($viewer)
|
||||
->setDatasource(new PhabricatorProjectDatasource());
|
||||
|
||||
$description = id(new PhabricatorRemarkupControl())
|
||||
->setLabel(pht('Description'))
|
||||
->setName('description')
|
||||
|
@ -511,6 +534,7 @@ final class PhabricatorCalendarEventEditController
|
|||
->appendControl($edit_policies)
|
||||
->appendControl($subscribers)
|
||||
->appendControl($invitees)
|
||||
->appendChild($projects)
|
||||
->appendChild($description)
|
||||
->appendChild($icon);
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ final class PhabricatorCalendarEventQuery
|
|||
|
||||
private $generateGhosts = false;
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorCalendarEvent();
|
||||
}
|
||||
|
||||
public function setGenerateGhosts($generate_ghosts) {
|
||||
$this->generateGhosts = $generate_ghosts;
|
||||
return $this;
|
||||
|
|
|
@ -15,66 +15,137 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
return 'PhabricatorCalendarApplication';
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
|
||||
$saved->setParameter(
|
||||
'rangeStart',
|
||||
$this->readDateFromRequest($request, 'rangeStart'));
|
||||
|
||||
$saved->setParameter(
|
||||
'rangeEnd',
|
||||
$this->readDateFromRequest($request, 'rangeEnd'));
|
||||
|
||||
$saved->setParameter(
|
||||
'upcoming',
|
||||
$this->readBoolFromRequest($request, 'upcoming'));
|
||||
|
||||
$saved->setParameter(
|
||||
'invitedPHIDs',
|
||||
$this->readUsersFromRequest($request, 'invited'));
|
||||
|
||||
$saved->setParameter(
|
||||
'creatorPHIDs',
|
||||
$this->readUsersFromRequest($request, 'creators'));
|
||||
|
||||
$saved->setParameter(
|
||||
'isCancelled',
|
||||
$request->getStr('isCancelled'));
|
||||
|
||||
$saved->setParameter(
|
||||
'display',
|
||||
$request->getStr('display'));
|
||||
|
||||
return $saved;
|
||||
public function newQuery() {
|
||||
return new PhabricatorCalendarEventQuery();
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new PhabricatorCalendarEventQuery())
|
||||
->setGenerateGhosts(true);
|
||||
protected function shouldShowOrderField() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
return array(
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Created By'))
|
||||
->setKey('creatorPHIDs')
|
||||
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
|
||||
id(new PhabricatorSearchDatasourceField())
|
||||
->setLabel(pht('Invited'))
|
||||
->setKey('invitedPHIDs')
|
||||
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
|
||||
id(new PhabricatorSearchDateControlField())
|
||||
->setLabel(pht('Occurs After'))
|
||||
->setKey('rangeStart'),
|
||||
id(new PhabricatorSearchDateControlField())
|
||||
->setLabel(pht('Occurs Before'))
|
||||
->setKey('rangeEnd')
|
||||
->setAliases(array('rangeEnd')),
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setKey('upcoming')
|
||||
->setOptions(array(
|
||||
'upcoming' => pht('Show only upcoming events.'),
|
||||
)),
|
||||
id(new PhabricatorSearchSelectField())
|
||||
->setLabel(pht('Cancelled Events'))
|
||||
->setKey('isCancelled')
|
||||
->setOptions($this->getCancelledOptions())
|
||||
->setDefault('active'),
|
||||
id(new PhabricatorSearchSelectField())
|
||||
->setLabel(pht('Display Options'))
|
||||
->setKey('display')
|
||||
->setOptions($this->getViewOptions())
|
||||
->setDefault('month'),
|
||||
);
|
||||
}
|
||||
|
||||
private function getCancelledOptions() {
|
||||
return array(
|
||||
'active' => pht('Active Events Only'),
|
||||
'cancelled' => pht('Cancelled Events Only'),
|
||||
'both' => pht('Both Cancelled and Active Events'),
|
||||
);
|
||||
}
|
||||
|
||||
private function getViewOptions() {
|
||||
return array(
|
||||
'month' => pht('Month View'),
|
||||
'day' => pht('Day View'),
|
||||
'list' => pht('List View'),
|
||||
);
|
||||
}
|
||||
|
||||
protected function buildQueryFromParameters(array $map) {
|
||||
$query = $this->newQuery();
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
if ($map['creatorPHIDs']) {
|
||||
$query->withCreatorPHIDs($map['creatorPHIDs']);
|
||||
}
|
||||
|
||||
if ($map['invitedPHIDs']) {
|
||||
$query->withInvitedPHIDs($map['invitedPHIDs']);
|
||||
}
|
||||
|
||||
$range_start = $map['rangeStart'];
|
||||
$range_end = $map['rangeEnd'];
|
||||
$display = $map['display'];
|
||||
|
||||
if ($map['upcoming'] && $map['upcoming'][0] == 'upcoming') {
|
||||
$upcoming = true;
|
||||
} else {
|
||||
$upcoming = false;
|
||||
}
|
||||
|
||||
list($range_start, $range_end) = $this->getQueryDateRange(
|
||||
$range_start,
|
||||
$range_end,
|
||||
$display,
|
||||
$upcoming);
|
||||
|
||||
$query->withDateRange($range_start, $range_end);
|
||||
|
||||
switch ($map['isCancelled']) {
|
||||
case 'active':
|
||||
$query->withIsCancelled(false);
|
||||
break;
|
||||
case 'cancelled':
|
||||
$query->withIsCancelled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return $query->setGenerateGhosts(true);
|
||||
}
|
||||
|
||||
private function getQueryDateRange(
|
||||
$start_date_wild,
|
||||
$end_date_wild,
|
||||
$display,
|
||||
$upcoming) {
|
||||
|
||||
$start_date_value = $this->getSafeDate($start_date_wild);
|
||||
$end_date_value = $this->getSafeDate($end_date_wild);
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
$timezone = new DateTimeZone($viewer->getTimezoneIdentifier());
|
||||
$min_range = null;
|
||||
$max_range = null;
|
||||
|
||||
$min_range = $this->getDateFrom($saved)->getEpoch();
|
||||
$max_range = $this->getDateTo($saved)->getEpoch();
|
||||
$min_range = $start_date_value->getEpoch();
|
||||
$max_range = $end_date_value->getEpoch();
|
||||
|
||||
$user_datasource = id(new PhabricatorPeopleUserFunctionDatasource())
|
||||
->setViewer($viewer);
|
||||
|
||||
if ($this->isMonthView($saved) ||
|
||||
$this->isDayView($saved)) {
|
||||
if ($display == 'month' || $display == 'day') {
|
||||
list($start_year, $start_month, $start_day) =
|
||||
$this->getDisplayYearAndMonthAndDay($saved);
|
||||
$this->getDisplayYearAndMonthAndDay($min_range, $max_range, $display);
|
||||
|
||||
$start_day = new DateTime(
|
||||
"{$start_year}-{$start_month}-{$start_day}",
|
||||
$timezone);
|
||||
$next = clone $start_day;
|
||||
|
||||
if ($this->isMonthView($saved)) {
|
||||
if ($display == 'month') {
|
||||
$next->modify('+1 month');
|
||||
} else if ($this->isDayView($saved)) {
|
||||
$next->modify('+6 day');
|
||||
} else if ($display == 'day') {
|
||||
$next->modify('+7 day');
|
||||
}
|
||||
|
||||
$display_start = $start_day->format('U');
|
||||
|
@ -92,7 +163,7 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
if (!$min_range || ($min_range < $display_start)) {
|
||||
$min_range = $display_start;
|
||||
|
||||
if ($this->isMonthView($saved) &&
|
||||
if ($display == 'month' &&
|
||||
$first_of_month !== $start_of_week) {
|
||||
$interim_day_num = ($first_of_month + 7 - $start_of_week) % 7;
|
||||
$min_range = id(clone $start_day)
|
||||
|
@ -103,18 +174,17 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
if (!$max_range || ($max_range > $display_end)) {
|
||||
$max_range = $display_end;
|
||||
|
||||
if ($this->isMonthView($saved) &&
|
||||
if ($display == 'month' &&
|
||||
$last_of_month !== $end_of_week) {
|
||||
$interim_day_num = ($end_of_week + 7 - $last_of_month) % 7;
|
||||
$max_range = id(clone $next)
|
||||
->modify('+'.$interim_day_num.' days')
|
||||
->format('U');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ($saved->getParameter('upcoming')) {
|
||||
if ($upcoming) {
|
||||
if ($min_range) {
|
||||
$min_range = max(time(), $min_range);
|
||||
} else {
|
||||
|
@ -122,128 +192,7 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
}
|
||||
}
|
||||
|
||||
if ($min_range || $max_range) {
|
||||
$query->withDateRange($min_range, $max_range);
|
||||
}
|
||||
|
||||
$invited_phids = $saved->getParameter('invitedPHIDs', array());
|
||||
$invited_phids = $user_datasource->evaluateTokens($invited_phids);
|
||||
if ($invited_phids) {
|
||||
$query->withInvitedPHIDs($invited_phids);
|
||||
}
|
||||
|
||||
$creator_phids = $saved->getParameter('creatorPHIDs', array());
|
||||
$creator_phids = $user_datasource->evaluateTokens($creator_phids);
|
||||
if ($creator_phids) {
|
||||
$query->withCreatorPHIDs($creator_phids);
|
||||
}
|
||||
|
||||
$is_cancelled = $saved->getParameter('isCancelled', 'active');
|
||||
|
||||
switch ($is_cancelled) {
|
||||
case 'active':
|
||||
$query->withIsCancelled(false);
|
||||
break;
|
||||
case 'cancelled':
|
||||
$query->withIsCancelled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved) {
|
||||
|
||||
$range_start = $this->getDateFrom($saved);
|
||||
$e_start = null;
|
||||
|
||||
$range_end = $this->getDateTo($saved);
|
||||
$e_end = null;
|
||||
|
||||
if (!$range_start->isValid()) {
|
||||
$this->addError(pht('Start date is not valid.'));
|
||||
$e_start = pht('Invalid');
|
||||
}
|
||||
|
||||
if (!$range_end->isValid()) {
|
||||
$this->addError(pht('End date is not valid.'));
|
||||
$e_end = pht('Invalid');
|
||||
}
|
||||
|
||||
$start_epoch = $range_start->getEpoch();
|
||||
$end_epoch = $range_end->getEpoch();
|
||||
|
||||
if ($start_epoch && $end_epoch && ($start_epoch > $end_epoch)) {
|
||||
$this->addError(pht('End date must be after start date.'));
|
||||
$e_start = pht('Invalid');
|
||||
$e_end = pht('Invalid');
|
||||
}
|
||||
|
||||
$upcoming = $saved->getParameter('upcoming');
|
||||
$is_cancelled = $saved->getParameter('isCancelled', 'active');
|
||||
$display = $saved->getParameter('display', 'month');
|
||||
|
||||
$invited_phids = $saved->getParameter('invitedPHIDs', array());
|
||||
$creator_phids = $saved->getParameter('creatorPHIDs', array());
|
||||
$resolution_types = array(
|
||||
'active' => pht('Active Events Only'),
|
||||
'cancelled' => pht('Cancelled Events Only'),
|
||||
'both' => pht('Both Cancelled and Active Events'),
|
||||
);
|
||||
$display_options = array(
|
||||
'month' => pht('Month View'),
|
||||
'day' => pht('Day View (beta)'),
|
||||
'list' => pht('List View'),
|
||||
);
|
||||
|
||||
$form
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorPeopleUserFunctionDatasource())
|
||||
->setName('creators')
|
||||
->setLabel(pht('Created By'))
|
||||
->setValue($creator_phids))
|
||||
->appendControl(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource(new PhabricatorPeopleUserFunctionDatasource())
|
||||
->setName('invited')
|
||||
->setLabel(pht('Invited'))
|
||||
->setValue($invited_phids))
|
||||
->appendChild(
|
||||
id(new AphrontFormDateControl())
|
||||
->setLabel(pht('Occurs After'))
|
||||
->setUser($this->requireViewer())
|
||||
->setName('rangeStart')
|
||||
->setError($e_start)
|
||||
->setValue($range_start))
|
||||
->appendChild(
|
||||
id(new AphrontFormDateControl())
|
||||
->setLabel(pht('Occurs Before'))
|
||||
->setUser($this->requireViewer())
|
||||
->setName('rangeEnd')
|
||||
->setError($e_end)
|
||||
->setValue($range_end))
|
||||
->appendChild(
|
||||
id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'upcoming',
|
||||
1,
|
||||
pht('Show only upcoming events.'),
|
||||
$upcoming))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Cancelled Events'))
|
||||
->setName('isCancelled')
|
||||
->setValue($is_cancelled)
|
||||
->setOptions($resolution_types))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Display Options'))
|
||||
->setName('display')
|
||||
->setValue($display)
|
||||
->setOptions($display_options));
|
||||
return array($min_range, $max_range);
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
|
@ -279,7 +228,9 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
case 'day':
|
||||
return $query->setParameter('display', 'day');
|
||||
case 'upcoming':
|
||||
return $query->setParameter('upcoming', true);
|
||||
return $query->setParameter('upcoming', array(
|
||||
0 => 'upcoming',
|
||||
));
|
||||
case 'all':
|
||||
return $query;
|
||||
}
|
||||
|
@ -311,6 +262,7 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
||||
$viewer = $this->requireViewer();
|
||||
$list = new PHUIObjectItemListView();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$from = phabricator_datetime($event->getDateFrom(), $viewer);
|
||||
$duration = '';
|
||||
|
@ -353,11 +305,15 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
array $statuses,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
$now = time();
|
||||
|
||||
list($start_year, $start_month) =
|
||||
$this->getDisplayYearAndMonthAndDay($query);
|
||||
$this->getDisplayYearAndMonthAndDay(
|
||||
$this->getQueryDateFrom($query)->getEpoch(),
|
||||
$this->getQueryDateTo($query)->getEpoch(),
|
||||
$query->getParameter('display'));
|
||||
|
||||
$now_year = phabricator_format_local_time($now, $viewer, 'Y');
|
||||
$now_month = phabricator_format_local_time($now, $viewer, 'm');
|
||||
|
@ -365,15 +321,15 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
|
||||
if ($start_month == $now_month && $start_year == $now_year) {
|
||||
$month_view = new PHUICalendarMonthView(
|
||||
$this->getDateFrom($query),
|
||||
$this->getDateTo($query),
|
||||
$this->getQueryDateFrom($query),
|
||||
$this->getQueryDateTo($query),
|
||||
$start_month,
|
||||
$start_year,
|
||||
$now_day);
|
||||
} else {
|
||||
$month_view = new PHUICalendarMonthView(
|
||||
$this->getDateFrom($query),
|
||||
$this->getDateTo($query),
|
||||
$this->getQueryDateFrom($query),
|
||||
$this->getQueryDateTo($query),
|
||||
$start_month,
|
||||
$start_year);
|
||||
}
|
||||
|
@ -414,13 +370,18 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
array $statuses,
|
||||
PhabricatorSavedQuery $query,
|
||||
array $handles) {
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
list($start_year, $start_month, $start_day) =
|
||||
$this->getDisplayYearAndMonthAndDay($query);
|
||||
$this->getDisplayYearAndMonthAndDay(
|
||||
$this->getQueryDateFrom($query)->getEpoch(),
|
||||
$this->getQueryDateTo($query)->getEpoch(),
|
||||
$query->getParameter('display'));
|
||||
|
||||
$day_view = id(new PHUICalendarDayView(
|
||||
$this->getDateFrom($query),
|
||||
$this->getDateTo($query),
|
||||
$this->getQueryDateFrom($query)->getEpoch(),
|
||||
$this->getQueryDateTo($query)->getEpoch(),
|
||||
$start_year,
|
||||
$start_month,
|
||||
$start_day))
|
||||
|
@ -465,21 +426,26 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
}
|
||||
|
||||
private function getDisplayYearAndMonthAndDay(
|
||||
PhabricatorSavedQuery $query) {
|
||||
$range_start,
|
||||
$range_end,
|
||||
$display) {
|
||||
|
||||
$viewer = $this->requireViewer();
|
||||
$epoch = null;
|
||||
|
||||
if ($this->calendarYear && $this->calendarMonth) {
|
||||
$start_year = $this->calendarYear;
|
||||
$start_month = $this->calendarMonth;
|
||||
$start_day = $this->calendarDay ? $this->calendarDay : 1;
|
||||
} else {
|
||||
$epoch = $this->getDateFrom($query)->getEpoch();
|
||||
if (!$epoch) {
|
||||
$epoch = $this->getDateTo($query)->getEpoch();
|
||||
if (!$epoch) {
|
||||
$epoch = time();
|
||||
}
|
||||
if ($range_start) {
|
||||
$epoch = $range_start;
|
||||
} else if ($range_end) {
|
||||
$epoch = $range_end;
|
||||
} else {
|
||||
$epoch = time();
|
||||
}
|
||||
if ($this->isMonthView($query)) {
|
||||
if ($display == 'month') {
|
||||
$day = 1;
|
||||
} else {
|
||||
$day = phabricator_format_local_time($epoch, $viewer, 'd');
|
||||
|
@ -499,20 +465,30 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
}
|
||||
}
|
||||
|
||||
private function getDateFrom(PhabricatorSavedQuery $saved) {
|
||||
return $this->getDate($saved, 'rangeStart');
|
||||
private function getQueryDateFrom(PhabricatorSavedQuery $saved) {
|
||||
return $this->getQueryDate($saved, 'rangeStart');
|
||||
}
|
||||
|
||||
private function getDateTo(PhabricatorSavedQuery $saved) {
|
||||
return $this->getDate($saved, 'rangeEnd');
|
||||
private function getQueryDateTo(PhabricatorSavedQuery $saved) {
|
||||
return $this->getQueryDate($saved, 'rangeEnd');
|
||||
}
|
||||
|
||||
private function getDate(PhabricatorSavedQuery $saved, $key) {
|
||||
private function getQueryDate(PhabricatorSavedQuery $saved, $key) {
|
||||
$viewer = $this->requireViewer();
|
||||
|
||||
$wild = $saved->getParameter($key);
|
||||
if ($wild) {
|
||||
$value = AphrontFormDateControlValue::newFromWild($viewer, $wild);
|
||||
return $this->getSafeDate($wild);
|
||||
}
|
||||
|
||||
private function getSafeDate($value) {
|
||||
$viewer = $this->requireViewer();
|
||||
if ($value) {
|
||||
// ideally this would be consistent and always pass in the same type
|
||||
if ($value instanceof AphrontFormDateControlValue) {
|
||||
return $value;
|
||||
} else {
|
||||
$value = AphrontFormDateControlValue::newFromWild($viewer, $value);
|
||||
}
|
||||
} else {
|
||||
$value = AphrontFormDateControlValue::newFromEpoch(
|
||||
$viewer,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
implements PhabricatorPolicyInterface,
|
||||
PhabricatorProjectInterface,
|
||||
PhabricatorMarkupInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorSubscribableInterface,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMetaMTAMailPHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'MTAM';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('MetaMTA Mail');
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorMetaMTAApplication';
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new PhabricatorMetaMTAMail();
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new PhabricatorMetaMTAMailQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$mail = $objects[$phid];
|
||||
|
||||
$id = $mail->getID();
|
||||
$name = pht('Mail %d', $id);
|
||||
|
||||
$handle
|
||||
->setName($name)
|
||||
->setFullName($name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorMetaMTAMailQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'mail.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'mail.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
protected function getPrimaryTableAlias() {
|
||||
return 'mail';
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorMetaMTAMail();
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorMetaMTAApplication';
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
/**
|
||||
* @task recipients Managing Recipients
|
||||
*/
|
||||
final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
||||
final class PhabricatorMetaMTAMail
|
||||
extends PhabricatorMetaMTADAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
const STATUS_QUEUE = 'queued';
|
||||
const STATUS_SENT = 'sent';
|
||||
|
@ -12,6 +14,7 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
|||
|
||||
const RETRY_DELAY = 5;
|
||||
|
||||
protected $actorPHID;
|
||||
protected $parameters;
|
||||
protected $status;
|
||||
protected $message;
|
||||
|
@ -29,10 +32,12 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
|||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'parameters' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'actorPHID' => 'phid?',
|
||||
'status' => 'text32',
|
||||
'relatedPHID' => 'phid?',
|
||||
|
||||
|
@ -44,6 +49,9 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
|||
'status' => array(
|
||||
'columns' => array('status'),
|
||||
),
|
||||
'key_actorPHID' => array(
|
||||
'columns' => array('actorPHID'),
|
||||
),
|
||||
'relatedPHID' => array(
|
||||
'columns' => array('relatedPHID'),
|
||||
),
|
||||
|
@ -54,6 +62,11 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
|||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
return PhabricatorPHID::generateNewPHID(
|
||||
PhabricatorMetaMTAMailPHIDType::TYPECONST);
|
||||
}
|
||||
|
||||
protected function setParam($param, $value) {
|
||||
$this->parameters[$param] = $value;
|
||||
return $this;
|
||||
|
@ -211,9 +224,14 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
|||
|
||||
public function setFrom($from) {
|
||||
$this->setParam('from', $from);
|
||||
$this->setActorPHID($from);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFrom() {
|
||||
return $this->getParam('from');
|
||||
}
|
||||
|
||||
public function setRawFrom($raw_email, $raw_name) {
|
||||
$this->setParam('raw-from', array($raw_email, $raw_name));
|
||||
return $this;
|
||||
|
@ -993,4 +1011,29 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
|||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return PhabricatorPolicies::POLICY_NOONE;
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
$actor_phids = $this->getAllActorPHIDs();
|
||||
$actor_phids = $this->expandRecipients($actor_phids);
|
||||
return in_array($viewer->getPHID(), $actor_phids);
|
||||
}
|
||||
|
||||
public function describeAutomaticCapability($capability) {
|
||||
return pht(
|
||||
'The mail sender and message recipients can always see the mail.');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
final class PhabricatorNotificationBuilder extends Phobject {
|
||||
|
||||
private $stories;
|
||||
private $parsedStories;
|
||||
private $user = null;
|
||||
|
||||
public function __construct(array $stories) {
|
||||
assert_instances_of($stories, 'PhabricatorFeedStory');
|
||||
$this->stories = $stories;
|
||||
}
|
||||
|
||||
|
@ -14,7 +16,11 @@ final class PhabricatorNotificationBuilder extends Phobject {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function buildView() {
|
||||
private function parseStories() {
|
||||
|
||||
if ($this->parsedStories) {
|
||||
return $this->parsedStories;
|
||||
}
|
||||
|
||||
$stories = $this->stories;
|
||||
$stories = mpull($stories, null, 'getChronologicalKey');
|
||||
|
@ -100,6 +106,12 @@ final class PhabricatorNotificationBuilder extends Phobject {
|
|||
$stories = mpull($stories, null, 'getChronologicalKey');
|
||||
krsort($stories);
|
||||
|
||||
$this->parsedStories = $stories;
|
||||
return $stories;
|
||||
}
|
||||
|
||||
public function buildView() {
|
||||
$stories = $this->parseStories();
|
||||
$null_view = new AphrontNullView();
|
||||
|
||||
foreach ($stories as $story) {
|
||||
|
@ -114,4 +126,39 @@ final class PhabricatorNotificationBuilder extends Phobject {
|
|||
|
||||
return $null_view;
|
||||
}
|
||||
|
||||
public function buildDict() {
|
||||
$stories = $this->parseStories();
|
||||
$dict = array();
|
||||
|
||||
foreach ($stories as $story) {
|
||||
if ($story instanceof PhabricatorApplicationTransactionFeedStory) {
|
||||
$dict[] = array(
|
||||
'desktopReady' => true,
|
||||
'title' => $story->renderText(),
|
||||
'body' => $story->renderTextBody(),
|
||||
'href' => $story->getURI(),
|
||||
'icon' => $story->getImageURI(),
|
||||
);
|
||||
} else if ($story instanceof PhabricatorNotificationTestFeedStory) {
|
||||
$dict[] = array(
|
||||
'desktopReady' => true,
|
||||
'title' => pht('Test Notification'),
|
||||
'body' => $story->renderText(),
|
||||
'href' => null,
|
||||
'icon' => PhabricatorUser::getDefaultProfileImageURI(),
|
||||
);
|
||||
} else {
|
||||
$dict[] = array(
|
||||
'desktopReady' => false,
|
||||
'title' => null,
|
||||
'body' => null,
|
||||
'href' => null,
|
||||
'icon' => null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $dict;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,10 +33,17 @@ final class PhabricatorNotificationIndividualController
|
|||
|
||||
$builder = new PhabricatorNotificationBuilder(array($story));
|
||||
$content = $builder->buildView()->render();
|
||||
$dict = $builder->buildDict();
|
||||
$data = $dict[0];
|
||||
|
||||
$response = array(
|
||||
'pertinent' => true,
|
||||
'primaryObjectPHID' => $story->getPrimaryObjectPHID(),
|
||||
'desktopReady' => $data['desktopReady'],
|
||||
'href' => $data['href'],
|
||||
'icon' => $data['icon'],
|
||||
'title' => $data['title'],
|
||||
'body' => $data['body'],
|
||||
'content' => hsprintf('%s', $content),
|
||||
);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ final class PhabricatorNotificationTestController
|
|||
$request = $this->getRequest();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$story_type = 'PhabricatorNotificationAdHocFeedStory';
|
||||
$story_type = 'PhabricatorNotificationTestFeedStory';
|
||||
$story_data = array(
|
||||
'title' => pht(
|
||||
'This is a test notification, sent at %s.',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorNotificationAdHocFeedStory extends PhabricatorFeedStory {
|
||||
final class PhabricatorNotificationTestFeedStory extends PhabricatorFeedStory {
|
||||
|
||||
public function getPrimaryObjectPHID() {
|
||||
return $this->getAuthorPHID();
|
|
@ -266,7 +266,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
}
|
||||
|
||||
$query = $this->newQuery();
|
||||
if ($query) {
|
||||
if ($query && $this->shouldShowOrderField()) {
|
||||
$orders = $query->getBuiltinOrders();
|
||||
$orders = ipull($orders, 'name');
|
||||
|
||||
|
@ -293,6 +293,10 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
|
|||
return $field_map;
|
||||
}
|
||||
|
||||
protected function shouldShowOrderField() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private function adjustFieldsForDisplay(array $field_map) {
|
||||
$order = $this->getDefaultFieldOrder();
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSearchDateControlField
|
||||
extends PhabricatorSearchField {
|
||||
|
||||
protected function getValueExistsInRequest(AphrontRequest $request, $key) {
|
||||
// The control doesn't actually submit a value with the same name as the
|
||||
// key, so look for the "_d" value instead, which has the date part of the
|
||||
// control value.
|
||||
return $request->getExists($key.'_d');
|
||||
}
|
||||
|
||||
protected function getValueFromRequest(AphrontRequest $request, $key) {
|
||||
$value = AphrontFormDateControlValue::newFromRequest($request, $key);
|
||||
$value->setOptional(true);
|
||||
return $value->getDictionary();
|
||||
}
|
||||
|
||||
protected function newControl() {
|
||||
return id(new AphrontFormDateControl())
|
||||
->setAllowNull(true);
|
||||
}
|
||||
|
||||
protected function didReadValueFromSavedQuery($value) {
|
||||
if (!$value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof AphrontFormDateControlValue && $value->getEpoch()) {
|
||||
return $value->setOptional(true);
|
||||
}
|
||||
|
||||
$value = AphrontFormDateControlValue::newFromWild(
|
||||
$this->getViewer(),
|
||||
$value);
|
||||
return $value->setOptional(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorDesktopNotificationsSettingsPanel
|
||||
extends PhabricatorSettingsPanel {
|
||||
|
||||
public function isEnabled() {
|
||||
return PhabricatorEnv::getEnvConfig('notification.enabled') &&
|
||||
PhabricatorApplication::isClassInstalled(
|
||||
'PhabricatorNotificationsApplication');
|
||||
}
|
||||
|
||||
public function getPanelKey() {
|
||||
return 'desktopnotifications';
|
||||
}
|
||||
|
||||
public function getPanelName() {
|
||||
return pht('Desktop Notifications');
|
||||
}
|
||||
|
||||
public function getPanelGroup() {
|
||||
return pht('Application Settings');
|
||||
}
|
||||
|
||||
public function processRequest(AphrontRequest $request) {
|
||||
$user = $request->getUser();
|
||||
$preferences = $user->loadPreferences();
|
||||
|
||||
$pref = PhabricatorUserPreferences::PREFERENCE_DESKTOP_NOTIFICATIONS;
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$notifications = $request->getInt($pref);
|
||||
$preferences->setPreference($pref, $notifications);
|
||||
$preferences->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($this->getPanelURI('?saved=true'));
|
||||
}
|
||||
|
||||
$title = pht('Desktop Notifications');
|
||||
$control_id = celerity_generate_unique_node_id();
|
||||
$status_id = celerity_generate_unique_node_id();
|
||||
$browser_status_id = celerity_generate_unique_node_id();
|
||||
$cancel_ask = pht(
|
||||
'The dialog asking for permission to send desktop notifications was '.
|
||||
'closed without granting permission. Only application notifications '.
|
||||
'will be sent.');
|
||||
$accept_ask = pht(
|
||||
'Click "Save Preference" to persist these changes.');
|
||||
$reject_ask = pht(
|
||||
'Permission for desktop notifications was denied. Only application '.
|
||||
'notifications will be sent.');
|
||||
$no_support = pht(
|
||||
'This web browser does not support desktop notifications. Only '.
|
||||
'application notifications will be sent for this browser regardless of '.
|
||||
'this preference.');
|
||||
$default_status = phutil_tag(
|
||||
'span',
|
||||
array(),
|
||||
array(
|
||||
pht('This browser has not yet granted permission to send desktop '.
|
||||
'notifications for this Phabricator instance.'),
|
||||
phutil_tag('br'),
|
||||
phutil_tag('br'),
|
||||
javelin_tag(
|
||||
'button',
|
||||
array(
|
||||
'sigil' => 'desktop-notifications-permission-button',
|
||||
'class' => 'green',
|
||||
),
|
||||
pht('Grant Permission')),
|
||||
));
|
||||
$granted_status = phutil_tag(
|
||||
'span',
|
||||
array(),
|
||||
pht('This browser has been granted permission to send desktop '.
|
||||
'notifications for this Phabricator instance.'));
|
||||
$denied_status = phutil_tag(
|
||||
'span',
|
||||
array(),
|
||||
pht('This browser has denied permission to send desktop notifications '.
|
||||
'for this Phabricator instance. Consult your browser settings / '.
|
||||
'documentation to figure out how to clear this setting, do so, '.
|
||||
'and then re-visit this page to grant permission.'));
|
||||
$status_box = id(new PHUIInfoView())
|
||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||
->setID($status_id)
|
||||
->setIsHidden(true)
|
||||
->appendChild($accept_ask);
|
||||
|
||||
$control_config = array(
|
||||
'controlID' => $control_id,
|
||||
'statusID' => $status_id,
|
||||
'browserStatusID' => $browser_status_id,
|
||||
'defaultMode' => 0,
|
||||
'desktopMode' => 1,
|
||||
'cancelAsk' => $cancel_ask,
|
||||
'grantedAsk' => $accept_ask,
|
||||
'deniedAsk' => $reject_ask,
|
||||
'defaultStatus' => $default_status,
|
||||
'deniedStatus' => $denied_status,
|
||||
'grantedStatus' => $granted_status,
|
||||
'noSupport' => $no_support,
|
||||
);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel($title)
|
||||
->setControlID($control_id)
|
||||
->setName($pref)
|
||||
->setValue($preferences->getPreference($pref))
|
||||
->setOptions(
|
||||
array(
|
||||
1 => pht('Send Desktop Notifications Too'),
|
||||
0 => pht('Send Application Notifications Only'),
|
||||
))
|
||||
->setCaption(
|
||||
pht(
|
||||
'Should Phabricator send desktop notifications? These are sent '.
|
||||
'in addition to the notifications within the Phabricator '.
|
||||
'application.'))
|
||||
->initBehavior(
|
||||
'desktop-notifications-control',
|
||||
$control_config))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Save Preference')));
|
||||
|
||||
$test_icon = id(new PHUIIconView())
|
||||
->setIconFont('fa-exclamation-triangle');
|
||||
$test_button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setWorkflow(true)
|
||||
->setText(pht('Send Test Notification'))
|
||||
->setHref('/notification/test/')
|
||||
->setIcon($test_icon);
|
||||
|
||||
$form_box = id(new PHUIObjectBoxView())
|
||||
->setHeader(
|
||||
id(new PHUIHeaderView())
|
||||
->setHeader(pht('Desktop Notifications'))
|
||||
->addActionLink($test_button))
|
||||
->setForm($form)
|
||||
->setInfoView($status_box)
|
||||
->setFormSaved($request->getBool('saved'));
|
||||
|
||||
$browser_status_box = id(new PHUIInfoView())
|
||||
->setID($browser_status_id)
|
||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||
->setIsHidden(true)
|
||||
->appendChild($default_status);
|
||||
|
||||
return array(
|
||||
$form_box,
|
||||
$browser_status_box,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -39,6 +39,7 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
|
|||
const PREFERENCE_CONPHERENCE_COLUMN = 'conpherence-column';
|
||||
|
||||
const PREFERENCE_RESOURCE_POSTPROCESSOR = 'resource-postprocessor';
|
||||
const PREFERENCE_DESKTOP_NOTIFICATIONS = 'desktop-notifications';
|
||||
|
||||
// These are in an unusual order for historic reasons.
|
||||
const MAILTAG_PREFERENCE_NOTIFY = 0;
|
||||
|
|
|
@ -116,6 +116,35 @@ class PhabricatorApplicationTransactionFeedStory
|
|||
return $text;
|
||||
}
|
||||
|
||||
public function renderTextBody() {
|
||||
$all_bodies = '';
|
||||
$new_target = PhabricatorApplicationTransaction::TARGET_TEXT;
|
||||
$xaction_phids = $this->getValue('transactionPHIDs');
|
||||
foreach ($xaction_phids as $xaction_phid) {
|
||||
$secondary_xaction = $this->getObject($xaction_phid);
|
||||
$old_target = $secondary_xaction->getRenderingTarget();
|
||||
$secondary_xaction->setRenderingTarget($new_target);
|
||||
$secondary_xaction->setHandles($this->getHandles());
|
||||
|
||||
$body = $secondary_xaction->getBodyForMail();
|
||||
if (nonempty($body)) {
|
||||
$all_bodies .= $body."\n";
|
||||
}
|
||||
$secondary_xaction->setRenderingTarget($old_target);
|
||||
}
|
||||
return trim($all_bodies);
|
||||
}
|
||||
|
||||
public function getImageURI() {
|
||||
$author_phid = $this->getPrimaryTransaction()->getAuthorPHID();
|
||||
return $this->getHandle($author_phid)->getImageURI();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$handle = $this->getHandle($this->getPrimaryObjectPHID());
|
||||
return PhabricatorEnv::getProductionURI($handle->getURI());
|
||||
}
|
||||
|
||||
public function renderAsTextForDoorkeeper(
|
||||
DoorkeeperFeedStoryPublisher $publisher) {
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ abstract class AphrontView extends Phobject
|
|||
$name,
|
||||
$config,
|
||||
$this->getDefaultResourceSource());
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ final class PHUIInfoView extends AphrontView {
|
|||
private $severity;
|
||||
private $id;
|
||||
private $buttons = array();
|
||||
private $isHidden;
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
|
@ -34,6 +35,11 @@ final class PHUIInfoView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setIsHidden($bool) {
|
||||
$this->isHidden = $bool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addButton(PHUIButtonView $button) {
|
||||
|
||||
$this->buttons[] = $button;
|
||||
|
@ -112,6 +118,7 @@ final class PHUIInfoView extends AphrontView {
|
|||
array(
|
||||
'id' => $this->id,
|
||||
'class' => $classes,
|
||||
'style' => $this->isHidden ? 'display: none;' : null,
|
||||
),
|
||||
array(
|
||||
$buttons,
|
||||
|
|
|
@ -54,6 +54,10 @@ final class PHUIFeedStoryView extends AphrontView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getImage() {
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
public function setImageHref($image_href) {
|
||||
$this->imageHref = $image_href;
|
||||
return $this;
|
||||
|
|
|
@ -208,8 +208,15 @@ final class PHUICalendarDayView extends AphrontView {
|
|||
private function getQueryRangeWarning() {
|
||||
$errors = array();
|
||||
|
||||
$range_start_epoch = $this->rangeStart->getEpoch();
|
||||
$range_end_epoch = $this->rangeEnd->getEpoch();
|
||||
$range_start_epoch = null;
|
||||
$range_end_epoch = null;
|
||||
|
||||
if ($this->rangeStart) {
|
||||
$range_start_epoch = $this->rangeStart->getEpoch();
|
||||
}
|
||||
if ($this->rangeEnd) {
|
||||
$range_end_epoch = $this->rangeEnd->getEpoch();
|
||||
}
|
||||
|
||||
$day_start = $this->getDateTime();
|
||||
$day_end = id(clone $day_start)->modify('+1 day');
|
||||
|
@ -226,10 +233,10 @@ final class PHUICalendarDayView extends AphrontView {
|
|||
$errors[] = pht('Part of the day is out of range');
|
||||
}
|
||||
|
||||
if (($this->rangeEnd->getEpoch() != null &&
|
||||
$this->rangeEnd->getEpoch() < $day_start) ||
|
||||
($this->rangeStart->getEpoch() != null &&
|
||||
$this->rangeStart->getEpoch() > $day_end)) {
|
||||
if (($range_end_epoch != null &&
|
||||
$range_end_epoch < $day_start) ||
|
||||
($range_start_epoch != null &&
|
||||
$range_start_epoch > $day_end)) {
|
||||
$errors[] = pht('Day is out of query range');
|
||||
}
|
||||
return $errors;
|
||||
|
|
|
@ -434,8 +434,15 @@ final class PHUICalendarMonthView extends AphrontView {
|
|||
private function getQueryRangeWarning() {
|
||||
$errors = array();
|
||||
|
||||
$range_start_epoch = $this->rangeStart->getEpoch();
|
||||
$range_end_epoch = $this->rangeEnd->getEpoch();
|
||||
$range_start_epoch = null;
|
||||
$range_end_epoch = null;
|
||||
|
||||
if ($this->rangeStart) {
|
||||
$range_start_epoch = $this->rangeStart->getEpoch();
|
||||
}
|
||||
if ($this->rangeEnd) {
|
||||
$range_end_epoch = $this->rangeEnd->getEpoch();
|
||||
}
|
||||
|
||||
$month_start = $this->getDateTime();
|
||||
$month_end = id(clone $month_start)->modify('+1 month');
|
||||
|
@ -452,10 +459,10 @@ final class PHUICalendarMonthView extends AphrontView {
|
|||
$errors[] = pht('Part of the month is out of range');
|
||||
}
|
||||
|
||||
if (($this->rangeEnd->getEpoch() != null &&
|
||||
$this->rangeEnd->getEpoch() < $month_start) ||
|
||||
($this->rangeStart->getEpoch() != null &&
|
||||
$this->rangeStart->getEpoch() > $month_end)) {
|
||||
if (($range_end_epoch != null &&
|
||||
$range_end_epoch < $month_start) ||
|
||||
($range_start_epoch != null &&
|
||||
$range_start_epoch > $month_end)) {
|
||||
$errors[] = pht('Month is out of query range');
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,12 @@ JX.behavior('aphlict-listen', function(config) {
|
|||
// Show the notification itself.
|
||||
new JX.Notification()
|
||||
.setContent(JX.$H(response.content))
|
||||
.setDesktopReady(response.desktopReady)
|
||||
.setKey(response.primaryObjectPHID)
|
||||
.setTitle(response.title)
|
||||
.setBody(response.body)
|
||||
.setHref(response.href)
|
||||
.setIcon(response.icon)
|
||||
.show();
|
||||
|
||||
// If the notification affected an object on this page, show a
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* @provides javelin-behavior-desktop-notifications-control
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-dom
|
||||
* javelin-uri
|
||||
* phabricator-notification
|
||||
*/
|
||||
|
||||
JX.behavior('desktop-notifications-control', function(config, statics) {
|
||||
|
||||
function findEl(id) {
|
||||
var el = null;
|
||||
try {
|
||||
el = JX.$(id);
|
||||
} catch (e) {
|
||||
// not found
|
||||
}
|
||||
return el;
|
||||
}
|
||||
function updateFormStatus(permission) {
|
||||
var statusEl = findEl(config.statusID);
|
||||
if (!statusEl) {
|
||||
return;
|
||||
}
|
||||
switch (permission) {
|
||||
case 'default':
|
||||
JX.DOM.setContent(statusEl.firstChild, config.cancelAsk);
|
||||
break;
|
||||
case 'granted':
|
||||
JX.DOM.setContent(statusEl.firstChild, config.grantedAsk);
|
||||
break;
|
||||
case 'denied':
|
||||
JX.DOM.setContent(statusEl.firstChild, config.deniedAsk);
|
||||
break;
|
||||
}
|
||||
JX.DOM.show(statusEl);
|
||||
}
|
||||
|
||||
function updateBrowserStatus(permission) {
|
||||
var browserStatusEl = findEl(config.browserStatusID);
|
||||
if (!browserStatusEl) {
|
||||
return;
|
||||
}
|
||||
switch (permission) {
|
||||
case 'default':
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-notice', true);
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-success', false);
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-error', false);
|
||||
JX.DOM.setContent(browserStatusEl, JX.$H(config.defaultStatus));
|
||||
break;
|
||||
case 'granted':
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-success', true);
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-notice', false);
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-error', false);
|
||||
JX.DOM.setContent(browserStatusEl, JX.$H(config.grantedStatus));
|
||||
break;
|
||||
case 'denied':
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-error', true);
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-notice', false);
|
||||
JX.DOM.alterClass(browserStatusEl, 'phui-info-severity-success', false);
|
||||
JX.DOM.setContent(browserStatusEl, JX.$H(config.deniedStatus));
|
||||
break;
|
||||
}
|
||||
JX.DOM.show(browserStatusEl);
|
||||
}
|
||||
|
||||
function installSelectListener() {
|
||||
var controlEl = findEl(config.controlID);
|
||||
if (!controlEl) {
|
||||
return;
|
||||
}
|
||||
var select = JX.DOM.find(controlEl, 'select');
|
||||
JX.DOM.listen(
|
||||
select,
|
||||
'change',
|
||||
null,
|
||||
function (e) {
|
||||
if (!JX.Notification.supportsDesktopNotifications()) {
|
||||
return;
|
||||
}
|
||||
var value = e.getTarget().value;
|
||||
if (value == config.desktopMode) {
|
||||
window.Notification.requestPermission(
|
||||
function (permission) {
|
||||
updateFormStatus(permission);
|
||||
updateBrowserStatus(permission);
|
||||
});
|
||||
} else {
|
||||
var statusEl = JX.$(config.statusID);
|
||||
JX.DOM.hide(statusEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function install() {
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'desktop-notifications-permission-button',
|
||||
function () {
|
||||
window.Notification.requestPermission(
|
||||
function (permission) {
|
||||
updateFormStatus(permission);
|
||||
updateBrowserStatus(permission);
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
statics.installed = statics.installed || install();
|
||||
if (!JX.Notification.supportsDesktopNotifications()) {
|
||||
var statusEl = JX.$(config.statusID);
|
||||
JX.DOM.setContent(statusEl.firstChild, config.noSupport);
|
||||
JX.DOM.show(statusEl);
|
||||
} else {
|
||||
updateBrowserStatus(window.Notification.permission);
|
||||
}
|
||||
installSelectListener();
|
||||
});
|
|
@ -26,15 +26,43 @@ JX.install('Notification', {
|
|||
_visible : false,
|
||||
_hideTimer : null,
|
||||
_duration : 12000,
|
||||
_desktopReady : false,
|
||||
_key : null,
|
||||
_title : null,
|
||||
_body : null,
|
||||
_href : null,
|
||||
_icon : null,
|
||||
|
||||
show : function() {
|
||||
var self = JX.Notification;
|
||||
if (!this._visible) {
|
||||
this._visible = true;
|
||||
|
||||
var self = JX.Notification;
|
||||
self._show(this);
|
||||
this._updateTimer();
|
||||
}
|
||||
|
||||
if (self.supportsDesktopNotifications() &&
|
||||
self.desktopNotificationsEnabled() &&
|
||||
this._desktopReady) {
|
||||
// Note: specifying "tag" means that notifications with matching
|
||||
// keys will aggregate.
|
||||
var n = new window.Notification(this._title, {
|
||||
icon: this._icon,
|
||||
body: this._body,
|
||||
tag: this._key,
|
||||
});
|
||||
n.onclick = JX.bind(n, function (href) {
|
||||
this.close();
|
||||
window.focus();
|
||||
if (href) {
|
||||
JX.$U(href).go();
|
||||
}
|
||||
}, this._href);
|
||||
// Note: some OS / browsers do this automagically; make the behavior
|
||||
// happen everywhere.
|
||||
setTimeout(n.close.bind(n), this._duration);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
|
@ -59,6 +87,36 @@ JX.install('Notification', {
|
|||
return this;
|
||||
},
|
||||
|
||||
setDesktopReady : function(ready) {
|
||||
this._desktopReady = ready;
|
||||
return this;
|
||||
},
|
||||
|
||||
setTitle : function(title) {
|
||||
this._title = title;
|
||||
return this;
|
||||
},
|
||||
|
||||
setBody : function(body) {
|
||||
this._body = body;
|
||||
return this;
|
||||
},
|
||||
|
||||
setHref : function(href) {
|
||||
this._href = href;
|
||||
return this;
|
||||
},
|
||||
|
||||
setKey : function(key) {
|
||||
this._key = key;
|
||||
return this;
|
||||
},
|
||||
|
||||
setIcon : function(icon) {
|
||||
this._icon = icon;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set duration before the notification fades away, in milliseconds. If set
|
||||
* to 0, the notification persists until dismissed.
|
||||
|
@ -97,6 +155,12 @@ JX.install('Notification', {
|
|||
},
|
||||
|
||||
statics : {
|
||||
supportsDesktopNotifications : function () {
|
||||
return 'Notification' in window;
|
||||
},
|
||||
desktopNotificationsEnabled : function () {
|
||||
return window.Notification.permission === 'granted';
|
||||
},
|
||||
_container : null,
|
||||
_listening : false,
|
||||
_active : [],
|
||||
|
|
Loading…
Reference in a new issue