Merge branch 'master' into phutil_tag
Auditors: vrana
This commit is contained in:
@@ -1174,7 +1174,7 @@ celerity_register_resource_map(array(
|
|||||||
),
|
),
|
||||||
'javelin-behavior-dark-console' =>
|
'javelin-behavior-dark-console' =>
|
||||||
array(
|
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',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
@@ -1864,6 +1864,18 @@ celerity_register_resource_map(array(
|
|||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/phame/phame-post-preview.js',
|
'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' =>
|
'javelin-behavior-pholio-mock-view' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/e5f432ac/rsrc/js/application/pholio/behavior-pholio-mock-view.js',
|
'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',
|
'uri' => '/res/pkg/bc0774e5/core.pkg.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
),
|
),
|
||||||
'3e0098ea' =>
|
'dca4a03d' =>
|
||||||
array(
|
array(
|
||||||
'name' => 'darkconsole.pkg.js',
|
'name' => 'darkconsole.pkg.js',
|
||||||
'symbols' =>
|
'symbols' =>
|
||||||
@@ -3496,7 +3508,7 @@ celerity_register_resource_map(array(
|
|||||||
0 => 'javelin-behavior-dark-console',
|
0 => 'javelin-behavior-dark-console',
|
||||||
1 => 'javelin-behavior-error-log',
|
1 => 'javelin-behavior-error-log',
|
||||||
),
|
),
|
||||||
'uri' => '/res/pkg/3e0098ea/darkconsole.pkg.js',
|
'uri' => '/res/pkg/dca4a03d/darkconsole.pkg.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
),
|
),
|
||||||
'8aaacd1b' =>
|
'8aaacd1b' =>
|
||||||
@@ -3666,7 +3678,7 @@ celerity_register_resource_map(array(
|
|||||||
'javelin-behavior-aphront-drag-and-drop-textarea' => '95d0d865',
|
'javelin-behavior-aphront-drag-and-drop-textarea' => '95d0d865',
|
||||||
'javelin-behavior-aphront-form-disable-on-submit' => 'bc0774e5',
|
'javelin-behavior-aphront-form-disable-on-submit' => 'bc0774e5',
|
||||||
'javelin-behavior-audit-preview' => 'f96657b8',
|
'javelin-behavior-audit-preview' => 'f96657b8',
|
||||||
'javelin-behavior-dark-console' => '3e0098ea',
|
'javelin-behavior-dark-console' => 'dca4a03d',
|
||||||
'javelin-behavior-device' => 'bc0774e5',
|
'javelin-behavior-device' => 'bc0774e5',
|
||||||
'javelin-behavior-differential-accept-with-errors' => '95d0d865',
|
'javelin-behavior-differential-accept-with-errors' => '95d0d865',
|
||||||
'javelin-behavior-differential-add-reviewers-and-ccs' => '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-differential-user-select' => '95d0d865',
|
||||||
'javelin-behavior-diffusion-commit-graph' => 'f96657b8',
|
'javelin-behavior-diffusion-commit-graph' => 'f96657b8',
|
||||||
'javelin-behavior-diffusion-pull-lastmodified' => '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-global-drag-and-drop' => 'bc0774e5',
|
||||||
'javelin-behavior-konami' => 'bc0774e5',
|
'javelin-behavior-konami' => 'bc0774e5',
|
||||||
'javelin-behavior-lightbox-attachments' => 'bc0774e5',
|
'javelin-behavior-lightbox-attachments' => 'bc0774e5',
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ phutil_register_library_map(array(
|
|||||||
'ConduitAPI_file_download_Method' => 'applications/files/conduit/ConduitAPI_file_download_Method.php',
|
'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_info_Method' => 'applications/files/conduit/ConduitAPI_file_info_Method.php',
|
||||||
'ConduitAPI_file_upload_Method' => 'applications/files/conduit/ConduitAPI_file_upload_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_Method' => 'applications/flag/conduit/ConduitAPI_flag_Method.php',
|
||||||
'ConduitAPI_flag_delete_Method' => 'applications/flag/conduit/ConduitAPI_flag_delete_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',
|
'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',
|
'PhabricatorApplicationAudit' => 'applications/audit/application/PhabricatorApplicationAudit.php',
|
||||||
'PhabricatorApplicationAuth' => 'applications/auth/application/PhabricatorApplicationAuth.php',
|
'PhabricatorApplicationAuth' => 'applications/auth/application/PhabricatorApplicationAuth.php',
|
||||||
'PhabricatorApplicationCalendar' => 'applications/calendar/application/PhabricatorApplicationCalendar.php',
|
'PhabricatorApplicationCalendar' => 'applications/calendar/application/PhabricatorApplicationCalendar.php',
|
||||||
|
'PhabricatorApplicationChatLog' => 'applications/chatlog/applications/PhabricatorApplicationChatLog.php',
|
||||||
'PhabricatorApplicationConduit' => 'applications/conduit/application/PhabricatorApplicationConduit.php',
|
'PhabricatorApplicationConduit' => 'applications/conduit/application/PhabricatorApplicationConduit.php',
|
||||||
'PhabricatorApplicationConfig' => 'applications/config/application/PhabricatorApplicationConfig.php',
|
'PhabricatorApplicationConfig' => 'applications/config/application/PhabricatorApplicationConfig.php',
|
||||||
'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php',
|
'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php',
|
||||||
@@ -1641,6 +1643,7 @@ phutil_register_library_map(array(
|
|||||||
'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
|
'ConduitAPI_file_download_Method' => 'ConduitAPIMethod',
|
||||||
'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
|
'ConduitAPI_file_info_Method' => 'ConduitAPIMethod',
|
||||||
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
|
'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod',
|
||||||
|
'ConduitAPI_file_uploadhash_Method' => 'ConduitAPIMethod',
|
||||||
'ConduitAPI_flag_Method' => 'ConduitAPIMethod',
|
'ConduitAPI_flag_Method' => 'ConduitAPIMethod',
|
||||||
'ConduitAPI_flag_delete_Method' => 'ConduitAPI_flag_Method',
|
'ConduitAPI_flag_delete_Method' => 'ConduitAPI_flag_Method',
|
||||||
'ConduitAPI_flag_edit_Method' => 'ConduitAPI_flag_Method',
|
'ConduitAPI_flag_edit_Method' => 'ConduitAPI_flag_Method',
|
||||||
@@ -2068,6 +2071,7 @@ phutil_register_library_map(array(
|
|||||||
'PhabricatorApplicationAudit' => 'PhabricatorApplication',
|
'PhabricatorApplicationAudit' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationAuth' => 'PhabricatorApplication',
|
'PhabricatorApplicationAuth' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationCalendar' => 'PhabricatorApplication',
|
'PhabricatorApplicationCalendar' => 'PhabricatorApplication',
|
||||||
|
'PhabricatorApplicationChatLog' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationConduit' => 'PhabricatorApplication',
|
'PhabricatorApplicationConduit' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationConfig' => 'PhabricatorApplication',
|
'PhabricatorApplicationConfig' => 'PhabricatorApplication',
|
||||||
'PhabricatorApplicationConfigOptions' => 'Phobject',
|
'PhabricatorApplicationConfigOptions' => 'Phobject',
|
||||||
|
|||||||
@@ -199,15 +199,7 @@ final class AphrontRequest {
|
|||||||
// No token in the request, check the HTTP header which is added for Ajax
|
// No token in the request, check the HTTP header which is added for Ajax
|
||||||
// requests.
|
// requests.
|
||||||
if (empty($token)) {
|
if (empty($token)) {
|
||||||
|
$token = self::getHTTPHeader(self::getCSRFHeaderName());
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$valid = $this->getUser()->validateCSRFToken($token);
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ class AphrontDefaultApplicationConfiguration
|
|||||||
protected function getResourceURIMapRules() {
|
protected function getResourceURIMapRules() {
|
||||||
return array(
|
return array(
|
||||||
'/res/' => array(
|
'/res/' => array(
|
||||||
|
'(?:(?P<mtime>[0-9]+)T/)?'.
|
||||||
'(?P<package>pkg/)?'.
|
'(?P<package>pkg/)?'.
|
||||||
'(?P<hash>[a-f0-9]{8})/'.
|
'(?P<hash>[a-f0-9]{8})/'.
|
||||||
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'
|
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'
|
||||||
|
|||||||
@@ -101,8 +101,7 @@ final class DarkConsoleXHProfPlugin extends DarkConsolePlugin {
|
|||||||
|
|
||||||
|
|
||||||
public function willShutdown() {
|
public function willShutdown() {
|
||||||
if (DarkConsoleXHProfPluginAPI::isProfilerRequested() &&
|
if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
|
||||||
(DarkConsoleXHProfPluginAPI::isProfilerRequested() !== 'all')) {
|
|
||||||
$this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler();
|
$this->xhprofID = DarkConsoleXHProfPluginAPI::stopProfiler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,25 +13,46 @@ final class DarkConsoleXHProfPluginAPI {
|
|||||||
return extension_loaded('xhprof');
|
return extension_loaded('xhprof');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getProfilerHeader() {
|
||||||
|
return 'X-Phabricator-Profiler';
|
||||||
|
}
|
||||||
|
|
||||||
public static function isProfilerRequested() {
|
public static function isProfilerRequested() {
|
||||||
if (!empty($_REQUEST['__profile__'])) {
|
if (!empty($_REQUEST['__profile__'])) {
|
||||||
return $_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')) {
|
if (PhabricatorEnv::getEnvConfig('debug.profile-rate')) {
|
||||||
$rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
|
$rate = PhabricatorEnv::getEnvConfig('debug.profile-rate');
|
||||||
if (mt_rand(1, $rate) == 1) {
|
if (mt_rand(1, $rate) == 1) {
|
||||||
$profilerRequested = true;
|
$sample_request = true;
|
||||||
} else {
|
} else {
|
||||||
$profilerRequested = false;
|
$sample_request = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $profilerRequested;
|
return $sample_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isProfilerStarted() {
|
||||||
|
return self::$profilerStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function includeXHProfLib() {
|
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() {
|
public static function hookProfiler() {
|
||||||
if (!self::isProfilerRequested()) {
|
if (!self::shouldStartProfiler()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,49 +123,49 @@ final class DarkConsoleXHProfPluginAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static function stopProfiler() {
|
public static function stopProfiler() {
|
||||||
if (self::$profilerStarted) {
|
if (!self::isProfilerStarted()) {
|
||||||
$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 {
|
|
||||||
return null;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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',
|
||||||
|
),
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -10,6 +10,10 @@ final class PhabricatorApplicationConduit extends PhabricatorApplication {
|
|||||||
return 'conduit';
|
return 'conduit';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function canUninstall() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function getHelpURI() {
|
public function getHelpURI() {
|
||||||
return PhabricatorEnv::getDoclink(
|
return PhabricatorEnv::getDoclink(
|
||||||
'article/Conduit_Technical_Documentation.html');
|
'article/Conduit_Technical_Documentation.html');
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ final class PhabricatorSetupCheckBaseURI extends PhabricatorSetupCheck {
|
|||||||
protected function executeChecks() {
|
protected function executeChecks() {
|
||||||
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
|
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
|
||||||
|
|
||||||
if (strpos($_SERVER['HTTP_HOST'], '.') === false) {
|
if (strpos(AphrontRequest::getHTTPHeader('Host'), '.') === false) {
|
||||||
$summary = pht(
|
$summary = pht(
|
||||||
'The domain does not contain a dot. This is necessary for some web '.
|
'The domain does not contain a dot. This is necessary for some web '.
|
||||||
'browsers to be able to set cookies.');
|
'browsers to be able to set cookies.');
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ final class PhabricatorConfigEditController
|
|||||||
case 'int':
|
case 'int':
|
||||||
case 'string':
|
case 'string':
|
||||||
case 'enum':
|
case 'enum':
|
||||||
|
case 'class':
|
||||||
return $value;
|
return $value;
|
||||||
case 'bool':
|
case 'bool':
|
||||||
return $value ? 'true' : 'false';
|
return $value ? 'true' : 'false';
|
||||||
|
|||||||
@@ -227,6 +227,11 @@ final class ConpherenceThreadQuery
|
|||||||
$conpherence_pic_phids[$conpherence->getPHID()] = $phid;
|
$conpherence_pic_phids[$conpherence->getPHID()] = $phid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$conpherence_pic_phids) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
$files = id(new PhabricatorFileQuery())
|
$files = id(new PhabricatorFileQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withPHIDs($conpherence_pic_phids)
|
->withPHIDs($conpherence_pic_phids)
|
||||||
|
|||||||
@@ -71,10 +71,6 @@ final class ConpherenceTransactionView extends AphrontView {
|
|||||||
break;
|
break;
|
||||||
case PhabricatorTransactions::TYPE_COMMENT:
|
case PhabricatorTransactions::TYPE_COMMENT:
|
||||||
$comment = $transaction->getComment();
|
$comment = $transaction->getComment();
|
||||||
$file_ids =
|
|
||||||
PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
|
|
||||||
array($comment->getContent())
|
|
||||||
);
|
|
||||||
$content = $this->markupEngine->getOutput(
|
$content = $this->markupEngine->getOutput(
|
||||||
$comment,
|
$comment,
|
||||||
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ final class DifferentialLintFieldSpecification
|
|||||||
'show' => $show,
|
'show' => $show,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isset($message['locations'])) {
|
if (!empty($message['locations'])) {
|
||||||
$locations = array();
|
$locations = array();
|
||||||
foreach ($message['locations'] as $location) {
|
foreach ($message['locations'] as $location) {
|
||||||
$other_line = idx($location, 'line');
|
$other_line = idx($location, 'line');
|
||||||
|
|||||||
@@ -420,14 +420,6 @@ final class DifferentialChangesetParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function getHighlightFuture($corpus) {
|
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(
|
return $this->highlightEngine->getHighlightFuture(
|
||||||
$this->highlightEngine->getLanguageFromFilename($this->filename),
|
$this->highlightEngine->getLanguageFromFilename($this->filename),
|
||||||
$corpus);
|
$corpus);
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ final class DiffusionGitRequest extends DiffusionRequest {
|
|||||||
protected function didInitialize() {
|
protected function didInitialize() {
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
if (!Filesystem::pathExists($repository->getLocalPath())) {
|
$this->validateWorkingCopy($repository->getLocalPath());
|
||||||
$this->raiseCloneException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->commit) {
|
if (!$this->commit) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ final class DiffusionMercurialRequest extends DiffusionRequest {
|
|||||||
protected function didInitialize() {
|
protected function didInitialize() {
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
if (!Filesystem::pathExists($repository->getLocalPath())) {
|
$this->validateWorkingCopy($repository->getLocalPath());
|
||||||
$this->raiseCloneException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand abbreviated hashes to full hashes so "/rXnnnn" (i.e., fewer than
|
// Expand abbreviated hashes to full hashes so "/rXnnnn" (i.e., fewer than
|
||||||
// 40 characters) works correctly.
|
// 40 characters) works correctly.
|
||||||
|
|||||||
@@ -544,6 +544,30 @@ abstract class DiffusionRequest {
|
|||||||
return $result;
|
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() {
|
protected function raiseCloneException() {
|
||||||
$host = php_uname('n');
|
$host = php_uname('n');
|
||||||
$callsign = $this->getRepository()->getCallsign();
|
$callsign = $this->getRepository()->getCallsign();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ final class PhabricatorTestStorageEngine
|
|||||||
public function writeFile($data, array $params) {
|
public function writeFile($data, array $params) {
|
||||||
AphrontWriteGuard::willWrite();
|
AphrontWriteGuard::willWrite();
|
||||||
self::$storage[self::$nextHandle] = $data;
|
self::$storage[self::$nextHandle] = $data;
|
||||||
return self::$nextHandle++;
|
return (string)self::$nextHandle++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function readFile($handle) {
|
public function readFile($handle) {
|
||||||
|
|||||||
@@ -132,8 +132,46 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||||||
return $file;
|
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'])) {
|
if (isset($params['storageEngines'])) {
|
||||||
$engines = $params['storageEngines'];
|
$engines = $params['storageEngines'];
|
||||||
@@ -221,6 +259,17 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||||||
return $file;
|
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) {
|
public function migrateToEngine(PhabricatorFileStorageEngine $engine) {
|
||||||
if (!$this->getID() || !$this->getStorageHandle()) {
|
if (!$this->getID() || !$this->getStorageHandle()) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
@@ -305,15 +354,28 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
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();
|
$ret = parent::delete();
|
||||||
|
|
||||||
$engine->deleteFile($this->getStorageHandle());
|
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function hashFileContent($data) {
|
||||||
|
return PhabricatorHash::digest($data);
|
||||||
|
}
|
||||||
|
|
||||||
public function loadFileData() {
|
public function loadFileData() {
|
||||||
|
|
||||||
$engine = $this->instantiateStorageEngine();
|
$engine = $this->instantiateStorageEngine();
|
||||||
|
|||||||
@@ -31,6 +31,55 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
|
|||||||
$this->assertEqual($data, $file->loadFileData());
|
$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() {
|
public function testFileStorageDelete() {
|
||||||
$engine = new PhabricatorTestStorageEngine();
|
$engine = new PhabricatorTestStorageEngine();
|
||||||
@@ -58,4 +107,23 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
|
|||||||
$this->assertEqual(true, $caught instanceof Exception);
|
$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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ abstract class PhabricatorPasteController extends PhabricatorController {
|
|||||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI('filter/')));
|
$nav->setBaseURI(new PhutilURI($this->getApplicationURI('filter/')));
|
||||||
|
|
||||||
if ($for_app) {
|
if ($for_app) {
|
||||||
$nav->addFilter('', 'Create Paste', $this->getApplicationURI('/create/'));
|
$nav->addFilter('', pht('Create Paste'),
|
||||||
|
$this->getApplicationURI('/create/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$nav->addLabel('Filters');
|
$nav->addLabel(pht('Filters'));
|
||||||
$nav->addFilter('all', 'All Pastes');
|
$nav->addFilter('all', pht('All Pastes'));
|
||||||
if ($user->isLoggedIn()) {
|
if ($user->isLoggedIn()) {
|
||||||
$nav->addFilter('my', 'My Pastes');
|
$nav->addFilter('my', pht('My Pastes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$nav->selectFilter($filter, 'all');
|
$nav->selectFilter($filter, 'all');
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
if ($is_create) {
|
if ($is_create) {
|
||||||
$text = $request->getStr('text');
|
$text = $request->getStr('text');
|
||||||
if (!strlen($text)) {
|
if (!strlen($text)) {
|
||||||
$e_text = 'Required';
|
$e_text = pht('Required');
|
||||||
$errors[] = 'The paste may not be blank.';
|
$errors[] = pht('The paste may not be blank.');
|
||||||
} else {
|
} else {
|
||||||
$e_text = null;
|
$e_text = null;
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($is_create && $parent) {
|
if ($is_create && $parent) {
|
||||||
$paste->setTitle('Fork of '.$parent->getFullName());
|
$paste->setTitle(pht('Fork of %s', $parent->getFullName()));
|
||||||
$paste->setLanguage($parent->getLanguage());
|
$paste->setLanguage($parent->getLanguage());
|
||||||
$text = $parent->getRawContent();
|
$text = $parent->getRawContent();
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
$error_view = null;
|
$error_view = null;
|
||||||
if ($errors) {
|
if ($errors) {
|
||||||
$error_view = id(new AphrontErrorView())
|
$error_view = id(new AphrontErrorView())
|
||||||
->setTitle('A fatal omission!')
|
->setTitle(pht('A Fatal Omission!'))
|
||||||
->setErrors($errors);
|
->setErrors($errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
$form->setFlexible(true);
|
$form->setFlexible(true);
|
||||||
|
|
||||||
$langs = array(
|
$langs = array(
|
||||||
'' => '(Detect From Filename in Title)',
|
'' => pht('(Detect From Filename in Title)'),
|
||||||
) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices');
|
) + PhabricatorEnv::getEnvConfig('pygments.dropdown-choices');
|
||||||
|
|
||||||
$form
|
$form
|
||||||
@@ -119,12 +119,12 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
->addHiddenInput('parent', $parent_id)
|
->addHiddenInput('parent', $parent_id)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
->setLabel('Title')
|
->setLabel(pht('Title'))
|
||||||
->setValue($paste->getTitle())
|
->setValue($paste->getTitle())
|
||||||
->setName('title'))
|
->setName('title'))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSelectControl())
|
id(new AphrontFormSelectControl())
|
||||||
->setLabel('Language')
|
->setLabel(pht('Language'))
|
||||||
->setName('language')
|
->setName('language')
|
||||||
->setValue($paste->getLanguage())
|
->setValue($paste->getLanguage())
|
||||||
->setOptions($langs));
|
->setOptions($langs));
|
||||||
@@ -146,7 +146,7 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
$form
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextAreaControl())
|
id(new AphrontFormTextAreaControl())
|
||||||
->setLabel('Text')
|
->setLabel(pht('Text'))
|
||||||
->setError($e_text)
|
->setError($e_text)
|
||||||
->setValue($text)
|
->setValue($text)
|
||||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||||
@@ -158,13 +158,13 @@ final class PhabricatorPasteEditController extends PhabricatorPasteController {
|
|||||||
array(
|
array(
|
||||||
'href' => $this->getApplicationURI('?parent='.$paste->getID())
|
'href' => $this->getApplicationURI('?parent='.$paste->getID())
|
||||||
),
|
),
|
||||||
'Fork'
|
pht('Fork')
|
||||||
);
|
);
|
||||||
$form
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormMarkupControl())
|
id(new AphrontFormMarkupControl())
|
||||||
->setLabel('Text')
|
->setLabel(pht('Text'))
|
||||||
->setValue(hsprintf(
|
->setValue(pht(
|
||||||
'Paste text can not be edited. %s to create a new paste.',
|
'Paste text can not be edited. %s to create a new paste.',
|
||||||
$fork_link)));
|
$fork_link)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
|||||||
public function getFullName() {
|
public function getFullName() {
|
||||||
$title = $this->getTitle();
|
$title = $this->getTitle();
|
||||||
if (!$title) {
|
if (!$title) {
|
||||||
$title = '(An Untitled Masterwork)';
|
$title = pht('(An Untitled Masterwork)');
|
||||||
}
|
}
|
||||||
return 'P'.$this->getID().' '.$title;
|
return 'P'.$this->getID().' '.$title;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ final class PhabricatorUserLog extends PhabricatorUserDAO {
|
|||||||
$this->setSession(idx($_COOKIE, 'phsid'));
|
$this->setSession(idx($_COOKIE, 'phsid'));
|
||||||
}
|
}
|
||||||
$this->details['host'] = php_uname('n');
|
$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();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,27 @@ final class PholioInlineController extends PholioController {
|
|||||||
$this->id
|
$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();
|
$inlines = array();
|
||||||
foreach ($inline_comments as $inline_comment) {
|
foreach ($inline_comments as $inline_comment) {
|
||||||
|
$author = id(new PhabricatorUser())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$inline_comment->getAuthorPHID()
|
||||||
|
);
|
||||||
$inlines[] = array(
|
$inlines[] = array(
|
||||||
'phid' => $inline_comment->getPHID(),
|
'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(),
|
'imageID' => $inline_comment->getImageID(),
|
||||||
'x' => $inline_comment->getX(),
|
'x' => $inline_comment->getX(),
|
||||||
'y' => $inline_comment->getY(),
|
'y' => $inline_comment->getY(),
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ final class PholioInlineSaveController extends PholioController {
|
|||||||
->setViewer($user)
|
->setViewer($user)
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW
|
||||||
PhabricatorPolicyCapability::CAN_EDIT,
|
|
||||||
))
|
))
|
||||||
->withIDs(array($request->getInt('mockID')))
|
->withIDs(array($request->getInt('mockID')))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|||||||
@@ -47,12 +47,22 @@ final class PholioMockImagesView extends AphrontView {
|
|||||||
$main_image_tag
|
$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(
|
$mockview[] = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'pholio-mock-image-container',
|
'class' => 'pholio-mock-image-container',
|
||||||
),
|
),
|
||||||
$main_image_tag);
|
array($main_image_tag, $inline_comments_holder));
|
||||||
|
|
||||||
if (count($this->mock->getImages()) > 1) {
|
if (count($this->mock->getImages()) > 1) {
|
||||||
$thumbnails = array();
|
$thumbnails = array();
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ final class PhrictionEditController
|
|||||||
'uri' => '/phriction/preview/?draftkey='.$draft_key,
|
'uri' => '/phriction/preview/?draftkey='.$draft_key,
|
||||||
));
|
));
|
||||||
|
|
||||||
return $this->buildStandardPageResponse(
|
return $this->buildApplicationPage(
|
||||||
array(
|
array(
|
||||||
$draft_note,
|
$draft_note,
|
||||||
$error_view,
|
$error_view,
|
||||||
@@ -258,6 +258,7 @@ final class PhrictionEditController
|
|||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => pht('Edit Document'),
|
'title' => pht('Edit Document'),
|
||||||
|
'device' => true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
final class PhabricatorApplicationProject extends PhabricatorApplication {
|
final class PhabricatorApplicationProject extends PhabricatorApplication {
|
||||||
|
|
||||||
public function getName() {
|
public function getName() {
|
||||||
return 'Projects';
|
return pht('Projects');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShortDescription() {
|
public function getShortDescription() {
|
||||||
return 'Organize Work';
|
return pht('Organize Work');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBaseURI() {
|
public function getBaseURI() {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ abstract class PhabricatorProjectController extends PhabricatorController {
|
|||||||
public function buildStandardPageResponse($view, array $data) {
|
public function buildStandardPageResponse($view, array $data) {
|
||||||
$page = $this->buildStandardPageView();
|
$page = $this->buildStandardPageView();
|
||||||
|
|
||||||
$page->setApplicationName('Project');
|
$page->setApplicationName(pht('Project'));
|
||||||
$page->setBaseURI('/project/');
|
$page->setBaseURI('/project/');
|
||||||
$page->setTitle(idx($data, 'title'));
|
$page->setTitle(idx($data, 'title'));
|
||||||
$page->setGlyph("\xE2\x98\xA3");
|
$page->setGlyph("\xE2\x98\xA3");
|
||||||
@@ -30,29 +30,29 @@ abstract class PhabricatorProjectController extends PhabricatorController {
|
|||||||
$edit_uri = '/project/edit/'.$id.'/';
|
$edit_uri = '/project/edit/'.$id.'/';
|
||||||
$members_uri = '/project/members/'.$id.'/';
|
$members_uri = '/project/members/'.$id.'/';
|
||||||
|
|
||||||
$nav_view->addFilter('dashboard', 'Dashboard');
|
$nav_view->addFilter('dashboard', pht('Dashboard'));
|
||||||
$nav_view->addFilter('feed', 'Feed');
|
$nav_view->addFilter('feed', pht('Feed'));
|
||||||
$nav_view->addFilter(null, 'Tasks '.$external_arrow, $tasks_uri);
|
$nav_view->addFilter(null, pht('Tasks').' '.$external_arrow, $tasks_uri);
|
||||||
$nav_view->addFilter(null, 'Wiki '.$external_arrow, $phriction_uri);
|
$nav_view->addFilter(null, pht('Wiki').' '.$external_arrow, $phriction_uri);
|
||||||
$nav_view->addFilter('people', 'People');
|
$nav_view->addFilter('people', pht('People'));
|
||||||
$nav_view->addFilter('about', 'About');
|
$nav_view->addFilter('about', pht('About'));
|
||||||
|
|
||||||
$user = $this->getRequest()->getUser();
|
$user = $this->getRequest()->getUser();
|
||||||
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
|
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
|
||||||
|
|
||||||
if (PhabricatorPolicyFilter::hasCapability($user, $project, $can_edit)) {
|
if (PhabricatorPolicyFilter::hasCapability($user, $project, $can_edit)) {
|
||||||
$nav_view->addFilter('edit', "Edit Project\xE2\x80\xA6", $edit_uri);
|
$nav_view->addFilter('edit', pht("Edit Project"), $edit_uri);
|
||||||
$nav_view->addFilter('members', "Edit Members\xE2\x80\xA6", $members_uri);
|
$nav_view->addFilter('members', pht("Edit Members"), $members_uri);
|
||||||
} else {
|
} else {
|
||||||
$nav_view->addFilter(
|
$nav_view->addFilter(
|
||||||
'edit',
|
'edit',
|
||||||
"Edit Project\xE2\x80\xA6",
|
pht("Edit Project"),
|
||||||
$edit_uri,
|
$edit_uri,
|
||||||
$relative = false,
|
$relative = false,
|
||||||
'disabled');
|
'disabled');
|
||||||
$nav_view->addFilter(
|
$nav_view->addFilter(
|
||||||
'members',
|
'members',
|
||||||
"Edit Members\xE2\x80\xA6",
|
pht("Edit Members"),
|
||||||
$members_uri,
|
$members_uri,
|
||||||
$relative = false,
|
$relative = false,
|
||||||
'disabled');
|
'disabled');
|
||||||
@@ -61,4 +61,40 @@ abstract class PhabricatorProjectController extends PhabricatorController {
|
|||||||
return $nav_view;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ final class PhabricatorProjectCreateController
|
|||||||
$error_view = null;
|
$error_view = null;
|
||||||
if ($errors) {
|
if ($errors) {
|
||||||
$error_view = new AphrontErrorView();
|
$error_view = new AphrontErrorView();
|
||||||
$error_view->setTitle('Form Errors');
|
$error_view->setTitle(pht('Form Errors'));
|
||||||
$error_view->setErrors($errors);
|
$error_view->setErrors($errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,13 +77,13 @@ final class PhabricatorProjectCreateController
|
|||||||
$form
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
->setLabel('Name')
|
->setLabel(pht('Name'))
|
||||||
->setName('name')
|
->setName('name')
|
||||||
->setValue($project->getName())
|
->setValue($project->getName())
|
||||||
->setError($e_name))
|
->setError($e_name))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextAreaControl())
|
id(new AphrontFormTextAreaControl())
|
||||||
->setLabel('Blurb')
|
->setLabel(pht('Blurb'))
|
||||||
->setName('blurb')
|
->setName('blurb')
|
||||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
|
||||||
->setValue($profile->getBlurb()));
|
->setValue($profile->getBlurb()));
|
||||||
@@ -92,10 +92,10 @@ final class PhabricatorProjectCreateController
|
|||||||
$dialog = id(new AphrontDialogView())
|
$dialog = id(new AphrontDialogView())
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||||
->setTitle('Create a New Project')
|
->setTitle(pht('Create a New Project'))
|
||||||
->appendChild($error_view)
|
->appendChild($error_view)
|
||||||
->appendChild($form)
|
->appendChild($form)
|
||||||
->addSubmitButton('Create Project')
|
->addSubmitButton(pht('Create Project'))
|
||||||
->addCancelButton('/project/');
|
->addCancelButton('/project/');
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
@@ -104,22 +104,32 @@ final class PhabricatorProjectCreateController
|
|||||||
$form
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue('Create')
|
->setValue(pht('Create'))
|
||||||
->addCancelButton('/project/'));
|
->addCancelButton('/project/'));
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
$panel
|
$panel
|
||||||
->setWidth(AphrontPanelView::WIDTH_FORM)
|
->setWidth(AphrontPanelView::WIDTH_FORM)
|
||||||
->setHeader('Create a New Project')
|
->setHeader(pht('Create a New Project'))
|
||||||
|
->setNoBackground()
|
||||||
->appendChild($form);
|
->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(
|
array(
|
||||||
|
$crumbs,
|
||||||
$error_view,
|
$error_view,
|
||||||
$panel,
|
$panel,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => 'Create new Project',
|
'title' => pht('Create New Project'),
|
||||||
|
'device' => true
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,14 +12,7 @@ final class PhabricatorProjectListController
|
|||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
|
||||||
$nav = new AphrontSideNavFilterView();
|
$nav = $this->buildSideNavView($this->filter);
|
||||||
$nav
|
|
||||||
->setBaseURI(new PhutilURI('/project/filter/'))
|
|
||||||
->addLabel('User')
|
|
||||||
->addFilter('active', 'Active')
|
|
||||||
->addLabel('All')
|
|
||||||
->addFilter('all', 'All Projects')
|
|
||||||
->addFilter('allactive','Active Projects');
|
|
||||||
$this->filter = $nav->selectFilter($this->filter, 'active');
|
$this->filter = $nav->selectFilter($this->filter, 'active');
|
||||||
|
|
||||||
$pager = new AphrontPagerView();
|
$pager = new AphrontPagerView();
|
||||||
@@ -33,21 +26,19 @@ final class PhabricatorProjectListController
|
|||||||
|
|
||||||
$view_phid = $request->getUser()->getPHID();
|
$view_phid = $request->getUser()->getPHID();
|
||||||
|
|
||||||
$status_filter = PhabricatorProjectQuery::STATUS_ANY;
|
|
||||||
|
|
||||||
switch ($this->filter) {
|
switch ($this->filter) {
|
||||||
case 'active':
|
case 'active':
|
||||||
$table_header = 'Your Projects';
|
$table_header = pht('Your Projects');
|
||||||
$query->withMemberPHIDs(array($view_phid));
|
$query->withMemberPHIDs(array($view_phid));
|
||||||
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
|
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
|
||||||
break;
|
break;
|
||||||
case 'allactive':
|
case 'allactive':
|
||||||
$status_filter = PhabricatorProjectQuery::STATUS_ACTIVE;
|
$table_header = pht('Active Projects');
|
||||||
$table_header = 'Active Projects';
|
$query->withStatus(PhabricatorProjectQuery::STATUS_ACTIVE);
|
||||||
// fallthrough
|
break;
|
||||||
case 'all':
|
case 'all':
|
||||||
$table_header = 'All Projects';
|
$table_header = pht('All Projects');
|
||||||
$query->withStatus($status_filter);
|
$query->withStatus(PhabricatorProjectQuery::STATUS_ANY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,11 +112,11 @@ final class PhabricatorProjectListController
|
|||||||
$table = new AphrontTableView($rows);
|
$table = new AphrontTableView($rows);
|
||||||
$table->setHeaders(
|
$table->setHeaders(
|
||||||
array(
|
array(
|
||||||
'Project',
|
pht('Project'),
|
||||||
'Status',
|
pht('Status'),
|
||||||
'Description',
|
pht('Description'),
|
||||||
'Population',
|
pht('Population'),
|
||||||
'Open Tasks',
|
pht('Open Tasks'),
|
||||||
));
|
));
|
||||||
$table->setColumnClasses(
|
$table->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
@@ -138,16 +129,25 @@ final class PhabricatorProjectListController
|
|||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
$panel->setHeader($table_header);
|
$panel->setHeader($table_header);
|
||||||
$panel->setCreateButton('Create New Project', '/project/create/');
|
|
||||||
$panel->appendChild($table);
|
$panel->appendChild($table);
|
||||||
|
$panel->setNoBackground();
|
||||||
$panel->appendChild($pager);
|
$panel->appendChild($pager);
|
||||||
|
|
||||||
$nav->appendChild($panel);
|
$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,
|
$nav,
|
||||||
array(
|
array(
|
||||||
'title' => 'Projects',
|
'title' => pht('Projects'),
|
||||||
|
'device' => true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ final class PhabricatorProjectMembersEditController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$header_name = 'Edit Members';
|
$header_name = pht('Edit Members');
|
||||||
$title = 'Edit Members';
|
$title = pht('Edit Members');
|
||||||
|
|
||||||
$list = $this->renderMemberList($handles);
|
$list = $this->renderMemberList($handles);
|
||||||
|
|
||||||
@@ -94,23 +94,24 @@ final class PhabricatorProjectMembersEditController
|
|||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTokenizerControl())
|
id(new AphrontFormTokenizerControl())
|
||||||
->setName('phids')
|
->setName('phids')
|
||||||
->setLabel('Add Members')
|
->setLabel(pht('Add Members'))
|
||||||
->setDatasource('/typeahead/common/users/'))
|
->setDatasource('/typeahead/common/users/'))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->addCancelButton('/project/view/'.$project->getID().'/')
|
->addCancelButton('/project/view/'.$project->getID().'/')
|
||||||
->setValue('Add Members'));
|
->setValue(pht('Add Members')));
|
||||||
$faux_form = id(new AphrontFormLayoutView())
|
$faux_form = id(new AphrontFormLayoutView())
|
||||||
->setBackgroundShading(true)
|
->setBackgroundShading(true)
|
||||||
->setPadded(true)
|
->setPadded(true)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormInsetView())
|
id(new AphrontFormInsetView())
|
||||||
->setTitle('Current Members ('.count($handles).')')
|
->setTitle(pht('Current Members (%d)', count($handles)))
|
||||||
->appendChild($list));
|
->appendChild($list));
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
$panel->setHeader($header_name);
|
$panel->setHeader($header_name);
|
||||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
$panel->setNoBackground();
|
||||||
$panel->appendChild($form);
|
$panel->appendChild($form);
|
||||||
$panel->appendChild(phutil_tag('br'));
|
$panel->appendChild(phutil_tag('br'));
|
||||||
$panel->appendChild($faux_form);
|
$panel->appendChild($faux_form);
|
||||||
@@ -119,10 +120,24 @@ final class PhabricatorProjectMembersEditController
|
|||||||
$nav->selectFilter('members');
|
$nav->selectFilter('members');
|
||||||
$nav->appendChild($panel);
|
$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,
|
$nav,
|
||||||
array(
|
array(
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
|
'device' => true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ final class PhabricatorProjectProfileController
|
|||||||
|
|
||||||
private $id;
|
private $id;
|
||||||
private $page;
|
private $page;
|
||||||
|
private $project;
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
public function willProcessRequest(array $data) {
|
||||||
$this->id = idx($data, 'id');
|
$this->id = idx($data, 'id');
|
||||||
@@ -24,6 +25,7 @@ final class PhabricatorProjectProfileController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$project = $query->executeOne();
|
$project = $query->executeOne();
|
||||||
|
$this->project = $project;
|
||||||
if (!$project) {
|
if (!$project) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
@@ -96,7 +98,7 @@ final class PhabricatorProjectProfileController
|
|||||||
array(
|
array(
|
||||||
'class' => $class,
|
'class' => $class,
|
||||||
),
|
),
|
||||||
'Join Project'));
|
pht('Join Project')));
|
||||||
} else {
|
} else {
|
||||||
$action = javelin_tag(
|
$action = javelin_tag(
|
||||||
'a',
|
'a',
|
||||||
@@ -105,7 +107,7 @@ final class PhabricatorProjectProfileController
|
|||||||
'sigil' => 'workflow',
|
'sigil' => 'workflow',
|
||||||
'class' => 'grey button',
|
'class' => 'grey button',
|
||||||
),
|
),
|
||||||
'Leave Project...');
|
pht('Leave Project...'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$header->addAction($action);
|
$header->addAction($action);
|
||||||
@@ -115,10 +117,10 @@ final class PhabricatorProjectProfileController
|
|||||||
$content = hsprintf('<div style="padding: 1em;">%s</div>', $content);
|
$content = hsprintf('<div style="padding: 1em;">%s</div>', $content);
|
||||||
$header->appendChild($content);
|
$header->appendChild($content);
|
||||||
|
|
||||||
return $this->buildStandardPageResponse(
|
return $this->buildApplicationPage(
|
||||||
$nav_view,
|
$nav_view,
|
||||||
array(
|
array(
|
||||||
'title' => $project->getName().' Project',
|
'title' => pht('%s Project', $project->getName()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,11 +145,11 @@ final class PhabricatorProjectProfileController
|
|||||||
<div class="phabricator-profile-info-pane">
|
<div class="phabricator-profile-info-pane">
|
||||||
<table class="phabricator-profile-info-table">
|
<table class="phabricator-profile-info-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Creator</th>
|
<th>%s</th>
|
||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Created</th>
|
<th>%s</th>
|
||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -155,15 +157,18 @@ final class PhabricatorProjectProfileController
|
|||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Blurb</th>
|
<th>%s</th>
|
||||||
<td>%s</td>
|
<td>%s</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>',
|
</div>',
|
||||||
|
pht('Creator'),
|
||||||
$handles[$project->getAuthorPHID()]->renderLink(),
|
$handles[$project->getAuthorPHID()]->renderLink(),
|
||||||
|
pht('Created'),
|
||||||
$timestamp,
|
$timestamp,
|
||||||
$project->getPHID(),
|
$project->getPHID(),
|
||||||
|
pht('Blurb'),
|
||||||
$blurb);
|
$blurb);
|
||||||
|
|
||||||
return $about;
|
return $about;
|
||||||
@@ -190,9 +195,10 @@ final class PhabricatorProjectProfileController
|
|||||||
|
|
||||||
return hsprintf(
|
return hsprintf(
|
||||||
'<div class="phabricator-profile-info-group">'.
|
'<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 class="phabricator-profile-info-pane">%s</div>'.
|
||||||
'</div>',
|
'</div>',
|
||||||
|
pht('People'),
|
||||||
$affiliated);
|
$affiliated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +213,7 @@ final class PhabricatorProjectProfileController
|
|||||||
$stories = $query->execute();
|
$stories = $query->execute();
|
||||||
|
|
||||||
if (!$stories) {
|
if (!$stories) {
|
||||||
return 'There are no stories about this project.';
|
return pht('There are no stories about this project.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->renderStories($stories);
|
return $this->renderStories($stories);
|
||||||
@@ -222,9 +228,10 @@ final class PhabricatorProjectProfileController
|
|||||||
|
|
||||||
return hsprintf(
|
return hsprintf(
|
||||||
'<div class="phabricator-profile-info-group">'.
|
'<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 class="phabricator-profile-info-pane">%s</div>'.
|
||||||
'</div>',
|
'</div>',
|
||||||
|
pht('Activity Feed'),
|
||||||
$view->render());
|
$view->render());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,17 +275,17 @@ final class PhabricatorProjectProfileController
|
|||||||
array(
|
array(
|
||||||
'href' => '/maniphest/view/all/?projects='.$project->getPHID(),
|
'href' => '/maniphest/view/all/?projects='.$project->getPHID(),
|
||||||
),
|
),
|
||||||
"View All Open Tasks \xC2\xBB");
|
pht("View All Open Tasks \xC2\xBB"));
|
||||||
|
|
||||||
$content = hsprintf(
|
$content = hsprintf(
|
||||||
'<div class="phabricator-profile-info-group">
|
'<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">'.
|
'<div class="phabricator-profile-info-pane">'.
|
||||||
'%s'.
|
'%s'.
|
||||||
'<div class="phabricator-profile-info-pane-more-link">%s</div>'.
|
'<div class="phabricator-profile-info-pane-more-link">%s</div>'.
|
||||||
'</div>
|
'</div>
|
||||||
</div>',
|
</div>',
|
||||||
$open,
|
pht('Open Tasks (%s)', $open),
|
||||||
$task_views,
|
$task_views,
|
||||||
$more_link);
|
$more_link);
|
||||||
|
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ final class PhabricatorProjectProfileEditController
|
|||||||
$profile->setBlurb($request->getStr('blurb'));
|
$profile->setBlurb($request->getStr('blurb'));
|
||||||
|
|
||||||
if (!strlen($project->getName())) {
|
if (!strlen($project->getName())) {
|
||||||
$e_name = 'Required';
|
$e_name = pht('Required');
|
||||||
$errors[] = 'Project name is required.';
|
$errors[] = pht('Project name is required.');
|
||||||
} else {
|
} else {
|
||||||
$e_name = null;
|
$e_name = null;
|
||||||
}
|
}
|
||||||
@@ -112,9 +112,9 @@ final class PhabricatorProjectProfileEditController
|
|||||||
$y = 50);
|
$y = 50);
|
||||||
$profile->setProfileImagePHID($xformed->getPHID());
|
$profile->setProfileImagePHID($xformed->getPHID());
|
||||||
} else {
|
} else {
|
||||||
$e_image = 'Not Supported';
|
$e_image = pht('Not Supported');
|
||||||
$errors[] =
|
$errors[] =
|
||||||
'This server only supports these image formats: '.
|
pht('This server only supports these image formats:').' '.
|
||||||
implode(', ', $supported_formats).'.';
|
implode(', ', $supported_formats).'.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,12 +132,12 @@ final class PhabricatorProjectProfileEditController
|
|||||||
$error_view = null;
|
$error_view = null;
|
||||||
if ($errors) {
|
if ($errors) {
|
||||||
$error_view = new AphrontErrorView();
|
$error_view = new AphrontErrorView();
|
||||||
$error_view->setTitle('Form Errors');
|
$error_view->setTitle(pht('Form Errors'));
|
||||||
$error_view->setErrors($errors);
|
$error_view->setErrors($errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
$header_name = 'Edit Project';
|
$header_name = pht('Edit Project');
|
||||||
$title = 'Edit Project';
|
$title = pht('Edit Project');
|
||||||
$action = '/project/edit/'.$project->getID().'/';
|
$action = '/project/edit/'.$project->getID().'/';
|
||||||
|
|
||||||
$policies = id(new PhabricatorPolicyQuery())
|
$policies = id(new PhabricatorPolicyQuery())
|
||||||
@@ -153,30 +153,30 @@ final class PhabricatorProjectProfileEditController
|
|||||||
->setEncType('multipart/form-data')
|
->setEncType('multipart/form-data')
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
->setLabel('Name')
|
->setLabel(pht('Name'))
|
||||||
->setName('name')
|
->setName('name')
|
||||||
->setValue($project->getName())
|
->setValue($project->getName())
|
||||||
->setError($e_name))
|
->setError($e_name))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSelectControl())
|
id(new AphrontFormSelectControl())
|
||||||
->setLabel('Project Status')
|
->setLabel(pht('Project Status'))
|
||||||
->setName('status')
|
->setName('status')
|
||||||
->setOptions($options)
|
->setOptions($options)
|
||||||
->setValue($project->getStatus()))
|
->setValue($project->getStatus()))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextAreaControl())
|
id(new AphrontFormTextAreaControl())
|
||||||
->setLabel('Blurb')
|
->setLabel(pht('Blurb'))
|
||||||
->setName('blurb')
|
->setName('blurb')
|
||||||
->setValue($profile->getBlurb()))
|
->setValue($profile->getBlurb()))
|
||||||
->appendChild(hsprintf(
|
->appendChild('<p class="aphront-form-instructions">'.
|
||||||
'<p class="aphront-form-instructions">NOTE: Policy settings are not '.
|
pht('NOTE: Policy settings are not yet fully implemented. '.
|
||||||
'yet fully implemented. Some interfaces still ignore these settings, '.
|
'Some interfaces still ignore these settings, '.
|
||||||
'particularly "Visible To".</p>'))
|
'particularly "Visible To".').'</p>')
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormPolicyControl())
|
id(new AphrontFormPolicyControl())
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setName('can_view')
|
->setName('can_view')
|
||||||
->setCaption('Members can always view a project.')
|
->setCaption(pht('Members can always view a project.'))
|
||||||
->setPolicyObject($project)
|
->setPolicyObject($project)
|
||||||
->setPolicies($policies)
|
->setPolicies($policies)
|
||||||
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
|
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
|
||||||
@@ -192,13 +192,13 @@ final class PhabricatorProjectProfileEditController
|
|||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setName('can_join')
|
->setName('can_join')
|
||||||
->setCaption(
|
->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)
|
->setPolicyObject($project)
|
||||||
->setPolicies($policies)
|
->setPolicies($policies)
|
||||||
->setCapability(PhabricatorPolicyCapability::CAN_JOIN))
|
->setCapability(PhabricatorPolicyCapability::CAN_JOIN))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormMarkupControl())
|
id(new AphrontFormMarkupControl())
|
||||||
->setLabel('Profile Image')
|
->setLabel(pht('Profile Image'))
|
||||||
->setValue(
|
->setValue(
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'img',
|
'img',
|
||||||
@@ -207,18 +207,20 @@ final class PhabricatorProjectProfileEditController
|
|||||||
))))
|
))))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormImageControl())
|
id(new AphrontFormImageControl())
|
||||||
->setLabel('Change Image')
|
->setLabel(pht('Change Image'))
|
||||||
->setName('image')
|
->setName('image')
|
||||||
->setError($e_image)
|
->setError($e_image)
|
||||||
->setCaption('Supported formats: '.implode(', ', $supported_formats)))
|
->setCaption(
|
||||||
|
pht('Supported formats:').' '.implode(', ', $supported_formats)))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->addCancelButton('/project/view/'.$project->getID().'/')
|
->addCancelButton('/project/view/'.$project->getID().'/')
|
||||||
->setValue('Save'));
|
->setValue(pht('Save')));
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
$panel->setHeader($header_name);
|
$panel->setHeader($header_name);
|
||||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
||||||
|
$panel->setNoBackground();
|
||||||
$panel->appendChild($form);
|
$panel->appendChild($form);
|
||||||
|
|
||||||
$nav = $this->buildLocalNavigation($project);
|
$nav = $this->buildLocalNavigation($project);
|
||||||
@@ -229,10 +231,24 @@ final class PhabricatorProjectProfileEditController
|
|||||||
$panel,
|
$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,
|
$nav,
|
||||||
array(
|
array(
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
|
'device' => true,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ final class PhabricatorProjectUpdateController
|
|||||||
case 'leave':
|
case 'leave':
|
||||||
$dialog = new AphrontDialogView();
|
$dialog = new AphrontDialogView();
|
||||||
$dialog->setUser($user);
|
$dialog->setUser($user);
|
||||||
$dialog->setTitle('Really leave project?');
|
$dialog->setTitle(pht('Really leave project?'));
|
||||||
$dialog->appendChild(phutil_tag('p', array(), pht(
|
$dialog->appendChild(phutil_tag('p', array(), pht(
|
||||||
'Your tremendous contributions to this project will be sorely '.
|
'Your tremendous contributions to this project will be sorely '.
|
||||||
'missed. Are you sure you want to leave?')));
|
'missed. Are you sure you want to leave?')));
|
||||||
$dialog->addCancelButton($project_uri);
|
$dialog->addCancelButton($project_uri);
|
||||||
$dialog->addSubmitButton('Leave Project');
|
$dialog->addSubmitButton(pht('Leave Project'));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ final class PhabricatorProjectEditor extends PhabricatorEditor {
|
|||||||
|
|
||||||
if ($slug == '/') {
|
if ($slug == '/') {
|
||||||
throw new PhabricatorProjectNameCollisionException(
|
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();
|
$id = $project->getID();
|
||||||
|
|||||||
@@ -44,8 +44,10 @@ final class PhabricatorSettingsPanelDiffPreferences
|
|||||||
1 => pht('Enable Filetree'),
|
1 => pht('Enable Filetree'),
|
||||||
))
|
))
|
||||||
->setCaption(
|
->setCaption(
|
||||||
pht("When looking at a revision or commit, show affected files ".
|
pht("When looking at a revision or commit, enable a sidebar ".
|
||||||
"in a sidebar.")))
|
"showing affected files. You can press %s to show or hide ".
|
||||||
|
"the sidebar.",
|
||||||
|
phutil_tag('tt', array(), 'f'))))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue(pht('Save Preferences')));
|
->setValue(pht('Save Preferences')));
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ abstract class CelerityResourceController extends PhabricatorController {
|
|||||||
throw new Exception("Only static resources may be served.");
|
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')) {
|
!PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||||
// Return a "304 Not Modified". We don't care about the value of this
|
// 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.
|
// field since we never change what resource is served by a given URI.
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ final class CelerityStaticResourceResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function renderResource(array $resource) {
|
private function renderResource(array $resource) {
|
||||||
$uri = PhabricatorEnv::getCDNURI($resource['uri']);
|
$uri = $this->getURI($resource);
|
||||||
switch ($resource['type']) {
|
switch ($resource['type']) {
|
||||||
case 'css':
|
case 'css':
|
||||||
return phutil_tag(
|
return phutil_tag(
|
||||||
@@ -148,6 +148,7 @@ final class CelerityStaticResourceResponse {
|
|||||||
$higher_priority_names = array(
|
$higher_priority_names = array(
|
||||||
'refresh-csrf',
|
'refresh-csrf',
|
||||||
'aphront-basic-tokenizer',
|
'aphront-basic-tokenizer',
|
||||||
|
'dark-console',
|
||||||
);
|
);
|
||||||
|
|
||||||
$higher_priority_behaviors = array_select_keys(
|
$higher_priority_behaviors = array_select_keys(
|
||||||
@@ -207,7 +208,7 @@ final class CelerityStaticResourceResponse {
|
|||||||
$this->resolveResources();
|
$this->resolveResources();
|
||||||
$resources = array();
|
$resources = array();
|
||||||
foreach ($this->packaged as $resource) {
|
foreach ($this->packaged as $resource) {
|
||||||
$resources[] = PhabricatorEnv::getCDNURI($resource['uri']);
|
$resources[] = $this->getURI($resource);
|
||||||
}
|
}
|
||||||
if ($resources) {
|
if ($resources) {
|
||||||
$response['javelin_resources'] = $resources;
|
$response['javelin_resources'] = $resources;
|
||||||
@@ -216,4 +217,32 @@ final class CelerityStaticResourceResponse {
|
|||||||
return $response;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ final class PhabricatorBot extends PhabricatorDaemon {
|
|||||||
|
|
||||||
private function routeMessage(PhabricatorBotMessage $message) {
|
private function routeMessage(PhabricatorBotMessage $message) {
|
||||||
$ignore = $this->getConfig('ignore');
|
$ignore = $this->getConfig('ignore');
|
||||||
if ($ignore && in_array($message->getSenderNickName(), $ignore)) {
|
if ($ignore && in_array($message->getSender(), $ignore)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ extends PhabricatorBaseProtocolAdapter {
|
|||||||
|
|
||||||
return id(new PhabricatorBotMessage())
|
return id(new PhabricatorBotMessage())
|
||||||
->setCommand('MESSAGE')
|
->setCommand('MESSAGE')
|
||||||
|
->setSender($m_obj['user_id'])
|
||||||
->setTarget($m_obj['room_id'])
|
->setTarget($m_obj['room_id'])
|
||||||
->setBody($m_obj['body']);
|
->setBody($m_obj['body']);
|
||||||
}
|
}
|
||||||
@@ -128,11 +129,23 @@ extends PhabricatorBaseProtocolAdapter {
|
|||||||
|
|
||||||
public function writeMessage(PhabricatorBotMessage $message) {
|
public function writeMessage(PhabricatorBotMessage $message) {
|
||||||
switch ($message->getCommand()) {
|
switch ($message->getCommand()) {
|
||||||
case 'MESSAGE':
|
case 'MESSAGE':
|
||||||
$this->speak(
|
$this->speak(
|
||||||
$message->getBody(),
|
$message->getBody(),
|
||||||
$message->getTarget());
|
$message->getTarget());
|
||||||
break;
|
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]);
|
unset($this->inRooms[$room_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function speak($message, $room_id) {
|
private function speak($message, $room_id, $type='TextMessage') {
|
||||||
$this->performPost(
|
$this->performPost(
|
||||||
"/room/{$room_id}/speak.json",
|
"/room/{$room_id}/speak.json",
|
||||||
array(
|
array(
|
||||||
'message' => array(
|
'message' => array(
|
||||||
'type' => 'TextMessage',
|
'type' => $type,
|
||||||
'body' => $message)));
|
'body' => $message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -161,12 +161,19 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
|||||||
|
|
||||||
if ($console) {
|
if ($console) {
|
||||||
require_celerity_resource('aphront-dark-console-css');
|
require_celerity_resource('aphront-dark-console-css');
|
||||||
|
|
||||||
|
$headers = array();
|
||||||
|
if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) {
|
||||||
|
$headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page';
|
||||||
|
}
|
||||||
|
|
||||||
Javelin::initBehavior(
|
Javelin::initBehavior(
|
||||||
'dark-console',
|
'dark-console',
|
||||||
array(
|
array(
|
||||||
'uri' => $request ? (string)$request->getRequestURI() : '?',
|
'uri' => $request ? (string)$request->getRequestURI() : '?',
|
||||||
'selected' => $user ? $user->getConsoleTab() : null,
|
'selected' => $user ? $user->getConsoleTab() : null,
|
||||||
'visible' => $user ? (int)$user->getConsoleVisible() : true,
|
'visible' => $user ? (int)$user->getConsoleVisible() : true,
|
||||||
|
'headers' => $headers,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Change this to initBehavior when there is some behavior to initialize
|
// Change this to initBehavior when there is some behavior to initialize
|
||||||
@@ -359,7 +366,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
|||||||
$classes[] = 'phabricator-chromeless-page';
|
$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
|
// Try to guess the device resolution based on UA strings to avoid a flash
|
||||||
// of incorrectly-styled content.
|
// of incorrectly-styled content.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ final class PhabricatorStartup {
|
|||||||
|
|
||||||
private static $startTime;
|
private static $startTime;
|
||||||
private static $globals = array();
|
private static $globals = array();
|
||||||
|
private static $capturingOutput;
|
||||||
|
|
||||||
|
|
||||||
/* -( Accessing Request Information )-------------------------------------- */
|
/* -( Accessing Request Information )-------------------------------------- */
|
||||||
@@ -78,6 +79,8 @@ final class PhabricatorStartup {
|
|||||||
self::verifyRewriteRules();
|
self::verifyRewriteRules();
|
||||||
|
|
||||||
self::detectPostMaxSizeTriggered();
|
self::detectPostMaxSizeTriggered();
|
||||||
|
|
||||||
|
self::beginOutputCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -149,6 +152,26 @@ final class PhabricatorStartup {
|
|||||||
phutil_load_library($phabricator_root.'/src');
|
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 )---------------------------------------------- */
|
/* -( In Case of Apocalypse )---------------------------------------------- */
|
||||||
|
|
||||||
@@ -157,6 +180,7 @@ final class PhabricatorStartup {
|
|||||||
* @task apocalypse
|
* @task apocalypse
|
||||||
*/
|
*/
|
||||||
public static function didFatal($message) {
|
public static function didFatal($message) {
|
||||||
|
self::endOutputCapture();
|
||||||
$access_log = self::getGlobal('log.access');
|
$access_log = self::getGlobal('log.access');
|
||||||
|
|
||||||
if ($access_log) {
|
if ($access_log) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ try {
|
|||||||
PhabricatorStartup::setGlobal('log.access', $access_log);
|
PhabricatorStartup::setGlobal('log.access', $access_log);
|
||||||
$access_log->setData(
|
$access_log->setData(
|
||||||
array(
|
array(
|
||||||
'R' => idx($_SERVER, 'HTTP_REFERER', '-'),
|
'R' => AphrontRequest::getHTTPHeader('Referer', '-'),
|
||||||
'r' => idx($_SERVER, 'REMOTE_ADDR', '-'),
|
'r' => idx($_SERVER, 'REMOTE_ADDR', '-'),
|
||||||
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
|
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
|
||||||
));
|
));
|
||||||
@@ -30,11 +30,12 @@ try {
|
|||||||
|
|
||||||
$response = PhabricatorSetupCheck::willProcessRequest();
|
$response = PhabricatorSetupCheck::willProcessRequest();
|
||||||
if ($response) {
|
if ($response) {
|
||||||
|
PhabricatorStartup::endOutputCapture();
|
||||||
$sink->writeResponse($response);
|
$sink->writeResponse($response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$host = $_SERVER['HTTP_HOST'];
|
$host = AphrontRequest::getHTTPHeader('Host');
|
||||||
$path = $_REQUEST['__path__'];
|
$path = $_REQUEST['__path__'];
|
||||||
|
|
||||||
switch ($host) {
|
switch ($host) {
|
||||||
@@ -102,8 +103,21 @@ try {
|
|||||||
$response = $application->willSendResponse($response, $controller);
|
$response = $application->willSendResponse($response, $controller);
|
||||||
$response->setRequest($request);
|
$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) {
|
} catch (Exception $ex) {
|
||||||
$write_guard->dispose();
|
$write_guard->dispose();
|
||||||
if ($access_log) {
|
if ($access_log) {
|
||||||
@@ -132,25 +146,7 @@ try {
|
|||||||
$access_log->write();
|
$access_log->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DarkConsoleXHProfPluginAPI::isProfilerRequested()) {
|
DarkConsoleXHProfPluginAPI::saveProfilerSample($request, $access_log);
|
||||||
$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();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage());
|
PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage());
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
.pholio-mock-image-container {
|
.pholio-mock-image-container {
|
||||||
background-color: #282828;
|
background-color: #282828;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pholio-mock-carousel {
|
.pholio-mock-carousel {
|
||||||
@@ -43,3 +44,35 @@
|
|||||||
margin: 10px 0px;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ JX.behavior('dark-console', function(config, statics) {
|
|||||||
config.key = config.key || root.getAttribute('data-console-key');
|
config.key = config.key || root.getAttribute('data-console-key');
|
||||||
add_request(config);
|
add_request(config);
|
||||||
|
|
||||||
|
|
||||||
// Do first-time setup.
|
// Do first-time setup.
|
||||||
function setup_console() {
|
function setup_console() {
|
||||||
statics.root = JX.$('darkconsole');
|
statics.root = JX.$('darkconsole');
|
||||||
@@ -42,6 +41,16 @@ JX.behavior('dark-console', function(config, statics) {
|
|||||||
|
|
||||||
install_shortcut();
|
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;
|
return statics.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,14 +139,56 @@ JX.behavior('pholio-mock-view', function(config) {
|
|||||||
endY: Math.max(startPos.y,endPos.y),
|
endY: Math.max(startPos.y,endPos.y),
|
||||||
comment: comment};
|
comment: comment};
|
||||||
|
|
||||||
|
|
||||||
inlineComment.addData(commentToAdd);
|
inlineComment.addData(commentToAdd);
|
||||||
inlineComment.send();
|
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() {
|
function load_inline_comments() {
|
||||||
var data = JX.Stratcom.getData(JX.$(config.mainID));
|
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_url = "/pholio/inline/" + data['imageID'] + "/";
|
||||||
var inline_comments = new JX.Request(inline_comments_url, function(r) {
|
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(
|
var inlineSelection = JX.$N(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
id: r[i].phid,
|
id: r[i].phid + "_selection",
|
||||||
className: 'pholio-mock-select-border',
|
className: 'pholio-mock-select-border',
|
||||||
title: r[i].content
|
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.DOM.appendContent(wrapper, inlineSelection);
|
||||||
|
|
||||||
JX.$V(r[i].x, r[i].y).setPos(inlineSelection);
|
JX.$V(r[i].x, r[i].y).setPos(inlineSelection);
|
||||||
JX.$V(r[i].width, r[i].height)
|
JX.$V(r[i].width, r[i].height).setDim(inlineSelection);
|
||||||
.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();
|
inline_comments.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
load_inline_comments();
|
load_inline_comments();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user