Page MenuHomestyx hydra

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
index e91884ef58..6d17506831 100644
--- a/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
+++ b/src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
@@ -1,384 +1,384 @@
<?php
/**
* @group aphront
*/
class AphrontDefaultApplicationConfiguration
extends AphrontApplicationConfiguration {
public function __construct() {
}
public function getApplicationName() {
return 'aphront-default';
}
public function getURIMap() {
return $this->getResourceURIMapRules() + array(
'/(?:(?P<filter>(?:jump))/)?' =>
'PhabricatorDirectoryMainController',
'/typeahead/' => array(
'common/(?P<type>\w+)/'
=> 'PhabricatorTypeaheadCommonDatasourceController',
),
'/login/' => array(
'' => 'PhabricatorLoginController',
'email/' => 'PhabricatorEmailLoginController',
'etoken/(?P<token>\w+)/' => 'PhabricatorEmailTokenController',
'refresh/' => 'PhabricatorRefreshCSRFController',
'validate/' => 'PhabricatorLoginValidateController',
'mustverify/' => 'PhabricatorMustVerifyEmailController',
),
'/logout/' => 'PhabricatorLogoutController',
'/oauth/' => array(
'(?P<provider>\w+)/' => array(
'login/' => 'PhabricatorOAuthLoginController',
'diagnose/' => 'PhabricatorOAuthDiagnosticsController',
'unlink/' => 'PhabricatorOAuthUnlinkController',
),
),
'/ldap/' => array(
'login/' => 'PhabricatorLDAPLoginController',
'unlink/' => 'PhabricatorLDAPUnlinkController',
),
'/oauthserver/' => array(
'auth/' => 'PhabricatorOAuthServerAuthController',
'test/' => 'PhabricatorOAuthServerTestController',
'token/' => 'PhabricatorOAuthServerTokenController',
'clientauthorization/' => array(
'' => 'PhabricatorOAuthClientAuthorizationListController',
'delete/(?P<phid>[^/]+)/' =>
'PhabricatorOAuthClientAuthorizationDeleteController',
'edit/(?P<phid>[^/]+)/' =>
'PhabricatorOAuthClientAuthorizationEditController',
),
'client/' => array(
'' => 'PhabricatorOAuthClientListController',
'create/' => 'PhabricatorOAuthClientEditController',
'delete/(?P<phid>[^/]+)/' => 'PhabricatorOAuthClientDeleteController',
'edit/(?P<phid>[^/]+)/' => 'PhabricatorOAuthClientEditController',
'view/(?P<phid>[^/]+)/' => 'PhabricatorOAuthClientViewController',
),
),
'/xhprof/' => array(
'list/(?P<view>[^/]+)/' => 'PhabricatorXHProfSampleListController',
'profile/(?P<phid>[^/]+)/' => 'PhabricatorXHProfProfileController',
),
'/~/' => 'DarkConsoleController',
'/search/' => array(
'' => 'PhabricatorSearchController',
'(?P<key>[^/]+)/' => 'PhabricatorSearchController',
'attach/(?P<phid>[^/]+)/(?P<type>\w+)/(?:(?P<action>\w+)/)?'
=> 'PhabricatorSearchAttachController',
'select/(?P<type>\w+)/'
=> 'PhabricatorSearchSelectController',
'index/(?P<phid>[^/]+)/' => 'PhabricatorSearchIndexController',
),
'/status/' => 'PhabricatorStatusController',
'/help/' => array(
'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController',
),
'/chatlog/' => array(
'' =>
'PhabricatorChatLogChannelListController',
'channel/(?P<channel>[^/]+)/' =>
'PhabricatorChatLogChannelLogController',
),
'/notification/' => array(
'(?:(?P<filter>all|unread)/)?'
=> 'PhabricatorNotificationListController',
'panel/' => 'PhabricatorNotificationPanelController',
'individual/' => 'PhabricatorNotificationIndividualController',
'status/' => 'PhabricatorNotificationStatusController',
'clear/' => 'PhabricatorNotificationClearController',
),
'/phortune/' => array(
'stripe/' => array(
'testpaymentform/' => 'PhortuneStripeTestPaymentFormController',
),
),
);
}
protected function getResourceURIMapRules() {
return array(
'/res/' => array(
'(?P<package>pkg/)?'.
'(?P<hash>[a-f0-9]{8})/'.
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'
=> 'CelerityPhabricatorResourceController',
),
);
}
public function buildRequest() {
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($_GET + $_POST);
$request->setApplicationConfiguration($this);
return $request;
}
public function handleException(Exception $ex) {
$request = $this->getRequest();
// For Conduit requests, return a Conduit response.
if ($request->isConduit()) {
$response = new ConduitAPIResponse();
$response->setErrorCode(get_class($ex));
$response->setErrorInfo($ex->getMessage());
return id(new AphrontJSONResponse())
->setContent($response->toDictionary());
}
// For non-workflow requests, return a Ajax response.
if ($request->isAjax() && !$request->isJavelinWorkflow()) {
$response = new AphrontAjaxResponse();
$response->setError(
array(
'code' => get_class($ex),
'info' => $ex->getMessage(),
));
return $response;
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$user = $request->getUser();
if (!$user) {
// If we hit an exception very early, we won't have a user.
$user = new PhabricatorUser();
}
if ($ex instanceof PhabricatorPolicyException) {
if (!$user->isLoggedIn()) {
// If the user isn't logged in, just give them a login form. This is
// probably a generally more useful response than a policy dialog that
// they have to click through to get a login form.
//
// Possibly we should add a header here like "you need to login to see
// the thing you are trying to look at".
$login_controller = new PhabricatorLoginController($request);
return $login_controller->processRequest();
}
$content =
'<div class="aphront-policy-exception">'.
phutil_escape_html($ex->getMessage()).
'</div>';
$dialog = new AphrontDialogView();
$dialog
->setTitle(
$is_serious
? 'Access Denied'
: "You Shall Not Pass")
->setClass('aphront-access-dialog')
->setUser($user)
->appendChild($content);
if ($this->getRequest()->isAjax()) {
$dialog->addCancelButton('/', 'Close');
} else {
$dialog->addCancelButton('/', $is_serious ? 'OK' : 'Away With Thee');
}
$response = new AphrontDialogResponse();
$response->setDialog($dialog);
return $response;
}
if ($ex instanceof AphrontUsageException) {
$error = new AphrontErrorView();
$error->setTitle(phutil_escape_html($ex->getTitle()));
$error->appendChild(phutil_escape_html($ex->getMessage()));
$view = new PhabricatorStandardPageView();
$view->setRequest($this->getRequest());
$view->appendChild($error);
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
// Always log the unhandled exception.
phlog($ex);
$class = phutil_escape_html(get_class($ex));
$message = phutil_escape_html($ex->getMessage());
if ($ex instanceof AphrontQuerySchemaException) {
$message .=
"\n\n".
"NOTE: This usually indicates that the MySQL schema has not been ".
"properly upgraded. Run 'bin/storage upgrade' to ensure your ".
"schema is up to date.";
}
if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) {
$trace = $this->renderStackTrace($ex->getTrace(), $user);
} else {
$trace = null;
}
$content =
'<div class="aphront-unhandled-exception">'.
'<div class="exception-message">'.$message.'</div>'.
$trace.
'</div>';
$dialog = new AphrontDialogView();
$dialog
->setTitle('Unhandled Exception ("'.$class.'")')
->setClass('aphront-exception-dialog')
->setUser($user)
->appendChild($content);
if ($this->getRequest()->isAjax()) {
$dialog->addCancelButton('/', 'Close');
}
$response = new AphrontDialogResponse();
$response->setDialog($dialog);
return $response;
}
public function willSendResponse(AphrontResponse $response) {
return $response;
}
public function build404Controller() {
return array(new Phabricator404Controller($this->getRequest()), array());
}
public function buildRedirectController($uri) {
return array(
new PhabricatorRedirectController($this->getRequest()),
array(
'uri' => $uri,
));
}
private function renderStackTrace($trace, PhabricatorUser $user) {
$libraries = PhutilBootloader::getInstance()->getAllLibraries();
// TODO: Make this configurable?
$path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
$callsigns = array(
'arcanist' => 'ARC',
'phutil' => 'PHU',
'phabricator' => 'P',
);
$rows = array();
$depth = count($trace);
foreach ($trace as $part) {
$lib = null;
$file = idx($part, 'file');
$relative = $file;
foreach ($libraries as $library) {
$root = phutil_get_library_root($library);
if (Filesystem::isDescendant($file, $root)) {
$lib = $library;
$relative = Filesystem::readablePath($file, $root);
break;
}
}
$where = '';
if (isset($part['class'])) {
$where .= $part['class'].'::';
}
if (isset($part['function'])) {
$where .= $part['function'].'()';
}
if ($file) {
if (isset($callsigns[$lib])) {
$attrs = array('title' => $file);
try {
$attrs['href'] = $user->loadEditorLink(
'/src/'.$relative,
$part['line'],
$callsigns[$lib]);
} catch (Exception $ex) {
// The database can be inaccessible.
}
if (empty($attrs['href'])) {
$attrs['href'] = sprintf($path, $callsigns[$lib]).
str_replace(DIRECTORY_SEPARATOR, '/', $relative).
'$'.$part['line'];
$attrs['target'] = '_blank';
}
- $file_name = phutil_render_tag(
+ $file_name = phutil_tag(
'a',
$attrs,
- phutil_escape_html($relative));
+ $relative);
} else {
- $file_name = phutil_render_tag(
+ $file_name = phutil_tag(
'span',
array(
'title' => $file,
),
- phutil_escape_html($relative));
+ $relative);
}
$file_name = $file_name.' : '.(int)$part['line'];
} else {
$file_name = '<em>(Internal)</em>';
}
$rows[] = array(
$depth--,
phutil_escape_html($lib),
$file_name,
phutil_escape_html($where),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Depth',
'Library',
'File',
'Where',
));
$table->setColumnClasses(
array(
'n',
'',
'',
'wide',
));
return
'<div class="exception-trace">'.
'<div class="exception-trace-header">Stack Trace</div>'.
$table->render().
'</div>';
}
}
diff --git a/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php b/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php
index 20123516f8..fae5ff2cb1 100644
--- a/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php
+++ b/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php
@@ -1,151 +1,151 @@
<?php
/**
* @group console
*/
final class DarkConsoleErrorLogPlugin extends DarkConsolePlugin {
public function getName() {
$count = count($this->getData());
if ($count) {
return
'<span style="color: #ff0000;">&bull;</span> '.
"Error Log ({$count})";
}
return 'Error Log';
}
public function getDescription() {
return 'Shows errors and warnings.';
}
public function generateData() {
return DarkConsoleErrorLogPluginAPI::getErrors();
}
public function render() {
$data = $this->getData();
$rows = array();
$details = '';
foreach ($data as $index => $row) {
$file = $row['file'];
$line = $row['line'];
- $tag = phutil_render_tag(
+ $tag = phutil_tag(
'a',
array(
'onclick' => jsprintf('show_details(%d)', $index),
),
- phutil_escape_html($row['str'].' at ['.basename($file).':'.$line.']'));
+ $row['str'].' at ['.basename($file).':'.$line.']');
$rows[] = array($tag);
$details .=
'<div class="dark-console-panel-error-details" id="row-details-'.
$index.'">'.
phutil_escape_html($row['details'])."\n".
'Stack trace:'."\n";
foreach ($row['trace'] as $key => $entry) {
$line = '';
if (isset($entry['class'])) {
$line .= $entry['class'].'::';
}
$line .= idx($entry, 'function', '');
$href = null;
if (isset($entry['file'])) {
$line .= ' called at ['.$entry['file'].':'.$entry['line'].']';
try {
$user = $this->getRequest()->getUser();
$href = $user->loadEditorLink($entry['file'], $entry['line'], '');
} catch (Exception $ex) {
// The database can be inaccessible.
}
}
- $details .= phutil_render_tag(
+ $details .= phutil_tag(
'a',
array(
'href' => $href,
),
- phutil_escape_html($line));
+ $line);
$details .= "\n";
}
$details .= '</div>';
}
$table = new AphrontTableView($rows);
$table->setClassName('error-log');
$table->setHeaders(array('Error'));
$table->setNoDataString('No errors.');
return '<div>'.
'<div>'.$table->render().'</div>'.
'<pre class="PhabricatorMonospaced">'.
$details.'</pre>'.
'</div>';
}
}
/*
$data = $this->getData();
if (!$data) {
return
<x:frag>
<div class="mu">No errors.</div>
</x:frag>;
}
$markup = <table class="LConsoleErrors" />;
$alt = false;
foreach ($data as $error) {
$row = <tr class={$alt ? 'alt' : null} />;
$text = $error['error'];
$text = preg_replace('/\(in .* on line \d+\)$/', '', trim($text));
$trace = $error['trace'];
$trace = explode("\n", $trace);
if (!$trace) {
$trace = array('unknown@0@unknown');
}
foreach ($trace as $idx => $traceline) {
list($file, $line, $where) = array_merge(
explode('@', $traceline),
array('?', '?', '?'));
if ($where == 'DarkConsole->addError' ||
$where == 'debug_rlog') {
unset($trace[$idx]);
}
}
$row->appendChild(<th rowspan={count($trace)}>{$text}</th>);
foreach ($trace as $traceline) {
list($file, $line, $where) = array_merge(
explode('@', $traceline),
array('?', '?', '?'));
$row->appendChild(<td>{$file}:{$line}</td>);
$row->appendChild(<td>{$where}()</td>);
$markup->appendChild($row);
$row = <tr class={$alt ? 'alt' : null} />;
}
$alt = !$alt;
}
return
<x:frag>
<h1>Errors</h1>
<div class="LConsoleErrors">{$markup}</div>
</x:frag>;
*/
diff --git a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php b/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php
index 9f2a03dfd6..1745e1f274 100644
--- a/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php
+++ b/src/applications/chatlog/controller/PhabricatorChatLogChannelListController.php
@@ -1,46 +1,46 @@
<?php
final class PhabricatorChatLogChannelListController
extends PhabricatorChatLogController {
public function processRequest() {
$table = new PhabricatorChatLogEvent();
$channels = queryfx_all(
$table->establishConnection('r'),
'SELECT DISTINCT channel FROM %T',
$table->getTableName());
$rows = array();
foreach ($channels as $channel) {
$name = $channel['channel'];
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/chatlog/channel/'.phutil_escape_uri($name).'/',
),
- phutil_escape_html($name)));
+ $name));
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Channel',
));
$table->setColumnClasses(
array(
'pri wide',
));
$panel = new AphrontPanelView();
$panel->appendChild($table);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Channel List',
));
}
}
diff --git a/src/applications/conduit/controller/PhabricatorConduitListController.php b/src/applications/conduit/controller/PhabricatorConduitListController.php
index 7c16896516..617ca48288 100644
--- a/src/applications/conduit/controller/PhabricatorConduitListController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitListController.php
@@ -1,81 +1,81 @@
<?php
/**
* @group conduit
*/
final class PhabricatorConduitListController
extends PhabricatorConduitController {
public function processRequest() {
$method_groups = $this->getMethodFilters();
$rows = array();
foreach ($method_groups as $group => $methods) {
foreach ($methods as $info) {
switch ($info['status']) {
case ConduitAPIMethod::METHOD_STATUS_DEPRECATED:
$status = 'Deprecated';
break;
case ConduitAPIMethod::METHOD_STATUS_UNSTABLE:
$status = 'Unstable';
break;
default:
$status = null;
break;
}
$rows[] = array(
$group,
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/conduit/method/'.$info['full_name'],
),
- phutil_escape_html($info['full_name'])),
+ $info['full_name']),
$info['description'],
$status,
);
$group = null;
}
}
$table = new AphrontTableView($rows);
$table->setHeaders(array(
'Group',
'Name',
'Description',
'Status',
));
$table->setColumnClasses(array(
'pri',
'pri',
'wide',
null,
));
$panel = new AphrontPanelView();
$panel->setHeader('Conduit Methods');
$panel->appendChild($table);
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
$utils = new AphrontPanelView();
$utils->setHeader('Utilities');
$utils->appendChild(
'<ul>'.
'<li><a href="/conduit/log/">Log</a> - Conduit Method Calls</li>'.
'<li><a href="/conduit/token/">Token</a> - Certificate Install</li>'.
'</ul>');
$utils->setWidth(AphrontPanelView::WIDTH_FULL);
$this->setShowSideNav(false);
return $this->buildStandardPageResponse(
array(
$panel,
$utils,
),
array(
'title' => 'Conduit Console',
));
}
}
diff --git a/src/applications/config/controller/PhabricatorConfigAllController.php b/src/applications/config/controller/PhabricatorConfigAllController.php
index e5bbc7552a..e140ffc9ef 100644
--- a/src/applications/config/controller/PhabricatorConfigAllController.php
+++ b/src/applications/config/controller/PhabricatorConfigAllController.php
@@ -1,97 +1,97 @@
<?php
final class PhabricatorConfigAllController
extends PhabricatorConfigController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$rows = array();
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
ksort($options);
foreach ($options as $option) {
$key = $option->getKey();
if ($option->getMasked()) {
$value = '<em>'.pht('Masked').'</em>';
} else if ($option->getHidden()) {
$value = '<em>'.pht('Hidden').'</em>';
} else {
$value = PhabricatorEnv::getEnvConfig($key);
$value = PhabricatorConfigJSON::prettyPrintJSON($value);
$value = phutil_escape_html($value);
}
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $this->getApplicationURI('edit/'.$key.'/'),
),
- phutil_escape_html($key)),
+ $key),
$value,
);
}
$table = id(new AphrontTableView($rows))
->setDeviceReadyTable(true)
->setColumnClasses(
array(
'',
'wide',
))
->setHeaders(
array(
pht('Key'),
pht('Value'),
));
$title = pht('Current Settings');
$crumbs = $this
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName($title));
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setNoBackground();
$phabricator_root = dirname(phutil_get_library_root('phabricator'));
$future = id(new ExecFuture('git log --format=%%H -n 1 --'))
->setCWD($phabricator_root);
list($err, $stdout) = $future->resolve();
if (!$err) {
$display_version = trim($stdout);
} else {
$display_version = pht('Unknown');
}
$version_property_list = id(new PhabricatorPropertyListView());
$version_property_list->addProperty('Version',
phutil_escape_html($display_version));
$version_path = $phabricator_root.'/conf/local/VERSION';
if (Filesystem::pathExists($version_path)) {
$version_from_file = Filesystem::readFile($version_path);
$version_property_list->addProperty('Local Version',
phutil_escape_html($version_from_file));
}
$nav = $this->buildSideNavView();
$nav->selectFilter('all/');
$nav->setCrumbs($crumbs);
$nav->appendChild($version_property_list);
$nav->appendChild($panel);
return $this->buildApplicationPage(
$nav,
array(
'title' => $title,
'device' => true,
)
);
}
}
diff --git a/src/applications/config/view/PhabricatorSetupIssueView.php b/src/applications/config/view/PhabricatorSetupIssueView.php
index c85caed1ee..645999f190 100644
--- a/src/applications/config/view/PhabricatorSetupIssueView.php
+++ b/src/applications/config/view/PhabricatorSetupIssueView.php
@@ -1,333 +1,333 @@
<?php
final class PhabricatorSetupIssueView extends AphrontView {
private $issue;
public function setIssue(PhabricatorSetupIssue $issue) {
$this->issue = $issue;
return $this;
}
public function getIssue() {
return $this->issue;
}
public function render() {
$issue = $this->getIssue();
$description = phutil_render_tag(
'div',
array(
'class' => 'setup-issue-instructions',
),
nl2br(phutil_escape_html($issue->getMessage())));
$configs = $issue->getPHPConfig();
if ($configs) {
$description .= $this->renderPHPConfig($configs);
}
$configs = $issue->getPhabricatorConfig();
if ($configs) {
$description .= $this->renderPhabricatorConfig($configs);
}
$commands = $issue->getCommands();
if ($commands) {
$run_these = pht("Run these %d command(s):", count($commands));
$description .= phutil_render_tag(
'div',
array(
'class' => 'setup-issue-config',
),
phutil_render_tag('p', array(), $run_these).
phutil_render_tag('pre', array(), implode("\n", $commands)));
}
$extensions = $issue->getPHPExtensions();
if ($extensions) {
$install_these = pht(
"Install these %d PHP extension(s):", count($extensions));
$install_info = pht(
"You can usually install a PHP extension using <tt>apt-get</tt> or ".
"<tt>yum</tt>. Common package names are ".
"<tt>php-<em>extname</em></tt> or <tt>php5-<em>extname</em></tt>. ".
"Try commands like these:");
// TODO: We should do a better job of detecting how to install extensions
// on the current system.
$install_commands = array(
"$ sudo apt-get install php5-<em>extname</em> # Debian / Ubuntu",
"$ sudo yum install php-<em>extname</em> # Red Hat / Derivatives",
);
$install_commands = implode("\n", $install_commands);
$fallback_info = pht(
"If those commands don't work, try Google. The process of installing ".
"PHP extensions is not specific to Phabricator, and any instructions ".
"you can find for installing them on your system should work. On Mac ".
"OS X, you might want to try Homebrew.");
$restart_info = pht(
"After installing new PHP extensions, <strong>restart your webserver ".
"for the changes to take effect</strong>.");
$description .= phutil_render_tag(
'div',
array(
'class' => 'setup-issue-config',
),
phutil_render_tag('p', array(), $install_these).
phutil_render_tag('pre', array(), implode("\n", $extensions)).
phutil_render_tag('p', array(), $install_info).
phutil_render_tag('pre', array(), $install_commands).
phutil_render_tag('p', array(), $fallback_info).
phutil_render_tag('p', array(), $restart_info));
}
$next = phutil_render_tag(
'div',
array(
'class' => 'setup-issue-next',
),
pht('To continue, resolve this problem and reload the page.'));
- $name = phutil_render_tag(
+ $name = phutil_tag(
'div',
array(
'class' => 'setup-issue-name',
),
- phutil_escape_html($issue->getName()));
+ $issue->getName());
return phutil_render_tag(
'div',
array(
'class' => 'setup-issue',
),
$name.$description.$next);
}
private function renderPhabricatorConfig(array $configs) {
$issue = $this->getIssue();
$table_info = phutil_render_tag(
'p',
array(),
pht(
"The current Phabricator configuration has these %d value(s):",
count($configs)));
$table = array();
foreach ($configs as $key) {
$table[] = '<tr>';
$table[] = '<th>'.phutil_escape_html($key).'</th>';
$value = PhabricatorEnv::getUnrepairedEnvConfig($key);
if ($value === null) {
$value = '<em>null</em>';
} else if ($value === false) {
$value = '<em>false</em>';
} else if ($value === true) {
$value = '<em>true</em>';
} else {
$value = phutil_escape_html(
PhabricatorConfigJSON::prettyPrintJSON($value));
}
$table[] = '<td>'.$value.'</td>';
$table[] = '</tr>';
}
$table = phutil_render_tag(
'table',
array(
),
implode("\n", $table));
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
if ($this->getIssue()->getIsFatal()) {
$update_info = phutil_render_tag(
'p',
array(),
pht(
"To update these %d value(s), run these command(s) from the command ".
"line:",
count($configs)));
$update = array();
foreach ($configs as $key) {
$cmd = '<tt>phabricator/ $</tt> ./bin/config set '.
phutil_escape_html($key).' '.
'<em>value</em>';
$update[] = $cmd;
}
$update = phutil_render_tag('pre', array(), implode("\n", $update));
} else {
$update = array();
foreach ($configs as $config) {
if (!idx($options, $config) || $options[$config]->getLocked()) {
continue;
}
$link = phutil_render_tag(
'a',
array(
'href' => '/config/edit/'.$config.'/?issue='.$issue->getIssueKey(),
),
pht('Edit %s', phutil_escape_html($config)));
$update[] = '<li>'.$link.'</li>';
}
if ($update) {
$update = '<ul>'.implode("\n", $update).'</ul>';
$update_info = phutil_render_tag(
'p',
array(),
pht("You can update these %d value(s) here:", count($configs)));
} else {
$update = null;
$update_info = null;
}
}
return phutil_render_tag(
'div',
array(
'class' => 'setup-issue-config',
),
self::renderSingleView(
array(
$table_info,
$table,
$update_info,
$update,
)));
}
private function renderPHPConfig(array $configs) {
$table_info = phutil_render_tag(
'p',
array(),
pht(
"The current PHP configuration has these %d value(s):",
count($configs)));
$table = array();
foreach ($configs as $key) {
$table[] = '<tr>';
$table[] = '<th>'.phutil_escape_html($key).'</th>';
$value = ini_get($key);
if ($value === null) {
$value = '<em>null</em>';
} else if ($value === false) {
$value = '<em>false</em>';
} else if ($value === true) {
$value = '<em>true</em>';
} else if ($value === '') {
$value = '<em>(empty string)</em>';
} else {
$value = phutil_escape_html($value);
}
$table[] = '<td>'.$value.'</td>';
$table[] = '</tr>';
}
$table = phutil_render_tag(
'table',
array(
),
implode("\n", $table));
ob_start();
phpinfo();
$phpinfo = ob_get_clean();
$rex = '@Loaded Configuration File\s*</td><td class="v">(.*?)</td>@i';
$matches = null;
$ini_loc = null;
if (preg_match($rex, $phpinfo, $matches)) {
$ini_loc = trim($matches[1]);
}
$rex = '@Additional \.ini files parsed\s*</td><td class="v">(.*?)</td>@i';
$more_loc = array();
if (preg_match($rex, $phpinfo, $matches)) {
$more_loc = trim($matches[1]);
if ($more_loc == '(none)') {
$more_loc = array();
} else {
$more_loc = preg_split('/\s*,\s*/', $more_loc);
}
}
if (!$ini_loc) {
$info = phutil_render_tag(
'p',
array(),
pht(
"To update these %d value(s), edit your PHP configuration file.",
count($configs)));
} else {
$info = phutil_render_tag(
'p',
array(),
pht(
"To update these %d value(s), edit your PHP configuration file, ".
"located here:",
count($configs)));
- $info .= phutil_render_tag(
+ $info .= phutil_tag(
'pre',
array(),
- phutil_escape_html($ini_loc));
+ $ini_loc);
}
if ($more_loc) {
$info .= phutil_render_tag(
'p',
array(),
pht(
"PHP also loaded these configuration file(s):",
count($more_loc)));
- $info .= phutil_render_tag(
+ $info .= phutil_tag(
'pre',
array(),
- phutil_escape_html(implode("\n", $more_loc)));
+ implode("\n", $more_loc));
}
$info .= phutil_render_tag(
'p',
array(),
pht(
"You can find more information about PHP configuration values in the ".
"%s.",
phutil_render_tag(
'a',
array(
'href' => 'http://php.net/manual/ini.list.php',
),
pht('PHP Documentation'))));
$info .= phutil_render_tag(
'p',
array(),
pht(
"After editing the PHP configuration, <strong>restart your ".
"webserver for the changes to take effect</strong>."));
return phutil_render_tag(
'div',
array(
'class' => 'setup-issue-config',
),
$table_info.$table.$info);
}
}
diff --git a/src/applications/countdown/controller/PhabricatorCountdownListController.php b/src/applications/countdown/controller/PhabricatorCountdownListController.php
index 1f8190254d..47cc610c6d 100644
--- a/src/applications/countdown/controller/PhabricatorCountdownListController.php
+++ b/src/applications/countdown/controller/PhabricatorCountdownListController.php
@@ -1,95 +1,95 @@
<?php
final class PhabricatorCountdownListController
extends PhabricatorCountdownController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page'));
$pager->setURI($request->getRequestURI(), 'page');
$timers = id(new PhabricatorTimer())->loadAllWhere(
'1 = 1 ORDER BY id DESC LIMIT %d, %d',
$pager->getOffset(),
$pager->getPageSize() + 1);
$timers = $pager->sliceResults($timers);
$phids = mpull($timers, 'getAuthorPHID');
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($timers as $timer) {
$edit_button = null;
$delete_button = null;
if ($user->getIsAdmin() ||
($user->getPHID() == $timer->getAuthorPHID())) {
$edit_button = phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/countdown/edit/'.$timer->getID().'/'
),
'Edit');
$delete_button = javelin_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => '/countdown/delete/'.$timer->getID().'/',
'sigil' => 'workflow'
),
'Delete');
}
$rows[] = array(
phutil_escape_html($timer->getID()),
$handles[$timer->getAuthorPHID()]->renderLink(),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/countdown/'.$timer->getID().'/',
),
- phutil_escape_html($timer->getTitle())),
+ $timer->getTitle()),
phabricator_datetime($timer->getDatepoint(), $user),
$edit_button,
$delete_button,
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'ID',
'Author',
'Title',
'End Date',
'',
''
));
$table->setColumnClasses(
array(
null,
null,
'wide pri',
null,
'action',
'action',
));
$panel = id(new AphrontPanelView())
->appendChild($table)
->setHeader('Timers')
->setCreateButton('Create Timer', '/countdown/edit/')
->appendChild($pager);
return $this->buildStandardPageResponse($panel,
array(
'title' => 'Countdown',
));
}
}
diff --git a/src/applications/daemon/view/PhabricatorDaemonLogEventsView.php b/src/applications/daemon/view/PhabricatorDaemonLogEventsView.php
index 57a3a5db2e..732b3b6a44 100644
--- a/src/applications/daemon/view/PhabricatorDaemonLogEventsView.php
+++ b/src/applications/daemon/view/PhabricatorDaemonLogEventsView.php
@@ -1,109 +1,109 @@
<?php
final class PhabricatorDaemonLogEventsView extends AphrontView {
private $events;
private $combinedLog;
public function setEvents(array $events) {
assert_instances_of($events, 'PhabricatorDaemonLogEvent');
$this->events = $events;
return $this;
}
public function setCombinedLog($is_combined) {
$this->combinedLog = $is_combined;
return $this;
}
public function render() {
$rows = array();
if (!$this->user) {
throw new Exception("Call setUser() before rendering!");
}
foreach ($this->events as $event) {
// Limit display log size. If a daemon gets stuck in an output loop this
// page can be like >100MB if we don't truncate stuff. Try to do cheap
// line-based truncation first, and fall back to expensive UTF-8 character
// truncation if that doesn't get things short enough.
$message = $event->getMessage();
$more_lines = null;
$more_chars = null;
$line_limit = 12;
if (substr_count($message, "\n") > $line_limit) {
$message = explode("\n", $message);
$more_lines = count($message) - $line_limit;
$message = array_slice($message, 0, $line_limit);
$message = implode("\n", $message);
}
$char_limit = 8192;
if (strlen($message) > $char_limit) {
$message = phutil_utf8v($message);
$more_chars = count($message) - $char_limit;
$message = array_slice($message, 0, $char_limit);
$message = implode('', $message);
}
$more = null;
if ($more_chars) {
$more = number_format($more_chars);
$more = "\n<... {$more} more characters ...>";
} else if ($more_lines) {
$more = number_format($more_lines);
$more = "\n<... {$more} more lines ...>";
}
$row = array(
phutil_escape_html($event->getLogType()),
phabricator_date($event->getEpoch(), $this->user),
phabricator_time($event->getEpoch(), $this->user),
str_replace("\n", '<br />', phutil_escape_html($message.$more)),
);
if ($this->combinedLog) {
array_unshift(
$row,
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/daemon/log/'.$event->getLogID().'/',
),
- phutil_escape_html('Daemon '.$event->getLogID())));
+ 'Daemon '.$event->getLogID()));
}
$rows[] = $row;
}
$classes = array(
'',
'',
'right',
'wide wrap',
);
$headers = array(
'Type',
'Date',
'Time',
'Message',
);
if ($this->combinedLog) {
array_unshift($classes, 'pri');
array_unshift($headers, 'Daemon');
}
$log_table = new AphrontTableView($rows);
$log_table->setHeaders($headers);
$log_table->setColumnClasses($classes);
return $log_table->render();
}
}
diff --git a/src/applications/differential/controller/DifferentialDiffViewController.php b/src/applications/differential/controller/DifferentialDiffViewController.php
index b212c20293..1ca4a9de73 100644
--- a/src/applications/differential/controller/DifferentialDiffViewController.php
+++ b/src/applications/differential/controller/DifferentialDiffViewController.php
@@ -1,156 +1,156 @@
<?php
final class DifferentialDiffViewController extends DifferentialController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$diff = id(new DifferentialDiff())->load($this->id);
if (!$diff) {
return new Aphront404Response();
}
if ($diff->getRevisionID()) {
$top_panel = new AphrontPanelView();
$top_panel->setWidth(AphrontPanelView::WIDTH_WIDE);
- $link = phutil_render_tag(
+ $link = phutil_tag(
'a',
array(
'href' => PhabricatorEnv::getURI('/D'.$diff->getRevisionID()),
),
- phutil_escape_html('D'.$diff->getRevisionID()));
+ 'D'.$diff->getRevisionID());
$top_panel->appendChild(
"<h1>".pht('This diff belongs to revision %s', $link)."</h1>");
} else {
$action_panel = new AphrontPanelView();
$action_panel->setHeader('Preview Diff');
$action_panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$action_panel->appendChild(
'<p class="aphront-panel-instructions">'.pht('Review the diff for '.
'correctness. When you are satisfied, either <strong>create a new '.
'revision</strong> or <strong>update an existing revision</strong>.'));
// TODO: implmenent optgroup support in AphrontFormSelectControl?
$select = array();
$select[] = '<optgroup label="Create New Revision">';
$select[] = '<option value="">'.
pht('Create a new Revision...').
'</option>';
$select[] = '</optgroup>';
$revision_data = new DifferentialRevisionListData(
DifferentialRevisionListData::QUERY_OPEN_OWNED,
array($request->getUser()->getPHID()));
$revisions = $revision_data->loadRevisions();
if ($revisions) {
$select[] = '<optgroup label="'.pht('Update Existing Revision').'">';
foreach ($revisions as $revision) {
- $select[] = phutil_render_tag(
+ $select[] = phutil_tag(
'option',
array(
'value' => $revision->getID(),
),
- phutil_escape_html($revision->getTitle()));
+ $revision->getTitle());
}
$select[] = '</optgroup>';
}
$select =
'<select name="revisionID">'.
implode("\n", $select).
'</select>';
$action_form = new AphrontFormView();
$action_form
->setUser($request->getUser())
->setAction('/differential/revision/edit/')
->addHiddenInput('diffID', $diff->getID())
->addHiddenInput('viaDiffView', 1)
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Attach To'))
->setValue($select))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Continue')));
$action_panel->appendChild($action_form);
$top_panel = $action_panel;
}
$props = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$diff->getID());
$props = mpull($props, 'getData', 'getName');
$aux_fields = DifferentialFieldSelector::newSelector()
->getFieldSpecifications();
foreach ($aux_fields as $key => $aux_field) {
if (!$aux_field->shouldAppearOnDiffView()) {
unset($aux_fields[$key]);
} else {
$aux_field->setUser($this->getRequest()->getUser());
}
}
$dict = array();
foreach ($aux_fields as $key => $aux_field) {
$aux_field->setDiff($diff);
$aux_field->setManualDiff($diff);
$aux_field->setDiffProperties($props);
$value = $aux_field->renderValueForDiffView();
if (strlen($value)) {
$label = rtrim($aux_field->renderLabelForDiffView(), ':');
$dict[$label] = $value;
}
}
$action_panel = new AphrontHeadsupView();
$action_panel->setProperties($dict);
$action_panel->setHeader(pht('Diff Properties'));
$changesets = $diff->loadChangesets();
$changesets = msort($changesets, 'getSortKey');
$table_of_contents = id(new DifferentialDiffTableOfContentsView())
->setChangesets($changesets)
->setVisibleChangesets($changesets)
->setUnitTestData(idx($props, 'arc:unit', array()));
$refs = array();
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
}
$details = id(new DifferentialChangesetListView())
->setChangesets($changesets)
->setVisibleChangesets($changesets)
->setRenderingReferences($refs)
->setStandaloneURI('/differential/changeset/')
->setDiff($diff)
->setTitle(pht('Diff %d', $diff->getID()))
->setUser($request->getUser());
return $this->buildStandardPageResponse(
id(new DifferentialPrimaryPaneView())
->appendChild(
array(
$top_panel->render(),
$action_panel->render(),
$table_of_contents->render(),
$details->render(),
)),
array(
'title' => pht('Diff View'),
));
}
}
diff --git a/src/applications/differential/field/specification/DifferentialTitleFieldSpecification.php b/src/applications/differential/field/specification/DifferentialTitleFieldSpecification.php
index 6a1c6ce20c..07095e0800 100644
--- a/src/applications/differential/field/specification/DifferentialTitleFieldSpecification.php
+++ b/src/applications/differential/field/specification/DifferentialTitleFieldSpecification.php
@@ -1,98 +1,98 @@
<?php
final class DifferentialTitleFieldSpecification
extends DifferentialFreeformFieldSpecification {
private $title;
private $error = true;
public function shouldAppearOnEdit() {
return true;
}
protected function didSetRevision() {
$this->title = $this->getRevision()->getTitle();
}
public function setValueFromRequest(AphrontRequest $request) {
$this->title = $request->getStr('title');
$this->error = null;
return $this;
}
public function renderEditControl() {
return id(new AphrontFormTextAreaControl())
->setLabel('Title')
->setName('title')
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setError($this->error)
->setValue($this->title);
}
public function shouldExtractMentions() {
return true;
}
public function willWriteRevision(DifferentialRevisionEditor $editor) {
$this->getRevision()->setTitle($this->title);
}
public function validateField() {
if (!strlen($this->title)) {
$this->error = 'Required';
throw new DifferentialFieldValidationException(
"You must provide a title.");
}
}
public function shouldAppearOnCommitMessage() {
return true;
}
public function getCommitMessageKey() {
return 'title';
}
public function setValueFromParsedCommitMessage($value) {
$this->title = $value;
return $this;
}
public function shouldOverwriteWhenCommitMessageIsEdited() {
return true;
}
public function renderLabelForCommitMessage() {
return 'Title';
}
public function renderValueForCommitMessage($is_edit) {
return $this->title;
}
public function parseValueFromCommitMessage($value) {
return preg_replace('/\s*\n\s*/', ' ', $value);
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Revision';
}
public function getColumnClassForRevisionList() {
return 'wide pri';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
- return phutil_render_tag(
+ return phutil_tag(
'a',
array(
'href' => '/D'.$revision->getID(),
),
- phutil_escape_html($revision->getTitle()));
+ $revision->getTitle());
}
}
diff --git a/src/applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php b/src/applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php
index e13117d2b5..dfa474be67 100644
--- a/src/applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php
+++ b/src/applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php
@@ -1,131 +1,131 @@
<?php
final class DifferentialChangesetFileTreeSideNavBuilder {
private $title;
private $baseURI;
private $anchorName;
public function setAnchorName($anchor_name) {
$this->anchorName = $anchor_name;
return $this;
}
public function getAnchorName() {
return $this->anchorName;
}
public function setBaseURI(PhutilURI $base_uri) {
$this->baseURI = $base_uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function getTitle() {
return $this->title;
}
public function build(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI($this->getBaseURI());
$nav->setFlexible(true);
$anchor = $this->getAnchorName();
$tree = new PhutilFileTree();
foreach ($changesets as $changeset) {
try {
$tree->addPath($changeset->getFilename(), $changeset);
} catch (Exception $ex) {
// TODO: See T1702. When viewing the versus diff of diffs, we may
// have files with the same filename. For example, if you have a setup
// like this in SVN:
//
// a/
// README
// b/
// README
//
// ...and you run "arc diff" once from a/, and again from b/, you'll
// get two diffs with path README. However, in the versus diff view we
// will compute their absolute repository paths and detect that they
// aren't really the same file. This is correct, but causes us to
// throw when inserting them.
//
// We should probably compute the smallest unique path for each file
// and show these as "a/README" and "b/README" when diffed against
// one another. However, we get this wrong in a lot of places (the
// other TOC shows two "README" files, and we generate the same anchor
// hash for both) so I'm just stopping the bleeding until we can get
// a proper fix in place.
}
}
require_celerity_resource('phabricator-filetree-view-css');
$filetree = array();
$path = $tree;
while (($path = $path->getNextNode())) {
$data = $path->getData();
$name = $path->getName();
$style = 'padding-left: '.(2 + (3 * $path->getDepth())).'px';
$href = null;
if ($data) {
$href = '#'.$data->getAnchorName();
$title = $name;
$icon = 'phabricator-filetree-icon-file';
} else {
$name .= '/';
$title = $path->getFullPath().'/';
$icon = 'phabricator-filetree-icon-dir';
}
$icon = phutil_render_tag(
'span',
array(
'class' => 'phabricator-filetree-icon '.$icon,
),
'');
- $name_element = phutil_render_tag(
+ $name_element = phutil_tag(
'span',
array(
'class' => 'phabricator-filetree-name',
),
- phutil_escape_html($name));
+ $name);
$filetree[] = javelin_render_tag(
$href ? 'a' : 'span',
array(
'href' => $href,
'style' => $style,
'title' => $title,
'class' => 'phabricator-filetree-item',
),
$icon.$name_element);
}
$tree->destroy();
$filetree =
'<div class="phabricator-filetree">'.
implode("\n", $filetree).
'</div>';
$nav->addLabel(pht('Changed Files'));
$nav->addCustomBlock($filetree);
$nav->setActive(true);
$nav->selectFilter(null);
return $nav;
}
}
diff --git a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
index ff00f973e9..8e7e9f926c 100644
--- a/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
+++ b/src/applications/differential/view/DifferentialRevisionUpdateHistoryView.php
@@ -1,323 +1,323 @@
<?php
final class DifferentialRevisionUpdateHistoryView extends AphrontView {
private $diffs = array();
private $selectedVersusDiffID;
private $selectedDiffID;
private $selectedWhitespace;
public function setDiffs(array $diffs) {
assert_instances_of($diffs, 'DifferentialDiff');
$this->diffs = $diffs;
return $this;
}
public function setSelectedVersusDiffID($id) {
$this->selectedVersusDiffID = $id;
return $this;
}
public function setSelectedDiffID($id) {
$this->selectedDiffID = $id;
return $this;
}
public function setSelectedWhitespace($whitespace) {
$this->selectedWhitespace = $whitespace;
return $this;
}
public function render() {
require_celerity_resource('differential-core-view-css');
require_celerity_resource('differential-revision-history-css');
$data = array(
array(
'name' => 'Base',
'id' => null,
'desc' => 'Base',
'age' => null,
'obj' => null,
),
);
$seq = 0;
foreach ($this->diffs as $diff) {
$data[] = array(
'name' => 'Diff '.(++$seq),
'id' => $diff->getID(),
'desc' => $diff->getDescription(),
'age' => $diff->getDateCreated(),
'obj' => $diff,
);
}
$max_id = $diff->getID();
$idx = 0;
$rows = array();
$disable = false;
$radios = array();
$last_base = null;
foreach ($data as $row) {
$diff = $row['obj'];
$name = $row['name'];
$id = $row['id'];
$old_class = null;
$new_class = null;
if ($id) {
$new_checked = ($this->selectedDiffID == $id);
$new = javelin_render_tag(
'input',
array(
'type' => 'radio',
'name' => 'id',
'value' => $id,
'checked' => $new_checked ? 'checked' : null,
'sigil' => 'differential-new-radio',
));
if ($new_checked) {
$new_class = " revhistory-new-now";
$disable = true;
}
} else {
$new = null;
}
if ($max_id != $id) {
$uniq = celerity_generate_unique_node_id();
$old_checked = ($this->selectedVersusDiffID == $id);
$old = phutil_tag(
'input',
array(
'type' => 'radio',
'name' => 'vs',
'value' => $id,
'id' => $uniq,
'checked' => $old_checked ? 'checked' : null,
'disabled' => $disable ? 'disabled' : null,
));
$radios[] = $uniq;
if ($old_checked) {
$old_class = " revhistory-old-now";
}
} else {
$old = null;
}
$desc = $row['desc'];
if ($row['age']) {
$age = phabricator_datetime($row['age'], $this->getUser());
} else {
$age = null;
}
if (++$idx % 2) {
$class = ' class="alt"';
} else {
$class = null;
}
if ($diff) {
$lint = self::renderDiffLintStar($row['obj']);
$unit = self::renderDiffUnitStar($row['obj']);
$lint_message = self::getDiffLintMessage($diff);
$unit_message = self::getDiffUnitMessage($diff);
$lint_title = ' title="'.phutil_escape_html($lint_message).'"';
$unit_title = ' title="'.phutil_escape_html($unit_message).'"';
$base = $this->renderBaseRevision($diff);
} else {
$lint = null;
$unit = null;
$lint_title = null;
$unit_title = null;
$base = null;
}
if ($last_base !== null && $base !== $last_base) {
// TODO: Render some kind of notice about rebases.
}
$last_base = $base;
- $id_link = phutil_render_tag(
+ $id_link = phutil_tag(
'a',
array('href' => '/differential/diff/'.$id.'/'),
- phutil_escape_html($id));
+ $id);
$rows[] =
'<tr'.$class.'>'.
'<td class="revhistory-name">'.phutil_escape_html($name).'</td>'.
'<td class="revhistory-id">'.$id_link.'</td>'.
'<td class="revhistory-base">'.phutil_escape_html($base).'</td>'.
'<td class="revhistory-desc">'.phutil_escape_html($desc).'</td>'.
'<td class="revhistory-age">'.$age.'</td>'.
'<td class="revhistory-star"'.$lint_title.'>'.$lint.'</td>'.
'<td class="revhistory-star"'.$unit_title.'>'.$unit.'</td>'.
'<td class="revhistory-old'.$old_class.'">'.$old.'</td>'.
'<td class="revhistory-new'.$new_class.'">'.$new.'</td>'.
'</tr>';
}
Javelin::initBehavior(
'differential-diff-radios',
array(
'radios' => $radios,
));
$options = array(
DifferentialChangesetParser::WHITESPACE_IGNORE_FORCE => 'Ignore All',
DifferentialChangesetParser::WHITESPACE_IGNORE_ALL => 'Ignore Most',
DifferentialChangesetParser::WHITESPACE_IGNORE_TRAILING =>
'Ignore Trailing',
DifferentialChangesetParser::WHITESPACE_SHOW_ALL => 'Show All',
);
$select = '<select name="whitespace">';
foreach ($options as $value => $label) {
- $select .= phutil_render_tag(
+ $select .= phutil_tag(
'option',
array(
'value' => $value,
'selected' => ($value == $this->selectedWhitespace)
? 'selected'
: null,
),
- phutil_escape_html($label));
+ $label);
}
$select .= '</select>';
return
id(new PhabricatorHeaderView())
->setHeader(pht('Revision Update History'))
->render() .
'<div class="differential-revision-history differential-panel">'.
'<form action="#toc">'.
'<table class="differential-revision-history-table">'.
'<tr>'.
'<th>'.pht('Diff').'</th>'.
'<th>'.pht('ID').'</th>'.
'<th>'.pht('Base').'</th>'.
'<th>'.pht('Description').'</th>'.
'<th>'.pht('Created').'</th>'.
'<th>'.pht('Lint').'</th>'.
'<th>'.pht('Unit').'</th>'.
'</tr>'.
implode("\n", $rows).
'<tr>'.
'<td colspan="9" class="diff-differ-submit">'.
'<label>'.pht('Whitespace Changes: %s', $select).'</label>'.
'<button>'.pht('Show Diff').'</button>'.
'</td>'.
'</tr>'.
'</table>'.
'</form>'.
'</div>';
}
const STAR_NONE = 'none';
const STAR_OKAY = 'okay';
const STAR_WARN = 'warn';
const STAR_FAIL = 'fail';
const STAR_SKIP = 'skip';
public static function renderDiffLintStar(DifferentialDiff $diff) {
static $map = array(
DifferentialLintStatus::LINT_NONE => self::STAR_NONE,
DifferentialLintStatus::LINT_OKAY => self::STAR_OKAY,
DifferentialLintStatus::LINT_WARN => self::STAR_WARN,
DifferentialLintStatus::LINT_FAIL => self::STAR_FAIL,
DifferentialLintStatus::LINT_SKIP => self::STAR_SKIP,
DifferentialLintStatus::LINT_POSTPONED => self::STAR_SKIP
);
$star = idx($map, $diff->getLintStatus(), self::STAR_FAIL);
return self::renderDiffStar($star);
}
public static function renderDiffUnitStar(DifferentialDiff $diff) {
static $map = array(
DifferentialUnitStatus::UNIT_NONE => self::STAR_NONE,
DifferentialUnitStatus::UNIT_OKAY => self::STAR_OKAY,
DifferentialUnitStatus::UNIT_WARN => self::STAR_WARN,
DifferentialUnitStatus::UNIT_FAIL => self::STAR_FAIL,
DifferentialUnitStatus::UNIT_SKIP => self::STAR_SKIP,
DifferentialUnitStatus::UNIT_POSTPONED => self::STAR_SKIP,
);
$star = idx($map, $diff->getUnitStatus(), self::STAR_FAIL);
return self::renderDiffStar($star);
}
public static function getDiffLintMessage(DifferentialDiff $diff) {
switch ($diff->getLintStatus()) {
case DifferentialLintStatus::LINT_NONE:
return 'No Linters Available';
case DifferentialLintStatus::LINT_OKAY:
return 'Lint OK';
case DifferentialLintStatus::LINT_WARN:
return 'Lint Warnings';
case DifferentialLintStatus::LINT_FAIL:
return 'Lint Errors';
case DifferentialLintStatus::LINT_SKIP:
return 'Lint Skipped';
case DifferentialLintStatus::LINT_POSTPONED:
return 'Lint Postponed';
}
return '???';
}
public static function getDiffUnitMessage(DifferentialDiff $diff) {
switch ($diff->getUnitStatus()) {
case DifferentialUnitStatus::UNIT_NONE:
return 'No Unit Test Coverage';
case DifferentialUnitStatus::UNIT_OKAY:
return 'Unit Tests OK';
case DifferentialUnitStatus::UNIT_WARN:
return 'Unit Test Warnings';
case DifferentialUnitStatus::UNIT_FAIL:
return 'Unit Test Errors';
case DifferentialUnitStatus::UNIT_SKIP:
return 'Unit Tests Skipped';
case DifferentialUnitStatus::UNIT_POSTPONED:
return 'Unit Tests Postponed';
}
return '???';
}
private static function renderDiffStar($star) {
$class = 'diff-star-'.$star;
return
'<span class="'.$class.'">'.
"\xE2\x98\x85".
'</span>';
}
private function renderBaseRevision(DifferentialDiff $diff) {
switch ($diff->getSourceControlSystem()) {
case 'git':
$base = $diff->getSourceControlBaseRevision();
if (strpos($base, '@') === false) {
return substr($base, 0, 7);
} else {
// The diff is from git-svn
$base = explode('@', $base);
$base = last($base);
return $base;
}
case 'svn':
$base = $diff->getSourceControlBaseRevision();
$base = explode('@', $base);
$base = last($base);
return $base;
default:
return null;
}
}
}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseFileController.php b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
index edab2227ed..c0835332f4 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseFileController.php
+++ b/src/applications/diffusion/controller/DiffusionBrowseFileController.php
@@ -1,948 +1,948 @@
<?php
final class DiffusionBrowseFileController extends DiffusionController {
private $corpusType = 'text';
private $lintCommit;
private $lintMessages;
public function processRequest() {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$before = $request->getStr('before');
if ($before) {
return $this->buildBeforeResponse($before);
}
$path = $drequest->getPath();
$selected = $request->getStr('view');
$preferences = $request->getUser()->loadPreferences();
if (!$selected) {
$selected = $preferences->getPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_VIEW,
'highlighted');
} else if ($request->isFormPost() && $selected != 'raw') {
$preferences->setPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_VIEW,
$selected);
$preferences->save();
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI()->alter('view', $selected));
}
$needs_blame = ($selected == 'blame' || $selected == 'plainblame');
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
$this->diffusionRequest);
$file_query->setViewer($request->getUser());
$file_query->setNeedsBlame($needs_blame);
$file_query->loadFileContent();
$data = $file_query->getRawData();
if ($selected === 'raw') {
return $this->buildRawResponse($path, $data);
}
$this->loadLintMessages();
// Build the content of the file.
$corpus = $this->buildCorpus(
$selected,
$file_query,
$needs_blame,
$drequest,
$path,
$data);
require_celerity_resource('diffusion-source-css');
if ($this->corpusType == 'text') {
$view_select_panel = $this->renderViewSelectPanel($selected);
} else {
$view_select_panel = null;
}
// Render the page.
$content = array();
$follow = $request->getStr('follow');
if ($follow) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$notice->setTitle('Unable to Continue');
switch ($follow) {
case 'first':
$notice->appendChild(
"Unable to continue tracing the history of this file because ".
"this commit is the first commit in the repository.");
break;
case 'created':
$notice->appendChild(
"Unable to continue tracing the history of this file because ".
"this commit created the file.");
break;
}
$content[] = $notice;
}
$renamed = $request->getStr('renamed');
if ($renamed) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('File Renamed');
$notice->appendChild(
"File history passes through a rename from '".
phutil_escape_html($drequest->getPath())."' to '".
phutil_escape_html($renamed)."'.");
$content[] = $notice;
}
$content[] = $view_select_panel;
$content[] = $corpus;
$content[] = $this->buildOpenRevisions();
$nav = $this->buildSideNav('browse', true);
$nav->appendChild($content);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$nav->setCrumbs($crumbs);
$basename = basename($this->getDiffusionRequest()->getPath());
return $this->buildApplicationPage(
$nav,
array(
'title' => $basename,
));
}
private function loadLintMessages() {
$drequest = $this->getDiffusionRequest();
$branch = $drequest->loadBranch();
if (!$branch || !$branch->getLintCommit()) {
return;
}
$this->lintCommit = $branch->getLintCommit();
$conn = id(new PhabricatorRepository())->establishConnection('r');
$where = '';
if ($drequest->getLint()) {
$where = qsprintf(
$conn,
'AND code = %s',
$drequest->getLint());
}
$this->lintMessages = queryfx_all(
$conn,
'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
PhabricatorRepository::TABLE_LINTMESSAGE,
$branch->getID(),
$where,
'/'.$drequest->getPath());
}
private function buildCorpus($selected,
DiffusionFileContentQuery $file_query,
$needs_blame,
DiffusionRequest $drequest,
$path,
$data) {
if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
$file = $this->loadFileForData($path, $data);
$file_uri = $file->getBestURI();
if ($file->isViewableImage()) {
$this->corpusType = 'image';
return $this->buildImageCorpus($file_uri);
} else {
$this->corpusType = 'binary';
return $this->buildBinaryCorpus($file_uri, $data);
}
}
switch ($selected) {
case 'plain':
$style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
- $corpus = phutil_render_tag(
+ $corpus = phutil_tag(
'textarea',
array(
'style' => $style,
),
- phutil_escape_html($file_query->getRawData()));
+ $file_query->getRawData());
break;
case 'plainblame':
$style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
list($text_list, $rev_list, $blame_dict) =
$file_query->getBlameData();
$rows = array();
foreach ($text_list as $k => $line) {
$rev = $rev_list[$k];
if (isset($blame_dict[$rev]['handle'])) {
$author = $blame_dict[$rev]['handle']->getName();
} else {
$author = $blame_dict[$rev]['author'];
}
$rows[] =
sprintf("%-10s %-20s %s", substr($rev, 0, 7), $author, $line);
}
- $corpus = phutil_render_tag(
+ $corpus = phutil_tag(
'textarea',
array(
'style' => $style,
),
- phutil_escape_html(implode("\n", $rows)));
+ implode("\n", $rows));
break;
case 'highlighted':
case 'blame':
default:
require_celerity_resource('syntax-highlighting-css');
list($text_list, $rev_list, $blame_dict) = $file_query->getBlameData();
$text_list = implode("\n", $text_list);
$text_list = PhabricatorSyntaxHighlighter::highlightWithFilename(
$path,
$text_list);
$text_list = explode("\n", $text_list);
$rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict,
$needs_blame, $drequest, $file_query, $selected);
$id = celerity_generate_unique_node_id();
$projects = $drequest->loadArcanistProjects();
$langs = array();
foreach ($projects as $project) {
$ls = $project->getSymbolIndexLanguages();
if (!$ls) {
continue;
}
$dep_projects = $project->getSymbolIndexProjects();
$dep_projects[] = $project->getPHID();
foreach ($ls as $lang) {
if (!isset($langs[$lang])) {
$langs[$lang] = array();
}
$langs[$lang] += $dep_projects + array($project);
}
}
$lang = last(explode('.', $drequest->getPath()));
$prefs = $this->getRequest()->getUser()->loadPreferences();
$pref_symbols = $prefs->getPreference(
PhabricatorUserPreferences::PREFERENCE_DIFFUSION_SYMBOLS);
if (isset($langs[$lang]) && $pref_symbols != 'disabled') {
Javelin::initBehavior(
'repository-crossreference',
array(
'container' => $id,
'lang' => $lang,
'projects' => $langs[$lang],
));
}
$corpus_table = javelin_render_tag(
'table',
array(
'class' => "diffusion-source remarkup-code PhabricatorMonospaced",
'sigil' => 'diffusion-source',
),
implode("\n", $rows));
$corpus = phutil_render_tag(
'div',
array(
'style' => 'padding: 0 2em;',
'id' => $id,
),
$corpus_table);
break;
}
return $corpus;
}
private function renderViewSelectPanel($selected) {
$toggle_blame = array(
'highlighted' => 'blame',
'blame' => 'highlighted',
'plain' => 'plainblame',
'plainblame' => 'plain',
'raw' => 'raw', // not a real case.
);
$toggle_highlight = array(
'highlighted' => 'plain',
'blame' => 'plainblame',
'plain' => 'highlighted',
'plainblame' => 'blame',
'raw' => 'raw', // not a real case.
);
$user = $this->getRequest()->getUser();
$base_uri = $this->getRequest()->getRequestURI();
$blame_on = ($selected == 'blame' || $selected == 'plainblame');
if ($blame_on) {
$blame_text = pht('Disable Blame');
} else {
$blame_text = pht('Enable Blame');
}
$blame_button = $this->createViewAction(
$blame_text,
$base_uri->alter('view', $toggle_blame[$selected]),
$user);
$highlight_on = ($selected == 'blame' || $selected == 'highlighted');
if ($highlight_on) {
$highlight_text = pht('Disable Highlighting');
} else {
$highlight_text = pht('Enable Highlighting');
}
$highlight_button = $this->createViewAction(
$highlight_text,
$base_uri->alter('view', $toggle_highlight[$selected]),
$user);
$href = null;
if ($this->getRequest()->getStr('lint') !== null) {
$lint_text = pht('Hide %d Lint Message(s)', count($this->lintMessages));
$href = $base_uri->alter('lint', null);
} else if ($this->lintCommit === null) {
$lint_text = pht('Lint not Available');
} else {
$lint_text = pht(
'Show %d Lint Message(s)',
count($this->lintMessages));
$href = $this->getDiffusionRequest()->generateURI(array(
'action' => 'browse',
'commit' => $this->lintCommit,
))->alter('lint', '');
}
$lint_button = $this->createViewAction(
$lint_text,
$href,
$user);
if (!$href) {
$lint_button->setDisabled(true);
}
$raw_button = $this->createViewAction(
pht('View Raw File'),
$base_uri->alter('view', 'raw'),
$user,
'file');
$edit_button = $this->createEditAction();
return id(new PhabricatorActionListView())
->setUser($user)
->addAction($blame_button)
->addAction($highlight_button)
->addAction($lint_button)
->addAction($raw_button)
->addAction($edit_button);
}
private function createViewAction(
$localized_text,
$href,
$user,
$icon = null) {
return id(new PhabricatorActionView())
->setName($localized_text)
->setIcon($icon)
->setUser($user)
->setRenderAsForm(true)
->setHref($href);
}
private function createEditAction() {
$request = $this->getRequest();
$user = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
$line = nonempty((int)$drequest->getLine(), 1);
$callsign = $repository->getCallsign();
$editor_link = $user->loadEditorLink($path, $line, $callsign);
$action = id(new PhabricatorActionView())
->setName(pht('Open in Editor'))
->setIcon('edit');
$action->setHref($editor_link);
$action->setDisabled(!$editor_link);
return $action;
}
private function buildDisplayRows(
array $text_list,
array $rev_list,
array $blame_dict,
$needs_blame,
DiffusionRequest $drequest,
DiffusionFileContentQuery $file_query,
$selected) {
if ($blame_dict) {
$epoch_list = ipull(ifilter($blame_dict, 'epoch'), 'epoch');
$epoch_min = min($epoch_list);
$epoch_max = max($epoch_list);
$epoch_range = ($epoch_max - $epoch_min) + 1;
}
$line_arr = array();
$line_str = $drequest->getLine();
$ranges = explode(',', $line_str);
foreach ($ranges as $range) {
if (strpos($range, '-') !== false) {
list($min, $max) = explode('-', $range, 2);
$line_arr[] = array(
'min' => min($min, $max),
'max' => max($min, $max),
);
} else if (strlen($range)) {
$line_arr[] = array(
'min' => $range,
'max' => $range,
);
}
}
$display = array();
$line_number = 1;
$last_rev = null;
$color = null;
foreach ($text_list as $k => $line) {
$display_line = array(
'color' => null,
'epoch' => null,
'commit' => null,
'author' => null,
'target' => null,
'highlighted' => null,
'line' => $line_number,
'data' => $line,
);
if ($needs_blame) {
// If the line's rev is same as the line above, show empty content
// with same color; otherwise generate blame info. The newer a change
// is, the more saturated the color.
$rev = idx($rev_list, $k, $last_rev);
if ($last_rev == $rev) {
$display_line['color'] = $color;
} else {
$blame = $blame_dict[$rev];
if (!isset($blame['epoch'])) {
$color = '#ffd'; // Render as warning.
} else {
$color_ratio = ($blame['epoch'] - $epoch_min) / $epoch_range;
$color_value = 0xF6 * (1.0 - $color_ratio);
$color = sprintf(
'#%02x%02x%02x',
$color_value,
0xF6,
$color_value);
}
$display_line['epoch'] = idx($blame, 'epoch');
$display_line['color'] = $color;
$display_line['commit'] = $rev;
if (isset($blame['handle'])) {
$author_link = $blame['handle']->renderLink();
} else {
- $author_link = phutil_render_tag(
+ $author_link = phutil_tag(
'span',
array(
),
- phutil_escape_html($blame['author']));
+ $blame['author']);
}
$display_line['author'] = $author_link;
$last_rev = $rev;
}
}
if ($line_arr) {
if ($line_number == $line_arr[0]['min']) {
$display_line['target'] = true;
}
foreach ($line_arr as $range) {
if ($line_number >= $range['min'] &&
$line_number <= $range['max']) {
$display_line['highlighted'] = true;
}
}
}
$display[] = $display_line;
++$line_number;
}
$commits = array_filter(ipull($display, 'commit'));
if ($commits) {
$commits = id(new PhabricatorAuditCommitQuery())
->withIdentifiers($drequest->getRepository()->getID(), $commits)
->needCommitData(true)
->execute();
$commits = mpull($commits, null, 'getCommitIdentifier');
}
$revision_ids = id(new DifferentialRevision())
->loadIDsByCommitPHIDs(mpull($commits, 'getPHID'));
$revisions = array();
if ($revision_ids) {
$revisions = id(new DifferentialRevision())->loadAllWhere(
'id IN (%Ld)',
$revision_ids);
}
$request = $this->getRequest();
$user = $request->getUser();
Javelin::initBehavior('phabricator-oncopy', array());
$engine = null;
$inlines = array();
if ($this->getRequest()->getStr('lint') !== null && $this->lintMessages) {
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($user);
foreach ($this->lintMessages as $message) {
$inline = id(new PhabricatorAuditInlineComment())
->setID($message['id'])
->setSyntheticAuthor(
ArcanistLintSeverity::getStringForSeverity($message['severity']).
' '.$message['code'].' ('.$message['name'].')')
->setLineNumber($message['line'])
->setContent($message['description']);
$inlines[$message['line']][] = $inline;
$engine->addObject(
$inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
}
$engine->process();
require_celerity_resource('differential-changeset-view-css');
}
$rows = $this->renderInlines(
idx($inlines, 0, array()),
$needs_blame,
$engine);
foreach ($display as $line) {
$line_href = $drequest->generateURI(
array(
'action' => 'browse',
'line' => $line['line'],
'stable' => true,
));
$blame = array();
if ($line['color']) {
$color = $line['color'];
$before_link = null;
$commit_link = null;
$revision_link = null;
if (idx($line, 'commit')) {
$commit = $line['commit'];
$summary = 'Unknown';
if (idx($commits, $commit)) {
$summary = $commits[$commit]->getCommitData()->getSummary();
}
$tooltip = phabricator_date(
$line['epoch'],
$user)." \xC2\xB7 ".$summary;
Javelin::initBehavior('phabricator-tooltips', array());
require_celerity_resource('aphront-tooltip-css');
$commit_link = javelin_render_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'commit',
'commit' => $line['commit'],
)),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
),
phutil_escape_html(phutil_utf8_shorten($line['commit'], 9, '')));
$revision_id = null;
if (idx($commits, $commit)) {
$revision_id = idx($revision_ids, $commits[$commit]->getPHID());
}
if ($revision_id) {
$revision = idx($revisions, $revision_id);
if (!$revision) {
$tooltip = '(Invalid revision)';
} else {
$tooltip =
phabricator_date($revision->getDateModified(), $user).
" \xC2\xB7 ".
$revision->getTitle();
}
$revision_link = javelin_render_tag(
'a',
array(
'href' => '/D'.$revision_id,
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $tooltip,
'align' => 'E',
'size' => 600,
),
),
'D'.$revision_id);
}
$uri = $line_href->alter('before', $commit);
$before_link = javelin_render_tag(
'a',
array(
'href' => $uri->setQueryParam('view', 'blame'),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'Skip Past This Commit',
'align' => 'E',
'size' => 300,
),
),
"\xC2\xAB");
}
$blame[] = phutil_render_tag(
'th',
array(
'class' => 'diffusion-blame-link',
'style' => 'background: '.$color,
),
$before_link);
$blame[] = phutil_render_tag(
'th',
array(
'class' => 'diffusion-rev-link',
'style' => 'background: '.$color,
),
$commit_link);
$blame[] = phutil_render_tag(
'th',
array(
'class' => 'diffusion-rev-link',
'style' => 'background: '.$color,
),
$revision_link);
$blame[] = phutil_render_tag(
'th',
array(
'class' => 'diffusion-author-link',
'style' => 'background: '.$color,
),
idx($line, 'author'));
}
- $line_link = phutil_render_tag(
+ $line_link = phutil_tag(
'a',
array(
'href' => $line_href,
),
- phutil_escape_html($line['line']));
+ $line['line']);
$blame[] = javelin_render_tag(
'th',
array(
'class' => 'diffusion-line-link',
'sigil' => 'diffusion-line-link',
'style' => isset($color) ? 'background: '.$color : null,
),
$line_link);
Javelin::initBehavior('diffusion-line-linker');
$blame = implode('', $blame);
if ($line['target']) {
Javelin::initBehavior(
'diffusion-jump-to',
array(
'target' => 'scroll_target',
));
$anchor_text = '<a id="scroll_target"></a>';
} else {
$anchor_text = null;
}
$line_text = phutil_render_tag(
'td',
array(
),
$anchor_text.
"\xE2\x80\x8B". // NOTE: See phabricator-oncopy behavior.
$line['data']);
$rows[] = phutil_render_tag(
'tr',
array(
'class' => ($line['highlighted'] ? 'highlighted' : null),
),
$blame.
$line_text);
$rows = array_merge($rows, $this->renderInlines(
idx($inlines, $line['line'], array()),
$needs_blame,
$engine));
}
return $rows;
}
private function renderInlines(array $inlines, $needs_blame, $engine) {
$rows = array();
foreach ($inlines as $inline) {
$inline_view = id(new DifferentialInlineCommentView())
->setMarkupEngine($engine)
->setInlineComment($inline)
->render();
$rows[] =
'<tr class="inline">'.
str_repeat('<th></th>', ($needs_blame ? 5 : 1)).
'<td>'.$inline_view.'</td>'.
'</tr>';
}
return $rows;
}
private function loadFileForData($path, $data) {
return PhabricatorFile::buildFromFileDataOrHash(
$data,
array(
'name' => basename($path),
));
}
private function buildRawResponse($path, $data) {
$file = $this->loadFileForData($path, $data);
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
}
private function buildImageCorpus($file_uri) {
$properties = new PhabricatorPropertyListView();
$properties->addProperty(
pht('Image'),
phutil_tag(
'img',
array(
'src' => $file_uri,
)));
$actions = id(new PhabricatorActionListView())
->setUser($this->getRequest()->getUser())
->addAction($this->createEditAction());
return array($actions, $properties);
}
private function buildBinaryCorpus($file_uri, $data) {
$properties = new PhabricatorPropertyListView();
$size = strlen($data);
$properties->addTextContent(
pht('This is a binary file. It is %2$s byte(s) in length.',
$size,
PhutilTranslator::getInstance()->formatNumber($size))
);
$actions = id(new PhabricatorActionListView())
->setUser($this->getRequest()->getUser())
->addAction($this->createEditAction())
->addAction(id(new PhabricatorActionView())
->setName(pht('Download Binary File...'))
->setIcon('download')
->setHref($file_uri));
return array($actions, $properties);
}
private function buildBeforeResponse($before) {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
// NOTE: We need to get the grandparent so we can capture filename changes
// in the parent.
$parent = $this->loadParentRevisionOf($before);
$old_filename = null;
$was_created = false;
if ($parent) {
$grandparent = $this->loadParentRevisionOf(
$parent->getCommitIdentifier());
if ($grandparent) {
$rename_query = new DiffusionRenameHistoryQuery();
$rename_query->setRequest($drequest);
$rename_query->setOldCommit($grandparent->getCommitIdentifier());
$old_filename = $rename_query->loadOldFilename();
$was_created = $rename_query->getWasCreated();
}
}
$follow = null;
if ($was_created) {
// If the file was created in history, that means older commits won't
// have it. Since we know it existed at 'before', it must have been
// created then; jump there.
$target_commit = $before;
$follow = 'created';
} else if ($parent) {
// If we found a parent, jump to it. This is the normal case.
$target_commit = $parent->getCommitIdentifier();
} else {
// If there's no parent, this was probably created in the initial commit?
// And the "was_created" check will fail because we can't identify the
// grandparent. Keep the user at 'before'.
$target_commit = $before;
$follow = 'first';
}
$path = $drequest->getPath();
$renamed = null;
if ($old_filename !== null &&
$old_filename !== '/'.$path) {
$renamed = $path;
$path = $old_filename;
}
$line = null;
// If there's a follow error, drop the line so the user sees the message.
if (!$follow) {
$line = $this->getBeforeLineNumber($target_commit);
}
$before_uri = $drequest->generateURI(
array(
'action' => 'browse',
'commit' => $target_commit,
'line' => $line,
'path' => $path,
));
$before_uri->setQueryParams($request->getRequestURI()->getQueryParams());
$before_uri = $before_uri->alter('before', null);
$before_uri = $before_uri->alter('renamed', $renamed);
$before_uri = $before_uri->alter('follow', $follow);
return id(new AphrontRedirectResponse())->setURI($before_uri);
}
private function getBeforeLineNumber($target_commit) {
$drequest = $this->getDiffusionRequest();
$line = $drequest->getLine();
if (!$line) {
return null;
}
$diff_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest);
$diff_query->setAgainstCommit($target_commit);
try {
$raw_diff = $diff_query->loadRawDiff();
$old_line = 0;
$new_line = 0;
foreach (explode("\n", $raw_diff) as $text) {
if ($text[0] == '-' || $text[0] == ' ') {
$old_line++;
}
if ($text[0] == '+' || $text[0] == ' ') {
$new_line++;
}
if ($new_line == $line) {
return $old_line;
}
}
// We didn't find the target line.
return $line;
} catch (Exception $ex) {
return $line;
}
}
private function loadParentRevisionOf($commit) {
$drequest = $this->getDiffusionRequest();
$before_req = DiffusionRequest::newFromDictionary(
array(
'repository' => $drequest->getRepository(),
'commit' => $commit,
));
$query = DiffusionCommitParentsQuery::newFromDiffusionRequest($before_req);
$parents = $query->loadParents();
return head($parents);
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitBranchesController.php b/src/applications/diffusion/controller/DiffusionCommitBranchesController.php
index c456d5e9af..4b306f5b10 100644
--- a/src/applications/diffusion/controller/DiffusionCommitBranchesController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitBranchesController.php
@@ -1,32 +1,32 @@
<?php
final class DiffusionCommitBranchesController extends DiffusionController {
public function willProcessRequest(array $data) {
$this->diffusionRequest = DiffusionRequest::newFromDictionary($data);
}
public function processRequest() {
$request = $this->getDiffusionRequest();
$branch_query = DiffusionContainsQuery::newFromDiffusionRequest($request);
$branches = $branch_query->loadContainingBranches();
$branch_links = array();
foreach ($branches as $branch => $commit) {
- $branch_links[] = phutil_render_tag(
+ $branch_links[] = phutil_tag(
'a',
array(
'href' => $request->generateURI(
array(
'action' => 'browse',
'branch' => $branch,
)),
),
- phutil_escape_html($branch));
+ $branch);
}
return id(new AphrontAjaxResponse())
->setContent($branch_links ? implode(', ', $branch_links) : 'None');
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 72ba662840..8afcc4b0e7 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,923 +1,923 @@
<?php
final class DiffusionCommitController extends DiffusionController {
const CHANGES_LIMIT = 100;
private $auditAuthorityPHIDs;
private $highlightedAudits;
public function willProcessRequest(array $data) {
// This controller doesn't use blob/path stuff, just pass the dictionary
// in directly instead of using the AphrontRequest parsing mechanism.
$drequest = DiffusionRequest::newFromDictionary($data);
$this->diffusionRequest = $drequest;
}
public function processRequest() {
$drequest = $this->getDiffusionRequest();
$request = $this->getRequest();
$user = $request->getUser();
if ($request->getStr('diff')) {
return $this->buildRawDiffResponse($drequest);
}
$callsign = $drequest->getRepository()->getCallsign();
$content = array();
$repository = $drequest->getRepository();
$commit = $drequest->loadCommit();
if (!$commit) {
$query = DiffusionExistsQuery::newFromDiffusionRequest($drequest);
$exists = $query->loadExistentialData();
if (!$exists) {
return new Aphront404Response();
}
return $this->buildStandardPageResponse(
id(new AphrontErrorView())
->setTitle('Error displaying commit.')
->appendChild('Failed to load the commit because the commit has not '.
'been parsed yet.'),
array('title' => 'Commit Still Parsing')
);
}
$commit_data = $drequest->loadCommitData();
$commit->attachCommitData($commit_data);
$top_anchor = id(new PhabricatorAnchorView())
->setAnchorName('top')
->setNavigationMarker(true);
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
$changesets = null;
if ($is_foreign) {
$subpath = $commit_data->getCommitDetail('svn-subpath');
$error_panel = new AphrontErrorView();
$error_panel->setTitle('Commit Not Tracked');
$error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$error_panel->appendChild(
"This Diffusion repository is configured to track only one ".
"subdirectory of the entire Subversion repository, and this commit ".
"didn't affect the tracked subdirectory ('".
phutil_escape_html($subpath)."'), so no information is available.");
$content[] = $error_panel;
$content[] = $top_anchor;
} else {
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
require_celerity_resource('diffusion-commit-view-css');
require_celerity_resource('phabricator-remarkup-css');
$parent_query = DiffusionCommitParentsQuery::newFromDiffusionRequest(
$drequest);
$headsup_view = id(new PhabricatorHeaderView())
->setHeader('Commit Detail');
$headsup_actions = $this->renderHeadsupActionList($commit, $repository);
$commit_properties = $this->loadCommitProperties(
$commit,
$commit_data,
$parent_query->loadParents()
);
$property_list = id(new PhabricatorPropertyListView())
->setHasKeyboardShortcuts(true);
foreach ($commit_properties as $key => $value) {
$property_list->addProperty($key, $value);
}
$property_list->addTextContent(
'<div class="diffusion-commit-message phabricator-remarkup">'.
$engine->markupText($commit_data->getCommitMessage()).
'</div>'
);
$content[] = $top_anchor;
$content[] = $headsup_view;
$content[] = $headsup_actions;
$content[] = $property_list;
}
$query = new PhabricatorAuditQuery();
$query->withCommitPHIDs(array($commit->getPHID()));
$audit_requests = $query->execute();
$this->auditAuthorityPHIDs =
PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$content[] = $this->buildAuditTable($commit, $audit_requests);
$content[] = $this->buildComments($commit);
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$changes = $change_query->loadChanges();
$content[] = $this->buildMergesTable($commit);
$owners_paths = array();
if ($this->highlightedAudits) {
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
'phid IN (%Ls)',
mpull($this->highlightedAudits, 'getAuditorPHID'));
if ($packages) {
$owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'repositoryPHID = %s AND packageID IN (%Ld)',
$repository->getPHID(),
mpull($packages, 'getID'));
}
}
$change_table = new DiffusionCommitChangeTableView();
$change_table->setDiffusionRequest($drequest);
$change_table->setPathChanges($changes);
$change_table->setOwnersPaths($owners_paths);
$count = count($changes);
$bad_commit = null;
if ($count == 0) {
$bad_commit = queryfx_one(
id(new PhabricatorRepository())->establishConnection('r'),
'SELECT * FROM %T WHERE fullCommitName = %s',
PhabricatorRepository::TABLE_BADCOMMIT,
'r'.$callsign.$commit->getCommitIdentifier());
}
$pane_id = null;
if ($bad_commit) {
$error_panel = new AphrontErrorView();
$error_panel->setTitle('Bad Commit');
$error_panel->appendChild(
phutil_escape_html($bad_commit['description']));
$content[] = $error_panel;
} else if ($is_foreign) {
// Don't render anything else.
} else if (!count($changes)) {
$no_changes = new AphrontErrorView();
$no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$no_changes->setTitle('Not Yet Parsed');
// TODO: This can also happen with weird SVN changes that don't do
// anything (or only alter properties?), although the real no-changes case
// is extremely rare and might be impossible to produce organically. We
// should probably write some kind of "Nothing Happened!" change into the
// DB once we parse these changes so we can distinguish between
// "not parsed yet" and "no changes".
$no_changes->appendChild(
"This commit hasn't been fully parsed yet (or doesn't affect any ".
"paths).");
$content[] = $no_changes;
} else {
$change_panel = new AphrontPanelView();
$change_panel->setHeader("Changes (".number_format($count).")");
$change_panel->setID('toc');
if ($count > self::CHANGES_LIMIT) {
- $show_all_button = phutil_render_tag(
+ $show_all_button = phutil_tag(
'a',
array(
'class' => 'button green',
'href' => '?show_all=true',
),
- phutil_escape_html('Show All Changes'));
+ 'Show All Changes');
$warning_view = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
->setTitle('Very Large Commit')
->appendChild(
"<p>This commit is very large. Load each file individually.</p>");
$change_panel->appendChild($warning_view);
$change_panel->addButton($show_all_button);
}
$change_panel->appendChild($change_table);
$change_panel->setNoBackground();
$content[] = $change_panel;
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
$changes);
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$vcs_supports_directory_changes = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$vcs_supports_directory_changes = false;
break;
default:
throw new Exception("Unknown VCS.");
}
$references = array();
foreach ($changesets as $key => $changeset) {
$file_type = $changeset->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
if (!$vcs_supports_directory_changes) {
unset($changesets[$key]);
continue;
}
}
$references[$key] = $drequest->generateURI(
array(
'action' => 'rendering-ref',
'path' => $changeset->getFilename(),
));
}
// TODO: Some parts of the views still rely on properties of the
// DifferentialChangeset. Make the objects ephemeral to make sure we don't
// accidentally save them, and then set their ID to the appropriate ID for
// this application (the path IDs).
$path_ids = array_flip(mpull($changes, 'getPath'));
foreach ($changesets as $changeset) {
$changeset->makeEphemeral();
$changeset->setID($path_ids[$changeset->getFilename()]);
}
if ($count <= self::CHANGES_LIMIT) {
$visible_changesets = $changesets;
} else {
$visible_changesets = array();
$inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere(
'commitPHID = %s AND (auditCommentID IS NOT NULL OR authorPHID = %s)',
$commit->getPHID(),
$user->getPHID());
$path_ids = mpull($inlines, null, 'getPathID');
foreach ($changesets as $key => $changeset) {
if (array_key_exists($changeset->getID(), $path_ids)) {
$visible_changesets[$key] = $changeset;
}
}
}
$change_list_title = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier()
);
$change_list = new DifferentialChangesetListView();
$change_list->setTitle($change_list_title);
$change_list->setChangesets($changesets);
$change_list->setVisibleChangesets($visible_changesets);
$change_list->setRenderingReferences($references);
$change_list->setRenderURI('/diffusion/'.$callsign.'/diff/');
$change_list->setRepository($repository);
$change_list->setUser($user);
// pick the first branch for "Browse in Diffusion" View Option
$branches = $commit_data->getCommitDetail('seenOnBranches', array());
$first_branch = reset($branches);
$change_list->setBranch($first_branch);
$change_list->setStandaloneURI(
'/diffusion/'.$callsign.'/diff/');
$change_list->setRawFileURIs(
// TODO: Implement this, somewhat tricky if there's an octopus merge
// or whatever?
null,
'/diffusion/'.$callsign.'/diff/?view=r');
$change_list->setInlineCommentControllerURI(
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
$change_references = array();
foreach ($changesets as $key => $changeset) {
$change_references[$changeset->getID()] = $references[$key];
}
$change_table->setRenderingReferences($change_references);
// TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better.
require_celerity_resource('differential-core-view-css');
$pane_id = celerity_generate_unique_node_id();
$add_comment_view = $this->renderAddCommentPanel($commit,
$audit_requests,
$pane_id);
$main_pane = phutil_render_tag(
'div',
array(
'id' => $pane_id
),
$change_list->render().
id(new PhabricatorAnchorView())
->setAnchorName('comment')
->setNavigationMarker(true)
->render().
$add_comment_view);
$content[] = $main_pane;
}
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
$short_name = DiffusionView::nameCommit(
$repository,
$commit->getCommitIdentifier()
);
$crumbs = $this->buildCrumbs(array(
'commit' => true,
));
if ($changesets) {
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setAnchorName('top')
->setTitle($short_name)
->setBaseURI(new PhutilURI('/'.$commit_id))
->build($changesets)
->setCrumbs($crumbs)
->appendChild($content);
$content = $nav;
} else {
$content = array($crumbs, $content);
}
return $this->buildApplicationPage(
$content,
array(
'title' => $commit_id
)
);
}
private function loadCommitProperties(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data,
array $parents) {
assert_instances_of($parents, 'PhabricatorRepositoryCommit');
$user = $this->getRequest()->getUser();
$commit_phid = $commit->getPHID();
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid))
->withEdgeTypes(array(
PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK,
PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT
))
->execute();
$task_phids = array_keys(
$edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_TASK]
);
$proj_phids = array_keys(
$edges[$commit_phid][PhabricatorEdgeConfig::TYPE_COMMIT_HAS_PROJECT]
);
$phids = array_merge($task_phids, $proj_phids);
if ($data->getCommitDetail('authorPHID')) {
$phids[] = $data->getCommitDetail('authorPHID');
}
if ($data->getCommitDetail('reviewerPHID')) {
$phids[] = $data->getCommitDetail('reviewerPHID');
}
if ($data->getCommitDetail('committerPHID')) {
$phids[] = $data->getCommitDetail('committerPHID');
}
if ($data->getCommitDetail('differential.revisionPHID')) {
$phids[] = $data->getCommitDetail('differential.revisionPHID');
}
if ($parents) {
foreach ($parents as $parent) {
$phids[] = $parent->getPHID();
}
}
$handles = array();
if ($phids) {
$handles = $this->loadViewerHandles($phids);
}
$props = array();
if ($commit->getAuditStatus()) {
$status = PhabricatorAuditCommitStatusConstants::getStatusName(
$commit->getAuditStatus());
- $props['Status'] = phutil_render_tag(
+ $props['Status'] = phutil_tag(
'strong',
array(),
- phutil_escape_html($status));
+ $status);
}
$props['Committed'] = phabricator_datetime($commit->getEpoch(), $user);
$author_phid = $data->getCommitDetail('authorPHID');
if ($data->getCommitDetail('authorPHID')) {
$props['Author'] = $handles[$author_phid]->renderLink();
} else {
$props['Author'] = phutil_escape_html($data->getAuthorName());
}
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$props['Reviewer'] = $handles[$reviewer_phid]->renderLink();
}
$committer = $data->getCommitDetail('committer');
if ($committer) {
$committer_phid = $data->getCommitDetail('committerPHID');
if ($data->getCommitDetail('committerPHID')) {
$props['Committer'] = $handles[$committer_phid]->renderLink();
} else {
$props['Committer'] = phutil_escape_html($committer);
}
}
$revision_phid = $data->getCommitDetail('differential.revisionPHID');
if ($revision_phid) {
$props['Differential Revision'] = $handles[$revision_phid]->renderLink();
}
if ($parents) {
$parent_links = array();
foreach ($parents as $parent) {
$parent_links[] = $handles[$parent->getPHID()]->renderLink();
}
$props['Parents'] = implode(' &middot; ', $parent_links);
}
$request = $this->getDiffusionRequest();
$props['Branches'] = '<span id="commit-branches">Unknown</span>';
$props['Tags'] = '<span id="commit-tags">Unknown</span>';
$callsign = $request->getRepository()->getCallsign();
$root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier();
Javelin::initBehavior(
'diffusion-commit-branches',
array(
$root.'/branches/' => 'commit-branches',
$root.'/tags/' => 'commit-tags',
));
$refs = $this->buildRefs($request);
if ($refs) {
$props['References'] = $refs;
}
if ($task_phids) {
$task_list = array();
foreach ($task_phids as $phid) {
$task_list[] = $handles[$phid]->renderLink();
}
$task_list = implode('<br />', $task_list);
$props['Tasks'] = $task_list;
}
if ($proj_phids) {
$proj_list = array();
foreach ($proj_phids as $phid) {
$proj_list[] = $handles[$phid]->renderLink();
}
$proj_list = implode('<br />', $proj_list);
$props['Projects'] = $proj_list;
}
return $props;
}
private function buildAuditTable(
PhabricatorRepositoryCommit $commit,
array $audits) {
assert_instances_of($audits, 'PhabricatorRepositoryAuditRequest');
$user = $this->getRequest()->getUser();
$view = new PhabricatorAuditListView();
$view->setAudits($audits);
$view->setCommits(array($commit));
$view->setUser($user);
$view->setShowDescriptions(false);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
$view->setAuthorityPHIDs($this->auditAuthorityPHIDs);
$this->highlightedAudits = $view->getHighlightedAudits();
$panel = new AphrontPanelView();
$panel->setHeader('Audits');
$panel->setCaption('Audits you are responsible for are highlighted.');
$panel->appendChild($view);
$panel->setNoBackground();
return $panel;
}
private function buildComments(PhabricatorRepositoryCommit $commit) {
$user = $this->getRequest()->getUser();
$comments = id(new PhabricatorAuditComment())->loadAllWhere(
'targetPHID = %s ORDER BY dateCreated ASC',
$commit->getPHID());
$inlines = id(new PhabricatorAuditInlineComment())->loadAllWhere(
'commitPHID = %s AND auditCommentID IS NOT NULL',
$commit->getPHID());
$path_ids = mpull($inlines, 'getPathID');
$path_map = array();
if ($path_ids) {
$path_map = id(new DiffusionPathQuery())
->withPathIDs($path_ids)
->execute();
$path_map = ipull($path_map, 'path', 'id');
}
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($user);
foreach ($comments as $comment) {
$engine->addObject(
$comment,
PhabricatorAuditComment::MARKUP_FIELD_BODY);
}
foreach ($inlines as $inline) {
$engine->addObject(
$inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
}
$engine->process();
$view = new DiffusionCommentListView();
$view->setMarkupEngine($engine);
$view->setUser($user);
$view->setComments($comments);
$view->setInlineComments($inlines);
$view->setPathMap($path_map);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
return $view;
}
private function renderAddCommentPanel(
PhabricatorRepositoryCommit $commit,
array $audit_requests,
$pane_id = null) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$user = $this->getRequest()->getUser();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
'diffusion-audit-'.$commit->getID());
if ($draft) {
$draft = $draft->getDraft();
} else {
$draft = null;
}
$actions = $this->getAuditActions($commit, $audit_requests);
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/audit/addcomment/')
->addHiddenInput('commit', $commit->getPHID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Action')
->setName('action')
->setID('audit-action')
->setOptions($actions))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Add Auditors')
->setName('auditors')
->setControlID('add-auditors')
->setControlStyle('display: none')
->setID('add-auditors-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Add CCs')
->setName('ccs')
->setControlID('add-ccs')
->setControlStyle('display: none')
->setID('add-ccs-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel('Comments')
->setName('content')
->setValue($draft)
->setID('audit-content')
->setUser($user))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($is_serious ? 'Submit' : 'Cook the Books'));
$panel = new AphrontPanelView();
$panel->setHeader($is_serious ? 'Audit Commit' : 'Creative Accounting');
$panel->appendChild($form);
$panel->addClass('aphront-panel-accent');
$panel->addClass('aphront-panel-flush');
require_celerity_resource('phabricator-transaction-view-css');
Javelin::initBehavior(
'differential-add-reviewers-and-ccs',
array(
'dynamic' => array(
'add-auditors-tokenizer' => array(
'actions' => array('add_auditors' => 1),
'src' => '/typeahead/common/users/',
'row' => 'add-auditors',
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'placeholder' => 'Type a user name...',
),
'add-ccs-tokenizer' => array(
'actions' => array('add_ccs' => 1),
'src' => '/typeahead/common/mailable/',
'row' => 'add-ccs',
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'placeholder' => 'Type a user or mailing list...',
),
),
'select' => 'audit-action',
));
Javelin::initBehavior('differential-feedback-preview', array(
'uri' => '/audit/preview/'.$commit->getID().'/',
'preview' => 'audit-preview',
'content' => 'audit-content',
'action' => 'audit-action',
'previewTokenizers' => array(
'auditors' => 'add-auditors-tokenizer',
'ccs' => 'add-ccs-tokenizer',
),
'inline' => 'inline-comment-preview',
'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/',
));
$preview_panel =
'<div class="aphront-panel-preview aphront-panel-flush">
<div id="audit-preview">
<div class="aphront-panel-preview-loading-text">
Loading preview...
</div>
</div>
<div id="inline-comment-preview">
</div>
</div>';
return
phutil_render_tag(
'div',
array(
'class' => 'differential-add-comment-panel',
),
$panel->render().
$preview_panel);
}
/**
* Return a map of available audit actions for rendering into a <select />.
* This shows the user valid actions, and does not show nonsense/invalid
* actions (like closing an already-closed commit, or resigning from a commit
* you have no association with).
*/
private function getAuditActions(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$user = $this->getRequest()->getUser();
$user_is_author = ($commit->getAuthorPHID() == $user->getPHID());
$user_request = null;
foreach ($audit_requests as $audit_request) {
if ($audit_request->getAuditorPHID() == $user->getPHID()) {
$user_request = $audit_request;
break;
}
}
$actions = array();
$actions[PhabricatorAuditActionConstants::COMMENT] = true;
$actions[PhabricatorAuditActionConstants::ADD_CCS] = true;
$actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true;
// We allow you to accept your own commits. A use case here is that you
// notice an issue with your own commit and "Raise Concern" as an indicator
// to other auditors that you're on top of the issue, then later resolve it
// and "Accept". You can not accept on behalf of projects or packages,
// however.
$actions[PhabricatorAuditActionConstants::ACCEPT] = true;
$actions[PhabricatorAuditActionConstants::CONCERN] = true;
// To resign, a user must have authority on some request and not be the
// commit's author.
if (!$user_is_author) {
$may_resign = false;
$authority_map = array_fill_keys($this->auditAuthorityPHIDs, true);
foreach ($audit_requests as $request) {
if (empty($authority_map[$request->getAuditorPHID()])) {
continue;
}
$may_resign = true;
break;
}
// If the user has already resigned, don't show "Resign...".
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
if ($user_request) {
if ($user_request->getAuditStatus() == $status_resigned) {
$may_resign = false;
}
}
if ($may_resign) {
$actions[PhabricatorAuditActionConstants::RESIGN] = true;
}
}
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
$concern_raised = ($commit->getAuditStatus() == $status_concern);
$can_close_option = PhabricatorEnv::getEnvConfig(
'audit.can-author-close-audit');
if ($can_close_option && $user_is_author && $concern_raised) {
$actions[PhabricatorAuditActionConstants::CLOSE] = true;
}
foreach ($actions as $constant => $ignored) {
$actions[$constant] =
PhabricatorAuditActionConstants::getActionName($constant);
}
return $actions;
}
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
$drequest = $this->getDiffusionRequest();
$limit = 50;
$merge_query = DiffusionMergedCommitsQuery::newFromDiffusionRequest(
$drequest);
$merge_query->setLimit($limit + 1);
$merges = $merge_query->loadMergedCommits();
if (!$merges) {
return null;
}
$caption = null;
if (count($merges) > $limit) {
$merges = array_slice($merges, 0, $limit);
$caption =
"This commit merges more than {$limit} changes. Only the first ".
"{$limit} are shown.";
}
$history_table = new DiffusionHistoryTableView();
$history_table->setUser($this->getRequest()->getUser());
$history_table->setDiffusionRequest($drequest);
$history_table->setHistory($merges);
$history_table->loadRevisions();
$phids = $history_table->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$history_table->setHandles($handles);
$panel = new AphrontPanelView();
$panel->setHeader('Merged Changes');
$panel->setCaption($caption);
$panel->appendChild($history_table);
$panel->setNoBackground();
return $panel;
}
private function renderHeadsupActionList(
PhabricatorRepositoryCommit $commit,
PhabricatorRepository $repository) {
$request = $this->getRequest();
$user = $request->getUser();
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObject($commit);
// TODO -- integrate permissions into whether or not this action is shown
$uri = '/diffusion/'.$repository->getCallSign().'/commit/'.
$commit->getCommitIdentifier().'/edit/';
$action = id(new PhabricatorActionView())
->setName('Edit Commit')
->setHref($uri)
->setIcon('edit');
$actions->addAction($action);
require_celerity_resource('phabricator-object-selector-css');
require_celerity_resource('javelin-behavior-phabricator-object-selector');
if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$action = id(new PhabricatorActionView())
->setName('Edit Maniphest Tasks')
->setIcon('attach')
->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/')
->setWorkflow(true);
$actions->addAction($action);
}
if ($user->getIsAdmin()) {
$action = id(new PhabricatorActionView())
->setName('MetaMTA Transcripts')
->setIcon('file')
->setHref('/mail/?phid='.$commit->getPHID());
$actions->addAction($action);
}
$action = id(new PhabricatorActionView())
->setName('Herald Transcripts')
->setIcon('file')
->setHref('/herald/transcript/?phid='.$commit->getPHID())
->setWorkflow(true);
$actions->addAction($action);
$action = id(new PhabricatorActionView())
->setName('Download Raw Diff')
->setHref($request->getRequestURI()->alter('diff', true))
->setIcon('download');
$actions->addAction($action);
return $actions;
}
private function buildRefs(DiffusionRequest $request) {
// Not turning this into a proper Query class since it's pretty simple,
// one-off, and Git-specific.
$type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$repository = $request->getRepository();
if ($repository->getVersionControlSystem() != $type_git) {
return null;
}
list($stdout) = $repository->execxLocalCommand(
'log --format=%s -n 1 %s --',
'%d',
$request->getCommit());
// %d, gives a weird output format
// similar to (remote/one, remote/two, remote/three)
$refs = trim($stdout, "() \n");
if (!$refs) {
return null;
}
$refs = explode(',', $refs);
$refs = array_map('trim', $refs);
$ref_links = array();
foreach ($refs as $ref) {
- $ref_links[] = phutil_render_tag(
+ $ref_links[] = phutil_tag(
'a',
array(
'href' => $request->generateURI(
array(
'action' => 'browse',
'branch' => $ref,
)),
),
- phutil_escape_html($ref));
+ $ref);
}
$ref_links = implode(', ', $ref_links);
return $ref_links;
}
private function buildRawDiffResponse(DiffusionRequest $drequest) {
$raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest);
$raw_diff = $raw_query->loadRawDiff();
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $drequest->getCommit().'.diff',
));
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitTagsController.php b/src/applications/diffusion/controller/DiffusionCommitTagsController.php
index fea9d58716..5dbabff240 100644
--- a/src/applications/diffusion/controller/DiffusionCommitTagsController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitTagsController.php
@@ -1,49 +1,49 @@
<?php
final class DiffusionCommitTagsController extends DiffusionController {
public function willProcessRequest(array $data) {
$this->diffusionRequest = DiffusionRequest::newFromDictionary($data);
}
public function processRequest() {
$request = $this->getDiffusionRequest();
$tag_limit = 10;
$tag_query = DiffusionCommitTagsQuery::newFromDiffusionRequest($request);
$tag_query->setLimit($tag_limit + 1);
$tags = $tag_query->loadTags();
$has_more_tags = (count($tags) > $tag_limit);
$tags = array_slice($tags, 0, $tag_limit);
$tag_links = array();
foreach ($tags as $tag) {
- $tag_links[] = phutil_render_tag(
+ $tag_links[] = phutil_tag(
'a',
array(
'href' => $request->generateURI(
array(
'action' => 'browse',
'commit' => $tag->getName(),
)),
),
- phutil_escape_html($tag->getName()));
+ $tag->getName());
}
if ($has_more_tags) {
$tag_links[] = phutil_render_tag(
'a',
array(
'href' => $request->generateURI(
array(
'action' => 'tags',
)),
),
"More tags\xE2\x80\xA6");
}
return id(new AphrontAjaxResponse())
->setContent($tag_links ? implode(', ', $tag_links) : 'None');
}
}
diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php
index 4178c6bd2a..ea11c4831b 100644
--- a/src/applications/diffusion/controller/DiffusionController.php
+++ b/src/applications/diffusion/controller/DiffusionController.php
@@ -1,325 +1,325 @@
<?php
abstract class DiffusionController extends PhabricatorController {
protected $diffusionRequest;
public function willProcessRequest(array $data) {
if (isset($data['callsign'])) {
$drequest = DiffusionRequest::newFromAphrontRequestDictionary(
$data,
$this->getRequest());
$this->diffusionRequest = $drequest;
}
}
public function setDiffusionRequest(DiffusionRequest $request) {
$this->diffusionRequest = $request;
return $this;
}
protected function getDiffusionRequest() {
if (!$this->diffusionRequest) {
throw new Exception("No Diffusion request object!");
}
return $this->diffusionRequest;
}
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->setApplicationName('Diffusion');
$page->setBaseURI('/diffusion/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x89\x88");
$page->setSearchDefaultScope(PhabricatorSearchScope::SCOPE_COMMITS);
$page->appendChild($view);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
final protected function buildSideNav($selected, $has_change_view) {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI(''));
$navs = array(
'history' => 'History View',
'browse' => 'Browse View',
'change' => 'Change View',
);
if (!$has_change_view) {
unset($navs['change']);
}
$drequest = $this->getDiffusionRequest();
$branch = $drequest->loadBranch();
if ($branch && $branch->getLintCommit()) {
$navs['lint'] = 'Lint View';
}
$selected_href = null;
foreach ($navs as $action => $name) {
$href = $drequest->generateURI(
array(
'action' => $action,
));
if ($action == $selected) {
$selected_href = $href;
}
$nav->addFilter($href, $name, $href);
}
$nav->selectFilter($selected_href, null);
// TODO: URI encoding might need to be sorted out for this link.
$nav->addFilter(
'',
"Search Owners \xE2\x86\x97",
'/owners/view/search/'.
'?repository='.phutil_escape_uri($drequest->getCallsign()).
'&path='.phutil_escape_uri('/'.$drequest->getPath()));
return $nav;
}
public function buildCrumbs(array $spec = array()) {
$crumbs = $this->buildApplicationCrumbs();
$crumb_list = $this->buildCrumbList($spec);
foreach ($crumb_list as $crumb) {
$crumbs->addCrumb($crumb);
}
return $crumbs;
}
protected function buildOpenRevisions() {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
$path_map = id(new DiffusionPathIDQuery(array($path)))->loadPathIDs();
$path_id = idx($path_map, $path);
if (!$path_id) {
return null;
}
$revisions = id(new DifferentialRevisionQuery())
->withPath($repository->getID(), $path_id)
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED)
->setLimit(10)
->needRelationships(true)
->execute();
if (!$revisions) {
return null;
}
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setFields(DifferentialRevisionListView::getDefaultFields())
->setUser($this->getRequest()->getUser())
->loadAssets();
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
$panel = new AphrontPanelView();
$panel->setId('pending-differential-revisions');
$panel->setHeader('Pending Differential Revisions');
$panel->appendChild($view);
return $panel;
}
private function buildCrumbList(array $spec = array()) {
$spec = $spec + array(
'commit' => null,
'tags' => null,
'branches' => null,
'view' => null,
);
$crumb_list = array();
// On the home page, we don't have a DiffusionRequest.
if ($this->diffusionRequest) {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
} else {
$drequest = null;
$repository = null;
}
if (!$repository) {
return $crumb_list;
}
$callsign = $repository->getCallsign();
$repository_name = 'r'.$callsign;
if (!$spec['commit'] && !$spec['tags'] && !$spec['branches']) {
$branch_name = $drequest->getBranch();
if ($branch_name) {
$repository_name .= ' ('.$branch_name.')';
}
}
$crumb = id(new PhabricatorCrumbView())
->setName($repository_name);
if (!$spec['view'] && !$spec['commit'] &&
!$spec['tags'] && !$spec['branches']) {
$crumb_list[] = $crumb;
return $crumb_list;
}
$crumb->setHref("/diffusion/{$callsign}/");
$crumb_list[] = $crumb;
$raw_commit = $drequest->getRawCommit();
if ($spec['tags']) {
$crumb = new PhabricatorCrumbView();
if ($spec['commit']) {
$crumb->setName(
"Tags for r{$callsign}{$raw_commit}"
);
$crumb->setHref($drequest->generateURI(
array(
'action' => 'commit',
'commit' => $raw_commit,
))
);
} else {
$crumb->setName('Tags');
}
$crumb_list[] = $crumb;
return $crumb_list;
}
if ($spec['branches']) {
$crumb = id(new PhabricatorCrumbView())
->setName('Branches');
$crumb_list[] = $crumb;
return $crumb_list;
}
if ($spec['commit']) {
$crumb = id(new PhabricatorCrumbView())
->setName("r{$callsign}{$raw_commit}")
->setHref("r{$callsign}{$raw_commit}");
$crumb_list[] = $crumb;
return $crumb_list;
}
$crumb = new PhabricatorCrumbView();
$view = $spec['view'];
$path = null;
if (isset($spec['path'])) {
$path = $drequest->getPath();
}
if ($raw_commit) {
$commit_link = DiffusionView::linkCommit(
$repository,
$raw_commit);
} else {
$commit_link = '';
}
switch ($view) {
case 'history':
$view_name = 'History';
break;
case 'browse':
$view_name = 'Browse';
break;
case 'lint':
$view_name = 'Lint';
break;
case 'change':
$view_name = 'Change';
$crumb_list[] = $crumb->setRawName(
phutil_escape_html($path).' ('.$commit_link.')'
);
return $crumb_list;
}
$uri_params = array(
'action' => $view,
);
$crumb = id(new PhabricatorCrumbView())
->setName($view_name);
if (!strlen($path)) {
$crumb_list[] = $crumb;
} else {
$crumb->setHref($drequest->generateURI(
array(
'path' => '',
) + $uri_params)
);
$crumb_list[] = $crumb;
$path_parts = explode('/', $path);
do {
$last = array_pop($path_parts);
} while ($last == '');
$path_sections = array();
$thus_far = '';
foreach ($path_parts as $path_part) {
$thus_far .= $path_part.'/';
- $path_sections[] = phutil_render_tag(
+ $path_sections[] = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'path' => $thus_far,
) + $uri_params),
),
- phutil_escape_html($path_part));
+ $path_part);
}
$path_sections[] = phutil_escape_html($last);
$path_sections = '/'.implode('/', $path_sections);
$crumb_list[] = id(new PhabricatorCrumbView())
->setRawName($path_sections);
}
$last_crumb = array_pop($crumb_list);
if ($raw_commit) {
$jump_link = phutil_render_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'commit' => '',
) + $uri_params),
),
'Jump to HEAD');
$last_crumb->setRawName(
$last_crumb->getNameForRender() . " @ {$commit_link} ({$jump_link})"
);
} else if ($spec['view'] != 'lint') {
$last_crumb->setRawName(
$last_crumb->getNameForRender() . " @ HEAD"
);
}
$crumb_list[] = $last_crumb;
return $crumb_list;
}
}
diff --git a/src/applications/diffusion/controller/DiffusionExternalController.php b/src/applications/diffusion/controller/DiffusionExternalController.php
index 1f3fc6c6b2..738cee81fd 100644
--- a/src/applications/diffusion/controller/DiffusionExternalController.php
+++ b/src/applications/diffusion/controller/DiffusionExternalController.php
@@ -1,135 +1,134 @@
<?php
final class DiffusionExternalController extends DiffusionController {
public function willProcessRequest(array $data) {
// Don't build a DiffusionRequest.
}
public function processRequest() {
$request = $this->getRequest();
$uri = $request->getStr('uri');
$id = $request->getStr('id');
$repositories = id(new PhabricatorRepository())->loadAll();
if ($uri) {
$uri_path = id(new PhutilURI($uri))->getPath();
$matches = array();
// Try to figure out which tracked repository this external lives in by
// comparing repository metadata. We look for an exact match, but accept
// a partial match.
foreach ($repositories as $key => $repository) {
$remote_uri = new PhutilURI($repository->getRemoteURI());
if ($remote_uri->getPath() == $uri_path) {
$matches[$key] = 1;
}
if ($repository->getPublicRemoteURI() == $uri) {
$matches[$key] = 2;
}
if ($repository->getRemoteURI() == $uri) {
$matches[$key] = 3;
}
}
arsort($matches);
$best_match = head_key($matches);
if ($best_match) {
$repository = $repositories[$best_match];
$redirect = DiffusionRequest::generateDiffusionURI(
array(
'action' => 'browse',
'callsign' => $repository->getCallsign(),
'branch' => $repository->getDefaultBranch(),
'commit' => $id,
));
return id(new AphrontRedirectResponse())->setURI($redirect);
}
}
// TODO: This is a rare query but does a table scan, add a key?
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'commitIdentifier = %s',
$id);
if (empty($commits)) {
$desc = null;
if ($uri) {
$desc = phutil_escape_html($uri).', at ';
}
$desc .= phutil_escape_html($id);
$content = id(new AphrontErrorView())
->setTitle('Unknown External')
->setSeverity(AphrontErrorView::SEVERITY_WARNING)
->appendChild(
"<p>This external ({$desc}) does not appear in any tracked ".
"repository. It may exist in an untracked repository that ".
"Diffusion does not know about.</p>");
} else if (count($commits) == 1) {
$commit = head($commits);
$repo = $repositories[$commit->getRepositoryID()];
$redirect = DiffusionRequest::generateDiffusionURI(
array(
'action' => 'browse',
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'commit' => $commit->getCommitIdentifier(),
));
return id(new AphrontRedirectResponse())->setURI($redirect);
} else {
$rows = array();
foreach ($commits as $commit) {
$repo = $repositories[$commit->getRepositoryID()];
$href = DiffusionRequest::generateDiffusionURI(
array(
'action' => 'browse',
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'commit' => $commit->getCommitIdentifier(),
));
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $href,
),
- phutil_escape_html(
- 'r'.$repo->getCallsign().$commit->getCommitIdentifier())),
+ 'r'.$repo->getCallsign().$commit->getCommitIdentifier()),
phutil_escape_html($commit->loadCommitData()->getSummary()),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Commit',
'Description',
));
$table->setColumnClasses(
array(
'pri',
'wide',
));
$content = new AphrontPanelView();
$content->setHeader('Multiple Matching Commits');
$content->setCaption(
'This external reference matches multiple known commits.');
$content->appendChild($table);
}
return $this->buildStandardPageResponse(
$content,
array(
'title' => 'Unresolvable External',
));
}
}
diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php
index 4b9193616d..fe541054e7 100644
--- a/src/applications/diffusion/controller/DiffusionHistoryController.php
+++ b/src/applications/diffusion/controller/DiffusionHistoryController.php
@@ -1,102 +1,102 @@
<?php
final class DiffusionHistoryController extends DiffusionController {
public function processRequest() {
$drequest = $this->diffusionRequest;
$request = $this->getRequest();
$page_size = $request->getInt('pagesize', 100);
$offset = $request->getInt('page', 0);
$history_query = DiffusionHistoryQuery::newFromDiffusionRequest(
$drequest);
$history_query->setOffset($offset);
$history_query->setLimit($page_size + 1);
if (!$request->getBool('copies')) {
$history_query->needDirectChanges(true);
$history_query->needChildChanges(true);
}
$show_graph = !strlen($drequest->getPath());
if ($show_graph) {
$history_query->needParents(true);
}
$history = $history_query->loadHistory();
$pager = new AphrontPagerView();
$pager->setPageSize($page_size);
$pager->setOffset($offset);
if (count($history) == $page_size + 1) {
array_pop($history);
$pager->setHasMorePages(true);
} else {
$pager->setHasMorePages(false);
}
$pager->setURI($request->getRequestURI(), 'page');
$content = array();
if ($request->getBool('copies')) {
$button_title = 'Hide Copies/Branches';
$copies_new = null;
} else {
$button_title = 'Show Copies/Branches';
$copies_new = true;
}
- $button = phutil_render_tag(
+ $button = phutil_tag(
'a',
array(
'class' => 'button small grey',
'href' => $request->getRequestURI()->alter('copies', $copies_new),
),
- phutil_escape_html($button_title));
+ $button_title);
$history_table = new DiffusionHistoryTableView();
$history_table->setUser($request->getUser());
$history_table->setDiffusionRequest($drequest);
$history_table->setHistory($history);
$history_table->loadRevisions();
$phids = $history_table->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$history_table->setHandles($handles);
if ($show_graph) {
$history_table->setParents($history_query->getParents());
$history_table->setIsHead($offset == 0);
}
$history_panel = new AphrontPanelView();
$history_panel->setHeader('History');
$history_panel->addButton($button);
$history_panel->appendChild($history_table);
$history_panel->appendChild($pager);
$history_panel->setNoBackground();
$content[] = $history_panel;
// TODO: Sometimes we do have a change view, we need to look at the most
// recent history entry to figure it out.
$nav = $this->buildSideNav('history', false);
$nav->appendChild($content);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'history',
));
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => 'history',
));
}
}
diff --git a/src/applications/diffusion/controller/DiffusionHomeController.php b/src/applications/diffusion/controller/DiffusionHomeController.php
index d59504f69a..c951b764a8 100644
--- a/src/applications/diffusion/controller/DiffusionHomeController.php
+++ b/src/applications/diffusion/controller/DiffusionHomeController.php
@@ -1,222 +1,222 @@
<?php
final class DiffusionHomeController extends DiffusionController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$shortcuts = id(new PhabricatorRepositoryShortcut())->loadAll();
if ($shortcuts) {
$shortcuts = msort($shortcuts, 'getSequence');
$rows = array();
foreach ($shortcuts as $shortcut) {
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $shortcut->getHref(),
),
- phutil_escape_html($shortcut->getName())),
+ $shortcut->getName()),
phutil_escape_html($shortcut->getDescription()),
);
}
$shortcut_table = new AphrontTableView($rows);
$shortcut_table->setHeaders(
array(
'Link',
'',
));
$shortcut_table->setColumnClasses(
array(
'pri',
'wide',
));
$shortcut_panel = new AphrontPanelView();
$shortcut_panel->setHeader('Shortcuts');
$shortcut_panel->appendChild($shortcut_table);
} else {
$shortcut_panel = null;
}
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($user)
->execute();
foreach ($repositories as $key => $repo) {
if (!$repo->isTracked()) {
unset($repositories[$key]);
}
}
$repositories = msort($repositories, 'getName');
$repository_ids = mpull($repositories, 'getID');
$summaries = array();
$commits = array();
if ($repository_ids) {
$summaries = queryfx_all(
id(new PhabricatorRepository())->establishConnection('r'),
'SELECT * FROM %T WHERE repositoryID IN (%Ld)',
PhabricatorRepository::TABLE_SUMMARY,
$repository_ids);
$summaries = ipull($summaries, null, 'repositoryID');
$commit_ids = array_filter(ipull($summaries, 'lastCommitID'));
if ($commit_ids) {
$commit = new PhabricatorRepositoryCommit();
$commits = $commit->loadAllWhere('id IN (%Ld)', $commit_ids);
$commits = mpull($commits, null, 'getRepositoryID');
}
}
$branch = new PhabricatorRepositoryBranch();
$lint_messages = queryfx_all(
$branch->establishConnection('r'),
'SELECT b.repositoryID, b.name, COUNT(lm.id) AS n
FROM %T b
LEFT JOIN %T lm ON b.id = lm.branchID
GROUP BY b.id',
$branch->getTableName(),
PhabricatorRepository::TABLE_LINTMESSAGE);
$lint_messages = igroup($lint_messages, 'repositoryID');
$rows = array();
$show_lint = false;
foreach ($repositories as $repository) {
$id = $repository->getID();
$commit = idx($commits, $id);
$size = idx(idx($summaries, $id, array()), 'size', '-');
if ($size != '-') {
$size = hsprintf(
'<a href="%s">%s</a>',
DiffusionRequest::generateDiffusionURI(array(
'callsign' => $repository->getCallsign(),
'action' => 'history',
)),
number_format($size));
}
$lint_count = '';
$lint_branches = ipull(idx($lint_messages, $id, array()), 'n', 'name');
$branch = $repository->getDefaultArcanistBranch();
if (isset($lint_branches[$branch])) {
$show_lint = true;
$lint_count = phutil_render_tag(
'a',
array(
'href' => DiffusionRequest::generateDiffusionURI(array(
'callsign' => $repository->getCallsign(),
'action' => 'lint',
)),
),
number_format($lint_branches[$branch]));
}
$date = '-';
$time = '-';
if ($commit) {
$date = phabricator_date($commit->getEpoch(), $user);
$time = phabricator_time($commit->getEpoch(), $user);
}
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/diffusion/'.$repository->getCallsign().'/',
),
- phutil_escape_html($repository->getName())),
+ $repository->getName()),
phutil_escape_html($repository->getDetail('description')),
PhabricatorRepositoryType::getNameForRepositoryType(
$repository->getVersionControlSystem()),
$size,
$lint_count,
$commit
? DiffusionView::linkCommit(
$repository,
$commit->getCommitIdentifier())
: '-',
$date,
$time,
);
}
$repository_tool_uri = PhabricatorEnv::getProductionURI('/repository/');
$repository_tool = phutil_render_tag('a',
array(
'href' => $repository_tool_uri,
),
'repository tool');
$no_repositories_txt = 'This instance of Phabricator does not have any '.
'configured repositories. ';
if ($user->getIsAdmin()) {
$no_repositories_txt .= 'To setup one or more repositories, visit the '.
$repository_tool.'.';
} else {
$no_repositories_txt .= 'Ask an administrator to setup one or more '.
'repositories via the '.$repository_tool.'.';
}
$table = new AphrontTableView($rows);
$table->setNoDataString($no_repositories_txt);
$table->setHeaders(
array(
'Repository',
'Description',
'VCS',
'Commits',
'Lint',
'Last',
'Date',
'Time',
));
$table->setColumnClasses(
array(
'pri',
'wide',
'',
'n',
'n',
'n',
'',
'right',
));
$table->setColumnVisibility(
array(
true,
true,
true,
true,
$show_lint,
true,
true,
true,
));
$panel = new AphrontPanelView();
$panel->setHeader('Browse Repositories');
$panel->appendChild($table);
$panel->setNoBackground();
$crumbs = $this->buildCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('All Repositories'))
->setHref($this->getApplicationURI()));
return $this->buildStandardPageResponse(
array(
$crumbs,
$shortcut_panel,
$panel,
),
array(
'title' => 'Diffusion',
));
}
}
diff --git a/src/applications/diffusion/controller/DiffusionSymbolController.php b/src/applications/diffusion/controller/DiffusionSymbolController.php
index ab118c3948..17fcea83ae 100644
--- a/src/applications/diffusion/controller/DiffusionSymbolController.php
+++ b/src/applications/diffusion/controller/DiffusionSymbolController.php
@@ -1,154 +1,154 @@
<?php
final class DiffusionSymbolController extends DiffusionController {
private $name;
public function willProcessRequest(array $data) {
$this->name = $data['name'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$query = new DiffusionSymbolQuery();
$query->setName($this->name);
if ($request->getStr('context') !== null) {
$query->setContext($request->getStr('context'));
}
if ($request->getStr('type')) {
$query->setType($request->getStr('type'));
}
if ($request->getStr('lang')) {
$query->setLanguage($request->getStr('lang'));
}
if ($request->getStr('projects')) {
$phids = $request->getStr('projects');
$phids = explode(',', $phids);
$phids = array_filter($phids);
if ($phids) {
$projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere(
'phid IN (%Ls)',
$phids);
$projects = mpull($projects, 'getID');
if ($projects) {
$query->setProjectIDs($projects);
}
}
}
$query->needPaths(true);
$query->needArcanistProjects(true);
$query->needRepositories(true);
$symbols = $query->execute();
// For PHP builtins, jump to php.net documentation.
if ($request->getBool('jump') && count($symbols) == 0) {
if ($request->getStr('lang', 'php') == 'php') {
if ($request->getStr('type', 'function') == 'function') {
$functions = get_defined_functions();
if (in_array($this->name, $functions['internal'])) {
return id(new AphrontRedirectResponse())
->setURI('http://www.php.net/function.'.$this->name);
}
}
if ($request->getStr('type', 'class') == 'class') {
if (class_exists($this->name, false) ||
interface_exists($this->name, false)) {
if (id(new ReflectionClass($this->name))->isInternal()) {
return id(new AphrontRedirectResponse())
->setURI('http://www.php.net/class.'.$this->name);
}
}
}
}
}
$rows = array();
foreach ($symbols as $symbol) {
$project = $symbol->getArcanistProject();
if ($project) {
$project_name = $project->getName();
} else {
$project_name = '-';
}
$file = phutil_escape_html($symbol->getPath());
$line = phutil_escape_html($symbol->getLineNumber());
$repo = $symbol->getRepository();
if ($repo) {
$href = $symbol->getURI();
if ($request->getBool('jump') && count($symbols) == 1) {
// If this is a clickthrough from Differential, just jump them
// straight to the target if we got a single hit.
return id(new AphrontRedirectResponse())->setURI($href);
}
- $location = phutil_render_tag(
+ $location = phutil_tag(
'a',
array(
'href' => $href,
),
- phutil_escape_html($file.':'.$line));
+ $file.':'.$line);
} else if ($file) {
$location = phutil_escape_html($file.':'.$line);
} else {
$location = '?';
}
$rows[] = array(
phutil_escape_html($symbol->getSymbolType()),
phutil_escape_html($symbol->getSymbolContext()),
phutil_escape_html($symbol->getSymbolName()),
phutil_escape_html($symbol->getSymbolLanguage()),
phutil_escape_html($project_name),
$location,
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Type',
'Context',
'Name',
'Language',
'Project',
'File',
));
$table->setColumnClasses(
array(
'',
'',
'pri',
'',
'',
'',
));
$table->setNoDataString(
"No matching symbol could be found in any indexed project.");
$panel = new AphrontPanelView();
$panel->setHeader('Similar Symbols');
$panel->appendChild($table);
return $this->buildStandardPageResponse(
array(
$panel,
),
array(
'title' => 'Find Symbol',
));
}
}
diff --git a/src/applications/diffusion/view/DiffusionBranchTableView.php b/src/applications/diffusion/view/DiffusionBranchTableView.php
index ee95580932..4ac40d73ca 100644
--- a/src/applications/diffusion/view/DiffusionBranchTableView.php
+++ b/src/applications/diffusion/view/DiffusionBranchTableView.php
@@ -1,96 +1,96 @@
<?php
final class DiffusionBranchTableView extends DiffusionView {
private $branches;
private $commits = array();
public function setBranches(array $branches) {
assert_instances_of($branches, 'DiffusionBranchInformation');
$this->branches = $branches;
return $this;
}
public function setCommits(array $commits) {
$this->commits = mpull($commits, null, 'getCommitIdentifier');
return $this;
}
public function render() {
$drequest = $this->getDiffusionRequest();
$current_branch = $drequest->getBranch();
$rows = array();
$rowc = array();
foreach ($this->branches as $branch) {
$commit = idx($this->commits, $branch->getHeadCommitIdentifier());
if ($commit) {
$details = $commit->getCommitData()->getCommitMessage();
$details = idx(explode("\n", $details), 0);
$details = substr($details, 0, 80);
$datetime = phabricator_datetime($commit->getEpoch(), $this->user);
} else {
$datetime = null;
$details = null;
}
$rows[] = array(
phutil_render_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'history',
'branch' => $branch->getName(),
))
),
'History'
),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'browse',
'branch' => $branch->getName(),
)),
),
- phutil_escape_html($branch->getName())),
+ $branch->getName()),
self::linkCommit(
$drequest->getRepository(),
$branch->getHeadCommitIdentifier()),
$datetime,
AphrontTableView::renderSingleDisplayLine(
phutil_escape_html($details))
// TODO: etc etc
);
if ($branch->getName() == $current_branch) {
$rowc[] = 'highlighted';
} else {
$rowc[] = null;
}
}
$view = new AphrontTableView($rows);
$view->setHeaders(
array(
'History',
'Branch',
'Head',
'Modified',
'Details',
));
$view->setColumnClasses(
array(
'',
'pri',
'',
'',
'wide',
));
$view->setRowClasses($rowc);
return $view->render();
}
}
diff --git a/src/applications/diffusion/view/DiffusionBrowseTableView.php b/src/applications/diffusion/view/DiffusionBrowseTableView.php
index 004faf0a21..fdf1651474 100644
--- a/src/applications/diffusion/view/DiffusionBrowseTableView.php
+++ b/src/applications/diffusion/view/DiffusionBrowseTableView.php
@@ -1,289 +1,289 @@
<?php
final class DiffusionBrowseTableView extends DiffusionView {
private $paths;
private $handles = array();
public function setPaths(array $paths) {
assert_instances_of($paths, 'DiffusionRepositoryPath');
$this->paths = $paths;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function renderLastModifiedColumns(
array $handles,
PhabricatorRepositoryCommit $commit = null,
PhabricatorRepositoryCommitData $data = null) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$drequest = $this->getDiffusionRequest();
if ($commit) {
$epoch = $commit->getEpoch();
$modified = DiffusionView::linkCommit(
$drequest->getRepository(),
$commit->getCommitIdentifier());
$date = phabricator_date($epoch, $this->user);
$time = phabricator_time($epoch, $this->user);
} else {
$modified = '';
$date = '';
$time = '';
}
if ($data) {
$author_phid = $data->getCommitDetail('authorPHID');
if ($author_phid && isset($handles[$author_phid])) {
$author = $handles[$author_phid]->renderLink();
} else {
$author = self::renderName($data->getAuthorName());
}
$committer = $data->getCommitDetail('committer');
if ($committer) {
$committer_phid = $data->getCommitDetail('committerPHID');
if ($committer_phid && isset($handles[$committer_phid])) {
$committer = $handles[$committer_phid]->renderLink();
} else {
$committer = self::renderName($committer);
}
if ($author != $committer) {
$author .= '/'.$committer;
}
}
$details = AphrontTableView::renderSingleDisplayLine(
phutil_escape_html($data->getSummary()));
} else {
$author = '';
$details = '';
}
$return = array(
'commit' => $modified,
'date' => $date,
'time' => $time,
'author' => $author,
'details' => $details,
);
$lint = self::loadLintMessagesCount($drequest);
if ($lint !== null) {
$return['lint'] = hsprintf(
'<a href="%s">%s</a>',
$drequest->generateURI(array(
'action' => 'lint',
'lint' => null,
)),
number_format($lint));
}
return $return;
}
private static function loadLintMessagesCount(DiffusionRequest $drequest) {
$branch = $drequest->loadBranch();
if (!$branch) {
return null;
}
$conn = $drequest->getRepository()->establishConnection('r');
$where = '';
if ($drequest->getLint()) {
$where = qsprintf(
$conn,
'AND code = %s',
$drequest->getLint());
}
$like = (substr($drequest->getPath(), -1) == '/' ? 'LIKE %>' : '= %s');
return head(queryfx_one(
$conn,
'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q AND path '.$like,
PhabricatorRepository::TABLE_LINTMESSAGE,
$branch->getID(),
$where,
'/'.$drequest->getPath()));
}
public function render() {
$request = $this->getDiffusionRequest();
$repository = $request->getRepository();
$base_path = trim($request->getPath(), '/');
if ($base_path) {
$base_path = $base_path.'/';
}
$need_pull = array();
$rows = array();
$show_edit = false;
foreach ($this->paths as $path) {
$dir_slash = null;
$file_type = $path->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
$browse_text = $path->getPath().'/';
$dir_slash = '/';
$browse_link = '<strong>'.$this->linkBrowse(
$base_path.$path->getPath().$dir_slash,
array(
'html' => $this->renderPathIcon(
'dir',
$browse_text),
)).'</strong>';
} else if ($file_type == DifferentialChangeType::FILE_SUBMODULE) {
$browse_text = $path->getPath().'/';
$browse_link =
'<strong>'.
$this->linkExternal(
$path->getHash(),
$path->getExternalURI(),
$this->renderPathIcon(
'ext',
$browse_text)).
'</strong>';
} else {
if ($file_type == DifferentialChangeType::FILE_SYMLINK) {
$type = 'link';
} else {
$type = 'file';
}
$browse_text = $path->getPath();
$browse_link = $this->linkBrowse(
$base_path.$path->getPath(),
array(
'html' => $this->renderPathIcon($type, $browse_text),
));
}
$commit = $path->getLastModifiedCommit();
if ($commit) {
$drequest = clone $request;
$drequest->setPath($request->getPath().$path->getPath().$dir_slash);
$dict = $this->renderLastModifiedColumns(
$this->handles,
$commit,
$path->getLastCommitData());
} else {
$dict = array(
'lint' => celerity_generate_unique_node_id(),
'commit' => celerity_generate_unique_node_id(),
'date' => celerity_generate_unique_node_id(),
'time' => celerity_generate_unique_node_id(),
'author' => celerity_generate_unique_node_id(),
'details' => celerity_generate_unique_node_id(),
);
$uri = (string)$request->generateURI(
array(
'action' => 'lastmodified',
'path' => $base_path.$path->getPath().$dir_slash,
));
$need_pull[$uri] = $dict;
foreach ($dict as $k => $uniq) {
$dict[$k] = '<span id="'.$uniq.'"></span>';
}
}
$editor_button = '';
if ($this->user) {
$editor_link = $this->user->loadEditorLink(
$base_path.$path->getPath(),
1,
$request->getRepository()->getCallsign());
if ($editor_link) {
$show_edit = true;
$editor_button = phutil_render_tag(
'a',
array(
'href' => $editor_link,
),
'Edit');
}
}
$rows[] = array(
$this->linkHistory($base_path.$path->getPath().$dir_slash),
$editor_button,
$browse_link,
idx($dict, 'lint'),
$dict['commit'],
$dict['date'],
$dict['time'],
$dict['author'],
$dict['details'],
);
}
if ($need_pull) {
Javelin::initBehavior('diffusion-pull-lastmodified', $need_pull);
}
$branch = $this->getDiffusionRequest()->loadBranch();
$show_lint = ($branch && $branch->getLintCommit());
$lint = $request->getLint();
$view = new AphrontTableView($rows);
$view->setHeaders(
array(
'History',
'Edit',
'Path',
($lint ? phutil_escape_html($lint) : 'Lint'),
'Modified',
'Date',
'Time',
'Author/Committer',
'Details',
));
$view->setColumnClasses(
array(
'',
'',
'',
'n',
'',
'',
'right',
'',
'wide',
));
$view->setColumnVisibility(
array(
true,
$show_edit,
true,
$show_lint,
true,
true,
true,
true,
true,
));
return $view->render();
}
private function renderPathIcon($type, $text) {
require_celerity_resource('diffusion-icons-css');
- return phutil_render_tag(
+ return phutil_tag(
'span',
array(
'class' => 'diffusion-path-icon diffusion-path-icon-'.$type,
),
- phutil_escape_html($text));
+ $text);
}
}
diff --git a/src/applications/diffusion/view/DiffusionTagListView.php b/src/applications/diffusion/view/DiffusionTagListView.php
index dee0ec3d2a..b2291dcfe9 100644
--- a/src/applications/diffusion/view/DiffusionTagListView.php
+++ b/src/applications/diffusion/view/DiffusionTagListView.php
@@ -1,112 +1,111 @@
<?php
final class DiffusionTagListView extends DiffusionView {
private $tags;
private $commits = array();
private $handles = array();
public function setTags($tags) {
$this->tags = $tags;
return $this;
}
public function setCommits(array $commits) {
$this->commits = mpull($commits, null, 'getCommitIdentifier');
return $this;
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
public function getRequiredHandlePHIDs() {
return array_filter(mpull($this->commits, 'getAuthorPHID'));
}
public function render() {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$rows = array();
foreach ($this->tags as $tag) {
$commit = idx($this->commits, $tag->getCommitIdentifier());
- $tag_link = phutil_render_tag(
+ $tag_link = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'browse',
'commit' => $tag->getName(),
)),
),
- phutil_escape_html($tag->getName()));
+ $tag->getName());
- $commit_link = phutil_render_tag(
+ $commit_link = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'commit',
'commit' => $tag->getCommitIdentifier(),
)),
),
- phutil_escape_html(
$repository->formatCommitName(
- $tag->getCommitIdentifier())));
+ $tag->getCommitIdentifier()));
$author = null;
if ($commit && $commit->getAuthorPHID()) {
$author = $this->handles[$commit->getAuthorPHID()]->renderLink();
} else if ($commit && $commit->getCommitData()) {
$author = self::renderName($commit->getCommitData()->getAuthorName());
} else {
$author = self::renderName($tag->getAuthor());
}
$description = null;
if ($tag->getType() == 'git/tag') {
// In Git, a tag may be a "real" tag, or just a reference to a commit.
// If it's a real tag, use the message on the tag, since this may be
// unique data which isn't otherwise available.
$description = $tag->getDescription();
} else {
if ($commit && $commit->getCommitData()) {
$description = $commit->getCommitData()->getSummary();
} else {
$description = $tag->getDescription();
}
}
$description = phutil_escape_html($description);
$rows[] = array(
$tag_link,
$commit_link,
$description,
$author,
phabricator_datetime($tag->getEpoch(), $this->user),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Tag',
'Commit',
'Description',
'Author',
'Created',
));
$table->setColumnClasses(
array(
'pri',
'',
'wide',
));
return $table->render();
}
}
diff --git a/src/applications/diviner/controller/DivinerListController.php b/src/applications/diviner/controller/DivinerListController.php
index 7ac7aa453a..2125532b98 100644
--- a/src/applications/diviner/controller/DivinerListController.php
+++ b/src/applications/diviner/controller/DivinerListController.php
@@ -1,63 +1,63 @@
<?php
final class DivinerListController extends PhabricatorController {
public function processRequest() {
// TODO: Temporary implementation until Diviner is up and running inside
// Phabricator.
$links = array(
'http://www.phabricator.com/docs/phabricator/' => array(
'name' => 'Phabricator Ducks',
'flavor' => 'Oops, that should say "Docs".',
),
'http://www.phabricator.com/docs/arcanist/' => array(
'name' => 'Arcanist Docs',
'flavor' => 'Words have never been so finely crafted.',
),
'http://www.phabricator.com/docs/libphutil/' => array(
'name' => 'libphutil Docs',
'flavor' => 'Soothing prose; seductive poetry.',
),
'http://www.phabricator.com/docs/javelin/' => array(
'name' => 'Javelin Docs',
'flavor' => 'O, what noble scribe hath penned these words?',
),
);
require_celerity_resource('phabricator-directory-css');
$out = array();
foreach ($links as $href => $link) {
$name = $link['name'];
$flavor = $link['flavor'];
- $link = phutil_render_tag(
+ $link = phutil_tag(
'a',
array(
'href' => $href,
'target' => '_blank',
),
- phutil_escape_html($name));
+ $name);
$out[] =
'<div class="aphront-directory-item">'.
'<h1>'.$link.'</h1>'.
'<p>'.phutil_escape_html($flavor).'</p>'.
'</div>';
}
$out =
'<div class="aphront-directory-list">'.
implode("\n", $out).
'</div>';
return $this->buildApplicationPage(
$out,
array(
'device' => true,
'title' => 'Documentation',
));
}
}
diff --git a/src/applications/drydock/controller/DrydockController.php b/src/applications/drydock/controller/DrydockController.php
index a3bcc28ea1..e05c1f6839 100644
--- a/src/applications/drydock/controller/DrydockController.php
+++ b/src/applications/drydock/controller/DrydockController.php
@@ -1,163 +1,163 @@
<?php
abstract class DrydockController extends PhabricatorController {
final protected function buildSideNav($selected) {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/drydock/'));
$nav->addFilter('resource', 'Resources');
$nav->addFilter('lease', 'Leases');
$nav->addFilter('log', 'Logs');
$nav->selectFilter($selected, 'resource');
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNav(null)->getMenu();
}
protected function buildLogTableView(array $logs) {
assert_instances_of($logs, 'DrydockLog');
$user = $this->getRequest()->getUser();
$rows = array();
foreach ($logs as $log) {
$resource_uri = '/resource/'.$log->getResourceID().'/';
$resource_uri = $this->getApplicationURI($resource_uri);
$lease_uri = '/lease/'.$log->getLeaseID().'/';
$lease_uri = $this->getApplicationURI($lease_uri);
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $resource_uri,
),
- phutil_escape_html($log->getResourceID())),
- phutil_render_tag(
+ $log->getResourceID()),
+ phutil_tag(
'a',
array(
'href' => $lease_uri,
),
- phutil_escape_html($log->getLeaseID())),
+ $log->getLeaseID()),
phutil_escape_html($log->getMessage()),
phabricator_date($log->getEpoch(), $user),
);
}
$table = new AphrontTableView($rows);
$table->setDeviceReadyTable(true);
$table->setHeaders(
array(
'Resource',
'Lease',
'Message',
'Date',
));
$table->setShortHeaders(
array(
'R',
'L',
'Message',
'',
));
$table->setColumnClasses(
array(
'',
'',
'wide',
'',
));
return $table;
}
protected function buildLeaseListView(array $leases) {
assert_instances_of($leases, 'DrydockLease');
$user = $this->getRequest()->getUser();
$view = new PhabricatorObjectItemListView();
foreach ($leases as $lease) {
$item = id(new PhabricatorObjectItemView())
->setHeader($lease->getLeaseName())
->setHref($this->getApplicationURI('/lease/'.$lease->getID().'/'));
if ($lease->hasAttachedResource()) {
$resource = $lease->getResource();
$resource_href = '/resource/'.$resource->getID().'/';
$resource_href = $this->getApplicationURI($resource_href);
$resource_name = $resource->getName();
$item->addAttribute(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $resource_href,
),
- phutil_escape_html($resource_name)));
+ $resource_name));
}
$status = DrydockLeaseStatus::getNameForStatus($lease->getStatus());
$item->addAttribute(phutil_escape_html($status));
$date_created = phabricator_date($lease->getDateCreated(), $user);
$item->addAttribute(pht('Created on %s', $date_created));
if ($lease->isActive()) {
$item->setBarColor('green');
} else {
$item->setBarColor('red');
}
$view->addItem($item);
}
return $view;
}
protected function buildResourceListView(array $resources) {
assert_instances_of($resources, 'DrydockResource');
$user = $this->getRequest()->getUser();
$view = new PhabricatorObjectItemListView();
foreach ($resources as $resource) {
$name = pht('Resource %d', $resource->getID()).': '.$resource->getName();
$item = id(new PhabricatorObjectItemView())
->setHref($this->getApplicationURI('/resource/'.$resource->getID().'/'))
->setHeader($name);
$status = DrydockResourceStatus::getNameForStatus($resource->getStatus());
$item->addAttribute($status);
switch ($resource->getStatus()) {
case DrydockResourceStatus::STATUS_PENDING:
$item->setBarColor('yellow');
break;
case DrydockResourceStatus::STATUS_OPEN:
$item->setBarColor('green');
break;
case DrydockResourceStatus::STATUS_DESTROYED:
$item->setBarColor('black');
break;
default:
$item->setBarColor('red');
break;
}
$view->addItem($item);
}
return $view;
}
}
diff --git a/src/applications/feed/builder/PhabricatorFeedBuilder.php b/src/applications/feed/builder/PhabricatorFeedBuilder.php
index 25a127bc5c..dfb9751cf8 100644
--- a/src/applications/feed/builder/PhabricatorFeedBuilder.php
+++ b/src/applications/feed/builder/PhabricatorFeedBuilder.php
@@ -1,68 +1,68 @@
<?php
final class PhabricatorFeedBuilder {
private $stories;
private $framed;
public function __construct(array $stories) {
assert_instances_of($stories, 'PhabricatorFeedStory');
$this->stories = $stories;
}
public function setFramed($framed) {
$this->framed = $framed;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function buildView() {
if (!$this->user) {
throw new Exception('Call setUser() before buildView()!');
}
$user = $this->user;
$stories = $this->stories;
$null_view = new AphrontNullView();
require_celerity_resource('phabricator-feed-css');
$last_date = null;
foreach ($stories as $story) {
$story->setFramed($this->framed);
$date = ucfirst(phabricator_relative_date($story->getEpoch(), $user));
if ($date !== $last_date) {
if ($last_date !== null) {
$null_view->appendChild(
'<div class="phabricator-feed-story-date-separator"></div>');
}
$last_date = $date;
$null_view->appendChild(
- phutil_render_tag(
+ phutil_tag(
'div',
array(
'class' => 'phabricator-feed-story-date',
),
- phutil_escape_html($date)));
+ $date));
}
$view = $story->renderView();
$view->setUser($user);
$null_view->appendChild($view);
}
return id(new AphrontNullView())->appendChild(
'<div class="phabricator-feed-frame">'.
$null_view->render().
'</div>');
}
}
diff --git a/src/applications/feed/story/PhabricatorFeedStory.php b/src/applications/feed/story/PhabricatorFeedStory.php
index 5d9823aac4..b9715efca6 100644
--- a/src/applications/feed/story/PhabricatorFeedStory.php
+++ b/src/applications/feed/story/PhabricatorFeedStory.php
@@ -1,306 +1,306 @@
<?php
/**
* Manages rendering and aggregation of a story. A story is an event (like a
* user adding a comment) which may be represented in different forms on
* different channels (like feed, notifications and realtime alerts).
*
* @task load Loading Stories
* @task policy Policy Implementation
*/
abstract class PhabricatorFeedStory implements PhabricatorPolicyInterface {
private $data;
private $hasViewed;
private $framed;
private $handles = array();
private $objects = array();
/* -( Loading Stories )---------------------------------------------------- */
/**
* Given @{class:PhabricatorFeedStoryData} rows, load them into objects and
* construct appropriate @{class:PhabricatorFeedStory} wrappers for each
* data row.
*
* @param list<dict> List of @{class:PhabricatorFeedStoryData} rows from the
* database.
* @return list<PhabricatorFeedStory> List of @{class:PhabricatorFeedStory}
* objects.
* @task load
*/
public static function loadAllFromRows(array $rows, PhabricatorUser $viewer) {
$stories = array();
$data = id(new PhabricatorFeedStoryData())->loadAllFromArray($rows);
foreach ($data as $story_data) {
$class = $story_data->getStoryType();
try {
$ok =
class_exists($class) &&
is_subclass_of($class, 'PhabricatorFeedStory');
} catch (PhutilMissingSymbolException $ex) {
$ok = false;
}
// If the story type isn't a valid class or isn't a subclass of
// PhabricatorFeedStory, decline to load it.
if (!$ok) {
continue;
}
$key = $story_data->getChronologicalKey();
$stories[$key] = newv($class, array($story_data));
}
$object_phids = array();
$key_phids = array();
foreach ($stories as $key => $story) {
$phids = array();
foreach ($story->getRequiredObjectPHIDs() as $phid) {
$phids[$phid] = true;
}
if ($story->getPrimaryObjectPHID()) {
$phids[$story->getPrimaryObjectPHID()] = true;
}
$key_phids[$key] = $phids;
$object_phids += $phids;
}
$objects = id(new PhabricatorObjectHandleData(array_keys($object_phids)))
->setViewer($viewer)
->loadObjects();
foreach ($key_phids as $key => $phids) {
if (!$phids) {
continue;
}
$story_objects = array_select_keys($objects, array_keys($phids));
if (count($story_objects) != count($phids)) {
// An object this story requires either does not exist or is not visible
// to the user. Decline to render the story.
unset($stories[$key]);
unset($key_phids[$key]);
continue;
}
$stories[$key]->setObjects($story_objects);
}
$handle_phids = array();
foreach ($stories as $key => $story) {
foreach ($story->getRequiredHandlePHIDs() as $phid) {
$key_phids[$key][$phid] = true;
}
if ($story->getAuthorPHID()) {
$key_phids[$key][$story->getAuthorPHID()] = true;
}
$handle_phids += $key_phids[$key];
}
$handles = id(new PhabricatorObjectHandleData(array_keys($handle_phids)))
->setViewer($viewer)
->loadHandles();
foreach ($key_phids as $key => $phids) {
if (!$phids) {
continue;
}
$story_handles = array_select_keys($handles, array_keys($phids));
$stories[$key]->setHandles($story_handles);
}
return $stories;
}
public function setObjects(array $objects) {
$this->objects = $objects;
return $this;
}
public function getObject($phid) {
$object = idx($this->objects, $phid);
if (!$object) {
throw new Exception(
"Story is asking for an object it did not request ('{$phid}')!");
}
return $object;
}
public function getPrimaryObject() {
$phid = $this->getPrimaryObjectPHID();
if (!$phid) {
throw new Exception("Story has no primary object!");
}
return $this->getObject($phid);
}
public function getPrimaryObjectPHID() {
return null;
}
final public function __construct(PhabricatorFeedStoryData $data) {
$this->data = $data;
}
abstract public function renderView();
public function getRequiredHandlePHIDs() {
return array();
}
public function getRequiredObjectPHIDs() {
return array();
}
public function setHasViewed($has_viewed) {
$this->hasViewed = $has_viewed;
return $this;
}
public function getHasViewed() {
return $this->hasViewed;
}
final public function setFramed($framed) {
$this->framed = $framed;
return $this;
}
final public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
final protected function getObjects() {
return $this->objects;
}
final protected function getHandles() {
return $this->handles;
}
final protected function getHandle($phid) {
if (isset($this->handles[$phid])) {
if ($this->handles[$phid] instanceof PhabricatorObjectHandle) {
return $this->handles[$phid];
}
}
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setName("Unloaded Object '{$phid}'");
return $handle;
}
final public function getStoryData() {
return $this->data;
}
final public function getEpoch() {
return $this->getStoryData()->getEpoch();
}
final public function getChronologicalKey() {
return $this->getStoryData()->getChronologicalKey();
}
final public function getValue($key, $default = null) {
return $this->getStoryData()->getValue($key, $default);
}
final public function getAuthorPHID() {
return $this->getStoryData()->getAuthorPHID();
}
final protected function renderHandleList(array $phids) {
$list = array();
foreach ($phids as $phid) {
$list[] = $this->linkTo($phid);
}
return implode(', ', $list);
}
final protected function linkTo($phid) {
$handle = $this->getHandle($phid);
// NOTE: We render our own link here to customize the styling and add
// the '_top' target for framed feeds.
- return phutil_render_tag(
+ return phutil_tag(
'a',
array(
'href' => $handle->getURI(),
'target' => $this->framed ? '_top' : null,
),
- phutil_escape_html($handle->getLinkName()));
+ $handle->getLinkName());
}
final protected function renderString($str) {
return '<strong>'.phutil_escape_html($str).'</strong>';
}
final protected function renderSummary($text, $len = 128) {
if ($len) {
$text = phutil_utf8_shorten($text, $len);
}
$text = phutil_escape_html($text);
$text = str_replace("\n", '<br />', $text);
return $text;
}
public function getNotificationAggregations() {
return array();
}
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
/**
* @task policy
*/
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
/**
* @task policy
*/
public function getPolicy($capability) {
// If this story's primary object is a policy-aware object, use its policy
// to control story visiblity.
$primary_phid = $this->getPrimaryObjectPHID();
if (isset($this->objects[$primary_phid])) {
$object = $this->objects[$primary_phid];
if ($object instanceof PhabricatorPolicyInterface) {
return $object->getPolicy($capability);
}
}
// TODO: Remove this once all objects are policy-aware. For now, keep
// respecting the `feed.public` setting.
return PhabricatorEnv::getEnvConfig('feed.public')
? PhabricatorPolicies::POLICY_PUBLIC
: PhabricatorPolicies::POLICY_USER;
}
/**
* @task policy
*/
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
}
diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php
index 5dec02733e..62af029da8 100644
--- a/src/applications/herald/controller/HeraldTranscriptController.php
+++ b/src/applications/herald/controller/HeraldTranscriptController.php
@@ -1,517 +1,517 @@
<?php
final class HeraldTranscriptController extends HeraldController {
const FILTER_AFFECTED = 'affected';
const FILTER_OWNED = 'owned';
const FILTER_ALL = 'all';
private $id;
private $filter;
private $handles;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
$map = $this->getFilterMap();
$this->filter = idx($data, 'filter');
if (empty($map[$this->filter])) {
$this->filter = self::FILTER_AFFECTED;
}
}
public function processRequest() {
$xscript = id(new HeraldTranscript())->load($this->id);
if (!$xscript) {
throw new Exception('Uknown transcript!');
}
require_celerity_resource('herald-test-css');
$nav = $this->buildSideNav();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
$notice = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle('Old Transcript')
->appendChild(
'<p>Details of this transcript have been garbage collected.</p>');
$nav->appendChild($notice);
} else {
$filter = $this->getFilterPHIDs();
$this->filterTranscript($xscript, $filter);
$phids = array_merge($filter, $this->getTranscriptPHIDs($xscript));
$phids = array_unique($phids);
$phids = array_filter($phids);
$handles = $this->loadViewerHandles($phids);
$this->handles = $handles;
if ($xscript->getDryRun()) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('Dry Run');
$notice->appendChild(
'This was a dry run to test Herald rules, no actions were executed.');
$nav->appendChild($notice);
}
$apply_xscript_panel = $this->buildApplyTranscriptPanel(
$xscript);
$nav->appendChild($apply_xscript_panel);
$action_xscript_panel = $this->buildActionTranscriptPanel(
$xscript);
$nav->appendChild($action_xscript_panel);
$object_xscript_panel = $this->buildObjectTranscriptPanel(
$xscript);
$nav->appendChild($object_xscript_panel);
}
$crumbs = id($this->buildApplicationCrumbs())
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Transcripts'))
->setHref($this->getApplicationURI('/transcript/')))
->addCrumb(
id(new PhabricatorCrumbView())
->setName($xscript->getID()));
$nav->setCrumbs($crumbs);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Transcript',
));
}
protected function renderConditionTestValue($condition, $handles) {
$value = $condition->getTestValue();
if (!is_scalar($value) && $value !== null) {
foreach ($value as $key => $phid) {
$handle = idx($handles, $phid);
if ($handle) {
$value[$key] = $handle->getName();
} else {
// This shouldn't ever really happen as we are supposed to have
// grabbed handles for everything, but be super liberal in what
// we accept here since we expect all sorts of weird issues as we
// version the system.
$value[$key] = 'Unknown Object #'.$phid;
}
}
sort($value);
$value = implode(', ', $value);
}
return
'<span class="condition-test-value">'.
phutil_escape_html($value).
'</span>';
}
private function buildSideNav() {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/herald/transcript/'.$this->id.'/'));
$items = array();
$filters = $this->getFilterMap();
foreach ($filters as $key => $name) {
$nav->addFilter($key, $name);
}
$nav->selectFilter($this->filter, null);
return $nav;
}
protected function getFilterMap() {
return array(
self::FILTER_AFFECTED => 'Rules that Affected Me',
self::FILTER_OWNED => 'Rules I Own',
self::FILTER_ALL => 'All Rules',
);
}
protected function getFilterPHIDs() {
return array($this->getRequest()->getUser()->getPHID());
/* TODO
$viewer_id = $this->getRequest()->getUser()->getPHID();
$fbids = array();
if ($this->filter == self::FILTER_AFFECTED) {
$fbids[] = $viewer_id;
require_module_lazy('intern/subscriptions');
$datastore = new SubscriberDatabaseStore();
$lists = $datastore->getUserMailmanLists($viewer_id);
foreach ($lists as $list) {
$fbids[] = $list;
}
}
return $fbids;
*/
}
protected function getTranscriptPHIDs($xscript) {
$phids = array();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
return array();
}
$phids[] = $object_xscript->getPHID();
foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
// TODO: This is total hacks. Add another amazing layer of abstraction.
$target = (array)$apply_xscript->getTarget();
foreach ($target as $phid) {
if ($phid) {
$phids[] = $phid;
}
}
}
foreach ($xscript->getRuleTranscripts() as $rule_xscript) {
$phids[] = $rule_xscript->getRuleOwner();
}
$condition_xscripts = $xscript->getConditionTranscripts();
if ($condition_xscripts) {
$condition_xscripts = call_user_func_array(
'array_merge',
$condition_xscripts);
}
foreach ($condition_xscripts as $condition_xscript) {
$value = $condition_xscript->getTestValue();
// TODO: Also total hacks.
if (is_array($value)) {
foreach ($value as $phid) {
if ($phid) { // TODO: Probably need to make sure this "looks like" a
// PHID or decrease the level of hacks here; this used
// to be an is_numeric() check in Facebook land.
$phids[] = $phid;
}
}
}
}
return $phids;
}
protected function filterTranscript($xscript, $filter_phids) {
$filter_owned = ($this->filter == self::FILTER_OWNED);
$filter_affected = ($this->filter == self::FILTER_AFFECTED);
if (!$filter_owned && !$filter_affected) {
// No filtering to be done.
return;
}
if (!$xscript->getObjectTranscript()) {
return;
}
$user_phid = $this->getRequest()->getUser()->getPHID();
$keep_apply_xscripts = array();
$keep_rule_xscripts = array();
$filter_phids = array_fill_keys($filter_phids, true);
$rule_xscripts = $xscript->getRuleTranscripts();
foreach ($xscript->getApplyTranscripts() as $id => $apply_xscript) {
$rule_id = $apply_xscript->getRuleID();
if ($filter_owned) {
if (empty($rule_xscripts[$rule_id])) {
// No associated rule so you can't own this effect.
continue;
}
if ($rule_xscripts[$rule_id]->getRuleOwner() != $user_phid) {
continue;
}
} else if ($filter_affected) {
$targets = (array)$apply_xscript->getTarget();
if (!array_select_keys($filter_phids, $targets)) {
continue;
}
}
$keep_apply_xscripts[$id] = true;
if ($rule_id) {
$keep_rule_xscripts[$rule_id] = true;
}
}
foreach ($rule_xscripts as $rule_id => $rule_xscript) {
if ($filter_owned && $rule_xscript->getRuleOwner() == $user_phid) {
$keep_rule_xscripts[$rule_id] = true;
}
}
$xscript->setRuleTranscripts(
array_intersect_key(
$xscript->getRuleTranscripts(),
$keep_rule_xscripts));
$xscript->setApplyTranscripts(
array_intersect_key(
$xscript->getApplyTranscripts(),
$keep_apply_xscripts));
$xscript->setConditionTranscripts(
array_intersect_key(
$xscript->getConditionTranscripts(),
$keep_rule_xscripts));
}
private function buildApplyTranscriptPanel($xscript) {
$handles = $this->handles;
$action_names = HeraldActionConfig::getActionMessageMapForRuleType(null);
$rows = array();
foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
$target = $apply_xscript->getTarget();
switch ($apply_xscript->getAction()) {
case HeraldActionConfig::ACTION_NOTHING:
$target = '';
break;
case HeraldActionConfig::ACTION_FLAG:
$target = PhabricatorFlagColor::getColorName($target);
break;
default:
if ($target) {
foreach ($target as $k => $phid) {
$target[$k] = $handles[$phid]->getName();
}
$target = implode("\n", $target);
} else {
$target = '<empty>';
}
break;
}
$target = phutil_escape_html($target);
if ($apply_xscript->getApplied()) {
$outcome = '<span class="outcome-success">SUCCESS</span>';
} else {
$outcome = '<span class="outcome-failure">FAILURE</span>';
}
$outcome .= ' '.phutil_escape_html($apply_xscript->getAppliedReason());
$rows[] = array(
phutil_escape_html($action_names[$apply_xscript->getAction()]),
$target,
'<strong>Taken because:</strong> '.
phutil_escape_html($apply_xscript->getReason()).
'<br />'.
'<strong>Outcome:</strong> '.$outcome,
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString('No actions were taken.');
$table->setHeaders(
array(
'Action',
'Target',
'Details',
));
$table->setColumnClasses(
array(
'',
'',
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader(pht('Actions Taken'));
$panel->appendChild($table);
$panel->setNoBackground();
return $panel;
}
private function buildActionTranscriptPanel($xscript) {
$action_xscript = mgroup($xscript->getApplyTranscripts(), 'getRuleID');
$field_names = HeraldFieldConfig::getFieldMap();
$condition_names = HeraldConditionConfig::getConditionMap();
$handles = $this->handles;
$rule_markup = array();
foreach ($xscript->getRuleTranscripts() as $rule_id => $rule) {
$cond_markup = array();
foreach ($xscript->getConditionTranscriptsForRule($rule_id) as $cond) {
if ($cond->getNote()) {
$note =
'<div class="herald-condition-note">'.
phutil_escape_html($cond->getNote()).
'</div>';
} else {
$note = null;
}
if ($cond->getResult()) {
$result =
'<span class="herald-outcome condition-pass">'.
"\xE2\x9C\x93".
'</span>';
} else {
$result =
'<span class="herald-outcome condition-fail">'.
"\xE2\x9C\x98".
'</span>';
}
$cond_markup[] =
'<li>'.
$result.' Condition: '.
phutil_escape_html($field_names[$cond->getFieldName()]).
' '.
phutil_escape_html($condition_names[$cond->getCondition()]).
' '.
$this->renderConditionTestValue($cond, $handles).
$note.
'</li>';
}
if ($rule->getResult()) {
$result = '<span class="herald-outcome rule-pass">PASS</span>';
$class = 'herald-rule-pass';
} else {
$result = '<span class="herald-outcome rule-fail">FAIL</span>';
$class = 'herald-rule-fail';
}
$cond_markup[] =
'<li>'.$result.' '.phutil_escape_html($rule->getReason()).'</li>';
/*
if ($rule->getResult()) {
$actions = idx($action_xscript, $rule_id, array());
if ($actions) {
$cond_markup[] = <li><div class="action-header">Actions</div></li>;
foreach ($actions as $action) {
$target = $action->getTarget();
if ($target) {
foreach ((array)$target as $k => $phid) {
$target[$k] = $handles[$phid]->getName();
}
$target = <strong>: {implode(', ', $target)}</strong>;
}
$cond_markup[] =
<li>
{$action_names[$action->getAction()]}
{$target}
</li>;
}
}
}
*/
$user_phid = $this->getRequest()->getUser()->getPHID();
$name = $rule->getRuleName();
if ($rule->getRuleOwner() == $user_phid) {
// $name = <a href={"/herald/rule/".$rule->getRuleID()."/"}>{$name}</a>;
}
$rule_markup[] =
phutil_render_tag(
'li',
array(
'class' => $class,
),
'<div class="rule-name">'.
'<strong>'.phutil_escape_html($name).'</strong> '.
phutil_escape_html($handles[$rule->getRuleOwner()]->getName()).
'</div>'.
'<ul>'.implode("\n", $cond_markup).'</ul>');
}
$panel = new AphrontPanelView();
$panel->setHeader('Rule Details');
$panel->appendChild(
'<ul class="herald-explain-list">'.
implode("\n", $rule_markup).
'</ul>');
return $panel;
}
private function buildObjectTranscriptPanel($xscript) {
$field_names = HeraldFieldConfig::getFieldMap();
$object_xscript = $xscript->getObjectTranscript();
$data = array();
if ($object_xscript) {
$phid = $object_xscript->getPHID();
$handles = $this->loadViewerHandles(array($phid));
$data += array(
'Object Name' => $object_xscript->getName(),
'Object Type' => $object_xscript->getType(),
'Object PHID' => $phid,
'Object Link' => $handles[$phid]->renderLink(),
);
}
$data += $xscript->getMetadataMap();
if ($object_xscript) {
foreach ($object_xscript->getFields() as $field => $value) {
$field = idx($field_names, $field, '['.$field.'?]');
$data['Field: '.$field] = $value;
}
}
$rows = array();
foreach ($data as $name => $value) {
if (!is_scalar($value) && !is_null($value)) {
$value = implode("\n", $value);
}
if (strlen($value) > 256) {
- $value = phutil_render_tag(
+ $value = phutil_tag(
'textarea',
array(
'class' => 'herald-field-value-transcript',
),
- phutil_escape_html($value));
+ $value);
} else if ($name === 'Object Link') {
// The link cannot be escaped
} else {
$value = phutil_escape_html($value);
}
$rows[] = array(
phutil_escape_html($name),
$value,
);
}
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'header',
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader('Object Transcript');
$panel->appendChild($table);
return $panel;
}
}
diff --git a/src/applications/herald/view/HeraldRuleEditHistoryView.php b/src/applications/herald/view/HeraldRuleEditHistoryView.php
index d931d2b2d6..7e3fb7b625 100644
--- a/src/applications/herald/view/HeraldRuleEditHistoryView.php
+++ b/src/applications/herald/view/HeraldRuleEditHistoryView.php
@@ -1,73 +1,73 @@
<?php
final class HeraldRuleEditHistoryView extends AphrontView {
private $edits;
private $handles;
public function setEdits(array $edits) {
$this->edits = $edits;
return $this;
}
public function getEdits() {
return $this->edits;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function render() {
$rows = array();
foreach ($this->edits as $edit) {
$name = nonempty($edit->getRuleName(), 'Unknown Rule');
- $rule_name = phutil_render_tag(
+ $rule_name = phutil_tag(
'strong',
array(),
- phutil_escape_html($name));
+ $name);
switch ($edit->getAction()) {
case 'create':
$details = "Created rule '{$rule_name}'.";
break;
case 'delete':
$details = "Deleted rule '{$rule_name}'.";
break;
case 'edit':
default:
$details = "Edited rule '{$rule_name}'.";
break;
}
$rows[] = array(
$edit->getRuleID(),
$this->handles[$edit->getEditorPHID()]->renderLink(),
$details,
phabricator_datetime($edit->getDateCreated(), $this->user),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString("No edits for rule.");
$table->setHeaders(
array(
'Rule ID',
'Editor',
'Details',
'Edit Date',
));
$table->setColumnClasses(
array(
'',
'',
'wide',
'',
));
return $table->render();
}
}
diff --git a/src/applications/herald/view/HeraldRuleListView.php b/src/applications/herald/view/HeraldRuleListView.php
index 65e0c8d2e0..2bfe609bdf 100644
--- a/src/applications/herald/view/HeraldRuleListView.php
+++ b/src/applications/herald/view/HeraldRuleListView.php
@@ -1,109 +1,109 @@
<?php
final class HeraldRuleListView extends AphrontView {
private $rules;
private $handles;
private $showAuthor;
private $showRuleType;
public function setRules(array $rules) {
assert_instances_of($rules, 'HeraldRule');
$this->rules = $rules;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setShowAuthor($show_author) {
$this->showAuthor = $show_author;
return $this;
}
public function setShowRuleType($show_rule_type) {
$this->showRuleType = $show_rule_type;
return $this;
}
public function render() {
$type_map = HeraldRuleTypeConfig::getRuleTypeMap();
$rows = array();
foreach ($this->rules as $rule) {
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) {
$author = null;
} else {
$author = $this->handles[$rule->getAuthorPHID()]->renderLink();
}
- $name = phutil_render_tag(
+ $name = phutil_tag(
'a',
array(
'href' => '/herald/rule/'.$rule->getID().'/',
),
- phutil_escape_html($rule->getName()));
+ $rule->getName());
$edit_log = phutil_render_tag(
'a',
array(
'href' => '/herald/history/'.$rule->getID().'/',
),
'View Edit Log');
$delete = javelin_render_tag(
'a',
array(
'href' => '/herald/delete/'.$rule->getID().'/',
'sigil' => 'workflow',
'class' => 'button small grey',
),
'Delete');
$rows[] = array(
$type_map[$rule->getRuleType()],
$author,
$name,
$edit_log,
$delete,
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString("No matching rules.");
$table->setHeaders(
array(
'Rule Type',
'Author',
'Rule Name',
'Edit Log',
'',
));
$table->setColumnClasses(
array(
'',
'',
'wide pri',
'',
'action'
));
$table->setColumnVisibility(
array(
$this->showRuleType,
$this->showAuthor,
true,
true,
true,
));
return $table->render();
}
}
diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php
index bf44e11ecf..cee45c4c20 100644
--- a/src/applications/maniphest/controller/ManiphestReportController.php
+++ b/src/applications/maniphest/controller/ManiphestReportController.php
@@ -1,763 +1,763 @@
<?php
/**
* @group maniphest
*/
final class ManiphestReportController extends ManiphestController {
private $view;
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
$uri = $request->getRequestURI();
$project = head($request->getArr('set_project'));
$project = nonempty($project, null);
$uri = $uri->alter('project', $project);
$window = $request->getStr('set_window');
$uri = $uri->alter('window', $window);
return id(new AphrontRedirectResponse())->setURI($uri);
}
$base_nav = $this->buildBaseSideNav();
$base_nav->selectFilter('report', 'report');
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/maniphest/report/'));
$nav->addLabel('Open Tasks');
$nav->addFilter('user', 'By User');
$nav->addFilter('project', 'By Project');
$nav->addLabel('Burnup');
$nav->addFilter('burn', 'Burnup Rate');
$this->view = $nav->selectFilter($this->view, 'user');
require_celerity_resource('maniphest-report-css');
switch ($this->view) {
case 'burn':
$core = $this->renderBurn();
break;
case 'user':
case 'project':
$core = $this->renderOpenTasks();
break;
default:
return new Aphront404Response();
}
$nav->appendChild($core);
$base_nav->appendChild($nav);
return $this->buildStandardPageResponse(
$base_nav,
array(
'title' => 'Maniphest Reports',
));
}
public function renderBurn() {
$request = $this->getRequest();
$user = $request->getUser();
$handle = null;
$project_phid = $request->getStr('project');
if ($project_phid) {
$phids = array($project_phid);
$handles = $this->loadViewerHandles($phids);
$handle = $handles[$project_phid];
}
$table = new ManiphestTransaction();
$conn = $table->establishConnection('r');
$joins = '';
if ($project_phid) {
$joins = qsprintf(
$conn,
'JOIN %T t ON x.taskID = t.id
JOIN %T p ON p.taskPHID = t.phid AND p.projectPHID = %s',
id(new ManiphestTask())->getTableName(),
id(new ManiphestTaskProject())->getTableName(),
$project_phid);
}
$data = queryfx_all(
$conn,
'SELECT x.oldValue, x.newValue, x.dateCreated FROM %T x %Q
WHERE transactionType = %s
ORDER BY x.dateCreated ASC',
$table->getTableName(),
$joins,
ManiphestTransactionType::TYPE_STATUS);
$stats = array();
$day_buckets = array();
$open_tasks = array();
foreach ($data as $key => $row) {
// NOTE: Hack to avoid json_decode().
$oldv = trim($row['oldValue'], '"');
$newv = trim($row['newValue'], '"');
$old_is_open = ($oldv === (string)ManiphestTaskStatus::STATUS_OPEN);
$new_is_open = ($newv === (string)ManiphestTaskStatus::STATUS_OPEN);
$is_open = ($new_is_open && !$old_is_open);
$is_close = ($old_is_open && !$new_is_open);
$data[$key]['_is_open'] = $is_open;
$data[$key]['_is_close'] = $is_close;
if (!$is_open && !$is_close) {
// This is either some kind of bogus event, or a resolution change
// (e.g., resolved -> invalid). Just skip it.
continue;
}
$day_bucket = phabricator_format_local_time(
$row['dateCreated'],
$user,
'Yz');
$day_buckets[$day_bucket] = $row['dateCreated'];
if (empty($stats[$day_bucket])) {
$stats[$day_bucket] = array(
'open' => 0,
'close' => 0,
);
}
$stats[$day_bucket][$is_close ? 'close' : 'open']++;
}
$template = array(
'open' => 0,
'close' => 0,
);
$rows = array();
$rowc = array();
$last_month = null;
$last_month_epoch = null;
$last_week = null;
$last_week_epoch = null;
$week = null;
$month = null;
$last = last_key($stats) - 1;
$period = $template;
foreach ($stats as $bucket => $info) {
$epoch = $day_buckets[$bucket];
$week_bucket = phabricator_format_local_time(
$epoch,
$user,
'YW');
if ($week_bucket != $last_week) {
if ($week) {
$rows[] = $this->formatBurnRow(
'Week of '.phabricator_date($last_week_epoch, $user),
$week);
$rowc[] = 'week';
}
$week = $template;
$last_week = $week_bucket;
$last_week_epoch = $epoch;
}
$month_bucket = phabricator_format_local_time(
$epoch,
$user,
'Ym');
if ($month_bucket != $last_month) {
if ($month) {
$rows[] = $this->formatBurnRow(
phabricator_format_local_time($last_month_epoch, $user, 'F, Y'),
$month);
$rowc[] = 'month';
}
$month = $template;
$last_month = $month_bucket;
$last_month_epoch = $epoch;
}
$rows[] = $this->formatBurnRow(phabricator_date($epoch, $user), $info);
$rowc[] = null;
$week['open'] += $info['open'];
$week['close'] += $info['close'];
$month['open'] += $info['open'];
$month['close'] += $info['close'];
$period['open'] += $info['open'];
$period['close'] += $info['close'];
}
if ($week) {
$rows[] = $this->formatBurnRow(
'Week To Date',
$week);
$rowc[] = 'week';
}
if ($month) {
$rows[] = $this->formatBurnRow(
'Month To Date',
$month);
$rowc[] = 'month';
}
$rows[] = $this->formatBurnRow(
'All Time',
$period);
$rowc[] = 'aggregate';
$rows = array_reverse($rows);
$rowc = array_reverse($rowc);
$table = new AphrontTableView($rows);
$table->setRowClasses($rowc);
$table->setHeaders(
array(
'Period',
'Opened',
'Closed',
'Change',
));
$table->setColumnClasses(
array(
'right wide',
'n',
'n',
'n',
));
if ($handle) {
$header = "Task Burn Rate for Project ".$handle->renderLink();
$caption = "<p>NOTE: This table reflects tasks <em>currently</em> in ".
"the project. If a task was opened in the past but added to ".
"the project recently, it is counted on the day it was ".
"opened, not the day it was categorized. If a task was part ".
"of this project in the past but no longer is, it is not ".
"counted at all.</p>";
} else {
$header = "Task Burn Rate for All Tasks";
$caption = null;
}
$panel = new AphrontPanelView();
$panel->setHeader($header);
$panel->setCaption($caption);
$panel->appendChild($table);
$tokens = array();
if ($handle) {
$tokens = array(
$handle->getPHID() => $handle->getFullName(),
);
}
$filter = $this->renderReportFilters($tokens, $has_window = false);
$id = celerity_generate_unique_node_id();
$chart = phutil_render_tag(
'div',
array(
'id' => $id,
'style' => 'border: 1px solid #6f6f6f; '.
'margin: 1em 2em; '.
'height: 400px; ',
),
'');
list($burn_x, $burn_y) = $this->buildSeries($data);
require_celerity_resource('raphael-core');
require_celerity_resource('raphael-g');
require_celerity_resource('raphael-g-line');
Javelin::initBehavior('line-chart', array(
'hardpoint' => $id,
'x' => array(
$burn_x,
),
'y' => array(
$burn_y,
),
'xformat' => 'epoch',
));
return array($filter, $chart, $panel);
}
private function renderReportFilters(array $tokens, $has_window) {
$request = $this->getRequest();
$user = $request->getUser();
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/searchproject/')
->setLabel('Project')
->setLimit(1)
->setName('set_project')
->setValue($tokens));
if ($has_window) {
list($window_str, $ignored, $window_error) = $this->getWindow();
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('"Recently" Means')
->setName('set_window')
->setCaption(
'Configure the cutoff for the "Recently Closed" column.')
->setValue($window_str)
->setError($window_error));
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Filter By Project'));
$filter = new AphrontListFilterView();
$filter->appendChild($form);
return $filter;
}
private function buildSeries(array $data) {
$out = array();
$counter = 0;
foreach ($data as $row) {
$t = (int)$row['dateCreated'];
if ($row['_is_close']) {
--$counter;
$out[$t] = $counter;
} else if ($row['_is_open']) {
++$counter;
$out[$t] = $counter;
}
}
return array(array_keys($out), array_values($out));
}
private function formatBurnRow($label, $info) {
$delta = $info['open'] - $info['close'];
$fmt = number_format($delta);
if ($delta > 0) {
$fmt = '+'.$fmt;
$fmt = '<span class="red">'.$fmt.'</span>';
} else {
$fmt = '<span class="green">'.$fmt.'</span>';
}
return array(
$label,
number_format($info['open']),
number_format($info['close']),
$fmt);
}
public function renderOpenTasks() {
$request = $this->getRequest();
$user = $request->getUser();
$query = id(new ManiphestTaskQuery())
->withStatus(ManiphestTaskQuery::STATUS_OPEN);
$project_phid = $request->getStr('project');
$project_handle = null;
if ($project_phid) {
$phids = array($project_phid);
$handles = $this->loadViewerHandles($phids);
$project_handle = $handles[$project_phid];
$query->withAnyProjects($phids);
}
$tasks = $query->execute();
$recently_closed = $this->loadRecentlyClosedTasks();
$date = phabricator_date(time(), $user);
switch ($this->view) {
case 'user':
$result = mgroup($tasks, 'getOwnerPHID');
$leftover = idx($result, '', array());
unset($result['']);
$result_closed = mgroup($recently_closed, 'getOwnerPHID');
$leftover_closed = idx($result_closed, '', array());
unset($result_closed['']);
$base_link = '/maniphest/?users=';
$leftover_name = phutil_render_tag(
'a',
array(
'href' => $base_link.ManiphestTaskOwner::OWNER_UP_FOR_GRABS,
),
'<em>(Up For Grabs)</em>');
$col_header = 'User';
$header = 'Open Tasks by User and Priority ('.$date.')';
break;
case 'project':
$result = array();
$leftover = array();
foreach ($tasks as $task) {
$phids = $task->getProjectPHIDs();
if ($phids) {
foreach ($phids as $project_phid) {
$result[$project_phid][] = $task;
}
} else {
$leftover[] = $task;
}
}
$result_closed = array();
$leftover_closed = array();
foreach ($recently_closed as $task) {
$phids = $task->getProjectPHIDs();
if ($phids) {
foreach ($phids as $project_phid) {
$result_closed[$project_phid][] = $task;
}
} else {
$leftover_closed[] = $task;
}
}
$base_link = '/maniphest/view/all/?projects=';
$leftover_name = phutil_render_tag(
'a',
array(
'href' => $base_link.ManiphestTaskOwner::PROJECT_NO_PROJECT,
),
'<em>(No Project)</em>');
$col_header = 'Project';
$header = 'Open Tasks by Project and Priority ('.$date.')';
break;
}
$phids = array_keys($result);
$handles = $this->loadViewerHandles($phids);
$handles = msort($handles, 'getName');
$order = $request->getStr('order', 'name');
list($order, $reverse) = AphrontTableView::parseSort($order);
require_celerity_resource('aphront-tooltip-css');
Javelin::initBehavior('phabricator-tooltips', array());
$rows = array();
$pri_total = array();
foreach (array_merge($handles, array(null)) as $handle) {
if ($handle) {
if (($project_handle) &&
($project_handle->getPHID() == $handle->getPHID())) {
// If filtering by, e.g., "bugs", don't show a "bugs" group.
continue;
}
$tasks = idx($result, $handle->getPHID(), array());
- $name = phutil_render_tag(
+ $name = phutil_tag(
'a',
array(
'href' => $base_link.$handle->getPHID(),
),
- phutil_escape_html($handle->getName()));
+ $handle->getName());
$closed = idx($result_closed, $handle->getPHID(), array());
} else {
$tasks = $leftover;
$name = $leftover_name;
$closed = $leftover_closed;
}
$taskv = $tasks;
$tasks = mgroup($tasks, 'getPriority');
$row = array();
$row[] = $name;
$total = 0;
foreach (ManiphestTaskPriority::getTaskPriorityMap() as $pri => $label) {
$n = count(idx($tasks, $pri, array()));
if ($n == 0) {
$row[] = '-';
} else {
$row[] = number_format($n);
}
$total += $n;
}
$row[] = number_format($total);
list($link, $oldest_all) = $this->renderOldest($taskv);
$row[] = $link;
$normal_or_better = array();
foreach ($taskv as $id => $task) {
if ($task->getPriority() < ManiphestTaskPriority::PRIORITY_NORMAL) {
continue;
}
$normal_or_better[$id] = $task;
}
list($link, $oldest_pri) = $this->renderOldest($normal_or_better);
$row[] = $link;
if ($closed) {
$task_ids = implode(',', mpull($closed, 'getID'));
- $row[] = phutil_render_tag(
+ $row[] = phutil_tag(
'a',
array(
'href' => '/maniphest/view/custom/?s=oc&tasks='.$task_ids,
'target' => '_blank',
),
- phutil_escape_html(number_format(count($closed))));
+ number_format(count($closed)));
} else {
$row[] = '-';
}
switch ($order) {
case 'total':
$row['sort'] = $total;
break;
case 'oldest-all':
$row['sort'] = $oldest_all;
break;
case 'oldest-pri':
$row['sort'] = $oldest_pri;
break;
case 'closed':
$row['sort'] = count($closed);
break;
case 'name':
default:
$row['sort'] = $handle ? $handle->getName() : '~';
break;
}
$rows[] = $row;
}
$rows = isort($rows, 'sort');
foreach ($rows as $k => $row) {
unset($rows[$k]['sort']);
}
if ($reverse) {
$rows = array_reverse($rows);
}
$cname = array($col_header);
$cclass = array('pri right wide');
$pri_map = ManiphestTaskPriority::getTaskBriefPriorityMap();
foreach ($pri_map as $pri => $label) {
$cname[] = $label;
$cclass[] = 'n';
}
$cname[] = 'Total';
$cclass[] = 'n';
$cname[] = javelin_render_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'Oldest open task.',
'size' => 200,
),
),
'Oldest (All)');
$cclass[] = 'n';
$cname[] = javelin_render_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'Oldest open task, excluding those with Low or Wishlist '.
'priority.',
'size' => 200,
),
),
'Oldest (Pri)');
$cclass[] = 'n';
list($ignored, $window_epoch) = $this->getWindow();
$cname[] = javelin_render_tag(
'span',
array(
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'Closed after '.phabricator_datetime($window_epoch, $user),
'size' => 260
),
),
'Recently Closed');
$cclass[] = 'n';
$table = new AphrontTableView($rows);
$table->setHeaders($cname);
$table->setColumnClasses($cclass);
$table->makeSortable(
$request->getRequestURI(),
'order',
$order,
$reverse,
array(
'name',
null,
null,
null,
null,
null,
null,
'total',
'oldest-all',
'oldest-pri',
'closed',
));
$panel = new AphrontPanelView();
$panel->setHeader($header);
$panel->appendChild($table);
$tokens = array();
if ($project_handle) {
$tokens = array(
$project_handle->getPHID() => $project_handle->getFullName(),
);
}
$filter = $this->renderReportFilters($tokens, $has_window = true);
return array($filter, $panel);
}
/**
* Load all the tasks that have been recently closed.
*/
private function loadRecentlyClosedTasks() {
list($ignored, $window_epoch) = $this->getWindow();
$table = new ManiphestTask();
$xtable = new ManiphestTransaction();
$conn_r = $table->establishConnection('r');
$tasks = queryfx_all(
$conn_r,
'SELECT t.* FROM %T t JOIN %T x ON x.taskID = t.id
WHERE t.status != 0
AND x.oldValue IN (null, %s, %s)
AND x.newValue NOT IN (%s, %s)
AND t.dateModified >= %d
AND x.dateCreated >= %d',
$table->getTableName(),
$xtable->getTableName(),
// TODO: Gross. This table is not meant to be queried like this. Build
// real stats tables.
json_encode((int)ManiphestTaskStatus::STATUS_OPEN),
json_encode((string)ManiphestTaskStatus::STATUS_OPEN),
json_encode((int)ManiphestTaskStatus::STATUS_OPEN),
json_encode((string)ManiphestTaskStatus::STATUS_OPEN),
$window_epoch,
$window_epoch);
return id(new ManiphestTask())->loadAllFromArray($tasks);
}
/**
* Parse the "Recently Means" filter into:
*
* - A string representation, like "12 AM 7 days ago" (default);
* - a locale-aware epoch representation; and
* - a possible error.
*/
private function getWindow() {
$request = $this->getRequest();
$user = $request->getUser();
$window_str = $this->getRequest()->getStr('window', '12 AM 7 days ago');
$error = null;
$window_epoch = null;
// Do locale-aware parsing so that the user's timezone is assumed for
// time windows like "3 PM", rather than assuming the server timezone.
$timezone = new DateTimeZone($user->getTimezoneIdentifier());
try {
$date = new DateTime($window_str, $timezone);
$window_epoch = $date->format('U');
} catch (Exception $e) {
$error = 'Invalid';
$window_epoch = time() - (60 * 60 * 24 * 7);
}
// If the time ends up in the future, convert it to the corresponding time
// and equal distance in the past. This is so users can type "6 days" (which
// means "6 days from now") and get the behavior of "6 days ago", rather
// than no results (because the window epoch is in the future). This might
// be a little confusing because it casues "tomorrow" to mean "yesterday"
// and "2022" (or whatever) to mean "ten years ago", but these inputs are
// nonsense anyway.
if ($window_epoch > time()) {
$window_epoch = time() - ($window_epoch - time());
}
return array($window_str, $window_epoch, $error);
}
private function renderOldest(array $tasks) {
assert_instances_of($tasks, 'ManiphestTask');
$oldest = null;
foreach ($tasks as $id => $task) {
if (($oldest === null) ||
($task->getDateCreated() < $tasks[$oldest]->getDateCreated())) {
$oldest = $id;
}
}
if ($oldest === null) {
return array('-', 0);
}
$oldest = $tasks[$oldest];
$raw_age = (time() - $oldest->getDateCreated());
$age = number_format($raw_age / (24 * 60 * 60)).' d';
$link = javelin_render_tag(
'a',
array(
'href' => '/T'.$oldest->getID(),
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => 'T'.$oldest->getID().': '.$oldest->getTitle(),
),
'target' => '_blank',
),
phutil_escape_html($age));
return array($link, $raw_age);
}
}
diff --git a/src/applications/maniphest/controller/ManiphestSavedQueryListController.php b/src/applications/maniphest/controller/ManiphestSavedQueryListController.php
index 3578aad4e2..9957aa0991 100644
--- a/src/applications/maniphest/controller/ManiphestSavedQueryListController.php
+++ b/src/applications/maniphest/controller/ManiphestSavedQueryListController.php
@@ -1,132 +1,132 @@
<?php
/**
* @group maniphest
*/
final class ManiphestSavedQueryListController extends ManiphestController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$nav = $this->buildBaseSideNav();
$queries = id(new ManiphestSavedQuery())->loadAllWhere(
'userPHID = %s ORDER BY name ASC',
$user->getPHID());
$default = null;
if ($request->isFormPost()) {
$new_default = null;
foreach ($queries as $query) {
if ($query->getID() == $request->getInt('default')) {
$new_default = $query;
}
}
if ($this->getDefaultQuery()) {
$this->getDefaultQuery()->setIsDefault(0)->save();
}
if ($new_default) {
$new_default->setIsDefault(1)->save();
}
return id(new AphrontRedirectResponse())->setURI('/maniphest/custom/');
}
$rows = array();
foreach ($queries as $query) {
if ($query->getIsDefault()) {
$default = $query;
}
$rows[] = array(
phutil_tag(
'input',
array(
'type' => 'radio',
'name' => 'default',
'value' => $query->getID(),
'checked' => ($query->getIsDefault() ? 'checked' : null),
)),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/maniphest/view/custom/?key='.$query->getQueryKey(),
),
- phutil_escape_html($query->getName())),
+ $query->getName()),
phutil_render_tag(
'a',
array(
'href' => '/maniphest/custom/edit/'.$query->getID().'/',
'class' => 'grey small button',
),
'Edit'),
javelin_render_tag(
'a',
array(
'href' => '/maniphest/custom/delete/'.$query->getID().'/',
'class' => 'grey small button',
'sigil' => 'workflow',
),
'Delete'),
);
}
$rows[] = array(
phutil_tag(
'input',
array(
'type' => 'radio',
'name' => 'default',
'value' => 0,
'checked' => ($default === null ? 'checked' : null),
)),
'<em>No Default</em>',
'',
'',
);
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Default',
'Name',
'Edit',
'Delete',
));
$table->setColumnClasses(
array(
'radio',
'wide pri',
'action',
'action',
));
$panel = new AphrontPanelView();
$panel->setHeader('Saved Custom Queries');
$panel->addButton(
phutil_render_tag(
'button',
array(),
'Save Default Query'));
$panel->appendChild($table);
$form = phabricator_render_form(
$user,
array(
'method' => 'POST',
'action' => $request->getRequestURI(),
),
$panel->render());
$nav->selectFilter('saved', 'saved');
$nav->appendChild($form);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Saved Queries',
));
}
}
diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
index 5bdb168d77..13482b8f35 100644
--- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
@@ -1,535 +1,535 @@
<?php
/**
* @group maniphest
*/
final class ManiphestTaskDetailController extends ManiphestController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$e_title = null;
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
$task = id(new ManiphestTask())->load($this->id);
if (!$task) {
return new Aphront404Response();
}
$workflow = $request->getStr('workflow');
$parent_task = null;
if ($workflow && is_numeric($workflow)) {
$parent_task = id(new ManiphestTask())->load($workflow);
}
$transactions = id(new ManiphestTransaction())->loadAllWhere(
'taskID = %d ORDER BY id ASC',
$task->getID());
$e_commit = PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT;
$e_dep_on = PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK;
$e_dep_by = PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK;
$e_rev = PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV;
$phid = $task->getPHID();
$query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($phid))
->withEdgeTypes(
array(
$e_commit,
$e_dep_on,
$e_dep_by,
$e_rev,
));
$edges = idx($query->execute(), $phid);
$phids = array_fill_keys($query->getDestinationPHIDs(), true);
foreach ($transactions as $transaction) {
foreach ($transaction->extractPHIDs() as $phid) {
$phids[$phid] = true;
}
}
foreach ($task->getCCPHIDs() as $phid) {
$phids[$phid] = true;
}
foreach ($task->getProjectPHIDs() as $phid) {
$phids[$phid] = true;
}
if ($task->getOwnerPHID()) {
$phids[$task->getOwnerPHID()] = true;
}
$phids[$task->getAuthorPHID()] = true;
$attached = $task->getAttached();
foreach ($attached as $type => $list) {
foreach ($list as $phid => $info) {
$phids[$phid] = true;
}
}
if ($parent_task) {
$phids[$parent_task->getPHID()] = true;
}
$phids = array_keys($phids);
$this->loadHandles($phids);
$context_bar = null;
if ($parent_task) {
$context_bar = new AphrontContextBarView();
$context_bar->addButton(
phutil_render_tag(
'a',
array(
'href' => '/maniphest/task/create/?parent='.$parent_task->getID(),
'class' => 'green button',
),
'Create Another Subtask'));
$context_bar->appendChild(
'Created a subtask of <strong>'.
$this->getHandle($parent_task->getPHID())->renderLink().
'</strong>');
} else if ($workflow == 'create') {
$context_bar = new AphrontContextBarView();
$context_bar->addButton('<label>Create Another:</label>');
$context_bar->addButton(
phutil_render_tag(
'a',
array(
'href' => '/maniphest/task/create/?template='.$task->getID(),
'class' => 'green button',
),
'Similar Task'));
$context_bar->addButton(
phutil_render_tag(
'a',
array(
'href' => '/maniphest/task/create/',
'class' => 'green button',
),
'Empty Task'));
$context_bar->appendChild('New task created.');
}
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($user);
$engine->addObject($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION);
foreach ($transactions as $xaction) {
if ($xaction->hasComments()) {
$engine->addObject($xaction, ManiphestTransaction::MARKUP_FIELD_BODY);
}
}
$engine->process();
$extensions = ManiphestTaskExtensions::newExtensions();
$aux_fields = $extensions->getAuxiliaryFieldSpecifications();
if ($aux_fields) {
$task->loadAndAttachAuxiliaryAttributes();
}
$transaction_types = ManiphestTransactionType::getTransactionTypeMap();
$resolution_types = ManiphestTaskStatus::getTaskStatusMap();
if ($task->getStatus() == ManiphestTaskStatus::STATUS_OPEN) {
$resolution_types = array_select_keys(
$resolution_types,
array(
ManiphestTaskStatus::STATUS_CLOSED_RESOLVED,
ManiphestTaskStatus::STATUS_CLOSED_WONTFIX,
ManiphestTaskStatus::STATUS_CLOSED_INVALID,
ManiphestTaskStatus::STATUS_CLOSED_SPITE,
));
} else {
$resolution_types = array(
ManiphestTaskStatus::STATUS_OPEN => 'Reopened',
);
$transaction_types[ManiphestTransactionType::TYPE_STATUS] =
'Reopen Task';
unset($transaction_types[ManiphestTransactionType::TYPE_PRIORITY]);
unset($transaction_types[ManiphestTransactionType::TYPE_OWNER]);
}
$default_claim = array(
$user->getPHID() => $user->getUsername().' ('.$user->getRealName().')',
);
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
$task->getPHID());
if ($draft) {
$draft_text = $draft->getDraft();
} else {
$draft_text = null;
}
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
if ($is_serious) {
// Prevent tasks from being closed "out of spite" in serious business
// installs.
unset($resolution_types[ManiphestTaskStatus::STATUS_CLOSED_SPITE]);
}
$comment_form = new AphrontFormView();
$comment_form
->setUser($user)
->setAction('/maniphest/transaction/save/')
->setEncType('multipart/form-data')
->addHiddenInput('taskID', $task->getID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Action')
->setName('action')
->setOptions($transaction_types)
->setID('transaction-action'))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Resolution')
->setName('resolution')
->setControlID('resolution')
->setControlStyle('display: none')
->setOptions($resolution_types))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Assign To')
->setName('assign_to')
->setControlID('assign_to')
->setControlStyle('display: none')
->setID('assign-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('CCs')
->setName('ccs')
->setControlID('ccs')
->setControlStyle('display: none')
->setID('cc-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Priority')
->setName('priority')
->setOptions($priority_map)
->setControlID('priority')
->setControlStyle('display: none')
->setValue($task->getPriority()))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Projects')
->setName('projects')
->setControlID('projects')
->setControlStyle('display: none')
->setID('projects-tokenizer')
->setDisableBehavior(true))
->appendChild(
id(new AphrontFormFileControl())
->setLabel('File')
->setName('file')
->setControlID('file')
->setControlStyle('display: none'))
->appendChild(
id(new PhabricatorRemarkupControl())
->setLabel('Comments')
->setName('comments')
->setValue($draft_text)
->setID('transaction-comments')
->setUser($user))
->appendChild(
id(new AphrontFormDragAndDropUploadControl())
->setLabel('Attached Files')
->setName('files')
->setActivatedClass('aphront-panel-view-drag-and-drop'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($is_serious ? 'Submit' : 'Avast!'));
$control_map = array(
ManiphestTransactionType::TYPE_STATUS => 'resolution',
ManiphestTransactionType::TYPE_OWNER => 'assign_to',
ManiphestTransactionType::TYPE_CCS => 'ccs',
ManiphestTransactionType::TYPE_PRIORITY => 'priority',
ManiphestTransactionType::TYPE_PROJECTS => 'projects',
ManiphestTransactionType::TYPE_ATTACH => 'file',
);
$tokenizer_map = array(
ManiphestTransactionType::TYPE_PROJECTS => array(
'id' => 'projects-tokenizer',
'src' => '/typeahead/common/projects/',
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'placeholder' => 'Type a project name...',
),
ManiphestTransactionType::TYPE_OWNER => array(
'id' => 'assign-tokenizer',
'src' => '/typeahead/common/users/',
'value' => $default_claim,
'limit' => 1,
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'placeholder' => 'Type a user name...',
),
ManiphestTransactionType::TYPE_CCS => array(
'id' => 'cc-tokenizer',
'src' => '/typeahead/common/mailable/',
'ondemand' => PhabricatorEnv::getEnvConfig('tokenizer.ondemand'),
'placeholder' => 'Type a user or mailing list...',
),
);
Javelin::initBehavior('maniphest-transaction-controls', array(
'select' => 'transaction-action',
'controlMap' => $control_map,
'tokenizers' => $tokenizer_map,
));
Javelin::initBehavior('maniphest-transaction-preview', array(
'uri' => '/maniphest/transaction/preview/'.$task->getID().'/',
'preview' => 'transaction-preview',
'comments' => 'transaction-comments',
'action' => 'transaction-action',
'map' => $control_map,
'tokenizers' => $tokenizer_map,
));
$comment_header = id(new PhabricatorHeaderView())
->setHeader($is_serious ? pht('Add Comment') : pht('Weigh In'));
$preview_panel =
'<div class="aphront-panel-preview">
<div id="transaction-preview">
<div class="aphront-panel-preview-loading-text">
'.pht('Loading preview...').'
</div>
</div>
</div>';
$transaction_view = new ManiphestTransactionListView();
$transaction_view->setTransactions($transactions);
$transaction_view->setHandles($this->getLoadedHandles());
$transaction_view->setUser($user);
$transaction_view->setAuxiliaryFields($aux_fields);
$transaction_view->setMarkupEngine($engine);
PhabricatorFeedStoryNotification::updateObjectNotificationViews(
$user, $task->getPHID());
$object_name = 'T'.$task->getID();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($object_name)
->setHref('/'.$object_name));
$header = $this->buildHeaderView($task);
$actions = $this->buildActionView($task);
$properties = $this->buildPropertyView($task, $aux_fields, $edges, $engine);
return $this->buildApplicationPage(
array(
$crumbs,
$context_bar,
$header,
$actions,
$properties,
$transaction_view,
$comment_header,
$comment_form,
$preview_panel,
),
array(
'title' => 'T'.$task->getID().' '.$task->getTitle(),
'pageObjects' => array($task->getPHID()),
'device' => true,
));
}
private function buildHeaderView(ManiphestTask $task) {
$view = id(new PhabricatorHeaderView())
->setObjectName('T'.$task->getID())
->setHeader($task->getTitle());
$status = $task->getStatus();
$status_name = ManiphestTaskStatus::getTaskStatusFullName($status);
$status_color = ManiphestTaskStatus::getTaskStatusTagColor($status);
$view->addTag(
id(new PhabricatorTagView())
->setType(PhabricatorTagView::TYPE_STATE)
->setName($status_name)
->setBackgroundColor($status_color));
return $view;
}
private function buildActionView(ManiphestTask $task) {
$viewer = $this->getRequest()->getUser();
$id = $task->getID();
$phid = $task->getPHID();
$view = new PhabricatorActionListView();
$view->setUser($viewer);
$view->setObject($task);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Task'))
->setIcon('edit')
->setHref($this->getApplicationURI("/task/edit/{$id}/")));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Merge Duplicates'))
->setHref("/search/attach/{$phid}/TASK/merge/")
->setWorkflow(true)
->setWorkflow(true)
->setIcon('merge'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Subtask'))
->setHref($this->getApplicationURI("/task/create/?parent={$id}"))
->setIcon('fork'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Dependencies'))
->setHref("/search/attach/{$phid}/TASK/dependencies/")
->setWorkflow(true)
->setIcon('link'));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Differential Revisions'))
->setHref("/search/attach/{$phid}/DREV/")
->setWorkflow(true)
->setIcon('attach'));
return $view;
}
private function buildPropertyView(
ManiphestTask $task,
array $aux_fields,
array $edges,
PhabricatorMarkupEngine $engine) {
$viewer = $this->getRequest()->getUser();
$view = new PhabricatorPropertyListView();
$view->addProperty(
pht('Assigned To'),
$task->getOwnerPHID()
? $this->getHandle($task->getOwnerPHID())->renderLink()
: '<em>'.pht('None').'</em>');
$view->addProperty(
pht('Priority'),
phutil_escape_html(
ManiphestTaskPriority::getTaskPriorityName($task->getPriority())));
$view->addProperty(
pht('Subscribers'),
$task->getCCPHIDs()
? $this->renderHandlesForPHIDs($task->getCCPHIDs(), ',')
: '<em>'.pht('None').'</em>');
$view->addProperty(
pht('Author'),
$this->getHandle($task->getAuthorPHID())->renderLink());
$source = $task->getOriginalEmailSource();
if ($source) {
$subject = '[T'.$task->getID().'] '.$task->getTitle();
$view->addProperty(
pht('From Email'),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => 'mailto:'.$source.'?subject='.$subject
),
- phutil_escape_html($source)));
+ $source));
}
$view->addProperty(
pht('Projects'),
$task->getProjectPHIDs()
? $this->renderHandlesForPHIDs($task->getProjectPHIDs(), ',')
: '<em>'.pht('None').'</em>');
foreach ($aux_fields as $aux_field) {
$aux_key = $aux_field->getAuxiliaryKey();
$aux_field->setValue($task->getAuxiliaryAttribute($aux_key));
$value = $aux_field->renderForDetailView();
if (strlen($value)) {
$view->addProperty($aux_field->getLabel(), $value);
}
}
$edge_types = array(
PhabricatorEdgeConfig::TYPE_TASK_DEPENDED_ON_BY_TASK
=> pht('Dependent Tasks'),
PhabricatorEdgeConfig::TYPE_TASK_DEPENDS_ON_TASK
=> pht('Depends On'),
PhabricatorEdgeConfig::TYPE_TASK_HAS_RELATED_DREV
=> pht('Differential Revisions'),
PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT
=> pht('Commits'),
);
foreach ($edge_types as $edge_type => $edge_name) {
if ($edges[$edge_type]) {
$view->addProperty(
$edge_name,
$this->renderHandlesForPHIDs(array_keys($edges[$edge_type])));
}
}
$attached = $task->getAttached();
$file_infos = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_FILE);
if ($file_infos) {
$file_phids = array_keys($file_infos);
$files = id(new PhabricatorFile())->loadAllWhere(
'phid IN (%Ls)',
$file_phids);
$file_view = new PhabricatorFileLinkListView();
$file_view->setFiles($files);
$view->addProperty(
pht('Files'),
$file_view->render());
}
if (strlen($task->getDescription())) {
$view->addSectionHeader(pht('Description'));
$view->addTextContent(
phutil_render_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$engine->getOutput($task, ManiphestTask::MARKUP_FIELD_DESCRIPTION)));
}
return $view;
}
}
diff --git a/src/applications/maniphest/view/ManiphestTaskProjectsView.php b/src/applications/maniphest/view/ManiphestTaskProjectsView.php
index d83a706e0f..5e63043238 100644
--- a/src/applications/maniphest/view/ManiphestTaskProjectsView.php
+++ b/src/applications/maniphest/view/ManiphestTaskProjectsView.php
@@ -1,59 +1,58 @@
<?php
/**
* @group maniphest
*/
final class ManiphestTaskProjectsView extends ManiphestView {
private $handles;
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function render() {
require_celerity_resource('phabricator-project-tag-css');
$show = array_slice($this->handles, 0, 2);
$tags = array();
foreach ($show as $handle) {
- $tags[] = phutil_render_tag(
+ $tags[] = phutil_tag(
'a',
array(
'href' => $handle->getURI(),
'class' => 'phabricator-project-tag',
),
- phutil_escape_html(
- phutil_utf8_shorten($handle->getName(), 24)));
+ phutil_utf8_shorten($handle->getName(), 24));
}
if (count($this->handles) > 2) {
require_celerity_resource('aphront-tooltip-css');
Javelin::initBehavior('phabricator-tooltips');
$all = array();
foreach ($this->handles as $handle) {
$all[] = $handle->getName();
}
$tags[] = javelin_render_tag(
'span',
array(
'class' => 'phabricator-project-tag',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => implode(', ', $all),
'size' => 200,
),
),
"\xE2\x80\xA6");
}
return implode("\n", $tags);
}
}
diff --git a/src/applications/maniphest/view/ManiphestTaskSummaryView.php b/src/applications/maniphest/view/ManiphestTaskSummaryView.php
index a3adbab980..075bee78dd 100644
--- a/src/applications/maniphest/view/ManiphestTaskSummaryView.php
+++ b/src/applications/maniphest/view/ManiphestTaskSummaryView.php
@@ -1,138 +1,138 @@
<?php
/**
* @group maniphest
*/
final class ManiphestTaskSummaryView extends ManiphestView {
private $task;
private $handles;
private $showBatchControls;
private $showSubpriorityControls;
public function setTask(ManiphestTask $task) {
$this->task = $task;
return $this;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function setShowBatchControls($show_batch_controls) {
$this->showBatchControls = $show_batch_controls;
return $this;
}
public function setShowSubpriorityControls($show_subpriority_controls) {
$this->showSubpriorityControls = $show_subpriority_controls;
return $this;
}
public static function getPriorityClass($priority) {
$classes = array(
ManiphestTaskPriority::PRIORITY_UNBREAK_NOW => 'pri-unbreak',
ManiphestTaskPriority::PRIORITY_TRIAGE => 'pri-triage',
ManiphestTaskPriority::PRIORITY_HIGH => 'pri-high',
ManiphestTaskPriority::PRIORITY_NORMAL => 'pri-normal',
ManiphestTaskPriority::PRIORITY_LOW => 'pri-low',
ManiphestTaskPriority::PRIORITY_WISH => 'pri-wish',
);
return idx($classes, $priority);
}
public function render() {
if (!$this->user) {
throw new Exception("Call setUser() before rendering!");
}
$task = $this->task;
$handles = $this->handles;
require_celerity_resource('maniphest-task-summary-css');
$pri_class = self::getPriorityClass($task->getPriority());
$status_map = ManiphestTaskStatus::getTaskStatusMap();
$batch = null;
if ($this->showBatchControls) {
$batch =
'<td class="maniphest-task-batch">'.
javelin_render_tag(
'input',
array(
'type' => 'checkbox',
'name' => 'batch[]',
'value' => $task->getID(),
'sigil' => 'maniphest-batch',
),
null).
'</td>';
}
$projects_view = new ManiphestTaskProjectsView();
$projects_view->setHandles(
array_select_keys(
$this->handles,
$task->getProjectPHIDs()));
$control_class = null;
$control_sigil = null;
if ($this->showSubpriorityControls) {
$control_class = 'maniphest-active-handle';
$control_sigil = 'maniphest-task-handle';
}
$handle = javelin_render_tag(
'td',
array(
'class' => 'maniphest-task-handle '.$pri_class.' '.$control_class,
'sigil' => $control_sigil,
),
'');
return javelin_render_tag(
'table',
array(
'class' => 'maniphest-task-summary',
'sigil' => 'maniphest-task',
'meta' => array(
'taskID' => $task->getID(),
),
),
'<tr>'.
$handle.
$batch.
'<td class="maniphest-task-number">'.
'T'.$task->getID().
'</td>'.
'<td class="maniphest-task-status">'.
idx($status_map, $task->getStatus(), 'Unknown').
'</td>'.
'<td class="maniphest-task-owner">'.
($task->getOwnerPHID()
? $handles[$task->getOwnerPHID()]->renderLink()
: '<em>None</em>').
'</td>'.
'<td class="maniphest-task-name">'.
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/T'.$task->getID(),
),
- phutil_escape_html($task->getTitle())).
+ $task->getTitle()).
'</td>'.
'<td class="maniphest-task-projects">'.
$projects_view->render().
'</td>'.
'<td class="maniphest-task-updated">'.
phabricator_date($task->getDateModified(), $this->user).
'</td>'.
'</tr>');
}
}
diff --git a/src/applications/meta/view/PhabricatorApplicationLaunchView.php b/src/applications/meta/view/PhabricatorApplicationLaunchView.php
index c5c45ae1ef..d3658cdf29 100644
--- a/src/applications/meta/view/PhabricatorApplicationLaunchView.php
+++ b/src/applications/meta/view/PhabricatorApplicationLaunchView.php
@@ -1,135 +1,135 @@
<?php
final class PhabricatorApplicationLaunchView extends AphrontView {
private $application;
private $status;
private $fullWidth;
public function setFullWidth($full_width) {
$this->fullWidth = $full_width;
return $this;
}
public function setApplication(PhabricatorApplication $application) {
$this->application = $application;
return $this;
}
public function setApplicationStatus(array $status) {
$this->status = $status;
return $this;
}
public function render() {
$application = $this->application;
require_celerity_resource('phabricator-application-launch-view-css');
require_celerity_resource('sprite-apps-large-css');
$content = array();
$icon = null;
$create_button = null;
if ($application) {
- $content[] = phutil_render_tag(
+ $content[] = phutil_tag(
'span',
array(
'class' => 'phabricator-application-launch-name',
),
- phutil_escape_html($application->getName()));
+ $application->getName());
if ($application->isBeta()) {
$content[] = phutil_render_tag(
'span',
array(
'class' => 'phabricator-application-beta',
),
"\xCE\xB2");
}
if ($this->fullWidth) {
- $content[] = phutil_render_tag(
+ $content[] = phutil_tag(
'span',
array(
'class' => 'phabricator-application-launch-description',
),
- phutil_escape_html($application->getShortDescription()));
+ $application->getShortDescription());
}
$count = 0;
if ($this->status) {
foreach ($this->status as $status) {
$count += $status->getCount();
}
}
if ($count) {
- $content[] = phutil_render_tag(
+ $content[] = phutil_tag(
'span',
array(
'class' => 'phabricator-application-launch-attention',
),
- phutil_escape_html($count));
+ $count);
}
$classes = array();
$classes[] = 'phabricator-application-launch-icon';
$styles = array();
if ($application->getIconURI()) {
$styles[] = 'background-image: url('.$application->getIconURI().')';
} else {
$icon = $application->getIconName();
$classes[] = 'sprite-apps-large';
$classes[] = 'app-'.$icon.'-light-large';
}
$icon = phutil_render_tag(
'span',
array(
'class' => implode(' ', $classes),
'style' => nonempty(implode('; ', $styles), null),
),
'');
$classes = array();
if ($application->getQuickCreateURI()) {
$classes[] = 'phabricator-application-create-icon';
$classes[] = 'sprite-icon';
$classes[] = 'action-new-grey';
$plus_icon = phutil_render_tag(
'span',
array(
'class' => implode(' ', $classes),
));
$create_button = phutil_render_tag(
'a',
array(
'href' => $application->getQuickCreateURI(),
'class' => 'phabricator-application-launch-create',
),
$plus_icon);
$classes = array();
$classes[] = 'application-tile-create';
}
}
$classes[] = 'phabricator-application-launch-container';
if ($this->fullWidth) {
$classes[] = 'application-tile-full';
}
$app_button = phutil_render_tag(
$application ? 'a' : 'div',
array(
'class' => implode(' ', $classes),
'href' => $application ? $application->getBaseURI() : null,
'title' => $application ? $application->getShortDescription() : null,
),
$icon.
$this->renderSingleView($content));
return $app_button.$create_button;
}
}
diff --git a/src/applications/meta/view/PhabricatorApplicationStatusView.php b/src/applications/meta/view/PhabricatorApplicationStatusView.php
index 81718177eb..cc2339b9c3 100644
--- a/src/applications/meta/view/PhabricatorApplicationStatusView.php
+++ b/src/applications/meta/view/PhabricatorApplicationStatusView.php
@@ -1,48 +1,48 @@
<?php
final class PhabricatorApplicationStatusView extends AphrontView {
private $count;
private $text;
private $type;
const TYPE_NEEDS_ATTENTION = 'needs';
const TYPE_INFO = 'info';
const TYPE_OKAY = 'okay';
const TYPE_WARNING = 'warning';
const TYPE_EMPTY = 'empty';
public function setType($type) {
$this->type = $type;
return $this;
}
public function setText($text) {
$this->text = $text;
return $this;
}
public function setCount($count) {
$this->count = $count;
return $this;
}
public function getCount() {
return $this->count;
}
public function render() {
$classes = array(
'phabricator-application-status',
'phabricator-application-status-type-'.$this->type,
);
- return phutil_render_tag(
+ return phutil_tag(
'span',
array(
'class' => implode(' ', $classes),
),
- phutil_escape_html($this->text));
+ $this->text);
}
}
diff --git a/src/applications/metamta/PhabricatorMetaMTAWorker.php b/src/applications/metamta/PhabricatorMetaMTAWorker.php
index 4e7bff2331..f3075033b3 100644
--- a/src/applications/metamta/PhabricatorMetaMTAWorker.php
+++ b/src/applications/metamta/PhabricatorMetaMTAWorker.php
@@ -1,51 +1,51 @@
<?php
final class PhabricatorMetaMTAWorker
extends PhabricatorWorker {
private $message;
public function getWaitBeforeRetry(PhabricatorWorkerTask $task) {
$message = $this->loadMessage();
if (!$message) {
return null;
}
$wait = max($message->getNextRetry() - time(), 0) + 15;
return $wait;
}
public function doWork() {
$message = $this->loadMessage();
if (!$message
|| $message->getStatus() != PhabricatorMetaMTAMail::STATUS_QUEUE) {
return;
}
$id = $message->getID();
$message->sendNow();
// task failed if the message is still queued
// (instead of sent, void, or failed)
if ($message->getStatus() == PhabricatorMetaMTAMail::STATUS_QUEUE) {
throw new Exception('Failed to send message');
}
}
private function loadMessage() {
if (!$this->message) {
$message_id = $this->getTaskData();
$this->message = id(new PhabricatorMetaMTAMail())->load($message_id);
if (!$this->message) {
return null;
}
}
return $this->message;
}
public function renderForDisplay() {
- return phutil_render_tag(
+ return phutil_tag(
'a',
array('href' => '/mail/view/'.$this->getTaskData().'/'),
- phutil_escape_html($this->getTaskData()));
+ $this->getTaskData());
}
}
diff --git a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php
index b10d5c3c9a..072167576a 100644
--- a/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php
+++ b/src/applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php
@@ -1,137 +1,137 @@
<?php
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientListController
extends PhabricatorOAuthClientBaseController {
public function getFilter() {
return 'client';
}
public function processRequest() {
$title = 'OAuth Clients';
$request = $this->getRequest();
$current_user = $request->getUser();
$offset = $request->getInt('offset', 0);
$page_size = 100;
$pager = new AphrontPagerView();
$request_uri = $request->getRequestURI();
$pager->setURI($request_uri, 'offset');
$pager->setPageSize($page_size);
$pager->setOffset($offset);
$query = new PhabricatorOAuthServerClientQuery();
$query->withCreatorPHIDs(array($current_user->getPHID()));
$clients = $query->executeWithOffsetPager($pager);
$rows = array();
$rowc = array();
$highlight = $this->getHighlightPHIDs();
foreach ($clients as $client) {
$row = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $client->getViewURI(),
),
- phutil_escape_html($client->getName())
+ $client->getName()
),
$client->getPHID(),
$client->getSecret(),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $client->getRedirectURI(),
),
- phutil_escape_html($client->getRedirectURI())
+ $client->getRedirectURI()
),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => $client->getEditURI(),
),
'Edit'
),
);
$rows[] = $row;
if (isset($highlight[$client->getPHID()])) {
$rowc[] = 'highlighted';
} else {
$rowc[] = '';
}
}
$panel = $this->buildClientList($rows, $rowc, $title);
return $this->buildStandardPageResponse(
array(
$this->getNoticeView(),
$panel->appendChild($pager)
),
array('title' => $title)
);
}
private function buildClientList($rows, $rowc, $title) {
$table = new AphrontTableView($rows);
$table->setRowClasses($rowc);
$table->setHeaders(
array(
'Client',
'ID',
'Secret',
'Redirect URI',
'',
));
$table->setColumnClasses(
array(
'',
'',
'',
'',
'action',
));
if (empty($rows)) {
$table->setNoDataString(
'You have not created any clients for this OAuthServer.'
);
}
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader($title);
return $panel;
}
private function getNoticeView() {
$edited = $this->getRequest()->getStr('edited');
$new = $this->getRequest()->getStr('new');
$deleted = $this->getRequest()->getBool('deleted');
if ($edited) {
$edited = phutil_escape_html($edited);
$title = 'Successfully edited client with id '.$edited.'.';
} else if ($new) {
$new = phutil_escape_html($new);
$title = 'Successfully created client with id '.$new.'.';
} else if ($deleted) {
$title = 'Successfully deleted client.';
} else {
$title = null;
}
if ($title) {
$view = new AphrontErrorView();
$view->setTitle($title);
$view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
} else {
$view = null;
}
return $view;
}
}
diff --git a/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationEditController.php b/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationEditController.php
index 06d2969e67..ad834f75cf 100644
--- a/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationEditController.php
+++ b/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationEditController.php
@@ -1,98 +1,98 @@
<?php
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientAuthorizationEditController
extends PhabricatorOAuthClientAuthorizationBaseController {
public function processRequest() {
$phid = $this->getAuthorizationPHID();
$title = 'Edit OAuth Client Authorization';
$request = $this->getRequest();
$current_user = $request->getUser();
$authorization = id(new PhabricatorOAuthClientAuthorization())
->loadOneWhere('phid = %s',
$phid);
if (empty($authorization)) {
return new Aphront404Response();
}
if ($authorization->getUserPHID() != $current_user->getPHID()) {
$message = 'Access denied to client authorization with phid '.$phid.'. '.
'Only the user who authorized the client has permission to '.
'edit the authorization.';
return id(new Aphront403Response())
->setForbiddenText($message);
}
if ($request->isFormPost()) {
$scopes = PhabricatorOAuthServerScope::getScopesFromRequest($request);
$authorization->setScope($scopes);
$authorization->save();
return id(new AphrontRedirectResponse())
->setURI('/oauthserver/clientauthorization/?edited='.$phid);
}
$client_phid = $authorization->getClientPHID();
$client = id(new PhabricatorOAuthServerClient())
->loadOneWhere('phid = %s',
$client_phid);
$created = phabricator_datetime($authorization->getDateCreated(),
$current_user);
$updated = phabricator_datetime($authorization->getDateModified(),
$current_user);
$panel = new AphrontPanelView();
$delete_button = phutil_render_tag(
'a',
array(
'href' => $authorization->getDeleteURI(),
'class' => 'grey button',
),
'Delete OAuth Client Authorization');
$panel->addButton($delete_button);
$panel->setHeader($title);
$form = id(new AphrontFormView())
->setUser($current_user)
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Client')
->setValue(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $client->getViewURI(),
),
- phutil_escape_html($client->getName())))
+ $client->getName()))
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Created')
->setValue($created)
)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Last Updated')
->setValue($updated)
)
->appendChild(
PhabricatorOAuthServerScope::getCheckboxControl(
$authorization->getScope()
)
)
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save OAuth Client Authorization')
->addCancelButton('/oauthserver/clientauthorization/')
);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array('title' => $title)
);
}
}
diff --git a/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationListController.php b/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationListController.php
index 9468457353..c74b040296 100644
--- a/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationListController.php
+++ b/src/applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationListController.php
@@ -1,158 +1,158 @@
<?php
/**
* @group oauthserver
*/
final class PhabricatorOAuthClientAuthorizationListController
extends PhabricatorOAuthClientAuthorizationBaseController {
protected function getFilter() {
return 'clientauthorization';
}
public function processRequest() {
$title = 'OAuth Client Authorizations';
$request = $this->getRequest();
$current_user = $request->getUser();
$offset = $request->getInt('offset', 0);
$page_size = 100;
$pager = new AphrontPagerView();
$request_uri = $request->getRequestURI();
$pager->setURI($request_uri, 'offset');
$pager->setPageSize($page_size);
$pager->setOffset($offset);
$query = new PhabricatorOAuthClientAuthorizationQuery();
$query->withUserPHIDs(array($current_user->getPHID()));
$authorizations = $query->executeWithOffsetPager($pager);
$client_authorizations = mpull($authorizations, null, 'getClientPHID');
$client_phids = array_keys($client_authorizations);
if ($client_phids) {
$clients = id(new PhabricatorOAuthServerClient())
->loadAllWhere('phid in (%Ls)',
$client_phids);
} else {
$clients = array();
}
$client_dict = mpull($clients, null, 'getPHID');
$rows = array();
$rowc = array();
$highlight = $this->getHighlightPHIDs();
foreach ($client_authorizations as $client_phid => $authorization) {
$client = $client_dict[$client_phid];
$created = phabricator_datetime($authorization->getDateCreated(),
$current_user);
$updated = phabricator_datetime($authorization->getDateModified(),
$current_user);
$scope_doc_href = PhabricatorEnv::getDoclink(
'article/Using_the_Phabricator_OAuth_Server.html#scopes'
);
$row = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $client->getViewURI(),
),
- phutil_escape_html($client->getName())
+ $client->getName()
),
phutil_render_tag(
'a',
array(
'href' => $scope_doc_href,
),
$authorization->getScopeString()
),
phabricator_datetime(
$authorization->getDateCreated(),
$current_user
),
phabricator_datetime(
$authorization->getDateModified(),
$current_user
),
phutil_render_tag(
'a',
array(
'class' => 'small button grey',
'href' => $authorization->getEditURI(),
),
'Edit'
),
);
$rows[] = $row;
if (isset($highlight[$authorization->getPHID()])) {
$rowc[] = 'highlighted';
} else {
$rowc[] = '';
}
}
$panel = $this->buildClientAuthorizationList($rows, $rowc, $title);
return $this->buildStandardPageResponse(
array(
$this->getNoticeView(),
$panel->appendChild($pager),
),
array('title' => $title)
);
}
private function buildClientAuthorizationList($rows, $rowc, $title) {
$table = new AphrontTableView($rows);
$table->setRowClasses($rowc);
$table->setHeaders(
array(
'Client',
'Scope',
'Created',
'Updated',
'',
));
$table->setColumnClasses(
array(
'wide pri',
'',
'',
'',
'action',
));
if (empty($rows)) {
$table->setNoDataString(
'You have not authorized any clients for this OAuthServer.'
);
}
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader($title);
return $panel;
}
private function getNoticeView() {
$edited = $this->getRequest()->getStr('edited');
$deleted = $this->getRequest()->getBool('deleted');
if ($edited) {
$edited = phutil_escape_html($edited);
$title = 'Successfully edited client authorization.';
} else if ($deleted) {
$title = 'Successfully deleted client authorization.';
} else {
$title = null;
}
if ($title) {
$view = new AphrontErrorView();
$view->setTitle($title);
$view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
} else {
$view = null;
}
return $view;
}
}
diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php
index f8137b977d..485a3014ab 100644
--- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php
@@ -1,236 +1,236 @@
<?php
final class PhabricatorOwnersDetailController
extends PhabricatorOwnersController {
private $id;
private $package;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$package = id(new PhabricatorOwnersPackage())->load($this->id);
if (!$package) {
return new Aphront404Response();
}
$this->package = $package;
$paths = $package->loadPaths();
$owners = $package->loadOwners();
$repository_phids = array();
foreach ($paths as $path) {
$repository_phids[$path->getRepositoryPHID()] = true;
}
if ($repository_phids) {
$repositories = id(new PhabricatorRepository())->loadAllWhere(
'phid in (%Ls)',
array_keys($repository_phids));
$repositories = mpull($repositories, null, 'getPHID');
} else {
$repositories = array();
}
$phids = array();
foreach ($owners as $owner) {
$phids[$owner->getUserPHID()] = true;
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$rows = array();
$rows[] = array(
'Name',
phutil_escape_html($package->getName()));
$rows[] = array(
'Description',
phutil_escape_html($package->getDescription()));
$primary_owner = null;
$primary_phid = $package->getPrimaryOwnerPHID();
if ($primary_phid && isset($handles[$primary_phid])) {
$primary_owner =
'<strong>'.$handles[$primary_phid]->renderLink().'</strong>';
}
$rows[] = array(
'Primary Owner',
$primary_owner,
);
$owner_links = array();
foreach ($owners as $owner) {
$owner_links[] = $handles[$owner->getUserPHID()]->renderLink();
}
$owner_links = implode('<br />', $owner_links);
$rows[] = array(
'Owners',
$owner_links);
$rows[] = array(
'Auditing',
$package->getAuditingEnabled() ? 'Enabled' : 'Disabled',
);
$path_links = array();
foreach ($paths as $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if (!$repo) {
continue;
}
$href = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPath(),
'action' => 'browse'
));
$repo_name = '<strong>'.phutil_escape_html($repo->getName()).
'</strong>';
- $path_link = phutil_render_tag(
+ $path_link = phutil_tag(
'a',
array(
'href' => (string) $href,
),
- phutil_escape_html($path->getPath()));
+ $path->getPath());
$path_links[] =
($path->getExcluded() ? '&ndash;' : '+').' '.
$repo_name.' '.$path_link;
}
$path_links = implode('<br />', $path_links);
$rows[] = array(
'Paths',
$path_links);
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'header',
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader(
'Package Details for "'.phutil_escape_html($package->getName()).'"');
$panel->addButton(
javelin_render_tag(
'a',
array(
'href' => '/owners/delete/'.$package->getID().'/',
'class' => 'button grey',
'sigil' => 'workflow',
),
'Delete Package'));
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/owners/edit/'.$package->getID().'/',
'class' => 'button',
),
'Edit Package'));
$panel->appendChild($table);
$key = 'package/'.$package->getID();
$this->setSideNavFilter($key);
$commit_views = array();
$commit_uri = id(new PhutilURI('/audit/view/packagecommits/'))
->setQueryParams(
array(
'phid' => $package->getPHID(),
));
$attention_query = id(new PhabricatorAuditCommitQuery())
->withPackagePHIDs(array($package->getPHID()))
->withStatus(PhabricatorAuditCommitQuery::STATUS_OPEN)
->needCommitData(true)
->needAudits(true)
->setLimit(10);
$attention_commits = $attention_query->execute();
if ($attention_commits) {
$view = new PhabricatorAuditCommitListView();
$view->setUser($user);
$view->setCommits($attention_commits);
$commit_views[] = array(
'view' => $view,
'header' => 'Commits in this Package that Need Attention',
'button' => phutil_render_tag(
'a',
array(
'href' => $commit_uri->alter('status', 'open'),
'class' => 'button grey',
),
'View All Problem Commits'),
);
}
$all_query = id(new PhabricatorAuditCommitQuery())
->withPackagePHIDs(array($package->getPHID()))
->needCommitData(true)
->needAudits(true)
->setLimit(100);
$all_commits = $all_query->execute();
$view = new PhabricatorAuditCommitListView();
$view->setUser($user);
$view->setCommits($all_commits);
$view->setNoDataString('No commits in this package.');
$commit_views[] = array(
'view' => $view,
'header' => 'Recent Commits in Package',
'button' => phutil_render_tag(
'a',
array(
'href' => $commit_uri,
'class' => 'button grey',
),
'View All Package Commits'),
);
$phids = array();
foreach ($commit_views as $commit_view) {
$phids[] = $commit_view['view']->getRequiredHandlePHIDs();
}
$phids = array_mergev($phids);
$handles = $this->loadViewerHandles($phids);
$commit_panels = array();
foreach ($commit_views as $commit_view) {
$commit_panel = new AphrontPanelView();
$commit_panel->setHeader(phutil_escape_html($commit_view['header']));
if (isset($commit_view['button'])) {
$commit_panel->addButton($commit_view['button']);
}
$commit_view['view']->setHandles($handles);
$commit_panel->appendChild($commit_view['view']);
$commit_panels[] = $commit_panel;
}
return $this->buildStandardPageResponse(
array(
$panel,
$commit_panels,
),
array(
'title' => "Package '".$package->getName()."'",
));
}
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
$package = $this->package;
$view->addFilter('package/'.$package->getID(), 'Details');
}
}
diff --git a/src/applications/owners/controller/PhabricatorOwnersListController.php b/src/applications/owners/controller/PhabricatorOwnersListController.php
index b734b38d56..c23ed1eb5f 100644
--- a/src/applications/owners/controller/PhabricatorOwnersListController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersListController.php
@@ -1,316 +1,316 @@
<?php
final class PhabricatorOwnersListController
extends PhabricatorOwnersController {
protected $view;
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view', 'owned');
$this->setSideNavFilter('view/'.$this->view);
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$package = new PhabricatorOwnersPackage();
$owner = new PhabricatorOwnersOwner();
$path = new PhabricatorOwnersPath();
$repository_phid = '';
if ($request->getStr('repository') != '') {
$repository_phid = id(new PhabricatorRepository())
->loadOneWhere('callsign = %s', $request->getStr('repository'))
->getPHID();
}
switch ($this->view) {
case 'search':
$packages = array();
$conn_r = $package->establishConnection('r');
$where = array('1 = 1');
$join = array();
$having = '';
if ($request->getStr('name')) {
$where[] = qsprintf(
$conn_r,
'p.name LIKE %~',
$request->getStr('name'));
}
if ($repository_phid || $request->getStr('path')) {
$join[] = qsprintf(
$conn_r,
'JOIN %T path ON path.packageID = p.id',
$path->getTableName());
if ($repository_phid) {
$where[] = qsprintf(
$conn_r,
'path.repositoryPHID = %s',
$repository_phid);
}
if ($request->getStr('path')) {
$where[] = qsprintf(
$conn_r,
'(path.path LIKE %~ AND NOT path.excluded) OR
%s LIKE CONCAT(REPLACE(path.path, %s, %s), %s)',
$request->getStr('path'),
$request->getStr('path'),
'_',
'\_',
'%');
$having = 'HAVING MAX(path.excluded) = 0';
}
}
if ($request->getArr('owner')) {
$join[] = qsprintf(
$conn_r,
'JOIN %T o ON o.packageID = p.id',
$owner->getTableName());
$where[] = qsprintf(
$conn_r,
'o.userPHID IN (%Ls)',
$request->getArr('owner'));
}
$data = queryfx_all(
$conn_r,
'SELECT p.* FROM %T p %Q WHERE %Q GROUP BY p.id %Q',
$package->getTableName(),
implode(' ', $join),
'('.implode(') AND (', $where).')',
$having);
$packages = $package->loadAllFromArray($data);
$header = 'Search Results';
$nodata = 'No packages match your query.';
break;
case 'owned':
$data = queryfx_all(
$package->establishConnection('r'),
'SELECT p.* FROM %T p JOIN %T o ON p.id = o.packageID
WHERE o.userPHID = %s GROUP BY p.id',
$package->getTableName(),
$owner->getTableName(),
$user->getPHID());
$packages = $package->loadAllFromArray($data);
$header = 'Owned Packages';
$nodata = 'No owned packages';
break;
case 'all':
$packages = $package->loadAll();
$header = 'All Packages';
$nodata = 'There are no defined packages.';
break;
}
$content = $this->renderPackageTable(
$packages,
$header,
$nodata);
$filter = new AphrontListFilterView();
$owners_search_value = array();
if ($request->getArr('owner')) {
$phids = $request->getArr('owner');
$phid = reset($phids);
$handles = $this->loadViewerHandles(array($phid));
$owners_search_value = array(
$phid => $handles[$phid]->getFullName(),
);
}
$callsigns = array('' => '(Any Repository)');
$repositories = id(new PhabricatorRepository())
->loadAllWhere('1 = 1 ORDER BY callsign');
foreach ($repositories as $repository) {
$callsigns[$repository->getCallsign()] =
$repository->getCallsign().': '.$repository->getName();
}
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/owners/view/search/')
->setMethod('GET')
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel('Name')
->setValue($request->getStr('name')))
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/usersorprojects/')
->setLimit(1)
->setName('owner')
->setLabel('Owner')
->setValue($owners_search_value))
->appendChild(
id(new AphrontFormSelectControl())
->setName('repository')
->setLabel('Repository')
->setOptions($callsigns)
->setValue($request->getStr('repository')))
->appendChild(
id(new AphrontFormTextControl())
->setName('path')
->setLabel('Path')
->setValue($request->getStr('path')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Search for Packages'));
$filter->appendChild($form);
return $this->buildStandardPageResponse(
array(
$filter,
$content,
),
array(
'title' => 'Package Index',
));
}
private function renderPackageTable(array $packages, $header, $nodata) {
assert_instances_of($packages, 'PhabricatorOwnersPackage');
if ($packages) {
$package_ids = mpull($packages, 'getID');
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID IN (%Ld)',
$package_ids);
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID in (%Ld)',
$package_ids);
$phids = array();
foreach ($owners as $owner) {
$phids[$owner->getUserPHID()] = true;
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$repository_phids = array();
foreach ($paths as $path) {
$repository_phids[$path->getRepositoryPHID()] = true;
}
if ($repository_phids) {
$repositories = id(new PhabricatorRepository())->loadAllWhere(
'phid in (%Ls)',
array_keys($repository_phids));
} else {
$repositories = array();
}
$repositories = mpull($repositories, null, 'getPHID');
$owners = mgroup($owners, 'getPackageID');
$paths = mgroup($paths, 'getPackageID');
} else {
$handles = array();
$repositories = array();
$owners = array();
$paths = array();
}
$rows = array();
foreach ($packages as $package) {
$pkg_owners = idx($owners, $package->getID(), array());
foreach ($pkg_owners as $key => $owner) {
$pkg_owners[$key] = $handles[$owner->getUserPHID()]->renderLink();
if ($owner->getUserPHID() == $package->getPrimaryOwnerPHID()) {
$pkg_owners[$key] = '<strong>'.$pkg_owners[$key].'</strong>';
}
}
$pkg_owners = implode('<br />', $pkg_owners);
$pkg_paths = idx($paths, $package->getID(), array());
foreach ($pkg_paths as $key => $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if ($repo) {
$href = DiffusionRequest::generateDiffusionURI(
array(
'callsign' => $repo->getCallsign(),
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPath(),
'action' => 'browse',
));
$pkg_paths[$key] =
($path->getExcluded() ? '&ndash;' : '+').' '.
'<strong>'.phutil_escape_html($repo->getName()).'</strong> '.
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => (string) $href,
),
- phutil_escape_html($path->getPath()));
+ $path->getPath());
} else {
$pkg_paths[$key] = phutil_escape_html($path->getPath());
}
}
$pkg_paths = implode('<br />', $pkg_paths);
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/owners/package/'.$package->getID().'/',
),
- phutil_escape_html($package->getName())),
+ $package->getName()),
$pkg_owners,
$pkg_paths,
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/audit/view/packagecommits/?phid='.$package->getPHID(),
),
- phutil_escape_html('Related Commits'))
+ 'Related Commits')
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Name',
'Owners',
'Paths',
'Related Commits',
));
$table->setColumnClasses(
array(
'pri',
'',
'wide wrap',
'narrow',
));
$panel = new AphrontPanelView();
$panel->setHeader($header);
$panel->appendChild($table);
$panel->setNoBackground();
return $panel;
}
protected function getExtraPackageViews(AphrontSideNavFilterView $view) {
if ($this->view == 'search') {
$view->addFilter('view/search', 'Search Results');
}
}
}
diff --git a/src/applications/people/controller/PhabricatorPeopleListController.php b/src/applications/people/controller/PhabricatorPeopleListController.php
index 8ec8bf07d3..ce6f60e45a 100644
--- a/src/applications/people/controller/PhabricatorPeopleListController.php
+++ b/src/applications/people/controller/PhabricatorPeopleListController.php
@@ -1,139 +1,139 @@
<?php
final class PhabricatorPeopleListController
extends PhabricatorPeopleController {
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$is_admin = $viewer->getIsAdmin();
$user = new PhabricatorUser();
$count = queryfx_one(
$user->establishConnection('r'),
'SELECT COUNT(*) N FROM %T',
$user->getTableName());
$count = idx($count, 'N', 0);
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page', 0));
$pager->setCount($count);
$pager->setURI($request->getRequestURI(), 'page');
$users = id(new PhabricatorPeopleQuery())
->needPrimaryEmail(true)
->executeWithOffsetPager($pager);
$rows = array();
foreach ($users as $user) {
$primary_email = $user->loadPrimaryEmail();
if ($primary_email && $primary_email->getIsVerified()) {
$email = 'Verified';
} else {
$email = 'Unverified';
}
$status = array();
if ($user->getIsDisabled()) {
$status[] = 'Disabled';
}
if ($user->getIsAdmin()) {
$status[] = 'Admin';
}
if ($user->getIsSystemAgent()) {
$status[] = 'System Agent';
}
$status = implode(', ', $status);
$rows[] = array(
phabricator_date($user->getDateCreated(), $viewer),
phabricator_time($user->getDateCreated(), $viewer),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/p/'.$user->getUsername().'/',
),
- phutil_escape_html($user->getUserName())),
+ $user->getUserName()),
phutil_escape_html($user->getRealName()),
$status,
$email,
phutil_render_tag(
'a',
array(
'class' => 'button grey small',
'href' => '/people/edit/'.$user->getID().'/',
),
'Administrate User'),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Join Date',
'Time',
'Username',
'Real Name',
'Roles',
'Email',
'',
));
$table->setColumnClasses(
array(
null,
'right',
'pri',
'wide',
null,
null,
'action',
));
$table->setColumnVisibility(
array(
true,
true,
true,
true,
$is_admin,
$is_admin,
$is_admin,
));
$panel = new AphrontPanelView();
$panel->setHeader('People ('.number_format($count).')');
$panel->appendChild($table);
$panel->appendChild($pager);
if ($is_admin) {
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/people/edit/',
'class' => 'button green',
),
'Create New Account'));
if (PhabricatorEnv::getEnvConfig('ldap.auth-enabled')) {
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/people/ldap/',
'class' => 'button green'
),
'Import from LDAP'));
}
}
$nav = $this->buildSideNavView();
$nav->selectFilter('people');
$nav->appendChild($panel);
return $this->buildApplicationPage(
$nav,
array(
'title' => 'People',
));
}
}
diff --git a/src/applications/people/controller/PhabricatorPeopleLogsController.php b/src/applications/people/controller/PhabricatorPeopleLogsController.php
index 1621bd58fe..9b10f34396 100644
--- a/src/applications/people/controller/PhabricatorPeopleLogsController.php
+++ b/src/applications/people/controller/PhabricatorPeopleLogsController.php
@@ -1,234 +1,234 @@
<?php
final class PhabricatorPeopleLogsController
extends PhabricatorPeopleController {
public function shouldRequireAdmin() {
return true;
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$filter_activity = $request->getStr('activity');
$filter_ip = $request->getStr('ip');
$filter_session = $request->getStr('session');
$filter_user = $request->getArr('user', array());
$filter_actor = $request->getArr('actor', array());
$user_value = array();
$actor_value = array();
$phids = array_merge($filter_user, $filter_actor);
if ($phids) {
$handles = $this->loadViewerHandles($phids);
if ($filter_user) {
$filter_user = reset($filter_user);
$user_value = array(
$filter_user => $handles[$filter_user]->getFullName(),
);
}
if ($filter_actor) {
$filter_actor = reset($filter_actor);
$actor_value = array(
$filter_actor => $handles[$filter_actor]->getFullName(),
);
}
}
$form = new AphrontFormView();
$form
->setUser($user)
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Filter Actor')
->setName('actor')
->setLimit(1)
->setValue($actor_value)
->setDatasource('/typeahead/common/accounts/'))
->appendChild(
id(new AphrontFormTokenizerControl())
->setLabel('Filter User')
->setName('user')
->setLimit(1)
->setValue($user_value)
->setDatasource('/typeahead/common/accounts/'))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Show Activity')
->setName('activity')
->setValue($filter_activity)
->setOptions(
array(
'' => 'All Activity',
'admin' => 'Admin Activity',
)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Filter IP')
->setName('ip')
->setValue($filter_ip)
->setCaption(
'Enter an IP (or IP prefix) to show only activity by that remote '.
'address.'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Filter Session')
->setName('session')
->setValue($filter_session))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Filter Logs'));
$log_table = new PhabricatorUserLog();
$conn_r = $log_table->establishConnection('r');
$where_clause = array();
$where_clause[] = '1 = 1';
if ($filter_user) {
$where_clause[] = qsprintf(
$conn_r,
'userPHID = %s',
$filter_user);
}
if ($filter_actor) {
$where_clause[] = qsprintf(
$conn_r,
'actorPHID = %s',
$filter_actor);
}
if ($filter_activity == 'admin') {
$where_clause[] = qsprintf(
$conn_r,
'action NOT IN (%Ls)',
array(
PhabricatorUserLog::ACTION_LOGIN,
PhabricatorUserLog::ACTION_LOGOUT,
PhabricatorUserLog::ACTION_LOGIN_FAILURE,
));
}
if ($filter_ip) {
$where_clause[] = qsprintf(
$conn_r,
'remoteAddr LIKE %>',
$filter_ip);
}
if ($filter_session) {
$where_clause[] = qsprintf(
$conn_r,
'session = %s',
$filter_session);
}
$where_clause = '('.implode(') AND (', $where_clause).')';
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'page');
$pager->setOffset($request->getInt('page'));
$pager->setPageSize(500);
$logs = $log_table->loadAllWhere(
'(%Q) ORDER BY dateCreated DESC LIMIT %d, %d',
$where_clause,
$pager->getOffset(),
$pager->getPageSize() + 1);
$logs = $pager->sliceResults($logs);
$phids = array();
foreach ($logs as $log) {
$phids[$log->getActorPHID()] = true;
$phids[$log->getUserPHID()] = true;
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($logs as $log) {
$rows[] = array(
phabricator_date($log->getDateCreated(),$user),
phabricator_time($log->getDateCreated(),$user),
$log->getAction(),
$log->getActorPHID()
? phutil_escape_html($handles[$log->getActorPHID()]->getName())
: null,
phutil_escape_html($handles[$log->getUserPHID()]->getName()),
json_encode($log->getOldValue(), true),
json_encode($log->getNewValue(), true),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $request
->getRequestURI()
->alter('ip', $log->getRemoteAddr()),
),
- phutil_escape_html($log->getRemoteAddr())),
- phutil_render_tag(
+ $log->getRemoteAddr()),
+ phutil_tag(
'a',
array(
'href' => $request
->getRequestURI()
->alter('session', $log->getSession()),
),
- phutil_escape_html($log->getSession())),
+ $log->getSession()),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Date',
'Time',
'Action',
'Actor',
'User',
'Old',
'New',
'IP',
'Session',
));
$table->setColumnClasses(
array(
'',
'right',
'',
'',
'',
'wrap',
'wrap',
'',
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader('Activity Logs');
$panel->appendChild($table);
$panel->appendChild($pager);
$filter = new AphrontListFilterView();
$filter->appendChild($form);
$nav = $this->buildSideNavView();
$nav->selectFilter('logs');
$nav->appendChild(
array(
$filter,
$panel,
));
return $this->buildApplicationPage(
$nav,
array(
'title' => 'Activity Logs',
));
}
}
diff --git a/src/applications/phame/view/PhamePostView.php b/src/applications/phame/view/PhamePostView.php
index 6e63fe7f17..60ab1dbc5d 100644
--- a/src/applications/phame/view/PhamePostView.php
+++ b/src/applications/phame/view/PhamePostView.php
@@ -1,239 +1,238 @@
<?php
/**
* @group phame
*/
final class PhamePostView extends AphrontView {
private $post;
private $author;
private $body;
private $skin;
private $summary;
public function setSkin(PhameBlogSkin $skin) {
$this->skin = $skin;
return $this;
}
public function getSkin() {
return $this->skin;
}
public function setAuthor(PhabricatorObjectHandle $author) {
$this->author = $author;
return $this;
}
public function getAuthor() {
return $this->author;
}
public function setPost(PhamePost $post) {
$this->post = $post;
return $this;
}
public function getPost() {
return $this->post;
}
public function setBody($body) {
$this->body = $body;
return $this;
}
public function getBody() {
return $this->body;
}
public function setSummary($summary) {
$this->summary = $summary;
return $this;
}
public function getSummary() {
return $this->summary;
}
public function renderTitle() {
$href = $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle());
return phutil_render_tag(
'h2',
array(
'class' => 'phame-post-title',
),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $href,
),
- phutil_escape_html($this->getPost()->getTitle())));
+ $this->getPost()->getTitle()));
}
public function renderDatePublished() {
- return phutil_render_tag(
+ return phutil_tag(
'div',
array(
'class' => 'phame-post-date',
),
- phutil_escape_html(
pht(
'Published on %s by %s',
phabricator_datetime(
$this->getPost()->getDatePublished(),
$this->getUser()),
- $this->getAuthor()->getName())));
+ $this->getAuthor()->getName()));
}
public function renderBody() {
return phutil_render_tag(
'div',
array(
'class' => 'phame-post-body',
),
$this->getBody());
}
public function renderSummary() {
return phutil_render_tag(
'div',
array(
'class' => 'phame-post-body',
),
$this->getSummary());
}
public function renderComments() {
$post = $this->getPost();
switch ($post->getCommentsWidget()) {
case 'facebook':
$comments = $this->renderFacebookComments();
break;
case 'disqus':
$comments = $this->renderDisqusComments();
break;
case 'none':
default:
$comments = null;
break;
}
return $comments;
}
public function render() {
return phutil_render_tag(
'div',
array(
'class' => 'phame-post',
),
$this->renderTitle().
$this->renderDatePublished().
$this->renderBody().
$this->renderComments());
}
public function renderWithSummary() {
return phutil_render_tag(
'div',
array(
'class' => 'phame-post',
),
$this->renderTitle().
$this->renderDatePublished().
$this->renderSummary());
}
private function renderFacebookComments() {
$fb_id = PhabricatorEnv::getEnvConfig('facebook.application-id');
if (!$fb_id) {
return null;
}
$fb_root = phutil_render_tag('div',
array(
'id' => 'fb-root',
),
''
);
$c_uri = '//connect.facebook.net/en_US/all.js#xfbml=1&appId='.$fb_id;
$fb_js = jsprintf(
'<script>(function(d, s, id) {'.
' var js, fjs = d.getElementsByTagName(s)[0];'.
' if (d.getElementById(id)) return;'.
' js = d.createElement(s); js.id = id;'.
' js.src = %s;'.
' fjs.parentNode.insertBefore(js, fjs);'.
'}(document, \'script\', \'facebook-jssdk\'));</script>',
$c_uri
);
$uri = $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle());
$fb_comments = phutil_render_tag('div',
array(
'class' => 'fb-comments',
'data-href' => $uri,
'data-num-posts' => 5,
),
''
);
return phutil_render_tag(
'div',
array(
'class' => 'phame-comments-facebook',
),
$fb_root.
$fb_js.
$fb_comments);
}
private function renderDisqusComments() {
$disqus_shortname = PhabricatorEnv::getEnvConfig('disqus.shortname');
if (!$disqus_shortname) {
return null;
}
$post = $this->getPost();
$disqus_thread = phutil_tag('div',
array(
'id' => 'disqus_thread'
)
);
// protip - try some var disqus_developer = 1; action to test locally
$disqus_js = jsprintf(
'<script>'.
' var disqus_shortname = "phabricator";'.
' var disqus_identifier = %s;'.
' var disqus_url = %s;'.
' var disqus_title = %s;'.
'(function() {'.
' var dsq = document.createElement("script");'.
' dsq.type = "text/javascript";'.
' dsq.async = true;'.
' dsq.src = "http://" + disqus_shortname + ".disqus.com/embed.js";'.
'(document.getElementsByTagName("head")[0] ||'.
' document.getElementsByTagName("body")[0]).appendChild(dsq);'.
'})(); </script>',
$post->getPHID(),
$this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()),
$post->getTitle()
);
return phutil_render_tag(
'div',
array(
'class' => 'phame-comments-disqus',
),
$disqus_thread.
$disqus_js);
}
}
diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php
index 7fd232c385..c16059d505 100644
--- a/src/applications/phid/PhabricatorObjectHandle.php
+++ b/src/applications/phid/PhabricatorObjectHandle.php
@@ -1,216 +1,216 @@
<?php
final class PhabricatorObjectHandle {
private $uri;
private $phid;
private $type;
private $name;
private $email;
private $fullName;
private $imageURI;
private $timestamp;
private $alternateID;
private $status = PhabricatorObjectHandleStatus::STATUS_OPEN;
private $title;
private $complete;
private $disabled;
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
return $this->uri;
}
public function setPHID($phid) {
$this->phid = $phid;
return $this;
}
public function getPHID() {
return $this->phid;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setStatus($status) {
$this->status = $status;
return $this;
}
public function getStatus() {
return $this->status;
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setFullName($full_name) {
$this->fullName = $full_name;
return $this;
}
public function getFullName() {
if ($this->fullName !== null) {
return $this->fullName;
}
return $this->getName();
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setImageURI($uri) {
$this->imageURI = $uri;
return $this;
}
public function getImageURI() {
return $this->imageURI;
}
public function setTimestamp($timestamp) {
$this->timestamp = $timestamp;
return $this;
}
public function getTimestamp() {
return $this->timestamp;
}
public function setAlternateID($alternate_id) {
$this->alternateID = $alternate_id;
return $this;
}
public function getAlternateID() {
return $this->alternateID;
}
public function getTypeName() {
static $map = array(
PhabricatorPHIDConstants::PHID_TYPE_USER => 'User',
PhabricatorPHIDConstants::PHID_TYPE_TASK => 'Task',
PhabricatorPHIDConstants::PHID_TYPE_DREV => 'Revision',
PhabricatorPHIDConstants::PHID_TYPE_CMIT => 'Commit',
PhabricatorPHIDConstants::PHID_TYPE_WIKI => 'Phriction',
);
return idx($map, $this->getType());
}
/**
* Set whether or not the underlying object is complete. See
* @{method:isComplete} for an explanation of what it means to be complete.
*
* @param bool True if the handle represents a complete object.
* @return this
*/
public function setComplete($complete) {
$this->complete = $complete;
return $this;
}
/**
* Determine if the handle represents an object which was completely loaded
* (i.e., the underlying object exists) vs an object which could not be
* completely loaded (e.g., the type or data for the PHID could not be
* identified or located).
*
* Basically, @{class:PhabricatorObjectHandleData} gives you back a handle for
* any PHID you give it, but it gives you a complete handle only for valid
* PHIDs.
*
* @return bool True if the handle represents a complete object.
*/
public function isComplete() {
return $this->complete;
}
/**
* Set whether or not the underlying object is disabled. See
* @{method:isDisabled} for an explanation of what it means to be disabled.
*
* @param bool True if the handle represents a disabled object.
* @return this
*/
public function setDisabled($disabled) {
$this->disabled = $disabled;
return $this;
}
/**
* Determine if the handle represents an object which has been disabled --
* for example, disabled users, archived projects, etc. These objects are
* complete and exist, but should be excluded from some system interactions
* (for instance, they usually should not appear in typeaheads, and should
* not have mail/notifications delivered to or about them).
*
* @return bool True if the handle represents a disabled object.
*/
public function isDisabled() {
return $this->disabled;
}
public function renderLink() {
$name = $this->getLinkName();
$class = null;
$title = null;
if ($this->status != PhabricatorObjectHandleStatus::STATUS_OPEN) {
$class .= ' handle-status-'.$this->status;
$title = (isset($this->title) ? $this->title : $this->status);
}
if ($this->disabled) {
$class .= ' handle-disabled';
$title = 'disabled'; // Overwrite status.
}
- return phutil_render_tag(
+ return phutil_tag(
'a',
array(
'href' => $this->getURI(),
'class' => $class,
'title' => $title,
),
- phutil_escape_html($name));
+ $name);
}
public function getLinkName() {
switch ($this->getType()) {
case PhabricatorPHIDConstants::PHID_TYPE_USER:
case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
$name = $this->getName();
break;
default:
$name = $this->getFullName();
break;
}
return $name;
}
}
diff --git a/src/applications/phid/controller/PhabricatorPHIDLookupController.php b/src/applications/phid/controller/PhabricatorPHIDLookupController.php
index 7fa5e64e12..c6228df5c2 100644
--- a/src/applications/phid/controller/PhabricatorPHIDLookupController.php
+++ b/src/applications/phid/controller/PhabricatorPHIDLookupController.php
@@ -1,87 +1,87 @@
<?php
final class PhabricatorPHIDLookupController
extends PhabricatorPHIDController {
public function processRequest() {
$request = $this->getRequest();
$phids = $request->getStrList('phids');
if ($phids) {
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($handles as $handle) {
if ($handle->getURI()) {
- $link = phutil_render_tag(
+ $link = phutil_tag(
'a',
array(
'href' => $handle->getURI(),
),
- phutil_escape_html($handle->getURI()));
+ $handle->getURI());
} else {
$link = null;
}
$rows[] = array(
phutil_escape_html($handle->getPHID()),
phutil_escape_html($handle->getType()),
phutil_escape_html($handle->getName()),
$link,
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'PHID',
'Type',
'Name',
'URI',
));
$table->setColumnClasses(
array(
null,
null,
null,
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader('PHID Handles');
$panel->appendChild($table);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'PHID Lookup Results',
));
}
$lookup_form = new AphrontFormView();
$lookup_form->setUser($request->getUser());
$lookup_form
->setAction('/phid/')
->appendChild(
id(new AphrontFormTextAreaControl())
->setName('phids')
->setCaption('Enter PHIDs separated by spaces or commas.'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Lookup PHIDs'));
$lookup_panel = new AphrontPanelView();
$lookup_panel->setHeader('Lookup PHIDs');
$lookup_panel->appendChild($lookup_form);
$lookup_panel->setWidth(AphrontPanelView::WIDTH_WIDE);
return $this->buildStandardPageResponse(
array(
$lookup_panel,
),
array(
'title' => 'PHID Lookup',
));
}
}
diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php
index c3a3b30f67..bf7c620be9 100644
--- a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php
+++ b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php
@@ -1,32 +1,32 @@
<?php
final class PhabricatorXHPASTViewStreamController
extends PhabricatorXHPASTViewPanelController {
public function processRequest() {
$storage = $this->getStorageTree();
$input = $storage->getInput();
$stdout = $storage->getStdout();
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
$input,
array(0, $stdout, ''));
$tokens = array();
foreach ($tree->getRawTokenStream() as $id => $token) {
$seq = $id;
$name = $token->getTypeName();
$title = "Token {$seq}: {$name}";
- $tokens[] = phutil_render_tag(
+ $tokens[] = phutil_tag(
'span',
array(
'title' => $title,
'class' => 'token',
),
- phutil_escape_html($token->getValue()));
+ $token->getValue());
}
return $this->buildXHPASTViewPanelResponse(implode('', $tokens));
}
}
diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php
index 2e07a0317e..5b7673386e 100644
--- a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php
+++ b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php
@@ -1,45 +1,45 @@
<?php
final class PhabricatorXHPASTViewTreeController
extends PhabricatorXHPASTViewPanelController {
public function processRequest() {
$storage = $this->getStorageTree();
$input = $storage->getInput();
$stdout = $storage->getStdout();
$tree = XHPASTTree::newFromDataAndResolvedExecFuture(
$input,
array(0, $stdout, ''));
$tree = '<ul>'.$this->buildTree($tree->getRootNode()).'</ul>';
return $this->buildXHPASTViewPanelResponse($tree);
}
protected function buildTree($root) {
try {
$name = $root->getTypeName();
$title = $root->getDescription();
} catch (Exception $ex) {
$name = '???';
$title = '???';
}
$tree = array();
$tree[] =
'<li>'.
- phutil_render_tag(
+ phutil_tag(
'span',
array(
'title' => $title,
),
- phutil_escape_html($name)).
+ $name).
'</li>';
foreach ($root->getChildren() as $child) {
$tree[] = '<ul>'.$this->buildTree($child).'</ul>';
}
return implode("\n", $tree);
}
}
diff --git a/src/applications/phriction/controller/PhrictionDiffController.php b/src/applications/phriction/controller/PhrictionDiffController.php
index ee03806fac..2702854661 100644
--- a/src/applications/phriction/controller/PhrictionDiffController.php
+++ b/src/applications/phriction/controller/PhrictionDiffController.php
@@ -1,265 +1,265 @@
<?php
/**
* @group phriction
*/
final class PhrictionDiffController
extends PhrictionController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$document = id(new PhrictionDocument())->load($this->id);
if (!$document) {
return new Aphront404Response();
}
$current = id(new PhrictionContent())->load($document->getContentID());
$l = $request->getInt('l');
$r = $request->getInt('r');
$ref = $request->getStr('ref');
if ($ref) {
list($l, $r) = explode(',', $ref);
}
$content = id(new PhrictionContent())->loadAllWhere(
'documentID = %d AND version IN (%Ld)',
$document->getID(),
array($l, $r));
$content = mpull($content, null, 'getVersion');
$content_l = idx($content, $l, null);
$content_r = idx($content, $r, null);
if (!$content_l || !$content_r) {
return new Aphront404Response();
}
$text_l = $content_l->getContent();
$text_r = $content_r->getContent();
$text_l = wordwrap($text_l, 80);
$text_r = wordwrap($text_r, 80);
$engine = new PhabricatorDifferenceEngine();
$changeset = $engine->generateChangesetFromFileContent($text_l, $text_r);
$changeset->setOldProperties(
array(
'Title' => $content_l->getTitle(),
));
$changeset->setNewProperties(
array(
'Title' => $content_r->getTitle(),
));
$whitespace_mode = DifferentialChangesetParser::WHITESPACE_SHOW_ALL;
$parser = new DifferentialChangesetParser();
$parser->setChangeset($changeset);
$parser->setRenderingReference("{$l},{$r}");
$parser->setWhitespaceMode($whitespace_mode);
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($user);
$engine->process();
$parser->setMarkupEngine($engine);
$spec = $request->getStr('range');
list($range_s, $range_e, $mask) =
DifferentialChangesetParser::parseRangeSpecification($spec);
$output = $parser->render($range_s, $range_e, $mask);
if ($request->isAjax()) {
return id(new PhabricatorChangesetResponse())
->setRenderedChangeset($output);
}
require_celerity_resource('differential-changeset-view-css');
require_celerity_resource('syntax-highlighting-css');
require_celerity_resource('phriction-document-css');
Javelin::initBehavior('differential-show-more', array(
'uri' => '/phriction/diff/'.$document->getID().'/',
'whitespace' => $whitespace_mode,
));
$slug = $document->getSlug();
$revert_l = $this->renderRevertButton($content_l, $current);
$revert_r = $this->renderRevertButton($content_r, $current);
$crumbs = new AphrontCrumbsView();
$crumbs->setCrumbs(
array(
'Phriction',
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => PhrictionDocument::getSlugURI($slug),
),
- phutil_escape_html($current->getTitle())),
+ $current->getTitle()),
phutil_render_tag(
'a',
array(
'href' => '/phriction/history/'.$document->getSlug().'/',
),
'History'),
phutil_escape_html("Changes Between Version {$l} and Version {$r}"),
));
$comparison_table = $this->renderComparisonTable(
array(
$content_r,
$content_l,
));
$navigation_table = null;
if ($l + 1 == $r) {
$nav_l = ($l > 1);
$nav_r = ($r != $current->getVersion());
$uri = $request->getRequestURI();
if ($nav_l) {
$link_l = phutil_render_tag(
'a',
array(
'href' => $uri->alter('l', $l - 1)->alter('r', $r - 1),
),
"\xC2\xAB Previous Change");
} else {
$link_l = 'Original Change';
}
$link_r = null;
if ($nav_r) {
$link_r = phutil_render_tag(
'a',
array(
'href' => $uri->alter('l', $l + 1)->alter('r', $r + 1),
),
"Next Change \xC2\xBB");
} else {
$link_r = 'Most Recent Change';
}
$navigation_table =
'<table class="phriction-history-nav-table">
<tr>
<td class="nav-prev">'.$link_l.'</td>
<td class="nav-next">'.$link_r.'</td>
</tr>
</table>';
}
$output =
'<div class="phriction-document-history-diff">'.
$comparison_table->render().
'<br />'.
'<br />'.
$navigation_table.
'<table class="phriction-revert-table">'.
'<tr><td>'.$revert_l.'</td><td>'.$revert_r.'</td>'.
'</table>'.
$output.
'</div>';
return $this->buildStandardPageResponse(
array(
$crumbs,
$output,
),
array(
'title' => 'Document History',
));
}
private function renderRevertButton(
PhrictionContent $content,
PhrictionContent $current) {
$document_id = $content->getDocumentID();
$version = $content->getVersion();
if ($content->getChangeType() == PhrictionChangeType::CHANGE_DELETE) {
// Don't show an edit/revert button for changes which deleted the content
// since it's silly.
return null;
}
if ($content->getID() == $current->getID()) {
return phutil_render_tag(
'a',
array(
'href' => '/phriction/edit/'.$document_id.'/',
'class' => 'button',
),
'Edit Current Version');
}
return phutil_render_tag(
'a',
array(
'href' => '/phriction/edit/'.$document_id.'/?revert='.$version,
'class' => 'button',
),
'Revert to Version '.phutil_escape_html($version).'...');
}
private function renderComparisonTable(array $content) {
assert_instances_of($content, 'PhrictionContent');
$user = $this->getRequest()->getUser();
$phids = mpull($content, 'getAuthorPHID');
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($content as $c) {
$rows[] = array(
phabricator_date($c->getDateCreated(), $user),
phabricator_time($c->getDateCreated(), $user),
phutil_escape_html('Version '.$c->getVersion()),
$handles[$c->getAuthorPHID()]->renderLink(),
phutil_escape_html($c->getDescription()),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Date',
'Time',
'Version',
'Author',
'Description',
));
$table->setColumnClasses(
array(
'',
'right',
'pri',
'',
'wide',
));
return $table;
}
}
diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php
index 7b7a88fd98..f48e562d18 100644
--- a/src/applications/phriction/controller/PhrictionDocumentController.php
+++ b/src/applications/phriction/controller/PhrictionDocumentController.php
@@ -1,358 +1,358 @@
<?php
/**
* @group phriction
*/
final class PhrictionDocumentController
extends PhrictionController {
private $slug;
public function willProcessRequest(array $data) {
$this->slug = $data['slug'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$slug = PhabricatorSlug::normalize($this->slug);
if ($slug != $this->slug) {
$uri = PhrictionDocument::getSlugURI($slug);
// Canonicalize pages to their one true URI.
return id(new AphrontRedirectResponse())->setURI($uri);
}
require_celerity_resource('phriction-document-css');
$document = id(new PhrictionDocument())->loadOneWhere(
'slug = %s',
$slug);
$version_note = null;
if (!$document) {
$document = new PhrictionDocument();
if (PhrictionDocument::isProjectSlug($slug)) {
$project = id(new PhabricatorProject())->loadOneWhere(
'phrictionSlug = %s',
PhrictionDocument::getProjectSlugIdentifier($slug));
if (!$project) {
return new Aphront404Response();
}
}
$create_uri = '/phriction/edit/?slug='.$slug;
$create_sentence =
'You can <strong>'.
phutil_render_tag(
'a',
array(
'href' => $create_uri,
),
'create a new document').
'</strong>.';
$button = phutil_render_tag(
'a',
array(
'href' => $create_uri,
'class' => 'green button',
),
'Create Page');
$page_content =
'<div class="phriction-content">'.
'<em>No content here!</em><br />'.
'No document found at <tt>'.phutil_escape_html($slug).'</tt>. '.
$create_sentence.
'</div>';
$page_title = 'Page Not Found';
$buttons = $button;
} else {
$version = $request->getInt('v');
if ($version) {
$content = id(new PhrictionContent())->loadOneWhere(
'documentID = %d AND version = %d',
$document->getID(),
$version);
if (!$content) {
return new Aphront404Response();
}
if ($content->getID() != $document->getContentID()) {
$version_note = new AphrontErrorView();
$version_note->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$version_note->setTitle('Older Version');
$version_note->appendChild(
'You are viewing an older version of this document, as it '.
'appeared on '.
phabricator_datetime($content->getDateCreated(), $user).'.');
}
} else {
$content = id(new PhrictionContent())->load($document->getContentID());
}
$page_title = $content->getTitle();
$project_phid = null;
if (PhrictionDocument::isProjectSlug($slug)) {
$project = id(new PhabricatorProject())->loadOneWhere(
'phrictionSlug = %s',
PhrictionDocument::getProjectSlugIdentifier($slug));
if ($project) {
$project_phid = $project->getPHID();
}
}
$phids = array_filter(
array(
$content->getAuthorPHID(),
$project_phid,
));
$handles = $this->loadViewerHandles($phids);
$age = time() - $content->getDateCreated();
$age = floor($age / (60 * 60 * 24));
if ($age < 1) {
$when = 'today';
} else if ($age == 1) {
$when = 'yesterday';
} else {
$when = "{$age} days ago";
}
$project_info = null;
if ($project_phid) {
$project_info =
'<br />This document is about the project '.
$handles[$project_phid]->renderLink().'.';
}
$index_link = phutil_render_tag(
'a',
array(
'href' => '/phriction/',
),
pht('Document Index'));
$byline =
'<div class="phriction-byline">'.
"Last updated {$when} by ".
$handles[$content->getAuthorPHID()]->renderLink().'.'.
$project_info.
'</div>';
$doc_status = $document->getStatus();
if ($doc_status == PhrictionDocumentStatus::STATUS_EXISTS) {
$core_content = $content->renderContent($user);
} else if ($doc_status == PhrictionDocumentStatus::STATUS_DELETED) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('Document Deleted');
$notice->appendChild(
'This document has been deleted. You can edit it to put new content '.
'here, or use history to revert to an earlier version.');
$core_content = $notice->render();
} else {
throw new Exception("Unknown document status '{$doc_status}'!");
}
$page_content =
'<div class="phriction-content">'.
$index_link.
$byline.
$core_content.
'</div>';
}
if ($version_note) {
$version_note = $version_note->render();
}
$children = $this->renderChildren($slug);
$crumbs = $this->buildApplicationCrumbs();
$crumb_views = $this->renderBreadcrumbs($slug);
foreach ($crumb_views as $view) {
$crumbs->addCrumb($view);
}
$actions = $this->buildActionView($user, $document);
$header = id(new PhabricatorHeaderView())
->setHeader($page_title);
$page =
$crumbs->render().
$header->render().
$actions->render().
$version_note.
$page_content.
$children;
return $this->buildApplicationPage(
array(
$page,
),
array(
'title' => $page_title,
'device' => true,
));
}
private function buildActionView(
PhabricatorUser $user,
PhrictionDocument $document) {
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$document,
PhabricatorPolicyCapability::CAN_EDIT);
$slug = PhabricatorSlug::normalize($this->slug);
return id(new PhabricatorActionListView())
->setUser($user)
->setObject($document)
->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Document'))
->setIcon('edit')
->setHref('/phriction/edit/'.$document->getID().'/'))
->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setIcon('history')
->setHref(PhrictionDocument::getSlugURI($slug, 'history')));
}
private function renderChildren($slug) {
$document_dao = new PhrictionDocument();
$content_dao = new PhrictionContent();
$conn = $document_dao->establishConnection('r');
$limit = 50;
$d_child = PhabricatorSlug::getDepth($slug) + 1;
$d_grandchild = PhabricatorSlug::getDepth($slug) + 2;
// Select children and grandchildren.
$children = queryfx_all(
$conn,
'SELECT d.slug, d.depth, c.title FROM %T d JOIN %T c
ON d.contentID = c.id
WHERE d.slug LIKE %> AND d.depth IN (%d, %d)
AND d.status = %d
ORDER BY d.depth, c.title LIMIT %d',
$document_dao->getTableName(),
$content_dao->getTableName(),
($slug == '/' ? '' : $slug),
$d_child,
$d_grandchild,
PhrictionDocumentStatus::STATUS_EXISTS,
$limit);
if (!$children) {
return;
}
// We're going to render in one of three modes to try to accommodate
// different information scales:
//
// - If we found fewer than $limit rows, we know we have all the children
// and grandchildren and there aren't all that many. We can just render
// everything.
// - If we found $limit rows but the results included some grandchildren,
// we just throw them out and render only the children, as we know we
// have them all.
// - If we found $limit rows and the results have no grandchildren, we
// have a ton of children. Render them and then let the user know that
// this is not an exhaustive list.
if (count($children) == $limit) {
$more_children = true;
foreach ($children as $child) {
if ($child['depth'] == $d_grandchild) {
$more_children = false;
}
}
$show_grandchildren = false;
} else {
$show_grandchildren = true;
$more_children = false;
}
$grandchildren = array();
foreach ($children as $key => $child) {
if ($child['depth'] == $d_child) {
continue;
} else {
unset($children[$key]);
if ($show_grandchildren) {
$ancestors = PhabricatorSlug::getAncestry($child['slug']);
$grandchildren[end($ancestors)][] = $child;
}
}
}
// Fill in any missing children.
$known_slugs = ipull($children, null, 'slug');
foreach ($grandchildren as $slug => $ignored) {
if (empty($known_slugs[$slug])) {
$children[] = array(
'slug' => $slug,
'depth' => $d_child,
'title' => PhabricatorSlug::getDefaultTitle($slug),
'empty' => true,
);
}
}
$children = isort($children, 'title');
$list = array();
$list[] = '<ul>';
foreach ($children as $child) {
$list[] = $this->renderChildDocumentLink($child);
$grand = idx($grandchildren, $child['slug'], array());
if ($grand) {
$list[] = '<ul>';
foreach ($grand as $grandchild) {
$list[] = $this->renderChildDocumentLink($grandchild);
}
$list[] = '</ul>';
}
}
if ($more_children) {
$list[] = '<li>More...</li>';
}
$list[] = '</ul>';
$list = implode("\n", $list);
return
'<div class="phriction-children">'.
'<div class="phriction-children-header">Document Hierarchy</div>'.
$list.
'</div>';
}
private function renderChildDocumentLink(array $info) {
$title = nonempty($info['title'], '(Untitled Document)');
- $item = phutil_render_tag(
+ $item = phutil_tag(
'a',
array(
'href' => PhrictionDocument::getSlugURI($info['slug']),
),
- phutil_escape_html($title));
+ $title);
if (isset($info['empty'])) {
$item = '<em>'.$item.'</em>';
}
return '<li>'.$item.'</li>';
}
}
diff --git a/src/applications/phriction/controller/PhrictionListController.php b/src/applications/phriction/controller/PhrictionListController.php
index abd90432e9..65bfefe701 100644
--- a/src/applications/phriction/controller/PhrictionListController.php
+++ b/src/applications/phriction/controller/PhrictionListController.php
@@ -1,160 +1,160 @@
<?php
/**
* @group phriction
*/
final class PhrictionListController
extends PhrictionController {
private $view;
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$views = array(
'active' => pht('Active Documents'),
'all' => pht('All Documents'),
'updates' => pht('Recently Updated'),
);
if (empty($views[$this->view])) {
$this->view = 'active';
}
$nav = $this->buildSideNavView($this->view);
$header = id(new PhabricatorHeaderView())
->setHeader($views[$this->view]);
$nav->appendChild(
array(
$header,
));
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'page');
$pager->setOffset($request->getInt('page'));
$documents = $this->loadDocuments($pager);
$content = mpull($documents, 'getContent');
$phids = mpull($content, 'getAuthorPHID');
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($documents as $document) {
$content = $document->getContent();
$rows[] = array(
$handles[$content->getAuthorPHID()]->renderLink(),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => PhrictionDocument::getSlugURI($document->getSlug()),
),
- phutil_escape_html($content->getTitle())),
+ $content->getTitle()),
phabricator_date($content->getDateCreated(), $user),
phabricator_time($content->getDateCreated(), $user),
);
}
$document_table = new AphrontTableView($rows);
$document_table->setHeaders(
array(
'Last Editor',
'Title',
'Last Update',
'Time',
));
$document_table->setColumnClasses(
array(
'',
'wide pri',
'right',
'right',
));
$view_header = $views[$this->view];
$panel = new AphrontPanelView();
$panel->appendChild($document_table);
$panel->appendChild($pager);
$nav->appendChild($panel);
return $this->buildApplicationPage(
$nav,
array(
'title' => pht('Phriction Main'),
));
}
private function loadDocuments(AphrontPagerView $pager) {
// TODO: Do we want/need a query object for this?
$document_dao = new PhrictionDocument();
$content_dao = new PhrictionContent();
$conn = $document_dao->establishConnection('r');
switch ($this->view) {
case 'active':
$data = queryfx_all(
$conn,
'SELECT * FROM %T WHERE status != %d ORDER BY id DESC LIMIT %d, %d',
$document_dao->getTableName(),
PhrictionDocumentStatus::STATUS_DELETED,
$pager->getOffset(),
$pager->getPageSize() + 1);
break;
case 'all':
$data = queryfx_all(
$conn,
'SELECT * FROM %T ORDER BY id DESC LIMIT %d, %d',
$document_dao->getTableName(),
$pager->getOffset(),
$pager->getPageSize() + 1);
break;
case 'updates':
// TODO: This query is a little suspicious, verify we don't need to key
// or change it once we get more data.
$data = queryfx_all(
$conn,
'SELECT d.* FROM %T d JOIN %T c ON c.documentID = d.id
GROUP BY c.documentID
ORDER BY MAX(c.id) DESC LIMIT %d, %d',
$document_dao->getTableName(),
$content_dao->getTableName(),
$pager->getOffset(),
$pager->getPageSize() + 1);
break;
default:
throw new Exception("Unknown view '{$this->view}'!");
}
$data = $pager->sliceResults($data);
$documents = $document_dao->loadAllFromArray($data);
if ($documents) {
$content = $content_dao->loadAllWhere(
'documentID IN (%Ld)',
mpull($documents, 'getID'));
$content = mpull($content, null, 'getDocumentID');
foreach ($documents as $document) {
$document->attachContent($content[$document->getID()]);
}
}
return $documents;
}
}
diff --git a/src/applications/policy/filter/PhabricatorPolicy.php b/src/applications/policy/filter/PhabricatorPolicy.php
index b901efbfbc..305cae4cd1 100644
--- a/src/applications/policy/filter/PhabricatorPolicy.php
+++ b/src/applications/policy/filter/PhabricatorPolicy.php
@@ -1,103 +1,103 @@
<?php
final class PhabricatorPolicy {
private $phid;
private $name;
private $type;
private $href;
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setPHID($phid) {
$this->phid = $phid;
return $this;
}
public function getPHID() {
return $this->phid;
}
public function setHref($href) {
$this->href = $href;
return $this;
}
public function getHref() {
return $this->href;
}
public function getSortKey() {
return sprintf(
'%02d%s',
PhabricatorPolicyType::getPolicyTypeOrder($this->getType()),
$this->getSortName());
}
private function getSortName() {
if ($this->getType() == PhabricatorPolicyType::TYPE_GLOBAL) {
static $map = array(
PhabricatorPolicies::POLICY_PUBLIC => 0,
PhabricatorPolicies::POLICY_USER => 1,
PhabricatorPolicies::POLICY_ADMIN => 2,
PhabricatorPolicies::POLICY_NOONE => 3,
);
return idx($map, $this->getPHID());
}
return $this->getName();
}
public function getFullName() {
switch ($this->getType()) {
case PhabricatorPolicyType::TYPE_PROJECT:
return pht('Project: %s', $this->getName());
case PhabricatorPolicyType::TYPE_MASKED:
return pht('Other: %s', $this->getName());
default:
return $this->getName();
}
}
public function renderDescription() {
if ($this->getHref()) {
- $desc = phutil_render_tag(
+ $desc = phutil_tag(
'a',
array(
'href' => $this->getHref(),
),
- phutil_escape_html($this->getName()));
+ $this->getName());
} else {
$desc = phutil_escape_html($this->getName());
}
switch ($this->getType()) {
case PhabricatorPolicyType::TYPE_PROJECT:
return pht('%s (Project)', $desc);
case PhabricatorPolicyType::TYPE_MASKED:
return pht(
'%s (You do not have permission to view policy details.)',
$desc);
default:
return $desc;
}
}
}
diff --git a/src/applications/ponder/view/PonderQuestionSummaryView.php b/src/applications/ponder/view/PonderQuestionSummaryView.php
index cc03d6d138..a06cdcc46d 100644
--- a/src/applications/ponder/view/PonderQuestionSummaryView.php
+++ b/src/applications/ponder/view/PonderQuestionSummaryView.php
@@ -1,83 +1,81 @@
<?php
final class PonderQuestionSummaryView extends AphrontView {
private $question;
private $handles;
public function setQuestion($question) {
$this->question = $question;
return $this;
}
public function setHandles($handles) {
$this->handles = $handles;
return $this;
}
public function render() {
require_celerity_resource('ponder-feed-view-css');
$user = $this->user;
$question = $this->question;
$author_phid = $question->getAuthorPHID();
$handles = $this->handles;
$authorlink = $handles[$author_phid]
->renderLink();
$votecount =
'<div class="ponder-summary-votes">'.
phutil_escape_html($question->getVoteCount()).
'<div class="ponder-question-label">'.
'votes'.
'</div>'.
'</div>';
$answerclass = "ponder-summary-answers";
if ($question->getAnswercount() == 0) {
$answerclass .= " ponder-not-answered";
}
$answercount =
'<div class="ponder-summary-answers">'.
phutil_escape_html($question->getAnswerCount()).
'<div class="ponder-question-label">'.
'answers'.
'</div>'.
'</div>';
$title =
'<h2 class="ponder-question-title">'.
- phutil_render_tag(
+ phutil_tag(
'a',
array(
"href" => '/Q' . $question->getID(),
),
- phutil_escape_html(
'Q' . $question->getID() .
' ' . $question->getTitle()
- )
) .
'</h2>';
$rhs =
'<div class="ponder-metadata">'.
$title.
'<span class="ponder-small-metadata">'.
'asked on '.
phabricator_datetime($question->getDateCreated(), $user).
' by ' . $authorlink.
'</span>'.
'</div>';
$summary =
'<div class="ponder-question-summary">'.
$votecount.
$answercount.
$rhs.
'</div>';
return $summary;
}
}
diff --git a/src/applications/project/controller/PhabricatorProjectListController.php b/src/applications/project/controller/PhabricatorProjectListController.php
index ca215fdf19..869a7c2404 100644
--- a/src/applications/project/controller/PhabricatorProjectListController.php
+++ b/src/applications/project/controller/PhabricatorProjectListController.php
@@ -1,154 +1,154 @@
<?php
final class PhabricatorProjectListController
extends PhabricatorProjectController {
private $filter;
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
public function processRequest() {
$request = $this->getRequest();
$nav = new AphrontSideNavFilterView();
$nav
->setBaseURI(new PhutilURI('/project/filter/'))
->addLabel('User')
->addFilter('active', 'Active')
->addLabel('All')
->addFilter('all', 'All Projects')
->addFilter('allactive','Active Projects');
$this->filter = $nav->selectFilter($this->filter, 'active');
$pager = new AphrontPagerView();
$pager->setPageSize(250);
$pager->setURI($request->getRequestURI(), 'page');
$pager->setOffset($request->getInt('page'));
$query = new PhabricatorProjectQuery();
$query->setViewer($request->getUser());
$query->needMembers(true);
$view_phid = $request->getUser()->getPHID();
$status_filter = PhabricatorProjectQuery::STATUS_ANY;
switch ($this->filter) {
case 'active':
$table_header = 'Your Projects';
$query->withMemberPHIDs(array($view_phid));
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
break;
case 'allactive':
$status_filter = PhabricatorProjectQuery::STATUS_ACTIVE;
$table_header = 'Active Projects';
// fallthrough
case 'all':
$table_header = 'All Projects';
$query->withStatus($status_filter);
break;
}
$projects = $query->executeWithOffsetPager($pager);
$project_phids = mpull($projects, 'getPHID');
$profiles = array();
if ($projects) {
$profiles = id(new PhabricatorProjectProfile())->loadAllWhere(
'projectPHID in (%Ls)',
$project_phids);
$profiles = mpull($profiles, null, 'getProjectPHID');
}
$tasks = array();
$groups = array();
if ($project_phids) {
$query = id(new ManiphestTaskQuery())
->withAnyProjects($project_phids)
->withStatus(ManiphestTaskQuery::STATUS_OPEN)
->setLimit(PHP_INT_MAX);
$tasks = $query->execute();
foreach ($tasks as $task) {
foreach ($task->getProjectPHIDs() as $phid) {
$groups[$phid][] = $task;
}
}
}
$rows = array();
foreach ($projects as $project) {
$phid = $project->getPHID();
$profile = idx($profiles, $phid);
$members = $project->getMemberPHIDs();
$group = idx($groups, $phid, array());
$task_count = count($group);
$population = count($members);
if ($profile) {
$blurb = $profile->getBlurb();
$blurb = phutil_utf8_shorten($blurb, 64);
} else {
$blurb = null;
}
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/project/view/'.$project->getID().'/',
),
- phutil_escape_html($project->getName())),
+ $project->getName()),
phutil_escape_html(
PhabricatorProjectStatus::getNameForStatus($project->getStatus())),
phutil_escape_html($blurb),
phutil_escape_html($population),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/maniphest/view/all/?projects='.$phid,
),
- phutil_escape_html($task_count)),
+ $task_count),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Project',
'Status',
'Description',
'Population',
'Open Tasks',
));
$table->setColumnClasses(
array(
'pri',
'',
'wide',
'',
''
));
$panel = new AphrontPanelView();
$panel->setHeader($table_header);
$panel->setCreateButton('Create New Project', '/project/create/');
$panel->appendChild($table);
$panel->appendChild($pager);
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Projects',
));
}
}
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php b/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php
index 692636405f..0ade8c291a 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanelProfile.php
@@ -1,209 +1,209 @@
<?php
final class PhabricatorSettingsPanelProfile
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'profile';
}
public function getPanelName() {
return pht('Profile');
}
public function getPanelGroup() {
return pht('Account Information');
}
public function processRequest(AphrontRequest $request) {
$user = $request->getUser();
$profile = id(new PhabricatorUserProfile())->loadOneWhere(
'userPHID = %s',
$user->getPHID());
if (!$profile) {
$profile = new PhabricatorUserProfile();
$profile->setUserPHID($user->getPHID());
}
$supported_formats = PhabricatorFile::getTransformableImageFormats();
$e_image = null;
$errors = array();
if ($request->isFormPost()) {
$profile->setTitle($request->getStr('title'));
$profile->setBlurb($request->getStr('blurb'));
$sex = $request->getStr('sex');
$sexes = array(PhutilPerson::SEX_MALE, PhutilPerson::SEX_FEMALE);
if (in_array($sex, $sexes)) {
$user->setSex($sex);
} else {
$user->setSex(null);
}
// Checked in runtime.
$user->setTranslation($request->getStr('translation'));
$default_image = $request->getExists('default_image');
if ($default_image) {
$profile->setProfileImagePHID(null);
$user->setProfileImagePHID(null);
} else if (!empty($_FILES['image'])) {
$err = idx($_FILES['image'], 'error');
if ($err != UPLOAD_ERR_NO_FILE) {
$file = PhabricatorFile::newFromPHPUpload(
$_FILES['image'],
array(
'authorPHID' => $user->getPHID(),
));
$okay = $file->isTransformableImage();
if ($okay) {
$xformer = new PhabricatorImageTransformer();
// Generate the large picture for the profile page.
$large_xformed = $xformer->executeProfileTransform(
$file,
$width = 280,
$min_height = 140,
$max_height = 420);
$profile->setProfileImagePHID($large_xformed->getPHID());
// Generate the small picture for comments, etc.
$small_xformed = $xformer->executeProfileTransform(
$file,
$width = 50,
$min_height = 50,
$max_height = 50);
$user->setProfileImagePHID($small_xformed->getPHID());
} else {
$e_image = 'Not Supported';
$errors[] =
'This server only supports these image formats: '.
implode(', ', $supported_formats).'.';
}
}
}
if (!$errors) {
$user->save();
$profile->save();
$response = id(new AphrontRedirectResponse())
->setURI($this->getPanelURI('?saved=true'));
return $response;
}
}
$error_view = null;
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setErrors($errors);
} else {
if ($request->getStr('saved')) {
$error_view = new AphrontErrorView();
$error_view->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$error_view->setTitle('Changes Saved');
$error_view->appendChild('<p>Your changes have been saved.</p>');
$error_view = $error_view->render();
}
}
$img_src = $user->loadProfileImageURI();
$profile_uri = PhabricatorEnv::getURI('/p/'.$user->getUsername().'/');
$sexes = array(
PhutilPerson::SEX_UNKNOWN => 'Unknown',
PhutilPerson::SEX_MALE => 'Male',
PhutilPerson::SEX_FEMALE => 'Female',
);
$translations = array();
$symbols = id(new PhutilSymbolLoader())
->setType('class')
->setAncestorClass('PhabricatorTranslation')
->setConcreteOnly(true)
->selectAndLoadSymbols();
foreach ($symbols as $symbol) {
$class = $symbol['name'];
$translations[$class] = newv($class, array())->getName();
}
asort($translations);
$default = PhabricatorEnv::newObjectFromConfig('translation.provider');
$translations = array(
'' => 'Server Default ('.$default->getName().')',
) + $translations;
$form = new AphrontFormView();
$form
->setUser($request->getUser())
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Title')
->setName('title')
->setValue($profile->getTitle())
->setCaption('Serious business title.'))
->appendChild(
id(new AphrontFormSelectControl())
->setOptions($sexes)
->setLabel('Sex')
->setName('sex')
->setValue($user->getSex()))
->appendChild(
id(new AphrontFormSelectControl())
->setOptions($translations)
->setLabel('Translation')
->setName('translation')
->setValue($user->getTranslation()))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Profile URI')
->setValue(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $profile_uri,
),
- phutil_escape_html($profile_uri))))
+ $profile_uri)))
->appendChild(
'<p class="aphront-form-instructions">Write something about yourself! '.
'Make sure to include <strong>important information</strong> like '.
'your favorite Pokemon and which Starcraft race you play.</p>')
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Blurb')
->setName('blurb')
->setValue($profile->getBlurb()))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Profile Image')
->setValue(
phutil_tag(
'img',
array(
'src' => $img_src,
))))
->appendChild(
id(new AphrontFormImageControl())
->setLabel('Change Image')
->setName('image')
->setError($e_image)
->setCaption('Supported formats: '.implode(', ', $supported_formats)))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/p/'.$user->getUsername().'/'));
$panel = new AphrontPanelView();
$panel->setHeader('Edit Profile Details');
$panel->appendChild($form);
$panel->setNoBackground();
return array(
$error_view,
$panel,
);
}
}
diff --git a/src/applications/settings/panel/PhabricatorSettingsPanelSSHKeys.php b/src/applications/settings/panel/PhabricatorSettingsPanelSSHKeys.php
index 7bf018a2b5..0c33b30f0f 100644
--- a/src/applications/settings/panel/PhabricatorSettingsPanelSSHKeys.php
+++ b/src/applications/settings/panel/PhabricatorSettingsPanelSSHKeys.php
@@ -1,266 +1,266 @@
<?php
final class PhabricatorSettingsPanelSSHKeys
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'ssh';
}
public function getPanelName() {
return pht('SSH Public Keys');
}
public function getPanelGroup() {
return pht('Authentication');
}
public function isEnabled() {
return PhabricatorEnv::getEnvConfig('auth.sshkeys.enabled');
}
public function processRequest(AphrontRequest $request) {
$user = $request->getUser();
$edit = $request->getStr('edit');
$delete = $request->getStr('delete');
if (!$edit && !$delete) {
return $this->renderKeyListView($request);
}
$id = nonempty($edit, $delete);
if ($id && is_numeric($id)) {
// NOTE: Prevent editing/deleting of keys you don't own.
$key = id(new PhabricatorUserSSHKey())->loadOneWhere(
'userPHID = %s AND id = %d',
$user->getPHID(),
(int)$id);
if (!$key) {
return new Aphront404Response();
}
} else {
$key = new PhabricatorUserSSHKey();
$key->setUserPHID($user->getPHID());
}
if ($delete) {
return $this->processDelete($request, $key);
}
$e_name = true;
$e_key = true;
$errors = array();
$entire_key = $key->getEntireKey();
if ($request->isFormPost()) {
$key->setName($request->getStr('name'));
$entire_key = $request->getStr('key');
if (!strlen($entire_key)) {
$errors[] = 'You must provide an SSH Public Key.';
$e_key = 'Required';
} else {
$parts = str_replace("\n", '', trim($entire_key));
$parts = preg_split('/\s+/', $parts);
if (count($parts) == 2) {
$parts[] = ''; // Add an empty comment part.
} else if (count($parts) == 3) {
// This is the expected case.
} else {
if (preg_match('/private\s*key/i', $entire_key)) {
// Try to give the user a better error message if it looks like
// they uploaded a private key.
$e_key = 'Invalid';
$errors[] = 'Provide your public key, not your private key!';
} else {
$e_key = 'Invalid';
$errors[] = 'Provided public key is not properly formatted.';
}
}
if (!$errors) {
list($type, $body, $comment) = $parts;
if (!preg_match('/^ssh-dsa|ssh-rsa$/', $type)) {
$e_key = 'Invalid';
$errors[] = 'Public key should be "ssh-dsa" or "ssh-rsa".';
} else {
$key->setKeyType($type);
$key->setKeyBody($body);
$key->setKeyHash(md5($body));
$key->setKeyComment($comment);
$e_key = null;
}
}
}
if (!strlen($key->getName())) {
$errors[] = 'You must name this public key.';
$e_name = 'Required';
} else {
$e_name = null;
}
if (!$errors) {
try {
$key->save();
return id(new AphrontRedirectResponse())
->setURI($this->getPanelURI());
} catch (AphrontQueryDuplicateKeyException $ex) {
$e_key = 'Duplicate';
$errors[] = 'This public key is already associated with a user '.
'account.';
}
}
}
$error_view = null;
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setErrors($errors);
}
$is_new = !$key->getID();
if ($is_new) {
$header = 'Add New SSH Public Key';
$save = 'Add Key';
} else {
$header = 'Edit SSH Public Key';
$save = 'Save Changes';
}
$form = id(new AphrontFormView())
->setUser($user)
->addHiddenInput('edit', $is_new ? 'true' : $key->getID())
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setName('name')
->setValue($key->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Public Key')
->setName('key')
->setValue($entire_key)
->setError($e_key))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getPanelURI())
->setValue($save));
$panel = new AphrontPanelView();
$panel->setHeader($header);
$panel->appendChild($form);
$panel->setNoBackground();
return id(new AphrontNullView())
->appendChild(
array(
$error_view,
$panel,
));
}
private function renderKeyListView(AphrontRequest $request) {
$user = $request->getUser();
$keys = id(new PhabricatorUserSSHKey())->loadAllWhere(
'userPHID = %s',
$user->getPHID());
$rows = array();
foreach ($keys as $key) {
$rows[] = array(
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => $this->getPanelURI('?edit='.$key->getID()),
),
- phutil_escape_html($key->getName())),
+ $key->getName()),
phutil_escape_html($key->getKeyComment()),
phutil_escape_html($key->getKeyType()),
phabricator_date($key->getDateCreated(), $user),
phabricator_time($key->getDateCreated(), $user),
javelin_render_tag(
'a',
array(
'href' => $this->getPanelURI('?delete='.$key->getID()),
'class' => 'small grey button',
'sigil' => 'workflow',
),
'Delete'),
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString("You haven't added any SSH Public Keys.");
$table->setHeaders(
array(
'Name',
'Comment',
'Type',
'Created',
'Time',
'',
));
$table->setColumnClasses(
array(
'wide pri',
'',
'',
'',
'right',
'action',
));
$panel = new AphrontPanelView();
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => $this->getPanelURI('?edit=true'),
'class' => 'green button',
),
'Add New Public Key'));
$panel->setHeader('SSH Public Keys');
$panel->appendChild($table);
$panel->setNoBackground();
return $panel;
}
private function processDelete(
AphrontRequest $request,
PhabricatorUserSSHKey $key) {
$user = $request->getUser();
$name = phutil_escape_html($key->getName());
if ($request->isDialogFormPost()) {
$key->delete();
return id(new AphrontReloadResponse())
->setURI($this->getPanelURI());
}
$dialog = id(new AphrontDialogView())
->setUser($user)
->addHiddenInput('delete', $key->getID())
->setTitle('Really delete SSH Public Key?')
->appendChild(
'<p>The key "<strong>'.$name.'</strong>" will be permanently deleted, '.
'and you will not longer be able to use the corresponding private key '.
'to authenticate.</p>')
->addSubmitButton('Delete Public Key')
->addCancelButton($this->getPanelURI());
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
}
diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteListController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteListController.php
index 21ea6948be..19f16e9332 100644
--- a/src/applications/slowvote/controller/PhabricatorSlowvoteListController.php
+++ b/src/applications/slowvote/controller/PhabricatorSlowvoteListController.php
@@ -1,167 +1,167 @@
<?php
/**
* @group slowvote
*/
final class PhabricatorSlowvoteListController
extends PhabricatorSlowvoteController {
private $view;
const VIEW_ALL = 'all';
const VIEW_CREATED = 'created';
const VIEW_VOTED = 'voted';
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$views = array(
self::VIEW_ALL => 'All Slowvotes',
self::VIEW_CREATED => 'Created',
self::VIEW_VOTED => 'Voted In',
);
$view = isset($views[$this->view])
? $this->view
: self::VIEW_ALL;
$side_nav = $this->renderSideNav($views, $view);
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page'));
$pager->setURI($request->getRequestURI(), 'page');
$polls = $this->loadPolls($pager, $view);
$phids = mpull($polls, 'getAuthorPHID');
$handles = $this->loadViewerHandles($phids);
$rows = array();
foreach ($polls as $poll) {
$rows[] = array(
'V'.$poll->getID(),
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/V'.$poll->getID(),
),
- phutil_escape_html($poll->getQuestion())),
+ $poll->getQuestion()),
$handles[$poll->getAuthorPHID()]->renderLink(),
phabricator_date($poll->getDateCreated(), $user),
phabricator_time($poll->getDateCreated(), $user),
);
}
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'',
'pri wide',
'',
'',
'right',
));
$table->setHeaders(
array(
'ID',
'Poll',
'Author',
'Date',
'Time',
));
$panel = new AphrontPanelView();
$panel->setHeader($this->getTableHeader($view));
$panel->setCreateButton('Create Slowvote', '/vote/create/');
$panel->appendChild($table);
$panel->appendChild($pager);
$side_nav->appendChild($panel);
return $this->buildStandardPageResponse(
$side_nav,
array(
'title' => 'Slowvotes',
));
}
private function loadPolls(AphrontPagerView $pager, $view) {
$request = $this->getRequest();
$user = $request->getUser();
$poll = new PhabricatorSlowvotePoll();
$conn = $poll->establishConnection('r');
$offset = $pager->getOffset();
$limit = $pager->getPageSize() + 1;
switch ($view) {
case self::VIEW_ALL:
$data = queryfx_all(
$conn,
'SELECT * FROM %T ORDER BY id DESC LIMIT %d, %d',
$poll->getTableName(),
$offset,
$limit);
break;
case self::VIEW_CREATED:
$data = queryfx_all(
$conn,
'SELECT * FROM %T WHERE authorPHID = %s ORDER BY id DESC
LIMIT %d, %d',
$poll->getTableName(),
$user->getPHID(),
$offset,
$limit);
break;
case self::VIEW_VOTED:
$choice = new PhabricatorSlowvoteChoice();
$data = queryfx_all(
$conn,
'SELECT p.* FROM %T p JOIN %T o
ON o.pollID = p.id
WHERE o.authorPHID = %s
GROUP BY p.id
ORDER BY p.id DESC
LIMIT %d, %d',
$poll->getTableName(),
$choice->getTableName(),
$user->getPHID(),
$offset,
$limit);
break;
}
$data = $pager->sliceResults($data);
return $poll->loadAllFromArray($data);
}
private function renderSideNav(array $views, $view) {
$side_nav = new AphrontSideNavFilterView();
$side_nav->setBaseURI(new PhutilURI('/vote/view/'));
foreach ($views as $key => $name) {
$side_nav->addFilter($key, $name);
}
$side_nav->selectFilter($view, null);
return $side_nav;
}
private function getTableHeader($view) {
static $headers = array(
self::VIEW_ALL
=> 'Slowvotes Not Yet Consumed by the Ravages of Time',
self::VIEW_CREATED
=> 'Slowvotes Birthed from Your Noblest of Great Minds',
self::VIEW_VOTED
=> 'Slowvotes Within Which You Express Your Mighty Opinion',
);
return idx($headers, $view);
}
}
diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
index 4331bc27aa..04c4e2e566 100644
--- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
+++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php
@@ -1,174 +1,174 @@
<?php
/**
* @concrete-extensible
*/
class PhabricatorApplicationTransactionCommentView extends AphrontView {
private $submitButtonName;
private $action;
private $previewPanelID;
private $previewTimelineID;
private $previewToggleID;
private $formID;
private $statusID;
private $commentID;
private $draft;
public function setDraft(PhabricatorDraft $draft) {
$this->draft = $draft;
return $this;
}
public function getDraft() {
return $this->draft;
}
public function setSubmitButtonName($submit_button_name) {
$this->submitButtonName = $submit_button_name;
return $this;
}
public function getSubmitButtonName() {
return $this->submitButtonName;
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function getAction() {
return $this->action;
}
public function render() {
$data = array();
$comment = $this->renderCommentPanel();
$preview = $this->renderPreviewPanel();
Javelin::initBehavior(
'phabricator-transaction-comment-form',
array(
'formID' => $this->getFormID(),
'timelineID' => $this->getPreviewTimelineID(),
'panelID' => $this->getPreviewPanelID(),
'statusID' => $this->getStatusID(),
'commentID' => $this->getCommentID(),
'loadingString' => pht('Loading Preview...'),
'savingString' => pht('Saving Draft...'),
'draftString' => pht('Saved Draft'),
'actionURI' => $this->getAction(),
'draftKey' => $this->getDraft()->getDraftKey(),
));
return self::renderSingleView(
array(
$comment,
$preview,
));
}
private function renderCommentPanel() {
$status = phutil_render_tag(
'div',
array(
'id' => $this->getStatusID(),
),
'');
$draft_comment = '';
if ($this->getDraft()) {
$draft_comment = $this->getDraft()->getDraft();
}
return id(new AphrontFormView())
->setUser($this->getUser())
->setFlexible(true)
->addSigil('transaction-append')
->setWorkflow(true)
->setAction($this->getAction())
->setID($this->getFormID())
->appendChild(
id(new PhabricatorRemarkupControl())
->setID($this->getCommentID())
->setName('comment')
->setLabel(pht('Comment'))
->setUser($this->getUser())
->setValue($draft_comment))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($this->getSubmitButtonName()))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue($status));
}
private function renderPreviewPanel() {
$preview = id(new PhabricatorTimelineView())
->setID($this->getPreviewTimelineID());
- $header = phutil_render_tag(
+ $header = phutil_tag(
'div',
array(
'class' => 'phabricator-timeline-preview-header',
),
- phutil_escape_html(pht('Preview')));
+ pht('Preview'));
return phutil_render_tag(
'div',
array(
'id' => $this->getPreviewPanelID(),
'style' => 'display: none',
),
self::renderSingleView(
array(
$header,
$preview,
)));
}
private function getPreviewPanelID() {
if (!$this->previewPanelID) {
$this->previewPanelID = celerity_generate_unique_node_id();
}
return $this->previewPanelID;
}
private function getPreviewTimelineID() {
if (!$this->previewTimelineID) {
$this->previewTimelineID = celerity_generate_unique_node_id();
}
return $this->previewTimelineID;
}
private function getFormID() {
if (!$this->formID) {
$this->formID = celerity_generate_unique_node_id();
}
return $this->formID;
}
private function getStatusID() {
if (!$this->statusID) {
$this->statusID = celerity_generate_unique_node_id();
}
return $this->statusID;
}
private function getCommentID() {
if (!$this->commentID) {
$this->commentID = celerity_generate_unique_node_id();
}
return $this->commentID;
}
}
diff --git a/src/applications/uiexample/examples/PhabricatorBarePageExample.php b/src/applications/uiexample/examples/PhabricatorBarePageExample.php
index 5a3bf279c4..15aeb681ba 100644
--- a/src/applications/uiexample/examples/PhabricatorBarePageExample.php
+++ b/src/applications/uiexample/examples/PhabricatorBarePageExample.php
@@ -1,25 +1,25 @@
<?php
final class PhabricatorBarePageExample extends PhabricatorUIExample {
public function getName() {
return 'Bare Page';
}
public function getDescription() {
return 'This is a bare page.';
}
public function renderExample() {
$view = new PhabricatorBarePageView();
$view->appendChild(
- phutil_render_tag(
+ phutil_tag(
'h1',
array(),
- phutil_escape_html($this->getDescription())));
+ $this->getDescription()));
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
}
}
diff --git a/src/applications/uiexample/examples/PhabricatorButtonsExample.php b/src/applications/uiexample/examples/PhabricatorButtonsExample.php
index 0553a4b59f..d234bc2308 100644
--- a/src/applications/uiexample/examples/PhabricatorButtonsExample.php
+++ b/src/applications/uiexample/examples/PhabricatorButtonsExample.php
@@ -1,45 +1,45 @@
<?php
final class PhabricatorButtonsExample extends PhabricatorUIExample {
public function getName() {
return 'Buttons';
}
public function getDescription() {
return 'Use <tt>&lt;button&gt;</tt> to render buttons.';
}
public function renderExample() {
$request = $this->getRequest();
$user = $request->getUser();
$colors = array('', 'green', 'grey', 'black', 'disabled');
$sizes = array('', 'small');
$tags = array('a', 'button');
$view = array();
foreach ($tags as $tag) {
foreach ($colors as $color) {
foreach ($sizes as $size) {
$class = implode(' ', array($color, $size));
if ($tag == 'a') {
$class .= ' button';
}
- $view[] = phutil_render_tag(
+ $view[] = phutil_tag(
$tag,
array(
'class' => $class,
),
- phutil_escape_html(ucwords($size.' '.$color.' '.$tag)));
+ ucwords($size.' '.$color.' '.$tag));
$view[] = '<br /><br />';
}
}
}
return '<div style="margin: 1em 2em;">'.implode('', $view).'</div>';
}
}
diff --git a/src/applications/xhprof/view/PhabricatorXHProfProfileView.php b/src/applications/xhprof/view/PhabricatorXHProfProfileView.php
index f4df87a422..fd06a49fdc 100644
--- a/src/applications/xhprof/view/PhabricatorXHProfProfileView.php
+++ b/src/applications/xhprof/view/PhabricatorXHProfProfileView.php
@@ -1,28 +1,28 @@
<?php
abstract class PhabricatorXHProfProfileView extends AphrontView {
private $baseURI;
private $isFramed;
public function setIsFramed($is_framed) {
$this->isFramed = $is_framed;
return $this;
}
public function setBaseURI($uri) {
$this->baseURI = $uri;
return $this;
}
protected function renderSymbolLink($symbol) {
- return phutil_render_tag(
+ return phutil_tag(
'a',
array(
'href' => $this->baseURI.'?symbol='.$symbol,
'target' => $this->isFramed ? '_top' : null,
),
- phutil_escape_html($symbol));
+ $symbol);
}
}
diff --git a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleMention.php b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleMention.php
index 1591d0b62b..f68d05fb64 100644
--- a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleMention.php
+++ b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleMention.php
@@ -1,130 +1,130 @@
<?php
/**
* @group markup
*/
final class PhabricatorRemarkupRuleMention
extends PhutilRemarkupRule {
const KEY_RULE_MENTION = 'rule.mention';
const KEY_RULE_MENTION_ORIGINAL = 'rule.mention.original';
const KEY_MENTIONED = 'phabricator.mentioned-user-phids';
// NOTE: The negative lookbehind prevents matches like "mail@lists", while
// allowing constructs like "@tomo/@mroch". Since we now allow periods in
// usernames, we can't resonably distinguish that "@company.com" isn't a
// username, so we'll incorrectly pick it up, but there's little to be done
// about that. We forbid terminal periods so that we can correctly capture
// "@joe" instead of "@joe." in "Hey, @joe.".
const REGEX = '/(?<!\w)@([a-zA-Z0-9._-]*[a-zA-Z0-9_-])/';
public function apply($text) {
return preg_replace_callback(
self::REGEX,
array($this, 'markupMention'),
$text);
}
private function markupMention($matches) {
$engine = $this->getEngine();
$token = $engine->storeText('');
// Store the original text exactly so we can preserve casing if it doesn't
// resolve into a username.
$original_key = self::KEY_RULE_MENTION_ORIGINAL;
$original = $engine->getTextMetadata($original_key, array());
$original[$token] = $matches[1];
$engine->setTextMetadata($original_key, $original);
$metadata_key = self::KEY_RULE_MENTION;
$metadata = $engine->getTextMetadata($metadata_key, array());
$username = strtolower($matches[1]);
if (empty($metadata[$username])) {
$metadata[$username] = array();
}
$metadata[$username][] = $token;
$engine->setTextMetadata($metadata_key, $metadata);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$metadata_key = self::KEY_RULE_MENTION;
$metadata = $engine->getTextMetadata($metadata_key, array());
if (empty($metadata)) {
// No mentions, or we already processed them.
return;
}
$original_key = self::KEY_RULE_MENTION_ORIGINAL;
$original = $engine->getTextMetadata($original_key, array());
$usernames = array_keys($metadata);
$user_table = new PhabricatorUser();
$real_user_names = queryfx_all(
$user_table->establishConnection('r'),
'SELECT username, phid, realName, isDisabled
FROM %T
WHERE username IN (%Ls)',
$user_table->getTableName(),
$usernames);
$actual_users = array();
$mentioned_key = self::KEY_MENTIONED;
$mentioned = $engine->getTextMetadata($mentioned_key, array());
foreach ($real_user_names as $row) {
$actual_users[strtolower($row['username'])] = $row;
$mentioned[$row['phid']] = $row['phid'];
}
$engine->setTextMetadata($mentioned_key, $mentioned);
foreach ($metadata as $username => $tokens) {
$exists = isset($actual_users[$username]);
if (!$exists) {
$class = 'phabricator-remarkup-mention-unknown';
} else if ($actual_users[$username]['isDisabled']) {
$class = 'phabricator-remarkup-mention-disabled';
} else {
$class = 'phabricator-remarkup-mention-exists';
}
if ($exists) {
- $tag = phutil_render_tag(
+ $tag = phutil_tag(
'a',
array(
'class' => $class,
'href' => '/p/'.$actual_users[$username]['username'].'/',
'target' => '_blank',
'title' => $actual_users[$username]['realName'],
),
- phutil_escape_html('@'.$actual_users[$username]['username']));
+ '@'.$actual_users[$username]['username']);
foreach ($tokens as $token) {
$engine->overwriteStoredText($token, $tag);
}
} else {
// NOTE: The structure here is different from the 'exists' branch,
// because we want to preserve the original text capitalization and it
// may differ for each token.
foreach ($tokens as $token) {
- $tag = phutil_render_tag(
+ $tag = phutil_tag(
'span',
array(
'class' => $class,
),
- phutil_escape_html('@'.idx($original, $token, $username)));
+ '@'.idx($original, $token, $username));
$engine->overwriteStoredText($token, $tag);
}
}
}
// Don't re-process these mentions.
$engine->setTextMetadata($metadata_key, array());
}
}
diff --git a/src/infrastructure/markup/rule/PhabricatorRemarkupRulePhriction.php b/src/infrastructure/markup/rule/PhabricatorRemarkupRulePhriction.php
index 04a10bed91..410d507b47 100644
--- a/src/infrastructure/markup/rule/PhabricatorRemarkupRulePhriction.php
+++ b/src/infrastructure/markup/rule/PhabricatorRemarkupRulePhriction.php
@@ -1,45 +1,45 @@
<?php
/**
* @group markup
*/
final class PhabricatorRemarkupRulePhriction
extends PhutilRemarkupRule {
public function apply($text) {
return preg_replace_callback(
'@\B\\[\\[([^|\\]]+)(?:\\|([^\\]]+))?\\]\\]\B@U',
array($this, 'markupDocumentLink'),
$text);
}
public function markupDocumentLink($matches) {
$link = trim($matches[1]);
$name = trim(idx($matches, 2, $link));
$name = explode('/', trim($name, '/'));
$name = end($name);
$uri = new PhutilURI($link);
$slug = $uri->getPath();
$fragment = $uri->getFragment();
$slug = PhabricatorSlug::normalize($slug);
$slug = PhrictionDocument::getSlugURI($slug);
$href = (string) id(new PhutilURI($slug))->setFragment($fragment);
if ($this->getEngine()->getState('toc')) {
$text = phutil_escape_html($name);
} else {
- $text = phutil_render_tag(
+ $text = phutil_tag(
'a',
array(
'href' => $href,
'class' => 'phriction-link',
),
- phutil_escape_html($name));
+ $name);
}
return $this->getEngine()->storeText($text);
}
}
diff --git a/src/view/control/AphrontAttachedFileView.php b/src/view/control/AphrontAttachedFileView.php
index 499162ba63..a728515387 100644
--- a/src/view/control/AphrontAttachedFileView.php
+++ b/src/view/control/AphrontAttachedFileView.php
@@ -1,57 +1,57 @@
<?php
final class AphrontAttachedFileView extends AphrontView {
private $file;
public function setFile(PhabricatorFile $file) {
$this->file = $file;
return $this;
}
public function render() {
require_celerity_resource('aphront-attached-file-view-css');
$file = $this->file;
$phid = $file->getPHID();
$thumb = phutil_tag(
'img',
array(
'src' => $file->getThumb60x45URI(),
'width' => 60,
'height' => 45,
));
- $name = phutil_render_tag(
+ $name = phutil_tag(
'a',
array(
'href' => $file->getViewURI(),
'target' => '_blank',
),
- phutil_escape_html($file->getName()));
+ $file->getName());
$size = number_format($file->getByteSize()).' bytes';
$remove = javelin_render_tag(
'a',
array(
'class' => 'button grey',
'sigil' => 'aphront-attached-file-view-remove',
// NOTE: Using 'ref' here instead of 'meta' because the file upload
// endpoint doesn't receive request metadata and thus can't generate
// a valid response with node metadata.
'ref' => $file->getPHID(),
),
"\xE2\x9C\x96"); // "Heavy Multiplication X"
return
'<table class="aphront-attached-file-view">
<tr>
<td>'.$thumb.'</td>
<th><strong>'.$name.'</strong><br />'.$size.'</th>
<td class="aphront-attached-file-view-remove">'.$remove.'</td>
</tr>
</table>';
}
}
diff --git a/src/view/control/AphrontTableView.php b/src/view/control/AphrontTableView.php
index b5f4f826a4..c3bffde73f 100644
--- a/src/view/control/AphrontTableView.php
+++ b/src/view/control/AphrontTableView.php
@@ -1,320 +1,320 @@
<?php
final class AphrontTableView extends AphrontView {
protected $data;
protected $headers;
protected $shortHeaders;
protected $rowClasses = array();
protected $columnClasses = array();
protected $cellClasses = array();
protected $zebraStripes = true;
protected $noDataString;
protected $className;
protected $columnVisibility = array();
private $deviceVisibility = array();
protected $sortURI;
protected $sortParam;
protected $sortSelected;
protected $sortReverse;
protected $sortValues;
private $deviceReadyTable;
public function __construct(array $data) {
$this->data = $data;
}
public function setHeaders(array $headers) {
$this->headers = $headers;
return $this;
}
public function setColumnClasses(array $column_classes) {
$this->columnClasses = $column_classes;
return $this;
}
public function setRowClasses(array $row_classes) {
$this->rowClasses = $row_classes;
return $this;
}
public function setCellClasses(array $cell_classes) {
$this->cellClasses = $cell_classes;
return $this;
}
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
}
public function setClassName($class_name) {
$this->className = $class_name;
return $this;
}
public function setZebraStripes($zebra_stripes) {
$this->zebraStripes = $zebra_stripes;
return $this;
}
public function setColumnVisibility(array $visibility) {
$this->columnVisibility = $visibility;
return $this;
}
public function setDeviceVisibility(array $device_visibility) {
$this->deviceVisibility = $device_visibility;
return $this;
}
public function setDeviceReadyTable($ready) {
$this->deviceReadyTable = $ready;
return $this;
}
public function setShortHeaders(array $short_headers) {
$this->shortHeaders = $short_headers;
return $this;
}
/**
* Parse a sorting parameter:
*
* list($sort, $reverse) = AphrontTableView::parseSortParam($sort_param);
*
* @param string Sort request parameter.
* @return pair Sort value, sort direction.
*/
public static function parseSort($sort) {
return array(ltrim($sort, '-'), preg_match('/^-/', $sort));
}
public function makeSortable(
PhutilURI $base_uri,
$param,
$selected,
$reverse,
array $sort_values) {
$this->sortURI = $base_uri;
$this->sortParam = $param;
$this->sortSelected = $selected;
$this->sortReverse = $reverse;
$this->sortValues = array_values($sort_values);
return $this;
}
public function render() {
require_celerity_resource('aphront-table-view-css');
$table_class = $this->className;
if ($this->deviceReadyTable) {
$table_class .= ' aphront-table-view-device-ready';
}
if ($table_class !== null) {
$table_class = ' class="aphront-table-view '.$table_class.'"';
} else {
$table_class = ' class="aphront-table-view"';
}
$table = array('<table'.$table_class.'>');
$col_classes = array();
foreach ($this->columnClasses as $key => $class) {
if (strlen($class)) {
$col_classes[] = $class;
} else {
$col_classes[] = null;
}
}
$visibility = array_values($this->columnVisibility);
$device_visibility = array_values($this->deviceVisibility);
$headers = $this->headers;
$short_headers = $this->shortHeaders;
$sort_values = $this->sortValues;
if ($headers) {
while (count($headers) > count($visibility)) {
$visibility[] = true;
}
while (count($headers) > count($device_visibility)) {
$device_visibility[] = true;
}
while (count($headers) > count($short_headers)) {
$short_headers[] = null;
}
while (count($headers) > count($sort_values)) {
$sort_values[] = null;
}
$table[] = '<tr>';
foreach ($headers as $col_num => $header) {
if (!$visibility[$col_num]) {
continue;
}
$classes = array();
if (!empty($col_classes[$col_num])) {
$classes[] = $col_classes[$col_num];
}
if (empty($device_visiblity[$col_num])) {
$classes[] = 'aphront-table-nodevice';
}
if ($sort_values[$col_num] !== null) {
$classes[] = 'aphront-table-view-sortable';
$sort_value = $sort_values[$col_num];
$sort_glyph_class = 'aphront-table-down-sort';
if ($sort_value == $this->sortSelected) {
if ($this->sortReverse) {
$sort_glyph_class = 'aphront-table-up-sort';
} else if (!$this->sortReverse) {
$sort_value = '-'.$sort_value;
}
$classes[] = 'aphront-table-view-sortable-selected';
}
$sort_glyph = phutil_render_tag(
'span',
array(
'class' => $sort_glyph_class,
),
'');
$header = phutil_render_tag(
'a',
array(
'href' => $this->sortURI->alter($this->sortParam, $sort_value),
'class' => 'aphront-table-view-sort-link',
),
$header.' '.$sort_glyph);
}
if ($classes) {
$class = ' class="'.implode(' ', $classes).'"';
} else {
$class = null;
}
if ($short_headers[$col_num] !== null) {
$header_nodevice = phutil_render_tag(
'span',
array(
'class' => 'aphront-table-view-nodevice',
),
$header);
- $header_device = phutil_render_tag(
+ $header_device = phutil_tag(
'span',
array(
'class' => 'aphront-table-view-device',
),
- phutil_escape_html($short_headers[$col_num]));
+ $short_headers[$col_num]);
$header = $header_nodevice.$header_device;
}
$table[] = '<th'.$class.'>'.$header.'</th>';
}
$table[] = '</tr>';
}
foreach ($col_classes as $key => $value) {
if (($sort_values[$key] !== null) &&
($sort_values[$key] == $this->sortSelected)) {
$value = trim($value.' sorted-column');
}
if ($value !== null) {
$col_classes[$key] = $value;
}
}
$data = $this->data;
if ($data) {
$row_num = 0;
foreach ($data as $row) {
while (count($row) > count($col_classes)) {
$col_classes[] = null;
}
while (count($row) > count($visibility)) {
$visibility[] = true;
}
$class = idx($this->rowClasses, $row_num);
if ($this->zebraStripes && ($row_num % 2)) {
if ($class !== null) {
$class = 'alt alt-'.$class;
} else {
$class = 'alt';
}
}
if ($class !== null) {
$class = ' class="'.$class.'"';
}
$table[] = '<tr'.$class.'>';
// NOTE: Use of a separate column counter is to allow this to work
// correctly if the row data has string or non-sequential keys.
$col_num = 0;
foreach ($row as $value) {
if (!$visibility[$col_num]) {
++$col_num;
continue;
}
$class = $col_classes[$col_num];
if (!empty($this->cellClasses[$row_num][$col_num])) {
$class = trim($class.' '.$this->cellClasses[$row_num][$col_num]);
}
if ($class !== null) {
$table[] = '<td class="'.$class.'">';
} else {
$table[] = '<td>';
}
$table[] = $value.'</td>';
++$col_num;
}
++$row_num;
}
} else {
$colspan = max(count(array_filter($visibility)), 1);
$table[] =
'<tr class="no-data"><td colspan="'.$colspan.'">'.
coalesce($this->noDataString, 'No data available.').
'</td></tr>';
}
$table[] = '</table>';
$html = implode('', $table);
return '<div class="aphront-table-wrap">'.$html.'</div>';
}
public static function renderSingleDisplayLine($line) {
// TODO: Is there a cleaner way to do this? We use a relative div with
// overflow hidden to provide the bounds, and an absolute span with
// white-space: pre to prevent wrapping. We need to append a character
// (&nbsp; -- nonbreaking space) afterward to give the bounds div height
// (alternatively, we could hard-code the line height). This is gross but
// it's not clear that there's a better appraoch.
return phutil_render_tag(
'div',
array(
'class' => 'single-display-line-bounds',
),
phutil_render_tag(
'span',
array(
'class' => 'single-display-line-content',
),
$line).'&nbsp;');
}
}
diff --git a/src/view/form/AphrontErrorView.php b/src/view/form/AphrontErrorView.php
index 632561ddec..d3dc20a0a3 100644
--- a/src/view/form/AphrontErrorView.php
+++ b/src/view/form/AphrontErrorView.php
@@ -1,91 +1,91 @@
<?php
final class AphrontErrorView extends AphrontView {
const SEVERITY_ERROR = 'error';
const SEVERITY_WARNING = 'warning';
const SEVERITY_NOTICE = 'notice';
const SEVERITY_NODATA = 'nodata';
private $title;
private $errors;
private $severity;
private $id;
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setSeverity($severity) {
$this->severity = $severity;
return $this;
}
public function setErrors(array $errors) {
$this->errors = $errors;
return $this;
}
public function setID($id) {
$this->id = $id;
return $this;
}
final public function render() {
require_celerity_resource('aphront-error-view-css');
$errors = $this->errors;
if ($errors) {
$list = array();
foreach ($errors as $error) {
- $list[] = phutil_render_tag(
+ $list[] = phutil_tag(
'li',
array(),
- phutil_escape_html($error));
+ $error);
}
$list = phutil_render_tag(
'ul',
array(
'class' => 'aphront-error-view-list',
),
implode("\n", $list));
} else {
$list = null;
}
$title = $this->title;
if (strlen($title)) {
- $title = phutil_render_tag(
+ $title = phutil_tag(
'h1',
array(
'class' => 'aphront-error-view-head',
),
- phutil_escape_html($title));
+ $title);
} else {
$title = null;
}
$this->severity = nonempty($this->severity, self::SEVERITY_ERROR);
$more_classes = array();
$more_classes[] = 'aphront-error-severity-'.$this->severity;
$more_classes = implode(' ', $more_classes);
return phutil_render_tag(
'div',
array(
'id' => $this->id,
'class' => 'aphront-error-view '.$more_classes,
),
$title.
phutil_render_tag(
'div',
array(
'class' => 'aphront-error-view-body',
),
$this->renderChildren().
$list));
}
}
diff --git a/src/view/form/control/AphrontFormCheckboxControl.php b/src/view/form/control/AphrontFormCheckboxControl.php
index d2d05e77a3..00a85f52a9 100644
--- a/src/view/form/control/AphrontFormCheckboxControl.php
+++ b/src/view/form/control/AphrontFormCheckboxControl.php
@@ -1,53 +1,53 @@
<?php
final class AphrontFormCheckboxControl extends AphrontFormControl {
private $boxes = array();
public function addCheckbox($name, $value, $label, $checked = false) {
$this->boxes[] = array(
'name' => $name,
'value' => $value,
'label' => $label,
'checked' => $checked,
);
return $this;
}
protected function getCustomControlClass() {
return 'aphront-form-control-checkbox';
}
protected function renderInput() {
$rows = array();
foreach ($this->boxes as $box) {
$id = celerity_generate_unique_node_id();
$checkbox = phutil_tag(
'input',
array(
'id' => $id,
'type' => 'checkbox',
'name' => $box['name'],
'value' => $box['value'],
'checked' => $box['checked'] ? 'checked' : null,
'disabled' => $this->getDisabled() ? 'disabled' : null,
));
- $label = phutil_render_tag(
+ $label = phutil_tag(
'label',
array(
'for' => $id,
),
- phutil_escape_html($box['label']));
+ $box['label']);
$rows[] =
'<tr>'.
'<td>'.$checkbox.'</td>'.
'<th>'.$label.'</th>'.
'</tr>';
}
return
'<table class="aphront-form-control-checkbox-layout">'.
implode("\n", $rows).
'</table>';
}
}
diff --git a/src/view/form/control/AphrontFormRadioButtonControl.php b/src/view/form/control/AphrontFormRadioButtonControl.php
index e15dd03c8d..6d6547c8b6 100644
--- a/src/view/form/control/AphrontFormRadioButtonControl.php
+++ b/src/view/form/control/AphrontFormRadioButtonControl.php
@@ -1,64 +1,64 @@
<?php
final class AphrontFormRadioButtonControl extends AphrontFormControl {
private $buttons = array();
public function addButton($value, $label, $caption, $class = null) {
$this->buttons[] = array(
'value' => $value,
'label' => $label,
'caption' => $caption,
'class' => $class,
);
return $this;
}
protected function getCustomControlClass() {
return 'aphront-form-control-radio';
}
protected function renderInput() {
$rows = array();
foreach ($this->buttons as $button) {
$id = celerity_generate_unique_node_id();
$radio = phutil_tag(
'input',
array(
'id' => $id,
'type' => 'radio',
'name' => $this->getName(),
'value' => $button['value'],
'checked' => ($button['value'] == $this->getValue())
? 'checked'
: null,
'disabled' => $this->getDisabled() ? 'disabled' : null,
));
- $label = phutil_render_tag(
+ $label = phutil_tag(
'label',
array(
'for' => $id,
'class' => $button['class'],
),
- phutil_escape_html($button['label']));
+ $button['label']);
if (strlen($button['caption'])) {
$label .=
'<div class="aphront-form-radio-caption">'.
phutil_escape_html($button['caption']).
'</div>';
}
$rows[] =
'<tr>'.
'<td>'.$radio.'</td>'.
'<th>'.$label.'</th>'.
'</tr>';
}
return
'<table class="aphront-form-control-radio-layout">'.
implode("\n", $rows).
'</table>';
}
}
diff --git a/src/view/form/control/AphrontFormSelectControl.php b/src/view/form/control/AphrontFormSelectControl.php
index 419d8ea352..bc01e742b3 100644
--- a/src/view/form/control/AphrontFormSelectControl.php
+++ b/src/view/form/control/AphrontFormSelectControl.php
@@ -1,67 +1,67 @@
<?php
final class AphrontFormSelectControl extends AphrontFormControl {
protected function getCustomControlClass() {
return 'aphront-form-control-select';
}
private $options;
public function setOptions(array $options) {
$this->options = $options;
return $this;
}
public function getOptions() {
return $this->options;
}
protected function renderInput() {
return self::renderSelectTag(
$this->getValue(),
$this->getOptions(),
array(
'name' => $this->getName(),
'disabled' => $this->getDisabled() ? 'disabled' : null,
'id' => $this->getID(),
));
}
public static function renderSelectTag(
$selected,
array $options,
array $attrs = array()) {
$option_tags = self::renderOptions($selected, $options);
return javelin_render_tag(
'select',
$attrs,
implode("\n", $option_tags));
}
private static function renderOptions($selected, array $options) {
$tags = array();
foreach ($options as $value => $thing) {
if (is_array($thing)) {
$tags[] = phutil_render_tag(
'optgroup',
array(
'label' => $value,
),
implode("\n", self::renderOptions($selected, $thing)));
} else {
- $tags[] = phutil_render_tag(
+ $tags[] = phutil_tag(
'option',
array(
'selected' => ($value == $selected) ? 'selected' : null,
'value' => $value,
),
- phutil_escape_html($thing));
+ $thing);
}
}
return $tags;
}
}
diff --git a/src/view/form/control/AphrontFormSubmitControl.php b/src/view/form/control/AphrontFormSubmitControl.php
index 9ec7593dcd..c2ccb915e5 100644
--- a/src/view/form/control/AphrontFormSubmitControl.php
+++ b/src/view/form/control/AphrontFormSubmitControl.php
@@ -1,36 +1,36 @@
<?php
final class AphrontFormSubmitControl extends AphrontFormControl {
protected $cancelButton;
public function addCancelButton($href, $label = 'Cancel') {
- $this->cancelButton = phutil_render_tag(
+ $this->cancelButton = phutil_tag(
'a',
array(
'href' => $href,
'class' => 'button grey',
),
- phutil_escape_html($label));
+ $label);
return $this;
}
protected function getCustomControlClass() {
return 'aphront-form-control-submit';
}
protected function renderInput() {
$submit_button = null;
if ($this->getValue()) {
- $submit_button = phutil_render_tag(
+ $submit_button = phutil_tag(
'button',
array(
'name' => '__submit__',
'disabled' => $this->getDisabled() ? 'disabled' : null,
),
- phutil_escape_html($this->getValue()));
+ $this->getValue());
}
return $submit_button.$this->cancelButton;
}
}
diff --git a/src/view/form/control/AphrontFormTextAreaControl.php b/src/view/form/control/AphrontFormTextAreaControl.php
index 921d8caef3..311beaea8e 100644
--- a/src/view/form/control/AphrontFormTextAreaControl.php
+++ b/src/view/form/control/AphrontFormTextAreaControl.php
@@ -1,78 +1,78 @@
<?php
/**
* @concrete-extensible
*/
class AphrontFormTextAreaControl extends AphrontFormControl {
const HEIGHT_VERY_SHORT = 'very-short';
const HEIGHT_SHORT = 'short';
const HEIGHT_VERY_TALL = 'very-tall';
private $height;
private $readOnly;
private $customClass;
private $placeHolder;
public function setPlaceHolder($place_holder) {
$this->placeHolder = $place_holder;
return $this;
}
private function getPlaceHolder() {
return $this->placeHolder;
}
public function setHeight($height) {
$this->height = $height;
return $this;
}
public function setReadOnly($read_only) {
$this->readOnly = $read_only;
return $this;
}
protected function getReadOnly() {
return $this->readOnly;
}
protected function getCustomControlClass() {
return 'aphront-form-control-textarea';
}
public function setCustomClass($custom_class) {
$this->customClass = $custom_class;
return $this;
}
protected function renderInput() {
$height_class = null;
switch ($this->height) {
case self::HEIGHT_VERY_SHORT:
case self::HEIGHT_SHORT:
case self::HEIGHT_VERY_TALL:
$height_class = 'aphront-textarea-'.$this->height;
break;
}
$classes = array();
$classes[] = $height_class;
$classes[] = $this->customClass;
$classes = trim(implode(' ', $classes));
- return phutil_render_tag(
+ return phutil_tag(
'textarea',
array(
'name' => $this->getName(),
'disabled' => $this->getDisabled() ? 'disabled' : null,
'readonly' => $this->getReadonly() ? 'readonly' : null,
'class' => $classes,
'style' => $this->getControlStyle(),
'id' => $this->getID(),
'placeholder' => $this->getPlaceHolder(),
),
- phutil_escape_html($this->getValue()));
+ $this->getValue());
}
}
diff --git a/src/view/form/control/AphrontFormToggleButtonsControl.php b/src/view/form/control/AphrontFormToggleButtonsControl.php
index f4ec32bacd..43f83ed1b9 100644
--- a/src/view/form/control/AphrontFormToggleButtonsControl.php
+++ b/src/view/form/control/AphrontFormToggleButtonsControl.php
@@ -1,52 +1,52 @@
<?php
final class AphrontFormToggleButtonsControl extends AphrontFormControl {
private $baseURI;
private $param;
private $buttons;
public function setBaseURI(PhutilURI $uri, $param) {
$this->baseURI = $uri;
$this->param = $param;
return $this;
}
public function setButtons(array $buttons) {
$this->buttons = $buttons;
return $this;
}
protected function getCustomControlClass() {
return 'aphront-form-control-togglebuttons';
}
protected function renderInput() {
if (!$this->baseURI) {
throw new Exception('Call setBaseURI() before render()!');
}
$selected = $this->getValue();
$out = array();
foreach ($this->buttons as $value => $label) {
if ($value == $selected) {
$more = ' toggle-selected toggle-fixed';
} else {
$more = null;
}
- $out[] = phutil_render_tag(
+ $out[] = phutil_tag(
'a',
array(
'class' => 'toggle'.$more,
'href' => $this->baseURI->alter($this->param, $value),
),
- phutil_escape_html($label));
+ $label);
}
return implode('', $out);
}
}
diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php
index c148cc380d..30bbd218f2 100644
--- a/src/view/layout/PhabricatorActionView.php
+++ b/src/view/layout/PhabricatorActionView.php
@@ -1,138 +1,138 @@
<?php
final class PhabricatorActionView extends AphrontView {
private $name;
private $icon;
private $href;
private $disabled;
private $workflow;
private $renderAsForm;
public function setHref($href) {
$this->href = $href;
return $this;
}
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function setDisabled($disabled) {
$this->disabled = $disabled;
return $this;
}
public function setWorkflow($workflow) {
$this->workflow = $workflow;
return $this;
}
public function setRenderAsForm($form) {
$this->renderAsForm = $form;
return $this;
}
public function render() {
$icon = null;
if ($this->icon) {
$suffix = '';
if ($this->disabled) {
$suffix = '-grey';
}
require_celerity_resource('sprite-icon-css');
$icon = phutil_render_tag(
'span',
array(
'class' => 'phabricator-action-view-icon sprite-icon '.
'action-'.$this->icon.$suffix,
),
'');
}
if ($this->href) {
if ($this->renderAsForm) {
if (!$this->user) {
throw new Exception(
'Call setUser() when rendering an action as a form.');
}
$item = javelin_render_tag(
'button',
array(
'class' => 'phabricator-action-view-item',
),
phutil_escape_html($this->name));
$item = phabricator_render_form(
$this->user,
array(
'action' => $this->href,
'method' => 'POST',
'sigil' => $this->workflow ? 'workflow' : null,
),
$item);
} else {
$item = javelin_render_tag(
'a',
array(
'href' => $this->href,
'class' => 'phabricator-action-view-item',
'sigil' => $this->workflow ? 'workflow' : null,
),
phutil_escape_html($this->name));
}
} else {
- $item = phutil_render_tag(
+ $item = phutil_tag(
'span',
array(
'class' => 'phabricator-action-view-item',
),
- phutil_escape_html($this->name));
+ $this->name);
}
$classes = array();
$classes[] = 'phabricator-action-view';
if ($this->disabled) {
$classes[] = 'phabricator-action-view-disabled';
}
return phutil_render_tag(
'li',
array(
'class' => implode(' ', $classes),
),
$icon.$item);
}
public static function getAvailableIcons() {
$root = dirname(phutil_get_library_root('phabricator'));
$path = $root.'/resources/sprite/manifest/icon.json';
$data = Filesystem::readFile($path);
$manifest = json_decode($data, true);
$results = array();
$prefix = 'action-';
foreach ($manifest['sprites'] as $sprite) {
$name = $sprite['name'];
if (preg_match('/-(white|grey)$/', $name)) {
continue;
}
if (!strncmp($name, $prefix, strlen($prefix))) {
$results[] = substr($name, strlen($prefix));
}
}
return $results;
}
}
diff --git a/src/view/layout/PhabricatorHeaderView.php b/src/view/layout/PhabricatorHeaderView.php
index 97d26bd6b9..8d2d7a911b 100644
--- a/src/view/layout/PhabricatorHeaderView.php
+++ b/src/view/layout/PhabricatorHeaderView.php
@@ -1,61 +1,61 @@
<?php
final class PhabricatorHeaderView extends AphrontView {
private $objectName;
private $header;
private $tags = array();
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setObjectName($object_name) {
$this->objectName = $object_name;
return $this;
}
public function addTag(PhabricatorTagView $tag) {
$this->tags[] = $tag;
return $this;
}
public function render() {
require_celerity_resource('phabricator-header-view-css');
$header = phutil_escape_html($this->header);
if ($this->objectName) {
- $header = phutil_render_tag(
+ $header = phutil_tag(
'a',
array(
'href' => '/'.$this->objectName,
),
- phutil_escape_html($this->objectName)).' '.$header;
+ $this->objectName).' '.$header;
}
if ($this->tags) {
$header .= phutil_render_tag(
'span',
array(
'class' => 'phabricator-header-tags',
),
self::renderSingleView($this->tags));
}
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-header-shell',
),
phutil_render_tag(
'h1',
array(
'class' => 'phabricator-header-view',
),
$header));
}
}
diff --git a/src/view/layout/PhabricatorMenuItemView.php b/src/view/layout/PhabricatorMenuItemView.php
index 69f4d1db76..768ee2a709 100644
--- a/src/view/layout/PhabricatorMenuItemView.php
+++ b/src/view/layout/PhabricatorMenuItemView.php
@@ -1,131 +1,131 @@
<?php
final class PhabricatorMenuItemView extends AphrontTagView {
const TYPE_LINK = 'type-link';
const TYPE_SPACER = 'type-spacer';
const TYPE_LABEL = 'type-label';
private $name;
private $href;
private $type = self::TYPE_LINK;
private $isExternal;
private $key;
private $sortOrder = 1.0;
private $icon;
private $selected;
public function setProperty($property) {
$this->property = $property;
return $this;
}
public function getProperty() {
return $this->property;
}
public function setSelected($selected) {
$this->selected = $selected;
return $this;
}
public function getSelected() {
return $this->selected;
}
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function getIcon() {
return $this->icon;
}
public function setKey($key) {
$this->key = (string)$key;
return $this;
}
public function getKey() {
return $this->key;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setHref($href) {
$this->href = $href;
return $this;
}
public function getHref() {
return $this->href;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setIsExternal($is_external) {
$this->isExternal = $is_external;
return $this;
}
public function getIsExternal() {
return $this->isExternal;
}
public function setSortOrder($order) {
$this->sortOrder = $order;
return $this;
}
public function getSortOrder() {
return $this->sortOrder;
}
protected function getTagName() {
return $this->href ? 'a' : 'div';
}
protected function getTagAttributes() {
return array(
'class' => array(
'phabricator-menu-item-view',
'phabricator-menu-item-'.$this->type,
),
'href' => $this->href,
);
}
protected function getTagContent() {
$name = null;
if ($this->name) {
$external = null;
if ($this->isExternal) {
$external = " \xE2\x86\x97";
}
- $name = phutil_render_tag(
+ $name = phutil_tag(
'span',
array(
'class' => 'phabricator-menu-item-name',
),
- phutil_escape_html($this->name.$external));
+ $this->name.$external);
}
return $this->renderChildren().$name;
}
}
diff --git a/src/view/layout/PhabricatorObjectItemListView.php b/src/view/layout/PhabricatorObjectItemListView.php
index 8b5f933d7a..a145b7ba43 100644
--- a/src/view/layout/PhabricatorObjectItemListView.php
+++ b/src/view/layout/PhabricatorObjectItemListView.php
@@ -1,66 +1,66 @@
<?php
final class PhabricatorObjectItemListView extends AphrontView {
private $header;
private $items;
private $pager;
private $noDataString;
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setPager($pager) {
$this->pager = $pager;
return $this;
}
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
}
public function addItem(PhabricatorObjectItemView $item) {
$this->items[] = $item;
return $this;
}
public function render() {
require_celerity_resource('phabricator-object-item-list-view-css');
$header = null;
if (strlen($this->header)) {
- $header = phutil_render_tag(
+ $header = phutil_tag(
'h1',
array(
'class' => 'phabricator-object-item-list-header',
),
- phutil_escape_html($this->header));
+ $this->header);
}
if ($this->items) {
$items = $this->renderSingleView($this->items);
} else {
$string = nonempty($this->noDataString, pht('No data.'));
$items = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NODATA)
->appendChild(phutil_escape_html($string))
->render();
}
$pager = null;
if ($this->pager) {
$pager = $this->renderSingleView($this->pager);
}
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-object-item-list-view',
),
$header.$items.$pager);
}
}
diff --git a/src/view/layout/PhabricatorObjectItemView.php b/src/view/layout/PhabricatorObjectItemView.php
index ae40e59117..6161073f84 100644
--- a/src/view/layout/PhabricatorObjectItemView.php
+++ b/src/view/layout/PhabricatorObjectItemView.php
@@ -1,176 +1,176 @@
<?php
final class PhabricatorObjectItemView extends AphrontView {
private $header;
private $href;
private $attributes = array();
private $details = array();
private $dates = array();
private $icons = array();
private $barColor;
private $object;
private $effect;
public function setEffect($effect) {
$this->effect = $effect;
return $this;
}
public function getEffect() {
return $this->effect;
}
public function setObject($object) {
$this->object = $object;
return $this;
}
public function getObject() {
return $this->object;
}
public function setHref($href) {
$this->href = $href;
return $this;
}
public function getHref() {
return $this->href;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function getHeader() {
return $this->header;
}
public function addIcon($icon, $label = null) {
$this->icons[] = array(
'icon' => $icon,
'label' => $label,
);
return $this;
}
public function setBarColor($bar_color) {
$this->barColor = $bar_color;
return $this;
}
public function getBarColor() {
return $this->barColor;
}
public function addAttribute($attribute) {
$this->attributes[] = $attribute;
return $this;
}
public function render() {
- $header = phutil_render_tag(
+ $header = phutil_tag(
'a',
array(
'href' => $this->href,
'class' => 'phabricator-object-item-name',
),
- phutil_escape_html($this->header));
+ $this->header);
$icons = null;
if ($this->icons) {
$icon_list = array();
foreach ($this->icons as $spec) {
$icon = $spec['icon'];
$icon = phutil_render_tag(
'span',
array(
'class' => 'phabricator-object-item-icon-image '.
'sprite-icon action-'.$icon,
),
'');
- $label = phutil_render_tag(
+ $label = phutil_tag(
'span',
array(
'class' => 'phabricator-object-item-icon-label',
),
- phutil_escape_html($spec['label']));
+ $spec['label']);
$icon_list[] = phutil_render_tag(
'li',
array(
'class' => 'phabricator-object-item-icon',
),
$label.$icon);
}
$icons = phutil_render_tag(
'ul',
array(
'class' => 'phabricator-object-item-icons',
),
implode('', $icon_list));
}
$attrs = null;
if ($this->attributes) {
$attrs = array();
$spacer = phutil_render_tag(
'span',
array(
'class' => 'phabricator-object-item-attribute-spacer',
),
'&middot;');
$first = true;
foreach ($this->attributes as $attribute) {
$attrs[] = phutil_render_tag(
'li',
array(
'class' => 'phabricator-object-item-attribute',
),
($first ? null : $spacer).$attribute);
$first = false;
}
$attrs = phutil_render_tag(
'ul',
array(
'class' => 'phabricator-object-item-attributes',
),
implode('', $attrs));
}
$classes = array();
$classes[] = 'phabricator-object-item';
if ($this->barColor) {
$classes[] = 'phabricator-object-item-bar-color-'.$this->barColor;
}
switch ($this->effect) {
case 'highlighted':
$classes[] = 'phabricator-object-item-highlighted';
break;
case null:
break;
default:
throw new Exception("Invalid effect!");
}
$content = phutil_render_tag(
'div',
array(
'class' => 'phabricator-object-item-content',
),
$header.$attrs.$this->renderChildren());
return phutil_render_tag(
'div',
array(
'class' => implode(' ', $classes),
),
$icons.$content);
}
}
diff --git a/src/view/layout/PhabricatorPropertyListView.php b/src/view/layout/PhabricatorPropertyListView.php
index 8110791673..4fb140b04b 100644
--- a/src/view/layout/PhabricatorPropertyListView.php
+++ b/src/view/layout/PhabricatorPropertyListView.php
@@ -1,157 +1,157 @@
<?php
final class PhabricatorPropertyListView extends AphrontView {
private $parts = array();
private $hasKeyboardShortcuts;
protected function canAppendChild() {
return false;
}
public function setHasKeyboardShortcuts($has_keyboard_shortcuts) {
$this->hasKeyboardShortcuts = $has_keyboard_shortcuts;
return $this;
}
public function addProperty($key, $value) {
$current = array_pop($this->parts);
if (!$current || $current['type'] != 'property') {
if ($current) {
$this->parts[] = $current;
}
$current = array(
'type' => 'property',
'list' => array(),
);
}
$current['list'][] = array(
'key' => $key,
'value' => $value,
);
$this->parts[] = $current;
return $this;
}
public function addSectionHeader($name) {
$this->parts[] = array(
'type' => 'section',
'name' => $name,
);
return $this;
}
public function addTextContent($content) {
$this->parts[] = array(
'type' => 'text',
'content' => $content,
);
return $this;
}
public function render() {
require_celerity_resource('phabricator-property-list-view-css');
$items = array();
foreach ($this->parts as $part) {
$type = $part['type'];
switch ($type) {
case 'property':
$items[] = $this->renderPropertyPart($part);
break;
case 'section':
$items[] = $this->renderSectionPart($part);
break;
case 'text':
$items[] = $this->renderTextPart($part);
break;
default:
throw new Exception("Unknown part type '{$type}'!");
}
}
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-view',
),
$this->renderSingleView($items));
}
private function renderPropertyPart(array $part) {
$items = array();
foreach ($part['list'] as $spec) {
$key = $spec['key'];
$value = $spec['value'];
- $items[] = phutil_render_tag(
+ $items[] = phutil_tag(
'dt',
array(
'class' => 'phabricator-property-list-key',
),
- phutil_escape_html($key));
+ $key);
$items[] = phutil_render_tag(
'dd',
array(
'class' => 'phabricator-property-list-value',
),
$this->renderSingleView($value));
}
$list = phutil_render_tag(
'dl',
array(
'class' => 'phabricator-property-list-properties',
),
$this->renderSingleView($items));
$content = $this->renderChildren();
if (strlen($content)) {
$content = phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-content',
),
$content);
}
$shortcuts = null;
if ($this->hasKeyboardShortcuts) {
$shortcuts =
id(new AphrontKeyboardShortcutsAvailableView())->render();
}
return
$shortcuts.
phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-container',
),
$list.
'<div class="phabriator-property-list-view-end"></div>'
);
}
private function renderSectionPart(array $part) {
- return phutil_render_tag(
+ return phutil_tag(
'div',
array(
'class' => 'phabricator-property-list-section-header',
),
- phutil_escape_html($part['name']));
+ $part['name']);
}
private function renderTextPart(array $part) {
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-text-content',
),
$part['content']);
}
}
diff --git a/src/view/layout/PhabricatorTransactionView.php b/src/view/layout/PhabricatorTransactionView.php
index 4e6ab0d4ca..64e5bf54d1 100644
--- a/src/view/layout/PhabricatorTransactionView.php
+++ b/src/view/layout/PhabricatorTransactionView.php
@@ -1,147 +1,147 @@
<?php
final class PhabricatorTransactionView extends AphrontView {
private $imageURI;
private $actions = array();
private $epoch;
private $contentSource;
private $anchorName;
private $anchorText;
private $isPreview;
private $classes = array();
public function setImageURI($uri) {
$this->imageURI = $uri;
return $this;
}
public function setActions(array $actions) {
$this->actions = $actions;
return $this;
}
public function setEpoch($epoch) {
$this->epoch = $epoch;
return $this;
}
public function setContentSource(PhabricatorContentSource $source) {
$this->contentSource = $source;
return $this;
}
public function setAnchor($anchor_name, $anchor_text) {
$this->anchorName = $anchor_name;
$this->anchorText = $anchor_text;
return $this;
}
public function addClass($class) {
$this->classes[] = $class;
return $this;
}
public function setIsPreview($preview) {
$this->isPreview = $preview;
return $this;
}
public function render() {
if (!$this->user) {
throw new Exception("Call setUser() before render()!");
}
require_celerity_resource('phabricator-transaction-view-css');
$info = $this->renderTransactionInfo();
$actions = $this->renderTransactionActions();
$style = $this->renderTransactionStyle();
$content = $this->renderTransactionContent();
$classes = phutil_escape_html(implode(' ', $this->classes));
$transaction_id = $this->anchorName ? 'anchor-'.$this->anchorName : null;
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-transaction-view',
'id' => $transaction_id,
'style' => $style,
),
'<div class="phabricator-transaction-detail '.$classes.'">'.
'<div class="phabricator-transaction-header">'.
$info.
$actions.
'</div>'.
$content.
'</div>');
}
private function renderTransactionInfo() {
$info = array();
if ($this->contentSource) {
$content_source = new PhabricatorContentSourceView();
$content_source->setContentSource($this->contentSource);
$content_source->setUser($this->user);
$source = $content_source->render();
if ($source) {
$info[] = $source;
}
}
if ($this->isPreview) {
$info[] = 'PREVIEW';
} else if ($this->epoch) {
$info[] = phabricator_datetime($this->epoch, $this->user);
}
if ($this->anchorName) {
Javelin::initBehavior('phabricator-watch-anchor');
$anchor = id(new PhabricatorAnchorView())
->setAnchorName($this->anchorName)
->render();
- $info[] = $anchor.phutil_render_tag(
+ $info[] = $anchor.phutil_tag(
'a',
array(
'href' => '#'.$this->anchorName,
),
- phutil_escape_html($this->anchorText));
+ $this->anchorText);
}
$info = implode(' &middot; ', $info);
return
'<span class="phabricator-transaction-info">'.
$info.
'</span>';
}
private function renderTransactionActions() {
return implode('', $this->actions);
}
private function renderTransactionStyle() {
if ($this->imageURI) {
return 'background-image: url('.$this->imageURI.');';
} else {
return null;
}
}
private function renderTransactionContent() {
$content = $this->renderChildren();
if (!$content) {
return null;
}
return
'<div class="phabricator-transaction-content">'.
$content.
'</div>';
}
}
diff --git a/src/view/layout/headsup/AphrontHeadsupView.php b/src/view/layout/headsup/AphrontHeadsupView.php
index ba6fa8c827..9e3b8f0896 100644
--- a/src/view/layout/headsup/AphrontHeadsupView.php
+++ b/src/view/layout/headsup/AphrontHeadsupView.php
@@ -1,81 +1,81 @@
<?php
final class AphrontHeadsupView extends AphrontView {
private $actionList;
private $header;
private $properties;
private $objectName;
public function setActionList(AphrontHeadsupActionListView $action_list) {
$this->actionList = $action_list;
return $this;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setProperties(array $dict) {
$this->properties = $dict;
return $this;
}
public function setObjectName($name) {
$this->objectName = $name;
return $this;
}
public function render() {
$header =
'<h1>'.
- phutil_render_tag(
+ phutil_tag(
'a',
array(
'href' => '/'.$this->objectName,
'class' => 'aphront-headsup-object-name',
),
- phutil_escape_html($this->objectName)).
+ $this->objectName).
' '.
phutil_escape_html($this->header).
'</h1>';
require_celerity_resource('aphront-headsup-view-css');
$prop_table = null;
if ($this->properties) {
$prop_table = array();
foreach ($this->properties as $key => $value) {
$prop_table[] =
'<tr>'.
'<th>'.phutil_escape_html($key.':').'</th>'.
'<td>'.$value.'</td>'.
'</tr>';
}
$prop_table =
'<table class="aphront-headsup-property-table">'.
implode("\n", $prop_table).
'</table>';
}
$children = $this->renderChildren();
if (strlen($children)) {
$children =
'<div class="aphront-headsup-details">'.
$children.
'</div>';
}
return
'<div class="aphront-headsup-panel">'.
self::renderSingleView($this->actionList).
'<div class="aphront-headsup-core">'.
$header.
$prop_table.
$children.
'</div>'.
'</div>';
}
}
diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php
index f3c3596719..588467aca6 100644
--- a/src/view/page/menu/PhabricatorMainMenuView.php
+++ b/src/view/page/menu/PhabricatorMainMenuView.php
@@ -1,370 +1,370 @@
<?php
final class PhabricatorMainMenuView extends AphrontView {
private $defaultSearchScope;
private $controller;
private $applicationMenu;
public function setApplicationMenu(PhabricatorMenuView $application_menu) {
$this->applicationMenu = $application_menu;
return $this;
}
public function getApplicationMenu() {
return $this->applicationMenu;
}
public function setController(PhabricatorController $controller) {
$this->controller = $controller;
return $this;
}
public function getController() {
return $this->controller;
}
public function setDefaultSearchScope($default_search_scope) {
$this->defaultSearchScope = $default_search_scope;
return $this;
}
public function getDefaultSearchScope() {
return $this->defaultSearchScope;
}
public function render() {
$user = $this->user;
require_celerity_resource('phabricator-main-menu-view');
$header_id = celerity_generate_unique_node_id();
$menus = array();
$alerts = array();
if ($user->isLoggedIn()) {
list($menu, $dropdown) = $this->renderNotificationMenu();
$alerts[] = $menu;
$menus[] = $dropdown;
}
$phabricator_menu = $this->renderPhabricatorMenu();
if ($alerts) {
$alerts = phutil_render_tag(
'div',
array(
'class' => 'phabricator-main-menu-alerts',
),
self::renderSingleView($alerts));
}
$application_menu = $this->getApplicationMenu();
if ($application_menu) {
$application_menu->addClass('phabricator-dark-menu');
$application_menu->addClass('phabricator-application-menu');
}
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-main-menu',
'id' => $header_id,
),
self::renderSingleView(
array(
$this->renderPhabricatorMenuButton($header_id),
$application_menu
? $this->renderApplicationMenuButton($header_id)
: null,
$this->renderPhabricatorLogo(),
$alerts,
$phabricator_menu,
$application_menu,
))).
self::renderSingleView($menus);
}
private function renderSearch() {
$user = $this->user;
$result = null;
$keyboard_config = array(
'helpURI' => '/help/keyboardshortcut/',
);
if ($user->isLoggedIn()) {
$search = new PhabricatorMainMenuSearchView();
$search->setUser($user);
$search->setScope($this->getDefaultSearchScope());
$result = $search;
$pref_shortcut = PhabricatorUserPreferences::PREFERENCE_SEARCH_SHORTCUT;
if ($user->loadPreferences()->getPreference($pref_shortcut, true)) {
$keyboard_config['searchID'] = $search->getID();
}
}
Javelin::initBehavior('phabricator-keyboard-shortcuts', $keyboard_config);
if ($result) {
$result = id(new PhabricatorMenuItemView())
->addClass('phabricator-main-menu-search')
->appendChild($result);
}
return $result;
}
private function renderPhabricatorMenuButton($header_id) {
return javelin_render_tag(
'a',
array(
'class' => 'phabricator-main-menu-expand-button '.
'phabricator-expand-core-menu',
'sigil' => 'jx-toggle-class',
'meta' => array(
'map' => array(
$header_id => 'phabricator-core-menu-expanded',
),
),
),
phutil_render_tag(
'span',
array(
'class' => 'phabricator-menu-button-icon sprite-menu menu-icon-eye',
),
''));
}
public function renderApplicationMenuButton($header_id) {
return javelin_render_tag(
'a',
array(
'class' => 'phabricator-main-menu-expand-button '.
'phabricator-expand-application-menu',
'sigil' => 'jx-toggle-class',
'meta' => array(
'map' => array(
$header_id => 'phabricator-application-menu-expanded',
),
),
),
phutil_render_tag(
'span',
array(
'class' => 'phabricator-menu-button-icon sprite-menu menu-icon-app',
),
''));
}
private function renderPhabricatorMenu() {
$user = $this->getUser();
$controller = $this->getController();
$applications = PhabricatorApplication::getAllInstalledApplications();
$applications = msort($applications, 'getName');
$core = array();
$more = array();
$actions = array();
require_celerity_resource('sprite-apps-large-css');
$group_core = PhabricatorApplication::GROUP_CORE;
foreach ($applications as $application) {
if ($application->shouldAppearInLaunchView()) {
$icon = $application->getIconName().'-light-large';
$item = id(new PhabricatorMenuItemView())
->setName($application->getName())
->setHref($application->getBaseURI())
->appendChild($this->renderMenuIcon($icon));
if ($application->getApplicationGroup() == $group_core) {
$core[] = $item;
} else {
$more[] = $item;
}
}
$app_actions = $application->buildMainMenuItems($user, $controller);
foreach ($app_actions as $action) {
$actions[] = $action;
}
}
$view = new PhabricatorMenuView();
$view->addClass('phabricator-dark-menu');
$view->addClass('phabricator-core-menu');
$search = $this->renderSearch();
$view->appendChild($search);
$view
->newLabel(pht('Home'))
->addClass('phabricator-core-item-device');
$view->addMenuItem(
id(new PhabricatorMenuItemView())
->addClass('phabricator-core-item-device')
->setName(pht('Phabricator Home'))
->setHref('/')
->appendChild($this->renderMenuIcon('logo-light-large')));
if ($controller && $controller->getCurrentApplication()) {
$application = $controller->getCurrentApplication();
$icon = $application->getIconName().'-light-large';
$view->addMenuItem(
id(new PhabricatorMenuItemView())
->addClass('phabricator-core-item-device')
->setName(pht('%s Home', $application->getName()))
->appendChild($this->renderMenuIcon($icon))
->setHref($controller->getApplicationURI()));
}
if ($core) {
$view->addMenuItem(
id(new PhabricatorMenuItemView())
->addClass('phabricator-core-item-device')
->setType(PhabricatorMenuItemView::TYPE_LABEL)
->setName(pht('Core Applications')));
foreach ($core as $item) {
$item->addClass('phabricator-core-item-device');
$view->addMenuItem($item);
}
}
if ($actions) {
$actions = msort($actions, 'getSortOrder');
$view->addMenuItem(
id(new PhabricatorMenuItemView())
->addClass('phabricator-core-item-device')
->setType(PhabricatorMenuItemView::TYPE_LABEL)
->setName(pht('Actions')));
foreach ($actions as $action) {
$icon = $action->getIcon();
if ($icon) {
if ($action->getSelected()) {
$action->appendChild($this->renderMenuIcon($icon.'-blue-large'));
} else {
$action->appendChild($this->renderMenuIcon($icon.'-light-large'));
}
}
$view->addMenuItem($action);
}
}
if ($more) {
$view->addMenuItem(
id(new PhabricatorMenuItemView())
->addClass('phabricator-core-item-device')
->setType(PhabricatorMenuItemView::TYPE_LABEL)
->setName(pht('More Applications')));
foreach ($more as $item) {
$item->addClass('phabricator-core-item-device');
$view->addMenuItem($item);
}
}
return $view;
}
private function renderPhabricatorLogo() {
return phutil_render_tag(
'a',
array(
'class' => 'phabricator-main-menu-logo',
'href' => '/',
),
phutil_render_tag(
'span',
array(
'class' => 'sprite-menu phabricator-main-menu-logo-image',
),
''));
}
private function renderNotificationMenu() {
$user = $this->user;
require_celerity_resource('phabricator-notification-css');
require_celerity_resource('phabricator-notification-menu-css');
require_celerity_resource('sprite-menu-css');
$count_id = celerity_generate_unique_node_id();
$dropdown_id = celerity_generate_unique_node_id();
$bubble_id = celerity_generate_unique_node_id();
$count_number = id(new PhabricatorFeedStoryNotification())
->countUnread($user);
if ($count_number > 999) {
$count_number = "\xE2\x88\x9E";
}
- $count_tag = phutil_render_tag(
+ $count_tag = phutil_tag(
'span',
array(
'id' => $count_id,
'class' => 'phabricator-main-menu-alert-count'
),
- phutil_escape_html($count_number));
+ $count_number);
$icon_tag = phutil_render_tag(
'span',
array(
'class' => 'sprite-menu phabricator-main-menu-alert-icon',
),
'');
$container_classes = array(
'phabricator-main-menu-alert-bubble',
'sprite-menu',
'alert-notifications',
);
if ($count_number) {
$container_classes[] = 'alert-unread';
}
$bubble_tag = phutil_render_tag(
'a',
array(
'href' => '/notification/',
'class' => implode(' ', $container_classes),
'id' => $bubble_id,
),
$icon_tag.$count_tag);
Javelin::initBehavior(
'aphlict-dropdown',
array(
'bubbleID' => $bubble_id,
'countID' => $count_id,
'dropdownID' => $dropdown_id,
));
$notification_dropdown = javelin_render_tag(
'div',
array(
'id' => $dropdown_id,
'class' => 'phabricator-notification-menu',
'sigil' => 'phabricator-notification-menu',
'style' => 'display: none;',
),
'');
return array($bubble_tag, $notification_dropdown);
}
private function renderMenuIcon($name) {
return phutil_render_tag(
'span',
array(
'class' => 'phabricator-core-menu-icon '.
'sprite-apps-large app-'.$name,
),
'');
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 29, 2:45 AM (20 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
108165
Default Alt Text
(440 KB)

Event Timeline