diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c661570dca..f30a40224d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,271 +1,277 @@ array( 'Aphront404Response' => 'aphront/response/404', 'AphrontAjaxResponse' => 'aphront/response/ajax', 'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration', 'AphrontController' => 'aphront/controller', 'AphrontDatabaseConnection' => 'storage/connection/base', 'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration', 'AphrontDefaultApplicationController' => 'aphront/default/controller', 'AphrontDialogResponse' => 'aphront/response/dialog', 'AphrontDialogView' => 'view/dialog', 'AphrontErrorView' => 'view/form/error', 'AphrontFileResponse' => 'aphront/response/file', 'AphrontFormCheckboxControl' => 'view/form/control/checkbox', 'AphrontFormControl' => 'view/form/control/base', 'AphrontFormFileControl' => 'view/form/control/file', 'AphrontFormMarkupControl' => 'view/form/control/markup', 'AphrontFormSelectControl' => 'view/form/control/select', 'AphrontFormStaticControl' => 'view/form/control/static', 'AphrontFormSubmitControl' => 'view/form/control/submit', 'AphrontFormTextAreaControl' => 'view/form/control/textarea', 'AphrontFormTextControl' => 'view/form/control/text', 'AphrontFormTokenizerControl' => 'view/form/control/tokenizer', 'AphrontFormView' => 'view/form/base', 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', 'AphrontPageView' => 'view/page/base', 'AphrontPanelView' => 'view/layout/panel', 'AphrontQueryConnectionException' => 'storage/exception/connection', 'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost', 'AphrontQueryCountException' => 'storage/exception/count', 'AphrontQueryException' => 'storage/exception/base', 'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing', 'AphrontQueryParameterException' => 'storage/exception/parameter', 'AphrontQueryRecoverableException' => 'storage/exception/recoverable', 'AphrontRedirectResponse' => 'aphront/response/redirect', 'AphrontRequest' => 'aphront/request', 'AphrontResponse' => 'aphront/response/base', 'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontTableView' => 'view/control/table', 'AphrontURIMapper' => 'aphront/mapper', 'AphrontView' => 'view/base', 'AphrontWebpageResponse' => 'aphront/response/webpage', 'CelerityAPI' => 'infratructure/celerity/api', 'CelerityResourceController' => 'infratructure/celerity/controller', 'CelerityResourceMap' => 'infratructure/celerity/map', 'CelerityStaticResourceResponse' => 'infratructure/celerity/response', 'ConduitAPIMethod' => 'applications/conduit/method/base', 'ConduitAPIRequest' => 'applications/conduit/protocol/request', 'ConduitAPI_conduit_connect_Method' => 'applications/conduit/method/conduit/connect', 'ConduitAPI_differential_creatediff_Method' => 'applications/conduit/method/differential/creatediff', 'ConduitAPI_differential_setdiffproperty_Method' => 'applications/conduit/method/differential/setdiffproperty', 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', 'ConduitAPI_user_find_Method' => 'applications/conduit/method/user/find', 'ConduitException' => 'applications/conduit/protocol/exception', 'DifferentialAction' => 'applications/differential/constants/action', 'DifferentialChangeType' => 'applications/differential/constants/changetype', 'DifferentialChangeset' => 'applications/differential/storage/changeset', 'DifferentialChangesetDetailView' => 'applications/differential/view/changesetdetailview', 'DifferentialChangesetListView' => 'applications/differential/view/changesetlistview', 'DifferentialChangesetParser' => 'applications/differential/parser/changeset', 'DifferentialChangesetViewController' => 'applications/differential/controller/changesetview', 'DifferentialController' => 'applications/differential/controller/base', 'DifferentialDAO' => 'applications/differential/storage/base', 'DifferentialDiff' => 'applications/differential/storage/diff', 'DifferentialDiffProperty' => 'applications/differential/storage/diffproperty', 'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents', 'DifferentialDiffViewController' => 'applications/differential/controller/diffview', 'DifferentialHunk' => 'applications/differential/storage/hunk', 'DifferentialLintStatus' => 'applications/differential/constants/lintstatus', 'DifferentialRevision' => 'applications/differential/storage/revision', 'DifferentialRevisionControlSystem' => 'applications/differential/constants/revisioncontrolsystem', 'DifferentialRevisionEditController' => 'applications/differential/controller/revisionedit', 'DifferentialRevisionListController' => 'applications/differential/controller/revisionlist', 'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus', 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', 'Javelin' => 'infratructure/javelin/api', 'LiskDAO' => 'storage/lisk/dao', 'PhabricatorConduitAPIController' => 'applications/conduit/controller/api', 'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog', 'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console', 'PhabricatorConduitController' => 'applications/conduit/controller/base', 'PhabricatorConduitDAO' => 'applications/conduit/storage/base', 'PhabricatorConduitLogController' => 'applications/conduit/controller/log', 'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog', 'PhabricatorController' => 'applications/base/controller/base', 'PhabricatorDirectoryCategory' => 'applications/directory/storage/category', 'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete', 'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit', 'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist', 'PhabricatorDirectoryController' => 'applications/directory/controller/base', 'PhabricatorDirectoryDAO' => 'applications/directory/storage/base', 'PhabricatorDirectoryItem' => 'applications/directory/storage/item', 'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete', 'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit', 'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist', 'PhabricatorDirectoryMainController' => 'applications/directory/controller/main', 'PhabricatorFile' => 'applications/files/storage/file', 'PhabricatorFileController' => 'applications/files/controller/base', 'PhabricatorFileDAO' => 'applications/files/storage/base', 'PhabricatorFileListController' => 'applications/files/controller/list', 'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob', 'PhabricatorFileURI' => 'applications/files/uri', 'PhabricatorFileUploadController' => 'applications/files/controller/upload', 'PhabricatorFileViewController' => 'applications/files/controller/view', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', 'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/base', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'applications/metamta/adapter/phpmailerlite', 'PhabricatorMetaMTAController' => 'applications/metamta/controller/base', 'PhabricatorMetaMTADAO' => 'applications/metamta/storage/base', 'PhabricatorMetaMTAListController' => 'applications/metamta/controller/list', 'PhabricatorMetaMTAMail' => 'applications/metamta/storage/mail', + 'PhabricatorMetaMTAMailingList' => 'applications/metamta/storage/mailinglist', + 'PhabricatorMetaMTAMailingListEditController' => 'applications/metamta/controller/mailinglistedit', + 'PhabricatorMetaMTAMailingListsController' => 'applications/metamta/controller/mailinglists', 'PhabricatorMetaMTASendController' => 'applications/metamta/controller/send', 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view', 'PhabricatorObjectHandle' => 'applications/phid/handle', 'PhabricatorObjectHandleData' => 'applications/phid/handle/data', 'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDController' => 'applications/phid/controller/base', 'PhabricatorPHIDDAO' => 'applications/phid/storage/base', 'PhabricatorPHIDListController' => 'applications/phid/controller/list', 'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup', 'PhabricatorPHIDType' => 'applications/phid/storage/type', 'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit', 'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist', 'PhabricatorPeopleController' => 'applications/people/controller/base', 'PhabricatorPeopleEditController' => 'applications/people/controller/edit', 'PhabricatorPeopleListController' => 'applications/people/controller/list', 'PhabricatorPeopleProfileController' => 'applications/people/controller/profile', 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/base', 'PhabricatorUser' => 'applications/people/storage/user', 'PhabricatorUserDAO' => 'applications/people/storage/base', ), 'function' => array( '_qsprintf_check_scalar_type' => 'storage/qsprintf', '_qsprintf_check_type' => 'storage/qsprintf', 'celerity_generate_unique_node_id' => 'infratructure/celerity/api', 'celerity_register_resource_map' => 'infratructure/celerity/map', 'javelin_render_tag' => 'infratructure/javelin/markup', 'qsprintf' => 'storage/qsprintf', 'queryfx' => 'storage/queryfx', 'queryfx_all' => 'storage/queryfx', 'queryfx_one' => 'storage/queryfx', 'require_celerity_resource' => 'infratructure/celerity/api', 'vqsprintf' => 'storage/qsprintf', 'vqueryfx' => 'storage/queryfx', 'xsprintf_query' => 'storage/qsprintf', ), 'requires_class' => array( 'Aphront404Response' => 'AphrontResponse', 'AphrontAjaxResponse' => 'AphrontResponse', 'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration', 'AphrontDefaultApplicationController' => 'AphrontController', 'AphrontDialogResponse' => 'AphrontResponse', 'AphrontDialogView' => 'AphrontView', 'AphrontErrorView' => 'AphrontView', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormCheckboxControl' => 'AphrontFormControl', 'AphrontFormControl' => 'AphrontView', 'AphrontFormFileControl' => 'AphrontFormControl', 'AphrontFormMarkupControl' => 'AphrontFormControl', 'AphrontFormSelectControl' => 'AphrontFormControl', 'AphrontFormStaticControl' => 'AphrontFormControl', 'AphrontFormSubmitControl' => 'AphrontFormControl', 'AphrontFormTextAreaControl' => 'AphrontFormControl', 'AphrontFormTextControl' => 'AphrontFormControl', 'AphrontFormTokenizerControl' => 'AphrontFormControl', 'AphrontFormView' => 'AphrontView', 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', 'AphrontPageView' => 'AphrontView', 'AphrontPanelView' => 'AphrontView', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontRedirectResponse' => 'AphrontResponse', 'AphrontSideNavView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontWebpageResponse' => 'AphrontResponse', 'CelerityResourceController' => 'AphrontController', 'ConduitAPI_conduit_connect_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_creatediff_Method' => 'ConduitAPIMethod', 'ConduitAPI_differential_setdiffproperty_Method' => 'ConduitAPIMethod', 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', 'ConduitAPI_user_find_Method' => 'ConduitAPIMethod', 'DifferentialChangeset' => 'DifferentialDAO', 'DifferentialChangesetDetailView' => 'AphrontView', 'DifferentialChangesetListView' => 'AphrontView', 'DifferentialChangesetViewController' => 'DifferentialController', 'DifferentialController' => 'PhabricatorController', 'DifferentialDAO' => 'PhabricatorLiskDAO', 'DifferentialDiff' => 'DifferentialDAO', 'DifferentialDiffProperty' => 'DifferentialDAO', 'DifferentialDiffTableOfContentsView' => 'AphrontView', 'DifferentialDiffViewController' => 'DifferentialController', 'DifferentialHunk' => 'DifferentialDAO', 'DifferentialRevision' => 'DifferentialDAO', 'DifferentialRevisionEditController' => 'DifferentialController', 'DifferentialRevisionListController' => 'DifferentialController', 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO', 'PhabricatorConduitConsoleController' => 'PhabricatorConduitController', 'PhabricatorConduitController' => 'PhabricatorController', 'PhabricatorConduitDAO' => 'PhabricatorLiskDAO', 'PhabricatorConduitLogController' => 'PhabricatorConduitController', 'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO', 'PhabricatorController' => 'AphrontController', 'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryController' => 'PhabricatorController', 'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController', 'PhabricatorFile' => 'PhabricatorFileDAO', 'PhabricatorFileController' => 'PhabricatorController', 'PhabricatorFileDAO' => 'PhabricatorLiskDAO', 'PhabricatorFileListController' => 'PhabricatorFileController', 'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO', 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileViewController' => 'PhabricatorFileController', 'PhabricatorLiskDAO' => 'LiskDAO', 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter', 'PhabricatorMetaMTAController' => 'PhabricatorController', 'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO', 'PhabricatorMetaMTAListController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAMail' => 'PhabricatorMetaMTADAO', + 'PhabricatorMetaMTAMailingList' => 'PhabricatorMetaMTADAO', + 'PhabricatorMetaMTAMailingListEditController' => 'PhabricatorMetaMTAController', + 'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController', 'PhabricatorPHID' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController', 'PhabricatorPHIDController' => 'PhabricatorController', 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO', 'PhabricatorPHIDListController' => 'PhabricatorPHIDController', 'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController', 'PhabricatorPHIDType' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController', 'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleListController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorStandardPageView' => 'AphrontPageView', 'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', 'PhabricatorUser' => 'PhabricatorUserDAO', 'PhabricatorUserDAO' => 'PhabricatorLiskDAO', ), 'requires_interface' => array( ), )); diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index c61eec3dc2..977aa14cea 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -1,151 +1,154 @@ array( '$' => 'RepositoryListController', 'new/$' => 'RepositoryEditController', 'edit/(?\d+)/$' => 'RepositoryEditController', 'delete/(?\d+)/$' => 'RepositoryDeleteController', ), '/' => array( '$' => 'PhabricatorDirectoryMainController', ), '/directory/' => array( 'item/$' => 'PhabricatorDirectoryItemListController', 'item/edit/(?:(?\d+)/)?$' => 'PhabricatorDirectoryItemEditController', 'item/delete/(?\d+)/' => 'PhabricatorDirectoryItemDeleteController', 'category/$' => 'PhabricatorDirectoryCategoryListController', 'category/edit/(?:(?\d+)/)?$' => 'PhabricatorDirectoryCategoryEditController', 'category/delete/(?\d+)/' => 'PhabricatorDirectoryCategoryDeleteController', ), '/file/' => array( '$' => 'PhabricatorFileListController', 'upload/$' => 'PhabricatorFileUploadController', '(?info)/(?[^/]+)/' => 'PhabricatorFileViewController', '(?view)/(?[^/]+)/' => 'PhabricatorFileViewController', '(?download)/(?[^/]+)/' => 'PhabricatorFileViewController', ), '/phid/' => array( '$' => 'PhabricatorPHIDLookupController', 'list/$' => 'PhabricatorPHIDListController', 'type/$' => 'PhabricatorPHIDTypeListController', 'type/edit/(?:(?\d+)/)?$' => 'PhabricatorPHIDTypeEditController', 'new/$' => 'PhabricatorPHIDAllocateController', ), '/people/' => array( '$' => 'PhabricatorPeopleListController', 'edit/(?:(?\w+)/)?$' => 'PhabricatorPeopleEditController', ), '/p/(?\w+)/$' => 'PhabricatorPeopleProfileController', '/conduit/' => array( '$' => 'PhabricatorConduitConsoleController', 'method/(?[^/]+)$' => 'PhabricatorConduitConsoleController', 'log/$' => 'PhabricatorConduitLogController', ), '/api/(?[^/]+)$' => 'PhabricatorConduitAPIController', '/differential/' => array( '$' => 'DifferentialRevisionListController', 'diff/(?\d+)/$' => 'DifferentialDiffViewController', 'changeset/(?\d+)/$' => 'DifferentialChangesetViewController', 'revision/edit/(?:(?\d+)/)?$' => 'DifferentialRevisionEditController', ), '/res/' => array( '(?[a-f0-9]{8})/(?.+\.(?:css|js))$' => 'CelerityResourceController', ), '/typeahead/' => array( 'common/(?\w+)/$' => 'PhabricatorTypeaheadCommonDatasourceController', ), '/mail/' => array( '$' => 'PhabricatorMetaMTAListController', 'send/$' => 'PhabricatorMetaMTASendController', 'view/(?\d+)/$' => 'PhabricatorMetaMTAViewController', - ) + 'lists/$' => 'PhabricatorMetaMTAMailingListsController', + 'lists/edit/(?:(?\d+)/)?$' + => 'PhabricatorMetaMTAMailingListEditController', + ), ); } public function buildRequest() { $request = new AphrontRequest($this->getHost(), $this->getPath()); $request->setRequestData($_GET + $_POST); return $request; } public function handleException(Exception $ex) { $class = phutil_escape_html(get_class($ex)); $message = phutil_escape_html($ex->getMessage()); $content = '
'. '

Unhandled Exception "'.$class.'": '.$message.'

'. ''.phutil_escape_html((string)$ex).''. '
'; $view = new PhabricatorStandardPageView(); $view->appendChild($content); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } public function willSendResponse(AphrontResponse $response) { $request = $this->getRequest(); if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->appendChild( '
'. $response->buildResponseString(). '
'); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } } return $response; } } diff --git a/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php b/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php index aee09ab14d..2f68d22a9c 100644 --- a/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php +++ b/src/applications/metamta/controller/base/PhabricatorMetaMTAController.php @@ -1,34 +1,46 @@ setApplicationName('MetaMTA'); $page->setBaseURI('/mail/'); $page->setTitle(idx($data, 'title')); + $page->setTabs( + array( + 'queue' => array( + 'name' => 'Mail Queue', + 'href' => '/mail/', + ), + 'lists' => array( + 'name' => 'Mailing Lists', + 'href' => '/mail/lists/', + ), + ), + idx($data, 'tab')); $page->setGlyph("@"); $page->appendChild($view); $response = new AphrontWebpageResponse(); return $response->setContent($page->render()); } } diff --git a/src/applications/metamta/controller/list/PhabricatorMetaMTAListController.php b/src/applications/metamta/controller/list/PhabricatorMetaMTAListController.php index 1d3021e268..fd0b6bd60c 100644 --- a/src/applications/metamta/controller/list/PhabricatorMetaMTAListController.php +++ b/src/applications/metamta/controller/list/PhabricatorMetaMTAListController.php @@ -1,77 +1,78 @@ loadAllWhere( '1 = 1 ORDER BY id DESC LIMIT 100'); $rows = array(); foreach ($mails as $mail) { $rows[] = array( PhabricatorMetaMTAMail::getReadableStatus($mail->getStatus()), $mail->getRetryCount(), ($mail->getNextRetry() - time()).' s', date('Y-m-d g:i:s A', $mail->getDateCreated()), (time() - $mail->getDateModified()).' s', phutil_escape_html($mail->getSubject()), phutil_render_tag( 'a', array( 'class' => 'button small grey', 'href' => '/mail/view/'.$mail->getID().'/', ), 'View'), ); } $table = new AphrontTableView($rows); $table->setHeaders( array( 'Status', 'Retry', 'Next', 'Created', 'Updated', 'Subject', '', )); $table->setColumnClasses( array( null, null, null, null, null, 'wide', 'action', )); $panel = new AphrontPanelView(); $panel->appendChild($table); $panel->setHeader('MetaMTA Messages'); $panel->setCreateButton('Send New Message', '/mail/send/'); return $this->buildStandardPageResponse( $panel, array( 'title' => 'MetaMTA', + 'tab' => 'queue', )); } } diff --git a/src/applications/metamta/controller/list/__init__.php b/src/applications/metamta/controller/list/__init__.php index 520ca62a9d..79f3117912 100644 --- a/src/applications/metamta/controller/list/__init__.php +++ b/src/applications/metamta/controller/list/__init__.php @@ -1,17 +1,18 @@ id = idx($data, 'id'); + } + + public function processRequest() { + + if ($this->id) { + $list = id(new PhabricatorMetaMTAMailingList())->load($this->id); + if (!$list) { + return new Aphront404Response(); + } + } else { + $list = new PhabricatorMetaMTAMailingList(); + } + + $e_email = true; + $errors = array(); + + $request = $this->getRequest(); + if ($request->isFormPost()) { + $list->setName($request->getStr('name')); + $list->setEmail($request->getStr('email')); + $list->setURI($request->getStr('uri')); + + if (!strlen($list->getEmail())) { + $e_email = 'Required'; + $errors[] = 'Email is required.'; + } + + if (!$errors) { + $list->save(); + return id(new AphrontRedirectResponse()) + ->setURI('/mail/lists/'); + } + } + + $error_view = null; + if ($errors) { + $error_view = id(new AphrontErrorView()) + ->setTitle('Form Errors') + ->setErrors($errors); + } + + $form = new AphrontFormView(); + if ($list->getID()) { + $form->setAction('/mail/lists/edit/'.$list->getID().'/'); + } else { + $form->setAction('/mail/lists/edit/'); + } + + $form + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Email') + ->setName('email') + ->setValue($list->getEmail()) + ->setError($e_email)) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Name') + ->setName('name') + ->setValue($list->getName())) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('URI') + ->setName('uri') + ->setValue($list->getURI())) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('ID') + ->setValue(nonempty($list->getID(), '-'))) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('PHID') + ->setValue(nonempty($list->getPHID(), '-'))) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Save') + ->addCancelButton('/mail/lists/')); + + $panel = new AphrontPanelView(); + if ($list->getID()) { + $panel->setHeader('Edit Mailing List'); + } else { + $panel->setHeader('Create New Mailing List'); + } + + $panel->appendChild($form); + $panel->setWidth(AphrontPanelView::WIDTH_FORM); + + return $this->buildStandardPageResponse( + array($error_view, $panel), + array( + 'title' => 'Edit Mailing List', + )); + } + +} diff --git a/src/applications/metamta/controller/send/__init__.php b/src/applications/metamta/controller/mailinglistedit/__init__.php similarity index 71% copy from src/applications/metamta/controller/send/__init__.php copy to src/applications/metamta/controller/mailinglistedit/__init__.php index d606f64d0f..1469d2bd61 100644 --- a/src/applications/metamta/controller/send/__init__.php +++ b/src/applications/metamta/controller/mailinglistedit/__init__.php @@ -1,19 +1,21 @@ loadAllWhere( + $lists = id(new PhabricatorMetaMTAMailingList())->loadAllWhere( '1 = 1 ORDER BY id DESC LIMIT 100'); $rows = array(); - foreach ($mails as $mail) { + foreach ($lists as $list) { $rows[] = array( - PhabricatorMetaMTAMail::getReadableStatus($mail->getStatus()), - $mail->getRetryCount(), - ($mail->getNextRetry() - time()).' s', - date('Y-m-d g:i:s A', $mail->getDateCreated()), - (time() - $mail->getDateModified()).' s', - phutil_escape_html($mail->getSubject()), + phutil_escape_html($list->getPHID()), + phutil_escape_html($list->getEmail()), + phutil_escape_html($list->getName()), phutil_render_tag( 'a', array( - 'class' => 'button small grey', - 'href' => '/mail/view/'.$mail->getID().'/', + 'class' => 'button grey small', + 'href' => '/mail/lists/edit/'.$list->getID().'/', ), - 'View'), + 'Edit'), ); } $table = new AphrontTableView($rows); $table->setHeaders( array( - 'Status', - 'Retry', - 'Next', - 'Created', - 'Updated', - 'Subject', + 'PHID', + 'Email', + 'Name', '', )); $table->setColumnClasses( array( - null, - null, - null, null, null, 'wide', 'action', )); $panel = new AphrontPanelView(); $panel->appendChild($table); - $panel->setHeader('MetaMTA Messages'); - $panel->setCreateButton('Send New Message', '/mail/send/'); + $panel->setHeader('Mailing Lists'); + $panel->setCreateButton('Add New List', '/mail/lists/edit/'); return $this->buildStandardPageResponse( $panel, array( - 'title' => 'MetaMTA', + 'title' => 'Mailing Lists', + 'tab' => 'lists', )); } } diff --git a/src/applications/metamta/controller/list/__init__.php b/src/applications/metamta/controller/mailinglists/__init__.php similarity index 75% copy from src/applications/metamta/controller/list/__init__.php copy to src/applications/metamta/controller/mailinglists/__init__.php index 520ca62a9d..119d4a1842 100644 --- a/src/applications/metamta/controller/list/__init__.php +++ b/src/applications/metamta/controller/mailinglists/__init__.php @@ -1,17 +1,18 @@ getRequest(); if ($request->isFormPost()) { $mail = new PhabricatorMetaMTAMail(); $mail->addTos($request->getArr('to')); $mail->addCCs($request->getArr('cc')); $mail->setSubject($request->getStr('subject')); $mail->setBody($request->getStr('body')); // TODO! // $mail->setFrom($request->getViewerContext()->getUserID()); $mail->setSimulatedFailureCount($request->getInt('failures')); $mail->setIsHTML($request->getInt('html')); $mail->save(); if ($request->getInt('immediately')) { $mail->sendNow( $force_send = true, new PhabricatorMailImplementationPHPMailerLiteAdapter()); } return id(new AphrontRedirectResponse()) ->setURI('/mail/view/'.$mail->getID().'/'); } $failure_caption = "Enter a number to simulate that many consecutive send failures before ". "really attempting to deliver via the underlying MTA."; $form = new AphrontFormView(); $form->setAction('/mail/send/'); $form ->appendChild( '

This form will send a normal '. 'email using MetaMTA as a transport mechanism.

') ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel('To') ->setName('to') - ->setDatasource('/typeahead/common/user/')) + ->setDatasource('/typeahead/common/mailable/')) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel('CC') ->setName('cc') - ->setDatasource('/typeahead/common/user/')) + ->setDatasource('/typeahead/common/mailable/')) ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Subject') ->setName('subject')) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Body') ->setName('body')) ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Simulate Failures') ->setName('failures') ->setCaption($failure_caption)) ->appendChild( id(new AphrontFormCheckboxControl()) ->setLabel('HTML') ->addCheckbox('html', '1', 'Send as HTML email.')) ->appendChild( id(new AphrontFormCheckboxControl()) ->setLabel('Send Now') ->addCheckbox( 'immediately', '1', 'Send immediately, not via MetaMTA background script.')) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Send Mail')); $panel = new AphrontPanelView(); $panel->setHeader('Send Email'); $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_WIDE); return $this->buildStandardPageResponse( $panel, array( 'title' => 'Send Mail', )); } } diff --git a/src/applications/metamta/controller/send/__init__.php b/src/applications/metamta/controller/send/__init__.php index d606f64d0f..24025389ef 100644 --- a/src/applications/metamta/controller/send/__init__.php +++ b/src/applications/metamta/controller/send/__init__.php @@ -1,19 +1,20 @@ setApplicationName('MetaMTA'); - $page->setBaseURI('/mail/'); - $page->setTitle(idx($data, 'title')); - $page->setGlyph("@"); - $page->appendChild($view); + protected $name; + protected $phid; + protected $email; + protected $uri; - $response = new AphrontWebpageResponse(); - return $response->setContent($page->render()); + public function generatePHID() { + return PhabricatorPHID::generateNewPHID(self::TYPE_MAILING_LIST); + } + + public function getConfiguration() { + return array( + self::CONFIG_AUX_PHID => true, + ) + parent::getConfiguration(); } } diff --git a/src/applications/metamta/storage/mailinglist/__init__.php b/src/applications/metamta/storage/mailinglist/__init__.php new file mode 100644 index 0000000000..6ca6793cb3 --- /dev/null +++ b/src/applications/metamta/storage/mailinglist/__init__.php @@ -0,0 +1,13 @@ +phids = $phids; } public function loadHandles() { $types = array(); foreach ($this->phids as $phid) { $type = $this->lookupType($phid); $types[$type][] = $phid; } $handles = array(); foreach ($types as $type => $phids) { switch ($type) { case 'USER': $class = 'PhabricatorUser'; PhutilSymbolLoader::loadClass($class); $object = newv($class, array()); $users = $object->loadAllWhere('phid IN (%Ls)', $phids); $users = mpull($users, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); if (empty($users[$phid])) { $handle->setType(self::TYPE_UNKNOWN); $handle->setName('Unknown User'); } else { $user = $users[$phid]; $handle->setType($type); $handle->setName($user->getUsername()); $handle->setURI('/p/'.$user->getUsername().'/'); $handle->setEmail($user->getEmail()); } $handles[$phid] = $handle; } break; + case 'MLST': + $class = 'PhabricatorMetaMTAMailingList'; + + PhutilSymbolLoader::loadClass($class); + $object = newv($class, array()); + + $lists = $object->loadAllWhere('phid IN (%Ls)', $phids); + $lists = mpull($lists, null, 'getPHID'); + + foreach ($phids as $phid) { + $handle = new PhabricatorObjectHandle(); + $handle->setPHID($phid); + if (empty($lists[$phid])) { + $handle->setType(self::TYPE_UNKNOWN); + $handle->setName('Unknown Mailing List'); + } else { + $list = $lists[$phid]; + $handle->setType($type); + $handle->setEmail($list->getEmail()); + $handle->setName($list->getName()); + $handle->setURI($list->getURI()); + } + $handles[$phid] = $handle; + } + break; case 'FILE': $class = 'PhabricatorFile'; PhutilSymbolLoader::loadClass($class); $object = newv($class, array()); $files = $object->loadAllWhere('phid IN (%Ls)', $phids); $files = mpull($files, null, 'getPHID'); foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setPHID($phid); if (empty($files[$phid])) { $handle->setType(self::TYPE_UNKNOWN); $handle->setName('Unknown File'); } else { $file = $files[$phid]; $handle->setType($type); $handle->setName($file->getName()); $handle->setURI($file->getViewURI()); } $handles[$phid] = $handle; } break; default: foreach ($phids as $phid) { $handle = new PhabricatorObjectHandle(); $handle->setType($type); $handle->setPHID($phid); $handle->setName('Unknown Object'); $handles[$phid] = $handle; } break; } } return $handles; } private function lookupType($phid) { $matches = null; if (preg_match('/^PHID-([^-]{4})-/', $phid, $matches)) { return $matches[1]; } return self::TYPE_UNKNOWN; } } diff --git a/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php b/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php index 1758a1a948..acef3d6fdb 100644 --- a/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php +++ b/src/applications/typeahead/controller/common/PhabricatorTypeaheadCommonDatasourceController.php @@ -1,45 +1,69 @@ type = $data['type']; } public function processRequest() { + $need_users = false; + $need_lists = false; + switch ($this->type) { + case 'users': + $need_users = true; + break; + case 'mailable': + $need_users = true; + $need_lists = true; + break; + } + $data = array(); - $users = id(new PhabricatorUser())->loadAll(); - $data = array(); - foreach ($users as $user) { - $data[] = array( - $user->getUsername().' ('.$user->getRealName().')', - '/p/'.$user->getUsername(), - $user->getPHID(), - ); + if ($need_users) { + $users = id(new PhabricatorUser())->loadAll(); + foreach ($users as $user) { + $data[] = array( + $user->getUsername().' ('.$user->getRealName().')', + '/p/'.$user->getUsername(), + $user->getPHID(), + ); + } + } + + if ($need_lists) { + $lists = id(new PhabricatorMetaMTAMailingList())->loadAll(); + foreach ($lists as $list) { + $data[] = array( + $list->getEmail(), + $list->getURI(), + $list->getPHID(), + ); + } } return id(new AphrontAjaxResponse()) ->setContent($data); } } diff --git a/src/applications/typeahead/controller/common/__init__.php b/src/applications/typeahead/controller/common/__init__.php index 34f6ae27c4..5a38ece7c8 100644 --- a/src/applications/typeahead/controller/common/__init__.php +++ b/src/applications/typeahead/controller/common/__init__.php @@ -1,16 +1,17 @@