diff --git a/resources/celerity/map.php b/resources/celerity/map.php index dd4d978fed..480c4b602d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -391,7 +391,7 @@ return array( 'rsrc/js/application/files/behavior-document-engine.js' => 'ee0deff8', 'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab', 'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888', - 'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '191b4909', + 'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '549459b8', 'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e', 'rsrc/js/application/herald/PathTypeahead.js' => '662e9cea', 'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3', @@ -609,7 +609,7 @@ return array( 'javelin-behavior-event-all-day' => 'b41537c9', 'javelin-behavior-fancy-datepicker' => 'ecf4e799', 'javelin-behavior-global-drag-and-drop' => '960f6a39', - 'javelin-behavior-harbormaster-log' => '191b4909', + 'javelin-behavior-harbormaster-log' => '549459b8', 'javelin-behavior-herald-rule-editor' => '7ebaeed3', 'javelin-behavior-high-security-warning' => 'a464fe03', 'javelin-behavior-history-install' => '7ee2b591', @@ -960,9 +960,6 @@ return array( '185bbd53' => array( 'javelin-install', ), - '191b4909' => array( - 'javelin-behavior', - ), '1ad0a787' => array( 'javelin-install', 'javelin-reactor', @@ -1257,6 +1254,9 @@ return array( 'javelin-vector', 'javelin-typeahead-static-source', ), + '549459b8' => array( + 'javelin-behavior', + ), '54b612ba' => array( 'javelin-color', 'javelin-install', diff --git a/resources/sql/autopatches/20180423.mail.01.properties.sql b/resources/sql/autopatches/20180423.mail.01.properties.sql new file mode 100644 index 0000000000..d4fc008023 --- /dev/null +++ b/resources/sql/autopatches/20180423.mail.01.properties.sql @@ -0,0 +1,8 @@ +CREATE TABLE {$NAMESPACE}_metamta.metamta_mailproperties ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + objectPHID VARBINARY(64) NOT NULL, + mailProperties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_object` (objectPHID) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/scripts/install/install_ubuntu.sh b/scripts/install/install_ubuntu.sh index 5408cdb3e1..ab83c3a46c 100755 --- a/scripts/install/install_ubuntu.sh +++ b/scripts/install/install_ubuntu.sh @@ -5,9 +5,22 @@ confirm() { read -e ignored } -GIT='git' +INSTALL_URI=" https://phurl.io/u/install" + +failed() { + echo + echo + echo "Installation has failed." + echo "Text above this message might be useful to understanding what exactly failed." + echo + echo "Please follow this guide to manually complete installation:" + echo + echo $INSTALL_URI + echo + echo "We apologize for the inconvenience." + exit 3 +} -LTS="Ubuntu 10.04" ISSUE=`cat /etc/issue` if [[ $ISSUE != Ubuntu* ]] then @@ -15,20 +28,13 @@ then echo "to be something else. Your results may vary."; echo confirm -elif [[ `expr match "$ISSUE" "$LTS"` -eq ${#LTS} ]] -then - GIT='git-core' fi echo "PHABRICATOR UBUNTU INSTALL SCRIPT"; -echo "This script will install Phabricator and all of its core dependencies."; +echo "This script will install Apache, Phabricator and its core dependencies."; echo "Run it from the directory you want to install into."; echo -ROOT=`pwd` -echo "Phabricator will be installed to: ${ROOT}."; -confirm - echo "Testing sudo..." sudo true if [ $? -ne 0 ] @@ -37,31 +43,56 @@ then exit 1; fi; -echo "Installing dependencies: git, apache, mysql, php..."; -echo +echo 'Testing Ubuntu version...' -set +x +VERSION=`lsb_release -rs` +MAJOR=`expr match "$VERSION" '\([0-9]*\)'` -sudo apt-get -qq update -sudo apt-get install \ - $GIT mysql-server apache2 dpkg-dev \ - php5 php5-mysqlnd php5-gd php5-dev php5-curl php-apc php5-cli php5-json - -# Enable mod_rewrite -sudo a2enmod rewrite - -HAVEPCNTL=`php -r "echo extension_loaded('pcntl');"` -if [ $HAVEPCNTL != "1" ] +if [ "$MAJOR" -lt 16 ] then - echo "Installing pcntl..."; + echo 'This script is intented to install on modern operating systems; Your ' + echo 'operating system is too old for this script.' + echo 'You can still install Phabricator manually - please consult the installation' + echo 'guide to see how:' echo - apt-get source php5 - PHP5=`ls -1F | grep '^php5-.*/$'` - (cd $PHP5/ext/pcntl && phpize && ./configure && make && sudo make install) -else - echo "pcntl already installed"; + echo $INSTALL_URI + echo + exit 2 fi +# Ubuntu 16.04 LTS only has php 7.0 in their repos, so they need this extra ppa. +# Ubuntu 17.4 and up have official 7.2 builds. +if [ "$MAJOR" -eq 16 ] +then + echo 'This version of Ubuntu requires additional resources in order to install' + echo 'and run Phabricator.' + echo 'We will now add a the following package repository to your system:' + echo ' https://launchpad.net/~ondrej/+archive/ubuntu/php' + echo + echo 'This repository is generally considered safe to use.' + confirm + + sudo add-apt-repository -y ppa:ondrej/php || failed +fi + +ROOT=`pwd` +echo "Phabricator will be installed to: ${ROOT}."; +confirm + +echo "Installing dependencies: git, apache, mysql, php..."; +echo +sudo apt-get -qq update +sudo apt-get install \ + git mysql-server apache2 libapache2-mod-php \ + php php-mysql php-gd php-curl php-apcu php-cli php-json php-mbstring \ + || failed + +echo "Enabling mod_rewrite in Apache..." +echo +sudo a2enmod rewrite || failed + +echo "Downloading Phabricator and dependencies..." +echo if [ ! -e libphutil ] then git clone https://github.com/phacility/libphutil.git @@ -89,4 +120,4 @@ echo "Install probably worked mostly correctly. Continue with the 'Configuration echo echo " https://secure.phabricator.com/book/phabricator/article/configuration_guide/"; echo -echo "You can delete any php5-* stuff that's left over in this directory if you want."; +echo 'Next step is "Configuring Apache webserver".' diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index beec5058a6..9de57b2530 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1430,6 +1430,7 @@ phutil_register_library_map(array( 'HeraldActionGroup' => 'applications/herald/action/HeraldActionGroup.php', 'HeraldActionRecord' => 'applications/herald/storage/HeraldActionRecord.php', 'HeraldAdapter' => 'applications/herald/adapter/HeraldAdapter.php', + 'HeraldAdapterDatasource' => 'applications/herald/typeahead/HeraldAdapterDatasource.php', 'HeraldAlwaysField' => 'applications/herald/field/HeraldAlwaysField.php', 'HeraldAnotherRuleField' => 'applications/herald/field/HeraldAnotherRuleField.php', 'HeraldApplicationActionGroup' => 'applications/herald/action/HeraldApplicationActionGroup.php', @@ -1485,12 +1486,17 @@ phutil_register_library_map(array( 'HeraldRemarkupFieldValue' => 'applications/herald/value/HeraldRemarkupFieldValue.php', 'HeraldRemarkupRule' => 'applications/herald/remarkup/HeraldRemarkupRule.php', 'HeraldRule' => 'applications/herald/storage/HeraldRule.php', + 'HeraldRuleAdapter' => 'applications/herald/adapter/HeraldRuleAdapter.php', + 'HeraldRuleAdapterField' => 'applications/herald/field/rule/HeraldRuleAdapterField.php', 'HeraldRuleController' => 'applications/herald/controller/HeraldRuleController.php', 'HeraldRuleDatasource' => 'applications/herald/typeahead/HeraldRuleDatasource.php', 'HeraldRuleEditor' => 'applications/herald/editor/HeraldRuleEditor.php', + 'HeraldRuleField' => 'applications/herald/field/rule/HeraldRuleField.php', + 'HeraldRuleFieldGroup' => 'applications/herald/field/rule/HeraldRuleFieldGroup.php', 'HeraldRuleListController' => 'applications/herald/controller/HeraldRuleListController.php', 'HeraldRulePHIDType' => 'applications/herald/phid/HeraldRulePHIDType.php', 'HeraldRuleQuery' => 'applications/herald/query/HeraldRuleQuery.php', + 'HeraldRuleReplyHandler' => 'applications/herald/mail/HeraldRuleReplyHandler.php', 'HeraldRuleSearchEngine' => 'applications/herald/query/HeraldRuleSearchEngine.php', 'HeraldRuleSerializer' => 'applications/herald/editor/HeraldRuleSerializer.php', 'HeraldRuleTestCase' => 'applications/herald/storage/__tests__/HeraldRuleTestCase.php', @@ -1498,6 +1504,8 @@ phutil_register_library_map(array( 'HeraldRuleTransactionComment' => 'applications/herald/storage/HeraldRuleTransactionComment.php', 'HeraldRuleTranscript' => 'applications/herald/storage/transcript/HeraldRuleTranscript.php', 'HeraldRuleTypeConfig' => 'applications/herald/config/HeraldRuleTypeConfig.php', + 'HeraldRuleTypeDatasource' => 'applications/herald/typeahead/HeraldRuleTypeDatasource.php', + 'HeraldRuleTypeField' => 'applications/herald/field/rule/HeraldRuleTypeField.php', 'HeraldRuleViewController' => 'applications/herald/controller/HeraldRuleViewController.php', 'HeraldSchemaSpec' => 'applications/herald/storage/HeraldSchemaSpec.php', 'HeraldSelectFieldValue' => 'applications/herald/value/HeraldSelectFieldValue.php', @@ -3346,6 +3354,7 @@ phutil_register_library_map(array( 'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfEmailHeraldAction.php', 'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'applications/metamta/herald/PhabricatorMailOutboundRoutingSelfNotificationHeraldAction.php', 'PhabricatorMailOutboundStatus' => 'applications/metamta/constants/PhabricatorMailOutboundStatus.php', + 'PhabricatorMailPropertiesDestructionEngineExtension' => 'applications/metamta/engineextension/PhabricatorMailPropertiesDestructionEngineExtension.php', 'PhabricatorMailReceiver' => 'applications/metamta/receiver/PhabricatorMailReceiver.php', 'PhabricatorMailReceiverTestCase' => 'applications/metamta/receiver/__tests__/PhabricatorMailReceiverTestCase.php', 'PhabricatorMailReplyHandler' => 'applications/metamta/replyhandler/PhabricatorMailReplyHandler.php', @@ -3403,6 +3412,8 @@ phutil_register_library_map(array( 'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'applications/metamta/edge/PhabricatorMetaMTAMailHasRecipientEdgeType.php', 'PhabricatorMetaMTAMailListController' => 'applications/metamta/controller/PhabricatorMetaMTAMailListController.php', 'PhabricatorMetaMTAMailPHIDType' => 'applications/metamta/phid/PhabricatorMetaMTAMailPHIDType.php', + 'PhabricatorMetaMTAMailProperties' => 'applications/metamta/storage/PhabricatorMetaMTAMailProperties.php', + 'PhabricatorMetaMTAMailPropertiesQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php', 'PhabricatorMetaMTAMailQuery' => 'applications/metamta/query/PhabricatorMetaMTAMailQuery.php', 'PhabricatorMetaMTAMailSearchEngine' => 'applications/metamta/query/PhabricatorMetaMTAMailSearchEngine.php', 'PhabricatorMetaMTAMailSection' => 'applications/metamta/view/PhabricatorMetaMTAMailSection.php', @@ -6847,6 +6858,7 @@ phutil_register_library_map(array( 'HeraldActionGroup' => 'HeraldGroup', 'HeraldActionRecord' => 'HeraldDAO', 'HeraldAdapter' => 'Phobject', + 'HeraldAdapterDatasource' => 'PhabricatorTypeaheadDatasource', 'HeraldAlwaysField' => 'HeraldField', 'HeraldAnotherRuleField' => 'HeraldField', 'HeraldApplicationActionGroup' => 'HeraldActionGroup', @@ -6915,12 +6927,17 @@ phutil_register_library_map(array( 'PhabricatorDestructibleInterface', 'PhabricatorSubscribableInterface', ), + 'HeraldRuleAdapter' => 'HeraldAdapter', + 'HeraldRuleAdapterField' => 'HeraldRuleField', 'HeraldRuleController' => 'HeraldController', 'HeraldRuleDatasource' => 'PhabricatorTypeaheadDatasource', 'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', + 'HeraldRuleField' => 'HeraldField', + 'HeraldRuleFieldGroup' => 'HeraldFieldGroup', 'HeraldRuleListController' => 'HeraldController', 'HeraldRulePHIDType' => 'PhabricatorPHIDType', 'HeraldRuleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'HeraldRuleReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'HeraldRuleSearchEngine' => 'PhabricatorApplicationSearchEngine', 'HeraldRuleSerializer' => 'Phobject', 'HeraldRuleTestCase' => 'PhabricatorTestCase', @@ -6928,6 +6945,8 @@ phutil_register_library_map(array( 'HeraldRuleTransactionComment' => 'PhabricatorApplicationTransactionComment', 'HeraldRuleTranscript' => 'Phobject', 'HeraldRuleTypeConfig' => 'Phobject', + 'HeraldRuleTypeDatasource' => 'PhabricatorTypeaheadDatasource', + 'HeraldRuleTypeField' => 'HeraldRuleField', 'HeraldRuleViewController' => 'HeraldController', 'HeraldSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'HeraldSelectFieldValue' => 'HeraldFieldValue', @@ -9038,6 +9057,7 @@ phutil_register_library_map(array( 'PhabricatorMailOutboundRoutingSelfEmailHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', 'PhabricatorMailOutboundRoutingSelfNotificationHeraldAction' => 'PhabricatorMailOutboundRoutingHeraldAction', 'PhabricatorMailOutboundStatus' => 'Phobject', + 'PhabricatorMailPropertiesDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', 'PhabricatorMailReceiver' => 'Phobject', 'PhabricatorMailReceiverTestCase' => 'PhabricatorTestCase', 'PhabricatorMailReplyHandler' => 'Phobject', @@ -9106,6 +9126,11 @@ phutil_register_library_map(array( 'PhabricatorMetaMTAMailHasRecipientEdgeType' => 'PhabricatorEdgeType', 'PhabricatorMetaMTAMailListController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMailPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorMetaMTAMailProperties' => array( + 'PhabricatorMetaMTADAO', + 'PhabricatorPolicyInterface', + ), + 'PhabricatorMetaMTAMailPropertiesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorMetaMTAMailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorMetaMTAMailSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorMetaMTAMailSection' => 'Phobject', diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index fe1e80318f..2e6513c7a8 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -113,6 +113,7 @@ abstract class AphrontResponse extends Phobject { try { $cdn = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); + $base_uri = PhabricatorEnv::getURI('/'); } catch (Exception $ex) { return null; } @@ -124,8 +125,6 @@ abstract class AphrontResponse extends Phobject { // If an alternate file domain is not configured and the user is viewing // a Phame blog on a custom domain or some other custom site, we'll still // serve resources from the main site. Include the main site explicitly. - - $base_uri = PhabricatorEnv::getURI('/'); $base_uri = $this->newContentSecurityPolicySource($base_uri); $default = "'self' {$base_uri}"; diff --git a/src/applications/auth/storage/PhabricatorAuthSSHKey.php b/src/applications/auth/storage/PhabricatorAuthSSHKey.php index 2a9a6273bf..5bbb7de834 100644 --- a/src/applications/auth/storage/PhabricatorAuthSSHKey.php +++ b/src/applications/auth/storage/PhabricatorAuthSSHKey.php @@ -70,12 +70,6 @@ final class PhabricatorAuthSSHKey return parent::save(); } - public function getMailKey() { - // NOTE: We don't actually receive mail for these objects. It's OK for - // the mail key to be predictable until we do. - return PhabricatorHash::digestForIndex($this->getPHID()); - } - public function toPublicKey() { return PhabricatorAuthSSHPublicKey::newFromStoredKey($this); } diff --git a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php index 190a6a4920..42cd798584 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php @@ -7,7 +7,7 @@ final class DifferentialRevisionAcceptTransaction const ACTIONKEY = 'accept'; protected function getRevisionActionLabel() { - return pht("Accept Revision \xE2\x9C\x94"); + return pht('Accept Revision'); } protected function getRevisionActionDescription( @@ -162,7 +162,7 @@ final class DifferentialRevisionAcceptTransaction 'closed. Only open revisions can be accepted.')); } - if ($object->isDraft()) { + if ($object->isDraft() || !$object->getShouldBroadcast()) { throw new Exception( pht('You can not accept a draft revision.')); } diff --git a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php index a15ca0641b..0ed6db96bf 100644 --- a/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionRejectTransaction.php @@ -7,7 +7,7 @@ final class DifferentialRevisionRejectTransaction const ACTIONKEY = 'reject'; protected function getRevisionActionLabel() { - return pht("Request Changes \xE2\x9C\x98"); + return pht('Request Changes'); } protected function getRevisionActionDescription( @@ -73,7 +73,7 @@ final class DifferentialRevisionRejectTransaction 'not own.')); } - if ($object->isDraft()) { + if ($object->isDraft() || !$object->getShouldBroadcast()) { throw new Exception( pht('You can not request changes to a draft revision.')); } diff --git a/src/applications/files/favicon/PhabricatorFaviconRef.php b/src/applications/files/favicon/PhabricatorFaviconRef.php index d6c2fd80a8..08ade5283f 100644 --- a/src/applications/files/favicon/PhabricatorFaviconRef.php +++ b/src/applications/files/favicon/PhabricatorFaviconRef.php @@ -247,7 +247,14 @@ final class PhabricatorFaviconRef extends Phobject { $src_w = $template['width']; $src_h = $template['height']; - $template_data = $template['file']->loadFileData(); + try { + $template_data = $template['file']->loadFileData(); + } catch (Exception $ex) { + // In rare cases, we can end up with a corrupted or inaccessible file. + // If we do, just give up: otherwise, it's impossible to get pages to + // generate and not obvious how to fix it. + return null; + } if (!function_exists('imagecreatefromstring')) { return $template_data; diff --git a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php index fa3a01272b..a6860f579c 100644 --- a/src/applications/harbormaster/worker/HarbormasterTargetWorker.php +++ b/src/applications/harbormaster/worker/HarbormasterTargetWorker.php @@ -46,7 +46,13 @@ final class HarbormasterTargetWorker extends HarbormasterWorker { $build = $target->getBuild(); $viewer = $this->getViewer(); - $target->setDateStarted(time()); + // If this is the first time we're starting work on this target, mark the + // current time as the start time. If the target yields or waits, we may + // end up here again later, so we don't want to overwrite the start time if + // we already have a value. + if (!$target->getDateStarted()) { + $target->setDateStarted(PhabricatorTime::getNow()); + } try { if ($target->getBuildGeneration() !== $build->getBuildGeneration()) { diff --git a/src/applications/herald/adapter/HeraldRuleAdapter.php b/src/applications/herald/adapter/HeraldRuleAdapter.php new file mode 100644 index 0000000000..8ed851a1a9 --- /dev/null +++ b/src/applications/herald/adapter/HeraldRuleAdapter.php @@ -0,0 +1,74 @@ +rule = $this->newObject(); + } + + public function supportsApplicationEmail() { + return true; + } + + public function supportsRuleType($rule_type) { + switch ($rule_type) { + case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL: + case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL: + return true; + case HeraldRuleTypeConfig::RULE_TYPE_OBJECT: + default: + return false; + } + } + + public function setRule(HeraldRule $rule) { + $this->rule = $rule; + return $this; + } + + public function getRule() { + return $this->rule; + } + + public function setObject($object) { + $this->rule = $object; + return $this; + } + + public function getObject() { + return $this->rule; + } + + public function getAdapterContentName() { + return pht('Herald Rules'); + } + + public function getHeraldName() { + return $this->getRule()->getMonogram(); + } + +} diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php index 4ddab2669b..5c2d26cf5a 100644 --- a/src/applications/herald/controller/HeraldTestConsoleController.php +++ b/src/applications/herald/controller/HeraldTestConsoleController.php @@ -38,8 +38,10 @@ final class HeraldTestConsoleController extends HeraldController { $object = $this->getTestObject(); $adapter = $this->getTestAdapter(); + $source = $this->newContentSource($object); $adapter + ->setContentSource($source) ->setIsNewObject(false) ->setActingAsPHID($viewer->getPHID()) ->setViewer($viewer); @@ -218,4 +220,29 @@ final class HeraldTestConsoleController extends HeraldController { ->appendChild($view); } + private function newContentSource($object) { + $viewer = $this->getViewer(); + + // Try using the content source associated with the most recent transaction + // on the object. + + $query = PhabricatorApplicationTransactionQuery::newQueryForObject($object); + + $xaction = $query + ->setViewer($viewer) + ->withObjectPHIDs(array($object->getPHID())) + ->setLimit(1) + ->setOrder('newest') + ->executeOne(); + if ($xaction) { + return $xaction->getContentSource(); + } + + // If we couldn't find a transaction (which should be rare), fall back to + // building a new content source from the test console request itself. + + $request = $this->getRequest(); + return PhabricatorContentSource::newFromRequest($request); + } + } diff --git a/src/applications/herald/editor/HeraldRuleEditor.php b/src/applications/herald/editor/HeraldRuleEditor.php index 30480108f4..8c83ef6597 100644 --- a/src/applications/herald/editor/HeraldRuleEditor.php +++ b/src/applications/herald/editor/HeraldRuleEditor.php @@ -87,4 +87,54 @@ final class HeraldRuleEditor return; } + protected function shouldApplyHeraldRules( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function buildHeraldAdapter( + PhabricatorLiskDAO $object, + array $xactions) { + return id(new HeraldRuleAdapter()) + ->setRule($object); + } + + protected function shouldSendMail( + PhabricatorLiskDAO $object, + array $xactions) { + return true; + } + + protected function getMailTo(PhabricatorLiskDAO $object) { + $phids = array(); + + $phids[] = $this->getActingAsPHID(); + + if ($object->isPersonalRule()) { + $phids[] = $object->getAuthorPHID(); + } + + return $phids; + } + + protected function buildReplyHandler(PhabricatorLiskDAO $object) { + return id(new HeraldRuleReplyHandler()) + ->setMailReceiver($object); + } + + protected function buildMailTemplate(PhabricatorLiskDAO $object) { + $monogram = $object->getMonogram(); + $name = $object->getName(); + + $subject = pht('%s: %s', $monogram, $name); + + return id(new PhabricatorMetaMTAMail()) + ->setSubject($subject); + } + + protected function getMailSubjectPrefix() { + return pht('[Herald]'); + } + } diff --git a/src/applications/herald/field/rule/HeraldRuleAdapterField.php b/src/applications/herald/field/rule/HeraldRuleAdapterField.php new file mode 100644 index 0000000000..bcb57f520f --- /dev/null +++ b/src/applications/herald/field/rule/HeraldRuleAdapterField.php @@ -0,0 +1,29 @@ +getContentType(); + } + + protected function getHeraldFieldStandardType() { + return self::STANDARD_PHID; + } + + protected function getDatasource() { + return new HeraldAdapterDatasource(); + } + + protected function getDatasourceValueMap() { + $adapters = HeraldAdapter::getAllAdapters(); + return mpull($adapters, 'getAdapterContentName', 'getAdapterContentType'); + } + +} diff --git a/src/applications/herald/field/rule/HeraldRuleField.php b/src/applications/herald/field/rule/HeraldRuleField.php new file mode 100644 index 0000000000..00ade268b3 --- /dev/null +++ b/src/applications/herald/field/rule/HeraldRuleField.php @@ -0,0 +1,14 @@ +getRuleType(); + } + + protected function getHeraldFieldStandardType() { + return self::STANDARD_PHID; + } + + protected function getDatasource() { + return new HeraldRuleTypeDatasource(); + } + + protected function getDatasourceValueMap() { + return HeraldRuleTypeConfig::getRuleTypeMap(); + } + +} diff --git a/src/applications/herald/mail/HeraldRuleReplyHandler.php b/src/applications/herald/mail/HeraldRuleReplyHandler.php new file mode 100644 index 0000000000..c4bf9fad44 --- /dev/null +++ b/src/applications/herald/mail/HeraldRuleReplyHandler.php @@ -0,0 +1,16 @@ +buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + protected function renderSpecialTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + + private function buildResults() { + $results = array(); + + $adapters = HeraldAdapter::getAllAdapters(); + foreach ($adapters as $adapter) { + $value = $adapter->getAdapterContentType(); + $name = $adapter->getAdapterContentName(); + + $result = id(new PhabricatorTypeaheadResult()) + ->setPHID($value) + ->setName($name); + + $results[$value] = $result; + } + + return $results; + } + +} diff --git a/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php b/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php new file mode 100644 index 0000000000..8dfa7d0f6a --- /dev/null +++ b/src/applications/herald/typeahead/HeraldRuleTypeDatasource.php @@ -0,0 +1,42 @@ +buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + protected function renderSpecialTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + + private function buildResults() { + $results = array(); + + $type_map = HeraldRuleTypeConfig::getRuleTypeMap(); + foreach ($type_map as $type => $name) { + $result = id(new PhabricatorTypeaheadResult()) + ->setPHID($type) + ->setName($name); + + $results[$type] = $result; + } + + return $results; + } + +} diff --git a/src/applications/metamta/engineextension/PhabricatorMailPropertiesDestructionEngineExtension.php b/src/applications/metamta/engineextension/PhabricatorMailPropertiesDestructionEngineExtension.php new file mode 100644 index 0000000000..4e9e0fe38f --- /dev/null +++ b/src/applications/metamta/engineextension/PhabricatorMailPropertiesDestructionEngineExtension.php @@ -0,0 +1,28 @@ +getPHID(); + $viewer = $engine->getViewer(); + + $properties = id(new PhabricatorMetaMTAMailPropertiesQuery()) + ->setViewer($viewer) + ->withObjectPHIDs(array($object_phid)) + ->executeOne(); + if ($properties) { + $properties->delete(); + } + } + +} diff --git a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php index 3cbe2345eb..46c444571b 100644 --- a/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php +++ b/src/applications/metamta/management/PhabricatorMailManagementReceiveTestWorkflow.php @@ -139,8 +139,10 @@ final class PhabricatorMailManagementReceiveTestWorkflow throw new Exception(pht("No such object '%s'!", $to)); } + $mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($object); + $hash = PhabricatorObjectMailReceiver::computeMailHash( - $object->getMailKey(), + $mail_key, $user->getPHID()); $header_content['to'] = $to.'+'.$user->getID().'+'.$hash.'@test.com'; diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php new file mode 100644 index 0000000000..e6ec09f4ff --- /dev/null +++ b/src/applications/metamta/query/PhabricatorMetaMTAMailPropertiesQuery.php @@ -0,0 +1,51 @@ +ids = $ids; + return $this; + } + + public function withObjectPHIDs(array $object_phids) { + $this->objectPHIDs = $object_phids; + return $this; + } + + public function newResultObject() { + return new PhabricatorMetaMTAMailProperties(); + } + + protected function loadPage() { + return $this->loadStandardPage($this->newResultObject()); + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->objectPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'objectPHID IN (%Ls)', + $this->objectPHIDs); + } + + return $where; + } + + public function getQueryApplicationClass() { + return 'PhabricatorMetaMTAApplication'; + } + +} diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php index 5985458ab4..801f7a8e6f 100644 --- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php @@ -124,7 +124,8 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver { $check_phid = $sender->getPHID(); } - $expect_hash = self::computeMailHash($object->getMailKey(), $check_phid); + $mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($object); + $expect_hash = self::computeMailHash($mail_key, $check_phid); if (!phutil_hashes_are_identical($expect_hash, $parts['hash'])) { throw new PhabricatorMetaMTAReceivedMailProcessingException( diff --git a/src/applications/metamta/receiver/__tests__/PhabricatorObjectMailReceiverTestCase.php b/src/applications/metamta/receiver/__tests__/PhabricatorObjectMailReceiverTestCase.php index 2c2e0561cf..a697436d21 100644 --- a/src/applications/metamta/receiver/__tests__/PhabricatorObjectMailReceiverTestCase.php +++ b/src/applications/metamta/receiver/__tests__/PhabricatorObjectMailReceiverTestCase.php @@ -98,8 +98,11 @@ final class PhabricatorObjectMailReceiverTestCase if ($is_bad_hash) { $hash = PhabricatorObjectMailReceiver::computeMailHash('x', 'y'); } else { + + $mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($task); + $hash = PhabricatorObjectMailReceiver::computeMailHash( - $task->getMailKey(), + $mail_key, $is_public ? $task->getPHID() : $user->getPHID()); } diff --git a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php index f8dd784e3b..70b524b52f 100644 --- a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php +++ b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php @@ -136,8 +136,11 @@ abstract class PhabricatorMailReplyHandler extends Phobject { // We compute a hash using the object's own PHID to prevent an attacker // from blindly interacting with objects that they haven't ever received // mail about by just sending to D1@, D2@, etc... + + $mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($receiver); + $hash = PhabricatorObjectMailReceiver::computeMailHash( - $receiver->getMailKey(), + $mail_key, $receiver->getPHID()); $address = "{$prefix}{$receiver_id}+public+{$hash}@{$domain}"; @@ -159,8 +162,11 @@ abstract class PhabricatorMailReplyHandler extends Phobject { $receiver = $this->getMailReceiver(); $receiver_id = $receiver->getID(); $user_id = $user->getID(); + + $mail_key = PhabricatorMetaMTAMailProperties::loadMailKey($receiver); + $hash = PhabricatorObjectMailReceiver::computeMailHash( - $receiver->getMailKey(), + $mail_key, $user->getPHID()); $domain = $this->getReplyHandlerDomain(); diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMailProperties.php b/src/applications/metamta/storage/PhabricatorMetaMTAMailProperties.php new file mode 100644 index 0000000000..eb454b650e --- /dev/null +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMailProperties.php @@ -0,0 +1,91 @@ + array( + 'mailProperties' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array(), + self::CONFIG_KEY_SCHEMA => array( + 'key_object' => array( + 'columns' => array('objectPHID'), + 'unique' => true, + ), + ), + ) + parent::getConfiguration(); + } + + public function getMailProperty($key, $default = null) { + return idx($this->mailProperties, $key, $default); + } + + public function setMailProperty($key, $value) { + $this->mailProperties[$key] = $value; + return $this; + } + + public static function loadMailKey($object) { + // If this is an older object with an onboard "mailKey" property, just + // use it. + // TODO: We should eventually get rid of these and get rid of this + // piece of code. + if ($object->hasProperty('mailKey')) { + return $object->getMailKey(); + } + + $viewer = PhabricatorUser::getOmnipotentUser(); + $object_phid = $object->getPHID(); + + $properties = id(new PhabricatorMetaMTAMailPropertiesQuery()) + ->setViewer($viewer) + ->withObjectPHIDs(array($object_phid)) + ->executeOne(); + if (!$properties) { + $properties = id(new self()) + ->setObjectPHID($object_phid); + } + + $mail_key = $properties->getMailProperty('mailKey'); + if ($mail_key !== null) { + return $mail_key; + } + + $mail_key = Filesystem::readRandomCharacters(20); + $properties->setMailProperty('mailKey', $mail_key); + + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + $properties->save(); + unset($unguarded); + + return $mail_key; + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::POLICY_NOONE; + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + +} diff --git a/src/docs/user/userguide/diffusion_existing.diviner b/src/docs/user/userguide/diffusion_existing.diviner index bf09d9ef1f..341146f0fd 100644 --- a/src/docs/user/userguide/diffusion_existing.diviner +++ b/src/docs/user/userguide/diffusion_existing.diviner @@ -47,7 +47,7 @@ There are two primary ways to import an existing repository: Once the import completes, change the "I/O Type" on the **Observe** URI to "No I/O" mode to automatically convert it into a hosted repository. -**Push to Empty Repository**: Create an activate an empty repository, then push +**Push to Empty Repository**: Create and activate an empty repository, then push all of your changes to the empty repository. In Git and Mercurial, you can do this with `git push` or `hg push`. diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php index d33f042d0b..a1e7ae4eae 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -1659,6 +1659,14 @@ final class PhabricatorUSEnglishTranslation 'you can no longer see were discarded.', ), + 'This draft revision will be sent for review once %s '. + 'build(s) pass: %s.' => array( + 'This draft revision will be sent for review once this build '. + 'passes: %2$s.', + 'This draft revision will be sent for review once these builds '. + 'pass: %2$s.', + ), + ); } diff --git a/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js b/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js index 5bff2c1bb0..db60c47055 100644 --- a/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js +++ b/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js @@ -16,7 +16,7 @@ JX.behavior('harbormaster-log', function(config) { e.kill(); - expand(e.getTarget(), true); + expand(e.getNode('harbormaster-log-expand'), true); }); function expand(node, is_action) {