1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-28 00:10:57 +01:00

Merge branch 'master' into phutil_tag

Auditors: vrana
This commit is contained in:
epriestley 2013-02-13 12:42:57 -08:00
commit 4bd2ad9270
49 changed files with 855 additions and 241 deletions

View file

@ -1174,7 +1174,7 @@ celerity_register_resource_map(array(
),
'javelin-behavior-dark-console' =>
array(
'uri' => '/res/ae7f15ce/rsrc/js/application/core/behavior-dark-console.js',
'uri' => '/res/89aeb6c0/rsrc/js/application/core/behavior-dark-console.js',
'type' => 'js',
'requires' =>
array(
@ -1864,6 +1864,18 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/phame/phame-post-preview.js',
),
'javelin-behavior-pholio-edit-inline-comment' =>
array(
'uri' => '/res/61759cd8/rsrc/js/application/pholio/behavior-pholio-edit-inline-comment.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/pholio/behavior-pholio-edit-inline-comment.js',
),
'javelin-behavior-pholio-mock-view' =>
array(
'uri' => '/res/e5f432ac/rsrc/js/application/pholio/behavior-pholio-mock-view.js',
@ -3488,7 +3500,7 @@ celerity_register_resource_map(array(
'uri' => '/res/pkg/bc0774e5/core.pkg.js',
'type' => 'js',
),
'3e0098ea' =>
'dca4a03d' =>
array(
'name' => 'darkconsole.pkg.js',
'symbols' =>
@ -3496,7 +3508,7 @@ celerity_register_resource_map(array(
0 => 'javelin-behavior-dark-console',
1 => 'javelin-behavior-error-log',
),
'uri' => '/res/pkg/3e0098ea/darkconsole.pkg.js',
'uri' => '/res/pkg/dca4a03d/darkconsole.pkg.js',
'type' => 'js',
),
'8aaacd1b' =>
@ -3666,7 +3678,7 @@ celerity_register_resource_map(array(
'javelin-behavior-aphront-drag-and-drop-textarea' => '95d0d865',
'javelin-behavior-aphront-form-disable-on-submit' => 'bc0774e5',
'javelin-behavior-audit-preview' => 'f96657b8',
'javelin-behavior-dark-console' => '3e0098ea',
'javelin-behavior-dark-console' => 'dca4a03d',
'javelin-behavior-device' => 'bc0774e5',
'javelin-behavior-differential-accept-with-errors' => '95d0d865',
'javelin-behavior-differential-add-reviewers-and-ccs' => '95d0d865',
@ -3682,7 +3694,7 @@ celerity_register_resource_map(array(
'javelin-behavior-differential-user-select' => '95d0d865',
'javelin-behavior-diffusion-commit-graph' => 'f96657b8',
'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8',
'javelin-behavior-error-log' => '3e0098ea',
'javelin-behavior-error-log' => 'dca4a03d',
'javelin-behavior-global-drag-and-drop' => 'bc0774e5',
'javelin-behavior-konami' => 'bc0774e5',
'javelin-behavior-lightbox-attachments' => 'bc0774e5',

View file

@ -146,6 +146,7 @@ phutil_register_library_map(array(
'ConduitAPI_file_download_Method' => 'applications/files/conduit/ConduitAPI_file_download_Method.php',
'ConduitAPI_file_info_Method' => 'applications/files/conduit/ConduitAPI_file_info_Method.php',
'ConduitAPI_file_upload_Method' => 'applications/files/conduit/ConduitAPI_file_upload_Method.php',
'ConduitAPI_file_uploadhash_Method' => 'applications/files/conduit/ConduitAPI_file_uploadhash_Method.php',
'ConduitAPI_flag_Method' => 'applications/flag/conduit/ConduitAPI_flag_Method.php',
'ConduitAPI_flag_delete_Method' => 'applications/flag/conduit/ConduitAPI_flag_delete_Method.php',
'ConduitAPI_flag_edit_Method' => 'applications/flag/conduit/ConduitAPI_flag_edit_Method.php',
@ -626,6 +627,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationAudit' => 'applications/audit/application/PhabricatorApplicationAudit.php',
'PhabricatorApplicationAuth' => 'applications/auth/application/PhabricatorApplicationAuth.php',
'PhabricatorApplicationCalendar' => 'applications/calendar/application/PhabricatorApplicationCalendar.php',
'PhabricatorApplicationChatLog' => 'applications/chatlog/applications/PhabricatorApplicationChatLog.php',
'PhabricatorApplicationConduit' => 'applications/conduit/application/PhabricatorApplicationConduit.php',
'PhabricatorApplicationConfig' => 'applications/config/application/PhabricatorApplicationConfig.php',
'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php',
@ -1641,6 +1643,7 @@ phutil_register_library_map(array(
'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
'ConduitAPI_file_uploadhash_Method' => 'ConduitAPIMethod',
'ConduitAPI_flag_Method' => 'ConduitAPIMethod',
'ConduitAPI_flag_delete_Method' => 'ConduitAPI_flag_Method',
'ConduitAPI_flag_edit_Method' => 'ConduitAPI_flag_Method',
@ -2068,6 +2071,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationAudit' => 'PhabricatorApplication',
'PhabricatorApplicationAuth' => 'PhabricatorApplication',
'PhabricatorApplicationCalendar' => 'PhabricatorApplication',
'PhabricatorApplicationChatLog' => 'PhabricatorApplication',
'PhabricatorApplicationConduit' => 'PhabricatorApplication',
'PhabricatorApplicationConfig' => 'PhabricatorApplication',
'PhabricatorApplicationConfigOptions' => 'Phobject',

View file

@ -199,15 +199,7 @@ final class AphrontRequest {
// No token in the request, check the HTTP header which is added for Ajax
// requests.
if (empty($token)) {
// PHP mangles HTTP headers by uppercasing them and replacing hyphens with
// underscores, then prepending 'HTTP_'.
$php_index = self::getCSRFHeaderName();
$php_index = strtoupper($php_index);
$php_index = str_replace('-', '_', $php_index);
$php_index = 'HTTP_'.$php_index;
$token = idx($_SERVER, $php_index);
$token = self::getHTTPHeader(self::getCSRFHeaderName());
}
$valid = $this->getUser()->validateCSRFToken($token);
@ -430,4 +422,14 @@ final class AphrontRequest {
}
public static function getHTTPHeader($name, $default = null) {
// PHP mangles HTTP headers by uppercasing them and replacing hyphens with
// underscores, then prepending 'HTTP_'.
$php_index = strtoupper($name);
$php_index = str_replace('-', '_', $php_index);
$php_index = 'HTTP_'.$php_index;
return idx($_SERVER, $php_index, $default);
}
}

View file

@ -122,6 +122,7 @@ class AphrontDefaultApplicationConfiguration
protected function getResourceURIMapRules() {
return array(
'/res/' => array(
'(?:(?P<mtime>[0-9]+)T/)?'.
'(?P<package>pkg/)?'.
'(?P<hash>[a-f0-9]{8})/'.
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'

View file

@ -101,8 +101,7 @@ final class DarkConsoleXHProfPlugin extends DarkConsolePlugin {
public function willShutdown() {
if (DarkConsoleXHProfPluginAPI::isProfilerRequested() &&
(DarkConsoleXHProfPluginAPI::isProfilerRequested() !== 'all')) {
if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
$this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler();
}
}

View file

@ -13,25 +13,46 @@ final class DarkConsoleXHProfPluginAPI {
return extension_loaded('xhprof');
}
public static function getProfilerHeader() {
return 'X-Phabricator-Profiler';
}
public static function isProfilerRequested() {
if (!empty($_REQUEST['__profile__'])) {
return $_REQUEST['__profile__'];
}
static $profilerRequested = null;
$header = AphrontRequest::getHTTPHeader(self::getProfilerHeader());
if ($header) {
return $header;
}
if (!isset($profilerRequested)) {
return false;
}
public static function shouldStartProfiler() {
if (self::isProfilerRequested()) {
return true;
}
static $sample_request = null;
if ($sample_request === null) {
if (PhabricatorEnv::getEnvConfig('debug.profile-rate')) {
$rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
if (mt_rand(1, $rate) == 1) {
$profilerRequested = true;
$sample_request = true;
} else {
$profilerRequested = false;
$sample_request = false;
}
}
}
return $profilerRequested;
return $sample_request;
}
public static function isProfilerStarted() {
return self::$profilerStarted;
}
public static function includeXHProfLib() {
@ -47,8 +68,40 @@ final class DarkConsoleXHProfPluginAPI {
}
public static function saveProfilerSample(
AphrontRequest $request,
$access_log) {
if (!self::isProfilerStarted()) {
return;
}
$profile = DarkConsoleXHProfPluginAPI::stopProfiler();
$profile_sample = id(new PhabricatorXHProfSample())
->setFilePHID($profile);
if (self::isProfilerRequested()) {
$sample_rate = 0;
} else {
$sample_rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
}
$profile_sample->setSampleRate($sample_rate);
if ($access_log) {
$profile_sample
->setUsTotal($access_log->getData('T'))
->setHostname($access_log->getData('h'))
->setRequestPath($access_log->getData('U'))
->setController($access_log->getData('C'))
->setUserPHID($request->getUser()->getPHID());
}
$profile_sample->save();
}
public static function hookProfiler() {
if (!self::isProfilerRequested()) {
if (!self::shouldStartProfiler()) {
return;
}
@ -70,49 +123,49 @@ final class DarkConsoleXHProfPluginAPI {
}
public static function stopProfiler() {
if (self::$profilerStarted) {
$data = xhprof_disable();
$data = serialize($data);
$file_class = 'PhabricatorFile';
// Since these happen on GET we can't do guarded writes. These also
// sometimes happen after we've disposed of the write guard; in this
// case we need to disable the whole mechanism.
$use_scope = AphrontWriteGuard::isGuardActive();
if ($use_scope) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
}
$caught = null;
try {
$file = call_user_func(
array($file_class, 'newFromFileData'),
$data,
array(
'mime-type' => 'application/xhprof',
'name' => 'profile.xhprof',
));
} catch (Exception $ex) {
$caught = $ex;
}
if ($use_scope) {
unset($unguarded);
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(false);
}
if ($caught) {
throw $caught;
} else {
return $file->getPHID();
}
} else {
if (!self::isProfilerStarted()) {
return null;
}
$data = xhprof_disable();
$data = serialize($data);
$file_class = 'PhabricatorFile';
// Since these happen on GET we can't do guarded writes. These also
// sometimes happen after we've disposed of the write guard; in this
// case we need to disable the whole mechanism.
$use_scope = AphrontWriteGuard::isGuardActive();
if ($use_scope) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
}
$caught = null;
try {
$file = call_user_func(
array($file_class, 'newFromFileData'),
$data,
array(
'mime-type' => 'application/xhprof',
'name' => 'profile.xhprof',
));
} catch (Exception $ex) {
$caught = $ex;
}
if ($use_scope) {
unset($unguarded);
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(false);
}
if ($caught) {
throw $caught;
} else {
return $file->getPHID();
}
}
}

View file

@ -0,0 +1,39 @@
<?php
final class PhabricatorApplicationChatLog extends PhabricatorApplication {
public function getBaseURI() {
return '/chatlog/';
}
public function getShortDescription() {
return 'Chat Log';
}
public function getIconName() {
return 'chatlog';
}
public function isBeta() {
return true;
}
public function getTitleGlyph() {
return "\xE0\xBC\x84";
}
public function getApplicationGroup() {
return self::GROUP_COMMUNICATION;
}
public function getRoutes() {
return array(
'/chatlog/' => array(
'' => 'PhabricatorChatLogChannelListController',
),
);
}
}

View file

@ -10,6 +10,10 @@ final class PhabricatorApplicationConduit extends PhabricatorApplication {
return 'conduit';
}
public function canUninstall() {
return false;
}
public function getHelpURI() {
return PhabricatorEnv::getDoclink(
'article/Conduit_Technical_Documentation.html');

View file

@ -5,7 +5,7 @@ final class PhabricatorSetupCheckBaseURI extends PhabricatorSetupCheck {
protected function executeChecks() {
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
if (strpos($_SERVER['HTTP_HOST'], '.') === false) {
if (strpos(AphrontRequest::getHTTPHeader('Host'), '.') === false) {
$summary = pht(
'The domain does not contain a dot. This is necessary for some web '.
'browsers to be able to set cookies.');

View file

@ -330,6 +330,7 @@ final class PhabricatorConfigEditController
case 'int':
case 'string':
case 'enum':
case 'class':
return $value;
case 'bool':
return $value ? 'true' : 'false';

View file

@ -227,6 +227,11 @@ final class ConpherenceThreadQuery
$conpherence_pic_phids[$conpherence->getPHID()] = $phid;
}
}
if (!$conpherence_pic_phids) {
return $this;
}
$files = id(new PhabricatorFileQuery())
->setViewer($this->getViewer())
->withPHIDs($conpherence_pic_phids)

View file

@ -71,10 +71,6 @@ final class ConpherenceTransactionView extends AphrontView {
break;
case PhabricatorTransactions::TYPE_COMMENT:
$comment = $transaction->getComment();
$file_ids =
PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
array($comment->getContent())
);
$content = $this->markupEngine->getOutput(
$comment,
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);

View file

@ -117,7 +117,7 @@ final class DifferentialLintFieldSpecification
'show' => $show,
);
if (isset($message['locations'])) {
if (!empty($message['locations'])) {
$locations = array();
foreach ($message['locations'] as $location) {
$other_line = idx($location, 'line');

View file

@ -420,14 +420,6 @@ final class DifferentialChangesetParser {
}
private function getHighlightFuture($corpus) {
if (preg_match('/\r(?!\n)/', $corpus)) {
// TODO: Pygments converts "\r" newlines into "\n" newlines, so we can't
// use it on files with "\r" newlines. If we have "\r" not followed by
// "\n" in the file, skip highlighting.
$result = phutil_escape_html($corpus);
return new ImmediateFuture($result);
}
return $this->highlightEngine->getHighlightFuture(
$this->highlightEngine->getLanguageFromFilename($this->filename),
$corpus);

View file

@ -12,9 +12,7 @@ final class DiffusionGitRequest extends DiffusionRequest {
protected function didInitialize() {
$repository = $this->getRepository();
if (!Filesystem::pathExists($repository->getLocalPath())) {
$this->raiseCloneException();
}
$this->validateWorkingCopy($repository->getLocalPath());
if (!$this->commit) {
return;

View file

@ -12,9 +12,7 @@ final class DiffusionMercurialRequest extends DiffusionRequest {
protected function didInitialize() {
$repository = $this->getRepository();
if (!Filesystem::pathExists($repository->getLocalPath())) {
$this->raiseCloneException();
}
$this->validateWorkingCopy($repository->getLocalPath());
// Expand abbreviated hashes to full hashes so "/rXnnnn" (i.e., fewer than
// 40 characters) works correctly.

View file

@ -544,6 +544,30 @@ abstract class DiffusionRequest {
return $result;
}
/**
* Check that the working copy of the repository is present and readable.
*
* @param string Path to the working copy.
*/
protected function validateWorkingCopy($path) {
if (!is_readable(dirname($path))) {
$this->raisePermissionException();
}
if (!Filesystem::pathExists($path)) {
$this->raiseCloneException();
}
}
protected function raisePermissionException() {
$host = php_uname('n');
$callsign = $this->getRepository()->getCallsign();
throw new DiffusionSetupException(
"The clone of this repository ('{$callsign}') on the local machine " .
"('{$host}') could not be read. Ensure that the repository is in a " .
"location where the web server has read permissions.");
}
protected function raiseCloneException() {
$host = php_uname('n');
$callsign = $this->getRepository()->getCallsign();

View file

@ -0,0 +1,45 @@
<?php
/**
* @group conduit
*/
final class ConduitAPI_file_uploadhash_Method extends ConduitAPIMethod {
public function getMethodDescription() {
return "Upload a file to the server using content hash.";
}
public function defineParamTypes() {
return array(
'hash' => 'required nonempty string',
'name' => 'required nonempty string',
);
}
public function defineReturnType() {
return 'phid or null';
}
public function defineErrorTypes() {
return array(
);
}
protected function execute(ConduitAPIRequest $request) {
$hash = $request->getValue('hash');
$name = $request->getValue('name');
$user = $request->getUser();
$file = PhabricatorFile::newFileFromContentHash(
$hash,
array(
'name' => $name,
'authorPHID' => $user->getPHID(),
));
if ($file) {
return $file->getPHID();
}
return $file;
}
}

View file

@ -18,7 +18,7 @@ final class PhabricatorTestStorageEngine
public function writeFile($data, array $params) {
AphrontWriteGuard::willWrite();
self::$storage[self::$nextHandle] = $data;
return self::$nextHandle++;
return (string)self::$nextHandle++;
}
public function readFile($handle) {

View file

@ -132,8 +132,46 @@ final class PhabricatorFile extends PhabricatorFileDAO
return $file;
}
public static function newFileFromContentHash($hash, $params) {
public static function newFromFileData($data, array $params = array()) {
// Check to see if a file with same contentHash exist
$file = id(new PhabricatorFile())->loadOneWhere(
'contentHash = %s LIMIT 1', $hash);
if ($file) {
// copy storageEngine, storageHandle, storageFormat
$copy_of_storage_engine = $file->getStorageEngine();
$copy_of_storage_handle = $file->getStorageHandle();
$copy_of_storage_format = $file->getStorageFormat();
$copy_of_byteSize = $file->getByteSize();
$copy_of_mimeType = $file->getMimeType();
$file_name = idx($params, 'name');
$file_name = self::normalizeFileName($file_name);
$authorPHID = idx($params, 'authorPHID');
$new_file = new PhabricatorFile();
$new_file->setName($file_name);
$new_file->setByteSize($copy_of_byteSize);
$new_file->setAuthorPHID($authorPHID);
$new_file->setContentHash($hash);
$new_file->setStorageEngine($copy_of_storage_engine);
$new_file->setStorageHandle($copy_of_storage_handle);
$new_file->setStorageFormat($copy_of_storage_format);
$new_file->setMimeType($copy_of_mimeType);
$new_file->save();
return $new_file;
}
return $file;
}
private static function buildFromFileData($data, array $params = array()) {
$selector = PhabricatorEnv::newObjectFromConfig('storage.engine-selector');
if (isset($params['storageEngines'])) {
$engines = $params['storageEngines'];
@ -221,6 +259,17 @@ final class PhabricatorFile extends PhabricatorFileDAO
return $file;
}
public static function newFromFileData($data, array $params = array()) {
$hash = self::hashFileContent($data);
$file = self::newFileFromContentHash($hash, $params);
if ($file) {
return $file;
}
return self::buildFromFileData($data, $params);
}
public function migrateToEngine(PhabricatorFileStorageEngine $engine) {
if (!$this->getID() || !$this->getStorageHandle()) {
throw new Exception(
@ -305,15 +354,28 @@ final class PhabricatorFile extends PhabricatorFileDAO
}
public function delete() {
$engine = $this->instantiateStorageEngine();
// Check to see if other files are using storage
$other_file = id(new PhabricatorFile())->loadAllWhere(
'storageEngine = %s AND storageHandle = %s AND
storageFormat = %s AND id != %d LIMIT 1', $this->getStorageEngine(),
$this->getStorageHandle(), $this->getStorageFormat(),
$this->getID());
// If this is the only file using the storage, delete storage
if (count($other_file) == 0) {
$engine = $this->instantiateStorageEngine();
$engine->deleteFile($this->getStorageHandle());
}
$ret = parent::delete();
$engine->deleteFile($this->getStorageHandle());
return $ret;
}
public static function hashFileContent($data) {
return PhabricatorHash::digest($data);
}
public function loadFileData() {
$engine = $this->instantiateStorageEngine();

View file

@ -31,6 +31,55 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
$this->assertEqual($data, $file->loadFileData());
}
public function testFileStorageUploadDifferentFiles() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$other_data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$first_file = PhabricatorFile::newFromFileData($data, $params);
$second_file = PhabricatorFile::newFromFileData($other_data, $params);
// Test that the the second file uses different storage handle from
// the first file.
$first_handle = $first_file->getStorageHandle();
$second_handle = $second_file->getStorageHandle();
$this->assertEqual(true, ($first_handle != $second_handle));
}
public function testFileStorageUploadSameFile() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$first_file = PhabricatorFile::newFromFileData($data, $params);
$second_file = PhabricatorFile::newFromFileData($data, $params);
// Test that the the second file uses the same storage handle as
// the first file.
$handle = $first_file->getStorageHandle();
$second_handle = $second_file->getStorageHandle();
$this->assertEqual($handle, $second_handle);
}
public function testFileStorageDelete() {
$engine = new PhabricatorTestStorageEngine();
@ -58,4 +107,23 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
$this->assertEqual(true, $caught instanceof Exception);
}
public function testFileStorageDeleteSharedHandle() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$params = array(
'name' => 'test.dat',
'storageEngines' => array(
$engine,
),
);
$first_file = PhabricatorFile::newFromFileData($data, $params);
$second_file = PhabricatorFile::newFromFileData($data, $params);
$first_file->delete();
$this->assertEqual($data, $second_file->loadFileData());
}
}

View file

@ -9,13 +9,14 @@ abstract class PhabricatorPasteController extends PhabricatorController {
$nav->setBaseURI(new PhutilURI($this->getApplicationURI('filter/')));
if ($for_app) {
$nav->addFilter('', 'Create Paste', $this->getApplicationURI('/create/'));
$nav->addFilter('', pht('Create Paste'),
$this->getApplicationURI('/create/'));
}
$nav->addLabel('Filters');
$nav->addFilter('all', 'All Pastes');
$nav->addLabel(pht('Filters'));
$nav->addFilter('all', pht('All Pastes'));
if ($user->isLoggedIn()) {
$nav->addFilter('my', 'My Pastes');
$nav->addFilter('my', pht('My Pastes'));
}
$nav->selectFilter($filter, 'all');

View file

@ -64,8 +64,8 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
if ($is_create) {
$text = $request->getStr('text');
if (!strlen($text)) {
$e_text = 'Required';
$errors[] = 'The paste may not be blank.';
$e_text = pht('Required');
$errors[] = pht('The paste may not be blank.');
} else {
$e_text = null;
}
@ -94,7 +94,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
}
} else {
if ($is_create && $parent) {
$paste->setTitle('Fork of '.$parent->getFullName());
$paste->setTitle(pht('Fork of %s', $parent->getFullName()));
$paste->setLanguage($parent->getLanguage());
$text = $parent->getRawContent();
}
@ -103,7 +103,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('A fatal omission!')
->setTitle(pht('A Fatal Omission!'))
->setErrors($errors);
}
@ -111,7 +111,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
$form->setFlexible(true);
$langs = array(
'' => '(Detect From Filename in Title)',
'' => pht('(Detect From Filename in Title)'),
) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices');
$form
@ -119,12 +119,12 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
->addHiddenInput('parent', $parent_id)
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Title')
->setLabel(pht('Title'))
->setValue($paste->getTitle())
->setName('title'))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Language')
->setLabel(pht('Language'))
->setName('language')
->setValue($paste->getLanguage())
->setOptions($langs));
@ -146,7 +146,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
$form
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Text')
->setLabel(pht('Text'))
->setError($e_text)
->setValue($text)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
@ -158,13 +158,13 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
array(
'href' => $this->getApplicationURI('?parent='.$paste->getID())
),
'Fork'
pht('Fork')
);
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Text')
->setValue(hsprintf(
->setLabel(pht('Text'))
->setValue(pht(
'Paste text can not be edited. %s to create a new paste.',
$fork_link)));
}

View file

@ -50,7 +50,7 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
public function getFullName() {
$title = $this->getTitle();
if (!$title) {
$title = '(An Untitled Masterwork)';
$title = pht('(An Untitled Masterwork)');
}
return 'P'.$this->getID().' '.$title;
}

View file

@ -75,7 +75,7 @@ final class PhabricatorUserLog extends PhabricatorUserDAO {
$this->setSession(idx($_COOKIE, 'phsid'));
}
$this->details['host'] = php_uname('n');
$this->details['user_agent'] = idx($_SERVER, 'HTTP_USER_AGENT');
$this->details['user_agent'] = AphrontRequest::getHTTPHeader('User-Agent');
return parent::save();
}

View file

@ -20,10 +20,27 @@ final class PholioInlineController extends PholioController {
$this->id
);
$inline_comments = array_merge(
$inline_comments,
id(new PholioTransactionComment())->loadAllWhere(
'imageid = %d AND authorphid = %s AND transactionphid IS NULL',
$this->id,
$user->getPHID()));
$inlines = array();
foreach ($inline_comments as $inline_comment) {
$author = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$inline_comment->getAuthorPHID()
);
$inlines[] = array(
'phid' => $inline_comment->getPHID(),
'userphid' => $author->getPHID(),
'username' => $author->getUserName(),
'canEdit' => ($inline_comment->
getEditPolicy(PhabricatorPolicyCapability::CAN_EDIT) ==
$user->getPHID()) ? true : false,
'transactionphid' => $inline_comment->getTransactionPHID(),
'imageID' => $inline_comment->getImageID(),
'x' => $inline_comment->getX(),
'y' => $inline_comment->getY(),

View file

@ -13,8 +13,7 @@ final class PholioInlineSaveController extends PholioController {
->setViewer($user)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
PhabricatorPolicyCapability::CAN_VIEW
))
->withIDs(array($request->getInt('mockID')))
->executeOne();

View file

@ -47,12 +47,22 @@ final class PholioMockImagesView extends AphrontView {
$main_image_tag
);
$inline_comments_holder = javelin_tag(
'div',
array(
'id' => 'mock-inline-comments',
'sigil' => 'mock-inline-comments',
'class' => 'pholio-mock-inline-comments'
),
"");
$mockview[] = phutil_tag(
'div',
array(
'class' => 'pholio-mock-image-container',
),
$main_image_tag);
array($main_image_tag, $inline_comments_holder));
if (count($this->mock->getImages()) > 1) {
$thumbnails = array();

View file

@ -249,7 +249,7 @@ final class PhrictionEditController
'uri' => '/phriction/preview/?draftkey='.$draft_key,
));
return $this->buildStandardPageResponse(
return $this->buildApplicationPage(
array(
$draft_note,
$error_view,
@ -258,6 +258,7 @@ final class PhrictionEditController
),
array(
'title' => pht('Edit Document'),
'device' => true,
));
}

View file

@ -3,11 +3,11 @@
final class PhabricatorApplicationProject extends PhabricatorApplication {
public function getName() {
return 'Projects';
return pht('Projects');
}
public function getShortDescription() {
return 'Organize Work';
return pht('Organize Work');
}
public function getBaseURI() {

View file

@ -5,7 +5,7 @@ abstract class PhabricatorProjectController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->setApplicationName('Project');
$page->setApplicationName(pht('Project'));
$page->setBaseURI('/project/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x98\xA3");
@ -30,29 +30,29 @@ abstract class PhabricatorProjectController extends PhabricatorController {
$edit_uri = '/project/edit/'.$id.'/';
$members_uri = '/project/members/'.$id.'/';
$nav_view->addFilter('dashboard', 'Dashboard');
$nav_view->addFilter('feed', 'Feed');
$nav_view->addFilter(null, 'Tasks '.$external_arrow, $tasks_uri);
$nav_view->addFilter(null, 'Wiki '.$external_arrow, $phriction_uri);
$nav_view->addFilter('people', 'People');
$nav_view->addFilter('about', 'About');
$nav_view->addFilter('dashboard', pht('Dashboard'));
$nav_view->addFilter('feed', pht('Feed'));
$nav_view->addFilter(null, pht('Tasks').' '.$external_arrow, $tasks_uri);
$nav_view->addFilter(null, pht('Wiki').' '.$external_arrow, $phriction_uri);
$nav_view->addFilter('people', pht('People'));
$nav_view->addFilter('about', pht('About'));
$user = $this->getRequest()->getUser();
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
if (PhabricatorPolicyFilter::hasCapability($user, $project, $can_edit)) {
$nav_view->addFilter('edit', "Edit Project\xE2\x80\xA6", $edit_uri);
$nav_view->addFilter('members', "Edit Members\xE2\x80\xA6", $members_uri);
$nav_view->addFilter('edit', pht("Edit Project"), $edit_uri);
$nav_view->addFilter('members', pht("Edit Members"), $members_uri);
} else {
$nav_view->addFilter(
'edit',
"Edit Project\xE2\x80\xA6",
pht("Edit Project"),
$edit_uri,
$relative = false,
'disabled');
$nav_view->addFilter(
'members',
"Edit Members\xE2\x80\xA6",
pht("Edit Members"),
$members_uri,
$relative = false,
'disabled');
@ -61,4 +61,40 @@ abstract class PhabricatorProjectController extends PhabricatorController {
return $nav_view;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(null, true)->getMenu();
}
public function buildSideNavView($filter = null, $for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav
->setBaseURI(new PhutilURI('/project/filter/'))
->addLabel(pht('User'))
->addFilter('active', pht('Active'))
->addLabel(pht('All'))
->addFilter('all', pht('All Projects'))
->addFilter('allactive', pht('Active Projects'))
->selectFilter($filter, 'active');
if ($for_app) {
$nav->addFilter('create/', pht('Create Project'));
}
return $nav;
}
public function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PhabricatorMenuItemView())
->setName(pht('Create Project'))
->setHref($this->getApplicationURI('create/'))
->setIcon('create'));
return $crumbs;
}
}

View file

@ -63,7 +63,7 @@ final class PhabricatorProjectCreateController
$error_view = null;
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setTitle(pht('Form Errors'));
$error_view->setErrors($errors);
}
@ -77,13 +77,13 @@ final class PhabricatorProjectCreateController
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setLabel(pht('Name'))
->setName('name')
->setValue($project->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Blurb')
->setLabel(pht('Blurb'))
->setName('blurb')
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setValue($profile->getBlurb()));
@ -92,10 +92,10 @@ final class PhabricatorProjectCreateController
$dialog = id(new AphrontDialogView())
->setUser($user)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle('Create a New Project')
->setTitle(pht('Create a New Project'))
->appendChild($error_view)
->appendChild($form)
->addSubmitButton('Create Project')
->addSubmitButton(pht('Create Project'))
->addCancelButton('/project/');
return id(new AphrontDialogResponse())->setDialog($dialog);
@ -104,22 +104,32 @@ final class PhabricatorProjectCreateController
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Create')
->setValue(pht('Create'))
->addCancelButton('/project/'));
$panel = new AphrontPanelView();
$panel
->setWidth(AphrontPanelView::WIDTH_FORM)
->setHeader('Create a New Project')
->setHeader(pht('Create a New Project'))
->setNoBackground()
->appendChild($form);
return $this->buildStandardPageResponse(
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Create Project'))
->setHref($this->getApplicationURI().'create/')
);
return $this->buildApplicationPage(
array(
$crumbs,
$error_view,
$panel,
),
array(
'title' => 'Create new Project',
'title' => pht('Create New Project'),
'device' => true
));
}
}

View file

@ -12,14 +12,7 @@ final class PhabricatorProjectListController
public function processRequest() {
$request = $this->getRequest();
$nav = new AphrontSideNavFilterView();
$nav
->setBaseURI(new PhutilURI('/project/filter/'))
->addLabel('User')
->addFilter('active', 'Active')
->addLabel('All')
->addFilter('all', 'All Projects')
->addFilter('allactive','Active Projects');
$nav = $this->buildSideNavView($this->filter);
$this->filter = $nav->selectFilter($this->filter, 'active');
$pager = new AphrontPagerView();
@ -33,21 +26,19 @@ final class PhabricatorProjectListController
$view_phid = $request->getUser()->getPHID();
$status_filter = PhabricatorProjectQuery::STATUS_ANY;
switch ($this->filter) {
case 'active':
$table_header = 'Your Projects';
$table_header = pht('Your Projects');
$query->withMemberPHIDs(array($view_phid));
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
break;
case 'allactive':
$status_filter = PhabricatorProjectQuery::STATUS_ACTIVE;
$table_header = 'Active Projects';
// fallthrough
$table_header = pht('Active Projects');
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
break;
case 'all':
$table_header = 'All Projects';
$query->withStatus($status_filter);
$table_header = pht('All Projects');
$query->withStatus(PhabricatorProjectQuery::STATUS_ANY);
break;
}
@ -121,11 +112,11 @@ final class PhabricatorProjectListController
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Project',
'Status',
'Description',
'Population',
'Open Tasks',
pht('Project'),
pht('Status'),
pht('Description'),
pht('Population'),
pht('Open Tasks'),
));
$table->setColumnClasses(
array(
@ -138,16 +129,25 @@ final class PhabricatorProjectListController
$panel = new AphrontPanelView();
$panel->setHeader($table_header);
$panel->setCreateButton('Create New Project', '/project/create/');
$panel->appendChild($table);
$panel->setNoBackground();
$panel->appendChild($pager);
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($table_header)
->setHref($this->getApplicationURI())
);
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => 'Projects',
'title' => pht('Projects'),
'device' => true,
));
}
}

View file

@ -83,8 +83,8 @@ final class PhabricatorProjectMembersEditController
);
}
$header_name = 'Edit Members';
$title = 'Edit Members';
$header_name = pht('Edit Members');
$title = pht('Edit Members');
$list = $this->renderMemberList($handles);
@ -94,23 +94,24 @@ final class PhabricatorProjectMembersEditController
->appendChild(
id(new AphrontFormTokenizerControl())
->setName('phids')
->setLabel('Add Members')
->setLabel(pht('Add Members'))
->setDatasource('/typeahead/common/users/'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/project/view/'.$project->getID().'/')
->setValue('Add Members'));
->setValue(pht('Add Members')));
$faux_form = id(new AphrontFormLayoutView())
->setBackgroundShading(true)
->setPadded(true)
->appendChild(
id(new AphrontFormInsetView())
->setTitle('Current Members ('.count($handles).')')
->setTitle(pht('Current Members (%d)', count($handles)))
->appendChild($list));
$panel = new AphrontPanelView();
$panel->setHeader($header_name);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->setNoBackground();
$panel->appendChild($form);
$panel->appendChild(phutil_tag('br'));
$panel->appendChild($faux_form);
@ -119,10 +120,24 @@ final class PhabricatorProjectMembersEditController
$nav->selectFilter('members');
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($project->getName())
->setHref('/project/view/'.$project->getID().'/')
);
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Edit Members'))
->setHref($this->getApplicationURI())
);
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => $title,
'device' => true,
));
}

View file

@ -5,6 +5,7 @@ final class PhabricatorProjectProfileController
private $id;
private $page;
private $project;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
@ -24,6 +25,7 @@ final class PhabricatorProjectProfileController
}
$project = $query->executeOne();
$this->project = $project;
if (!$project) {
return new Aphront404Response();
}
@ -96,7 +98,7 @@ final class PhabricatorProjectProfileController
array(
'class' => $class,
),
'Join Project'));
pht('Join Project')));
} else {
$action = javelin_tag(
'a',
@ -105,7 +107,7 @@ final class PhabricatorProjectProfileController
'sigil' => 'workflow',
'class' => 'grey button',
),
'Leave Project...');
pht('Leave Project...'));
}
$header->addAction($action);
@ -115,10 +117,10 @@ final class PhabricatorProjectProfileController
$content = hsprintf('<div style="padding: 1em;">%s</div>', $content);
$header->appendChild($content);
return $this->buildStandardPageResponse(
return $this->buildApplicationPage(
$nav_view,
array(
'title' => $project->getName().' Project',
'title' => pht('%s Project', $project->getName()),
));
}
@ -143,11 +145,11 @@ final class PhabricatorProjectProfileController
<div class="phabricator-profile-info-pane">
<table class="phabricator-profile-info-table">
<tr>
<th>Creator</th>
<th>%s</th>
<td>%s</td>
</tr>
<tr>
<th>Created</th>
<th>%s</th>
<td>%s</td>
</tr>
<tr>
@ -155,15 +157,18 @@ final class PhabricatorProjectProfileController
<td>%s</td>
</tr>
<tr>
<th>Blurb</th>
<th>%s</th>
<td>%s</td>
</tr>
</table>
</div>
</div>',
pht('Creator'),
$handles[$project->getAuthorPHID()]->renderLink(),
pht('Created'),
$timestamp,
$project->getPHID(),
pht('Blurb'),
$blurb);
return $about;
@ -190,9 +195,10 @@ final class PhabricatorProjectProfileController
return hsprintf(
'<div class="phabricator-profile-info-group">'.
'<h1 class="phabricator-profile-info-header">People</h1>'.
'<h1 class="phabricator-profile-info-header">%s</h1>'.
'<div class="phabricator-profile-info-pane">%s</div>'.
'</div>',
pht('People'),
$affiliated);
}
@ -207,7 +213,7 @@ final class PhabricatorProjectProfileController
$stories = $query->execute();
if (!$stories) {
return 'There are no stories about this project.';
return pht('There are no stories about this project.');
}
return $this->renderStories($stories);
@ -222,9 +228,10 @@ final class PhabricatorProjectProfileController
return hsprintf(
'<div class="phabricator-profile-info-group">'.
'<h1 class="phabricator-profile-info-header">Activity Feed</h1>'.
'<h1 class="phabricator-profile-info-header">%s</h1>'.
'<div class="phabricator-profile-info-pane">%s</div>'.
'</div>',
pht('Activity Feed'),
$view->render());
}
@ -268,17 +275,17 @@ final class PhabricatorProjectProfileController
array(
'href' => '/maniphest/view/all/?projects='.$project->getPHID(),
),
"View All Open Tasks \xC2\xBB");
pht("View All Open Tasks \xC2\xBB"));
$content = hsprintf(
'<div class="phabricator-profile-info-group">
<h1 class="phabricator-profile-info-header">Open Tasks (%s)</h1>'.
<h1 class="phabricator-profile-info-header">%s</h1>'.
'<div class="phabricator-profile-info-pane">'.
'%s'.
'<div class="phabricator-profile-info-pane-more-link">%s</div>'.
'</div>
</div>',
$open,
pht('Open Tasks (%s)', $open),
$task_views,
$more_link);

View file

@ -86,8 +86,8 @@ final class PhabricatorProjectProfileEditController
$profile->setBlurb($request->getStr('blurb'));
if (!strlen($project->getName())) {
$e_name = 'Required';
$errors[] = 'Project name is required.';
$e_name = pht('Required');
$errors[] = pht('Project name is required.');
} else {
$e_name = null;
}
@ -112,9 +112,9 @@ final class PhabricatorProjectProfileEditController
$y = 50);
$profile->setProfileImagePHID($xformed->getPHID());
} else {
$e_image = 'Not Supported';
$e_image = pht('Not Supported');
$errors[] =
'This server only supports these image formats: '.
pht('This server only supports these image formats:').' '.
implode(', ', $supported_formats).'.';
}
}
@ -132,12 +132,12 @@ final class PhabricatorProjectProfileEditController
$error_view = null;
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setTitle(pht('Form Errors'));
$error_view->setErrors($errors);
}
$header_name = 'Edit Project';
$title = 'Edit Project';
$header_name = pht('Edit Project');
$title = pht('Edit Project');
$action = '/project/edit/'.$project->getID().'/';
$policies = id(new PhabricatorPolicyQuery())
@ -153,30 +153,30 @@ final class PhabricatorProjectProfileEditController
->setEncType('multipart/form-data')
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
->setLabel(pht('Name'))
->setName('name')
->setValue($project->getName())
->setError($e_name))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Project Status')
->setLabel(pht('Project Status'))
->setName('status')
->setOptions($options)
->setValue($project->getStatus()))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Blurb')
->setLabel(pht('Blurb'))
->setName('blurb')
->setValue($profile->getBlurb()))
->appendChild(hsprintf(
'<p class="aphront-form-instructions">NOTE: Policy settings are not '.
'yet fully implemented. Some interfaces still ignore these settings, '.
'particularly "Visible To".</p>'))
->appendChild('<p class="aphront-form-instructions">'.
pht('NOTE: Policy settings are not yet fully implemented. '.
'Some interfaces still ignore these settings, '.
'particularly "Visible To".').'</p>')
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setName('can_view')
->setCaption('Members can always view a project.')
->setCaption(pht('Members can always view a project.'))
->setPolicyObject($project)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
@ -192,13 +192,13 @@ final class PhabricatorProjectProfileEditController
->setUser($user)
->setName('can_join')
->setCaption(
'Users who can edit a project can always join a project.')
pht('Users who can edit a project can always join a project.'))
->setPolicyObject($project)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_JOIN))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('Profile Image')
->setLabel(pht('Profile Image'))
->setValue(
phutil_tag(
'img',
@ -207,18 +207,20 @@ final class PhabricatorProjectProfileEditController
))))
->appendChild(
id(new AphrontFormImageControl())
->setLabel('Change Image')
->setLabel(pht('Change Image'))
->setName('image')
->setError($e_image)
->setCaption('Supported formats: '.implode(', ', $supported_formats)))
->setCaption(
pht('Supported formats:').' '.implode(', ', $supported_formats)))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/project/view/'.$project->getID().'/')
->setValue('Save'));
->setValue(pht('Save')));
$panel = new AphrontPanelView();
$panel->setHeader($header_name);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->setNoBackground();
$panel->appendChild($form);
$nav = $this->buildLocalNavigation($project);
@ -229,10 +231,24 @@ final class PhabricatorProjectProfileEditController
$panel,
));
return $this->buildStandardPageResponse(
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($project->getName())
->setHref('/project/view/'.$project->getID().'/')
);
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Edit Project'))
->setHref($this->getApplicationURI())
);
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'title' => $title,
'device' => true,
));
}
}

View file

@ -61,12 +61,12 @@ final class PhabricatorProjectUpdateController
case 'leave':
$dialog = new AphrontDialogView();
$dialog->setUser($user);
$dialog->setTitle('Really leave project?');
$dialog->setTitle(pht('Really leave project?'));
$dialog->appendChild(phutil_tag('p', array(), pht(
'Your tremendous contributions to this project will be sorely '.
'missed. Are you sure you want to leave?')));
$dialog->addCancelButton($project_uri);
$dialog->addSubmitButton('Leave Project');
$dialog->addSubmitButton(pht('Leave Project'));
break;
default:
return new Aphront404Response();

View file

@ -164,7 +164,8 @@ final class PhabricatorProjectEditor extends PhabricatorEditor {
if ($slug == '/') {
throw new PhabricatorProjectNameCollisionException(
"Project names must be unique and contain some letters or numbers.");
pht("Project names must be unique and contain some ".
"letters or numbers."));
}
$id = $project->getID();

View file

@ -44,8 +44,10 @@ final class PhabricatorSettingsPanelDiffPreferences
1 => pht('Enable Filetree'),
))
->setCaption(
pht("When looking at a revision or commit, show affected files ".
"in a sidebar.")))
pht("When looking at a revision or commit, enable a sidebar ".
"showing affected files. You can press %s to show or hide ".
"the sidebar.",
phutil_tag('tt', array(), 'f'))))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save Preferences')));

View file

@ -34,7 +34,7 @@ abstract class CelerityResourceController extends PhabricatorController {
throw new Exception("Only static resources may be served.");
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
if (AphrontRequest::getHTTPHeader('If-Modified-Since') &&
!PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
// Return a "304 Not Modified". We don't care about the value of this
// field since we never change what resource is served by a given URI.

View file

@ -104,7 +104,7 @@ final class CelerityStaticResourceResponse {
}
private function renderResource(array $resource) {
$uri = PhabricatorEnv::getCDNURI($resource['uri']);
$uri = $this->getURI($resource);
switch ($resource['type']) {
case 'css':
return phutil_tag(
@ -148,6 +148,7 @@ final class CelerityStaticResourceResponse {
$higher_priority_names = array(
'refresh-csrf',
'aphront-basic-tokenizer',
'dark-console',
);
$higher_priority_behaviors = array_select_keys(
@ -207,7 +208,7 @@ final class CelerityStaticResourceResponse {
$this->resolveResources();
$resources = array();
foreach ($this->packaged as $resource) {
$resources[] = PhabricatorEnv::getCDNURI($resource['uri']);
$resources[] = $this->getURI($resource);
}
if ($resources) {
$response['javelin_resources'] = $resources;
@ -216,4 +217,32 @@ final class CelerityStaticResourceResponse {
return $response;
}
private function getURI($resource) {
$uri = $resource['uri'];
// In developer mode, we dump file modification times into the URI. When a
// page is reloaded in the browser, any resources brought in by Ajax calls
// do not trigger revalidation, so without this it's very difficult to get
// changes to Ajaxed-in CSS to work (you must clear your cache or rerun
// the map script). In production, we can assume the map script gets run
// after changes, and safely skip this.
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
$root = dirname(phutil_get_library_root('phabricator')).'/webroot/';
if (isset($resource['disk'])) {
$mtime = (int)filemtime($root.$resource['disk']);
} else {
$mtime = 0;
foreach ($resource['symbols'] as $symbol) {
$map = CelerityResourceMap::getInstance();
$symbol_info = $map->lookupSymbolInformation($symbol);
$mtime = max($mtime, (int)filemtime($root.$symbol_info['disk']));
}
}
$uri = preg_replace('@^/res/@', '/res/'.$mtime.'T/', $uri);
}
return PhabricatorEnv::getCDNURI($uri);
}
}

View file

@ -107,7 +107,7 @@ final class PhabricatorBot extends PhabricatorDaemon {
private function routeMessage(PhabricatorBotMessage $message) {
$ignore = $this->getConfig('ignore');
if ($ignore && in_array($message->getSenderNickName(), $ignore)) {
if ($ignore && in_array($message->getSender(), $ignore)) {
return;
}

View file

@ -118,6 +118,7 @@ extends PhabricatorBaseProtocolAdapter {
return id(new PhabricatorBotMessage())
->setCommand('MESSAGE')
->setSender($m_obj['user_id'])
->setTarget($m_obj['room_id'])
->setBody($m_obj['body']);
}
@ -128,11 +129,23 @@ extends PhabricatorBaseProtocolAdapter {
public function writeMessage(PhabricatorBotMessage $message) {
switch ($message->getCommand()) {
case 'MESSAGE':
$this->speak(
$message->getBody(),
$message->getTarget());
break;
case 'MESSAGE':
$this->speak(
$message->getBody(),
$message->getTarget());
break;
case 'SOUND':
$this->speak(
$message->getBody(),
$message->getTarget(),
'SoundMessage');
break;
case 'PASTE':
$this->speak(
$message->getBody(),
$message->getTarget(),
'PasteMessage');
break;
}
}
@ -146,12 +159,12 @@ extends PhabricatorBaseProtocolAdapter {
unset($this->inRooms[$room_id]);
}
private function speak($message, $room_id) {
private function speak($message, $room_id, $type='TextMessage') {
$this->performPost(
"/room/{$room_id}/speak.json",
array(
'message' => array(
'type' => 'TextMessage',
'type' => $type,
'body' => $message)));
}

View file

@ -161,12 +161,19 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
if ($console) {
require_celerity_resource('aphront-dark-console-css');
$headers = array();
if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
$headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page';
}
Javelin::initBehavior(
'dark-console',
array(
'uri' => $request ? (string)$request->getRequestURI() : '?',
'selected' => $user ? $user->getConsoleTab() : null,
'visible' => $user ? (int)$user->getConsoleVisible() : true,
'headers' => $headers,
));
// Change this to initBehavior when there is some behavior to initialize
@ -359,7 +366,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
$classes[] = 'phabricator-chromeless-page';
}
$agent = idx($_SERVER, 'HTTP_USER_AGENT');
$agent = AphrontRequest::getHTTPHeader('User-Agent');
// Try to guess the device resolution based on UA strings to avoid a flash
// of incorrectly-styled content.

View file

@ -17,6 +17,7 @@ final class PhabricatorStartup {
private static $startTime;
private static $globals = array();
private static $capturingOutput;
/* -( Accessing Request Information )-------------------------------------- */
@ -78,6 +79,8 @@ final class PhabricatorStartup {
self::verifyRewriteRules();
self::detectPostMaxSizeTriggered();
self::beginOutputCapture();
}
@ -149,6 +152,26 @@ final class PhabricatorStartup {
phutil_load_library($phabricator_root.'/src');
}
/* -( Output Capture )----------------------------------------------------- */
public static function beginOutputCapture() {
if (self::$capturingOutput) {
self::didFatal("Already capturing output!");
}
self::$capturingOutput = true;
ob_start();
}
public static function endOutputCapture() {
if (!self::$capturingOutput) {
return null;
}
self::$capturingOutput = false;
return ob_get_clean();
}
/* -( In Case of Apocalypse )---------------------------------------------- */
@ -157,6 +180,7 @@ final class PhabricatorStartup {
* @task apocalypse
*/
public static function didFatal($message) {
self::endOutputCapture();
$access_log = self::getGlobal('log.access');
if ($access_log) {

View file

@ -15,7 +15,7 @@ try {
PhabricatorStartup::setGlobal('log.access', $access_log);
$access_log->setData(
array(
'R' => idx($_SERVER, 'HTTP_REFERER', '-'),
'R' => AphrontRequest::getHTTPHeader('Referer', '-'),
'r' => idx($_SERVER, 'REMOTE_ADDR', '-'),
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
));
@ -30,11 +30,12 @@ try {
$response = PhabricatorSetupCheck::willProcessRequest();
if ($response) {
PhabricatorStartup::endOutputCapture();
$sink->writeResponse($response);
return;
}
$host = $_SERVER['HTTP_HOST'];
$host = AphrontRequest::getHTTPHeader('Host');
$path = $_REQUEST['__path__'];
switch ($host) {
@ -102,8 +103,21 @@ try {
$response = $application->willSendResponse($response, $controller);
$response->setRequest($request);
$sink->writeResponse($response);
$unexpected_output = PhabricatorStartup::endOutputCapture();
if ($unexpected_output) {
$unexpected_output = "Unexpected output:\n\n{$unexpected_output}";
phlog($unexpected_output);
if ($response instanceof AphrontWebpageResponse) {
echo hsprintf(
'<div style="background: #eeddff; white-space: pre-wrap;
z-index: 200000; position: relative; padding: 8px;
font-family: monospace;">%s</div>',
$unexpected_output);
}
}
$sink->writeResponse($response);
} catch (Exception $ex) {
$write_guard->dispose();
if ($access_log) {
@ -132,25 +146,7 @@ try {
$access_log->write();
}
if (DarkConsoleXHProfPluginAPI::isProfilerRequested()) {
$profile = DarkConsoleXHProfPluginAPI::stopProfiler();
$profile_sample = id(new PhabricatorXHProfSample())
->setFilePHID($profile);
if (empty($_REQUEST['__profile__'])) {
$sample_rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
} else {
$sample_rate = 0;
}
$profile_sample->setSampleRate($sample_rate);
if ($access_log) {
$profile_sample->setUsTotal($access_log->getData('T'))
->setHostname($access_log->getData('h'))
->setRequestPath($access_log->getData('U'))
->setController($access_log->getData('C'))
->setUserPHID($request->getUser()->getPHID());
}
$profile_sample->save();
}
DarkConsoleXHProfPluginAPI::saveProfilerSample($request, $access_log);
} catch (Exception $ex) {
PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage());

View file

@ -4,6 +4,7 @@
.pholio-mock-image-container {
background-color: #282828;
text-align: center;
vertical-align: middle;
}
.pholio-mock-carousel {
@ -43,3 +44,35 @@
margin: 10px 0px;
}
.pholio-mock-inline-comments {
display: inline-block;
margin-left: 10px;
text-align: left;
padding-bottom: 10px;
}
.pholio-inline-comment {
border: 1px solid #aa8;
background: #f9f9f1;
margin-bottom: 2px;
padding: 8px 10px;
}
.pholio-inline-comment-header {
border-bottom: 1px solid #cca;
color: #333;
font-weight: bold;
padding-bottom: 6px;
margin-bottom: 4px;
}
.pholio-mock-inline-comment-highlight {
background-color: #F0B160;
}
.pholio-inline-head-links a {
font-weight: normal;
margin-left: 5px;
}

View file

@ -14,7 +14,6 @@ JX.behavior('dark-console', function(config, statics) {
config.key = config.key || root.getAttribute('data-console-key');
add_request(config);
// Do first-time setup.
function setup_console() {
statics.root = JX.$('darkconsole');
@ -42,6 +41,16 @@ JX.behavior('dark-console', function(config, statics) {
install_shortcut();
if (config.headers) {
// If the main page had profiling enabled, also enable it for any Ajax
// requests.
JX.Request.listen('open', function(r) {
for (var k in config.headers) {
r.getTransport().setRequestHeader(k, config.headers[k]);
}
});
}
return statics.root;
}

View file

@ -139,14 +139,56 @@ JX.behavior('pholio-mock-view', function(config) {
endY: Math.max(startPos.y,endPos.y),
comment: comment};
inlineComment.addData(commentToAdd);
inlineComment.send();
load_inline_comments();
});
function forge_inline_comment(data) {
var comment_head = data.username;
if (data.transactionphid == null) comment_head += " (draft)";
var links = null;
if (data.canEdit && data.transactionphid == null) {
links = JX.$N(
'span',
{
className: 'pholio-inline-head-links'
},
[
JX.$N('a',{href: 'http://www.google.fi'},'Edit'),
JX.$N('a',{href: 'http://www.google.fi'},'Delete')
]);
}
var comment_header = JX.$N(
'div',
{
className: 'pholio-inline-comment-header'
},
[comment_head, links]);
var comment_body = JX.$N(
'div',
{},
data.content);
var inline_comment = JX.$N(
'div',
{
id: data.phid + "_comment",
className: 'pholio-inline-comment'
},
[comment_header, comment_body]);
return inline_comment;
}
function load_inline_comments() {
var data = JX.Stratcom.getData(JX.$(config.mainID));
var comment_holder = JX.$('mock-inline-comments');
JX.DOM.setContent(comment_holder, '');
var inline_comments_url = "/pholio/inline/" + data['imageID'] + "/";
var inline_comments = new JX.Request(inline_comments_url, function(r) {
@ -156,25 +198,68 @@ JX.behavior('pholio-mock-view', function(config) {
var inlineSelection = JX.$N(
'div',
{
id: r[i].phid,
id: r[i].phid + "_selection",
className: 'pholio-mock-select-border',
title: r[i].content
});
JX.Stratcom.addData(
inlineSelection,
{phid: r[i].phid});
JX.Stratcom.addSigil(inlineSelection, "image_selection");
JX.DOM.appendContent(comment_holder, forge_inline_comment(r[i]));
JX.DOM.appendContent(wrapper, inlineSelection);
JX.$V(r[i].x, r[i].y).setPos(inlineSelection);
JX.$V(r[i].width, r[i].height)
.setDim(inlineSelection);
JX.$V(r[i].width, r[i].height).setDim(inlineSelection);
if (r[i].transactionphid == null) {
var inlineDraft = JX.$N(
'div',{className: 'pholio-mock-select-fill'});
JX.$V(r[i].x, r[i].y).setPos(inlineDraft);
JX.$V(r[i].width, r[i].height).setDim(inlineDraft);
JX.Stratcom.addData(
inlineDraft,
{phid: r[i].phid});
JX.Stratcom.addSigil(inlineDraft, "image_selection");
JX.DOM.appendContent(wrapper, inlineDraft);
}
}
}
JX.Stratcom.listen(
'mouseover',
'image_selection',
function(e) {
var data = e.getNodeData('image_selection');
var inline_comment = JX.$(data.phid + "_comment");
JX.DOM.alterClass(inline_comment,
'pholio-mock-inline-comment-highlight', true);
});
JX.Stratcom.listen(
'mouseout',
'image_selection',
function(e) {
var data = e.getNodeData('image_selection');
var inline_comment = JX.$(data.phid + "_comment");
JX.DOM.alterClass(inline_comment,
'pholio-mock-inline-comment-highlight', false);
});
});
inline_comments.send();
}
load_inline_comments();
});