1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-04 20:52:43 +01:00

(stable) Promote 2019 Week 34

This commit is contained in:
epriestley 2019-08-27 16:32:17 -07:00
commit 3e02f3c89c
167 changed files with 7176 additions and 3543 deletions

View file

@ -1 +0,0 @@
../scripts/user/account_admin.php

View file

@ -1 +0,0 @@
../scripts/people/manage_people.php

1
bin/user Symbolic link
View file

@ -0,0 +1 @@
../scripts/setup/manage_user.php

View file

@ -92,7 +92,7 @@ return array(
'rsrc/css/application/pholio/pholio.css' => '88ef5ef1',
'rsrc/css/application/phortune/phortune-credit-card-form.css' => '3b9868a8',
'rsrc/css/application/phortune/phortune-invoice.css' => '4436b241',
'rsrc/css/application/phortune/phortune.css' => '12e8251a',
'rsrc/css/application/phortune/phortune.css' => '508a1a5e',
'rsrc/css/application/phrequent/phrequent.css' => 'bd79cc67',
'rsrc/css/application/phriction/phriction-document-css.css' => '03380da0',
'rsrc/css/application/policy/policy-edit.css' => '8794e2ed',
@ -810,7 +810,7 @@ return array(
'pholio-inline-comments-css' => '722b48c2',
'phortune-credit-card-form' => 'd12d214f',
'phortune-credit-card-form-css' => '3b9868a8',
'phortune-css' => '12e8251a',
'phortune-css' => '508a1a5e',
'phortune-invoice-css' => '4436b241',
'phrequent-css' => 'bd79cc67',
'phriction-document-css' => '03380da0',

View file

@ -0,0 +1,12 @@
CREATE TABLE {$NAMESPACE}_phortune.phortune_accountemail (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
accountPHID VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
address VARCHAR(128) NOT NULL COLLATE {$COLLATE_SORT},
status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
addressKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
accessKey VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_phortune.phortune_accountemailtransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) NOT NULL,
oldValue LONGTEXT NOT NULL,
newValue LONGTEXT NOT NULL,
contentSource LONGTEXT NOT NULL,
metadata LONGTEXT NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,10 @@
<?php
$edge_type = PhortuneAccountHasMerchantEdgeType::EDGECONST;
$table = new PhortuneCart();
foreach (new LiskMigrationIterator($table) as $cart) {
id(new PhabricatorEdgeEditor())
->addEdge($cart->getAccountPHID(), $edge_type, $cart->getMerchantPHID())
->save();
}

View file

@ -0,0 +1,10 @@
<?php
$edge_type = PhortuneAccountHasMerchantEdgeType::EDGECONST;
$table = new PhortuneSubscription();
foreach (new LiskMigrationIterator($table) as $sub) {
id(new PhabricatorEdgeEditor())
->addEdge($sub->getAccountPHID(), $edge_type, $sub->getMerchantPHID())
->save();
}

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_phortune.phortune_paymentmethodtransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) NOT NULL,
oldValue LONGTEXT NOT NULL,
newValue LONGTEXT NOT NULL,
contentSource LONGTEXT NOT NULL,
metadata LONGTEXT NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_phortune.phortune_subscriptiontransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) NOT NULL,
oldValue LONGTEXT NOT NULL,
newValue LONGTEXT NOT NULL,
contentSource LONGTEXT NOT NULL,
metadata LONGTEXT NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phortune.phortune_merchant
DROP viewPolicy;

View file

@ -6,8 +6,8 @@ require_once $root.'/scripts/__init_script__.php';
$args = new PhutilArgumentParser($argv);
$args->setSynopsis(<<<EOSYNOPSIS
**people** __command__ [__options__]
Manage user profiles and accounts.
**user** __command__ [__options__]
Modify user accounts to regain access to an install.
EOSYNOPSIS
);

View file

@ -1,228 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$table = new PhabricatorUser();
$any_user = queryfx_one(
$table->establishConnection('r'),
'SELECT * FROM %T LIMIT 1',
$table->getTableName());
$is_first_user = (!$any_user);
if ($is_first_user) {
echo pht(
"WARNING\n\n".
"You're about to create the first account on this install. Normally, ".
"you should use the web interface to create the first account, not ".
"this script.\n\n".
"If you use the web interface, it will drop you into a nice UI workflow ".
"which gives you more help setting up your install. If you create an ".
"account with this script instead, you will skip the setup help and you ".
"will not be able to access it later.");
if (!phutil_console_confirm(pht('Skip easy setup and create account?'))) {
echo pht('Cancelled.')."\n";
exit(1);
}
}
echo pht(
'Enter a username to create a new account or edit an existing account.');
$username = phutil_console_prompt(pht('Enter a username:'));
if (!strlen($username)) {
echo pht('Cancelled.')."\n";
exit(1);
}
if (!PhabricatorUser::validateUsername($username)) {
$valid = PhabricatorUser::describeValidUsername();
echo pht("The username '%s' is invalid. %s", $username, $valid)."\n";
exit(1);
}
$user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$username);
if (!$user) {
$original = new PhabricatorUser();
echo pht("There is no existing user account '%s'.", $username)."\n";
$ok = phutil_console_confirm(
pht("Do you want to create a new '%s' account?", $username),
$default_no = false);
if (!$ok) {
echo pht('Cancelled.')."\n";
exit(1);
}
$user = new PhabricatorUser();
$user->setUsername($username);
$is_new = true;
} else {
$original = clone $user;
echo pht("There is an existing user account '%s'.", $username)."\n";
$ok = phutil_console_confirm(
pht("Do you want to edit the existing '%s' account?", $username),
$default_no = false);
if (!$ok) {
echo pht('Cancelled.')."\n";
exit(1);
}
$is_new = false;
}
$user_realname = $user->getRealName();
if (strlen($user_realname)) {
$realname_prompt = ' ['.$user_realname.']:';
} else {
$realname_prompt = ':';
}
$realname = nonempty(
phutil_console_prompt(pht('Enter user real name').$realname_prompt),
$user_realname);
$user->setRealName($realname);
// When creating a new user we prompt for an email address; when editing an
// existing user we just skip this because it would be quite involved to provide
// a reasonable CLI interface for editing multiple addresses and managing email
// verification and primary addresses.
$create_email = null;
if ($is_new) {
do {
$email = phutil_console_prompt(pht('Enter user email address:'));
$duplicate = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$email);
if ($duplicate) {
echo pht(
"ERROR: There is already a user with that email address. ".
"Each user must have a unique email address.\n");
} else {
break;
}
} while (true);
$create_email = $email;
}
$is_system_agent = $user->getIsSystemAgent();
$set_system_agent = phutil_console_confirm(
pht('Is this user a bot?'),
$default_no = !$is_system_agent);
$verify_email = null;
$set_verified = false;
// Allow administrators to verify primary email addresses at this time in edit
// scenarios. (Create will work just fine from here as we auto-verify email
// on create.)
if (!$is_new) {
$verify_email = $user->loadPrimaryEmail();
if (!$verify_email->getIsVerified()) {
$set_verified = phutil_console_confirm(
pht('Should the primary email address be verified?'),
$default_no = true);
} else {
// Already verified so let's not make a fuss.
$verify_email = null;
}
}
$is_admin = $user->getIsAdmin();
$set_admin = phutil_console_confirm(
pht('Should this user be an administrator?'),
$default_no = !$is_admin);
echo "\n\n".pht('ACCOUNT SUMMARY')."\n\n";
$tpl = "%12s %-30s %-30s\n";
printf($tpl, null, pht('OLD VALUE'), pht('NEW VALUE'));
printf($tpl, pht('Username'), $original->getUsername(), $user->getUsername());
printf($tpl, pht('Real Name'), $original->getRealName(), $user->getRealName());
if ($is_new) {
printf($tpl, pht('Email'), '', $create_email);
}
printf(
$tpl,
pht('Bot'),
$original->getIsSystemAgent() ? 'Y' : 'N',
$set_system_agent ? 'Y' : 'N');
if ($verify_email) {
printf(
$tpl,
pht('Verify Email'),
$verify_email->getIsVerified() ? 'Y' : 'N',
$set_verified ? 'Y' : 'N');
}
printf(
$tpl,
pht('Admin'),
$original->getIsAdmin() ? 'Y' : 'N',
$set_admin ? 'Y' : 'N');
echo "\n";
if (!phutil_console_confirm(pht('Save these changes?'), $default_no = false)) {
echo pht('Cancelled.')."\n";
exit(1);
}
$user->openTransaction();
$editor = new PhabricatorUserEditor();
// TODO: This is wrong, but we have a chicken-and-egg problem when you use
// this script to create the first user.
$editor->setActor($user);
if ($is_new) {
$email = id(new PhabricatorUserEmail())
->setAddress($create_email)
->setIsVerified(1);
// Unconditionally approve new accounts created from the CLI.
$user->setIsApproved(1);
$editor->createNewUser($user, $email);
} else {
if ($verify_email) {
$user->setIsEmailVerified(1);
$verify_email->setIsVerified($set_verified ? 1 : 0);
}
$editor->updateUser($user, $verify_email);
}
$editor->makeSystemAgentUser($user, $set_system_agent);
$xactions = array();
$xactions[] = id(new PhabricatorUserTransaction())
->setTransactionType(
PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE)
->setNewValue($set_admin);
$actor = PhabricatorUser::getOmnipotentUser();
$content_source = PhabricatorContentSource::newForSource(
PhabricatorConsoleContentSource::SOURCECONST);
$people_application_phid = id(new PhabricatorPeopleApplication())->getPHID();
$transaction_editor = id(new PhabricatorUserTransactionEditor())
->setActor($actor)
->setActingAsPHID($people_application_phid)
->setContentSource($content_source)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$transaction_editor->applyTransactions($user, $xactions);
$user->saveTransaction();
echo pht('Saved changes.')."\n";

View file

@ -1,73 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
if ($argc !== 5) {
echo pht(
"Usage: %s\n",
'add_user.php <username> <email> <realname> <admin_user>');
exit(1);
}
$username = $argv[1];
$email = $argv[2];
$realname = $argv[3];
$admin = $argv[4];
$admin = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$argv[4]);
if (!$admin) {
throw new Exception(
pht(
'Admin user must be the username of a valid Phabricator account, used '.
'to send the new user a welcome email.'));
}
$existing_user = id(new PhabricatorUser())->loadOneWhere(
'username = %s',
$username);
if ($existing_user) {
throw new Exception(
pht(
"There is already a user with the username '%s'!",
$username));
}
$existing_email = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$email);
if ($existing_email) {
throw new Exception(
pht(
"There is already a user with the email '%s'!",
$email));
}
$user = new PhabricatorUser();
$user->setUsername($username);
$user->setRealname($realname);
$user->setIsApproved(1);
$email_object = id(new PhabricatorUserEmail())
->setAddress($email)
->setIsVerified(1);
id(new PhabricatorUserEditor())
->setActor($admin)
->createNewUser($user, $email_object);
$welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine())
->setSender($admin)
->setRecipient($user);
if ($welcome_engine->canSendMail()) {
$welcome_engine->sendMail();
}
echo pht(
"Created user '%s' (realname='%s', email='%s').\n",
$username,
$realname,
$email);

View file

@ -1222,6 +1222,7 @@ phutil_register_library_map(array(
'DrydockResourceQuery' => 'applications/drydock/query/DrydockResourceQuery.php',
'DrydockResourceReclaimLogType' => 'applications/drydock/logtype/DrydockResourceReclaimLogType.php',
'DrydockResourceReleaseController' => 'applications/drydock/controller/DrydockResourceReleaseController.php',
'DrydockResourceSearchConduitAPIMethod' => 'applications/drydock/conduit/DrydockResourceSearchConduitAPIMethod.php',
'DrydockResourceSearchEngine' => 'applications/drydock/query/DrydockResourceSearchEngine.php',
'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php',
'DrydockResourceUpdateWorker' => 'applications/drydock/worker/DrydockResourceUpdateWorker.php',
@ -4050,6 +4051,8 @@ phutil_register_library_map(array(
'PhabricatorPeopleMailEngine' => 'applications/people/mail/PhabricatorPeopleMailEngine.php',
'PhabricatorPeopleMailEngineException' => 'applications/people/mail/PhabricatorPeopleMailEngineException.php',
'PhabricatorPeopleManageProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php',
'PhabricatorPeopleManagementEmpowerWorkflow' => 'applications/people/management/PhabricatorPeopleManagementEmpowerWorkflow.php',
'PhabricatorPeopleManagementEnableWorkflow' => 'applications/people/management/PhabricatorPeopleManagementEnableWorkflow.php',
'PhabricatorPeopleManagementWorkflow' => 'applications/people/management/PhabricatorPeopleManagementWorkflow.php',
'PhabricatorPeopleNewController' => 'applications/people/controller/PhabricatorPeopleNewController.php',
'PhabricatorPeopleNoOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php',
@ -4059,7 +4062,6 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileCommitsController' => 'applications/people/controller/PhabricatorPeopleProfileCommitsController.php',
'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php',
'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php',
'PhabricatorPeopleProfileImageWorkflow' => 'applications/people/management/PhabricatorPeopleProfileImageWorkflow.php',
'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php',
'PhabricatorPeopleProfileMenuEngine' => 'applications/people/engine/PhabricatorPeopleProfileMenuEngine.php',
'PhabricatorPeopleProfilePictureController' => 'applications/people/controller/PhabricatorPeopleProfilePictureController.php',
@ -5219,25 +5221,50 @@ phutil_register_library_map(array(
'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php',
'PhortuneAccountAddManagerController' => 'applications/phortune/controller/account/PhortuneAccountAddManagerController.php',
'PhortuneAccountBillingAddressTransaction' => 'applications/phortune/xaction/PhortuneAccountBillingAddressTransaction.php',
'PhortuneAccountBillingController' => 'applications/phortune/controller/account/PhortuneAccountBillingController.php',
'PhortuneAccountBillingNameTransaction' => 'applications/phortune/xaction/PhortuneAccountBillingNameTransaction.php',
'PhortuneAccountChargeListController' => 'applications/phortune/controller/account/PhortuneAccountChargeListController.php',
'PhortuneAccountChargesController' => 'applications/phortune/controller/account/PhortuneAccountChargesController.php',
'PhortuneAccountController' => 'applications/phortune/controller/account/PhortuneAccountController.php',
'PhortuneAccountDetailsController' => 'applications/phortune/controller/account/PhortuneAccountDetailsController.php',
'PhortuneAccountEditController' => 'applications/phortune/controller/account/PhortuneAccountEditController.php',
'PhortuneAccountEditEngine' => 'applications/phortune/editor/PhortuneAccountEditEngine.php',
'PhortuneAccountEditor' => 'applications/phortune/editor/PhortuneAccountEditor.php',
'PhortuneAccountEmail' => 'applications/phortune/storage/PhortuneAccountEmail.php',
'PhortuneAccountEmailAddressTransaction' => 'applications/phortune/xaction/PhortuneAccountEmailAddressTransaction.php',
'PhortuneAccountEmailAddressesController' => 'applications/phortune/controller/account/PhortuneAccountEmailAddressesController.php',
'PhortuneAccountEmailEditController' => 'applications/phortune/controller/account/PhortuneAccountEmailEditController.php',
'PhortuneAccountEmailEditEngine' => 'applications/phortune/editor/PhortuneAccountEmailEditEngine.php',
'PhortuneAccountEmailEditor' => 'applications/phortune/editor/PhortuneAccountEmailEditor.php',
'PhortuneAccountEmailPHIDType' => 'applications/phortune/phid/PhortuneAccountEmailPHIDType.php',
'PhortuneAccountEmailQuery' => 'applications/phortune/query/PhortuneAccountEmailQuery.php',
'PhortuneAccountEmailRotateController' => 'applications/phortune/controller/account/PhortuneAccountEmailRotateController.php',
'PhortuneAccountEmailRotateTransaction' => 'applications/phortune/xaction/PhortuneAccountEmailRotateTransaction.php',
'PhortuneAccountEmailStatus' => 'applications/phortune/constants/PhortuneAccountEmailStatus.php',
'PhortuneAccountEmailStatusController' => 'applications/phortune/controller/account/PhortuneAccountEmailStatusController.php',
'PhortuneAccountEmailStatusTransaction' => 'applications/phortune/xaction/PhortuneAccountEmailStatusTransaction.php',
'PhortuneAccountEmailTransaction' => 'applications/phortune/storage/PhortuneAccountEmailTransaction.php',
'PhortuneAccountEmailTransactionQuery' => 'applications/phortune/query/PhortuneAccountEmailTransactionQuery.php',
'PhortuneAccountEmailTransactionType' => 'applications/phortune/xaction/PhortuneAccountEmailTransactionType.php',
'PhortuneAccountEmailViewController' => 'applications/phortune/controller/account/PhortuneAccountEmailViewController.php',
'PhortuneAccountHasMemberEdgeType' => 'applications/phortune/edge/PhortuneAccountHasMemberEdgeType.php',
'PhortuneAccountHasMerchantEdgeType' => 'applications/phortune/edge/PhortuneAccountHasMerchantEdgeType.php',
'PhortuneAccountListController' => 'applications/phortune/controller/account/PhortuneAccountListController.php',
'PhortuneAccountManagerController' => 'applications/phortune/controller/account/PhortuneAccountManagerController.php',
'PhortuneAccountManagersController' => 'applications/phortune/controller/account/PhortuneAccountManagersController.php',
'PhortuneAccountNameTransaction' => 'applications/phortune/xaction/PhortuneAccountNameTransaction.php',
'PhortuneAccountOrderListController' => 'applications/phortune/controller/account/PhortuneAccountOrderListController.php',
'PhortuneAccountOrdersController' => 'applications/phortune/controller/account/PhortuneAccountOrdersController.php',
'PhortuneAccountOverviewController' => 'applications/phortune/controller/account/PhortuneAccountOverviewController.php',
'PhortuneAccountPHIDType' => 'applications/phortune/phid/PhortuneAccountPHIDType.php',
'PhortuneAccountPaymentMethodController' => 'applications/phortune/controller/account/PhortuneAccountPaymentMethodController.php',
'PhortuneAccountPaymentMethodViewController' => 'applications/phortune/controller/account/PhortuneAccountPaymentMethodViewController.php',
'PhortuneAccountProfileController' => 'applications/phortune/controller/account/PhortuneAccountProfileController.php',
'PhortuneAccountQuery' => 'applications/phortune/query/PhortuneAccountQuery.php',
'PhortuneAccountSubscriptionAutopayController' => 'applications/phortune/controller/account/PhortuneAccountSubscriptionAutopayController.php',
'PhortuneAccountSubscriptionController' => 'applications/phortune/controller/account/PhortuneAccountSubscriptionController.php',
'PhortuneAccountSubscriptionViewController' => 'applications/phortune/controller/account/PhortuneAccountSubscriptionViewController.php',
'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php',
'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php',
'PhortuneAccountTransactionType' => 'applications/phortune/xaction/PhortuneAccountTransactionType.php',
'PhortuneAccountViewController' => 'applications/phortune/controller/account/PhortuneAccountViewController.php',
'PhortuneAdHocCart' => 'applications/phortune/cart/PhortuneAdHocCart.php',
'PhortuneAdHocProduct' => 'applications/phortune/product/PhortuneAdHocProduct.php',
'PhortuneAddPaymentMethodAction' => 'applications/phortune/action/PhortuneAddPaymentMethodAction.php',
@ -5248,7 +5275,6 @@ phutil_register_library_map(array(
'PhortuneCartController' => 'applications/phortune/controller/cart/PhortuneCartController.php',
'PhortuneCartEditor' => 'applications/phortune/editor/PhortuneCartEditor.php',
'PhortuneCartImplementation' => 'applications/phortune/cart/PhortuneCartImplementation.php',
'PhortuneCartListController' => 'applications/phortune/controller/cart/PhortuneCartListController.php',
'PhortuneCartPHIDType' => 'applications/phortune/phid/PhortuneCartPHIDType.php',
'PhortuneCartQuery' => 'applications/phortune/query/PhortuneCartQuery.php',
'PhortuneCartReplyHandler' => 'applications/phortune/mail/PhortuneCartReplyHandler.php',
@ -5257,6 +5283,7 @@ phutil_register_library_map(array(
'PhortuneCartTransactionQuery' => 'applications/phortune/query/PhortuneCartTransactionQuery.php',
'PhortuneCartUpdateController' => 'applications/phortune/controller/cart/PhortuneCartUpdateController.php',
'PhortuneCartViewController' => 'applications/phortune/controller/cart/PhortuneCartViewController.php',
'PhortuneCartVoidController' => 'applications/phortune/controller/cart/PhortuneCartVoidController.php',
'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php',
'PhortuneChargePHIDType' => 'applications/phortune/phid/PhortuneChargePHIDType.php',
'PhortuneChargeQuery' => 'applications/phortune/query/PhortuneChargeQuery.php',
@ -5271,7 +5298,10 @@ phutil_register_library_map(array(
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
'PhortuneDisplayException' => 'applications/phortune/exception/PhortuneDisplayException.php',
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneInvoiceView' => 'applications/phortune/view/PhortuneInvoiceView.php',
'PhortuneExternalController' => 'applications/phortune/controller/external/PhortuneExternalController.php',
'PhortuneExternalOrderController' => 'applications/phortune/controller/external/PhortuneExternalOrderController.php',
'PhortuneExternalOverviewController' => 'applications/phortune/controller/external/PhortuneExternalOverviewController.php',
'PhortuneExternalUnsubscribeController' => 'applications/phortune/controller/external/PhortuneExternalUnsubscribeController.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMemberHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMemberHasAccountEdgeType.php',
'PhortuneMemberHasMerchantEdgeType' => 'applications/phortune/edge/PhortuneMemberHasMerchantEdgeType.php',
@ -5281,35 +5311,56 @@ phutil_register_library_map(array(
'PhortuneMerchantContactInfoTransaction' => 'applications/phortune/xaction/PhortuneMerchantContactInfoTransaction.php',
'PhortuneMerchantController' => 'applications/phortune/controller/merchant/PhortuneMerchantController.php',
'PhortuneMerchantDescriptionTransaction' => 'applications/phortune/xaction/PhortuneMerchantDescriptionTransaction.php',
'PhortuneMerchantDetailsController' => 'applications/phortune/controller/merchant/PhortuneMerchantDetailsController.php',
'PhortuneMerchantEditController' => 'applications/phortune/controller/merchant/PhortuneMerchantEditController.php',
'PhortuneMerchantEditEngine' => 'applications/phortune/editor/PhortuneMerchantEditEngine.php',
'PhortuneMerchantEditor' => 'applications/phortune/editor/PhortuneMerchantEditor.php',
'PhortuneMerchantHasAccountEdgeType' => 'applications/phortune/edge/PhortuneMerchantHasAccountEdgeType.php',
'PhortuneMerchantHasMemberEdgeType' => 'applications/phortune/edge/PhortuneMerchantHasMemberEdgeType.php',
'PhortuneMerchantInvoiceCreateController' => 'applications/phortune/controller/merchant/PhortuneMerchantInvoiceCreateController.php',
'PhortuneMerchantInvoiceEmailTransaction' => 'applications/phortune/xaction/PhortuneMerchantInvoiceEmailTransaction.php',
'PhortuneMerchantInvoiceFooterTransaction' => 'applications/phortune/xaction/PhortuneMerchantInvoiceFooterTransaction.php',
'PhortuneMerchantListController' => 'applications/phortune/controller/merchant/PhortuneMerchantListController.php',
'PhortuneMerchantManagerController' => 'applications/phortune/controller/merchant/PhortuneMerchantManagerController.php',
'PhortuneMerchantManagersController' => 'applications/phortune/controller/merchant/PhortuneMerchantManagersController.php',
'PhortuneMerchantNameTransaction' => 'applications/phortune/xaction/PhortuneMerchantNameTransaction.php',
'PhortuneMerchantOrderListController' => 'applications/phortune/controller/merchant/PhortuneMerchantOrderListController.php',
'PhortuneMerchantOrdersController' => 'applications/phortune/controller/merchant/PhortuneMerchantOrdersController.php',
'PhortuneMerchantOverviewController' => 'applications/phortune/controller/merchant/PhortuneMerchantOverviewController.php',
'PhortuneMerchantPHIDType' => 'applications/phortune/phid/PhortuneMerchantPHIDType.php',
'PhortuneMerchantPictureController' => 'applications/phortune/controller/merchant/PhortuneMerchantPictureController.php',
'PhortuneMerchantPictureTransaction' => 'applications/phortune/xaction/PhortuneMerchantPictureTransaction.php',
'PhortuneMerchantProfileController' => 'applications/phortune/controller/merchant/PhortuneMerchantProfileController.php',
'PhortuneMerchantProviderDisableController' => 'applications/phortune/controller/merchant/PhortuneMerchantProviderDisableController.php',
'PhortuneMerchantProviderEditController' => 'applications/phortune/controller/merchant/PhortuneMerchantProviderEditController.php',
'PhortuneMerchantProviderViewController' => 'applications/phortune/controller/merchant/PhortuneMerchantProviderViewController.php',
'PhortuneMerchantProvidersController' => 'applications/phortune/controller/merchant/PhortuneMerchantProvidersController.php',
'PhortuneMerchantQuery' => 'applications/phortune/query/PhortuneMerchantQuery.php',
'PhortuneMerchantSearchEngine' => 'applications/phortune/query/PhortuneMerchantSearchEngine.php',
'PhortuneMerchantSubscriptionListController' => 'applications/phortune/controller/merchant/PhortuneMerchantSubscriptionListController.php',
'PhortuneMerchantSubscriptionsController' => 'applications/phortune/controller/merchant/PhortuneMerchantSubscriptionsController.php',
'PhortuneMerchantTransaction' => 'applications/phortune/storage/PhortuneMerchantTransaction.php',
'PhortuneMerchantTransactionQuery' => 'applications/phortune/query/PhortuneMerchantTransactionQuery.php',
'PhortuneMerchantTransactionType' => 'applications/phortune/xaction/PhortuneMerchantTransactionType.php',
'PhortuneMerchantViewController' => 'applications/phortune/controller/merchant/PhortuneMerchantViewController.php',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
'PhortuneOrderDescriptionView' => 'applications/phortune/view/PhortuneOrderDescriptionView.php',
'PhortuneOrderItemsView' => 'applications/phortune/view/PhortuneOrderItemsView.php',
'PhortuneOrderSummaryView' => 'applications/phortune/view/PhortuneOrderSummaryView.php',
'PhortuneOrderTableView' => 'applications/phortune/view/PhortuneOrderTableView.php',
'PhortuneOrderView' => 'applications/phortune/view/PhortuneOrderView.php',
'PhortunePayPalPaymentProvider' => 'applications/phortune/provider/PhortunePayPalPaymentProvider.php',
'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php',
'PhortunePaymentMethodCreateController' => 'applications/phortune/controller/payment/PhortunePaymentMethodCreateController.php',
'PhortunePaymentMethodDisableController' => 'applications/phortune/controller/payment/PhortunePaymentMethodDisableController.php',
'PhortunePaymentMethodEditController' => 'applications/phortune/controller/payment/PhortunePaymentMethodEditController.php',
'PhortunePaymentMethodCreateController' => 'applications/phortune/controller/paymentmethod/PhortunePaymentMethodCreateController.php',
'PhortunePaymentMethodDisableController' => 'applications/phortune/controller/paymentmethod/PhortunePaymentMethodDisableController.php',
'PhortunePaymentMethodEditController' => 'applications/phortune/controller/paymentmethod/PhortunePaymentMethodEditController.php',
'PhortunePaymentMethodEditor' => 'applications/phortune/editor/PhortunePaymentMethodEditor.php',
'PhortunePaymentMethodNameTransaction' => 'applications/phortune/xaction/paymentmethod/PhortunePaymentMethodNameTransaction.php',
'PhortunePaymentMethodPHIDType' => 'applications/phortune/phid/PhortunePaymentMethodPHIDType.php',
'PhortunePaymentMethodPolicyCodex' => 'applications/phortune/codex/PhortunePaymentMethodPolicyCodex.php',
'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.php',
'PhortunePaymentMethodStatusTransaction' => 'applications/phortune/xaction/paymentmethod/PhortunePaymentMethodStatusTransaction.php',
'PhortunePaymentMethodTransaction' => 'applications/phortune/storage/PhortunePaymentMethodTransaction.php',
'PhortunePaymentMethodTransactionQuery' => 'applications/phortune/query/PhortunePaymentMethodTransactionQuery.php',
'PhortunePaymentMethodTransactionType' => 'applications/phortune/xaction/paymentmethod/PhortunePaymentMethodTransactionType.php',
'PhortunePaymentProvider' => 'applications/phortune/provider/PhortunePaymentProvider.php',
'PhortunePaymentProviderConfig' => 'applications/phortune/storage/PhortunePaymentProviderConfig.php',
'PhortunePaymentProviderConfigEditor' => 'applications/phortune/editor/PhortunePaymentProviderConfigEditor.php',
@ -5325,24 +5376,25 @@ phutil_register_library_map(array(
'PhortuneProductQuery' => 'applications/phortune/query/PhortuneProductQuery.php',
'PhortuneProductViewController' => 'applications/phortune/controller/product/PhortuneProductViewController.php',
'PhortuneProviderActionController' => 'applications/phortune/controller/provider/PhortuneProviderActionController.php',
'PhortuneProviderDisableController' => 'applications/phortune/controller/provider/PhortuneProviderDisableController.php',
'PhortuneProviderEditController' => 'applications/phortune/controller/provider/PhortuneProviderEditController.php',
'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
'PhortunePurchasePHIDType' => 'applications/phortune/phid/PhortunePurchasePHIDType.php',
'PhortunePurchaseQuery' => 'applications/phortune/query/PhortunePurchaseQuery.php',
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php',
'PhortuneSubscriptionAutopayTransaction' => 'applications/phortune/xaction/subscription/PhortuneSubscriptionAutopayTransaction.php',
'PhortuneSubscriptionCart' => 'applications/phortune/cart/PhortuneSubscriptionCart.php',
'PhortuneSubscriptionEditController' => 'applications/phortune/controller/subscription/PhortuneSubscriptionEditController.php',
'PhortuneSubscriptionEditor' => 'applications/phortune/editor/PhortuneSubscriptionEditor.php',
'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php',
'PhortuneSubscriptionListController' => 'applications/phortune/controller/subscription/PhortuneSubscriptionListController.php',
'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php',
'PhortuneSubscriptionPolicyCodex' => 'applications/phortune/codex/PhortuneSubscriptionPolicyCodex.php',
'PhortuneSubscriptionProduct' => 'applications/phortune/product/PhortuneSubscriptionProduct.php',
'PhortuneSubscriptionQuery' => 'applications/phortune/query/PhortuneSubscriptionQuery.php',
'PhortuneSubscriptionSearchEngine' => 'applications/phortune/query/PhortuneSubscriptionSearchEngine.php',
'PhortuneSubscriptionTableView' => 'applications/phortune/view/PhortuneSubscriptionTableView.php',
'PhortuneSubscriptionViewController' => 'applications/phortune/controller/subscription/PhortuneSubscriptionViewController.php',
'PhortuneSubscriptionTransaction' => 'applications/phortune/storage/PhortuneSubscriptionTransaction.php',
'PhortuneSubscriptionTransactionQuery' => 'applications/phortune/query/PhortuneSubscriptionTransactionQuery.php',
'PhortuneSubscriptionTransactionType' => 'applications/phortune/xaction/subscription/PhortuneSubscriptionTransactionType.php',
'PhortuneSubscriptionWorker' => 'applications/phortune/worker/PhortuneSubscriptionWorker.php',
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
@ -7021,6 +7073,7 @@ phutil_register_library_map(array(
'DrydockResource' => array(
'DrydockDAO',
'PhabricatorPolicyInterface',
'PhabricatorConduitResultInterface',
),
'DrydockResourceActivationFailureLogType' => 'DrydockLogType',
'DrydockResourceActivationYieldLogType' => 'DrydockLogType',
@ -7034,6 +7087,7 @@ phutil_register_library_map(array(
'DrydockResourceQuery' => 'DrydockQuery',
'DrydockResourceReclaimLogType' => 'DrydockLogType',
'DrydockResourceReleaseController' => 'DrydockResourceController',
'DrydockResourceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'DrydockResourceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DrydockResourceStatus' => 'PhabricatorObjectStatus',
'DrydockResourceUpdateWorker' => 'DrydockWorker',
@ -10329,6 +10383,8 @@ phutil_register_library_map(array(
'PhabricatorPeopleMailEngine' => 'Phobject',
'PhabricatorPeopleMailEngineException' => 'Exception',
'PhabricatorPeopleManageProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleManagementEmpowerWorkflow' => 'PhabricatorPeopleManagementWorkflow',
'PhabricatorPeopleManagementEnableWorkflow' => 'PhabricatorPeopleManagementWorkflow',
'PhabricatorPeopleManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorPeopleNewController' => 'PhabricatorPeopleController',
'PhabricatorPeopleNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
@ -10338,7 +10394,6 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileCommitsController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileImageWorkflow' => 'PhabricatorPeopleManagementWorkflow',
'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileMenuEngine' => 'PhabricatorProfileMenuEngine',
'PhabricatorPeopleProfilePictureController' => 'PhabricatorPeopleProfileController',
@ -11746,27 +11801,57 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
),
'PhortuneAccountAddManagerController' => 'PhortuneController',
'PhortuneAccountAddManagerController' => 'PhortuneAccountController',
'PhortuneAccountBillingAddressTransaction' => 'PhortuneAccountTransactionType',
'PhortuneAccountBillingController' => 'PhortuneAccountProfileController',
'PhortuneAccountBillingNameTransaction' => 'PhortuneAccountTransactionType',
'PhortuneAccountChargeListController' => 'PhortuneController',
'PhortuneAccountChargeListController' => 'PhortuneAccountProfileController',
'PhortuneAccountChargesController' => 'PhortuneAccountProfileController',
'PhortuneAccountController' => 'PhortuneController',
'PhortuneAccountDetailsController' => 'PhortuneAccountProfileController',
'PhortuneAccountEditController' => 'PhortuneController',
'PhortuneAccountEditEngine' => 'PhabricatorEditEngine',
'PhortuneAccountEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneAccountEmail' => array(
'PhortuneDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
),
'PhortuneAccountEmailAddressTransaction' => 'PhortuneAccountEmailTransactionType',
'PhortuneAccountEmailAddressesController' => 'PhortuneAccountProfileController',
'PhortuneAccountEmailEditController' => 'PhortuneAccountController',
'PhortuneAccountEmailEditEngine' => 'PhabricatorEditEngine',
'PhortuneAccountEmailEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneAccountEmailPHIDType' => 'PhabricatorPHIDType',
'PhortuneAccountEmailQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneAccountEmailRotateController' => 'PhortuneAccountController',
'PhortuneAccountEmailRotateTransaction' => 'PhortuneAccountEmailTransactionType',
'PhortuneAccountEmailStatus' => 'Phobject',
'PhortuneAccountEmailStatusController' => 'PhortuneAccountController',
'PhortuneAccountEmailStatusTransaction' => 'PhortuneAccountEmailTransactionType',
'PhortuneAccountEmailTransaction' => 'PhabricatorModularTransaction',
'PhortuneAccountEmailTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneAccountEmailTransactionType' => 'PhabricatorModularTransactionType',
'PhortuneAccountEmailViewController' => 'PhortuneAccountController',
'PhortuneAccountHasMemberEdgeType' => 'PhabricatorEdgeType',
'PhortuneAccountHasMerchantEdgeType' => 'PhabricatorEdgeType',
'PhortuneAccountListController' => 'PhortuneController',
'PhortuneAccountManagerController' => 'PhortuneAccountProfileController',
'PhortuneAccountManagersController' => 'PhortuneAccountProfileController',
'PhortuneAccountNameTransaction' => 'PhortuneAccountTransactionType',
'PhortuneAccountOrderListController' => 'PhortuneAccountProfileController',
'PhortuneAccountOrdersController' => 'PhortuneAccountProfileController',
'PhortuneAccountOverviewController' => 'PhortuneAccountProfileController',
'PhortuneAccountPHIDType' => 'PhabricatorPHIDType',
'PhortuneAccountPaymentMethodController' => 'PhortuneAccountProfileController',
'PhortuneAccountPaymentMethodViewController' => 'PhortuneAccountController',
'PhortuneAccountProfileController' => 'PhortuneAccountController',
'PhortuneAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneAccountSubscriptionAutopayController' => 'PhortuneAccountController',
'PhortuneAccountSubscriptionController' => 'PhortuneAccountProfileController',
'PhortuneAccountSubscriptionViewController' => 'PhortuneAccountController',
'PhortuneAccountTransaction' => 'PhabricatorModularTransaction',
'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneAccountTransactionType' => 'PhabricatorModularTransactionType',
'PhortuneAccountViewController' => 'PhortuneAccountProfileController',
'PhortuneAdHocCart' => 'PhortuneCartImplementation',
'PhortuneAdHocProduct' => 'PhortuneProductImplementation',
'PhortuneAddPaymentMethodAction' => 'PhabricatorSystemAction',
@ -11774,6 +11859,7 @@ phutil_register_library_map(array(
'PhortuneDAO',
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
),
'PhortuneCartAcceptController' => 'PhortuneCartController',
'PhortuneCartCancelController' => 'PhortuneCartController',
@ -11781,7 +11867,6 @@ phutil_register_library_map(array(
'PhortuneCartController' => 'PhortuneController',
'PhortuneCartEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneCartImplementation' => 'Phobject',
'PhortuneCartListController' => 'PhortuneController',
'PhortuneCartPHIDType' => 'PhabricatorPHIDType',
'PhortuneCartQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneCartReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
@ -11790,9 +11875,11 @@ phutil_register_library_map(array(
'PhortuneCartTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneCartUpdateController' => 'PhortuneCartController',
'PhortuneCartViewController' => 'PhortuneCartController',
'PhortuneCartVoidController' => 'PhortuneCartController',
'PhortuneCharge' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
),
'PhortuneChargePHIDType' => 'PhabricatorPHIDType',
'PhortuneChargeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
@ -11807,7 +11894,10 @@ phutil_register_library_map(array(
'PhortuneDAO' => 'PhabricatorLiskDAO',
'PhortuneDisplayException' => 'Exception',
'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneInvoiceView' => 'AphrontTagView',
'PhortuneExternalController' => 'PhortuneController',
'PhortuneExternalOrderController' => 'PhortuneExternalController',
'PhortuneExternalOverviewController' => 'PhortuneExternalController',
'PhortuneExternalUnsubscribeController' => 'PhortuneExternalController',
'PhortuneLandingController' => 'PhortuneController',
'PhortuneMemberHasAccountEdgeType' => 'PhabricatorEdgeType',
'PhortuneMemberHasMerchantEdgeType' => 'PhabricatorEdgeType',
@ -11816,43 +11906,67 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
),
'PhortuneMerchantAddManagerController' => 'PhortuneController',
'PhortuneMerchantAddManagerController' => 'PhortuneMerchantController',
'PhortuneMerchantCapability' => 'PhabricatorPolicyCapability',
'PhortuneMerchantContactInfoTransaction' => 'PhortuneMerchantTransactionType',
'PhortuneMerchantController' => 'PhortuneController',
'PhortuneMerchantDescriptionTransaction' => 'PhortuneMerchantTransactionType',
'PhortuneMerchantEditController' => 'PhortuneMerchantController',
'PhortuneMerchantDetailsController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantEditController' => 'PhortuneController',
'PhortuneMerchantEditEngine' => 'PhabricatorEditEngine',
'PhortuneMerchantEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneMerchantHasAccountEdgeType' => 'PhabricatorEdgeType',
'PhortuneMerchantHasMemberEdgeType' => 'PhabricatorEdgeType',
'PhortuneMerchantInvoiceCreateController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantInvoiceCreateController' => 'PhortuneMerchantController',
'PhortuneMerchantInvoiceEmailTransaction' => 'PhortuneMerchantTransactionType',
'PhortuneMerchantInvoiceFooterTransaction' => 'PhortuneMerchantTransactionType',
'PhortuneMerchantListController' => 'PhortuneMerchantController',
'PhortuneMerchantManagerController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantListController' => 'PhortuneController',
'PhortuneMerchantManagersController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantNameTransaction' => 'PhortuneMerchantTransactionType',
'PhortuneMerchantOrderListController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantOrdersController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantOverviewController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantPHIDType' => 'PhabricatorPHIDType',
'PhortuneMerchantPictureController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantPictureController' => 'PhortuneMerchantController',
'PhortuneMerchantPictureTransaction' => 'PhortuneMerchantTransactionType',
'PhortuneMerchantProfileController' => 'PhortuneController',
'PhortuneMerchantProfileController' => 'PhortuneMerchantController',
'PhortuneMerchantProviderDisableController' => 'PhortuneMerchantController',
'PhortuneMerchantProviderEditController' => 'PhortuneMerchantController',
'PhortuneMerchantProviderViewController' => 'PhortuneMerchantController',
'PhortuneMerchantProvidersController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneMerchantSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhortuneMerchantSubscriptionListController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantSubscriptionsController' => 'PhortuneMerchantProfileController',
'PhortuneMerchantTransaction' => 'PhabricatorModularTransaction',
'PhortuneMerchantTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneMerchantTransactionType' => 'PhabricatorModularTransactionType',
'PhortuneMerchantViewController' => 'PhortuneMerchantProfileController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
'PhortuneOrderDescriptionView' => 'AphrontView',
'PhortuneOrderItemsView' => 'PhortuneOrderView',
'PhortuneOrderSummaryView' => 'PhortuneOrderView',
'PhortuneOrderTableView' => 'AphrontView',
'PhortuneOrderView' => 'AphrontView',
'PhortunePayPalPaymentProvider' => 'PhortunePaymentProvider',
'PhortunePaymentMethod' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
'PhabricatorPolicyCodexInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhortunePaymentMethodCreateController' => 'PhortuneController',
'PhortunePaymentMethodDisableController' => 'PhortuneController',
'PhortunePaymentMethodEditController' => 'PhortuneController',
'PhortunePaymentMethodEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortunePaymentMethodNameTransaction' => 'PhortunePaymentMethodTransactionType',
'PhortunePaymentMethodPHIDType' => 'PhabricatorPHIDType',
'PhortunePaymentMethodPolicyCodex' => 'PhabricatorPolicyCodex',
'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortunePaymentMethodStatusTransaction' => 'PhortunePaymentMethodTransactionType',
'PhortunePaymentMethodTransaction' => 'PhabricatorModularTransaction',
'PhortunePaymentMethodTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortunePaymentMethodTransactionType' => 'PhabricatorModularTransactionType',
'PhortunePaymentProvider' => 'Phobject',
'PhortunePaymentProviderConfig' => array(
'PhortuneDAO',
@ -11875,8 +11989,6 @@ phutil_register_library_map(array(
'PhortuneProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneProductViewController' => 'PhortuneController',
'PhortuneProviderActionController' => 'PhortuneController',
'PhortuneProviderDisableController' => 'PhortuneMerchantController',
'PhortuneProviderEditController' => 'PhortuneMerchantController',
'PhortunePurchase' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
@ -11888,17 +12000,23 @@ phutil_register_library_map(array(
'PhortuneSubscription' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
'PhabricatorPolicyCodexInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhortuneSubscriptionAutopayTransaction' => 'PhortuneSubscriptionTransactionType',
'PhortuneSubscriptionCart' => 'PhortuneCartImplementation',
'PhortuneSubscriptionEditController' => 'PhortuneController',
'PhortuneSubscriptionEditor' => 'PhabricatorApplicationTransactionEditor',
'PhortuneSubscriptionImplementation' => 'Phobject',
'PhortuneSubscriptionListController' => 'PhortuneController',
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
'PhortuneSubscriptionPolicyCodex' => 'PhabricatorPolicyCodex',
'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhortuneSubscriptionTableView' => 'AphrontView',
'PhortuneSubscriptionViewController' => 'PhortuneController',
'PhortuneSubscriptionTransaction' => 'PhabricatorModularTransaction',
'PhortuneSubscriptionTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneSubscriptionTransactionType' => 'PhabricatorModularTransactionType',
'PhortuneSubscriptionWorker' => 'PhabricatorWorker',
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',

View file

@ -312,11 +312,17 @@ final class AphrontApplicationConfiguration
if ($response_exception) {
// If we encountered an exception while building a normal response, then
// encountered another exception while building a response for the first
// exception, just throw the original exception. It is more likely to be
// useful and point at a root cause than the second exception we ran into
// while telling the user about it.
// exception, throw an aggregate exception that will be unpacked by the
// higher-level handler. This is above our pay grade.
if ($original_exception) {
throw $original_exception;
throw new PhutilAggregateException(
pht(
'Encountered a processing exception, then another exception when '.
'trying to build a response for the first exception.'),
array(
$response_exception,
$original_exception,
));
}
// If we built a response successfully and then ran into an exception

View file

@ -8,16 +8,19 @@ final class Aphront404Response extends AphrontHTMLResponse {
public function buildResponseString() {
$request = $this->getRequest();
$user = $request->getUser();
$viewer = $request->getViewer();
$dialog = id(new AphrontDialogView())
->setUser($user)
->setViewer($viewer)
->setTitle(pht('404 Not Found'))
->addCancelButton('/', pht('Focus'))
->addCancelButton('/', pht('Return to Charted Waters'))
->appendParagraph(
pht(
'Do not dwell in the past, do not dream of the future, '.
'concentrate the mind on the present moment.'));
'You arrive at your destination, but there is nothing here.'))
->appendParagraph(
pht(
'Perhaps the real treasure was the friends you made '.
'along the way.'));
$view = id(new PhabricatorStandardPageView())
->setTitle(pht('404 Not Found'))

View file

@ -61,9 +61,39 @@ final class AphrontUnhandledExceptionResponse
return 'unhandled-exception';
}
protected function getResponseBody() {
$ex = $this->exception;
private function getExceptionList() {
return $this->expandException($this->exception);
}
private function expandException($root) {
if ($root instanceof PhutilAggregateException) {
$list = array();
$list[] = $root;
foreach ($root->getExceptions() as $ex) {
foreach ($this->expandException($ex) as $child) {
$list[] = $child;
}
}
return $list;
}
return array($root);
}
protected function getResponseBody() {
$body = array();
foreach ($this->getExceptionList() as $ex) {
$body[] = $this->newHTMLMessage($ex);
}
return $body;
}
private function newHTMLMessage($ex) {
if ($ex instanceof AphrontMalformedRequestException) {
$title = $ex->getTitle();
} else {
@ -122,12 +152,20 @@ final class AphrontUnhandledExceptionResponse
}
protected function buildPlainTextResponseString() {
$ex = $this->exception;
$messages = array();
foreach ($this->getExceptionList() as $exception) {
$messages[] = $this->newPlainTextMessage($exception);
}
return implode("\n\n", $messages);
}
private function newPlainTextMessage($exception) {
return pht(
'%s: %s',
get_class($ex),
$ex->getMessage());
get_class($exception),
$exception->getMessage());
}
}

View file

@ -481,7 +481,7 @@ abstract class PhabricatorController extends AphrontController {
protected function buildTransactionTimeline(
PhabricatorApplicationTransactionInterface $object,
PhabricatorApplicationTransactionQuery $query,
PhabricatorApplicationTransactionQuery $query = null,
PhabricatorMarkupEngine $engine = null,
$view_data = array()) {
@ -489,6 +489,17 @@ abstract class PhabricatorController extends AphrontController {
$viewer = $this->getViewer();
$xaction = $object->getApplicationTransactionTemplate();
if (!$query) {
$query = PhabricatorApplicationTransactionQuery::newQueryForObject(
$object);
if (!$query) {
throw new Exception(
pht(
'Unable to find transaction query for object of class "%s".',
get_class($object)));
}
}
$pager = id(new AphrontCursorPagerView())
->readFromRequest($request)
->setURI(new PhutilURI(

View file

@ -119,8 +119,11 @@ final class DifferentialCreateDiffConduitAPIMethod
break;
}
$source_path = $request->getValue('sourcePath');
$source_path = $this->normalizeSourcePath($source_path);
$diff_data_dict = array(
'sourcePath' => $request->getValue('sourcePath'),
'sourcePath' => $source_path,
'sourceMachine' => $request->getValue('sourceMachine'),
'branch' => $request->getValue('branch'),
'creationMethod' => $request->getValue('creationMethod'),
@ -158,4 +161,18 @@ final class DifferentialCreateDiffConduitAPIMethod
);
}
private function normalizeSourcePath($source_path) {
// See T13385. This property is probably headed for deletion. Until we get
// there, stop errors arising from running "arc diff" in a working copy
// with too many characters.
$max_size = id(new DifferentialDiff())
->getColumnMaximumByteLength('sourcePath');
return id(new PhutilUTF8StringTruncator())
->setMaximumBytes($max_size)
->setTerminator('')
->truncateString($source_path);
}
}

View file

@ -51,6 +51,12 @@ final class DifferentialRevisionSearchEngine
$map['createdEnd']);
}
if ($map['modifiedStart'] || $map['modifiedEnd']) {
$query->withUpdatedEpochBetween(
$map['modifiedStart'],
$map['modifiedEnd']);
}
return $query;
}
@ -100,6 +106,18 @@ final class DifferentialRevisionSearchEngine
->setKey('createdEnd')
->setDescription(
pht('Find revisions created at or before a particular time.')),
id(new PhabricatorSearchDateField())
->setLabel(pht('Modified After'))
->setKey('modifiedStart')
->setIsHidden(true)
->setDescription(
pht('Find revisions modified at or after a particular time.')),
id(new PhabricatorSearchDateField())
->setLabel(pht('Modified Before'))
->setKey('modifiedEnd')
->setIsHidden(true)
->setDescription(
pht('Find revisions modified at or before a particular time.')),
);
}

View file

@ -112,11 +112,6 @@ final class DifferentialRevision extends DifferentialDAO
'repositoryPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'authorPHID' => array(
'columns' => array('authorPHID', 'status'),
),
@ -131,6 +126,9 @@ final class DifferentialRevision extends DifferentialDAO
'key_status' => array(
'columns' => array('status', 'phid'),
),
'key_modified' => array(
'columns' => array('dateModified'),
),
),
) + parent::getConfiguration();
}

View file

@ -0,0 +1,18 @@
<?php
final class DrydockResourceSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'drydock.resource.search';
}
public function newSearchEngine() {
return new DrydockResourceSearchEngine();
}
public function getMethodSummary() {
return pht('Retrieve information about Drydock resources.');
}
}

View file

@ -44,6 +44,10 @@ final class DrydockLeaseSearchEngine
$query->withOwnerPHIDs($map['ownerPHIDs']);
}
if ($map['resourcePHIDs']) {
$query->withResourcePHIDs($map['resourcePHIDs']);
}
return $query;
}
@ -58,6 +62,11 @@ final class DrydockLeaseSearchEngine
->setKey('ownerPHIDs')
->setAliases(array('owner', 'owners', 'ownerPHID'))
->setDescription(pht('Search leases by owner.')),
id(new PhabricatorPHIDsSearchField())
->setLabel(pht('Resources'))
->setKey('resourcePHIDs')
->setAliases(array('resorucePHID', 'resource', 'resources'))
->setDescription(pht('Search leases by resource.')),
);
}

View file

@ -100,46 +100,50 @@ final class DrydockResourceQuery extends DrydockQuery {
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
'resource.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
'resource.phid IN (%Ls)',
$this->phids);
}
if ($this->types !== null) {
$where[] = qsprintf(
$conn,
'type IN (%Ls)',
'resource.type IN (%Ls)',
$this->types);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'status IN (%Ls)',
'resource.status IN (%Ls)',
$this->statuses);
}
if ($this->blueprintPHIDs !== null) {
$where[] = qsprintf(
$conn,
'blueprintPHID IN (%Ls)',
'resource.blueprintPHID IN (%Ls)',
$this->blueprintPHIDs);
}
if ($this->datasourceQuery !== null) {
$where[] = qsprintf(
$conn,
'name LIKE %>',
'resource.name LIKE %>',
$this->datasourceQuery);
}
return $where;
}
protected function getPrimaryTableAlias() {
return 'resource';
}
}

View file

@ -40,6 +40,10 @@ final class DrydockResourceSearchEngine
$query->withStatuses($map['statuses']);
}
if ($map['blueprintPHIDs']) {
$query->withBlueprintPHIDs($map['blueprintPHIDs']);
}
return $query;
}
@ -49,6 +53,12 @@ final class DrydockResourceSearchEngine
->setLabel(pht('Statuses'))
->setKey('statuses')
->setOptions(DrydockResourceStatus::getStatusMap()),
id(new PhabricatorPHIDsSearchField())
->setLabel(pht('Blueprints'))
->setKey('blueprintPHIDs')
->setAliases(array('blueprintPHID', 'blueprints', 'blueprint'))
->setDescription(
pht('Search for resources generated by particular blueprints.')),
);
}

View file

@ -1,7 +1,9 @@
<?php
final class DrydockResource extends DrydockDAO
implements PhabricatorPolicyInterface {
implements
PhabricatorPolicyInterface,
PhabricatorConduitResultInterface {
protected $id;
protected $phid;
@ -340,4 +342,38 @@ final class DrydockResource extends DrydockDAO
public function describeAutomaticCapability($capability) {
return pht('Resources inherit the policies of their blueprints.');
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('blueprintPHID')
->setType('phid')
->setDescription(pht('The blueprint which generated this resource.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('status')
->setType('map<string, wild>')
->setDescription(pht('Information about resource status.')),
);
}
public function getFieldValuesForConduit() {
$status = $this->getStatus();
return array(
'blueprintPHID' => $this->getBlueprintPHID(),
'status' => array(
'value' => $status,
'name' => DrydockResourceStatus::getNameForStatus($status),
),
);
}
public function getConduitSearchAttachments() {
return array();
}
}

View file

@ -261,6 +261,7 @@ EODOCS
$parent_type = ManiphestTaskDependedOnByTaskEdgeType::EDGECONST;
$subtask_type = ManiphestTaskDependsOnTaskEdgeType::EDGECONST;
$commit_type = ManiphestTaskHasCommitEdgeType::EDGECONST;
$src_phid = $object->getPHID();
if ($src_phid) {
@ -270,6 +271,7 @@ EODOCS
array(
$parent_type,
$subtask_type,
$commit_type,
));
$edge_query->execute();
@ -280,9 +282,14 @@ EODOCS
$subtask_phids = $edge_query->getDestinationPHIDs(
array($src_phid),
array($subtask_type));
$commit_phids = $edge_query->getDestinationPHIDs(
array($src_phid),
array($commit_type));
} else {
$parent_phids = array();
$subtask_phids = array();
$commit_phids = array();
}
$fields[] = id(new PhabricatorHandlesEditField())
@ -307,7 +314,19 @@ EODOCS
->setIsFormField(false)
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $subtask_type)
->setValue($parent_phids);
->setValue($subtask_phids);
$fields[] = id(new PhabricatorHandlesEditField())
->setKey('commits')
->setLabel(pht('Commits'))
->setDescription(pht('Related commits.'))
->setConduitDescription(pht('Change the related commits for this task.'))
->setConduitTypeDescription(pht('List of related commit PHIDs.'))
->setUseEdgeTransactions(true)
->setIsFormField(false)
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $commit_type)
->setValue($commit_phids);
return $fields;
}

View file

@ -17,14 +17,8 @@ final class PhabricatorPeopleEmpowerController
$done_uri = $this->getApplicationURI("manage/{$id}/");
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$done_uri);
$validation_exception = null;
if ($request->isFormPost()) {
if ($request->isFormOrHisecPost()) {
$xactions = array();
$xactions[] = id(new PhabricatorUserTransaction())
->setTransactionType(
@ -34,7 +28,8 @@ final class PhabricatorPeopleEmpowerController
$editor = id(new PhabricatorUserTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true);
->setContinueOnMissingFields(true)
->setCancelURI($done_uri);
try {
$editor->applyTransactions($user, $xactions);

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorPeopleManagementEmpowerWorkflow
extends PhabricatorPeopleManagementWorkflow {
protected function didConstruct() {
$arguments = array_merge(
$this->getUserSelectionArguments(),
array());
$this
->setName('empower')
->setExamples('**empower** --user __username__')
->setSynopsis(pht('Turn a user account into an administrator account.'))
->setArguments($arguments);
}
public function execute(PhutilArgumentParser $args) {
$user = $this->selectUser($args);
$display_name = $user->getUsername();
if ($user->getIsAdmin()) {
throw new PhutilArgumentUsageException(
pht(
'User account "%s" is already an administrator. You can only '.
'empower accounts that are not yet administrators.',
$display_name));
}
$xactions = array();
$xactions[] = $user->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE)
->setNewValue(true);
$this->applyTransactions($user, $xactions);
$this->logOkay(
pht('DONE'),
pht('Empowered user account "%s".', $display_name));
return 0;
}
}

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorPeopleManagementEnableWorkflow
extends PhabricatorPeopleManagementWorkflow {
protected function didConstruct() {
$arguments = array_merge(
$this->getUserSelectionArguments(),
array());
$this
->setName('enable')
->setExamples('**enable** --user __username__')
->setSynopsis(pht('Enable a disabled user account.'))
->setArguments($arguments);
}
public function execute(PhutilArgumentParser $args) {
$user = $this->selectUser($args);
$display_name = $user->getUsername();
if (!$user->getIsDisabled()) {
throw new PhutilArgumentUsageException(
pht(
'User account "%s" is not disabled. You can only enable accounts '.
'that are disabled.',
$display_name));
}
$xactions = array();
$xactions[] = $user->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorUserDisableTransaction::TRANSACTIONTYPE)
->setNewValue(false);
$this->applyTransactions($user, $xactions);
$this->logOkay(
pht('DONE'),
pht('Enabled user account "%s".', $display_name));
return 0;
}
}

View file

@ -3,45 +3,55 @@
abstract class PhabricatorPeopleManagementWorkflow
extends PhabricatorManagementWorkflow {
protected function buildIterator(PhutilArgumentParser $args) {
$usernames = $args->getArg('users');
final protected function getUserSelectionArguments() {
return array(
array(
'name' => 'user',
'param' => 'username',
'help' => pht('User account to act on.'),
),
);
}
if ($args->getArg('all')) {
if ($usernames) {
final protected function selectUser(PhutilArgumentParser $argv) {
$username = $argv->getArg('user');
if (!strlen($username)) {
throw new PhutilArgumentUsageException(
pht(
'Specify either a list of users or `%s`, but not both.',
'--all'));
}
return new LiskMigrationIterator(new PhabricatorUser());
'Select a user account to act on with "--user <username>".'));
}
if ($usernames) {
return $this->loadUsersWithUsernames($usernames);
}
return null;
}
protected function loadUsersWithUsernames(array $usernames) {
$users = array();
foreach($usernames as $username) {
$query = id(new PhabricatorPeopleQuery())
$user = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withUsernames(array($username))
->executeOne();
if (!$query) {
if (!$user) {
throw new PhutilArgumentUsageException(
pht(
'"%s" is not a valid username.',
'No user with username "%s" exists.',
$username));
}
$users[] = $query;
return $user;
}
return $users;
}
final protected function applyTransactions(
PhabricatorUser $user,
array $xactions) {
assert_instances_of($xactions, 'PhabricatorUserTransaction');
$viewer = $this->getViewer();
$application = id(new PhabricatorPeopleApplication())->getPHID();
$content_source = $this->newContentSource();
$editor = $user->getApplicationTransactionEditor()
->setActor($viewer)
->setActingAsPHID($application)
->setContentSource($content_source)
->setContinueOnMissingFields(true);
return $editor->applyTransactions($user, $xactions);
}
}

View file

@ -1,85 +0,0 @@
<?php
final class PhabricatorPeopleProfileImageWorkflow
extends PhabricatorPeopleManagementWorkflow {
protected function didConstruct() {
$this
->setName('profileimage')
->setExamples('**profileimage** --users __username__')
->setSynopsis(pht('Generate default profile images.'))
->setArguments(
array(
array(
'name' => 'all',
'help' => pht(
'Generate default profile images for all users.'),
),
array(
'name' => 'force',
'short' => 'f',
'help' => pht(
'Force a default profile image to be replaced.'),
),
array(
'name' => 'users',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$is_force = $args->getArg('force');
$is_all = $args->getArg('all');
$gd = function_exists('imagecreatefromstring');
if (!$gd) {
throw new PhutilArgumentUsageException(
pht(
'GD is not installed for php-cli. Aborting.'));
}
$iterator = $this->buildIterator($args);
if (!$iterator) {
throw new PhutilArgumentUsageException(
pht(
'Either specify a list of users to update, or use `%s` '.
'to update all users.',
'--all'));
}
$version = PhabricatorFilesComposeAvatarBuiltinFile::VERSION;
$generator = new PhabricatorFilesComposeAvatarBuiltinFile();
foreach ($iterator as $user) {
$username = $user->getUsername();
$default_phid = $user->getDefaultProfileImagePHID();
$gen_version = $user->getDefaultProfileImageVersion();
$generate = false;
if ($gen_version != $version) {
$generate = true;
}
if ($default_phid == null || $is_force || $generate) {
$console->writeOut(
"%s\n",
pht(
'Generating profile image for "%s".',
$username));
$generator->updateUser($user);
} else {
$console->writeOut(
"%s\n",
pht(
'Default profile image "%s" already set for "%s".',
$version,
$username));
}
}
}
}

View file

@ -86,4 +86,11 @@ final class PhabricatorUserEmpowerTransaction
return null;
}
public function shouldTryMFA(
$object,
PhabricatorApplicationTransaction $xaction) {
return true;
}
}

View file

@ -34,26 +34,6 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
return array(
'/phortune/' => array(
'' => 'PhortuneLandingController',
'(?P<accountID>\d+)/' => array(
'' => 'PhortuneAccountViewController',
'card/' => array(
'new/' => 'PhortunePaymentMethodCreateController',
),
'order/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneCartListController',
'subscription/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneSubscriptionListController',
'view/(?P<id>\d+)/'
=> 'PhortuneSubscriptionViewController',
'edit/(?P<id>\d+)/'
=> 'PhortuneSubscriptionEditController',
'order/(?P<subscriptionID>\d+)/'
=> 'PhortuneCartListController',
),
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneAccountChargeListController',
),
'card/(?P<id>\d+)/' => array(
'edit/' => 'PhortunePaymentMethodEditController',
'disable/' => 'PhortunePaymentMethodDisableController',
@ -63,21 +43,59 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
'checkout/' => 'PhortuneCartCheckoutController',
'(?P<action>print)/' => 'PhortuneCartViewController',
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
'accept/' => 'PhortuneCartAcceptController',
'void/' => 'PhortuneCartVoidController',
'update/' => 'PhortuneCartUpdateController',
),
'account/' => array(
'' => 'PhortuneAccountListController',
$this->getEditRoutePattern('edit/')
=> 'PhortuneAccountEditController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneAccountEditController',
'add/manager/(?:(?P<id>\d+)/)?'
=> 'PhortuneAccountAddManagerController',
'billing/(?:(?P<id>\d+)/)?' => 'PhortuneAccountBillingController',
'subscription/(?:(?P<id>\d+)/)?'
=> 'PhortuneAccountSubscriptionController',
'manager/' => array(
'(?:(?P<id>\d+)/)?' => 'PhortuneAccountManagerController',
'add/(?:(?P<id>\d+)/)?' => 'PhortuneAccountAddManagerController',
'(?P<accountID>\d+)/' => array(
'' => 'PhortuneAccountOverviewController',
'details/' => 'PhortuneAccountDetailsController',
'methods/' => array(
'' => 'PhortuneAccountPaymentMethodController',
'(?P<id>\d+)/' => 'PhortuneAccountPaymentMethodViewController',
'new/' => 'PhortunePaymentMethodCreateController',
),
'orders/' => array(
'' => 'PhortuneAccountOrdersController',
$this->getQueryRoutePattern('list/')
=> 'PhortuneAccountOrderListController',
),
'charges/' => array(
'' => 'PhortuneAccountChargesController',
$this->getQueryRoutePattern('list/')
=> 'PhortuneAccountChargeListController',
),
'subscriptions/' => array(
'' => 'PhortuneAccountSubscriptionController',
'(?P<subscriptionID>\d+)/' => array(
'' => 'PhortuneAccountSubscriptionViewController',
'autopay/(?P<methodID>\d+)/'
=> 'PhortuneAccountSubscriptionAutopayController',
$this->getQueryRoutePattern('orders/')
=> 'PhortuneAccountOrderListController',
),
),
'managers/' => array(
'' => 'PhortuneAccountManagersController',
'add/' => 'PhortuneAccountAddManagerController',
),
'addresses/' => array(
'' => 'PhortuneAccountEmailAddressesController',
'(?P<addressID>\d+)/' => array(
'' => 'PhortuneAccountEmailViewController',
'rotate/' => 'PhortuneAccountEmailRotateController',
'(?P<action>disable|enable)/'
=> 'PhortuneAccountEmailStatusController',
),
$this->getEditRoutePattern('edit/')
=> 'PhortuneAccountEmailEditController',
),
),
),
'product/' => array(
@ -91,37 +109,51 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
'(?P<id>\d+)/(?P<action>[^/]+)/'
=> 'PhortuneProviderActionController',
),
'external/(?P<addressKey>[^/]+)/(?P<accessKey>[^/]+)/' => array(
'' => 'PhortuneExternalOverviewController',
'unsubscribe/' => 'PhortuneExternalUnsubscribeController',
'order/' => array(
'(?P<orderID>[^/]+)/' => array(
'' => 'PhortuneExternalOrderController',
'(?P<action>print)/' => 'PhortuneExternalOrderController',
),
),
),
'merchant/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
'picture/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantPictureController',
$this->getQueryRoutePattern()
=> 'PhortuneMerchantListController',
$this->getEditRoutePattern('edit/')
=> 'PhortuneMerchantEditController',
'orders/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneCartListController',
'manager/' => array(
'(?:(?P<id>\d+)/)?' => 'PhortuneMerchantManagerController',
'add/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantAddManagerController',
),
'(?P<merchantID>\d+)/' => array(
'cart/(?P<id>\d+)/' => array(
'' => 'PhortuneCartViewController',
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
'update/' => 'PhortuneCartUpdateController',
'accept/' => 'PhortuneCartAcceptController',
'' => 'PhortuneMerchantOverviewController',
'details/' => 'PhortuneMerchantDetailsController',
'providers/' => array(
'' => 'PhortuneMerchantProvidersController',
'(?P<providerID>\d+)/' => array(
'' => 'PhortuneMerchantProviderViewController',
'disable/' => 'PhortuneMerchantProviderDisableController',
),
'subscription/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneSubscriptionListController',
'view/(?P<id>\d+)/'
=> 'PhortuneSubscriptionViewController',
'order/(?P<subscriptionID>\d+)/'
=> 'PhortuneCartListController',
$this->getEditRoutePattern('edit/')
=> 'PhortuneMerchantProviderEditController',
),
'invoice/' => array(
'new/' => 'PhortuneMerchantInvoiceCreateController',
'orders/' => array(
'' => 'PhortuneMerchantOrdersController',
$this->getQueryRoutePattern('list/')
=> 'PhortuneMerchantOrderListController',
),
'picture/' => array(
'edit/' => 'PhortuneMerchantPictureController',
),
'subscriptions/' => array(
'' => 'PhortuneMerchantSubscriptionsController',
$this->getQueryRoutePattern('list/')
=> 'PhortuneMerchantSubscriptionListController',
),
'managers/' => array(
'' => 'PhortuneMerchantManagersController',
'new/' => 'PhortuneMerchantAddManagerController',
),
),
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
),
),
);

View file

@ -0,0 +1,36 @@
<?php
final class PhortunePaymentMethodPolicyCodex
extends PhabricatorPolicyCodex {
public function getPolicySpecialRuleDescriptions() {
$object = $this->getObject();
$rules = array();
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setIsActive(true)
->setDescription(
pht(
'Account members may view and edit payment methods.'));
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->setIsActive(true)
->setDescription(
pht(
'Merchants you have a relationship with may view associated '.
'payment methods.'));
return $rules;
}
}

View file

@ -0,0 +1,36 @@
<?php
final class PhortuneSubscriptionPolicyCodex
extends PhabricatorPolicyCodex {
public function getPolicySpecialRuleDescriptions() {
$object = $this->getObject();
$rules = array();
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setIsActive(true)
->setDescription(
pht(
'Account members may view and edit subscriptions.'));
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
))
->setIsActive(true)
->setDescription(
pht(
'Merchants you have a relationship with may view associated '.
'subscriptions.'));
return $rules;
}
}

View file

@ -0,0 +1,31 @@
<?php
final class PhortuneAccountEmailStatus
extends Phobject {
const STATUS_ACTIVE = 'active';
const STATUS_DISABLED = 'disabled';
const STATUS_UNSUBSCRIBED = 'unsubscribed';
public static function getDefaultStatusConstant() {
return self::STATUS_ACTIVE;
}
private static function getMap() {
return array(
self::STATUS_ACTIVE => array(
'name' => pht('Active'),
'closed' => false,
),
self::STATUS_DISABLED => array(
'name' => pht('Disabled'),
'closed' => true,
),
self::STATUS_UNSUBSCRIBED => array(
'name' => pht('Unsubscribed'),
'closed' => true,
),
);
}
}

View file

@ -2,42 +2,6 @@
abstract class PhortuneController extends PhabricatorController {
protected function addAccountCrumb(
$crumbs,
PhortuneAccount $account,
$link = true) {
$name = $account->getName();
$href = null;
if ($link) {
$href = $this->getApplicationURI($account->getID().'/');
$crumbs->addTextCrumb($name, $href);
} else {
$crumbs->addTextCrumb($name);
}
}
protected function addMerchantCrumb(
$crumbs,
PhortuneMerchant $merchant,
$link = true) {
$name = $merchant->getName();
$href = null;
$crumbs->addTextCrumb(
pht('Merchants'),
$this->getApplicationURI('merchant/'));
if ($link) {
$href = $this->getApplicationURI('merchant/'.$merchant->getID().'/');
$crumbs->addTextCrumb($name, $href);
} else {
$crumbs->addTextCrumb($name);
}
}
private function loadEnabledProvidersForMerchant(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
@ -84,30 +48,4 @@ abstract class PhortuneController extends PhabricatorController {
return $providers;
}
protected function loadMerchantAuthority() {
$request = $this->getRequest();
$viewer = $this->getViewer();
$is_merchant = (bool)$request->getURIData('merchantID');
if (!$is_merchant) {
return null;
}
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('merchantID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return null;
}
$viewer->grantAuthority($merchant);
return $merchant;
}
}

View file

@ -11,7 +11,7 @@ final class PhortuneLandingController extends PhortuneController {
if (count($accounts) == 1) {
$account = head($accounts);
$next_uri = $this->getApplicationURI($account->getID().'/');
$next_uri = $account->getURI();
} else {
$next_uri = $this->getApplicationURI('account/');
}

View file

@ -1,27 +1,21 @@
<?php
final class PhortuneAccountAddManagerController extends PhortuneController {
final class PhortuneAccountAddManagerController
extends PhortuneAccountController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$account = $this->getAccount();
$id = $account->getID();
$v_managers = array();
$e_managers = null;
$account_uri = $this->getApplicationURI("/account/manager/{$id}/");
$account_uri = $this->getApplicationURI("/account/{$id}/managers/");
if ($request->isFormPost()) {
$xactions = array();
@ -53,23 +47,34 @@ final class PhortuneAccountAddManagerController extends PhortuneController {
}
}
$account_phid = $account->getPHID();
$handles = $viewer->loadHandles(array($account_phid));
$handle = $handles[$account_phid];
$form = id(new AphrontFormView())
->setUser($viewer)
->setViewer($viewer)
->appendInstructions(
pht(
'Choose one or more users to add as account managers. Managers '.
'have full control of the account.'))
->appendControl(
id(new AphrontFormStaticControl())
->setLabel(pht('Payment Account'))
->setValue($handle->renderLink()))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Managers'))
->setLabel(pht('Add Managers'))
->setName('managerPHIDs')
->setValue($v_managers)
->setError($e_managers));
return $this->newDialog()
->setTitle(pht('Add New Manager'))
->setTitle(pht('Add New Managers'))
->appendForm($form)
->setWidth(AphrontDialogView::WIDTH_FORM)
->addCancelButton($account_uri)
->addSubmitButton(pht('Add Manager'));
->addSubmitButton(pht('Add Managers'));
}
}

View file

@ -1,161 +0,0 @@
<?php
final class PhortuneAccountBillingController
extends PhortuneAccountProfileController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadAccount();
if ($response) {
return $response;
}
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Billing'));
$header = $this->buildHeaderView();
$methods = $this->buildPaymentMethodsSection($account);
$charge_history = $this->buildChargeHistorySection($account);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$methods,
$charge_history,
));
$navigation = $this->buildSideNavView('billing');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildPaymentMethodsSection(PhortuneAccount $account) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $account->getID();
// TODO: Allow adding a card here directly
$add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('New Payment Method'))
->setIcon('fa-plus')
->setHref($this->getApplicationURI("{$id}/card/new/"));
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Methods'));
$list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setFlush(true)
->setNoDataString(
pht('No payment methods associated with this account.'));
$methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->execute();
foreach ($methods as $method) {
$id = $method->getID();
$item = new PHUIObjectItemView();
$item->setHeader($method->getFullDisplayName());
switch ($method->getStatus()) {
case PhortunePaymentMethod::STATUS_ACTIVE:
$item->setStatusIcon('fa-check green');
$disable_uri = $this->getApplicationURI('card/'.$id.'/disable/');
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setHref($disable_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
break;
case PhortunePaymentMethod::STATUS_DISABLED:
$item->setStatusIcon('fa-ban lightbluetext');
$item->setDisabled(true);
break;
}
$provider = $method->buildPaymentProvider();
$item->addAttribute($provider->getPaymentMethodProviderDescription());
$edit_uri = $this->getApplicationURI('card/'.$id.'/edit/');
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$list->addItem($item);
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
}
private function buildChargeHistorySection(PhortuneAccount $account) {
$viewer = $this->getViewer();
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needCarts(true)
->setLimit(10)
->execute();
$phids = array();
foreach ($charges as $charge) {
$phids[] = $charge->getProviderPHID();
$phids[] = $charge->getCartPHID();
$phids[] = $charge->getMerchantPHID();
$phids[] = $charge->getPaymentMethodPHID();
}
$handles = $this->loadViewerHandles($phids);
$charges_uri = $this->getApplicationURI($account->getID().'/charge/');
$table = id(new PhortuneChargeTableView())
->setUser($viewer)
->setCharges($charges)
->setHandles($handles);
$header = id(new PHUIHeaderView())
->setHeader(pht('Charge History'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($charges_uri)
->setText(pht('View All Charges')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -1,71 +1,32 @@
<?php
final class PhortuneAccountChargeListController
extends PhortuneController {
extends PhortuneAccountProfileController {
private $account;
protected function shouldRequireAccountEditCapability() {
return false;
}
public function handleRequest(AphrontRequest $request) {
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$querykey = $request->getURIData('queryKey');
$account_id = $request->getURIData('accountID');
$account = $this->getAccount();
$engine = new PhortuneChargeSearchEngine();
if ($account_id) {
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($account_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$this->account = $account;
$engine->setAccount($account);
} else {
return new Aphront404Response();
}
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($querykey)
->setSearchEngine($engine)
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView() {
$viewer = $this->getViewer();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new PhortuneChargeSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
return id(new PhortuneChargeSearchEngine())
->setAccount($account)
->setController($this)
->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$account = $this->account;
if ($account) {
if ($this->hasAccount()) {
$account = $this->getAccount();
$id = $account->getID();
$crumbs->addTextCrumb(
$account->getName(),
$this->getApplicationURI("{$id}/"));
$crumbs->addTextCrumb(
pht('Charges'),
$this->getApplicationURI("{$id}/charge/"));
$account->getChargesURI());
}
return $crumbs;

View file

@ -0,0 +1,70 @@
<?php
final class PhortuneAccountChargesController
extends PhortuneAccountProfileController {
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Orders'))
->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$charge_history = $this->buildChargeHistorySection($account);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$authority,
$charge_history,
));
$navigation = $this->buildSideNavView('charges');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildChargeHistorySection(PhortuneAccount $account) {
$viewer = $this->getViewer();
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needCarts(true)
->setLimit(100)
->execute();
$charges_uri = $account->getChargeListURI();
$table = id(new PhortuneChargeTableView())
->setUser($viewer)
->setCharges($charges);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Charges'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($charges_uri)
->setText(pht('View All Charges')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -4,61 +4,159 @@ abstract class PhortuneAccountController
extends PhortuneController {
private $account;
private $merchants;
final public function handleRequest(AphrontRequest $request) {
if ($this->shouldRequireAccountEditCapability()) {
$response = $this->loadAccountForEdit();
} else {
$response = $this->loadAccountForView();
}
if ($response) {
return $response;
}
return $this->handleAccountRequest($request);
}
abstract protected function shouldRequireAccountEditCapability();
abstract protected function handleAccountRequest(AphrontRequest $request);
final protected function hasAccount() {
return (bool)$this->account;
}
final protected function getAccount() {
if ($this->account === null) {
throw new Exception(
pht(
'Unable to "getAccount()" before loading or setting account '.
'context.'));
}
protected function getAccount() {
return $this->account;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
// If we hit a policy exception, we can make it here without finding
// an account.
if ($this->hasAccount()) {
$account = $this->getAccount();
if ($account) {
$crumbs->addTextCrumb($account->getName(), $account->getURI());
}
return $crumbs;
}
protected function loadAccount() {
// TODO: Currently, you must be able to edit an account to view the detail
// page, because the account must be broadly visible so merchants can
// process orders but merchants should not be able to see all the details
// of an account. Ideally the profile pages should be visible to merchants,
// too, just with less information.
return $this->loadAccountForEdit();
private function loadAccountForEdit() {
return $this->loadAccountWithCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
));
}
private function loadAccountForView() {
return $this->loadAccountWithCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
));
}
protected function loadAccountForEdit() {
private function loadAccountWithCapabilities(array $capabilities) {
$viewer = $this->getViewer();
$request = $this->getRequest();
$account_id = $request->getURIData('accountID');
if (!$account_id) {
$account_id = $request->getURIData('id');
}
if (!$account_id) {
return new Aphront404Response();
throw new Exception(
pht(
'Controller ("%s") extends controller "%s", but is reachable '.
'with no "accountID" in URI.',
get_class($this),
__CLASS__));
}
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($account_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->requireCapabilities($capabilities)
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$this->account = $account;
$this->setAccount($account);
return null;
}
private function setAccount(PhortuneAccount $account) {
$this->account = $account;
$viewer = $this->getViewer();
if (!$account->isUserAccountMember($viewer)) {
$merchant_phids = $account->getMerchantPHIDs();
$merchants = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withPHIDs($merchant_phids)
->withMemberPHIDs(array($viewer->getPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$this->merchants = $merchants;
} else {
$this->merchants = array();
}
return $this;
}
final protected function getMerchants() {
if ($this->merchants === null) {
throw new Exception(
pht(
'Unable to "getMerchants()" before loading or setting account '.
'context.'));
}
return $this->merchants;
}
final protected function newAccountAuthorityView() {
$viewer = $this->getViewer();
$merchants = $this->getMerchants();
if (!$merchants) {
return null;
}
$merchant_phids = mpull($merchants, 'getPHID');
$merchant_handles = $viewer->loadHandles($merchant_phids);
$merchant_handles = iterator_to_array($merchant_handles);
$merchant_list = mpull($merchant_handles, 'renderLink');
$merchant_list = phutil_implode_html(', ', $merchant_list);
$merchant_message = pht(
'You can view this account because you control %d merchant(s) it '.
'has a relationship with: %s.',
phutil_count($merchants),
$merchant_list);
return id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors(
array(
$merchant_message,
));
}
}

View file

@ -0,0 +1,155 @@
<?php
final class PhortuneAccountDetailsController
extends PhortuneAccountProfileController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$details = $this->newDetailsView($account);
$curtain = $this->buildCurtainView($account);
$timeline = $this->buildTransactionTimeline(
$account,
new PhortuneAccountTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$authority,
$details,
$timeline,
));
$navigation = $this->buildSideNavView('details');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildCurtainView(PhortuneAccount $account) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI('account/edit/'.$account->getID().'/');
$curtain = $this->newCurtainView($account);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Account'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$member_phids = $account->getMemberPHIDs();
$handles = $viewer->loadHandles($member_phids);
$member_list = id(new PHUIObjectItemListView())
->setSimple(true);
foreach ($member_phids as $member_phid) {
$image_uri = $handles[$member_phid]->getImageURI();
$image_href = $handles[$member_phid]->getURI();
$person = $handles[$member_phid];
$member = id(new PHUIObjectItemView())
->setImageURI($image_uri)
->setHref($image_href)
->setHeader($person->getFullName());
$member_list->addItem($member);
}
$curtain->newPanel()
->setHeaderText(pht('Managers'))
->appendChild($member_list);
$merchant_list = id(new PHUIObjectItemListView())
->setSimple(true)
->setNoDataString(pht('No purchase history.'));
$merchant_phids = $account->getMerchantPHIDs();
$handles = $viewer->loadHandles($merchant_phids);
foreach ($merchant_phids as $merchant_phid) {
$handle = $handles[$merchant_phid];
$merchant = id(new PHUIObjectItemView())
->setImageURI($handle->getImageURI())
->setHref($handle->getURI())
->setHeader($handle->getFullName());
$merchant_list->addItem($merchant);
}
$curtain->newPanel()
->setHeaderText(pht('Merchants'))
->appendChild($merchant_list);
return $curtain;
}
private function newDetailsView(PhortuneAccount $account) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$view->addProperty(pht('Account Name'), $account->getName());
$display_name = $account->getBillingName();
if (!strlen($display_name)) {
$display_name = phutil_tag('em', array(), pht('None'));
}
$display_address = $account->getBillingAddress();
if (!strlen($display_address)) {
$display_address = phutil_tag('em', array(), pht('None'));
} else {
$display_address = phutil_escape_html_newlines($display_address);
}
$view->addProperty(pht('Billing Name'), $display_name);
$view->addProperty(pht('Billing Address'), $display_address);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Account Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($view);
}
}

View file

@ -0,0 +1,92 @@
<?php
final class PhortuneAccountEmailAddressesController
extends PhortuneAccountProfileController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Email Addresses'))
->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$addresses = $this->buildAddressesSection($account);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$authority,
$addresses,
));
$navigation = $this->buildSideNavView('addresses');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildAddressesSection(PhortuneAccount $account) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $account->getID();
$add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Add Address'))
->setIcon('fa-plus')
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit)
->setHref("/phortune/account/{$id}/addresses/edit/");
$header = id(new PHUIHeaderView())
->setHeader(pht('Billing Email Addresses'))
->addActionLink($add);
$addresses = id(new PhortuneAccountEmailQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->execute();
$list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(
pht(
'There are no billing email addresses associated '.
'with this account.'));
$addresses = id(new PhortuneAccountEmailQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->execute();
foreach ($addresses as $address) {
$item = id(new PHUIObjectItemView())
->setObjectName($address->getObjectName())
->setHeader($address->getAddress())
->setHref($address->getURI());
$list->addItem($item);
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
}
}

View file

@ -0,0 +1,22 @@
<?php
final class PhortuneAccountEmailEditController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$engine = id(new PhortuneAccountEmailEditEngine())
->setController($this);
if (!$request->getURIData('id')) {
$engine->setAccount($account);
}
return $engine->buildResponse();
}
}

View file

@ -0,0 +1,62 @@
<?php
final class PhortuneAccountEmailRotateController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$account = $this->getAccount();
$address = id(new PhortuneAccountEmailQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($request->getURIData('addressID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$address) {
return new Aphront404Response();
}
$address_uri = $address->getURI();
if ($request->isFormOrHisecPost()) {
$xactions = array();
$xactions[] = $address->getApplicationTransactionTemplate()
->setTransactionType(
PhortuneAccountEmailRotateTransaction::TRANSACTIONTYPE)
->setNewValue(true);
$address->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->setCancelURI($address_uri)
->applyTransactions($address, $xactions);
return id(new AphrontRedirectResponse())->setURI($address_uri);
}
return $this->newDialog()
->setTitle(pht('Rotate Access Key'))
->appendParagraph(
pht(
'Rotate the access key for email address %s?',
phutil_tag('strong', array(), $address->getAddress())))
->appendParagraph(
pht(
'Existing access links which have been sent to this email address '.
'will stop working.'))
->addSubmitButton(pht('Rotate Access Key'))
->addCancelButton($address_uri);
}
}

View file

@ -0,0 +1,137 @@
<?php
final class PhortuneAccountEmailStatusController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$account = $this->getAccount();
$address = id(new PhortuneAccountEmailQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($request->getURIData('addressID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$address) {
return new Aphront404Response();
}
$address_uri = $address->getURI();
$is_enable = false;
$is_disable = false;
$old_status = $address->getStatus();
switch ($request->getURIData('action')) {
case 'enable':
if ($old_status === PhortuneAccountEmailStatus::STATUS_ACTIVE) {
return $this->newDialog()
->setTitle(pht('Already Enabled'))
->appendParagraph(
pht(
'You can not enable this address because it is already '.
'active.'))
->addCancelButton($address_uri);
}
if ($old_status === PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED) {
return $this->newDialog()
->setTitle(pht('Permanently Unsubscribed'))
->appendParagraph(
pht(
'You can not enable this address because it has been '.
'permanently unsubscribed.'))
->addCancelButton($address_uri);
}
$new_status = PhortuneAccountEmailStatus::STATUS_ACTIVE;
$is_enable = true;
break;
case 'disable':
if ($old_status === PhortuneAccountEmailStatus::STATUS_DISABLED) {
return $this->newDialog()
->setTitle(pht('Already Disabled'))
->appendParagraph(
pht(
'You can not disabled this address because it is already '.
'disabled.'))
->addCancelButton($address_uri);
}
if ($old_status === PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED) {
return $this->newDialog()
->setTitle(pht('Permanently Unsubscribed'))
->appendParagraph(
pht(
'You can not disable this address because it has been '.
'permanently unsubscribed.'))
->addCancelButton($address_uri);
}
$new_status = PhortuneAccountEmailStatus::STATUS_DISABLED;
$is_disable = true;
break;
default:
return new Aphront404Response();
}
if ($request->isFormOrHisecPost()) {
$xactions = array();
$xactions[] = $address->getApplicationTransactionTemplate()
->setTransactionType(
PhortuneAccountEmailStatusTransaction::TRANSACTIONTYPE)
->setNewValue($new_status);
$address->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->setCancelURI($address_uri)
->applyTransactions($address, $xactions);
return id(new AphrontRedirectResponse())->setURI($address_uri);
}
$dialog = $this->newDialog();
$body = array();
if ($is_disable) {
$title = pht('Disable Address');
$body[] = pht(
'This address will no longer receive email, and access links will '.
'no longer function.');
$submit = pht('Disable Address');
} else {
$title = pht('Enable Address');
$body[] = pht(
'This address will receive email again, and existing links '.
'to access order history will work again.');
$submit = pht('Enable Address');
}
foreach ($body as $graph) {
$dialog->appendParagraph($graph);
}
return $dialog
->setTitle($title)
->addCancelButton($address_uri)
->addSubmitButton($submit);
}
}

View file

@ -0,0 +1,176 @@
<?php
final class PhortuneAccountEmailViewController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$account = $this->getAccount();
$address = id(new PhortuneAccountEmailQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($request->getURIData('addressID')))
->executeOne();
if (!$address) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Email Addresses'), $account->getEmailAddressesURI())
->addTextCrumb($address->getObjectName())
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Account Email: %s', $address->getAddress()));
$details = $this->newDetailsView($address);
$timeline = $this->buildTransactionTimeline(
$address,
new PhortuneAccountEmailTransactionQuery());
$timeline->setShouldTerminate(true);
$curtain = $this->buildCurtainView($address);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$details,
$timeline,
));
return $this->newPage()
->setTitle($address->getObjectName())
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtainView(PhortuneAccountEmail $address) {
$viewer = $this->getViewer();
$account = $address->getAccount();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$address,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI(
urisprintf(
'account/%d/addresses/edit/%d/',
$account->getID(),
$address->getID()));
if ($can_edit) {
$external_uri = $address->getExternalURI();
} else {
$external_uri = null;
}
$curtain = $this->newCurtainView($account);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Address'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
switch ($address->getStatus()) {
case PhortuneAccountEmailStatus::STATUS_ACTIVE:
$disable_name = pht('Disable Address');
$disable_icon = 'fa-times';
$can_disable = true;
$disable_action = 'disable';
break;
case PhortuneAccountEmailStatus::STATUS_DISABLED:
$disable_name = pht('Enable Address');
$disable_icon = 'fa-check';
$can_disable = true;
$disable_action = 'enable';
break;
case PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED:
$disable_name = pht('Disable Address');
$disable_icon = 'fa-times';
$can_disable = false;
$disable_action = 'disable';
break;
}
$disable_uri = $this->getApplicationURI(
urisprintf(
'account/%d/addresses/%d/%s/',
$account->getID(),
$address->getID(),
$disable_action));
$curtain->addAction(
id(new PhabricatorActionView())
->setName($disable_name)
->setIcon($disable_icon)
->setHref($disable_uri)
->setDisabled(!$can_disable)
->setWorkflow(true));
$rotate_uri = $this->getApplicationURI(
urisprintf(
'account/%d/addresses/%d/rotate/',
$account->getID(),
$address->getID()));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Rotate Access Key'))
->setIcon('fa-refresh')
->setHref($rotate_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Show External View'))
->setIcon('fa-eye')
->setHref($external_uri)
->setDisabled(!$can_edit)
->setOpenInNewWindow(true));
return $curtain;
}
private function newDetailsView(PhortuneAccountEmail $address) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$access_key = $address->getAccessKey();
// This is not a meaningful security barrier: the full plaintext of the
// access key is visible on the page in the link target of the "Show
// External View" action. It's just here to make it clear "Rotate Access
// Key" actually does something.
$prefix_length = 4;
$visible_part = substr($access_key, 0, $prefix_length);
$masked_part = str_repeat(
"\xE2\x80\xA2",
strlen($access_key) - $prefix_length);
$access_display = $visible_part.$masked_part;
$access_display = phutil_tag('tt', array(), $access_display);
$view->addProperty(pht('Email Address'), $address->getAddress());
$view->addProperty(pht('Access Key'), $access_display);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Email Address Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($view);
}
}

View file

@ -38,7 +38,7 @@ final class PhortuneAccountListController extends PhortuneController {
$item = id(new PHUIObjectItemView())
->setSubhead(pht('Account %d', $account->getID()))
->setHeader($account->getName())
->setHref($this->getApplicationURI($account->getID().'/'))
->setHref($account->getURI())
->setObject($account)
->setImageIcon('fa-user-circle');

View file

@ -1,26 +1,29 @@
<?php
final class PhortuneAccountManagerController
final class PhortuneAccountManagersController
extends PhortuneAccountProfileController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadAccount();
if ($response) {
return $response;
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Managers'));
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Managers'))
->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$members = $this->buildMembersSection($account);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
->setFooter(
array(
$authority,
$members,
));
@ -46,11 +49,11 @@ final class PhortuneAccountManagerController
$add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('New Manager'))
->setText(pht('Add Managers'))
->setIcon('fa-plus')
->setWorkflow(true)
->setDisabled(!$can_edit)
->setHref("/phortune/account/manager/add/{$id}/");
->setHref("/phortune/account/{$id}/managers/add/");
$header = id(new PHUIHeaderView())
->setHeader(pht('Account Managers'))

View file

@ -0,0 +1,55 @@
<?php
final class PhortuneAccountOrderListController
extends PhortuneAccountProfileController {
private $subscription;
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$account = $this->getAccount();
$engine = id(new PhortuneCartSearchEngine())
->setController($this)
->setAccount($account);
$subscription_id = $request->getURIData('subscriptionID');
if ($subscription_id) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($subscription_id))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$engine->setSubscription($subscription);
$this->subscription = $subscription;
}
return $engine->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$subscription = $this->subscription;
if ($subscription) {
$crumbs->addTextCrumb(
$subscription->getObjectName(),
$subscription->getURI());
} else if ($this->hasAccount()) {
$account = $this->getAccount();
$crumbs->addTextCrumb(pht('Orders'), $account->getOrdersURI());
}
return $crumbs;
}
}

View file

@ -0,0 +1,40 @@
<?php
final class PhortuneAccountOrdersController
extends PhortuneAccountProfileController {
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Orders'))
->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$order_history = $this->newRecentOrdersView($account, 100);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$authority,
$order_history,
));
$navigation = $this->buildSideNavView('orders');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
}

View file

@ -0,0 +1,126 @@
<?php
final class PhortuneAccountOverviewController
extends PhortuneAccountProfileController {
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$status = $this->buildStatusView($account, $invoices);
$invoices = $this->buildInvoicesSection($account, $invoices);
$purchase_history = $this->newRecentOrdersView($account, 10);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$authority,
$status,
$invoices,
$purchase_history,
));
$navigation = $this->buildSideNavView('overview');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildStatusView(PhortuneAccount $account, $invoices) {
$status_items = $this->getStatusItemsForAccount($account, $invoices);
$view = array();
foreach ($status_items as $item) {
$view[] = id(new PHUIInfoView())
->setSeverity(idx($item, 'severity'))
->appendChild(idx($item, 'note'));
}
return $view;
}
private function buildInvoicesSection(
PhortuneAccount $account,
array $carts) {
$viewer = $this->getViewer();
$table = id(new PhortuneOrderTableView())
->setNoDataString(pht('You have no unpaid invoices.'))
->setIsInvoices(true)
->setUser($viewer)
->setCarts($carts);
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices Due'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView())
->setIcon('fa-exchange')
->setHref($this->getApplicationURI('account/'))
->setName(pht('Switch Accounts')));
return $crumbs;
}
private function getStatusItemsForAccount(
PhortuneAccount $account,
array $invoices) {
$viewer = $this->getViewer();
assert_instances_of($invoices, 'PhortuneCart');
$items = array();
$methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->execute();
if ($invoices) {
$items[] = array(
'severity' => PHUIInfoView::SEVERITY_ERROR,
'note' => pht('You have %d unpaid invoice(s).', count($invoices)),
);
}
// TODO: If a payment method has expired or is expiring soon, we should
// add a status check for it.
return $items;
}
}

View file

@ -0,0 +1,96 @@
<?php
final class PhortuneAccountPaymentMethodController
extends PhortuneAccountProfileController {
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Payment Methods'))
->setBorder(true);
$authority = $this->newAccountAuthorityView();
$header = $this->buildHeaderView();
$methods = $this->buildPaymentMethodsSection($account);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$authority,
$methods,
));
$navigation = $this->buildSideNavView('methods');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildPaymentMethodsSection(PhortuneAccount $account) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $account->getID();
$add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Add Payment Method'))
->setIcon('fa-plus')
->setHref($this->getApplicationURI("account/{$id}/methods/new/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Methods'))
->addActionLink($add);
$list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setFlush(true)
->setNoDataString(
pht('There are no payment methods associated with this account.'));
$methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->execute();
foreach ($methods as $method) {
$id = $method->getID();
$item = id(new PHUIObjectItemView())
->setObjectName($method->getObjectName())
->setHeader($method->getFullDisplayName())
->setHref($method->getURI());
$provider = $method->buildPaymentProvider();
$item->addAttribute($provider->getPaymentMethodProviderDescription());
$list->addItem($item);
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
}
}

View file

@ -0,0 +1,154 @@
<?php
final class PhortuneAccountPaymentMethodViewController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$account = $this->getAccount();
$method = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($request->getURIData('id')))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->executeOne();
if (!$method) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Payment Methods'), $account->getPaymentMethodsURI())
->addTextCrumb($method->getObjectName())
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($method->getFullDisplayName());
$details = $this->newDetailsView($method);
$timeline = $this->buildTransactionTimeline(
$method,
new PhortunePaymentMethodTransactionQuery());
$timeline->setShouldTerminate(true);
$autopay = $this->newAutopayView($method);
$curtain = $this->buildCurtainView($method);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$details,
$autopay,
$timeline,
));
return $this->newPage()
->setTitle($method->getObjectName())
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtainView(PhortunePaymentMethod $method) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$method,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI(
urisprintf(
'card/%d/edit/',
$method->getID()));
$remove_uri = $this->getApplicationURI(
urisprintf(
'card/%d/disable/',
$method->getID()));
$curtain = $this->newCurtainView($method);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Payment Method'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Remove Payment Method'))
->setIcon('fa-times')
->setHref($remove_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
return $curtain;
}
private function newDetailsView(PhortunePaymentMethod $method) {
$viewer = $this->getViewer();
$merchant_phid = $method->getMerchantPHID();
$handles = $viewer->loadHandles(
array(
$merchant_phid,
));
$view = id(new PHUIPropertyListView())
->setUser($viewer);
if (strlen($method->getName())) {
$view->addProperty(pht('Name'), $method->getDisplayName());
}
$view->addProperty(pht('Summary'), $method->getSummary());
$view->addProperty(pht('Expires'), $method->getDisplayExpires());
$view->addProperty(
pht('Merchant'),
$handles[$merchant_phid]->renderLink());
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Payment Method Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($view);
}
private function newAutopayView(PhortunePaymentMethod $method) {
$viewer = $this->getViewer();
$subscriptions = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withPaymentMethodPHIDs(array($method->getPHID()))
->execute();
$table = id(new PhortuneSubscriptionTableView())
->setViewer($viewer)
->setSubscriptions($subscriptions)
->newTableView();
$table->setNoDataString(
pht(
'This payment method is not the default payment method for '.
'any subscriptions.'));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Autopay Subscriptions'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -3,10 +3,6 @@
abstract class PhortuneAccountProfileController
extends PhortuneAccountController {
public function buildApplicationMenu() {
return $this->buildSideNavView()->getMenu();
}
protected function buildHeaderView() {
$viewer = $this->getViewer();
$account = $this->getAccount();
@ -17,13 +13,16 @@ abstract class PhortuneAccountProfileController
->setHeader($title)
->setHeaderIcon('fa-user-circle');
return $header;
if ($this->getMerchants()) {
$customer_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setName(pht('Customer Account'))
->setColor('indigo')
->setIcon('fa-credit-card');
$header->addTag($customer_tag);
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->setBorder(true);
return $crumbs;
return $header;
}
protected function buildSideNavView($filter = null) {
@ -31,6 +30,8 @@ abstract class PhortuneAccountProfileController
$account = $this->getAccount();
$id = $account->getID();
$can_edit = !$this->getMerchants();
$nav = id(new AphrontSideNavFilterView())
->setBaseURI(new PhutilURI($this->getApplicationURI()));
@ -39,30 +40,103 @@ abstract class PhortuneAccountProfileController
$nav->addFilter(
'overview',
pht('Overview'),
$this->getApplicationURI("/{$id}/"),
$account->getURI(),
'fa-user-circle');
$nav->newLink('details')
->setName(pht('Account Details'))
->setHref($this->getApplicationURI("/account/{$id}/details/"))
->setIcon('fa-address-card-o')
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit);
$nav->addLabel(pht('Payments'));
$nav->addFilter(
'methods',
pht('Payment Methods'),
$account->getPaymentMethodsURI(),
'fa-credit-card');
$nav->addFilter(
'subscriptions',
pht('Subscriptions'),
$this->getApplicationURI("/account/subscription/{$id}/"),
$account->getSubscriptionsURI(),
'fa-retweet');
$nav->addFilter(
'billing',
pht('Billing / History'),
$this->getApplicationURI("/account/billing/{$id}/"),
'fa-credit-card');
'orders',
pht('Orders'),
$account->getOrdersURI(),
'fa-shopping-bag');
$nav->addFilter(
'charges',
pht('Charges'),
$account->getChargesURI(),
'fa-calculator');
$nav->addLabel(pht('Personnel'));
$nav->addFilter(
'managers',
pht('Managers'),
$this->getApplicationURI("/account/manager/{$id}/"),
pht('Account Managers'),
$this->getApplicationURI("/account/{$id}/managers/"),
'fa-group');
$nav->newLink('addresses')
->setname(pht('Email Addresses'))
->setHref($account->getEmailAddressesURI())
->setIcon('fa-envelope-o')
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit);
$nav->selectFilter($filter);
return $nav;
}
final protected function newRecentOrdersView(
PhortuneAccount $account,
$limit) {
$viewer = $this->getViewer();
$carts = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit($limit)
->execute();
$orders_uri = $account->getOrderListURI();
$table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($carts);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Orders'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($orders_uri)
->setText(pht('View All Orders')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -0,0 +1,137 @@
<?php
final class PhortuneAccountSubscriptionAutopayController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return true;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$account = $this->getAccount();
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('subscriptionID')))
->withAccountPHIDs(array($account->getPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$method = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('methodID')))
->withAccountPHIDs(array($subscription->getAccountPHID()))
->withMerchantPHIDs(array($subscription->getMerchantPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->executeOne();
if (!$method) {
return new Aphront404Response();
}
$next_uri = $subscription->getURI();
$autopay_phid = $subscription->getDefaultPaymentMethodPHID();
$is_stop = ($autopay_phid === $method->getPHID());
if ($request->isFormOrHisecPost()) {
if ($is_stop) {
$new_phid = null;
} else {
$new_phid = $method->getPHID();
}
$xactions = array();
$xactions[] = $subscription->getApplicationTransactionTemplate()
->setTransactionType(
PhortuneSubscriptionAutopayTransaction::TRANSACTIONTYPE)
->setNewValue($new_phid);
$editor = $subscription->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setCancelURI($next_uri);
$editor->applyTransactions($subscription, $xactions);
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
$method_phid = $method->getPHID();
$subscription_phid = $subscription->getPHID();
$handles = $viewer->loadHandles(
array(
$method_phid,
$subscription_phid,
));
$method_handle = $handles[$method_phid];
$subscription_handle = $handles[$subscription_phid];
$method_display = $method_handle->renderLink();
$method_display = phutil_tag(
'strong',
array(),
$method_display);
$subscription_display = $subscription_handle->renderLink();
$subscription_display = phutil_tag(
'strong',
array(),
$subscription_display);
$body = array();
if ($is_stop) {
$title = pht('Stop Autopay');
$body[] = pht(
'Remove %s as the automatic payment method for subscription %s?',
$method_display,
$subscription_display);
$body[] = pht(
'This payment method will no longer be charged automatically.');
$submit = pht('Stop Autopay');
} else {
$title = pht('Start Autopay');
$body[] = pht(
'Set %s as the automatic payment method for subscription %s?',
$method_display,
$subscription_display);
$body[] = pht(
'This payment method will be used to automatically pay future '.
'charges.');
$submit = pht('Start Autopay');
}
$dialog = $this->newDialog()
->setTitle($title)
->addCancelButton($next_uri)
->addSubmitButton($submit);
foreach ($body as $graph) {
$dialog->appendParagraph($graph);
}
return $dialog;
}
}

View file

@ -3,24 +3,28 @@
final class PhortuneAccountSubscriptionController
extends PhortuneAccountProfileController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadAccount();
if ($response) {
return $response;
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$account = $this->getAccount();
$title = $account->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Subscriptions'));
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Subscriptions'))
->setBorder(true);
$header = $this->buildHeaderView();
$authority = $this->newAccountAuthorityView();
$subscriptions = $this->buildSubscriptionsSection($account);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
->setFooter(
array(
$authority,
$subscriptions,
));
@ -43,11 +47,8 @@ final class PhortuneAccountSubscriptionController
->setLimit(25)
->execute();
$handles = $this->loadViewerHandles(mpull($subscriptions, 'getPHID'));
$table = id(new PhortuneSubscriptionTableView())
->setUser($viewer)
->setHandles($handles)
->setSubscriptions($subscriptions);
$header = id(new PHUIHeaderView())

View file

@ -0,0 +1,317 @@
<?php
final class PhortuneAccountSubscriptionViewController
extends PhortuneAccountController {
protected function shouldRequireAccountEditCapability() {
return false;
}
protected function handleAccountRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('subscriptionID')))
->needTriggers(true)
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$subscription,
PhabricatorPolicyCapability::CAN_EDIT);
$merchant = $subscription->getMerchant();
$account = $subscription->getAccount();
$account_id = $account->getID();
$subscription_id = $subscription->getID();
$title = $subscription->getSubscriptionFullName();
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-retweet');
$edit_uri = $subscription->getEditURI();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($subscription->getSubscriptionCrumbName())
->setBorder(true);
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
$next_invoice = $subscription->getTrigger()->getNextEventPrediction();
$properties->addProperty(
pht('Next Invoice'),
phabricator_datetime($next_invoice, $viewer));
$autopay = $this->newAutopayView($subscription);
$details = id(new PHUIObjectBoxView())
->setHeaderText(pht('Subscription Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($properties);
$due_box = $this->buildDueInvoices($subscription);
$invoice_box = $this->buildPastInvoices($subscription);
$timeline = $this->buildTransactionTimeline(
$subscription,
new PhortuneSubscriptionTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$details,
$autopay,
$due_box,
$invoice_box,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildDueInvoices(PhortuneSubscription $subscription) {
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withSubscriptionPHIDs(array($subscription->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$invoice_table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($invoices)
->setIsInvoices(true);
$invoice_header = id(new PHUIHeaderView())
->setHeader(pht('Invoices Due'));
return id(new PHUIObjectBoxView())
->setHeader($invoice_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($invoice_table);
}
private function buildPastInvoices(PhortuneSubscription $subscription) {
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withSubscriptionPHIDs(array($subscription->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit(50)
->execute();
$invoice_table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($invoices);
$account = $subscription->getAccount();
$merchant = $subscription->getMerchant();
$account_id = $account->getID();
$merchant_id = $merchant->getID();
$subscription_id = $subscription->getID();
$invoices_uri = $this->getApplicationURI(
"{$account_id}/subscription/order/{$subscription_id}/");
$invoice_header = id(new PHUIHeaderView())
->setHeader(pht('Past Invoices'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($invoices_uri)
->setText(pht('View All Invoices')));
return id(new PHUIObjectBoxView())
->setHeader($invoice_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($invoice_table);
}
private function newAutopayView(PhortuneSubscription $subscription) {
$viewer = $this->getViewer();
$account = $subscription->getAccount();
$add_method_uri = urisprintf(
'/account/%d/methods/new/?subscriptionID=%s',
$account->getID(),
$subscription->getID());
$add_method_uri = $this->getApplicationURI($add_method_uri);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$subscription,
PhabricatorPolicyCapability::CAN_EDIT);
$methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($subscription->getAccountPHID()))
->withMerchantPHIDs(array($subscription->getMerchantPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->execute();
$methods = mpull($methods, null, 'getPHID');
$autopay_phid = $subscription->getDefaultPaymentMethodPHID();
$autopay_method = idx($methods, $autopay_phid);
$header = id(new PHUIHeaderView())
->setHeader(pht('Autopay'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-plus')
->setHref($add_method_uri)
->setText(pht('Add Payment Method'))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
$methods = array_select_keys($methods, array($autopay_phid)) + $methods;
$rows = array();
$rowc = array();
foreach ($methods as $method) {
$is_autopay = ($autopay_method === $method);
$remove_uri = urisprintf(
'/card/%d/disable/?subscriptionID=%d',
$method->getID(),
$subscription->getID());
$remove_uri = $this->getApplicationURI($remove_uri);
$autopay_uri = urisprintf(
'/account/%d/subscriptions/%d/autopay/%d/',
$account->getID(),
$subscription->getID(),
$method->getID());
$autopay_uri = $this->getApplicationURI($autopay_uri);
$remove_button = id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-times')
->setText(pht('Delete'))
->setHref($remove_uri)
->setWorkflow(true)
->setDisabled(!$can_edit);
if ($is_autopay) {
$autopay_button = id(new PHUIButtonView())
->setColor('red')
->setIcon('fa-times')
->setText(pht('Stop Autopay'));
} else {
if ($autopay_method) {
$make_color = 'grey';
} else {
$make_color = 'green';
}
$autopay_button = id(new PHUIButtonView())
->setColor($make_color)
->setIcon('fa-retweet')
->setText(pht('Start Autopay'));
}
$autopay_button
->setTag('a')
->setHref($autopay_uri)
->setWorkflow(true)
->setDisabled(!$can_edit);
$rows[] = array(
$method->getID(),
phutil_tag(
'a',
array(
'href' => $method->getURI(),
),
$method->getFullDisplayName()),
$method->getDisplayExpires(),
$autopay_button,
$remove_button,
);
if ($is_autopay) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
}
$method_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('ID'),
pht('Payment Method'),
pht('Expires'),
null,
null,
))
->setRowClasses($rowc)
->setColumnClasses(
array(
null,
'pri wide',
null,
'right',
null,
));
if (!$autopay_method) {
$method_table->setNotice(
array(
id(new PHUIIconView())->setIcon('fa-warning yellow'),
' ',
pht('Autopay is not currently configured for this subscription.'),
));
} else {
$method_table->setNotice(
array(
id(new PHUIIconView())->setIcon('fa-check green'),
' ',
pht(
'Autopay is configured using %s.',
phutil_tag(
'a',
array(
'href' => $autopay_method->getURI(),
),
$autopay_method->getFullDisplayName())),
));
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($method_table);
}
}

View file

@ -1,238 +0,0 @@
<?php
final class PhortuneAccountViewController
extends PhortuneAccountProfileController {
public function handleRequest(AphrontRequest $request) {
$response = $this->loadAccount();
if ($response) {
return $response;
}
$account = $this->getAccount();
$title = $account->getName();
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
$header = $this->buildHeaderView();
$curtain = $this->buildCurtainView($account);
$status = $this->buildStatusView($account, $invoices);
$invoices = $this->buildInvoicesSection($account, $invoices);
$purchase_history = $this->buildPurchaseHistorySection($account);
$timeline = $this->buildTransactionTimeline(
$account,
new PhortuneAccountTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$status,
$invoices,
$purchase_history,
$timeline,
));
$navigation = $this->buildSideNavView('overview');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildStatusView(PhortuneAccount $account, $invoices) {
$status_items = $this->getStatusItemsForAccount($account, $invoices);
$view = array();
foreach ($status_items as $item) {
$view[] = id(new PHUIInfoView())
->setSeverity(idx($item, 'severity'))
->appendChild(idx($item, 'note'));
}
return $view;
}
private function buildCurtainView(PhortuneAccount $account) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI('account/edit/'.$account->getID().'/');
$curtain = $this->newCurtainView($account);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Account'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$member_phids = $account->getMemberPHIDs();
$handles = $viewer->loadHandles($member_phids);
$member_list = id(new PHUIObjectItemListView())
->setSimple(true);
foreach ($member_phids as $member_phid) {
$image_uri = $handles[$member_phid]->getImageURI();
$image_href = $handles[$member_phid]->getURI();
$person = $handles[$member_phid];
$member = id(new PHUIObjectItemView())
->setImageURI($image_uri)
->setHref($image_href)
->setHeader($person->getFullName());
$member_list->addItem($member);
}
$curtain->newPanel()
->setHeaderText(pht('Managers'))
->appendChild($member_list);
return $curtain;
}
private function buildInvoicesSection(
PhortuneAccount $account,
array $carts) {
$viewer = $this->getViewer();
$phids = array();
foreach ($carts as $cart) {
$phids[] = $cart->getPHID();
$phids[] = $cart->getMerchantPHID();
foreach ($cart->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$table = id(new PhortuneOrderTableView())
->setNoDataString(pht('You have no unpaid invoices.'))
->setIsInvoices(true)
->setUser($viewer)
->setCarts($carts)
->setHandles($handles);
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices Due'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
private function buildPurchaseHistorySection(PhortuneAccount $account) {
$viewer = $this->getViewer();
$carts = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit(10)
->execute();
$phids = array();
foreach ($carts as $cart) {
$phids[] = $cart->getPHID();
foreach ($cart->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$orders_uri = $this->getApplicationURI($account->getID().'/order/');
$table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($carts)
->setHandles($handles);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Orders'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($orders_uri)
->setText(pht('View All Orders')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView())
->setIcon('fa-exchange')
->setHref($this->getApplicationURI('account/'))
->setName(pht('Switch Accounts')));
return $crumbs;
}
private function getStatusItemsForAccount(
PhortuneAccount $account,
array $invoices) {
$viewer = $this->getViewer();
assert_instances_of($invoices, 'PhortuneCart');
$items = array();
$methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->execute();
if ($invoices) {
$items[] = array(
'severity' => PHUIInfoView::SEVERITY_ERROR,
'note' => pht('You have %d unpaid invoice(s).', count($invoices)),
);
}
// TODO: If a payment method has expired or is expiring soon, we should
// add a status check for it.
return $items;
}
}

View file

@ -3,27 +3,19 @@
final class PhortuneCartAcceptController
extends PhortuneCartController {
public function handleRequest(AphrontRequest $request) {
protected function shouldRequireAccountAuthority() {
return false;
}
protected function shouldRequireMerchantAuthority() {
return true;
}
protected function handleCartRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$cart = $this->getCart();
// You must control the merchant to accept orders.
$authority = $this->loadMerchantAuthority();
if (!$authority) {
return new Aphront404Response();
}
$cart = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($id))
->withMerchantPHIDs(array($authority->getPHID()))
->needPurchases(true)
->executeOne();
if (!$cart) {
return new Aphront404Response();
}
$cancel_uri = $cart->getDetailURI($authority);
$cancel_uri = $cart->getDetailURI();
if ($cart->getStatus() !== PhortuneCart::STATUS_REVIEW) {
return $this->newDialog()

View file

@ -3,26 +3,21 @@
final class PhortuneCartCancelController
extends PhortuneCartController {
public function handleRequest(AphrontRequest $request) {
protected function shouldRequireAccountAuthority() {
return false;
}
protected function shouldRequireMerchantAuthority() {
return false;
}
protected function handleCartRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$action = $request->getURIData('action');
$authority = $this->loadMerchantAuthority();
$cart_query = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($id))
->needPurchases(true);
if ($authority) {
$cart_query->withMerchantPHIDs(array($authority->getPHID()));
}
$cart = $cart_query->executeOne();
if (!$cart) {
return new Aphront404Response();
}
$cart = $this->getCart();
$authority = $this->getMerchantAuthority();
switch ($action) {
case 'cancel':
@ -45,7 +40,7 @@ final class PhortuneCartCancelController
return new Aphront404Response();
}
$cancel_uri = $cart->getDetailURI($authority);
$cancel_uri = $cart->getDetailURI();
$merchant = $cart->getMerchant();
try {
@ -60,7 +55,7 @@ final class PhortuneCartCancelController
return $this->newDialog()
->setTitle($title)
->appendChild($ex->getMessage())
->addCancelButton($cancel_uri);
->addCancelButton($cancel_uri, pht('Rats'));
}
$charges = id(new PhortuneChargeQuery())

View file

@ -3,19 +3,18 @@
final class PhortuneCartCheckoutController
extends PhortuneCartController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$cart = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($id))
->needPurchases(true)
->executeOne();
if (!$cart) {
return new Aphront404Response();
protected function shouldRequireAccountAuthority() {
return true;
}
protected function shouldRequireMerchantAuthority() {
return false;
}
protected function handleCartRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$cart = $this->getCart();
$cancel_uri = $cart->getCancelURI();
$merchant = $cart->getMerchant();
@ -139,7 +138,10 @@ final class PhortuneCartCheckoutController
'cartID' => $cart->getID(),
);
$payment_method_uri = $this->getApplicationURI("{$account_id}/card/new/");
$payment_method_uri = urisprintf(
'account/%d/methods/new/',
$account->getID());
$payment_method_uri = $this->getApplicationURI($payment_method_uri);
$payment_method_uri = new PhutilURI($payment_method_uri, $params);
$form = id(new AphrontFormView())
@ -205,7 +207,9 @@ final class PhortuneCartCheckoutController
->appendChild($form)
->appendChild($provider_form);
$description_box = $this->renderCartDescription($cart);
$description_view = id(new PhortuneOrderDescriptionView())
->setViewer($viewer)
->setOrder($cart);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Checkout'));
@ -218,9 +222,10 @@ final class PhortuneCartCheckoutController
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
->setFooter(
array(
$description_view,
$cart_box,
$description_box,
$payment_box,
));

View file

@ -3,61 +3,75 @@
abstract class PhortuneCartController
extends PhortuneController {
protected function buildCartContentTable(PhortuneCart $cart) {
private $cart;
private $merchantAuthority;
$rows = array();
foreach ($cart->getPurchases() as $purchase) {
$rows[] = array(
$purchase->getFullDisplayName(),
$purchase->getBasePriceAsCurrency()->formatForDisplay(),
$purchase->getQuantity(),
$purchase->getTotalPriceAsCurrency()->formatForDisplay(),
abstract protected function shouldRequireAccountAuthority();
abstract protected function shouldRequireMerchantAuthority();
abstract protected function handleCartRequest(AphrontRequest $request);
final public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
if ($this->shouldRequireAccountAuthority()) {
$capabilities = array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
} else {
$capabilities = array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
$rows[] = array(
phutil_tag('strong', array(), pht('Total')),
'',
'',
phutil_tag('strong', array(),
$cart->getTotalPriceAsCurrency()->formatForDisplay()),
);
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
pht('Item'),
pht('Price'),
pht('Qty.'),
pht('Total'),
));
$table->setColumnClasses(
array(
'wide',
'right',
'right',
'right',
));
return $table;
$cart = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->needPurchases(true)
->requireCapabilities($capabilities)
->executeOne();
if (!$cart) {
return new Aphront404Response();
}
protected function renderCartDescription(PhortuneCart $cart) {
$description = $cart->getDescription();
if (!strlen($description)) {
return null;
if ($this->shouldRequireMerchantAuthority()) {
PhabricatorPolicyFilter::requireCapability(
$viewer,
$cart->getMerchant(),
PhabricatorPolicyCapability::CAN_EDIT);
}
$output = new PHUIRemarkupView($this->getViewer(), $description);
$this->cart = $cart;
$box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($output);
$can_edit = PhortuneMerchantQuery::canViewersEditMerchants(
array($viewer->getPHID()),
array($cart->getMerchantPHID()));
if ($can_edit) {
$this->merchantAuthority = $cart->getMerchant();
} else {
$this->merchantAuthority = null;
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Description'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($box);
return $this->handleCartRequest($request);
}
final protected function getCart() {
return $this->cart;
}
final protected function getMerchantAuthority() {
return $this->merchantAuthority;
}
final protected function hasMerchantAuthority() {
return (bool)$this->merchantAuthority;
}
final protected function hasAccountAuthority() {
return (bool)PhabricatorPolicyFilter::hasCapability(
$this->getViewer(),
$this->getCart(),
PhabricatorPolicyCapability::CAN_EDIT);
}
}

View file

@ -1,134 +0,0 @@
<?php
final class PhortuneCartListController
extends PhortuneController {
private $merchant;
private $account;
private $subscription;
private $engine;
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$merchant_id = $request->getURIData('merchantID');
$account_id = $request->getURIData('accountID');
$subscription_id = $request->getURIData('subscriptionID');
$engine = id(new PhortuneCartSearchEngine())
->setViewer($viewer);
if ($merchant_id) {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($merchant_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$this->merchant = $merchant;
$viewer->grantAuthority($merchant);
$engine->setMerchant($merchant);
} else if ($account_id) {
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($account_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$this->account = $account;
$engine->setAccount($account);
} else {
return new Aphront404Response();
}
// NOTE: We must process this after processing the merchant authority, so
// it becomes visible in merchant contexts.
if ($subscription_id) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($subscription_id))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$this->subscription = $subscription;
$engine->setSubscription($subscription);
}
$this->engine = $engine;
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine($engine)
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView() {
$viewer = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$this->engine->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$subscription = $this->subscription;
$merchant = $this->merchant;
if ($merchant) {
$id = $merchant->getID();
$this->addMerchantCrumb($crumbs, $merchant);
if (!$subscription) {
$crumbs->addTextCrumb(
pht('Orders'),
$this->getApplicationURI("merchant/orders/{$id}/"));
}
}
$account = $this->account;
if ($account) {
$id = $account->getID();
$this->addAccountCrumb($crumbs, $account);
if (!$subscription) {
$crumbs->addTextCrumb(
pht('Orders'),
$this->getApplicationURI("{$id}/order/"));
}
}
if ($subscription) {
if ($merchant) {
$subscription_uri = $subscription->getMerchantURI();
} else {
$subscription_uri = $subscription->getURI();
}
$crumbs->addTextCrumb(
$subscription->getSubscriptionName(),
$subscription_uri);
}
return $crumbs;
}
}

View file

@ -3,25 +3,20 @@
final class PhortuneCartUpdateController
extends PhortuneCartController {
public function handleRequest(AphrontRequest $request) {
protected function shouldRequireAccountAuthority() {
return false;
}
protected function shouldRequireMerchantAuthority() {
return false;
}
protected function handleCartRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$authority = $this->loadMerchantAuthority();
$cart_query = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($id))
->needPurchases(true);
if ($authority) {
$cart_query->withMerchantPHIDs(array($authority->getPHID()));
}
$cart = $cart_query->executeOne();
if (!$cart) {
return new Aphront404Response();
}
$cart = $this->getCart();
$authority = $this->getMerchantAuthority();
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
@ -60,7 +55,7 @@ final class PhortuneCartUpdateController
}
return id(new AphrontRedirectResponse())
->setURI($cart->getDetailURI($authority));
->setURI($cart->getDetailURI());
}
}

View file

@ -3,312 +3,111 @@
final class PhortuneCartViewController
extends PhortuneCartController {
private $action = null;
protected function shouldRequireAccountAuthority() {
return false;
}
public function handleRequest(AphrontRequest $request) {
protected function shouldRequireMerchantAuthority() {
return false;
}
protected function handleCartRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$this->action = $request->getURIData('action');
$order = $this->getCart();
$authority = $this->getMerchantAuthority();
$can_edit = $this->hasAccountAuthority();
$authority = $this->loadMerchantAuthority();
require_celerity_resource('phortune-css');
$is_printable = ($request->getURIData('action') === 'print');
$query = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($id))
->needPurchases(true);
if ($authority) {
$query->withMerchantPHIDs(array($authority->getPHID()));
}
$cart = $query->executeOne();
if (!$cart) {
return new Aphront404Response();
}
$cart_table = $this->buildCartContentTable($cart);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$cart,
PhabricatorPolicyCapability::CAN_EDIT);
$errors = array();
$error_view = null;
$resume_uri = null;
switch ($cart->getStatus()) {
case PhortuneCart::STATUS_READY:
if ($authority && $cart->getIsInvoice()) {
// We arrived here by following the ad-hoc invoice workflow, and
// are acting with merchant authority.
$checkout_uri = PhabricatorEnv::getURI($cart->getCheckoutURI());
$invoice_message = array(
pht(
'Manual invoices do not automatically notify recipients yet. '.
'Send the payer this checkout link:'),
' ',
phutil_tag(
'a',
array(
'href' => $checkout_uri,
),
$checkout_uri),
);
$error_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors(array($invoice_message));
}
break;
case PhortuneCart::STATUS_PURCHASING:
if ($order->getStatus() === PhortuneCart::STATUS_PURCHASING) {
if ($can_edit) {
$resume_uri = $cart->getMetadataValue('provider.checkoutURI');
if ($resume_uri) {
$errors[] = pht(
'The checkout process has been started, but not yet completed. '.
'You can continue checking out by clicking %s, or cancel the '.
'order, or contact the merchant for assistance.',
phutil_tag('strong', array(), pht('Continue Checkout')));
} else {
$errors[] = pht(
'The checkout process has been started, but an error occurred. '.
'You can cancel the order or contact the merchant for '.
'assistance.');
$resume_uri = $order->getMetadataValue('provider.checkoutURI');
}
}
break;
case PhortuneCart::STATUS_CHARGED:
if ($can_edit) {
$errors[] = pht(
'You have been charged, but processing could not be completed. '.
'You can cancel your order, or contact the merchant for '.
'assistance.');
}
break;
case PhortuneCart::STATUS_HOLD:
if ($can_edit) {
$errors[] = pht(
'Payment for this order is on hold. You can click %s to check '.
'for updates, cancel the order, or contact the merchant for '.
'assistance.',
phutil_tag('strong', array(), pht('Update Status')));
}
break;
case PhortuneCart::STATUS_REVIEW:
if ($authority) {
$errors[] = pht(
'This order has been flagged for manual review. Review the order '.
'and choose %s to accept it or %s to reject it.',
phutil_tag('strong', array(), pht('Accept Order')),
phutil_tag('strong', array(), pht('Refund Order')));
} else if ($can_edit) {
$errors[] = pht(
'This order requires manual processing and will complete once '.
'the merchant accepts it.');
}
break;
case PhortuneCart::STATUS_PURCHASED:
$error_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_SUCCESS)
->appendChild(pht('This purchase has been completed.'));
break;
}
if ($errors) {
$error_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->appendChild($errors);
}
$details = $this->buildDetailsView($cart);
$curtain = $this->buildCurtainView(
$cart,
$can_edit,
$authority,
$resume_uri);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($cart->getName())
->setHeaderIcon('fa-shopping-cart');
->setHeader($order->getName())
->setHeaderIcon('fa-shopping-bag');
if ($cart->getStatus() == PhortuneCart::STATUS_PURCHASED) {
$done_uri = $cart->getDoneURI();
if ($order->getStatus() == PhortuneCart::STATUS_PURCHASED) {
$done_uri = $order->getDoneURI();
if ($done_uri) {
$header->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($done_uri)
->setIcon('fa-check-square green')
->setText($cart->getDoneActionName()));
->setText($order->getDoneActionName()));
}
}
$cart_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Cart Items'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($cart_table);
$description = $this->renderCartDescription($cart);
$charges = id(new PhortuneChargeQuery())
$order_view = id(new PhortuneOrderSummaryView())
->setViewer($viewer)
->withCartPHIDs(array($cart->getPHID()))
->needCarts(true)
->execute();
->setOrder($order)
->setResumeURI($resume_uri)
->setPrintable($is_printable);
$phids = array();
foreach ($charges as $charge) {
$phids[] = $charge->getProviderPHID();
$phids[] = $charge->getCartPHID();
$phids[] = $charge->getMerchantPHID();
$phids[] = $charge->getPaymentMethodPHID();
}
$handles = $this->loadViewerHandles($phids);
$crumbs = null;
$curtain = null;
$charges_table = id(new PhortuneChargeTableView())
->setUser($viewer)
->setHandles($handles)
->setCharges($charges)
->setShowOrder(false);
$main = array();
$tail = array();
$charges = id(new PHUIObjectBoxView())
->setHeaderText(pht('Charges'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($charges_table);
require_celerity_resource('phortune-invoice-css');
$account = $cart->getAccount();
if ($is_printable) {
$body_class = 'phortune-invoice-view';
$crumbs = $this->buildApplicationCrumbs();
if ($authority) {
$this->addMerchantCrumb($crumbs, $authority);
$tail[] = $order_view;
} else {
$this->addAccountCrumb($crumbs, $cart->getAccount());
}
$crumbs->addTextCrumb(pht('Cart %d', $cart->getID()));
$crumbs->setBorder(true);
$body_class = 'phortune-cart-page';
if (!$this->action) {
$class = 'phortune-cart-page';
$timeline = $this->buildTransactionTimeline(
$cart,
new PhortuneCartTransactionQuery());
$timeline
$curtain = $this->buildCurtainView(
$order,
$can_edit,
$authority,
$resume_uri);
$account = $order->getAccount();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($account->getName(), $account->getURI())
->addTextCrumb(pht('Orders'), $account->getOrdersURI())
->addTextCrumb($order->getObjectName())
->setBorder(true);
$timeline = $this->buildTransactionTimeline($order)
->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$error_view,
$details,
$cart_box,
$description,
$charges,
$timeline,
));
} else {
$class = 'phortune-invoice-view';
$crumbs = null;
$merchant_phid = $cart->getMerchantPHID();
$buyer_phid = $cart->getAuthorPHID();
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withPHIDs(array($merchant_phid))
->needProfileImage(true)
->executeOne();
$buyer = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withPHIDs(array($buyer_phid))
->needProfileImage(true)
->executeOne();
$merchant_contact = new PHUIRemarkupView(
$viewer,
$merchant->getContactInfo());
$account_name = $account->getBillingName();
if (!strlen($account_name)) {
$account_name = $buyer->getRealName();
$main[] = $order_view;
$main[] = $timeline;
}
$account_contact = $account->getBillingAddress();
if (strlen($account_contact)) {
$account_contact = new PHUIRemarkupView(
$viewer,
$account_contact);
}
$column_view = id(new PHUITwoColumnView())
->setMainColumn($main)
->setFooter($tail);
$view = id(new PhortuneInvoiceView())
->setMerchantName($merchant->getName())
->setMerchantLogo($merchant->getProfileImageURI())
->setMerchantContact($merchant_contact)
->setMerchantFooter($merchant->getInvoiceFooter())
->setAccountName($account_name)
->setAccountContact($account_contact)
->setStatus($error_view)
->setContent(
array(
$details,
$cart_box,
$charges,
));
if ($curtain) {
$column_view->setCurtain($curtain);
}
$page = $this->newPage()
->setTitle(pht('Cart %d', $cart->getID()))
->addClass($class)
->appendChild($view);
->addClass($body_class)
->setTitle(
array(
$order->getObjectName(),
$order->getName(),
))
->appendChild($column_view);
if ($crumbs) {
$page->setCrumbs($crumbs);
}
return $page;
}
private function buildDetailsView(PhortuneCart $cart) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($cart);
$handles = $this->loadViewerHandles(
array(
$cart->getAccountPHID(),
$cart->getAuthorPHID(),
$cart->getMerchantPHID(),
));
if ($this->action == 'print') {
$view->addProperty(pht('Order Name'), $cart->getName());
}
$view->addProperty(
pht('Account'),
$handles[$cart->getAccountPHID()]->renderLink());
$view->addProperty(
pht('Authorized By'),
$handles[$cart->getAuthorPHID()]->renderLink());
$view->addProperty(
pht('Merchant'),
$handles[$cart->getMerchantPHID()]->renderLink());
$view->addProperty(
pht('Status'),
PhortuneCart::getNameForStatus($cart->getStatus()));
$view->addProperty(
pht('Updated'),
phabricator_datetime($cart->getDateModified(), $viewer));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
private function buildCurtainView(
PhortuneCart $cart,
$can_edit,
@ -318,20 +117,43 @@ final class PhortuneCartViewController
$viewer = $this->getViewer();
$id = $cart->getID();
$curtain = $this->newCurtainView($cart);
$status = $cart->getStatus();
$is_ready = ($status === PhortuneCart::STATUS_READY);
$can_cancel = ($can_edit && $cart->canCancelOrder());
$can_checkout = ($can_edit && $is_ready);
$can_accept = ($status === PhortuneCart::STATUS_REVIEW);
$can_refund = ($authority && $cart->canRefundOrder());
$can_void = ($authority && $cart->canVoidOrder());
if ($authority) {
$prefix = 'merchant/'.$authority->getID().'/';
} else {
$prefix = '';
}
$cancel_uri = $this->getApplicationURI("cart/{$id}/cancel/");
$refund_uri = $this->getApplicationURI("cart/{$id}/refund/");
$update_uri = $this->getApplicationURI("cart/{$id}/update/");
$accept_uri = $this->getApplicationURI("cart/{$id}/accept/");
$print_uri = $this->getApplicationURI("cart/{$id}/print/");
$checkout_uri = $cart->getCheckoutURI();
$void_uri = $this->getApplicationURI("cart/{$id}/void/");
$cancel_uri = $this->getApplicationURI("{$prefix}cart/{$id}/cancel/");
$refund_uri = $this->getApplicationURI("{$prefix}cart/{$id}/refund/");
$update_uri = $this->getApplicationURI("{$prefix}cart/{$id}/update/");
$accept_uri = $this->getApplicationURI("{$prefix}cart/{$id}/accept/");
$print_uri = $this->getApplicationURI("{$prefix}cart/{$id}/print/");
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Printable Version'))
->setHref($print_uri)
->setOpenInNewWindow(true)
->setIcon('fa-print'));
$curtain->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Pay Now'))
->setIcon('fa-credit-card')
->setDisabled(!$can_checkout)
->setWorkflow(!$can_checkout)
->setHref($checkout_uri));
$curtain->addAction(
id(new PhabricatorActionView())
@ -341,24 +163,6 @@ final class PhortuneCartViewController
->setWorkflow(true)
->setHref($cancel_uri));
if ($authority) {
if ($cart->getStatus() == PhortuneCart::STATUS_REVIEW) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Accept Order'))
->setIcon('fa-check')
->setWorkflow(true)
->setHref($accept_uri));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Refund Order'))
->setIcon('fa-reply')
->setWorkflow(true)
->setHref($refund_uri));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Update Status'))
@ -369,16 +173,39 @@ final class PhortuneCartViewController
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Continue Checkout'))
->setIcon('fa-shopping-cart')
->setIcon('fa-shopping-bag')
->setHref($resume_uri));
}
if ($authority) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Printable Version'))
->setHref($print_uri)
->setOpenInNewWindow(true)
->setIcon('fa-print'));
->setType(PhabricatorActionView::TYPE_DIVIDER));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Accept Order'))
->setIcon('fa-check')
->setWorkflow(true)
->setDisabled(!$can_accept)
->setHref($accept_uri));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Refund Order'))
->setIcon('fa-reply')
->setWorkflow(true)
->setDisabled(!$can_refund)
->setHref($refund_uri));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Void Invoice'))
->setIcon('fa-times')
->setWorkflow(true)
->setDisabled(!$can_void)
->setHref($void_uri));
}
return $curtain;
}

View file

@ -0,0 +1,43 @@
<?php
final class PhortuneCartVoidController
extends PhortuneCartController {
protected function shouldRequireAccountAuthority() {
return false;
}
protected function shouldRequireMerchantAuthority() {
return true;
}
protected function handleCartRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$cart = $this->getCart();
$cancel_uri = $cart->getDetailURI();
try {
$title = pht('Unable to Void Invoice');
$cart->assertCanVoidOrder();
} catch (Exception $ex) {
return $this->newDialog()
->setTitle($title)
->appendChild($ex->getMessage())
->addCancelButton($cancel_uri);
}
if ($request->isFormPost()) {
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
return $this->newDialog()
->setTitle(pht('Void Invoice?'))
->appendParagraph(
pht(
'Really void this invoice? The customer will no longer be asked '.
'to submit payment for it.'))
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Void Invoice'));
}
}

View file

@ -0,0 +1,172 @@
<?php
abstract class PhortuneExternalController
extends PhortuneController {
private $email;
final public function shouldAllowPublic() {
return true;
}
abstract protected function handleExternalRequest(AphrontRequest $request);
final protected function hasAccountEmail() {
return (bool)$this->email;
}
final protected function getAccountEmail() {
return $this->email;
}
final protected function getExternalViewer() {
return PhabricatorUser::getOmnipotentUser();
}
final public function handleRequest(AphrontRequest $request) {
$address_key = $request->getURIData('addressKey');
$access_key = $request->getURIData('accessKey');
$viewer = $this->getViewer();
$xviewer = $this->getExternalViewer();
$email = id(new PhortuneAccountEmailQuery())
->setViewer($xviewer)
->withAddressKeys(array($address_key))
->executeOne();
if (!$email) {
return new Aphront404Response();
}
$account = $email->getAccount();
$can_see = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$email_display = phutil_tag('strong', array(), $email->getAddress());
$user_display = phutil_tag('strong', array(), $viewer->getUsername());
$actual_key = $email->getAccessKey();
if (!phutil_hashes_are_identical($access_key, $actual_key)) {
$dialog = $this->newDialog()
->setTitle(pht('Email Access Link Out of Date'))
->appendParagraph(
pht(
'You are trying to access this payment account as: %s',
$email_display))
->appendParagraph(
pht(
'The access link you have followed is out of date and no longer '.
'works.'));
if ($can_see) {
$dialog->appendParagraph(
pht(
'You are currently logged in as a user (%s) who has '.
'permission to manage the payment account, so you can '.
'continue to the updated link.',
$user_display));
$dialog->addCancelButton(
$email->getExternalURI(),
pht('Continue to Updated Link'));
} else {
$dialog->appendParagraph(
pht(
'To access information about this payment account, follow '.
'a more recent link or ask a user with access to give you '.
'an updated link.'));
}
return $dialog;
}
switch ($email->getStatus()) {
case PhortuneAccountEmailStatus::STATUS_ACTIVE:
break;
case PhortuneAccountEmailStatus::STATUS_DISABLED:
return $this->newDialog()
->setTitle(pht('Address Disabled'))
->appendParagraph(
pht(
'This email address (%s) has been disabled and no longer has '.
'access to this payment account.',
$email_display));
case PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED:
return $this->newDialog()
->setTitle(pht('Permanently Unsubscribed'))
->appendParagraph(
pht(
'This email address (%s) has been permanently unsubscribed '.
'and no longer has access to this payment account.',
$email_display));
break;
default:
return new Aphront404Response();
}
$this->email = $email;
return $this->handleExternalRequest($request);
}
final protected function newExternalCrumbs() {
$viewer = $this->getViewer();
$crumbs = new PHUICrumbsView();
if ($this->hasAccountEmail()) {
$email = $this->getAccountEmail();
$account = $email->getAccount();
$crumb_name = pht(
'Payment Account: %s',
$account->getName());
$crumb = id(new PHUICrumbView())
->setIcon('fa-diamond')
->setName($crumb_name)
->setHref($email->getExternalURI());
$crumbs
->addCrumb($crumb);
} else {
$crumb = id(new PHUICrumbView())
->setIcon('fa-diamond')
->setText(pht('External Account View'));
$crumbs->addCrumb($crumb);
}
return $crumbs;
}
final protected function newExternalView() {
$email = $this->getAccountEmail();
$xviewer = $this->getExternalViewer();
$origin_phid = $email->getAuthorPHID();
$handles = $xviewer->loadHandles(array($origin_phid));
$messages = array();
$messages[] = pht(
'You are viewing this payment account as: %s',
phutil_tag('strong', array(), $email->getAddress()));
$messages[] = pht(
'This email address was added to this payment account by: %s',
phutil_tag('strong', array(), $handles[$origin_phid]->getFullName()));
$messages[] = pht(
'Anyone who has a link to this page can view order history for '.
'this payment account.');
return id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors($messages);
}
}

View file

@ -0,0 +1,98 @@
<?php
final class PhortuneExternalOrderController
extends PhortuneExternalController {
protected function handleExternalRequest(AphrontRequest $request) {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$order = id(new PhortuneCartQuery())
->setViewer($xviewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($request->getURIData('orderID')))
->executeOne();
if (!$order) {
return new Aphront404Response();
}
$is_printable = ($request->getURIData('action') === 'print');
$order_view = id(new PhortuneOrderSummaryView())
->setViewer($xviewer)
->setOrder($order)
->setPrintable($is_printable);
$crumbs = null;
$curtain = null;
$main = array();
$tail = array();
require_celerity_resource('phortune-invoice-css');
if ($is_printable) {
$body_class = 'phortune-invoice-view';
$tail[] = $order_view;
} else {
$body_class = 'phortune-cart-page';
$curtain = $this->newCurtain($order);
$crumbs = $this->newExternalCrumbs()
->addTextCrumb($order->getObjectName())
->setBorder(true);
$timeline = $this->buildTransactionTimeline($order)
->setShouldTerminate(true);
$main[] = $order_view;
$main[] = $timeline;
}
$column_view = id(new PHUITwoColumnView())
->setMainColumn($main)
->setFooter($tail);
if ($curtain) {
$column_view->setCurtain($curtain);
}
$page = $this->newPage()
->addClass($body_class)
->setTitle(
array(
$order->getObjectName(),
$order->getName(),
))
->appendChild($column_view);
if ($crumbs) {
$page->setCrumbs($crumbs);
}
return $page;
}
private function newCurtain(PhortuneCart $order) {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$curtain = $this->newCurtainView($order);
$print_uri = $email->getExternalOrderPrintURI($order);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Printable Version'))
->setHref($print_uri)
->setOpenInNewWindow(true)
->setIcon('fa-print'));
return $curtain;
}
}

View file

@ -0,0 +1,101 @@
<?php
final class PhortuneExternalOverviewController
extends PhortuneExternalController {
protected function handleExternalRequest(AphrontRequest $request) {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$crumbs = $this->newExternalCrumbs()
->addTextCrumb(pht('Viewing As "%s"', $email->getAddress()))
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices and Receipts: %s', $account->getName()))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-times')
->setText(pht('Unsubscribe'))
->setHref($email->getUnsubscribeURI())
->setWorkflow(true));
$external_view = $this->newExternalView();
$invoices_view = $this->newInvoicesView();
$receipts_view = $this->newReceiptsView();
$column_view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$external_view,
$invoices_view,
$receipts_view,
));
return $this->newPage()
->setCrumbs($crumbs)
->setTitle(
array(
pht('Invoices and Receipts'),
$account->getName(),
))
->appendChild($column_view);
}
private function newInvoicesView() {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$invoices = id(new PhortuneCartQuery())
->setViewer($xviewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices'));
$invoices_table = id(new PhortuneOrderTableView())
->setViewer($xviewer)
->setAccountEmail($email)
->setCarts($invoices)
->setIsInvoices(true);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($invoices_table);
}
private function newReceiptsView() {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$receipts = id(new PhortuneCartQuery())
->setViewer($xviewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(false)
->execute();
$header = id(new PHUIHeaderView())
->setHeader(pht('Receipts'));
$receipts_table = id(new PhortuneOrderTableView())
->setViewer($xviewer)
->setAccountEmail($email)
->setCarts($receipts);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($receipts_table);
}
}

View file

@ -0,0 +1,67 @@
<?php
final class PhortuneExternalUnsubscribeController
extends PhortuneExternalController {
protected function handleExternalRequest(AphrontRequest $request) {
$xviewer = $this->getExternalViewer();
$email = $this->getAccountEmail();
$account = $email->getAccount();
$email_uri = $email->getExternalURI();
if ($request->isFormOrHisecPost()) {
$xactions = array();
$xactions[] = $email->getApplicationTransactionTemplate()
->setTransactionType(
PhortuneAccountEmailStatusTransaction::TRANSACTIONTYPE)
->setNewValue(PhortuneAccountEmailStatus::STATUS_UNSUBSCRIBED);
$email->getApplicationTransactionEditor()
->setActor($xviewer)
->setActingAsPHID($email->getPHID())
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->setCancelURI($email_uri)
->applyTransactions($email, $xactions);
return id(new AphrontRedirectResponse())->setURI($email_uri);
}
$email_display = phutil_tag(
'strong',
array(),
$email->getAddress());
$account_display = phutil_tag(
'strong',
array(),
$account->getName());
$submit = pht(
'Permanently Unsubscribe (%s)',
$email->getAddress());
return $this->newDialog()
->setTitle(pht('Permanently Unsubscribe'))
->appendParagraph(
pht(
'Permanently unsubscribe this email address (%s) from this '.
'payment account (%s)?',
$email_display,
$account_display))
->appendParagraph(
pht(
'You will no longer receive email and access links will no longer '.
'function.'))
->appendParagraph(
pht(
'This action is permanent and can not be undone.'))
->addCancelButton($email_uri)
->addSubmitButton($submit);
}
}

View file

@ -1,32 +1,23 @@
<?php
final class PhortuneMerchantAddManagerController extends PhortuneController {
final class PhortuneMerchantAddManagerController
extends PhortuneMerchantController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfileImage(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$v_members = array();
$e_members = null;
$merchant_uri = $this->getApplicationURI("/merchant/manager/{$id}/");
$merchant_uri = $merchant->getManagersURI();
if ($request->isFormPost()) {
if ($request->isFormOrHiSecPost()) {
$xactions = array();
$v_members = $request->getArr('memberPHIDs');
$v_members = $request->getArr('managerPHIDs');
$type_edge = PhabricatorTransactions::TYPE_EDGE;
$xactions[] = id(new PhortuneMerchantTransaction())
@ -59,13 +50,13 @@ final class PhortuneMerchantAddManagerController extends PhortuneController {
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setLabel(pht('Members'))
->setName('memberPHIDs')
->setLabel(pht('New Managers'))
->setName('managerPHIDs')
->setValue($v_members)
->setError($e_members));
return $this->newDialog()
->setTitle(pht('Add New Manager'))
->setTitle(pht('Add New Managers'))
->appendForm($form)
->setWidth(AphrontDialogView::WIDTH_FORM)
->addCancelButton($merchant_uri)

View file

@ -3,11 +3,80 @@
abstract class PhortuneMerchantController
extends PhortuneController {
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Merchants'),
$this->getApplicationURI('merchant/'));
return $crumbs;
private $merchant;
final protected function setMerchant(PhortuneMerchant $merchant) {
$this->merchant = $merchant;
return $this;
}
final protected function getMerchant() {
return $this->merchant;
}
final protected function hasMerchant() {
return (bool)$this->merchant;
}
final public function handleRequest(AphrontRequest $request) {
if ($this->shouldRequireMerchantEditCapability()) {
$response = $this->loadMerchantForEdit();
} else {
$response = $this->loadMerchantForView();
}
if ($response) {
return $response;
}
return $this->handleMerchantRequest($request);
}
abstract protected function shouldRequireMerchantEditCapability();
abstract protected function handleMerchantRequest(AphrontRequest $request);
private function loadMerchantForEdit() {
return $this->loadMerchantWithCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
));
}
private function loadMerchantForView() {
return $this->loadMerchantWithCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
));
}
private function loadMerchantWithCapabilities(array $capabilities) {
$viewer = $this->getViewer();
$request = $this->getRequest();
$merchant_id = $request->getURIData('merchantID');
if (!$merchant_id) {
throw new Exception(
pht(
'Controller ("%s") extends controller "%s", but is reachable '.
'with no "merchantID" in URI.',
get_class($this),
__CLASS__));
}
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($merchant_id))
->needProfileImage(true)
->requireCapabilities($capabilities)
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$this->setMerchant($merchant);
return null;
}
}

View file

@ -0,0 +1,151 @@
<?php
final class PhortuneMerchantDetailsController
extends PhortuneMerchantProfileController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = $this->getMerchant();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Account Details'))
->setBorder(true);
$header = $this->buildHeaderView();
$title = pht(
'%s %s',
$merchant->getObjectName(),
$merchant->getName());
$details = $this->buildDetailsView($merchant);
$curtain = $this->buildCurtainView($merchant);
$timeline = $this->buildTransactionTimeline(
$merchant,
new PhortuneMerchantTransactionQuery());
$timeline->setShouldTerminate(true);
$navigation = $this->buildSideNavView('details');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$details,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildDetailsView(PhortuneMerchant $merchant) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($merchant);
$invoice_from = $merchant->getInvoiceEmail();
if (!$invoice_from) {
$invoice_from = pht('No email address set');
$invoice_from = phutil_tag('em', array(), $invoice_from);
}
$view->addProperty(pht('Invoice From'), $invoice_from);
$description = $merchant->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
}
$contact_info = $merchant->getContactInfo();
if (strlen($contact_info)) {
$contact_info = new PHUIRemarkupView($viewer, $contact_info);
$view->addSectionHeader(
pht('Contact Information'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($contact_info);
}
$footer_info = $merchant->getInvoiceFooter();
if (strlen($footer_info)) {
$footer_info = new PHUIRemarkupView($viewer, $footer_info);
$view->addSectionHeader(
pht('Invoice Footer'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($footer_info);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
private function buildCurtainView(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($merchant);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Merchant'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/edit/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Logo'))
->setIcon('fa-picture-o')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/{$id}/picture/edit/")));
$member_phids = $merchant->getMemberPHIDs();
$handles = $viewer->loadHandles($member_phids);
$member_list = id(new PHUIObjectItemListView())
->setSimple(true);
foreach ($member_phids as $member_phid) {
$image_uri = $handles[$member_phid]->getImageURI();
$image_href = $handles[$member_phid]->getURI();
$person = $handles[$member_phid];
$member = id(new PHUIObjectItemView())
->setImageURI($image_uri)
->setHref($image_href)
->setHeader($person->getFullName());
$member_list->addItem($member);
}
$curtain->newPanel()
->setHeaderText(pht('Managers'))
->appendChild($member_list);
return $curtain;
}
}

View file

@ -1,7 +1,7 @@
<?php
final class PhortuneMerchantEditController
extends PhortuneMerchantController {
extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
return id(new PhortuneMerchantEditEngine())

View file

@ -1,9 +1,16 @@
<?php
final class PhortuneMerchantInvoiceCreateController
extends PhortuneMerchantProfileController {
extends PhortuneMerchantController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
// TODO: Make this work again, or destroy it.
return new Aphront404Response();
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$merchant = $this->loadMerchantAuthority();
@ -58,9 +65,10 @@ final class PhortuneMerchantInvoiceCreateController
}
if (!$target_account) {
$accounts = PhortuneAccountQuery::loadAccountsForUser(
$target_user,
PhabricatorContentSource::newFromRequest($request));
$accounts = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withMemberPHIDs(array($target_user->getPHID()))
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)

View file

@ -1,37 +1,12 @@
<?php
final class PhortuneMerchantListController
extends PhortuneMerchantController {
public function shouldAllowPublic() {
return true;
}
extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$querykey = $request->getURIData('queryKey');
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($querykey)
->setSearchEngine(new PhortuneMerchantSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView() {
$viewer = $this->getViewer();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new PhortuneMerchantSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
return id(new PhortuneMerchantSearchEngine())
->setController($this)
->buildResponse();
}
protected function buildApplicationCrumbs() {

View file

@ -1,33 +1,27 @@
<?php
final class PhortuneMerchantManagerController
final class PhortuneMerchantManagersController
extends PhortuneMerchantProfileController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfileImage(true)
->executeOne();
if (!$merchant) {
return new Aphront404Response();
protected function shouldRequireMerchantEditCapability() {
return false;
}
$this->setMerchant($merchant);
$header = $this->buildHeaderView();
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Managers'));
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Managers'))
->setBorder(true);
$header = $this->buildHeaderView();
$members = $this->buildMembersSection($merchant);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
->setFooter(
array(
$members,
));
@ -38,7 +32,6 @@ final class PhortuneMerchantManagerController
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildMembersSection(PhortuneMerchant $merchant) {
@ -51,12 +44,18 @@ final class PhortuneMerchantManagerController
$id = $merchant->getID();
$add_uri = urisprintf(
'merchant/%d/managers/new/',
$merchant->getID());
$add_uri = $this->getApplicationURI($add_uri);
$add = id(new PHUIButtonView())
->setTag('a')
->setText(pht('New Manager'))
->setIcon('fa-plus')
->setWorkflow(true)
->setHref("/phortune/merchant/manager/add/{$id}/");
->setDisabled(!$can_edit)
->setHref($add_uri);
$header = id(new PHUIHeaderView())
->setHeader(pht('Merchant Account Managers'))

View file

@ -0,0 +1,55 @@
<?php
final class PhortuneMerchantOrderListController
extends PhortuneMerchantProfileController {
private $subscription;
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$engine = id(new PhortuneCartSearchEngine())
->setController($this)
->setMerchant($merchant);
$subscription_id = $request->getURIData('subscriptionID');
if ($subscription_id) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($subscription_id))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$engine->setSubscription($subscription);
$this->subscription = $subscription;
}
return $engine->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$subscription = $this->subscription;
if ($subscription) {
$crumbs->addTextCrumb(
$subscription->getObjectName(),
$subscription->getURI());
} else if ($this->hasMerchant()) {
$merchant = $this->getMerchant();
$crumbs->addTextCrumb(pht('Orders'), $merchant->getOrdersURI());
}
return $crumbs;
}
}

View file

@ -0,0 +1,78 @@
<?php
final class PhortuneMerchantOrdersController
extends PhortuneMerchantProfileController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$merchant = $this->getMerchant();
$title = $merchant->getName();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Orders'))
->setBorder(true);
$header = $this->buildHeaderView();
$order_history = $this->newRecentOrdersView($merchant, 100);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$order_history,
));
$navigation = $this->buildSideNavView('orders');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function newRecentOrdersView(
PhortuneMerchant $merchant,
$limit) {
$viewer = $this->getViewer();
$carts = id(new PhortuneCartQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit($limit)
->execute();
$orders_uri = $merchant->getOrderListURI();
$table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($carts);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Orders'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($orders_uri)
->setText(pht('View All Orders')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -0,0 +1,136 @@
<?php
final class PhortuneMerchantOverviewController
extends PhortuneMerchantProfileController {
protected function shouldRequireMerchantEditCapability() {
return false;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$crumbs = $this->buildApplicationCrumbs()
->setBorder(true);
$header = $this->buildHeaderView();
$title = pht(
'%s %s',
$merchant->getObjectName(),
$merchant->getName());
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->execute();
$details = $this->buildDetailsView($merchant, $providers);
$navigation = $this->buildSideNavView('overview');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$details,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildDetailsView(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($merchant);
$status_view = new PHUIStatusListView();
$have_any = false;
$any_test = false;
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
if ($provider->isEnabled()) {
$have_any = true;
}
if (!$provider->isAcceptingLivePayments()) {
$any_test = true;
}
}
if ($have_any) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Accepts Payments'))
->setNote(pht('This merchant can accept payments.')));
if ($any_test) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('Test Mode'))
->setNote(pht('This merchant is accepting test payments.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Live Mode'))
->setNote(pht('This merchant is accepting live payments.')));
}
} else if ($providers) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_REJECT, 'red')
->setTarget(pht('No Enabled Providers'))
->setNote(
pht(
'All of the payment providers for this merchant are '.
'disabled.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('No Providers'))
->setNote(
pht(
'This merchant does not have any payment providers configured '.
'yet, so it can not accept payments. Add a provider.')));
}
$view->addProperty(pht('Status'), $status_view);
$description = $merchant->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
}
$contact_info = $merchant->getContactInfo();
if (strlen($contact_info)) {
$contact_info = new PHUIRemarkupView($viewer, $contact_info);
$view->addSectionHeader(
pht('Contact Information'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($contact_info);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
}

View file

@ -1,28 +1,17 @@
<?php
final class PhortuneMerchantPictureController
extends PhortuneMerchantProfileController {
extends PhortuneMerchantController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfileImage(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
protected function shouldRequireMerchantEditCapability() {
return true;
}
$this->setMerchant($merchant);
$uri = $merchant->getURI();
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$uri = $merchant->getDetailsURI();
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_file = true;
@ -222,12 +211,9 @@ final class PhortuneMerchantPictureController
$upload_box,
));
$navigation = $this->buildSideNavView();
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild(
array(
$view,

View file

@ -1,22 +1,7 @@
<?php
abstract class PhortuneMerchantProfileController
extends PhortuneController {
private $merchant;
public function setMerchant(PhortuneMerchant $merchant) {
$this->merchant = $merchant;
return $this;
}
public function getMerchant() {
return $this->merchant;
}
public function buildApplicationMenu() {
return $this->buildSideNavView()->getMenu();
}
extends PhortuneMerchantController {
protected function buildHeaderView() {
$viewer = $this->getViewer();
@ -26,20 +11,20 @@ abstract class PhortuneMerchantProfileController
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($merchant)
->setImage($merchant->getProfileImageURI());
return $header;
}
protected function buildApplicationCrumbs() {
$merchant = $this->getMerchant();
$id = $merchant->getID();
$merchant_uri = $this->getApplicationURI("/merchant/{$id}/");
$crumbs = parent::buildApplicationCrumbs();
if ($this->hasMerchant()) {
$merchant = $this->getMerchant();
$merchant_uri = $merchant->getURI();
$crumbs->addTextCrumb($merchant->getName(), $merchant_uri);
$crumbs->setBorder(true);
}
return $crumbs;
}
@ -58,31 +43,47 @@ abstract class PhortuneMerchantProfileController
$nav->addLabel(pht('Merchant'));
$nav->addFilter(
'overview',
pht('Overview'),
$this->getApplicationURI("/merchant/{$id}/"),
'fa-building-o');
$nav->newLink('overview')
->setName(pht('Overview'))
->setHref($merchant->getURI())
->setIcon('fa-building-o');
if ($can_edit) {
$nav->addFilter(
'orders',
pht('Orders'),
$this->getApplicationURI("merchant/orders/{$id}/"),
'fa-retweet');
$nav->newLink('details')
->setName(pht('Account Details'))
->setHref($merchant->getDetailsURI())
->setIcon('fa-address-card-o')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$nav->addFilter(
'subscriptions',
pht('Subscriptions'),
$this->getApplicationURI("merchant/{$id}/subscription/"),
'fa-shopping-cart');
$nav->addLabel(pht('Payments'));
$nav->addFilter(
'managers',
pht('Managers'),
$this->getApplicationURI("/merchant/manager/{$id}/"),
'fa-group');
}
$nav->newLink('providers')
->setName(pht('Payment Providers'))
->setHref($merchant->getPaymentProvidersURI())
->setIcon('fa-credit-card')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$nav->newLink('orders')
->setName(pht('Orders'))
->setHref($merchant->getOrdersURI())
->setIcon('fa-shopping-bag')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$nav->newLink('subscriptions')
->setName(pht('Subscriptions'))
->setHref($merchant->getSubscriptionsURI())
->setIcon('fa-retweet')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$nav->addLabel(pht('Personnel'));
$nav->newLink('managers')
->setName(pht('Managers'))
->setHref($merchant->getManagersURI())
->setIcon('fa-group');
$nav->selectFilter($filter);

View file

@ -1,11 +1,17 @@
<?php
final class PhortuneProviderDisableController
final class PhortuneMerchantProviderDisableController
extends PhortuneMerchantController {
public function handleRequest(AphrontRequest $request) {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = $this->getMerchant();
$id = $request->getURIData('providerID');
$provider_config = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
@ -20,9 +26,8 @@ final class PhortuneProviderDisableController
return new Aphront404Response();
}
$merchant = $provider_config->getMerchant();
$merchant_id = $merchant->getID();
$cancel_uri = $this->getApplicationURI("merchant/{$merchant_id}/");
$cancel_uri = $provider_config->getURI();
$provider = $provider_config->buildProvider();

View file

@ -1,16 +1,23 @@
<?php
final class PhortuneProviderEditController
final class PhortuneMerchantProviderEditController
extends PhortuneMerchantController {
public function handleRequest(AphrontRequest $request) {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$id = $request->getURIData('id');
if ($id) {
$provider_config = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withIDs(array($id))
->withMerchantPHIDs(array($merchant->getPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
@ -25,20 +32,8 @@ final class PhortuneProviderEditController
$merchant = $provider_config->getMerchant();
$merchant_id = $merchant->getID();
$cancel_uri = $this->getApplicationURI("merchant/{$merchant_id}/");
$cancel_uri = $provider_config->getURI();
} else {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($request->getStr('merchantID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$merchant_id = $merchant->getID();
$current_providers = id(new PhortunePaymentProviderConfigQuery())
@ -62,9 +57,7 @@ final class PhortuneProviderEditController
}
$provider_config->setProviderClass($class);
$cancel_uri = $this->getApplicationURI(
'provider/edit/?merchantID='.$merchant_id);
$cancel_uri = $merchant->getPaymentProvidersURI();
}
$provider = $provider_config->buildProvider();
@ -123,10 +116,12 @@ final class PhortuneProviderEditController
$xactions = array();
if (!$provider_config->getID()) {
$xactions[] = id(new PhortunePaymentProviderConfigTransaction())
->setTransactionType(
PhortunePaymentProviderConfigTransaction::TYPE_CREATE)
->setNewValue(true);
}
foreach ($xaction_values as $key => $value) {
$xactions[] = id(clone $template)
@ -143,9 +138,9 @@ final class PhortuneProviderEditController
$editor->applyTransactions($provider_config, $xactions);
$merchant_uri = $this->getApplicationURI(
'merchant/'.$merchant->getID().'/');
return id(new AphrontRedirectResponse())->setURI($merchant_uri);
$next_uri = $provider_config->getURI();
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
}
} else {
@ -155,7 +150,6 @@ final class PhortuneProviderEditController
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('merchantID', $merchant->getID())
->addHiddenInput('class', $provider_config->getProviderClass())
->addHiddenInput('edit', true)
->appendChild(
@ -261,7 +255,6 @@ final class PhortuneProviderEditController
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('merchantID', $merchant->getID())
->appendRemarkupInstructions(
pht('Choose the type of payment provider to add:'))
->appendChild($panel_classes)

View file

@ -0,0 +1,127 @@
<?php
final class PhortuneMerchantProviderViewController
extends PhortuneMerchantController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$provider = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('providerID')))
->executeOne();
if (!$provider) {
return new Aphront404Response();
}
$provider_type = $provider->buildProvider();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($merchant->getName(), $merchant->getURI())
->addTextCrumb(
pht('Payment Providers'),
$merchant->getPaymentProvidersURI())
->addTextCrumb($provider->getObjectName())
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Provider: %s', $provider_type->getName()));
$details = $this->newDetailsView($provider);
$timeline = $this->buildTransactionTimeline(
$provider,
new PhortunePaymentProviderConfigTransactionQuery());
$timeline->setShouldTerminate(true);
$curtain = $this->buildCurtainView($provider);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$details,
$timeline,
));
return $this->newPage()
->setTitle($provider->getObjectName())
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtainView(PhortunePaymentProviderConfig $provider) {
$viewer = $this->getViewer();
$merchant = $this->getMerchant();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$provider,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = $this->getApplicationURI(
urisprintf(
'merchant/%d/providers/edit/%d/',
$merchant->getID(),
$provider->getID()));
$disable_uri = $this->getApplicationURI(
urisprintf(
'merchant/%d/providers/%d/disable/',
$merchant->getID(),
$provider->getID()));
$curtain = $this->newCurtainView($provider);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Provider'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$provider_type = $provider->buildProvider();
if ($provider_type->isEnabled()) {
$disable_icon = 'fa-times';
$disable_name = pht('Disable Provider');
} else {
$disable_icon = 'fa-check';
$disable_name = pht('Enable Provider');
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName($disable_name)
->setIcon($disable_icon)
->setHref($disable_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
return $curtain;
}
private function newDetailsView(PhortunePaymentProviderConfig $provider) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$provider_type = $provider->buildProvider();
$view->addProperty(pht('Provider Type'), $provider_type->getName());
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Payment Provider Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($view);
}
}

View file

@ -0,0 +1,116 @@
<?php
final class PhortuneMerchantProvidersController
extends PhortuneMerchantProfileController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Payment Providers'))
->setBorder(true);
$header = $this->buildHeaderView();
$title = pht(
'%s %s',
$merchant->getObjectName(),
$merchant->getName());
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->execute();
$provider_list = $this->buildProviderList(
$merchant,
$providers);
$navigation = $this->buildSideNavView('providers');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$provider_list,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildProviderList(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$provider_list = id(new PHUIObjectItemListView())
->setNoDataString(pht('This merchant has no payment providers.'));
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
$provider_id = $provider_config->getID();
$item = id(new PHUIObjectItemView())
->setObjectName($provider_config->getObjectName())
->setHeader($provider->getName())
->setHref($provider_config->getURI());
if ($provider->isEnabled()) {
if ($provider->isAcceptingLivePayments()) {
$item->setStatusIcon('fa-check green');
} else {
$item->setStatusIcon('fa-warning yellow');
$item->addIcon('fa-exclamation-triangle', pht('Test Mode'));
}
$item->addAttribute($provider->getConfigureProvidesDescription());
} else {
$item->setDisabled(true);
$item->addAttribute(
phutil_tag('em', array(), pht('This payment provider is disabled.')));
}
$provider_list->addItem($item);
}
$add_uri = urisprintf(
'merchant/%d/providers/edit/',
$merchant->getID());
$add_uri = $this->getApplicationURI($add_uri);
$add_action = id(new PHUIButtonView())
->setTag('a')
->setHref($add_uri)
->setText(pht('Add Payment Provider'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-plus');
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Providers'))
->addActionLink($add_action);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($provider_list);
}
}

View file

@ -0,0 +1,50 @@
<?php
final class PhortuneMerchantSubscriptionListController
extends PhortuneMerchantProfileController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$merchant = $this->getMerchant();
$engine = id(new PhortuneCartSearchEngine())
->setController($this)
->setMerchant($merchant);
$subscription_id = $request->getURIData('subscriptionID');
if ($subscription_id) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($subscription_id))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$engine->setSubscription($subscription);
$this->subscription = $subscription;
}
return $engine->buildResponse();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
if ($this->hasMerchant()) {
$merchant = $this->getMerchant();
$crumbs->addTextCrumb(
pht('Subscriptions'),
$merchant->getSubscriptionsURI());
}
return $crumbs;
}
}

View file

@ -0,0 +1,68 @@
<?php
final class PhortuneMerchantSubscriptionsController
extends PhortuneMerchantProfileController {
protected function shouldRequireMerchantEditCapability() {
return true;
}
protected function handleMerchantRequest(AphrontRequest $request) {
$merchant = $this->getMerchant();
$title = $merchant->getName();
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Subscriptions'))
->setBorder(true);
$header = $this->buildHeaderView();
$subscriptions = $this->buildSubscriptionsSection($merchant);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$subscriptions,
));
$navigation = $this->buildSideNavView('subscriptions');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildSubscriptionsSection(PhortuneMerchant $merchant) {
$viewer = $this->getViewer();
$subscriptions = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->setLimit(25)
->execute();
$subscriptions_uri = $merchant->getSubscriptionListURI();
$table = id(new PhortuneSubscriptionTableView())
->setUser($viewer)
->setSubscriptions($subscriptions);
$header = id(new PHUIHeaderView())
->setHeader(pht('Subscriptions'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-list')
->setHref($subscriptions_uri)
->setText(pht('View All Subscriptions')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -1,324 +0,0 @@
<?php
final class PhortuneMerchantViewController
extends PhortuneMerchantProfileController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($id))
->needProfileImage(true)
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$this->setMerchant($merchant);
$crumbs = $this->buildApplicationCrumbs();
$header = $this->buildHeaderView();
$title = pht(
'Merchant %d %s',
$merchant->getID(),
$merchant->getName());
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->execute();
$details = $this->buildDetailsView($merchant, $providers);
$curtain = $this->buildCurtainView($merchant);
$provider_list = $this->buildProviderList(
$merchant,
$providers);
$timeline = $this->buildTransactionTimeline(
$merchant,
new PhortuneMerchantTransactionQuery());
$timeline->setShouldTerminate(true);
$navigation = $this->buildSideNavView('overview');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$details,
$provider_list,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
private function buildDetailsView(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($merchant);
$status_view = new PHUIStatusListView();
$have_any = false;
$any_test = false;
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
if ($provider->isEnabled()) {
$have_any = true;
}
if (!$provider->isAcceptingLivePayments()) {
$any_test = true;
}
}
if ($have_any) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Accepts Payments'))
->setNote(pht('This merchant can accept payments.')));
if ($any_test) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('Test Mode'))
->setNote(pht('This merchant is accepting test payments.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Live Mode'))
->setNote(pht('This merchant is accepting live payments.')));
}
} else if ($providers) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_REJECT, 'red')
->setTarget(pht('No Enabled Providers'))
->setNote(
pht(
'All of the payment providers for this merchant are '.
'disabled.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('No Providers'))
->setNote(
pht(
'This merchant does not have any payment providers configured '.
'yet, so it can not accept payments. Add a provider.')));
}
$view->addProperty(pht('Status'), $status_view);
$invoice_from = $merchant->getInvoiceEmail();
if (!$invoice_from) {
$invoice_from = pht('No email address set');
$invoice_from = phutil_tag('em', array(), $invoice_from);
}
$view->addProperty(pht('Invoice From'), $invoice_from);
$description = $merchant->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($description);
}
$contact_info = $merchant->getContactInfo();
if (strlen($contact_info)) {
$contact_info = new PHUIRemarkupView($viewer, $contact_info);
$view->addSectionHeader(
pht('Contact Info'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($contact_info);
}
$footer_info = $merchant->getInvoiceFooter();
if (strlen($footer_info)) {
$footer_info = new PHUIRemarkupView($viewer, $footer_info);
$view->addSectionHeader(
pht('Invoice Footer'),
PHUIPropertyListView::ICON_SUMMARY);
$view->addTextContent($footer_info);
}
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($view);
}
private function buildCurtainView(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($merchant);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Merchant'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/edit/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Logo'))
->setIcon('fa-camera')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/picture/{$id}/")));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('New Invoice'))
->setIcon('fa-fax')
->setHref($this->getApplicationURI("merchant/{$id}/invoice/new/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$member_phids = $merchant->getMemberPHIDs();
$handles = $viewer->loadHandles($member_phids);
$member_list = id(new PHUIObjectItemListView())
->setSimple(true);
foreach ($member_phids as $member_phid) {
$image_uri = $handles[$member_phid]->getImageURI();
$image_href = $handles[$member_phid]->getURI();
$person = $handles[$member_phid];
$member = id(new PHUIObjectItemView())
->setImageURI($image_uri)
->setHref($image_href)
->setHeader($person->getFullName());
$member_list->addItem($member);
}
$curtain->newPanel()
->setHeaderText(pht('Managers'))
->appendChild($member_list);
return $curtain;
}
private function buildProviderList(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$provider_list = id(new PHUIObjectItemListView())
->setFlush(true)
->setNoDataString(pht('This merchant has no payment providers.'));
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
$provider_id = $provider_config->getID();
$item = id(new PHUIObjectItemView())
->setHeader($provider->getName());
if ($provider->isEnabled()) {
if ($provider->isAcceptingLivePayments()) {
$item->setStatusIcon('fa-check green');
} else {
$item->setStatusIcon('fa-warning yellow');
$item->addIcon('fa-exclamation-triangle', pht('Test Mode'));
}
$item->addAttribute($provider->getConfigureProvidesDescription());
} else {
// Don't show disabled providers to users who can't manage the merchant
// account.
if (!$can_edit) {
continue;
}
$item->setDisabled(true);
$item->addAttribute(
phutil_tag('em', array(), pht('This payment provider is disabled.')));
}
if ($can_edit) {
$edit_uri = $this->getApplicationURI(
"/provider/edit/{$provider_id}/");
$disable_uri = $this->getApplicationURI(
"/provider/disable/{$provider_id}/");
if ($provider->isEnabled()) {
$disable_icon = 'fa-times';
$disable_name = pht('Disable');
} else {
$disable_icon = 'fa-check';
$disable_name = pht('Enable');
}
$item->addAction(
id(new PHUIListItemView())
->setIcon($disable_icon)
->setHref($disable_uri)
->setName($disable_name)
->setWorkflow(true));
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pencil')
->setHref($edit_uri)
->setName(pht('Edit')));
}
$provider_list->addItem($item);
}
$add_action = id(new PHUIButtonView())
->setTag('a')
->setHref($this->getApplicationURI('provider/edit/?merchantID='.$id))
->setText(pht('Add Payment Provider'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-plus');
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Providers'))
->addActionLink($add_action);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($provider_list);
}
}

View file

@ -1,303 +0,0 @@
<?php
final class PhortunePaymentMethodCreateController
extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$account_id = $request->getURIData('accountID');
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($account_id))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$account_id = $account->getID();
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($request->getInt('merchantID')))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$cart_id = $request->getInt('cartID');
$subscription_id = $request->getInt('subscriptionID');
if ($cart_id) {
$cancel_uri = $this->getApplicationURI("cart/{$cart_id}/checkout/");
} else if ($subscription_id) {
$cancel_uri = $this->getApplicationURI(
"{$account_id}/subscription/edit/{$subscription_id}/");
} else {
$cancel_uri = $this->getApplicationURI($account->getID().'/');
}
$providers = $this->loadCreatePaymentMethodProvidersForMerchant($merchant);
if (!$providers) {
throw new Exception(
pht(
'There are no payment providers enabled that can add payment '.
'methods.'));
}
if (count($providers) == 1) {
// If there's only one provider, always choose it.
$provider_id = head_key($providers);
} else {
$provider_id = $request->getInt('providerID');
if (empty($providers[$provider_id])) {
$choices = array();
foreach ($providers as $provider) {
$choices[] = $this->renderSelectProvider($provider);
}
$content = phutil_tag(
'div',
array(
'class' => 'phortune-payment-method-list',
),
$choices);
return $this->newDialog()
->setRenderDialogAsDiv(true)
->setTitle(pht('Add Payment Method'))
->appendParagraph(pht('Choose a payment method to add:'))
->appendChild($content)
->addCancelButton($cancel_uri);
}
}
$provider = $providers[$provider_id];
$errors = array();
$display_exception = null;
if ($request->isFormPost() && $request->getBool('isProviderForm')) {
$method = id(new PhortunePaymentMethod())
->setAccountPHID($account->getPHID())
->setAuthorPHID($viewer->getPHID())
->setMerchantPHID($merchant->getPHID())
->setProviderPHID($provider->getProviderConfig()->getPHID())
->setStatus(PhortunePaymentMethod::STATUS_ACTIVE);
// Limit the rate at which you can attempt to add payment methods. This
// is intended as a line of defense against using Phortune to validate a
// large list of stolen credit card numbers.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhortuneAddPaymentMethodAction(),
1);
if (!$errors) {
$errors = $this->processClientErrors(
$provider,
$request->getStr('errors'));
}
if (!$errors) {
$client_token_raw = $request->getStr('token');
$client_token = null;
try {
$client_token = phutil_json_decode($client_token_raw);
} catch (PhutilJSONParserException $ex) {
$errors[] = pht(
'There was an error decoding token information submitted by the '.
'client. Expected a JSON-encoded token dictionary, received: %s.',
nonempty($client_token_raw, pht('nothing')));
}
if (!$provider->validateCreatePaymentMethodToken($client_token)) {
$errors[] = pht(
'There was an error with the payment token submitted by the '.
'client. Expected a valid dictionary, received: %s.',
$client_token_raw);
}
if (!$errors) {
try {
$provider->createPaymentMethodFromRequest(
$request,
$method,
$client_token);
} catch (PhortuneDisplayException $exception) {
$display_exception = $exception;
} catch (Exception $ex) {
$errors = array(
pht('There was an error adding this payment method:'),
$ex->getMessage(),
);
}
}
}
if (!$errors && !$display_exception) {
$method->save();
// If we added this method on a cart flow, return to the cart to
// check out.
if ($cart_id) {
$next_uri = $this->getApplicationURI(
"cart/{$cart_id}/checkout/?paymentMethodID=".$method->getID());
} else if ($subscription_id) {
$next_uri = new PhutilURI($cancel_uri);
$next_uri->replaceQueryParam('added', true);
} else {
$account_uri = $this->getApplicationURI($account->getID().'/');
$next_uri = new PhutilURI($account_uri);
$next_uri->setFragment('payment');
}
return id(new AphrontRedirectResponse())->setURI($next_uri);
} else {
if ($display_exception) {
$dialog_body = $display_exception->getView();
} else {
$dialog_body = id(new PHUIInfoView())
->setErrors($errors);
}
return $this->newDialog()
->setTitle(pht('Error Adding Payment Method'))
->appendChild($dialog_body)
->addCancelButton($request->getRequestURI());
}
}
$form = $provider->renderCreatePaymentMethodForm($request, $errors);
$form
->setUser($viewer)
->setAction($request->getRequestURI())
->setWorkflow(true)
->addHiddenInput('providerID', $provider_id)
->addHiddenInput('cartID', $request->getInt('cartID'))
->addHiddenInput('subscriptionID', $request->getInt('subscriptionID'))
->addHiddenInput('isProviderForm', true)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Add Payment Method'))
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Method'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Add Payment Method'));
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Add Payment Method'))
->setHeaderIcon('fa-plus-square');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$box,
));
return $this->newPage()
->setTitle($provider->getPaymentMethodDescription())
->setCrumbs($crumbs)
->appendChild($view);
}
private function renderSelectProvider(
PhortunePaymentProvider $provider) {
$request = $this->getRequest();
$viewer = $request->getUser();
$description = $provider->getPaymentMethodDescription();
$icon_uri = $provider->getPaymentMethodIcon();
$details = $provider->getPaymentMethodProviderDescription();
$this->requireResource('phortune-css');
$icon = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)
->setSpriteIcon($provider->getPaymentMethodIcon());
$button = id(new PHUIButtonView())
->setSize(PHUIButtonView::BIG)
->setColor(PHUIButtonView::GREY)
->setIcon($icon)
->setText($description)
->setSubtext($details)
->setMetadata(array('disableWorkflow' => true));
$form = id(new AphrontFormView())
->setUser($viewer)
->setAction($request->getRequestURI())
->addHiddenInput('providerID', $provider->getProviderConfig()->getID())
->appendChild($button);
return $form;
}
private function processClientErrors(
PhortunePaymentProvider $provider,
$client_errors_raw) {
$errors = array();
$client_errors = null;
try {
$client_errors = phutil_json_decode($client_errors_raw);
} catch (PhutilJSONParserException $ex) {
$errors[] = pht(
'There was an error decoding error information submitted by the '.
'client. Expected a JSON-encoded list of error codes, received: %s.',
nonempty($client_errors_raw, pht('nothing')));
}
foreach (array_unique($client_errors) as $key => $client_error) {
$client_errors[$key] = $provider->translateCreatePaymentMethodErrorCode(
$client_error);
}
foreach (array_unique($client_errors) as $client_error) {
switch ($client_error) {
case PhortuneErrCode::ERR_CC_INVALID_NUMBER:
$message = pht(
'The card number you entered is not a valid card number. Check '.
'that you entered it correctly.');
break;
case PhortuneErrCode::ERR_CC_INVALID_CVC:
$message = pht(
'The CVC code you entered is not a valid CVC code. Check that '.
'you entered it correctly. The CVC code is a 3-digit or 4-digit '.
'numeric code which usually appears on the back of the card.');
break;
case PhortuneErrCode::ERR_CC_INVALID_EXPIRY:
$message = pht(
'The card expiration date is not a valid expiration date. Check '.
'that you entered it correctly. You can not add an expired card '.
'as a payment method.');
break;
default:
$message = $provider->getCreatePaymentMethodErrorMessage(
$client_error);
if (!$message) {
$message = pht(
"There was an unexpected error ('%s') processing payment ".
"information.",
$client_error);
phlog($message);
}
break;
}
$errors[$client_error] = $message;
}
return $errors;
}
}

View file

@ -1,58 +0,0 @@
<?php
final class PhortunePaymentMethodDisableController
extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$method_id = $request->getURIData('id');
$method = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withIDs(array($method_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$method) {
return new Aphront404Response();
}
if ($method->getStatus() == PhortunePaymentMethod::STATUS_DISABLED) {
return new Aphront400Response();
}
$account = $method->getAccount();
$account_id = $account->getID();
$account_uri = $this->getApplicationURI("/account/billing/{$account_id}/");
if ($request->isFormPost()) {
// TODO: ApplicationTransactions!!!!
$method
->setStatus(PhortunePaymentMethod::STATUS_DISABLED)
->save();
return id(new AphrontRedirectResponse())->setURI($account_uri);
}
return $this->newDialog()
->setTitle(pht('Remove Payment Method'))
->appendParagraph(
pht(
'Remove the payment method "%s" from your account?',
phutil_tag(
'strong',
array(),
$method->getFullDisplayName())))
->appendParagraph(
pht(
'You will no longer be able to make payments using this payment '.
'method.'))
->addCancelButton($account_uri)
->addSubmitButton(pht('Remove Payment Method'));
}
}

View file

@ -0,0 +1,462 @@
<?php
final class PhortunePaymentMethodCreateController
extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$account_id = $request->getURIData('accountID');
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($account_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$cart_id = $request->getInt('cartID');
$subscription_id = $request->getInt('subscriptionID');
$merchant_id = $request->getInt('merchantID');
if ($cart_id) {
$cart = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($cart_id))
->executeOne();
if (!$cart) {
return new Aphront404Response();
}
$subscription_phid = $cart->getSubscriptionPHID();
if ($subscription_phid) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withPHIDs(array($subscription_phid))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
} else {
$subscription = null;
}
$merchant = $cart->getMerchant();
$cart_id = $cart->getID();
$subscription_id = null;
$merchant_id = null;
$next_uri = $cart->getCheckoutURI();
} else if ($subscription_id) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withIDs(array($subscription_id))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$cart = null;
$merchant = $subscription->getMerchant();
$cart_id = null;
$subscription_id = $subscription->getID();
$merchant_id = null;
$next_uri = $subscription->getURI();
} else if ($merchant_id) {
$merchant_phids = $account->getMerchantPHIDs();
if ($merchant_phids) {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($merchant_id))
->withPHIDs($merchant_phids)
->executeOne();
} else {
$merchant = null;
}
if (!$merchant) {
return new Aphront404Response();
}
$cart = null;
$subscription = null;
$cart_id = null;
$subscription_id = null;
$merchant_id = $merchant->getID();
$next_uri = $account->getPaymentMethodsURI();
} else {
$next_uri = $account->getPaymentMethodsURI();
$merchant_phids = $account->getMerchantPHIDs();
if ($merchant_phids) {
$merchants = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withPHIDs($merchant_phids)
->needProfileImage(true)
->execute();
} else {
$merchants = array();
}
if (!$merchants) {
return $this->newDialog()
->setTitle(pht('No Merchants'))
->appendParagraph(
pht(
'You have not established a relationship with any merchants '.
'yet. Create an order or subscription before adding payment '.
'methods.'))
->addCancelButton($next_uri);
}
// If there's more than one merchant, ask the user to pick which one they
// want to pay. If there's only one, just pick it for them.
if (count($merchants) > 1) {
$menu = $this->newMerchantMenu($merchants);
$form = id(new AphrontFormView())
->appendInstructions(
pht(
'Choose the merchant you want to pay.'));
return $this->newDialog()
->setTitle(pht('Choose a Merchant'))
->appendForm($form)
->appendChild($menu)
->addCancelButton($next_uri);
}
$cart = null;
$subscription = null;
$merchant = head($merchants);
$cart_id = null;
$subscription_id = null;
$merchant_id = $merchant->getID();
}
$providers = $this->loadCreatePaymentMethodProvidersForMerchant($merchant);
if (!$providers) {
throw new Exception(
pht(
'There are no payment providers enabled that can add payment '.
'methods.'));
}
$state_params = array(
'cartID' => $cart_id,
'subscriptionID' => $subscription_id,
'merchantID' => $merchant_id,
);
$state_params = array_filter($state_params);
$state_uri = new PhutilURI($request->getRequestURI());
foreach ($state_params as $key => $value) {
$state_uri->replaceQueryParam($key, $value);
}
$provider_id = $request->getInt('providerID');
if (isset($providers[$provider_id])) {
$provider = $providers[$provider_id];
} else {
// If there's more than one provider, ask the user to pick how they
// want to pay. If there's only one, just pick it.
if (count($providers) > 1) {
$menu = $this->newProviderMenu($providers, $state_uri);
return $this->newDialog()
->setTitle(pht('Choose a Payment Method'))
->appendChild($menu)
->addCancelButton($next_uri);
}
$provider = head($providers);
}
$provider_id = $provider->getProviderConfig()->getID();
$state_params['providerID'] = $provider_id;
$errors = array();
$display_exception = null;
if ($request->isFormPost() && $request->getBool('isProviderForm')) {
$method = id(new PhortunePaymentMethod())
->setAccountPHID($account->getPHID())
->setAuthorPHID($viewer->getPHID())
->setMerchantPHID($merchant->getPHID())
->setProviderPHID($provider->getProviderConfig()->getPHID())
->setStatus(PhortunePaymentMethod::STATUS_ACTIVE);
// Limit the rate at which you can attempt to add payment methods. This
// is intended as a line of defense against using Phortune to validate a
// large list of stolen credit card numbers.
PhabricatorSystemActionEngine::willTakeAction(
array($viewer->getPHID()),
new PhortuneAddPaymentMethodAction(),
1);
if (!$errors) {
$errors = $this->processClientErrors(
$provider,
$request->getStr('errors'));
}
if (!$errors) {
$client_token_raw = $request->getStr('token');
$client_token = null;
try {
$client_token = phutil_json_decode($client_token_raw);
} catch (PhutilJSONParserException $ex) {
$errors[] = pht(
'There was an error decoding token information submitted by the '.
'client. Expected a JSON-encoded token dictionary, received: %s.',
nonempty($client_token_raw, pht('nothing')));
}
if (!$provider->validateCreatePaymentMethodToken($client_token)) {
$errors[] = pht(
'There was an error with the payment token submitted by the '.
'client. Expected a valid dictionary, received: %s.',
$client_token_raw);
}
if (!$errors) {
try {
$provider->createPaymentMethodFromRequest(
$request,
$method,
$client_token);
} catch (PhortuneDisplayException $exception) {
$display_exception = $exception;
} catch (Exception $ex) {
$errors = array(
pht('There was an error adding this payment method:'),
$ex->getMessage(),
);
}
}
}
if (!$errors && !$display_exception) {
$xactions = array();
$xactions[] = $method->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorTransactions::TYPE_CREATE)
->setNewValue(true);
$editor = id(new PhortunePaymentMethodEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($method, $xactions);
$next_uri = new PhutilURI($next_uri);
// If we added this method on a cart flow, return to the cart to
// checkout with this payment method selected.
if ($cart_id) {
$next_uri->replaceQueryParam('paymentMethodID', $method->getID());
}
return id(new AphrontRedirectResponse())->setURI($next_uri);
} else {
if ($display_exception) {
$dialog_body = $display_exception->getView();
} else {
$dialog_body = id(new PHUIInfoView())
->setErrors($errors);
}
return $this->newDialog()
->setTitle(pht('Error Adding Payment Method'))
->appendChild($dialog_body)
->addCancelButton($request->getRequestURI());
}
}
$form = $provider->renderCreatePaymentMethodForm($request, $errors);
$form
->setViewer($viewer)
->setAction($request->getPath())
->setWorkflow(true)
->addHiddenInput('isProviderForm', true)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Add Payment Method'))
->addCancelButton($next_uri));
foreach ($state_params as $key => $value) {
$form->addHiddenInput($key, $value);
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Method'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Add Payment Method'))
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Add Payment Method'))
->setHeaderIcon('fa-plus-square');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(
array(
$box,
));
return $this->newPage()
->setTitle($provider->getPaymentMethodDescription())
->setCrumbs($crumbs)
->appendChild($view);
}
private function processClientErrors(
PhortunePaymentProvider $provider,
$client_errors_raw) {
$errors = array();
$client_errors = null;
try {
$client_errors = phutil_json_decode($client_errors_raw);
} catch (PhutilJSONParserException $ex) {
$errors[] = pht(
'There was an error decoding error information submitted by the '.
'client. Expected a JSON-encoded list of error codes, received: %s.',
nonempty($client_errors_raw, pht('nothing')));
}
foreach (array_unique($client_errors) as $key => $client_error) {
$client_errors[$key] = $provider->translateCreatePaymentMethodErrorCode(
$client_error);
}
foreach (array_unique($client_errors) as $client_error) {
switch ($client_error) {
case PhortuneErrCode::ERR_CC_INVALID_NUMBER:
$message = pht(
'The card number you entered is not a valid card number. Check '.
'that you entered it correctly.');
break;
case PhortuneErrCode::ERR_CC_INVALID_CVC:
$message = pht(
'The CVC code you entered is not a valid CVC code. Check that '.
'you entered it correctly. The CVC code is a 3-digit or 4-digit '.
'numeric code which usually appears on the back of the card.');
break;
case PhortuneErrCode::ERR_CC_INVALID_EXPIRY:
$message = pht(
'The card expiration date is not a valid expiration date. Check '.
'that you entered it correctly. You can not add an expired card '.
'as a payment method.');
break;
default:
$message = $provider->getCreatePaymentMethodErrorMessage(
$client_error);
if (!$message) {
$message = pht(
"There was an unexpected error ('%s') processing payment ".
"information.",
$client_error);
phlog($message);
}
break;
}
$errors[$client_error] = $message;
}
return $errors;
}
private function newMerchantMenu(array $merchants) {
assert_instances_of($merchants, 'PhortuneMerchant');
$request = $this->getRequest();
$viewer = $this->getViewer();
$menu = id(new PHUIObjectItemListView())
->setUser($viewer)
->setBig(true)
->setFlush(true);
foreach ($merchants as $merchant) {
$merchant_uri = id(new PhutilURI($request->getRequestURI()))
->replaceQueryParam('merchantID', $merchant->getID());
$item = id(new PHUIObjectItemView())
->setObjectName($merchant->getObjectName())
->setHeader($merchant->getName())
->setHref($merchant_uri)
->setClickable(true)
->setImageURI($merchant->getProfileImageURI());
$menu->addItem($item);
}
return $menu;
}
private function newProviderMenu(array $providers, PhutilURI $state_uri) {
assert_instances_of($providers, 'PhortunePaymentProvider');
$request = $this->getRequest();
$viewer = $this->getViewer();
$menu = id(new PHUIObjectItemListView())
->setUser($viewer)
->setBig(true)
->setFlush(true);
foreach ($providers as $provider) {
$provider_id = $provider->getProviderConfig()->getID();
$provider_uri = id(clone $state_uri)
->replaceQueryParam('providerID', $provider_id);
$description = $provider->getPaymentMethodDescription();
$icon_uri = $provider->getPaymentMethodIcon();
$details = $provider->getPaymentMethodProviderDescription();
$icon = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)
->setSpriteIcon($icon_uri);
$item = id(new PHUIObjectItemView())
->setHeader($description)
->setHref($provider_uri)
->setClickable(true)
->addAttribute($details)
->setImageIcon($icon);
$menu->addItem($item);
}
return $menu;
}
}

View file

@ -0,0 +1,96 @@
<?php
final class PhortunePaymentMethodDisableController
extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$method_id = $request->getURIData('id');
$method = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withIDs(array($method_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$method) {
return new Aphront404Response();
}
if ($method->getStatus() == PhortunePaymentMethod::STATUS_DISABLED) {
return new Aphront400Response();
}
$subscription_id = $request->getInt('subscriptionID');
if ($subscription_id) {
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($subscription_id))
->withAccountPHIDs(array($method->getAccountPHID()))
->withMerchantPHIDs(array($method->getMerchantPHID()))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
} else {
$subscription = null;
}
$account = $method->getAccount();
$account_id = $account->getID();
$account_uri = $account->getPaymentMethodsURI();
if ($request->isFormPost()) {
$xactions = array();
$xactions[] = $method->getApplicationTransactionTemplate()
->setTransactionType(
PhortunePaymentMethodStatusTransaction::TRANSACTIONTYPE)
->setNewValue(PhortunePaymentMethod::STATUS_DISABLED);
$editor = id(new PhortunePaymentMethodEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($method, $xactions);
if ($subscription) {
$next_uri = $subscription->getURI();
} else {
$next_uri = $account_uri;
}
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
$method_phid = $method->getPHID();
$handles = $viewer->loadHandles(
array(
$method_phid,
));
$method_handle = $handles[$method_phid];
$method_display = $method_handle->renderLink();
$method_display = phutil_tag('strong', array(), $method_display);
return $this->newDialog()
->setTitle(pht('Remove Payment Method'))
->addHiddenInput('subscriptionID', $subscription_id)
->appendParagraph(
pht(
'Remove the payment method %s from your account?',
$method_display))
->appendParagraph(
pht(
'You will no longer be able to make payments using this payment '.
'method.'))
->addCancelButton($account_uri)
->addSubmitButton(pht('Remove Payment Method'));
}
}

View file

@ -20,25 +20,36 @@ final class PhortunePaymentMethodEditController
return new Aphront404Response();
}
$next_uri = $method->getURI();
$account = $method->getAccount();
$account_uri = $this->getApplicationURI($account->getID().'/');
$v_name = $method->getName();
if ($request->isFormPost()) {
$v_name = $request->getStr('name');
$name = $request->getStr('name');
$xactions = array();
// TODO: Use ApplicationTransactions
$xactions[] = $method->getApplicationTransactionTemplate()
->setTransactionType(
PhortunePaymentMethodNameTransaction::TRANSACTIONTYPE)
->setNewValue($v_name);
$method->setName($name);
$method->save();
$editor = id(new PhortunePaymentMethodEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
return id(new AphrontRedirectResponse())->setURI($account_uri);
$editor->applyTransactions($method, $xactions);
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
$provider = $method->buildPaymentProvider();
$form = id(new AphrontFormView())
->setUser($viewer)
->setViewer($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
@ -54,7 +65,7 @@ final class PhortunePaymentMethodEditController
->setValue($method->getDisplayExpires()))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($account_uri)
->addCancelButton($next_uri)
->setValue(pht('Save Changes')));
$box = id(new PHUIObjectBoxView())
@ -62,11 +73,12 @@ final class PhortunePaymentMethodEditController
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($account->getName(), $account_uri);
$crumbs->addTextCrumb($method->getDisplayName());
$crumbs->addTextCrumb(pht('Edit'));
$crumbs->setBorder(true);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($account->getName(), $account->getURI())
->addTextCrumb(pht('Payment Methods'), $account->getPaymentMethodsURI())
->addTextCrumb($method->getObjectName(), $method->getURI())
->addTextCrumb(pht('Edit'))
->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit Payment Method'))
@ -74,7 +86,8 @@ final class PhortunePaymentMethodEditController
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
->setFooter(
array(
$box,
));
@ -82,7 +95,6 @@ final class PhortunePaymentMethodEditController
->setTitle(pht('Edit Payment Method'))
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -1,185 +0,0 @@
<?php
final class PhortuneSubscriptionEditController extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$added = $request->getBool('added');
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$subscription->getURI());
$merchant = $subscription->getMerchant();
$account = $subscription->getAccount();
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
$header = id(new PHUIHeaderView())
->setHeader($subscription->getSubscriptionName());
$view_uri = $subscription->getURI();
$valid_methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->withStatuses(
array(
PhortunePaymentMethod::STATUS_ACTIVE,
))
->withMerchantPHIDs(array($merchant->getPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$valid_methods = mpull($valid_methods, null, 'getPHID');
$current_phid = $subscription->getDefaultPaymentMethodPHID();
$e_method = null;
if ($current_phid && empty($valid_methods[$current_phid])) {
$e_method = pht('Needs Update');
}
$errors = array();
if ($request->isFormPost()) {
$default_method_phid = $request->getStr('defaultPaymentMethodPHID');
if (!$default_method_phid) {
$default_method_phid = null;
$e_method = null;
} else if (empty($valid_methods[$default_method_phid])) {
$e_method = pht('Invalid');
if ($default_method_phid == $current_phid) {
$errors[] = pht(
'This subscription is configured to autopay with a payment method '.
'that has been deleted. Choose a valid payment method or disable '.
'autopay.');
} else {
$errors[] = pht('You must select a valid default payment method.');
}
}
// TODO: We should use transactions here, and move the validation logic
// inside the Editor.
if (!$errors) {
$subscription->setDefaultPaymentMethodPHID($default_method_phid);
$subscription->save();
return id(new AphrontRedirectResponse())
->setURI($view_uri);
}
}
// Add the option to disable autopay.
$disable_options = array(
'' => pht('(Disable Autopay)'),
);
// Don't require the user to make a valid selection if the current method
// has become invalid.
if ($current_phid && empty($valid_methods[$current_phid])) {
$current_options = array(
$current_phid => pht('<Deleted Payment Method>'),
);
} else {
$current_options = array();
}
// Add any available options.
$valid_options = mpull($valid_methods, 'getFullDisplayName', 'getPHID');
$options = $disable_options + $current_options + $valid_options;
$crumbs = $this->buildApplicationCrumbs();
$this->addAccountCrumb($crumbs, $account);
$crumbs->addTextCrumb(
pht('Subscription %d', $subscription->getID()),
$view_uri);
$crumbs->addTextCrumb(pht('Edit'));
$crumbs->setBorder(true);
$uri = $this->getApplicationURI($account->getID().'/card/new/');
$uri = new PhutilURI($uri);
$uri->replaceQueryParam('merchantID', $merchant->getID());
$uri->replaceQueryParam('subscriptionID', $subscription->getID());
$add_method_button = phutil_tag(
'a',
array(
'href' => $uri,
'class' => 'button button-grey',
),
pht('Add Payment Method...'));
$radio = id(new AphrontFormRadioButtonControl())
->setName('defaultPaymentMethodPHID')
->setLabel(pht('Autopay With'))
->setValue($current_phid)
->setError($e_method);
foreach ($options as $key => $value) {
$radio->addButton($key, $value, null);
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild($radio)
->appendChild(
id(new AphrontFormMarkupControl())
->setValue($add_method_button))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Changes'))
->addCancelButton($view_uri));
$box = id(new PHUIObjectBoxView())
->setUser($viewer)
->setHeaderText(pht('Subscription'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setFormErrors($errors)
->appendChild($form);
if ($added) {
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_SUCCESS)
->appendChild(pht('Payment method has been successfully added.'));
$box->setInfoView($info_view);
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Edit %s', $subscription->getSubscriptionName()))
->setHeaderIcon('fa-pencil');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$box,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

Some files were not shown because too many files have changed in this diff Show more