1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Modernize Macro application

Summary: Adds feed, email, notifications, comments, partial editing, subscriptions, enable/disable, flags and crumbs to Macro.

Test Plan:
{F26839}
{F26840}
{F26841}
{F26842}
{F26843}
{F26844}
{F26845}

Reviewers: vrana, btrahan, chad

Reviewed By: vrana

CC: aran

Maniphest Tasks: T2157, T175, T2104

Differential Revision: https://secure.phabricator.com/D4141
This commit is contained in:
epriestley 2012-12-11 14:01:03 -08:00
parent 4081579e79
commit ba7723d905
33 changed files with 1233 additions and 117 deletions

View file

@ -383,13 +383,20 @@ return array(
// distinguish between testing and development installs, for example.
'metamta.maniphest.subject-prefix' => '[Maniphest]',
// See 'metamta.pholio.reply-handler-domain'. This does the same thing, but
// See 'metamta.maniphest.reply-handler-domain'. This does the same thing, but
// affects Pholio.
'metamta.pholio.reply-handler-domain' => null,
// Prefix prepended to mail sent by Pholio.
'metamta.pholio.subject-prefix' => '[Pholio]',
// See 'metamta.maniphest.reply-handler-domain'. This does the same thing, but
// affects Macro.
'metamta.macro.reply-handler-domain' => null,
// Prefix prepended to mail sent by Macro.
'metamta.macro.subject-prefix' => '[Macro]',
// See 'metamta.maniphest.reply-handler-domain'. This does the same thing,
// but allows email replies via Differential.
'metamta.differential.reply-handler-domain' => null,

View file

@ -0,0 +1,54 @@
CREATE TABLE {$NAMESPACE}_file.macro_transaction (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
commentPHID VARCHAR(64) COLLATE utf8_bin,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin,
oldValue LONGTEXT NOT NULL COLLATE utf8_bin,
newValue LONGTEXT NOT NULL COLLATE utf8_bin,
contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
KEY `key_object` (objectPHID)
) ENGINE=InnoDB, COLLATE utf8_general_ci;
CREATE TABLE {$NAMESPACE}_file.macro_transaction_comment (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
transactionPHID VARCHAR(64) COLLATE utf8_bin,
authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
commentVersion INT UNSIGNED NOT NULL,
content LONGTEXT NOT NULL COLLATE utf8_bin,
contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
isDeleted BOOL NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (phid),
UNIQUE KEY `key_version` (transactionPHID, commentVersion)
) ENGINE=InnoDB, COLLATE utf8_general_ci;
ALTER TABLE {$NAMESPACE}_file.file_imagemacro
ADD dateCreated INT UNSIGNED NOT NULL;
ALTER TABLE {$NAMESPACE}_file.file_imagemacro
ADD dateModified INT UNSIGNED NOT NULL;
ALTER TABLE {$NAMESPACE}_file.file_imagemacro
ADD phid VARCHAR(64) NOT NULL COLLATE utf8_bin AFTER id;
ALTER TABLE {$NAMESPACE}_file.file_imagemacro
ADD isDisabled BOOL NOT NULL;
ALTER TABLE {$NAMESPACE}_file.file_imagemacro
ADD KEY `key_disabled` (isDisabled);

View file

@ -0,0 +1,18 @@
<?php
echo "Giving image macros PHIDs";
foreach (new LiskMigrationIterator(new PhabricatorFileImageMacro()) as $macro) {
if ($macro->getPHID()) {
continue;
}
echo ".";
queryfx(
$macro->establishConnection('r'),
'UPDATE %T SET phid = %s WHERE id = %d',
$macro->getTableName(),
$macro->generatePHID(),
$macro->getID());
}
echo "\nDone.\n";

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_file.file_imagemacro
ADD UNIQUE KEY `key_phid` (phid);

View file

@ -604,6 +604,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionEditor.php',
'PhabricatorApplicationTransactionFeedStory' => 'applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php',
'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php',
'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php',
'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php',
'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php',
@ -829,10 +830,18 @@ phutil_register_library_map(array(
'PhabricatorLoginController' => 'applications/auth/controller/PhabricatorLoginController.php',
'PhabricatorLoginValidateController' => 'applications/auth/controller/PhabricatorLoginValidateController.php',
'PhabricatorLogoutController' => 'applications/auth/controller/PhabricatorLogoutController.php',
'PhabricatorMacroCommentController' => 'applications/macro/controller/PhabricatorMacroCommentController.php',
'PhabricatorMacroController' => 'applications/macro/controller/PhabricatorMacroController.php',
'PhabricatorMacroDeleteController' => 'applications/macro/controller/PhabricatorMacroDeleteController.php',
'PhabricatorMacroDisableController' => 'applications/macro/controller/PhabricatorMacroDisableController.php',
'PhabricatorMacroEditController' => 'applications/macro/controller/PhabricatorMacroEditController.php',
'PhabricatorMacroEditor' => 'applications/macro/editor/PhabricatorMacroEditor.php',
'PhabricatorMacroListController' => 'applications/macro/controller/PhabricatorMacroListController.php',
'PhabricatorMacroReplyHandler' => 'applications/macro/mail/PhabricatorMacroReplyHandler.php',
'PhabricatorMacroTransaction' => 'applications/macro/storage/PhabricatorMacroTransaction.php',
'PhabricatorMacroTransactionComment' => 'applications/macro/storage/PhabricatorMacroTransactionComment.php',
'PhabricatorMacroTransactionQuery' => 'applications/macro/query/PhabricatorMacroTransactionQuery.php',
'PhabricatorMacroTransactionType' => 'applications/macro/constants/PhabricatorMacroTransactionType.php',
'PhabricatorMacroViewController' => 'applications/macro/controller/PhabricatorMacroViewController.php',
'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php',
'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php',
'PhabricatorMailImplementationPHPMailerAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerAdapter.php',
@ -1869,6 +1878,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionEditor' => 'PhabricatorEditor',
'PhabricatorApplicationTransactionFeedStory' => 'PhabricatorFeedStory',
'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationTransactionView' => 'AphrontView',
'PhabricatorApplicationTransactions' => 'PhabricatorApplication',
'PhabricatorApplicationUIExamples' => 'PhabricatorApplication',
'PhabricatorApplicationsListController' => 'PhabricatorController',
@ -2024,7 +2034,11 @@ phutil_register_library_map(array(
'PhabricatorFileDataController' => 'PhabricatorFileController',
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
'PhabricatorFileImageMacro' => 'PhabricatorFileDAO',
'PhabricatorFileImageMacro' =>
array(
0 => 'PhabricatorFileDAO',
1 => 'PhabricatorSubscribableInterface',
),
'PhabricatorFileInfoController' => 'PhabricatorFileController',
'PhabricatorFileLinkListView' => 'AphrontView',
'PhabricatorFileLinkView' => 'AphrontView',
@ -2080,10 +2094,17 @@ phutil_register_library_map(array(
'PhabricatorLoginController' => 'PhabricatorAuthController',
'PhabricatorLoginValidateController' => 'PhabricatorAuthController',
'PhabricatorLogoutController' => 'PhabricatorAuthController',
'PhabricatorMacroCommentController' => 'PhabricatorMacroController',
'PhabricatorMacroController' => 'PhabricatorController',
'PhabricatorMacroDeleteController' => 'PhabricatorMacroController',
'PhabricatorMacroDisableController' => 'PhabricatorMacroController',
'PhabricatorMacroEditController' => 'PhabricatorMacroController',
'PhabricatorMacroEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorMacroListController' => 'PhabricatorMacroController',
'PhabricatorMacroReplyHandler' => 'PhabricatorMailReplyHandler',
'PhabricatorMacroTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorMacroTransactionComment' => 'PhabricatorApplicationTransactionComment',
'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorMacroViewController' => 'PhabricatorMacroController',
'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter',
'PhabricatorMailImplementationPHPMailerAdapter' => 'PhabricatorMailImplementationAdapter',
'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',

View file

@ -161,6 +161,11 @@ final class AphrontRequest {
return array_key_exists($name, $this->requestData);
}
final public function getFileExists($name) {
return isset($_FILES[$name]) &&
(idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE);
}
final public function isHTTPPost() {
return ($_SERVER['REQUEST_METHOD'] == 'POST');
}

View file

@ -26,8 +26,11 @@ final class PhabricatorApplicationMacro extends PhabricatorApplication {
return array(
'/macro/' => array(
'' => 'PhabricatorMacroListController',
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'PhabricatorMacroEditController',
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroDeleteController',
'create/' => 'PhabricatorMacroEditController',
'view/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroViewController',
'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroCommentController',
'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroEditController',
'disable/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroDisableController',
),
);
}

View file

@ -0,0 +1,9 @@
<?php
final class PhabricatorMacroTransactionType {
const TYPE_NAME = 'macro:name';
const TYPE_DISABLED = 'macro:disabled';
const TYPE_FILE = 'macro:file';
}

View file

@ -0,0 +1,49 @@
<?php
final class PhabricatorMacroCommentController
extends PhabricatorMacroController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if (!$request->isFormPost()) {
return new Aphront400Response();
}
$macro = id(new PhabricatorFileImageMacro())->load($this->id);
if (!$macro) {
return new Aphront404Response();
}
$view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/');
$xactions = array();
$xactions[] = id(new PhabricatorMacroTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new PhabricatorMacroTransactionComment())
->setContent($request->getStr('comment')));
$editor = id(new PhabricatorMacroEditor())
->setActor($user)
->setContentSource(
PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
)))
->applyTransactions($macro, $xactions);
return id(new AphrontRedirectResponse())
->setURI($view_uri);
}
}

View file

@ -3,19 +3,39 @@
abstract class PhabricatorMacroController
extends PhabricatorController {
protected function buildSideNavView(PhabricatorFileImageMacro $macro = null) {
protected function buildSideNavView($for_app = false, $has_search = false) {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
$nav->addLabel('Create');
$nav->addFilter('edit', 'Create Macro');
$nav->addSpacer();
if ($for_app) {
$nav->addLabel('Create');
$nav->addFilter('', 'Create Macro', $this->getApplicationURI('/create/'));
}
$nav->addLabel('Macros');
$nav->addFilter('', 'All Macros');
$nav->addFilter('/', 'All Macros');
if ($has_search) {
$nav->addFilter('search', 'Search', $this->getRequest()->getRequestURI());
}
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView($for_app = true)->getMenu();
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PhabricatorMenuItemView())
->setName(pht('Create Macro'))
->setHref($this->getApplicationURI('/create/'))
->setIcon('create'));
return $crumbs;
}
}

View file

@ -1,42 +0,0 @@
<?php
final class PhabricatorMacroDeleteController
extends PhabricatorMacroController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$macro = id(new PhabricatorFileImageMacro())->load($this->id);
if (!$macro) {
return new Aphront404Response();
}
$request = $this->getRequest();
if ($request->isDialogFormPost()) {
$macro->delete();
return id(new AphrontRedirectResponse())->setURI(
$this->getApplicationURI());
}
$dialog = new AphrontDialogView();
$dialog
->setUser($request->getUser())
->setTitle('Really delete macro?')
->appendChild(
'<p>Really delete the much-beloved image macro "'.
phutil_escape_html($macro->getName()).'"? It will be sorely missed.'.
'</p>')
->setSubmitURI($this->getApplicationURI('/delete/'.$this->id.'/'))
->addSubmitButton('Delete')
->addCancelButton($this->getApplicationURI());
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -0,0 +1,55 @@
<?php
final class PhabricatorMacroDisableController
extends PhabricatorMacroController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$macro = id(new PhabricatorFileImageMacro())->load($this->id);
if (!$macro) {
return new Aphront404Response();
}
$view_uri = $this->getApplicationURI('/view/'.$this->id.'/');
if ($request->isDialogFormPost() || $macro->getIsDisabled()) {
$xaction = id(new PhabricatorMacroTransaction())
->setTransactionType(PhabricatorMacroTransactionType::TYPE_DISABLED)
->setNewValue($macro->getIsDisabled() ? 0 : 1);
$editor = id(new PhabricatorMacroEditor())
->setActor($user)
->setContentSource(
PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
)))
->applyTransactions($macro, array($xaction));
return id(new AphrontRedirectResponse())->setURI($view_uri);
}
$dialog = new AphrontDialogView();
$dialog
->setUser($request->getUser())
->setTitle('Really disable macro?')
->appendChild(
'<p>Really disable the much-beloved image macro "'.
phutil_escape_html($macro->getName()).'"? It will be sorely missed.'.
'</p>')
->setSubmitURI($this->getApplicationURI('/disable/'.$this->id.'/'))
->addSubmitButton('Disable')
->addCancelButton($view_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -22,56 +22,96 @@ final class PhabricatorMacroEditController
$errors = array();
$e_name = true;
$e_file = true;
$file = null;
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
$original = clone $macro;
$macro->setName($request->getStr('name'));
$new_name = null;
if ($request->getBool('name_form') || !$macro->getID()) {
$new_name = $request->getStr('name');
if (!strlen($macro->getName())) {
$errors[] = 'Macro name is required.';
$e_name = 'Required';
} else if (!preg_match('/^[a-z0-9_-]{3,}$/', $macro->getName())) {
$errors[] = 'Macro must be at least three characters long and contain '.
'only lowercase letters, digits, hyphen and underscore.';
$e_name = 'Invalid';
} else {
$e_name = null;
$macro->setName($new_name);
if (!strlen($macro->getName())) {
$errors[] = 'Macro name is required.';
$e_name = 'Required';
} else if (!preg_match('/^[a-z0-9_-]{3,}$/', $macro->getName())) {
$errors[] = 'Macro must be at least three characters long and '.
'contain only lowercase letters, digits, hyphen and '.
'underscore.';
$e_name = 'Invalid';
} else {
$e_name = null;
}
}
if (!$errors) {
$file = null;
if ($request->getFileExists('file')) {
$file = PhabricatorFile::newFromPHPUpload(
idx($_FILES, 'file'),
$_FILES['file'],
array(
'name' => $request->getStr('name'),
'authorPHID' => $user->getPHID(),
));
$macro->setFilePHID($file->getPHID());
} else if ($request->getStr('phid')) {
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$request->getStr('phid'));
}
if ($file) {
if (!$file->isViewableInBrowser()) {
$errors[] = pht('You must upload an image.');
$e_file = pht('Invalid');
} else {
$macro->setFilePHID($file->getPHID());
$e_file = null;
}
}
if (!$macro->getID() && !$file) {
$errors[] = 'You must upload an image to create a macro.';
$e_file = pht('Required');
}
if (!$errors) {
try {
$macro->save();
return id(new AphrontRedirectResponse())->setURI(
$this->getApplicationURI());
$xactions = array();
if ($new_name !== null) {
$xactions[] = id(new PhabricatorMacroTransaction())
->setTransactionType(PhabricatorMacroTransactionType::TYPE_NAME)
->setNewValue($new_name);
}
if ($file) {
$xactions[] = id(new PhabricatorMacroTransaction())
->setTransactionType(PhabricatorMacroTransactionType::TYPE_FILE)
->setNewValue($file->getPHID());
}
$editor = id(new PhabricatorMacroEditor())
->setActor($user)
->setContentSource(
PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
)))
->applyTransactions($original, $xactions);
$view_uri = $this->getApplicationURI('/view/'.$original->getID().'/');
return id(new AphrontRedirectResponse())->setURI($view_uri);
} catch (AphrontQueryDuplicateKeyException $ex) {
throw $ex;
$errors[] = 'Macro name is not unique!';
$e_name = 'Duplicate';
}
}
} else if ($this->id) {
$file = id(new PhabricatorFile())
->loadOneWhere('phid = %s', $macro->getFilePHID());
}
$caption = null;
if ($file) {
$caption = phutil_render_tag(
'img',
array(
'src' => $file->getViewURI(),
));
}
if ($errors) {
@ -82,7 +122,17 @@ final class PhabricatorMacroEditController
$error_view = null;
}
$current_file = null;
if ($macro->getFilePHID()) {
$current_file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$macro->getFilePHID());
}
$form = new AphrontFormView();
$form->setFlexible(true);
$form->addHiddenInput('name_form', 1);
$form->setUser($request->getUser());
$form
@ -93,35 +143,101 @@ final class PhabricatorMacroEditController
->setName('name')
->setValue($macro->getName())
->setCaption('This word or phrase will be replaced with the image.')
->setError($e_name))
->appendChild(
->setError($e_name));
if (!$macro->getID()) {
if ($current_file) {
$current_file_view = id(new PhabricatorFileLinkView())
->setFilePHID($current_file->getPHID())
->setFileName($current_file->getName())
->setFileViewable(true)
->setFileViewURI($current_file->getBestURI())
->render();
$form->addHiddenInput('phid', $current_file->getPHID());
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Selected File')
->setValue($current_file_view));
$other_label = pht('Change File');
} else {
$other_label = pht('File');
}
$form->appendChild(
id(new AphrontFormFileControl())
->setLabel('File')
->setLabel($other_label)
->setName('file')
->setCaption($caption)
->setError(true))
->setError($e_file));
}
$view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/');
if ($macro->getID()) {
$cancel_uri = $view_uri;
} else {
$cancel_uri = $this->getApplicationURI();
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save Image Macro')
->addCancelButton($this->getApplicationURI()));
->setValue(pht('Save Image Macro'))
->addCancelButton($cancel_uri));
$crumbs = $this->buildApplicationCrumbs();
$panel = new AphrontPanelView();
if ($macro->getID()) {
$title = 'Edit Image Macro';
} else {
$title = 'Create Image Macro';
}
$panel->setHeader($title);
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
$title = pht('Edit Image Macro');
$crumb = pht('Edit');
$nav = $this->buildSideNavView($macro);
$nav->selectFilter('#', 'edit');
$nav->appendChild($error_view);
$nav->appendChild($panel);
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setHref($view_uri)
->setName(pht('Macro "%s"', $macro->getName())));
} else {
$title = pht('Create Image Macro');
$crumb = pht('Create');
}
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setHref($request->getRequestURI())
->setName($crumb));
$header = id(new PhabricatorHeaderView())
->setHeader($title);
$upload = null;
if ($macro->getID()) {
$upload_header = id(new PhabricatorHeaderView())
->setHeader(pht('Upload New File'));
$upload_form = id(new AphrontFormView())
->setFlexible(true)
->setEncType('multipart/form-data')
->setUser($request->getUser())
->appendChild(
id(new AphrontFormFileControl())
->setLabel('File')
->setName('file'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Upload File'));
$upload = array($upload_header, $upload_form);
}
return $this->buildApplicationPage(
$nav,
array(
$crumbs,
$header,
$error_view,
$form,
$upload,
),
array(
'title' => $title,
));

View file

@ -70,8 +70,11 @@ final class PhabricatorMacroListController
$filter_view = new AphrontListFilterView();
$filter_view->appendChild($filter_form);
$nav = $this->buildSideNavView();
$nav->selectFilter('/');
$has_search = strlen($filter);
$nav = $this->buildSideNavView(
$for_app = false,
$has_search);
$nav->selectFilter($has_search ? 'search' : '/');
$nav->appendChild($filter_view);
@ -97,7 +100,7 @@ final class PhabricatorMacroListController
array(),
'Created on '.$datetime));
}
$item->setURI($this->getApplicationURI('/edit/'.$macro->getID().'/'));
$item->setURI($this->getApplicationURI('/view/'.$macro->getID().'/'));
$item->setHeader($macro->getName());
$pinboard->addItem($item);
@ -109,11 +112,20 @@ final class PhabricatorMacroListController
$nav->appendChild($list);
}
if ($filter === null) {
if (!strlen($filter)) {
$nav->appendChild($pager);
$name = pht('All Macros');
} else {
$name = pht('Search');
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($name)
->setHref($request->getRequestURI()));
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(

View file

@ -0,0 +1,193 @@
<?php
final class PhabricatorMacroViewController
extends PhabricatorMacroController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$macro = id(new PhabricatorFileImageMacro())->load($this->id);
if (!$macro) {
return new Aphront404Response();
}
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$macro->getFilePHID());
$title_short = pht('Macro "%s"', $macro->getName());
$title_long = pht('Image Macro "%s"', $macro->getName());
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$macro->getPHID());
$this->loadHandles($subscribers);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setHref($this->getApplicationURI('/view/'.$macro->getID().'/'))
->setName($title_short));
$actions = $this->buildActionView($macro);
$properties = $this->buildPropertyView($macro, $file, $subscribers);
$xactions = id(new PhabricatorMacroTransactionQuery())
->setViewer($request->getUser())
->withObjectPHIDs(array($macro->getPHID()))
->execute();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($user);
foreach ($xactions as $xaction) {
if ($xaction->getComment()) {
$engine->addObject(
$xaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
$timeline = id(new PhabricatorApplicationTransactionView())
->setViewer($user)
->setTransactions($xactions)
->setMarkupEngine($engine);
$header = id(new PhabricatorHeaderView())
->setHeader($title_long);
if ($macro->getIsDisabled()) {
$header->addTag(
id(new PhabricatorTagView())
->setType(PhabricatorTagView::TYPE_STATE)
->setName(pht('Macro Disabled'))
->setBackgroundColor(PhabricatorTagView::COLOR_RED));
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = id(new PhabricatorHeaderView())
->setHeader(
$is_serious
? pht('Add Comment')
: pht('Grovel in Awe'));
$add_comment_form = id(new AphrontFormView())
->setWorkflow(true)
->setFlexible(true)
->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/'))
->setUser($user)
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($user)
->setLabel('Comment')
->setName('comment'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(
$is_serious
? pht('Add Comment')
: pht('Lavish Praise')));
return $this->buildApplicationPage(
array(
$crumbs,
$header,
$actions,
$properties,
$timeline,
$add_comment_header,
$add_comment_form,
),
array(
'title' => $title_short,
));
}
private function buildActionView(PhabricatorFileImageMacro $macro) {
$view = new PhabricatorActionListView();
$view->setUser($this->getRequest()->getUser());
$view->setObject($macro);
$view->addAction(
id(new PhabricatorActionView())
->setName('Edit Macro')
->setHref($this->getApplicationURI('/edit/'.$macro->getID().'/'))
->setIcon('edit'));
if ($macro->getIsDisabled()) {
$view->addAction(
id(new PhabricatorActionView())
->setName('Restore Macro')
->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/'))
->setWorkflow(true)
->setIcon('undo'));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setName('Disable Macro')
->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/'))
->setWorkflow(true)
->setIcon('delete'));
}
return $view;
}
private function buildPropertyView(
PhabricatorFileImageMacro $macro,
PhabricatorFile $file = null,
array $subscribers) {
$view = new PhabricatorPropertyListView();
$view->addProperty(
pht('Name'),
phutil_escape_html($macro->getName()));
$view->addProperty(
pht('Status'),
$macro->getIsDisabled()
? pht('Disabled')
: pht('Enabled'));
$view->addProperty(
pht('Created'),
phabricator_date(
$macro->getDateCreated(),
$this->getRequest()->getUser()));
if ($subscribers) {
$sub_view = array();
foreach ($subscribers as $subscriber) {
$sub_view[] = $this->getHandle($subscriber)->renderLink();
}
$sub_view = implode(', ', $sub_view);
} else {
$sub_view = '<em>'.pht('None').'</em>';
}
$view->addProperty(
pht('Subscribers'),
$sub_view);
if ($file) {
$view->addTextContent(
phutil_render_tag(
'img',
array(
'src' => $file->getViewURI(),
'class' => 'phabricator-image-macro-hero',
)));
}
return $view;
}
}

View file

@ -0,0 +1,123 @@
<?php
final class PhabricatorMacroEditor
extends PhabricatorApplicationTransactionEditor {
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorMacroTransactionType::TYPE_NAME;
$types[] = PhabricatorMacroTransactionType::TYPE_DISABLED;
$types[] = PhabricatorMacroTransactionType::TYPE_FILE;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
return $object->getName();
case PhabricatorMacroTransactionType::TYPE_DISABLED:
return $object->getIsDisabled();
case PhabricatorMacroTransactionType::TYPE_FILE:
return $object->getFilePHID();
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
case PhabricatorMacroTransactionType::TYPE_DISABLED:
case PhabricatorMacroTransactionType::TYPE_FILE:
return $xaction->getNewValue();
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
$object->setName($xaction->getNewValue());
break;
case PhabricatorMacroTransactionType::TYPE_DISABLED:
$object->setIsDisabled($xaction->getNewValue());
break;
case PhabricatorMacroTransactionType::TYPE_FILE:
$object->setFilePHID($xaction->getNewValue());
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
return;
}
protected function mergeTransactions(
PhabricatorApplicationTransaction $u,
PhabricatorApplicationTransaction $v) {
$type = $u->getTransactionType();
switch ($type) {
case PhabricatorMacroTransactionType::TYPE_NAME:
case PhabricatorMacroTransactionType::TYPE_DISABLED:
case PhabricatorMacroTransactionType::TYPE_FILE:
return $v;
}
return parent::mergeTransactions($u, $v);
}
protected function supportsMail() {
return true;
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new PhabricatorMacroReplyHandler())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$name = $object->getName();
$name = 'Image Macro "'.$name.'"';
return id(new PhabricatorMetaMTAMail())
->setSubject($name)
->addHeader('Thread-Topic', $name);
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$this->requireActor()->getPHID(),
);
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$body->addTextSection(
pht('MACRO DETAIL'),
PhabricatorEnv::getProductionURI('/macro/view/'.$object->getID().'/'));
return $body;
}
protected function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.macro.subject-prefix');
}
protected function supportsFeed() {
return true;
}
}

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorMacroReplyHandler extends PhabricatorMailReplyHandler {
public function validateMailReceiver($mail_receiver) {
if (!($mail_receiver instanceof PhabricatorFileImageMacro)) {
throw new Exception("Mail receiver is not a PhabricatorFileImageMacro!");
}
}
public function getPrivateReplyHandlerEmailAddress(
PhabricatorObjectHandle $handle) {
return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'MCRO');
}
public function getPublicReplyHandlerEmailAddress() {
return $this->getDefaultPublicReplyHandlerEmailAddress('MCRO');
}
public function getReplyHandlerDomain() {
return PhabricatorEnv::getEnvConfig(
'metamta.macro.reply-handler-domain');
}
public function getReplyHandlerInstructions() {
if ($this->supportsReplies()) {
// TODO: Implement.
return null;
return "Reply to comment.";
} else {
return null;
}
}
protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) {
// TODO: Implement this.
return null;
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorMacroTransactionQuery
extends PhabricatorApplicationTransactionQuery {
protected function getTemplateApplicationTransaction() {
return new PhabricatorMacroTransaction();
}
}

View file

@ -1,16 +1,24 @@
<?php
final class PhabricatorFileImageMacro extends PhabricatorFileDAO {
final class PhabricatorFileImageMacro extends PhabricatorFileDAO
implements PhabricatorSubscribableInterface {
protected $filePHID;
protected $phid;
protected $name;
protected $isDisabled = 0;
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_MCRO);
}
static public function newFromImageURI($uri, $file_name, $image_macro_name) {
$file = PhabricatorFile::newFromFileDownload($uri, $file_name);
@ -25,5 +33,10 @@ final class PhabricatorFileImageMacro extends PhabricatorFileDAO {
return $image_macro;
}
public function isAutomaticallySubscribed($phid) {
return false;
}
}

View file

@ -0,0 +1,231 @@
<?php
final class PhabricatorMacroTransaction
extends PhabricatorApplicationTransaction {
public function getApplicationName() {
return 'file';
}
public function getTableName() {
return 'macro_transaction';
}
public function getApplicationTransactionType() {
return PhabricatorPHIDConstants::PHID_TYPE_MCRO;
}
public function getApplicationTransactionCommentObject() {
return new PhabricatorMacroTransactionComment();
}
public function getApplicationObjectTypeName() {
return pht('macro');
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_FILE:
if ($old !== null) {
$phids[] = $old;
}
$phids[] = $new;
break;
}
return $phids;
}
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
return ($old === null);
}
return parent::shouldHide();
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
return pht(
'%s renamed this macro from "%s" to "%s".',
$this->renderHandleLink($author_phid),
phutil_escape_html($old),
phutil_escape_html($new));
break;
case PhabricatorMacroTransactionType::TYPE_DISABLED:
if ($new) {
return pht(
'%s disabled this macro.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s restored this macro.',
$this->renderHandleLink($author_phid));
}
break;
case PhabricatorMacroTransactionType::TYPE_FILE:
if ($old === null) {
return pht(
'%s created this macro.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s changed the image for this macro from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($old),
$this->renderHandleLink($new));
}
break;
}
return parent::getTitle();
}
public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
return pht(
'%s renamed %s from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
phutil_escape_html($old),
phutil_escape_html($new));
case PhabricatorMacroTransactionType::TYPE_DISABLED:
if ($new) {
return pht(
'%s disabled %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s restored %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
case PhabricatorMacroTransactionType::TYPE_FILE:
if ($old === null) {
return pht(
'%s created %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s updated the image for %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
}
return parent::getTitleForFeed();
}
public function getActionName() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
if ($old === null) {
return pht('Created');
} else {
return pht('Renamed');
}
case PhabricatorMacroTransactionType::TYPE_DISABLED:
if ($new) {
return pht('Disabled');
} else {
return pht('Restored');
}
case PhabricatorMacroTransactionType::TYPE_FILE:
if ($old === null) {
return pht('Created');
} else {
return pht('Edited Image');
}
}
return parent::getActionName();
}
public function getActionStrength() {
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_DISABLED:
return 2.0;
case PhabricatorMacroTransactionType::TYPE_FILE:
return 1.5;
}
return parent::getActionStrength();
}
public function getIcon() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
return 'edit';
case PhabricatorMacroTransactionType::TYPE_FILE:
if ($old === null) {
return 'create';
} else {
return 'edit';
}
case PhabricatorMacroTransactionType::TYPE_DISABLED:
if ($new) {
return 'delete';
} else {
return 'undo';
}
}
return parent::getIcon();
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case PhabricatorMacroTransactionType::TYPE_NAME:
return PhabricatorTransactions::COLOR_BLUE;
case PhabricatorMacroTransactionType::TYPE_FILE:
if ($old === null) {
return PhabricatorTransactions::COLOR_GREEN;
} else {
return PhabricatorTransactions::COLOR_BLUE;
}
case PhabricatorMacroTransactionType::TYPE_DISABLED:
if ($new) {
return PhabricatorTransactions::COLOR_BLACK;
} else {
return PhabricatorTransactions::COLOR_SKY;
}
}
return parent::getColor();
}
}

View file

@ -0,0 +1,11 @@
<?php
final class PhabricatorMacroTransactionComment
extends PhabricatorApplicationTransactionComment {
public function getApplicationTransactionObject() {
return new PhabricatorMacroTransaction();
}
}

View file

@ -29,6 +29,7 @@ final class PhabricatorPHIDConstants {
const PHID_TYPE_QUES = 'QUES';
const PHID_TYPE_ANSW = 'ANSW';
const PHID_TYPE_MOCK = 'MOCK';
const PHID_TYPE_MCRO = 'MCRO';
const PHID_TYPE_XACT = 'XACT';
const PHID_TYPE_XCMT = 'XCMT';

View file

@ -120,12 +120,25 @@ final class PhabricatorObjectHandleData {
->execute();
$xactions += mpull($results, null, 'getPHID');
break;
case PhabricatorPHIDConstants::PHID_TYPE_MCRO:
$results = id(new PhabricatorMacroTransactionQuery())
->setViewer($this->viewer)
->withPHIDs($subtype_phids)
->execute();
$xactions += mpull($results, null, 'getPHID');
break;
}
}
foreach ($xactions as $xaction) {
$objects[$xaction->getPHID()] = $xaction;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_MCRO:
$macros = id(new PhabricatorFileImageMacro())->loadAllWhere(
'phid IN (%Ls)',
$phids);
$objects += mpull($macros, null, 'getPHID');
break;
}
}
@ -601,6 +614,27 @@ final class PhabricatorObjectHandleData {
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_MCRO:
$macros = id(new PhabricatorFileImageMacro())->loadAllWhere(
'phid IN (%Ls)',
$phids);
$macros = mpull($macros, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($macros[$phid])) {
$handle->setName('Unknown Macro');
} else {
$macro = $macros[$phid];
$handle->setName($macro->getName());
$handle->setFullName('Image Macro "'.$macro->getName().'"');
$handle->setURI('/macro/view/'.$macro->getID().'/');
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
default:
$loader = null;
if (isset($external_loaders[$type])) {

View file

@ -771,8 +771,6 @@ abstract class PhabricatorApplicationTransactionEditor
$story_type = $this->getFeedStoryType();
$story_data = $this->getFeedStoryData($object, $xactions);
phlog($subscribed_phids);
id(new PhabricatorFeedStoryPublisher())
->setStoryType($story_type)
->setStoryData($story_data)

View file

@ -2,9 +2,7 @@
abstract class PhabricatorApplicationTransaction
extends PhabricatorLiskDAO
implements PhabricatorPolicyInterface{
const MARKUP_FIELD_COMMENT = 'markup:comment';
implements PhabricatorPolicyInterface {
const TARGET_TEXT = 'text';
const TARGET_HTML = 'html';
@ -17,7 +15,6 @@ abstract class PhabricatorApplicationTransaction
protected $commentPHID;
protected $commentVersion = 0;
protected $transactionType;
protected $oldValue;
protected $newValue;
@ -60,6 +57,10 @@ abstract class PhabricatorApplicationTransaction
return PhabricatorContentSource::newFromSerialized($this->contentSource);
}
public function hasComment() {
return $this->getComment() && strlen($this->getComment()->getContent());
}
public function getComment() {
if ($this->commentNotLoaded) {
throw new Exception("Comment for this transaction was not loaded.");
@ -137,6 +138,16 @@ abstract class PhabricatorApplicationTransaction
}
public function getIcon() {
switch ($this->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
return 'comment';
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
return 'message';
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
return 'lock';
}
return null;
}

View file

@ -4,6 +4,8 @@ abstract class PhabricatorApplicationTransactionComment
extends PhabricatorLiskDAO
implements PhabricatorMarkupInterface, PhabricatorPolicyInterface {
const MARKUP_FIELD_COMMENT = 'markup:comment';
protected $transactionPHID;
protected $commentVersion;
protected $authorPHID;

View file

@ -0,0 +1,81 @@
<?php
/**
* @concrete-extensible
*/
class PhabricatorApplicationTransactionView extends AphrontView {
private $viewer;
private $transactions;
private $engine;
private $anchorOffset = 0;
public function setAnchorOffset($anchor_offset) {
$this->anchorOffset = $anchor_offset;
return $this;
}
public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
$this->engine = $engine;
return $this;
}
public function setTransactions(array $transactions) {
assert_instances_of($transactions, 'PhabricatorApplicationTransaction');
$this->transactions = $transactions;
return $this;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function render() {
$field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT;
if (!$this->engine) {
$engine = id(new PhabricatorMarkupEngine())
->setViewer($this->viewer);
foreach ($this->transactions as $xaction) {
if (!$xaction->hasComment()) {
continue;
}
$engine->addObject($xaction->getComment(), $field);
}
$engine->process();
$this->engine = $engine;
}
$view = new PhabricatorTimelineView();
$anchor = $this->anchorOffset;
foreach ($this->transactions as $xaction) {
if ($xaction->shouldHide()) {
continue;
}
$anchor++;
$event = id(new PhabricatorTimelineEventView())
->setViewer($this->viewer)
->setUserHandle($xaction->getHandle($xaction->getAuthorPHID()))
->setIcon($xaction->getIcon())
->setColor($xaction->getColor())
->setTitle($xaction->getTitle())
->setDateCreated($xaction->getDateCreated())
->setContentSource($xaction->getContentSource())
->setAnchor($anchor);
if ($xaction->hasComment()) {
$event->appendChild(
$this->engine->getOutput($xaction->getComment(), $field));
}
$view->addEvent($event);
}
return $view->render();
}
}

View file

@ -108,6 +108,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
PhabricatorPHIDConstants::PHID_TYPE_QUES => 'PonderQuestion',
PhabricatorPHIDConstants::PHID_TYPE_ANSW => 'PonderAnswer',
PhabricatorPHIDConstants::PHID_TYPE_MOCK => 'PholioMock',
PhabricatorPHIDConstants::PHID_TYPE_MCRO => 'PhabricatorFileImageMacro',
);

View file

@ -18,7 +18,8 @@ final class PhabricatorRemarkupRuleImageMacro
public function markupImageMacro($matches) {
if ($this->images === null) {
$this->images = array();
$rows = id(new PhabricatorFileImageMacro())->loadAll();
$rows = id(new PhabricatorFileImageMacro())->loadAllWhere(
'isDisabled = 0');
foreach ($rows as $row) {
$this->images[$row->getName()] = $row->getFilePHID();
}

View file

@ -1048,6 +1048,18 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql',
'name' => $this->getPatchPath('20121209.pholioxactions.sql'),
),
'20121209.xmacroadd.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121209.xmacroadd.sql'),
),
'20121209.xmacromigrate.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('20121209.xmacromigrate.php'),
),
'20121209.xmacromigratekey.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20121209.xmacromigratekey.sql'),
),
);
}

View file

@ -4,6 +4,7 @@ final class PhabricatorHeaderView extends AphrontView {
private $objectName;
private $header;
private $tags = array();
public function setHeader($header) {
$this->header = $header;
@ -15,6 +16,11 @@ final class PhabricatorHeaderView extends AphrontView {
return $this;
}
public function addTag(PhabricatorTagView $tag) {
$this->tags[] = $tag;
return $this;
}
public function render() {
require_celerity_resource('phabricator-header-view-css');
@ -29,6 +35,15 @@ final class PhabricatorHeaderView extends AphrontView {
phutil_escape_html($this->objectName)).' '.$header;
}
if ($this->tags) {
$header .= phutil_render_tag(
'div',
array(
'class' => 'phabricator-header-tags',
),
self::renderSingleView($this->tags));
}
return phutil_render_tag(
'h1',
array(

View file

@ -312,3 +312,8 @@
.remarkup-assist-right {
float: right;
}
.phabricator-image-macro-hero {
margin: 2em auto;
max-width: 90%;
}

View file

@ -11,3 +11,8 @@
.device-desktop .phabricator-header-view {
width: 66%;
}
.phabricator-header-tags {
font-size: 13px;
float: right;
}