diff --git a/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php b/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php index 98df6d113c..bf45706e84 100644 --- a/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php +++ b/src/aphront/console/plugin/DarkConsoleErrorLogPlugin.php @@ -1,154 +1,154 @@ <?php /** * @group console */ final class DarkConsoleErrorLogPlugin extends DarkConsolePlugin { public function getName() { $count = count($this->getData()); if ($count) { return pht('Error Log (%d)', $count); } return pht('Error Log'); } public function getOrder() { return 0; } public function getColor() { if (count($this->getData())) { return '#ff0000'; } return null; } public function getDescription() { return pht('Shows errors and warnings.'); } public function generateData() { return DarkConsoleErrorLogPluginAPI::getErrors(); } public function renderPanel() { $data = $this->getData(); $rows = array(); $details = ''; foreach ($data as $index => $row) { $file = $row['file']; $line = $row['line']; $tag = phutil_tag( 'a', array( 'onclick' => jsprintf('show_details(%d)', $index), ), $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"; + $details .= hsprintf( + '<div class="dark-console-panel-error-details" id="row-details-%s">'. + "%s\nStack trace:\n", + $index, + $row['details']); 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_tag( 'a', array( 'href' => $href, ), $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/auth/controller/PhabricatorLoginController.php b/src/applications/auth/controller/PhabricatorLoginController.php index 1ad981cf65..5a39d52372 100644 --- a/src/applications/auth/controller/PhabricatorLoginController.php +++ b/src/applications/auth/controller/PhabricatorLoginController.php @@ -1,312 +1,311 @@ <?php final class PhabricatorLoginController extends PhabricatorAuthController { public function shouldRequireLogin() { return false; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($user->isLoggedIn()) { // Kick the user out if they're already logged in. return id(new AphrontRedirectResponse())->setURI('/'); } if ($request->isAjax()) { // We end up here if the user clicks a workflow link that they need to // login to use. We give them a dialog saying "You need to login..". if ($request->isDialogFormPost()) { return id(new AphrontRedirectResponse())->setURI( $request->getRequestURI()); } $dialog = new AphrontDialogView(); $dialog->setUser($user); $dialog->setTitle(pht('Login Required')); $dialog->appendChild(phutil_tag('p', array(), pht( 'You must login to continue.'))); $dialog->addSubmitButton(pht('Login')); $dialog->addCancelButton('/', pht('Cancel')); return id(new AphrontDialogResponse())->setDialog($dialog); } if ($request->isConduit()) { // A common source of errors in Conduit client configuration is getting // the request path wrong. The client will end up here, so make some // effort to give them a comprehensible error message. $request_path = $this->getRequest()->getPath(); $conduit_path = '/api/<method>'; $example_path = '/api/conduit.ping'; $message = "ERROR: You are making a Conduit API request to '{$request_path}', ". "but the correct HTTP request path to use in order to access a ". "Conduit method is '{$conduit_path}' (for example, ". "'{$example_path}'). Check your configuration."; return id(new AphrontPlainTextResponse())->setContent($message); } $error_view = null; if ($request->getCookie('phusr') && $request->getCookie('phsid')) { // The session cookie is invalid, so clear it. $request->clearCookie('phusr'); $request->clearCookie('phsid'); $error_view = new AphrontErrorView(); $error_view->setTitle(pht('Invalid Session')); $error_view->setErrors(array( pht("Your login session is invalid. Try logging in again. If that ". "doesn't work, clear your browser cookies.") )); } $next_uri_path = $this->getRequest()->getPath(); if ($next_uri_path == '/login/') { $next_uri = '/'; } else { $next_uri = $this->getRequest()->getRequestURI(); } if (!$request->isFormPost()) { $request->setCookie('next_uri', $next_uri); } $password_auth = PhabricatorEnv::getEnvConfig('auth.password-auth-enabled'); $username_or_email = $request->getCookie('phusr'); $forms = array(); $errors = array(); if ($password_auth) { $require_captcha = false; $e_captcha = true; if ($request->isFormPost()) { if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) { $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP( PhabricatorUserLog::ACTION_LOGIN_FAILURE, 60 * 15); if (count($failed_attempts) > 5) { $require_captcha = true; if (!AphrontFormRecaptchaControl::processCaptcha($request)) { if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) { $e_captcha = pht('Invalid'); $errors[] = pht('CAPTCHA was not entered correctly.'); } else { $e_captcha = pht('Required'); $errors[] = pht('Too many login failures recently. You must '. 'submit a CAPTCHA with your login request.'); } } } } $username_or_email = $request->getStr('username_or_email'); $user = id(new PhabricatorUser())->loadOneWhere( 'username = %s', $username_or_email); if (!$user) { $user = PhabricatorUser::loadOneWithEmailAddress($username_or_email); } if (!$errors) { // Perform username/password tests only if we didn't get rate limited // by the CAPTCHA. $envelope = new PhutilOpaqueEnvelope($request->getStr('password')); if (!$user || !$user->comparePassword($envelope)) { $errors[] = pht('Bad username/password.'); } } if (!$errors) { $session_key = $user->establishSession('web'); $request->setCookie('phusr', $user->getUsername()); $request->setCookie('phsid', $session_key); $uri = new PhutilURI('/login/validate/'); $uri->setQueryParams( array( 'phusr' => $user->getUsername(), )); return id(new AphrontRedirectResponse()) ->setURI((string)$uri); } else { $log = PhabricatorUserLog::newLog( null, $user, PhabricatorUserLog::ACTION_LOGIN_FAILURE); $log->save(); $request->clearCookie('phusr'); $request->clearCookie('phsid'); } } if ($errors) { $error_view = new AphrontErrorView(); $error_view->setTitle(pht('Login Failed')); $error_view->setErrors($errors); } $form = new AphrontFormView(); $form ->setUser($request->getUser()) ->setAction('/login/') ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Username/Email')) ->setName('username_or_email') ->setValue($username_or_email)) ->appendChild( id(new AphrontFormPasswordControl()) ->setLabel(pht('Password')) ->setName('password') ->setCaption(hsprintf( '<a href="/login/email/">%s</a>', pht('Forgot your password? / Email Login')))); if ($require_captcha) { $form->appendChild( id(new AphrontFormRecaptchaControl()) ->setError($e_captcha)); } $form ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Login'))); // $panel->setCreateButton('Register New Account', '/login/register/'); $forms['Phabricator Login'] = $form; } $ldap_provider = new PhabricatorLDAPProvider(); if ($ldap_provider->isProviderEnabled()) { $ldap_form = new AphrontFormView(); $ldap_form ->setUser($request->getUser()) ->setAction('/ldap/login/') ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('LDAP username')) ->setName('username') ->setValue($username_or_email)) ->appendChild( id(new AphrontFormPasswordControl()) ->setLabel(pht('Password')) ->setName('password')); $ldap_form ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Login'))); $forms['LDAP Login'] = $ldap_form; } $providers = PhabricatorOAuthProvider::getAllProviders(); foreach ($providers as $provider) { $enabled = $provider->isProviderEnabled(); if (!$enabled) { continue; } $auth_uri = $provider->getAuthURI(); $redirect_uri = $provider->getRedirectURI(); $client_id = $provider->getClientID(); $provider_name = $provider->getProviderName(); $minimum_scope = $provider->getMinimumScope(); $extra_auth = $provider->getExtraAuthParameters(); // TODO: In theory we should use 'state' to prevent CSRF, but the total // effect of the CSRF attack is that an attacker can cause a user to login // to Phabricator if they're already logged into some OAuth provider. This // does not seem like the most severe threat in the world, and generating // CSRF for logged-out users is vaugely tricky. if ($provider->isProviderRegistrationEnabled()) { $title = pht("Login or Register with %s", $provider_name); $body = pht('Login or register for Phabricator using your %s account.', $provider_name); - $button = pht("Login or Register with %s", - phutil_escape_html($provider_name)); + $button = pht("Login or Register with %s", $provider_name); } else { $title = pht("Login with %s", $provider_name); $body = hsprintf( '%s<br /><br /><strong>%s</strong>', pht( 'Login to your existing Phabricator account using your %s account.', $provider_name), pht( 'You can not use %s to register a new account.', $provider_name)); - $button = pht("Log in with %s", phutil_escape_html($provider_name)); + $button = pht("Log in with %s", $provider_name); } $auth_form = new AphrontFormView(); $auth_form ->setAction($auth_uri) ->addHiddenInput('client_id', $client_id) ->addHiddenInput('redirect_uri', $redirect_uri) ->addHiddenInput('scope', $minimum_scope); foreach ($extra_auth as $key => $value) { $auth_form->addHiddenInput($key, $value); } $auth_form ->setUser($request->getUser()) ->setMethod('GET') ->appendChild(hsprintf( '<p class="aphront-form-instructions">%s</p>', $body)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue("{$button} \xC2\xBB")); $forms[$title] = $auth_form; } $panel = new AphrontPanelView(); $panel->setWidth(AphrontPanelView::WIDTH_FORM); $panel->setNoBackground(); foreach ($forms as $name => $form) { $panel->appendChild(phutil_tag('h1', array(), $name)); $panel->appendChild($form); $panel->appendChild(phutil_tag('br')); } $login_message = PhabricatorEnv::getEnvConfig('auth.login-message'); return $this->buildApplicationPage( array( $error_view, $login_message, $panel, ), array( 'title' => pht('Login'), 'device' => true )); } } diff --git a/src/applications/calendar/controller/PhabricatorCalendarViewStatusController.php b/src/applications/calendar/controller/PhabricatorCalendarViewStatusController.php index 9f96fd070a..6a7913e182 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarViewStatusController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarViewStatusController.php @@ -1,129 +1,129 @@ <?php final class PhabricatorCalendarViewStatusController extends PhabricatorCalendarController { private $phid; public function willProcessRequest(array $data) { $user = $this->getRequest()->getUser(); $this->phid = idx($data, 'phid', $user->getPHID()); $this->loadHandles(array($this->phid)); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $handle = $this->getHandle($this->phid); $statuses = id(new PhabricatorUserStatus()) ->loadAllWhere('userPHID = %s AND dateTo > UNIX_TIMESTAMP()', $this->phid); $nav = $this->buildSideNavView(); $nav->selectFilter($this->getFilter()); $page_title = $this->getPageTitle(); $status_list = $this->buildStatusList($statuses); $status_list->setNoDataString($this->getNoDataString()); $nav->appendChild( array( id(new PhabricatorHeaderView())->setHeader($page_title), $status_list, ) ); return $this->buildApplicationPage( $nav, array( 'title' => $page_title, 'device' => true ) ); } private function buildStatusList(array $statuses) { assert_instances_of($statuses, 'PhabricatorUserStatus'); $user = $this->getRequest()->getUser(); $list = new PhabricatorObjectItemListView(); foreach ($statuses as $status) { if ($status->getUserPHID() == $user->getPHID()) { $href = $this->getApplicationURI('/status/edit/'.$status->getID().'/'); } else { $from = $status->getDateFrom(); $month = phabricator_format_local_time($from, $user, 'm'); $year = phabricator_format_local_time($from, $user, 'Y'); $uri = new PhutilURI($this->getApplicationURI()); $uri->setQueryParams( array( 'month' => $month, 'year' => $year, ) ); $href = (string) $uri; } $from = phabricator_datetime($status->getDateFrom(), $user); $to = phabricator_datetime($status->getDateTo(), $user); $color = ($status->getStatus() == PhabricatorUserStatus::STATUS_AWAY) ? 'red' : 'yellow'; $item = id(new PhabricatorObjectItemView()) ->setHeader($status->getTerseSummary($user)) ->setHref($href) ->setBarColor($color) ->addAttribute(pht('From %s to %s', $from, $to)) ->addAttribute( phutil_utf8_shorten($status->getDescription(), 64)); $list->addItem($item); } return $list; } private function getNoDataString() { if ($this->isUserRequest()) { $no_data = pht('You do not have any upcoming status events.'); } else { $no_data = pht('%s does not have any upcoming status events.', - phutil_escape_html($this->getHandle($this->phid)->getName())); + $this->getHandle($this->phid)->getName()); } return $no_data; } private function getFilter() { if ($this->isUserRequest()) { $filter = 'status/'; } else { $filter = 'status/view/'.$this->phid.'/'; } return $filter; } private function getPageTitle() { if ($this->isUserRequest()) { $page_title = pht('Upcoming Statuses'); } else { $page_title = pht( 'Upcoming Statuses for %s', - phutil_escape_html($this->getHandle($this->phid)->getName()) + $this->getHandle($this->phid)->getName() ); } return $page_title; } private function isUserRequest() { $user = $this->getRequest()->getUser(); return $this->phid == $user->getPHID(); } } diff --git a/src/applications/calendar/view/AphrontCalendarMonthView.php b/src/applications/calendar/view/AphrontCalendarMonthView.php index 3367c01d88..c482204405 100644 --- a/src/applications/calendar/view/AphrontCalendarMonthView.php +++ b/src/applications/calendar/view/AphrontCalendarMonthView.php @@ -1,315 +1,318 @@ <?php final class AphrontCalendarMonthView extends AphrontView { private $month; private $year; private $holidays = array(); private $events = array(); private $browseURI; public function setBrowseURI($browse_uri) { $this->browseURI = $browse_uri; return $this; } private function getBrowseURI() { return $this->browseURI; } public function addEvent(AphrontCalendarEventView $event) { $this->events[] = $event; return $this; } public function setHolidays(array $holidays) { assert_instances_of($holidays, 'PhabricatorCalendarHoliday'); $this->holidays = mpull($holidays, null, 'getDay'); return $this; } public function __construct($month, $year) { $this->month = $month; $this->year = $year; } public function render() { if (empty($this->user)) { throw new Exception("Call setUser() before render()!"); } $events = msort($this->events, 'getEpochStart'); $days = $this->getDatesInMonth(); require_celerity_resource('aphront-calendar-view-css'); $first = reset($days); $empty = $first->format('w'); $markup = array(); $empty_box = '<div class="aphront-calendar-day aphront-calendar-empty">'. '</div>'; for ($ii = 0; $ii < $empty; $ii++) { $markup[] = $empty_box; } $show_events = array(); foreach ($days as $day) { $day_number = $day->format('j'); $holiday = idx($this->holidays, $day->format('Y-m-d')); $class = 'aphront-calendar-day'; $weekday = $day->format('w'); if ($holiday || $weekday == 0 || $weekday == 6) { $class .= ' aphront-calendar-not-work-day'; } $day->setTime(0, 0, 0); $epoch_start = $day->format('U'); $day->modify('+1 day'); $epoch_end = $day->format('U'); if ($weekday == 0) { $show_events = array(); } else { $show_events = array_fill_keys( array_keys($show_events), '<div class="aphront-calendar-event aphront-calendar-event-empty">'. ' '. '</div>'); } foreach ($events as $event) { if ($event->getEpochStart() >= $epoch_end) { // This list is sorted, so we can stop looking. break; } if ($event->getEpochStart() < $epoch_end && $event->getEpochEnd() > $epoch_start) { $show_events[$event->getUserPHID()] = $this->renderEvent( $event, $epoch_start, $epoch_end); } } $holiday_markup = null; if ($holiday) { - $name = phutil_escape_html($holiday->getName()); - $holiday_markup = - '<div class="aphront-calendar-holiday" title="'.$name.'">'. - $name. - '</div>'; + $name = $holiday->getName(); + $holiday_markup = phutil_tag( + 'div', + array( + 'class' => 'aphront-calendar-holiday', + 'title' => $name, + ), + $name); } $markup[] = '<div class="'.$class.'">'. '<div class="aphront-calendar-date-number">'. $day_number. '</div>'. $holiday_markup. implode("\n", $show_events). '</div>'; } $table = array(); $rows = array_chunk($markup, 7); foreach ($rows as $row) { $table[] = '<tr>'; while (count($row) < 7) { $row[] = $empty_box; } foreach ($row as $cell) { $table[] = '<td>'.$cell.'</td>'; } $table[] = '</tr>'; } $table = '<table class="aphront-calendar-view">'. $this->renderCalendarHeader($first). '<tr class="aphront-calendar-day-of-week-header">'. '<th>Sun</th>'. '<th>Mon</th>'. '<th>Tue</th>'. '<th>Wed</th>'. '<th>Thu</th>'. '<th>Fri</th>'. '<th>Sat</th>'. '</tr>'. implode("\n", $table). '</table>'; return $table; } private function renderCalendarHeader(DateTime $date) { $colspan = 7; $left_th = ''; $right_th = ''; // check for a browseURI, which means we need "fancy" prev / next UI $uri = $this->getBrowseURI(); if ($uri) { $colspan = 5; $uri = new PhutilURI($uri); list($prev_year, $prev_month) = $this->getPrevYearAndMonth(); $query = array('year' => $prev_year, 'month' => $prev_month); $prev_link = phutil_tag( 'a', array('href' => (string) $uri->setQueryParams($query)), "\xE2\x86\x90" ); list($next_year, $next_month) = $this->getNextYearAndMonth(); $query = array('year' => $next_year, 'month' => $next_month); $next_link = phutil_tag( 'a', array('href' => (string) $uri->setQueryParams($query)), "\xE2\x86\x92" ); $left_th = '<th>'.$prev_link.'</th>'; $right_th = '<th>'.$next_link.'</th>'; } return '<tr class="aphront-calendar-month-year-header">'. $left_th. '<th colspan="'.$colspan.'">'.$date->format('F Y').'</th>'. $right_th. '</tr>'; } private function getNextYearAndMonth() { $month = $this->month; $year = $this->year; $next_year = $year; $next_month = $month + 1; if ($next_month == 13) { $next_year = $year + 1; $next_month = 1; } return array($next_year, $next_month); } private function getPrevYearAndMonth() { $month = $this->month; $year = $this->year; $prev_year = $year; $prev_month = $month - 1; if ($prev_month == 0) { $prev_year = $year - 1; $prev_month = 12; } return array($prev_year, $prev_month); } /** * Return a DateTime object representing the first moment in each day in the * month, according to the user's locale. * * @return list List of DateTimes, one for each day. */ private function getDatesInMonth() { $user = $this->user; $timezone = new DateTimeZone($user->getTimezoneIdentifier()); $month = $this->month; $year = $this->year; // Get the year and month numbers of the following month, so we can // determine when this month ends. list($next_year, $next_month) = $this->getNextYearAndMonth(); $end_date = new DateTime("{$next_year}-{$next_month}-01", $timezone); $end_epoch = $end_date->format('U'); $days = array(); for ($day = 1; $day <= 31; $day++) { $day_date = new DateTime("{$year}-{$month}-{$day}", $timezone); $day_epoch = $day_date->format('U'); if ($day_epoch >= $end_epoch) { break; } else { $days[] = $day_date; } } return $days; } private function renderEvent( AphrontCalendarEventView $event, $epoch_start, $epoch_end) { $user = $this->user; $event_start = $event->getEpochStart(); $event_end = $event->getEpochEnd(); $classes = array(); $when = array(); $classes[] = 'aphront-calendar-event'; if ($event_start < $epoch_start) { $classes[] = 'aphront-calendar-event-continues-before'; $when[] = 'Started '.phabricator_datetime($event_start, $user); } else { $when[] = 'Starts at '.phabricator_time($event_start, $user); } if ($event_end > $epoch_end) { $classes[] = 'aphront-calendar-event-continues-after'; $when[] = 'Ends '.phabricator_datetime($event_end, $user); } else { $when[] = 'Ends at '.phabricator_time($event_end, $user); } Javelin::initBehavior('phabricator-tooltips'); $info = $event->getName(); if ($event->getDescription()) { $info .= "\n\n".$event->getDescription(); } if ($user->getPHID() == $event->getUserPHID()) { $tag = 'a'; $href = '/calendar/status/edit/'.$event->getEventID().'/'; } else { $tag = 'div'; $href = null; } $text_div = javelin_tag( $tag, array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => $info."\n\n".implode("\n", $when), 'size' => 240, ), 'class' => 'aphront-calendar-event-text', 'href' => $href, ), phutil_utf8_shorten($event->getName(), 32)); return javelin_tag( 'div', array( 'class' => implode(' ', $classes), ), $text_div); } } diff --git a/src/applications/conpherence/storage/ConpherenceTransaction.php b/src/applications/conpherence/storage/ConpherenceTransaction.php index 64afcf9157..b2ff0f97c0 100644 --- a/src/applications/conpherence/storage/ConpherenceTransaction.php +++ b/src/applications/conpherence/storage/ConpherenceTransaction.php @@ -1,129 +1,129 @@ <?php /** * @group conpherence */ final class ConpherenceTransaction extends PhabricatorApplicationTransaction { public function getApplicationName() { return 'conpherence'; } public function getApplicationTransactionType() { return PhabricatorPHIDConstants::PHID_TYPE_CONP; } public function getApplicationTransactionCommentObject() { return new ConpherenceTransactionComment(); } public function getApplicationObjectTypeName() { return pht('conpherence'); } public function shouldHide() { $old = $this->getOldValue(); switch ($this->getTransactionType()) { case ConpherenceTransactionType::TYPE_PARTICIPANTS: return ($old === null); case ConpherenceTransactionType::TYPE_TITLE: case ConpherenceTransactionType::TYPE_PICTURE: return false; case ConpherenceTransactionType::TYPE_FILES: case ConpherenceTransactionType::TYPE_PICTURE_CROP: return true; } return parent::shouldHide(); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case ConpherenceTransactionType::TYPE_TITLE: if ($old && $new) { $title = pht( '%s renamed this conpherence from "%s" to "%s".', $this->renderHandleLink($author_phid), - phutil_escape_html($old), - phutil_escape_html($new)); + $old, + $new); } else if ($old) { $title = pht( '%s deleted the conpherence name "%s".', $this->renderHandleLink($author_phid), - phutil_escape_html($old)); + $old); } else { $title = pht( '%s named this conpherence "%s".', $this->renderHandleLink($author_phid), - phutil_escape_html($new)); + $new); } return $title; case ConpherenceTransactionType::TYPE_FILES: return pht( '%s updated the conpherence files.', $this->renderHandleLink($author_phid)); case ConpherenceTransactionType::TYPE_PICTURE: return pht( '%s updated the conpherence image.', $this->renderHandleLink($author_phid)); case ConpherenceTransactionType::TYPE_PARTICIPANTS: $add = array_diff($new, $old); $rem = array_diff($old, $new); if ($add && $rem) { $title = pht( '%s edited participant(s), added %d: %s; removed %d: %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add), count($rem), $this->renderHandleList($rem)); } else if ($add) { $title = pht( '%s added %d participant(s): %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add)); } else { $title = pht( '%s removed %d partipant(s): %s.', $this->renderHandleLink($author_phid), count($rem), $this->renderHandleList($rem)); } return $title; break; } return parent::getTitle(); } public function getRequiredHandlePHIDs() { $phids = parent::getRequiredHandlePHIDs(); $old = $this->getOldValue(); $new = $this->getNewValue(); $phids[] = $this->getAuthorPHID(); switch ($this->getTransactionType()) { case ConpherenceTransactionType::TYPE_PICTURE: case ConpherenceTransactionType::TYPE_TITLE: case ConpherenceTransactionType::TYPE_FILES: break; case ConpherenceTransactionType::TYPE_PARTICIPANTS: $phids = array_merge($phids, $this->getOldValue()); $phids = array_merge($phids, $this->getNewValue()); break; } return $phids; } } diff --git a/src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php b/src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php index 79405a4718..de6a89f05a 100644 --- a/src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php +++ b/src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php @@ -1,194 +1,193 @@ <?php final class DifferentialReviewersFieldSpecification extends DifferentialFieldSpecification { private $reviewers = array(); private $error; public function shouldAppearOnRevisionView() { return true; } public function getRequiredHandlePHIDsForRevisionView() { return $this->getReviewerPHIDs(); } public function renderLabelForRevisionView() { return 'Reviewers:'; } public function renderValueForRevisionView() { return $this->renderUserList($this->getReviewerPHIDs()); } private function getReviewerPHIDs() { $revision = $this->getRevision(); return $revision->getReviewers(); } public function shouldAppearOnEdit() { return true; } protected function didSetRevision() { $this->reviewers = $this->getReviewerPHIDs(); } public function getRequiredHandlePHIDsForRevisionEdit() { return $this->reviewers; } public function setValueFromRequest(AphrontRequest $request) { $this->reviewers = $request->getArr('reviewers'); return $this; } public function validateField() { if (!$this->hasRevision()) { return; } $self = PhabricatorEnv::getEnvConfig('differential.allow-self-accept'); if ($self) { return; } $author_phid = $this->getRevision()->getAuthorPHID(); if (!in_array($author_phid, $this->reviewers)) { return; } $this->error = 'Invalid'; throw new DifferentialFieldValidationException( "The owner of a revision may not be a reviewer."); } public function renderEditControl() { $reviewer_map = array(); foreach ($this->reviewers as $phid) { $reviewer_map[$phid] = $this->getHandle($phid)->getFullName(); } return id(new AphrontFormTokenizerControl()) ->setLabel('Reviewers') ->setName('reviewers') ->setUser($this->getUser()) ->setDatasource('/typeahead/common/users/') ->setValue($reviewer_map) ->setError($this->error); } public function willWriteRevision(DifferentialRevisionEditor $editor) { $editor->setReviewers($this->reviewers); } public function shouldAppearOnCommitMessage() { return true; } public function getCommitMessageKey() { return 'reviewerPHIDs'; } public function setValueFromParsedCommitMessage($value) { $this->reviewers = nonempty($value, array()); return $this; } public function renderLabelForCommitMessage() { return 'Reviewers'; } public function getRequiredHandlePHIDsForCommitMessage() { return $this->reviewers; } public function renderValueForCommitMessage($is_edit) { if (!$this->reviewers) { return null; } $names = array(); foreach ($this->reviewers as $phid) { $names[] = $this->getHandle($phid)->getName(); } return implode(', ', $names); } public function getSupportedCommitMessageLabels() { return array( 'Reviewer', 'Reviewers', ); } public function parseValueFromCommitMessage($value) { return $this->parseCommitMessageUserList($value); } public function shouldAppearOnRevisionList() { return true; } public function renderHeaderForRevisionList() { return 'Reviewers'; } public function renderValueForRevisionList(DifferentialRevision $revision) { $primary_reviewer = $revision->getPrimaryReviewer(); if ($primary_reviewer) { $other_reviewers = array_flip($revision->getReviewers()); unset($other_reviewers[$primary_reviewer]); if ($other_reviewers) { $names = array(); foreach ($other_reviewers as $reviewer => $_) { - $names[] = phutil_escape_html( - $this->getHandle($reviewer)->getLinkName()); + $names[] = $this->getHandle($reviewer)->getLinkName(); } $suffix = javelin_tag( 'abbr', array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => implode(', ', $names), 'align' => 'E', ), ), '(+'.(count($names)).')'); } else { $suffix = null; } return hsprintf( '%s %s', $this->getHandle($primary_reviewer)->renderLink(), $suffix); } else { return phutil_tag('em', array(), 'None'); } } public function getRequiredHandlePHIDsForRevisionList( DifferentialRevision $revision) { return $revision->getReviewers(); } public function renderValueForMail($phase) { if ($phase == DifferentialMailPhase::COMMENT) { return null; } if (!$this->reviewers) { return null; } $handles = id(new PhabricatorObjectHandleData($this->reviewers)) ->loadHandles(); $handles = array_select_keys( $handles, array($this->getRevision()->getPrimaryReviewer())) + $handles; $names = mpull($handles, 'getName'); return 'Reviewers: '.implode(', ', $names); } } diff --git a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php index cd30fa0c90..fc980608c4 100644 --- a/src/applications/differential/view/DifferentialDiffTableOfContentsView.php +++ b/src/applications/differential/view/DifferentialDiffTableOfContentsView.php @@ -1,270 +1,269 @@ <?php final class DifferentialDiffTableOfContentsView extends AphrontView { private $changesets = array(); private $visibleChangesets = array(); private $references = array(); private $repository; private $diff; private $renderURI = '/differential/changeset/'; private $revisionID; private $whitespace; private $unitTestData; public function setChangesets($changesets) { $this->changesets = $changesets; return $this; } public function setVisibleChangesets($visible_changesets) { $this->visibleChangesets = $visible_changesets; return $this; } public function setRenderingReferences(array $references) { $this->references = $references; return $this; } public function setRepository(PhabricatorRepository $repository) { $this->repository = $repository; return $this; } public function setDiff(DifferentialDiff $diff) { $this->diff = $diff; return $this; } public function setUnitTestData($unit_test_data) { $this->unitTestData = $unit_test_data; return $this; } public function setRevisionID($revision_id) { $this->revisionID = $revision_id; return $this; } public function setWhitespace($whitespace) { $this->whitespace = $whitespace; return $this; } public function render() { require_celerity_resource('differential-core-view-css'); require_celerity_resource('differential-table-of-contents-css'); $rows = array(); $coverage = array(); if ($this->unitTestData) { $coverage_by_file = array(); foreach ($this->unitTestData as $result) { $test_coverage = idx($result, 'coverage'); if (!$test_coverage) { continue; } foreach ($test_coverage as $file => $results) { $coverage_by_file[$file][] = $results; } } foreach ($coverage_by_file as $file => $coverages) { $coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages); } } $changesets = $this->changesets; $paths = array(); foreach ($changesets as $id => $changeset) { $type = $changeset->getChangeType(); $ftype = $changeset->getFileType(); $ref = idx($this->references, $id); $link = $this->renderChangesetLink($changeset, $ref); if (DifferentialChangeType::isOldLocationChangeType($type)) { $away = $changeset->getAwayPaths(); if (count($away) > 1) { $meta = array(); if ($type == DifferentialChangeType::TYPE_MULTICOPY) { $meta[] = pht('Deleted after being copied to multiple locations:'); } else { $meta[] = pht('Copied to multiple locations:'); } foreach ($away as $path) { - $meta[] = phutil_escape_html($path); + $meta[] = $path; } - $meta = implode('<br />', $meta); + $meta = phutil_implode_html(phutil_tag('br'), $meta); } else { if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) { - $meta = pht('Moved to %s', phutil_escape_html(reset($away))); + $meta = pht('Moved to %s', reset($away)); } else { - $meta = pht('Copied to %s', phutil_escape_html(reset($away))); + $meta = pht('Copied to %s', reset($away)); } } } else if ($type == DifferentialChangeType::TYPE_MOVE_HERE) { - $meta = pht('Moved from %s', - phutil_escape_html($changeset->getOldFile())); + $meta = pht('Moved from %s', $changeset->getOldFile()); } else if ($type == DifferentialChangeType::TYPE_COPY_HERE) { - $meta = pht('Copied from %s', - phutil_escape_html($changeset->getOldFile())); + $meta = pht('Copied from %s', $changeset->getOldFile()); } else { $meta = null; } $line_count = $changeset->getAffectedLineCount(); if ($line_count == 0) { $lines = null; } else { $lines = ' '.pht('(%d line(s))', $line_count); } $char = DifferentialChangeType::getSummaryCharacterForChangeType($type); $chartitle = DifferentialChangeType::getFullNameForChangeType($type); $desc = DifferentialChangeType::getShortNameForFileType($ftype); if ($desc) { $desc = '('.$desc.')'; } $pchar = ($changeset->getOldProperties() === $changeset->getNewProperties()) ? null : '<span title="'.pht('Properties Changed').'">M</span>'; $fname = $changeset->getFilename(); $cov = $this->renderCoverage($coverage, $fname); if ($cov === null) { $mcov = $cov = '<em>-</em>'; } else { $mcov = phutil_tag( 'div', array( 'id' => 'differential-mcoverage-'.md5($fname), 'class' => 'differential-mcoverage-loading', ), (isset($this->visibleChangesets[$id]) ? 'Loading...' : '?')); } $rows[] = '<tr>'. phutil_tag( 'td', array( 'class' => 'differential-toc-char', 'title' => $chartitle, ), $char). '<td class="differential-toc-prop">'.$pchar.'</td>'. '<td class="differential-toc-ftype">'.$desc.'</td>'. '<td class="differential-toc-file">'.$link.$lines.'</td>'. '<td class="differential-toc-cov">'.$cov.'</td>'. '<td class="differential-toc-mcov">'.$mcov.'</td>'. '</tr>'; if ($meta) { - $rows[] = + $rows[] = hsprintf( '<tr>'. '<td colspan="3"></td>'. - '<td class="differential-toc-meta">'.$meta.'</td>'. - '</tr>'; + '<td class="differential-toc-meta">%s</td>'. + '</tr>', + $meta); } if ($this->diff && $this->repository) { $paths[] = $changeset->getAbsoluteRepositoryPath($this->repository, $this->diff); } } $editor_link = null; if ($paths && $this->user) { $editor_link = $this->user->loadEditorLink( $paths, 1, // line number $this->repository->getCallsign()); if ($editor_link) { $editor_link = phutil_tag( 'a', array( 'href' => $editor_link, 'class' => 'button differential-toc-edit-all', ), pht('Open All in Editor')); } } $reveal_link = javelin_tag( 'a', array( 'sigil' => 'differential-reveal-all', 'mustcapture' => true, 'class' => 'button differential-toc-reveal-all', ), pht('Show All Context')); $buttons = '<tr><td colspan="7">'. $editor_link.$reveal_link. '</td></tr>'; return id(new PhabricatorAnchorView()) ->setAnchorName('toc') ->setNavigationMarker(true) ->render(). id(new PhabricatorHeaderView()) ->setHeader(pht('Table of Contents')) ->render(). '<div class="differential-toc differential-panel">'. '<table>'. '<tr>'. '<th></th>'. '<th></th>'. '<th></th>'. '<th>Path</th>'. '<th class="differential-toc-cov">'. pht('Coverage (All)'). '</th>'. '<th class="differential-toc-mcov">'. pht('Coverage (Touched)'). '</th>'. '</tr>'. implode("\n", $rows). $buttons. '</table>'. '</div>'; } private function renderCoverage(array $coverage, $file) { $info = idx($coverage, $file); if (!$info) { return null; } $not_covered = substr_count($info, 'U'); $covered = substr_count($info, 'C'); if (!$not_covered && !$covered) { return null; } return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered))); } private function renderChangesetLink(DifferentialChangeset $changeset, $ref) { $display_file = $changeset->getDisplayFilename(); return javelin_tag( 'a', array( 'href' => '#'.$changeset->getAnchorName(), 'meta' => array( 'id' => 'diff-'.$changeset->getAnchorName(), 'ref' => $ref, ), 'sigil' => 'differential-load', ), $display_file); } } diff --git a/src/applications/differential/view/DifferentialRevisionCommentView.php b/src/applications/differential/view/DifferentialRevisionCommentView.php index 251c6d12fd..3f6a46bba0 100644 --- a/src/applications/differential/view/DifferentialRevisionCommentView.php +++ b/src/applications/differential/view/DifferentialRevisionCommentView.php @@ -1,289 +1,307 @@ <?php final class DifferentialRevisionCommentView extends AphrontView { private $comment; private $handles; private $markupEngine; private $preview; private $inlines; private $changesets; private $target; private $anchorName; private $versusDiffID; public function setComment($comment) { $this->comment = $comment; return $this; } public function setHandles(array $handles) { assert_instances_of($handles, 'PhabricatorObjectHandle'); $this->handles = $handles; return $this; } public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) { $this->markupEngine = $markup_engine; return $this; } public function setPreview($preview) { $this->preview = $preview; return $this; } public function setInlineComments(array $inline_comments) { assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface'); $this->inlines = $inline_comments; return $this; } public function setChangesets(array $changesets) { assert_instances_of($changesets, 'DifferentialChangeset'); // Ship these in sorted by getSortKey() and keyed by ID... or else! $this->changesets = $changesets; return $this; } public function setTargetDiff($target) { $this->target = $target; return $this; } public function setVersusDiffID($diff_vs) { $this->versusDiffID = $diff_vs; return $this; } public function setAnchorName($anchor_name) { $this->anchorName = $anchor_name; return $this; } public function render() { if (!$this->user) { throw new Exception("Call setUser() before rendering!"); } require_celerity_resource('phabricator-remarkup-css'); require_celerity_resource('differential-revision-comment-css'); $comment = $this->comment; $action = $comment->getAction(); $action_class = 'differential-comment-action-'.$action; $info = array(); $content = $comment->getContent(); $hide_comments = true; if (strlen(rtrim($content))) { $hide_comments = false; $content = $this->markupEngine->getOutput( $comment, PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY); $content = '<div class="phabricator-remarkup">'. $content. '</div>'; } $inline_render = $this->renderInlineComments(); if ($inline_render) { $hide_comments = false; } $author = $this->handles[$comment->getAuthorPHID()]; $author_link = $author->renderLink(); $metadata = $comment->getMetadata(); $added_reviewers = idx( $metadata, DifferentialComment::METADATA_ADDED_REVIEWERS, array()); $removed_reviewers = idx( $metadata, DifferentialComment::METADATA_REMOVED_REVIEWERS, array()); $added_ccs = idx( $metadata, DifferentialComment::METADATA_ADDED_CCS, array()); $verb = DifferentialAction::getActionPastTenseVerb($comment->getAction()); - $verb = phutil_escape_html($verb); $actions = array(); // TODO: i18n switch ($comment->getAction()) { case DifferentialAction::ACTION_ADDCCS: - $actions[] = "{$author_link} added CCs: ". - $this->renderHandleList($added_ccs)."."; + $actions[] = hsprintf( + "%s added CCs: %s.", + $author_link, + $this->renderHandleList($added_ccs)); $added_ccs = null; break; case DifferentialAction::ACTION_ADDREVIEWERS: - $actions[] = "{$author_link} added reviewers: ". - $this->renderHandleList($added_reviewers)."."; + $actions[] = hsprintf( + "%s added reviewers: %s.", + $author_link, + $this->renderHandleList($added_reviewers)); $added_reviewers = null; break; case DifferentialAction::ACTION_UPDATE: $diff_id = idx($metadata, DifferentialComment::METADATA_DIFF_ID); if ($diff_id) { $diff_link = phutil_tag( 'a', array( 'href' => '/D'.$comment->getRevisionID().'?id='.$diff_id, ), 'Diff #'.$diff_id); - $actions[] = "{$author_link} updated this revision to {$diff_link}."; + $actions[] = hsprintf( + "%s updated this revision to %s.", + $author_link, + $diff_link); } else { - $actions[] = "{$author_link} {$verb} this revision."; + $actions[] = hsprintf( + "%s %s this revision.", + $author_link, + $verb); } break; default: - $actions[] = "{$author_link} {$verb} this revision."; + $actions[] = hsprintf( + "%s %s this revision.", + $author_link, + $verb); break; } if ($added_reviewers) { - $actions[] = "{$author_link} added reviewers: ". - $this->renderHandleList($added_reviewers)."."; + $actions[] = hsprintf( + "%s added reviewers: %s.", + $author_link, + $this->renderHandleList($added_reviewers)); } if ($removed_reviewers) { - $actions[] = "{$author_link} removed reviewers: ". - $this->renderHandleList($removed_reviewers)."."; + $actions[] = hsprintf( + "%s removed reviewers: %s.", + $author_link, + $this->renderHandleList($removed_reviewers)); } if ($added_ccs) { - $actions[] = "{$author_link} added CCs: ". - $this->renderHandleList($added_ccs)."."; + $actions[] = hsprintf( + "%s added CCs: %s.", + $author_link, + $this->renderHandleList($added_ccs)); } foreach ($actions as $key => $action) { - $actions[$key] = '<div>'.$action.'</div>'; + $actions[$key] = phutil_tag('div', array(), $action); } $xaction_view = id(new PhabricatorTransactionView()) ->setUser($this->user) ->setImageURI($author->getImageURI()) ->setContentSource($comment->getContentSource()) ->addClass($action_class) ->setActions($actions); if ($this->preview) { $xaction_view->setIsPreview($this->preview); } else { $xaction_view->setEpoch($comment->getDateCreated()); if ($this->anchorName) { $anchor_text = 'D'.$comment->getRevisionID(). '#'.preg_replace('/^comment-/', '', $this->anchorName); $xaction_view->setAnchor($this->anchorName, $anchor_text); } } if (!$hide_comments) { $xaction_view->appendChild( '<div class="differential-comment-core">'. $content. '</div>'. $this->renderSingleView($inline_render)); } return $xaction_view->render(); } private function renderHandleList(array $phids) { $result = array(); foreach ($phids as $phid) { $result[] = $this->handles[$phid]->renderLink(); } - return implode(', ', $result); + return phutil_implode_html(', ', $result); } private function renderInlineComments() { if (!$this->inlines) { return null; } $inlines = $this->inlines; $changesets = $this->changesets; $inlines_by_changeset = mgroup($inlines, 'getChangesetID'); $inlines_by_changeset = array_select_keys( $inlines_by_changeset, array_keys($this->changesets)); $view = new PhabricatorInlineSummaryView(); foreach ($inlines_by_changeset as $changeset_id => $inlines) { $changeset = $changesets[$changeset_id]; $items = array(); foreach ($inlines as $inline) { $on_target = ($this->target) && ($this->target->getID() == $changeset->getDiffID()); $is_visible = false; if ($inline->getIsNewFile()) { // This comment is on the right side of the versus diff, and visible // on the left side of the page. if ($this->versusDiffID) { if ($changeset->getDiffID() == $this->versusDiffID) { $is_visible = true; } } // This comment is on the right side of the target diff, and visible // on the right side of the page. if ($on_target) { $is_visible = true; } } else { // Ths comment is on the left side of the target diff, and visible // on the left side of the page. if (!$this->versusDiffID) { if ($on_target) { $is_visible = true; } } // TODO: We still get one edge case wrong here, when we have a // versus diff and the file didn't exist in the old version. The // comment is visible because we show the left side of the target // diff when there's no corresponding file in the versus diff, but // we incorrectly link it off-page. } $item = array( 'id' => $inline->getID(), 'line' => $inline->getLineNumber(), 'length' => $inline->getLineLength(), 'content' => $this->markupEngine->getOutput( $inline, DifferentialInlineComment::MARKUP_FIELD_BODY), ); if (!$is_visible) { $diff_id = $changeset->getDiffID(); $item['where'] = '(On Diff #'.$diff_id.')'; $item['href'] = 'D'.$this->comment->getRevisionID(). '?id='.$diff_id. '#inline-'.$inline->getID(); } $items[] = $item; } $view->addCommentGroup($changeset->getFilename(), $items); } return $view; } } diff --git a/src/applications/diffusion/view/DiffusionCommentView.php b/src/applications/diffusion/view/DiffusionCommentView.php index 1b37302b34..bdd2257a73 100644 --- a/src/applications/diffusion/view/DiffusionCommentView.php +++ b/src/applications/diffusion/view/DiffusionCommentView.php @@ -1,208 +1,210 @@ <?php final class DiffusionCommentView extends AphrontView { private $comment; private $commentNumber; private $handles; private $isPreview; private $pathMap; private $inlineComments; private $markupEngine; public function setComment(PhabricatorAuditComment $comment) { $this->comment = $comment; return $this; } public function setCommentNumber($comment_number) { $this->commentNumber = $comment_number; return $this; } public function setHandles(array $handles) { assert_instances_of($handles, 'PhabricatorObjectHandle'); $this->handles = $handles; return $this; } public function setIsPreview($is_preview) { $this->isPreview = $is_preview; return $this; } public function setInlineComments(array $inline_comments) { assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface'); $this->inlineComments = $inline_comments; return $this; } public function setPathMap(array $path_map) { $this->pathMap = $path_map; return $this; } public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) { $this->markupEngine = $markup_engine; return $this; } public function getMarkupEngine() { return $this->markupEngine; } public function getRequiredHandlePHIDs() { return array($this->comment->getActorPHID()); } private function getHandle($phid) { if (empty($this->handles[$phid])) { throw new Exception("Unloaded handle '{$phid}'!"); } return $this->handles[$phid]; } public function render() { $comment = $this->comment; $author = $this->getHandle($comment->getActorPHID()); $author_link = $author->renderLink(); $actions = $this->renderActions(); $content = $this->renderContent(); $classes = $this->renderClasses(); $xaction_view = id(new PhabricatorTransactionView()) ->setUser($this->user) ->setImageURI($author->getImageURI()) ->setActions($actions) ->appendChild($content); if ($this->isPreview) { $xaction_view->setIsPreview(true); } else { $xaction_view ->setAnchor('comment-'.$this->commentNumber, '#'.$this->commentNumber) ->setEpoch($comment->getDateCreated()); } foreach ($classes as $class) { $xaction_view->addClass($class); } return $xaction_view->render(); } private function renderActions() { $comment = $this->comment; $author = $this->getHandle($comment->getActorPHID()); $author_link = $author->renderLink(); $action = $comment->getAction(); $verb = PhabricatorAuditActionConstants::getActionPastTenseVerb($action); $metadata = $comment->getMetadata(); $added_auditors = idx( $metadata, PhabricatorAuditComment::METADATA_ADDED_AUDITORS, array()); $added_ccs = idx( $metadata, PhabricatorAuditComment::METADATA_ADDED_CCS, array()); $actions = array(); if ($action == PhabricatorAuditActionConstants::ADD_CCS) { $rendered_ccs = $this->renderHandleList($added_ccs); - $actions[] = "{$author_link} added CCs: {$rendered_ccs}."; + $actions[] = hsprintf("%s added CCs: %s.", $author_link, $rendered_ccs); } else if ($action == PhabricatorAuditActionConstants::ADD_AUDITORS) { $rendered_auditors = $this->renderHandleList($added_auditors); - $actions[] = "{$author_link} added auditors: ". - "{$rendered_auditors}."; + $actions[] = hsprintf( + "%s added auditors: %s.", + $author_link, + $rendered_auditors); } else { - $actions[] = "{$author_link} ".phutil_escape_html($verb)." this commit."; + $actions[] = hsprintf("%s %s this commit.", $author_link, $verb); } foreach ($actions as $key => $action) { - $actions[$key] = '<div>'.$action.'</div>'; + $actions[$key] = phutil_tag('div', array(), $action); } return $actions; } private function renderContent() { $comment = $this->comment; $engine = $this->getMarkupEngine(); if (!strlen($comment->getContent()) && empty($this->inlineComments)) { return null; } else { return '<div class="phabricator-remarkup">'. $engine->getOutput( $comment, PhabricatorAuditComment::MARKUP_FIELD_BODY). $this->renderSingleView($this->renderInlines()). '</div>'; } } private function renderInlines() { if (!$this->inlineComments) { return null; } $engine = $this->getMarkupEngine(); $inlines_by_path = mgroup($this->inlineComments, 'getPathID'); $view = new PhabricatorInlineSummaryView(); foreach ($inlines_by_path as $path_id => $inlines) { $path = idx($this->pathMap, $path_id); if ($path === null) { continue; } $items = array(); foreach ($inlines as $inline) { $items[] = array( 'id' => $inline->getID(), 'line' => $inline->getLineNumber(), 'length' => $inline->getLineLength(), 'content' => $engine->getOutput( $inline, PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY), ); } $view->addCommentGroup($path, $items); } return $view; } private function renderHandleList(array $phids) { $result = array(); foreach ($phids as $phid) { $result[] = $this->handles[$phid]->renderLink(); } - return implode(', ', $result); + return phutil_implode_html(', ', $result); } private function renderClasses() { $comment = $this->comment; $classes = array(); switch ($comment->getAction()) { case PhabricatorAuditActionConstants::ACCEPT: $classes[] = 'audit-accept'; break; case PhabricatorAuditActionConstants::CONCERN: $classes[] = 'audit-concern'; break; } return $classes; } } diff --git a/src/applications/feed/story/PhabricatorFeedStoryCommit.php b/src/applications/feed/story/PhabricatorFeedStoryCommit.php index 0e2030cbf3..57989e200c 100644 --- a/src/applications/feed/story/PhabricatorFeedStoryCommit.php +++ b/src/applications/feed/story/PhabricatorFeedStoryCommit.php @@ -1,95 +1,102 @@ <?php final class PhabricatorFeedStoryCommit extends PhabricatorFeedStory { public function getPrimaryObjectPHID() { return $this->getValue('commitPHID'); } public function getRequiredHandlePHIDs() { return array( $this->getValue('committerPHID'), ); } public function renderView() { $data = $this->getStoryData(); $author = null; if ($data->getValue('authorPHID')) { $author = $this->linkTo($data->getValue('authorPHID')); } else { - $author = phutil_escape_html($data->getValue('authorName')); + $author = $data->getValue('authorName'); } $committer = null; if ($data->getValue('committerPHID')) { $committer = $this->linkTo($data->getValue('committerPHID')); } else if ($data->getValue('committerName')) { - $committer = phutil_escape_html($data->getValue('committerName')); + $committer = $data->getValue('committerName'); } $commit = $this->linkTo($data->getValue('commitPHID')); if (!$committer) { $committer = $author; $author = null; } if ($author) { - $title = "{$committer} committed {$commit} (authored by {$author})"; + $title = hsprintf( + "%s committed %s (authored by %s)", + $committer, + $commit, + $author); } else { - $title = "{$committer} committed {$commit}"; + $title = hsprintf( + "%s committed %s", + $committer, + $commit); } $view = new PhabricatorFeedStoryView(); $view->setTitle($title); $view->setEpoch($data->getEpoch()); if ($data->getValue('authorPHID')) { $view->setImage($this->getHandle($data->getAuthorPHID())->getImageURI()); } $content = $this->renderSummary($data->getValue('summary')); $view->appendChild($content); return $view; } public function renderText() { $author = null; if ($this->getAuthorPHID()) { $author = $this->getHandle($this->getAuthorPHID())->getLinkName(); } else { $author = $this->getValue('authorName'); } $committer = null; if ($this->getValue('committerPHID')) { $committer_handle = $this->getHandle($this->getValue('committerPHID')); $committer = $committer_handle->getLinkName(); } else if ($this->getValue('committerName')) { $committer = $this->getValue('committerName'); } $commit_handle = $this->getHandle($this->getPrimaryObjectPHID()); $commit_uri = PhabricatorEnv::getURI($commit_handle->getURI()); $commit_name = $commit_handle->getLinkName(); if (!$committer) { $committer = $author; $author = null; } if ($author) { $text = "{$committer} (authored by {$author})". "committed {$commit_name} {$commit_uri}"; } else { $text = "{$committer} committed {$commit_name} {$commit_uri}"; } return $text; } } diff --git a/src/applications/flag/events/PhabricatorFlagsUIEventListener.php b/src/applications/flag/events/PhabricatorFlagsUIEventListener.php index 1ec6e7f1c0..13ba11321b 100644 --- a/src/applications/flag/events/PhabricatorFlagsUIEventListener.php +++ b/src/applications/flag/events/PhabricatorFlagsUIEventListener.php @@ -1,54 +1,54 @@ <?php final class PhabricatorFlagsUIEventListener extends PhutilEventListener { public function register() { $this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS); } public function handleEvent(PhutilEvent $event) { switch ($event->getType()) { case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: $this->handleActionEvent($event); break; } } private function handleActionEvent($event) { $user = $event->getUser(); $object = $event->getValue('object'); if (!$object || !$object->getPHID()) { // If we have no object, or the object doesn't have a PHID yet, we can't // flag it. return; } $flag = PhabricatorFlagQuery::loadUserFlag($user, $object->getPHID()); if ($flag) { $color = PhabricatorFlagColor::getColorName($flag->getColor()); $flag_action = id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/flag/delete/'.$flag->getID().'/') - ->setName(phutil_escape_html('Remove '.$color.' Flag')) + ->setName('Remove '.$color.' Flag') ->setIcon('flag-'.$flag->getColor()); } else { $flag_action = id(new PhabricatorActionView()) ->setWorkflow(true) ->setHref('/flag/edit/'.$object->getPHID().'/') ->setName('Flag For Later') ->setIcon('flag-ghost'); if (!$user->isLoggedIn()) { $flag_action->setDisabled(true); } } $actions = $event->getValue('actions'); $actions[] = $flag_action; $event->setValue('actions', $actions); } } diff --git a/src/applications/macro/storage/PhabricatorMacroTransaction.php b/src/applications/macro/storage/PhabricatorMacroTransaction.php index 5711d7899e..bd0ac70c49 100644 --- a/src/applications/macro/storage/PhabricatorMacroTransaction.php +++ b/src/applications/macro/storage/PhabricatorMacroTransaction.php @@ -1,231 +1,231 @@ <?php final class PhabricatorMacroTransaction extends PhabricatorApplicationTransaction { public function getApplicationName() { return 'file'; } public function getTableName() { return 'macro_transaction'; } public function getApplicationTransactionType() { return PhabricatorPHIDConstants::PHID_TYPE_MCRO; } public function getApplicationTransactionCommentObject() { return new PhabricatorMacroTransactionComment(); } public function getApplicationObjectTypeName() { return pht('macro'); } public function getRequiredHandlePHIDs() { $phids = parent::getRequiredHandlePHIDs(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_FILE: if ($old !== null) { $phids[] = $old; } $phids[] = $new; break; } return $phids; } public function shouldHide() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_NAME: return ($old === null); } return parent::shouldHide(); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_NAME: return pht( '%s renamed this macro from "%s" to "%s".', $this->renderHandleLink($author_phid), - phutil_escape_html($old), - phutil_escape_html($new)); + $old, + $new); break; case PhabricatorMacroTransactionType::TYPE_DISABLED: if ($new) { return pht( '%s disabled this macro.', $this->renderHandleLink($author_phid)); } else { return pht( '%s restored this macro.', $this->renderHandleLink($author_phid)); } break; case PhabricatorMacroTransactionType::TYPE_FILE: if ($old === null) { return pht( '%s created this macro.', $this->renderHandleLink($author_phid)); } else { return pht( '%s changed the image for this macro from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($old), $this->renderHandleLink($new)); } break; } return parent::getTitle(); } public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_NAME: return pht( '%s renamed %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), - phutil_escape_html($old), - phutil_escape_html($new)); + $old, + $new); case PhabricatorMacroTransactionType::TYPE_DISABLED: if ($new) { return pht( '%s disabled %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } else { return pht( '%s restored %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } case PhabricatorMacroTransactionType::TYPE_FILE: if ($old === null) { return pht( '%s created %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } else { return pht( '%s updated the image for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } } return parent::getTitleForFeed(); } public function getActionName() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_NAME: if ($old === null) { return pht('Created'); } else { return pht('Renamed'); } case PhabricatorMacroTransactionType::TYPE_DISABLED: if ($new) { return pht('Disabled'); } else { return pht('Restored'); } case PhabricatorMacroTransactionType::TYPE_FILE: if ($old === null) { return pht('Created'); } else { return pht('Edited Image'); } } return parent::getActionName(); } public function getActionStrength() { switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_DISABLED: return 2.0; case PhabricatorMacroTransactionType::TYPE_FILE: return 1.5; } return parent::getActionStrength(); } public function getIcon() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_NAME: return 'edit'; case PhabricatorMacroTransactionType::TYPE_FILE: if ($old === null) { return 'create'; } else { return 'edit'; } case PhabricatorMacroTransactionType::TYPE_DISABLED: if ($new) { return 'delete'; } else { return 'undo'; } } return parent::getIcon(); } public function getColor() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorMacroTransactionType::TYPE_NAME: return PhabricatorTransactions::COLOR_BLUE; case PhabricatorMacroTransactionType::TYPE_FILE: if ($old === null) { return PhabricatorTransactions::COLOR_GREEN; } else { return PhabricatorTransactions::COLOR_BLUE; } case PhabricatorMacroTransactionType::TYPE_DISABLED: if ($new) { return PhabricatorTransactions::COLOR_BLACK; } else { return PhabricatorTransactions::COLOR_SKY; } } return parent::getColor(); } } diff --git a/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldDefaultSpecification.php b/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldDefaultSpecification.php index b393a6e1de..67abdf407d 100644 --- a/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldDefaultSpecification.php +++ b/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldDefaultSpecification.php @@ -1,222 +1,222 @@ <?php /** * @group maniphest */ class ManiphestAuxiliaryFieldDefaultSpecification extends ManiphestAuxiliaryFieldSpecification { private $required; private $fieldType; private $selectOptions; private $checkboxLabel; private $checkboxValue; private $error; private $shouldCopyWhenCreatingSimilarTask; const TYPE_SELECT = 'select'; const TYPE_STRING = 'string'; const TYPE_INT = 'int'; const TYPE_BOOL = 'bool'; public function getFieldType() { return $this->fieldType; } public function setFieldType($val) { $this->fieldType = $val; return $this; } public function getError() { return $this->error; } public function setError($val) { $this->error = $val; return $this; } public function getSelectOptions() { return $this->selectOptions; } public function setSelectOptions($array) { $this->selectOptions = $array; return $this; } public function setRequired($bool) { $this->required = $bool; return $this; } public function isRequired() { return $this->required; } public function setCheckboxLabel($checkbox_label) { $this->checkboxLabel = $checkbox_label; return $this; } public function getCheckboxLabel() { return $this->checkboxLabel; } public function setCheckboxValue($checkbox_value) { $this->checkboxValue = $checkbox_value; return $this; } public function getCheckboxValue() { return $this->checkboxValue; } public function renderControl() { $control = null; $type = $this->getFieldType(); switch ($type) { case self::TYPE_INT: $control = new AphrontFormTextControl(); break; case self::TYPE_STRING: $control = new AphrontFormTextControl(); break; case self::TYPE_SELECT: $control = new AphrontFormSelectControl(); $control->setOptions($this->getSelectOptions()); break; case self::TYPE_BOOL: $control = new AphrontFormCheckboxControl(); break; default: $label = $this->getLabel(); throw new ManiphestAuxiliaryFieldTypeException( "Field type '{$type}' is not a valid type (for field '{$label}')."); break; } if ($type == self::TYPE_BOOL) { $control->addCheckbox( 'auxiliary['.$this->getAuxiliaryKey().']', 1, $this->getCheckboxLabel(), (bool)$this->getValue()); } else { $control->setValue($this->getValue()); $control->setName('auxiliary['.$this->getAuxiliaryKey().']'); } $control->setLabel($this->getLabel()); $control->setCaption($this->getCaption()); $control->setError($this->getError()); return $control; } public function setValueFromRequest($request) { $aux_post_values = $request->getArr('auxiliary'); return $this->setValue(idx($aux_post_values, $this->getAuxiliaryKey(), '')); } public function getValueForStorage() { return $this->getValue(); } public function setValueFromStorage($value) { return $this->setValue($value); } public function validate() { switch ($this->getFieldType()) { case self::TYPE_INT: if (!is_numeric($this->getValue())) { throw new ManiphestAuxiliaryFieldValidationException( $this->getLabel().' must be an integer value.' ); } break; case self::TYPE_BOOL: return true; case self::TYPE_STRING: return true; case self::TYPE_SELECT: return true; } } public function renderForDetailView() { switch ($this->getFieldType()) { case self::TYPE_BOOL: if ($this->getValue()) { - return phutil_escape_html($this->getCheckboxValue()); + return $this->getCheckboxValue(); } else { return null; } case self::TYPE_SELECT: $display = idx($this->getSelectOptions(), $this->getValue()); - return phutil_escape_html($display); + return $display; } return parent::renderForDetailView(); } public function renderTransactionDescription( ManiphestTransaction $transaction, $target) { $label = $this->getLabel(); $old = $transaction->getOldValue(); $new = $transaction->getNewValue(); switch ($this->getFieldType()) { case self::TYPE_BOOL: if ($new) { $desc = "set field '{$label}' true"; } else { $desc = "set field '{$label}' false"; } break; case self::TYPE_SELECT: $old_display = idx($this->getSelectOptions(), $old); $new_display = idx($this->getSelectOptions(), $new); if ($old === null) { $desc = "set field '{$label}' to '{$new_display}'"; } else { $desc = "changed field '{$label}' ". "from '{$old_display}' to '{$new_display}'"; } break; default: if (!strlen($old)) { if (!strlen($new)) { return null; } $desc = "set field '{$label}' to '{$new}'"; } else { $desc = "updated '{$label}' ". "from '{$old}' to '{$new}'"; } break; } if ($target == self::RENDER_TARGET_HTML) { $desc = phutil_escape_html($desc); } return $desc; } public function setShouldCopyWhenCreatingSimilarTask($copy) { $this->shouldCopyWhenCreatingSimilarTask = $copy; return $this; } public function shouldCopyWhenCreatingSimilarTask() { return $this->shouldCopyWhenCreatingSimilarTask; } } diff --git a/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldSpecification.php b/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldSpecification.php index fba027e838..216227194d 100644 --- a/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldSpecification.php +++ b/src/applications/maniphest/auxiliaryfield/ManiphestAuxiliaryFieldSpecification.php @@ -1,141 +1,141 @@ <?php /** * @group maniphest */ abstract class ManiphestAuxiliaryFieldSpecification { const RENDER_TARGET_HTML = 'html'; const RENDER_TARGET_TEXT = 'text'; private $label; private $auxiliaryKey; private $caption; private $value; public function setLabel($val) { $this->label = $val; return $this; } public function getLabel() { return $this->label; } public function setAuxiliaryKey($val) { $this->auxiliaryKey = $val; return $this; } public function getAuxiliaryKey() { return $this->auxiliaryKey; } public function setCaption($val) { $this->caption = $val; return $this; } public function getCaption() { return $this->caption; } public function setValue($val) { $this->value = $val; return $this; } public function getValue() { return $this->value; } public function validate() { return true; } public function isRequired() { return false; } public function setType($val) { $this->type = $val; return $this; } public function getType() { return $this->type; } public function renderControl() { return null; } public function renderForDetailView() { - return phutil_escape_html($this->getValue()); + return $this->getValue(); } /** * When the user creates a task, the UI prompts them to "Create another * similar task". This copies some fields (e.g., Owner and CCs) but not other * fields (e.g., description). If this custom field should also be copied, * return true from this method. * * @return bool True to copy the default value from the template task when * creating a new similar task. */ public function shouldCopyWhenCreatingSimilarTask() { return false; } /** * Render a verb to appear in email titles when a transaction involving this * field occurs. Specifically, Maniphest emails are formatted like this: * * [Maniphest] [Verb Here] TNNN: Task title here * ^^^^^^^^^ * * You should optionally return a title-case verb or short phrase like * "Created", "Retitled", "Closed", "Resolved", "Commented On", * "Lowered Priority", etc., which describes the transaction. * * @param ManiphestTransaction The transaction which needs description. * @return string|null A short description of the transaction. */ public function renderTransactionEmailVerb( ManiphestTransaction $transaction) { return null; } /** * Render a short description of the transaction, to appear above comments * in the Maniphest transaction log. The string will be rendered after the * acting user's name. Examples are: * * added a comment * added alincoln to CC * claimed this task * created this task * closed this task out of spite * * You should return a similar string, describing the transaction. * * Note the ##$target## parameter -- Maniphest needs to render transaction * descriptions for different targets, like web and email. This method will * be called with a ##ManiphestAuxiliaryFieldSpecification::RENDER_TARGET_*## * constant describing the intended target. * * @param ManiphestTransaction The transaction which needs description. * @param const Constant describing the rendering target (e.g., html or text). * @return string|null Description of the transaction. */ public function renderTransactionDescription( ManiphestTransaction $transaction, $target) { return 'updated a custom field'; } } diff --git a/src/applications/paste/controller/PhabricatorPasteListController.php b/src/applications/paste/controller/PhabricatorPasteListController.php index def6cc8f66..7d22d6fd62 100644 --- a/src/applications/paste/controller/PhabricatorPasteListController.php +++ b/src/applications/paste/controller/PhabricatorPasteListController.php @@ -1,121 +1,121 @@ <?php final class PhabricatorPasteListController extends PhabricatorPasteController { public function shouldRequireLogin() { return false; } private $filter; public function willProcessRequest(array $data) { $this->filter = idx($data, 'filter'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $query = id(new PhabricatorPasteQuery()) ->setViewer($user) ->needContent(true); $nav = $this->buildSideNavView($this->filter); $filter = $nav->getSelectedFilter(); switch ($filter) { case 'my': $query->withAuthorPHIDs(array($user->getPHID())); $title = pht('My Pastes'); $nodata = pht("You haven't created any Pastes yet."); break; case 'all': $title = pht('All Pastes'); $nodata = pht("There are no Pastes yet."); break; } $pager = new AphrontCursorPagerView(); $pager->readFromRequest($request); $pastes = $query->executeWithCursorPager($pager); $list = $this->buildPasteList($pastes); $list->setPager($pager); $list->setNoDataString($nodata); $header = id(new PhabricatorHeaderView()) ->setHeader($title); $nav->appendChild( array( $header, $list, )); $crumbs = $this ->buildApplicationCrumbs($nav) ->addCrumb( id(new PhabricatorCrumbView()) ->setName($title) ->setHref($this->getApplicationURI('filter/'.$filter.'/'))); $nav->setCrumbs($crumbs); return $this->buildApplicationPage( $nav, array( 'title' => $title, 'device' => true, ) ); } private function buildPasteList(array $pastes) { assert_instances_of($pastes, 'PhabricatorPaste'); $user = $this->getRequest()->getUser(); $this->loadHandles(mpull($pastes, 'getAuthorPHID')); $lang_map = PhabricatorEnv::getEnvConfig('pygments.dropdown-choices'); $list = new PhabricatorObjectItemListView(); $list->setUser($user); foreach ($pastes as $paste) { $created = phabricator_date($paste->getDateCreated(), $user); $author = $this->getHandle($paste->getAuthorPHID())->renderLink(); $source_code = $this->buildSourceCodeView($paste, 5)->render(); $source_code = phutil_tag( 'div', array( 'class' => 'phabricator-source-code-summary', ), $source_code); $line_count = count(explode("\n", $paste->getContent())); $line_count = pht( '%s Line(s)', new PhutilNumber($line_count)); $item = id(new PhabricatorObjectItemView()) ->setHeader($paste->getFullName()) ->setHref('/P'.$paste->getID()) ->setObject($paste) ->addAttribute(pht('Created %s by %s', $created, $author)) ->addIcon('none', $line_count) ->appendChild($source_code); $lang_name = $paste->getLanguage(); if ($lang_name) { $lang_name = idx($lang_map, $lang_name, $lang_name); - $item->addIcon('none', phutil_escape_html($lang_name)); + $item->addIcon('none', $lang_name); } $list->addItem($item); } return $list; } } diff --git a/src/applications/pholio/storage/PholioTransaction.php b/src/applications/pholio/storage/PholioTransaction.php index 55a5ab7ba6..ece88cbdd5 100644 --- a/src/applications/pholio/storage/PholioTransaction.php +++ b/src/applications/pholio/storage/PholioTransaction.php @@ -1,62 +1,62 @@ <?php /** * @group pholio */ final class PholioTransaction extends PhabricatorApplicationTransaction { public function getApplicationName() { return 'pholio'; } public function getApplicationTransactionType() { return PhabricatorPHIDConstants::PHID_TYPE_MOCK; } public function getApplicationTransactionCommentObject() { return new PholioTransactionComment(); } public function getApplicationObjectTypeName() { return pht('mock'); } public function shouldHide() { $old = $this->getOldValue(); switch ($this->getTransactionType()) { case PholioTransactionType::TYPE_NAME: case PholioTransactionType::TYPE_DESCRIPTION: return ($old === null); } return parent::shouldHide(); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PholioTransactionType::TYPE_NAME: return pht( '%s renamed this mock from "%s" to "%s".', $this->renderHandleLink($author_phid), - phutil_escape_html($old), - phutil_escape_html($new)); + $old, + $new); break; case PholioTransactionType::TYPE_DESCRIPTION: return pht( '%s updated the description of this mock. '. 'The old description was: %s', $this->renderHandleLink($author_phid), - phutil_escape_html($old)); + $old); } return parent::getTitle(); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewInputController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewInputController.php index 0d90598190..48ba6afc33 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewInputController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewInputController.php @@ -1,11 +1,10 @@ <?php final class PhabricatorXHPASTViewInputController extends PhabricatorXHPASTViewPanelController { public function processRequest() { $input = $this->getStorageTree()->getInput(); - return $this->buildXHPASTViewPanelResponse( - phutil_escape_html($input)); + return $this->buildXHPASTViewPanelResponse($input); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php index b4494eb44e..0e43c418f1 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php @@ -1,71 +1,70 @@ <?php abstract class PhabricatorXHPASTViewPanelController extends PhabricatorXHPASTViewController { private $id; private $storageTree; public function willProcessRequest(array $data) { $this->id = $data['id']; $this->storageTree = id(new PhabricatorXHPASTViewParseTree()) ->load($this->id); if (!$this->storageTree) { throw new Exception("No such AST!"); } } protected function getStorageTree() { return $this->storageTree; } protected function buildXHPASTViewPanelResponse($content) { - $content = + $content = hsprintf( '<!DOCTYPE html>'. '<html>'. '<head>'. '<style type="text/css"> body { white-space: pre; font: 10px "Monaco"; cursor: pointer; } .token { padding: 2px 4px; margin: 2px 2px; border: 1px solid #bbbbbb; line-height: 24px; } ul { margin: 0 0 0 1em; padding: 0; list-style: none; line-height: 1em; } li { margin: 0; padding: 0; } li span { background: #dddddd; padding: 3px 6px; } </style>'. '</head>'. - '<body>'. - $content. - '</body>'. - '</html>'; + '<body>%s</body>'. + '</html>', + $content); $response = new AphrontWebpageResponse(); $response->setFrameable(true); $response->setContent($content); return $response; } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php index bf7c620be9..4931ab29e3 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php @@ -1,32 +1,33 @@ <?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_tag( 'span', array( 'title' => $title, 'class' => 'token', ), $token->getValue()); } - return $this->buildXHPASTViewPanelResponse(implode('', $tokens)); + return $this->buildXHPASTViewPanelResponse( + phutil_implode_html('', $tokens)); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php index 5b7673386e..b332784e92 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>'; + $tree = phutil_tag('ul', array(), $this->buildTree($tree->getRootNode())); 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_tag( - 'span', - array( - 'title' => $title, - ), - $name). - '</li>'; + $tree[] = phutil_tag( + 'li', + array(), + phutil_tag( + 'span', + array( + 'title' => $title, + ), + $name)); foreach ($root->getChildren() as $child) { - $tree[] = '<ul>'.$this->buildTree($child).'</ul>'; + $tree[] = phutil_tag('ul', array(), $this->buildTree($child)); } - return implode("\n", $tree); + return phutil_implode_html("\n", $tree); } } diff --git a/src/applications/policy/filter/PhabricatorPolicy.php b/src/applications/policy/filter/PhabricatorPolicy.php index 305cae4cd1..8f8d1120f8 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_tag( 'a', array( 'href' => $this->getHref(), ), $this->getName()); } else { - $desc = phutil_escape_html($this->getName()); + $desc = $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/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php index 3a3c7deb3a..93e5144ae0 100644 --- a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php +++ b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php @@ -1,84 +1,84 @@ <?php final class PhabricatorSubscriptionsUIEventListener extends PhutilEventListener { public function register() { $this->listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS); } public function handleEvent(PhutilEvent $event) { switch ($event->getType()) { case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: $this->handleActionEvent($event); break; } } private function handleActionEvent($event) { $user = $event->getUser(); $object = $event->getValue('object'); if (!$object || !$object->getPHID()) { // No object, or the object has no PHID yet. No way to subscribe. return; } if (!($object instanceof PhabricatorSubscribableInterface)) { // This object isn't subscribable. return; } if ($object->isAutomaticallySubscribed($user->getPHID())) { $sub_action = id(new PhabricatorActionView()) ->setWorkflow(true) ->setUser($user) ->setDisabled(true) ->setRenderAsForm(true) ->setHref('/subscriptions/add/'.$object->getPHID().'/') - ->setName(phutil_escape_html('Automatically Subscribed')) + ->setName('Automatically Subscribed') ->setIcon('subscribe-auto'); } else { $subscribed = false; if ($user->isLoggedIn()) { $src_phid = $object->getPHID(); $dst_phid = $user->getPHID(); $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_SUBSCRIBER; $edges = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($src_phid)) ->withEdgeTypes(array($edge_type)) ->withDestinationPHIDs(array($user->getPHID())) ->execute(); $subscribed = isset($edges[$src_phid][$edge_type][$dst_phid]); } if ($subscribed) { $sub_action = id(new PhabricatorActionView()) ->setUser($user) ->setWorkflow(true) ->setRenderAsForm(true) ->setHref('/subscriptions/delete/'.$object->getPHID().'/') - ->setName(phutil_escape_html('Unsubscribe')) + ->setName('Unsubscribe') ->setIcon('subscribe-delete'); } else { $sub_action = id(new PhabricatorActionView()) ->setUser($user) ->setWorkflow(true) ->setRenderAsForm(true) ->setHref('/subscriptions/add/'.$object->getPHID().'/') - ->setName(phutil_escape_html('Subscribe')) + ->setName('Subscribe') ->setIcon('subscribe-add'); } if (!$user->isLoggedIn()) { $sub_action->setDisabled(true); } } $actions = $event->getValue('actions'); $actions[] = $sub_action; $event->setValue('actions', $actions); } } diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index 00bbde7e3d..6494929c15 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -1,348 +1,348 @@ <?php abstract class PhabricatorApplicationTransaction extends PhabricatorLiskDAO implements PhabricatorPolicyInterface { const TARGET_TEXT = 'text'; const TARGET_HTML = 'html'; protected $phid; protected $objectPHID; protected $authorPHID; protected $viewPolicy; protected $editPolicy; protected $commentPHID; protected $commentVersion = 0; protected $transactionType; protected $oldValue; protected $newValue; protected $contentSource; private $comment; private $commentNotLoaded; private $handles; private $renderingTarget = self::TARGET_HTML; abstract public function getApplicationTransactionType(); abstract public function getApplicationTransactionCommentObject(); abstract public function getApplicationObjectTypeName(); public function generatePHID() { $type = PhabricatorPHIDConstants::PHID_TYPE_XACT; $subtype = $this->getApplicationTransactionType(); return PhabricatorPHID::generateNewPHID($type, $subtype); } public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'oldValue' => self::SERIALIZATION_JSON, 'newValue' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function setContentSource(PhabricatorContentSource $content_source) { $this->contentSource = $content_source->serialize(); return $this; } public function getContentSource() { return PhabricatorContentSource::newFromSerialized($this->contentSource); } public function hasComment() { return $this->getComment() && strlen($this->getComment()->getContent()); } public function getComment() { if ($this->commentNotLoaded) { throw new Exception("Comment for this transaction was not loaded."); } return $this->comment; } public function attachComment( PhabricatorApplicationTransactionComment $comment) { $this->comment = $comment; $this->commentNotLoaded = false; return $this; } public function setCommentNotLoaded($not_loaded) { $this->commentNotLoaded = $not_loaded; return $this; } /* -( Rendering )---------------------------------------------------------- */ public function setRenderingTarget($rendering_target) { $this->renderingTarget = $rendering_target; return $this; } public function getRenderingTarget() { return $this->renderingTarget; } public function getRequiredHandlePHIDs() { $phids = array(); $old = $this->getOldValue(); $new = $this->getNewValue(); $phids[] = array($this->getAuthorPHID()); switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_SUBSCRIBERS: $phids[] = $old; $phids[] = $new; break; } return array_mergev($phids); } public function setHandles(array $handles) { $this->handles = $handles; return $this; } public function getHandle($phid) { if (empty($this->handles[$phid])) { throw new Exception( "Transaction requires a handle ('{$phid}') it did not load."); } return $this->handles[$phid]; } public function getHandles() { if ($this->handles === null) { throw new Exception( 'Transaction requires handles and it did not load them.' ); } return $this->handles; } protected function renderHandleLink($phid) { if ($this->renderingTarget == self::TARGET_HTML) { return $this->getHandle($phid)->renderLink(); } else { - return $this->getHandle($phid)->getName(); + return hsprintf('%s', $this->getHandle($phid)->getName()); } } protected function renderHandleList(array $phids) { $links = array(); foreach ($phids as $phid) { $links[] = $this->renderHandleLink($phid); } - return phutil_safe_html(implode(', ', $links)); + return phutil_implode_html(', ', $links); } public function getIcon() { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: return 'comment'; case PhabricatorTransactions::TYPE_SUBSCRIBERS: return 'message'; case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: return 'lock'; } return null; } public function getColor() { return null; } public function shouldHide() { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: if ($this->getOldValue() === null) { return true; } else { return false; } break; } return false; } public function getNoEffectDescription() { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: return pht('You can not post an empty comment.'); case PhabricatorTransactions::TYPE_VIEW_POLICY: return pht( 'This %s already has that view policy.', $this->getApplicationObjectTypeName()); case PhabricatorTransactions::TYPE_EDIT_POLICY: return pht( 'This %s already has that edit policy.', $this->getApplicationObjectTypeName()); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht( 'All users are already subscribed to this %s.', $this->getApplicationObjectTypeName()); } return pht('Transaction has no effect.'); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: return pht( '%s added a comment.', $this->renderHandleLink($author_phid)); case PhabricatorTransactions::TYPE_VIEW_POLICY: // TODO: Render human-readable. return pht( '%s changed the visibility of this %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->getApplicationObjectTypeName(), - phutil_escape_html($old), - phutil_escape_html($new)); + $old, + $new); case PhabricatorTransactions::TYPE_EDIT_POLICY: // TODO: Render human-readable. return pht( '%s changed the edit policy of this %s from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->getApplicationObjectTypeName(), - phutil_escape_html($old), - phutil_escape_html($new)); + $old, + $new); case PhabricatorTransactions::TYPE_SUBSCRIBERS: $add = array_diff($new, $old); $rem = array_diff($old, $new); if ($add && $rem) { return pht( '%s edited subscriber(s), added %d: %s; removed %d: %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add), count($rem), $this->renderHandleList($rem)); } else if ($add) { return pht( '%s added %d subscriber(s): %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add)); } else { return pht( '%s removed %d subscribers: %s.', $this->renderHandleLink($author_phid), count($rem), $this->renderHandleList($rem)); } break; default: return pht( '%s edited this %s.', $this->renderHandleLink($author_phid), $this->getApplicationObjectTypeName()); } } public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: return pht( '%s added a comment to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_VIEW_POLICY: return pht( '%s changed the visibility for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_EDIT_POLICY: return pht( '%s changed the edit policy for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht( '%s updated subscribers of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } return $this->getTitle(); } public function getActionStrength() { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: return 0.5; } return 1.0; } public function getActionName() { switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: return pht('Commented On'); case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: return pht('Changed Policy'); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht('Changed Subscribers'); default: return pht('Updated'); } } public function getMailTags() { return array(); } /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return ($viewer->getPHID() == $this->getAuthorPHID()); } } diff --git a/src/view/layout/PhabricatorSourceCodeView.php b/src/view/layout/PhabricatorSourceCodeView.php index 790ea663d3..166c951b36 100644 --- a/src/view/layout/PhabricatorSourceCodeView.php +++ b/src/view/layout/PhabricatorSourceCodeView.php @@ -1,82 +1,82 @@ <?php final class PhabricatorSourceCodeView extends AphrontView { private $lines; private $limit; public function setLimit($limit) { $this->limit = $limit; return $this; } public function setLines(array $lines) { $this->lines = $lines; return $this; } public function render() { require_celerity_resource('phabricator-source-code-view-css'); require_celerity_resource('syntax-highlighting-css'); Javelin::initBehavior('phabricator-oncopy', array()); $line_number = 1; $rows = array(); foreach ($this->lines as $line) { $hit_limit = $this->limit && ($line_number == $this->limit) && (count($this->lines) != $this->limit); if ($hit_limit) { $content_number = ''; $content_line = phutil_tag( 'span', array( 'class' => 'c', ), pht('...')); } else { - $content_number = phutil_escape_html($line_number); + $content_number = $line_number; $content_line = "\xE2\x80\x8B".$line; } // TODO: Provide nice links. $rows[] = '<tr>'. '<th class="phabricator-source-line">'. $content_number. '</th>'. '<td class="phabricator-source-code">'. $content_line. '</td>'. '</tr>'; if ($hit_limit) { break; } $line_number++; } $classes = array(); $classes[] = 'phabricator-source-code-view'; $classes[] = 'remarkup-code'; $classes[] = 'PhabricatorMonospaced'; return phutil_tag( 'div', array( 'class' => 'phabricator-source-code-container', ), phutil_tag( 'table', array( 'class' => implode(' ', $classes), ), new PhutilSafeHTML(implode('', $rows)))); } }