mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Merge branch 'master' into phutil_tag
(Sync.)
This commit is contained in:
commit
0f1bdbe147
59 changed files with 876 additions and 346 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -24,3 +24,6 @@
|
||||||
/conf/local/local.json
|
/conf/local/local.json
|
||||||
/conf/local/ENVIRONMENT
|
/conf/local/ENVIRONMENT
|
||||||
/conf/local/VERSION
|
/conf/local/VERSION
|
||||||
|
|
||||||
|
# Impact Font
|
||||||
|
/resources/font/impact.ttf
|
||||||
|
|
|
@ -798,13 +798,11 @@ return array(
|
||||||
// behalf, silencing the warning.
|
// behalf, silencing the warning.
|
||||||
'phabricator.timezone' => null,
|
'phabricator.timezone' => null,
|
||||||
|
|
||||||
// When unhandled exceptions occur, stack traces are hidden by default.
|
// Show stack traces when unhandled exceptions occur, force reloading of
|
||||||
// You can enable traces for development to make it easier to debug problems.
|
// static resources (skipping the cache), show an error callout if a page
|
||||||
'phabricator.show-stack-traces' => false,
|
// generated PHP errors, warnings, or notices, force disk reads when
|
||||||
|
// reloading. This option should not be enabled in production.
|
||||||
// Shows an error callout if a page generated PHP errors, warnings or notices.
|
'phabricator.developer-mode' => false,
|
||||||
// This makes it harder to miss problems while developing Phabricator.
|
|
||||||
'phabricator.show-error-callout' => false,
|
|
||||||
|
|
||||||
// When users write comments which have URIs, they'll be automatically linked
|
// When users write comments which have URIs, they'll be automatically linked
|
||||||
// if the protocol appears in this set. This whitelist is primarily to prevent
|
// if the protocol appears in this set. This whitelist is primarily to prevent
|
||||||
|
@ -1220,15 +1218,6 @@ return array(
|
||||||
// unlikely that you need to modify this.
|
// unlikely that you need to modify this.
|
||||||
'celerity.resource-hash' => 'd9455ea150622ee044f7931dabfa52aa',
|
'celerity.resource-hash' => 'd9455ea150622ee044f7931dabfa52aa',
|
||||||
|
|
||||||
// In a development environment, it is desirable to force static resources
|
|
||||||
// (CSS and JS) to be read from disk on every request, so that edits to them
|
|
||||||
// appear when you reload the page even if you haven't updated the resource
|
|
||||||
// maps. This setting ensures requests will be verified against the state on
|
|
||||||
// disk. Generally, you should leave this off in production (caching behavior
|
|
||||||
// and performance improve with it off) but turn it on in development. (These
|
|
||||||
// settings are the defaults.)
|
|
||||||
'celerity.force-disk-reads' => false,
|
|
||||||
|
|
||||||
// Minify static resources by removing whitespace and comments. You should
|
// Minify static resources by removing whitespace and comments. You should
|
||||||
// enable this in production, but disable it in development.
|
// enable this in production, but disable it in development.
|
||||||
'celerity.minify' => false,
|
'celerity.minify' => false,
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|
||||||
|
'phabricator.developer-mode' => true,
|
||||||
'darkconsole.enabled' => true,
|
'darkconsole.enabled' => true,
|
||||||
'celerity.force-disk-reads' => true,
|
|
||||||
'phabricator.show-stack-traces' => true,
|
|
||||||
'phabricator.show-error-callout' => true,
|
|
||||||
'celerity.minify' => false,
|
|
||||||
|
|
||||||
) + phabricator_read_config_file('default');
|
) + phabricator_read_config_file('default');
|
||||||
|
|
27
externals/javelinjs/src/core/Event.js
vendored
27
externals/javelinjs/src/core/Event.js
vendored
|
@ -133,17 +133,18 @@ JX.install('Event', {
|
||||||
var r = this.getRawEvent();
|
var r = this.getRawEvent();
|
||||||
return r.which == 3 || r.button == 2;
|
return r.which == 3 || r.button == 2;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a click event is a normal click (left mouse button, no
|
* Determine if a mouse event is a normal event (left mouse button, no
|
||||||
* modifier keys).
|
* modifier keys).
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* @task info
|
* @task info
|
||||||
*/
|
*/
|
||||||
isNormalClick : function() {
|
isNormalMouseEvent : function() {
|
||||||
if (this.getType() != 'click') {
|
var supportedEvents = ['click','mouseup','mousedown'];
|
||||||
|
|
||||||
|
if (supportedEvents.indexOf(this.getType()) == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +165,22 @@ JX.install('Event', {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a click event is a normal click (left mouse button, no
|
||||||
|
* modifier keys).
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @task info
|
||||||
|
*/
|
||||||
|
isNormalClick : function() {
|
||||||
|
if (this.getType() != 'click') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isNormalMouseEvent();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the node corresponding to the specified key in this event's node map.
|
* Get the node corresponding to the specified key in this event's node map.
|
||||||
* This is a simple helper method that makes the API for accessing nodes
|
* This is a simple helper method that makes the API for accessing nodes
|
||||||
|
|
|
@ -978,6 +978,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php',
|
'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php',
|
||||||
'PhabricatorMenuItemView' => 'view/layout/PhabricatorMenuItemView.php',
|
'PhabricatorMenuItemView' => 'view/layout/PhabricatorMenuItemView.php',
|
||||||
'PhabricatorMenuView' => 'view/layout/PhabricatorMenuView.php',
|
'PhabricatorMenuView' => 'view/layout/PhabricatorMenuView.php',
|
||||||
|
'PhabricatorMenuViewTestCase' => 'view/layout/__tests__/PhabricatorMenuViewTestCase.php',
|
||||||
'PhabricatorMercurialGraphStream' => 'applications/repository/daemon/PhabricatorMercurialGraphStream.php',
|
'PhabricatorMercurialGraphStream' => 'applications/repository/daemon/PhabricatorMercurialGraphStream.php',
|
||||||
'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php',
|
'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php',
|
||||||
'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php',
|
'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php',
|
||||||
|
@ -2395,6 +2396,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
|
'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
|
||||||
'PhabricatorMenuItemView' => 'AphrontTagView',
|
'PhabricatorMenuItemView' => 'AphrontTagView',
|
||||||
'PhabricatorMenuView' => 'AphrontTagView',
|
'PhabricatorMenuView' => 'AphrontTagView',
|
||||||
|
'PhabricatorMenuViewTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorMetaMTAController' => 'PhabricatorController',
|
'PhabricatorMetaMTAController' => 'PhabricatorController',
|
||||||
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
|
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
|
||||||
|
|
|
@ -228,6 +228,26 @@ final class AphrontRequest {
|
||||||
$more_info = "(This was a web request, {$token_info}.)";
|
$more_info = "(This was a web request, {$token_info}.)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Give a more detailed explanation of how to avoid the exception
|
||||||
|
// in developer mode.
|
||||||
|
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||||
|
$more_info = $more_info .
|
||||||
|
"To avoid this error, use phabricator_form() to construct forms. " .
|
||||||
|
"If you are already using phabricator_form(), make sure the form " .
|
||||||
|
"'action' uses a relative URI (i.e., begins with a '/'). Forms " .
|
||||||
|
"using absolute URIs do not include CSRF tokens, to prevent " .
|
||||||
|
"leaking tokens to external sites.\n\n" .
|
||||||
|
"If this page performs writes which do not require CSRF " .
|
||||||
|
"protection (usually, filling caches or logging), you can use " .
|
||||||
|
"AphrontWriteGuard::beginScopedUnguardedWrites() to temporarily " .
|
||||||
|
"bypass CSRF protection while writing. You should use this only " .
|
||||||
|
"for writes which can not be protected with normal CSRF " .
|
||||||
|
"mechanisms.\n\n" .
|
||||||
|
"Some UI elements (like PhabricatorActionListView) also have " .
|
||||||
|
"methods which will allow you to render links as forms (like " .
|
||||||
|
"setRenderAsForm(true)).";
|
||||||
|
}
|
||||||
|
|
||||||
// This should only be able to happen if you load a form, pull your
|
// This should only be able to happen if you load a form, pull your
|
||||||
// internet for 6 hours, and then reconnect and immediately submit,
|
// internet for 6 hours, and then reconnect and immediately submit,
|
||||||
// but give the user some indication of what happened since the workflow
|
// but give the user some indication of what happened since the workflow
|
||||||
|
|
|
@ -238,7 +238,7 @@ class AphrontDefaultApplicationConfiguration
|
||||||
"schema is up to date.";
|
"schema is up to date.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) {
|
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||||
$trace = $this->renderStackTrace($ex->getTrace(), $user);
|
$trace = $this->renderStackTrace($ex->getTrace(), $user);
|
||||||
} else {
|
} else {
|
||||||
$trace = null;
|
$trace = null;
|
||||||
|
|
|
@ -22,7 +22,6 @@ final class PhabricatorApplicationAuth extends PhabricatorApplication {
|
||||||
$item->setIcon('power');
|
$item->setIcon('power');
|
||||||
$item->setWorkflow(true);
|
$item->setWorkflow(true);
|
||||||
$item->setHref('/logout/');
|
$item->setHref('/logout/');
|
||||||
$item->setSortOrder(2.0);
|
|
||||||
$item->setSelected(($controller instanceof PhabricatorLogoutController));
|
$item->setSelected(($controller instanceof PhabricatorLogoutController));
|
||||||
$items[] = $item;
|
$items[] = $item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,9 +70,10 @@ final class PhabricatorOAuthFailureView extends AphrontView {
|
||||||
|
|
||||||
$provider_key = $provider->getProviderKey();
|
$provider_key = $provider->getProviderKey();
|
||||||
$diagnose = hsprintf(
|
$diagnose = hsprintf(
|
||||||
'<a href="/oauth/'.$provider_key.'/diagnose/" class="button green">'.
|
'<a href="/oauth/%s/diagnose/" class="button green">'.
|
||||||
'Diagnose %s OAuth Problems'.
|
'Diagnose %s OAuth Problems'.
|
||||||
'</a>',
|
'</a>',
|
||||||
|
$provider_key,
|
||||||
$provider_name);
|
$provider_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,9 @@ abstract class PhabricatorApplication {
|
||||||
$uninstalled =
|
$uninstalled =
|
||||||
PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications');
|
PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications');
|
||||||
|
|
||||||
if (isset($uninstalled[get_class($this)])) {
|
if (!$this->canUninstall()) {
|
||||||
|
return true;
|
||||||
|
} else if (isset($uninstalled[get_class($this)])) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
@ -227,8 +229,21 @@ abstract class PhabricatorApplication {
|
||||||
|
|
||||||
/* -( Application Management )--------------------------------------------- */
|
/* -( Application Management )--------------------------------------------- */
|
||||||
|
|
||||||
public static function getAllApplications() {
|
public static function getByClass($class_name) {
|
||||||
|
|
||||||
|
$selected = null;
|
||||||
|
$applications = PhabricatorApplication::getAllApplications();
|
||||||
|
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
if (get_class($application) == $class_name) {
|
||||||
|
$selected = $application;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllApplications() {
|
||||||
$classes = id(new PhutilSymbolLoader())
|
$classes = id(new PhutilSymbolLoader())
|
||||||
->setAncestorClass(__CLASS__)
|
->setAncestorClass(__CLASS__)
|
||||||
->setConcreteOnly(true)
|
->setConcreteOnly(true)
|
||||||
|
@ -241,6 +256,13 @@ abstract class PhabricatorApplication {
|
||||||
$apps[] = $app;
|
$apps[] = $app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reorder the applications into "application order". Notably, this ensures
|
||||||
|
// their event handlers register in application order.
|
||||||
|
$apps = msort($apps, 'getApplicationOrder');
|
||||||
|
$apps = mgroup($apps, 'getApplicationGroup');
|
||||||
|
$apps = array_select_keys($apps, self::getApplicationGroups()) + $apps;
|
||||||
|
$apps = array_mergev($apps);
|
||||||
|
|
||||||
return $apps;
|
return $apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,31 +276,24 @@ abstract class PhabricatorApplication {
|
||||||
PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications');
|
PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (empty($applications)) {
|
if (empty($applications)) {
|
||||||
$classes = id(new PhutilSymbolLoader())
|
$all_applications = self::getAllApplications();
|
||||||
->setAncestorClass(__CLASS__)
|
|
||||||
->setConcreteOnly(true)
|
|
||||||
->selectAndLoadSymbols();
|
|
||||||
|
|
||||||
$apps = array();
|
$apps = array();
|
||||||
foreach ($classes as $class) {
|
foreach ($all_applications as $app) {
|
||||||
|
$class = get_class($app);
|
||||||
if (isset($uninstalled[$class['name']])) {
|
if (isset($uninstalled[$class])) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$app = newv($class['name'], array());
|
|
||||||
|
|
||||||
if (!$app->isEnabled()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$show_beta && $app->isBeta()) {
|
if (!$app->isEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$apps[] = $app;
|
if (!$show_beta && $app->isBeta()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$apps[] = $app;
|
||||||
}
|
}
|
||||||
|
|
||||||
$applications = $apps;
|
$applications = $apps;
|
||||||
|
|
|
@ -86,48 +86,18 @@ final class PhabricatorDeveloperConfigOptions
|
||||||
"data to look at eventually). In development, it may be useful to ".
|
"data to look at eventually). In development, it may be useful to ".
|
||||||
"set it to 1 in order to debug performance problems.\n\n".
|
"set it to 1 in order to debug performance problems.\n\n".
|
||||||
"NOTE: You must install XHProf for profiling to work.")),
|
"NOTE: You must install XHProf for profiling to work.")),
|
||||||
$this->newOption('phabricator.show-stack-traces', 'bool', false)
|
$this->newOption('phabricator.developer-mode', 'bool', false)
|
||||||
->setBoolOptions(
|
->setBoolOptions(
|
||||||
array(
|
array(
|
||||||
pht('Show stack traces'),
|
pht('Enable developer mode'),
|
||||||
pht('Hide stack traces'),
|
pht('Disable developer mode'),
|
||||||
))
|
))
|
||||||
->setSummary(pht("Show stack traces when unhandled exceptions occur."))
|
->setSummary(pht("Enable verbose error reporting and disk reads."))
|
||||||
->setDescription(
|
->setDescription(
|
||||||
pht(
|
pht(
|
||||||
"When unhandled exceptions occur, stack traces are hidden by ".
|
"This option enables verbose error reporting (stack traces, ".
|
||||||
"default. You can enable traces for development to make it easier ".
|
"error callouts) and forces disk reads of static assets on ".
|
||||||
"to debug problems.")),
|
"every reload.")),
|
||||||
$this->newOption('phabricator.show-error-callout', 'bool', false)
|
|
||||||
->setBoolOptions(
|
|
||||||
array(
|
|
||||||
pht('Show error callout'),
|
|
||||||
pht('Hide error callout'),
|
|
||||||
))
|
|
||||||
->setSummary(pht("Show error callout."))
|
|
||||||
->setDescription(
|
|
||||||
pht(
|
|
||||||
"Shows an error callout if a page generated PHP errors, warnings ".
|
|
||||||
"or notices. This makes it harder to miss problems while ".
|
|
||||||
"developing Phabricator. A callout is simply a red error at the ".
|
|
||||||
"top of the page.")),
|
|
||||||
$this->newOption('celerity.force-disk-reads', 'bool', false)
|
|
||||||
->setBoolOptions(
|
|
||||||
array(
|
|
||||||
pht('Force disk reads'),
|
|
||||||
pht("Don't force disk reads"),
|
|
||||||
))
|
|
||||||
->setSummary(pht("Force Celerity to read from disk on every request."))
|
|
||||||
->setDescription(
|
|
||||||
pht(
|
|
||||||
"In a development environment, it is desirable to force static ".
|
|
||||||
"resources (CSS and JS) to be read from disk on every request, so ".
|
|
||||||
"that edits to them appear when you reload the page even if you ".
|
|
||||||
"haven't updated the resource maps. This setting ensures requests ".
|
|
||||||
"will be verified against the state on disk. Generally, you ".
|
|
||||||
"should leave this off in production (caching behavior and ".
|
|
||||||
"performance improve with it off) but turn it on in development. ".
|
|
||||||
"(These settings are the defaults.)")),
|
|
||||||
$this->newOption('celerity.minify', 'bool', false)
|
$this->newOption('celerity.minify', 'bool', false)
|
||||||
->setBoolOptions(
|
->setBoolOptions(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -10,6 +10,15 @@ final class PhabricatorConfigResponse extends AphrontHTMLResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildResponseString() {
|
public function buildResponseString() {
|
||||||
|
// Check to make sure we aren't requesting this via ajax or conduit
|
||||||
|
if (isset($_REQUEST['__ajax__']) || isset($_REQUEST['__conduit__'])) {
|
||||||
|
// We don't want to flood the console with html, just return a simple
|
||||||
|
// message for now.
|
||||||
|
return pht(
|
||||||
|
"This install has a fatal setup error, access the internet web ".
|
||||||
|
"version to view details and resolve it.");
|
||||||
|
}
|
||||||
|
|
||||||
$resources = $this->buildResources();
|
$resources = $this->buildResources();
|
||||||
|
|
||||||
$view = $this->view->render();
|
$view = $this->view->render();
|
||||||
|
|
|
@ -44,6 +44,7 @@ final class ConpherenceUpdateController extends
|
||||||
'ip' => $request->getRemoteAddr()
|
'ip' => $request->getRemoteAddr()
|
||||||
));
|
));
|
||||||
$editor = id(new ConpherenceEditor())
|
$editor = id(new ConpherenceEditor())
|
||||||
|
->setContinueOnNoEffect($request->isContinueRequest())
|
||||||
->setContentSource($content_source)
|
->setContentSource($content_source)
|
||||||
->setActor($user);
|
->setActor($user);
|
||||||
|
|
||||||
|
@ -55,28 +56,6 @@ final class ConpherenceUpdateController extends
|
||||||
$conpherence,
|
$conpherence,
|
||||||
$message
|
$message
|
||||||
);
|
);
|
||||||
$time = time();
|
|
||||||
$conpherence->openTransaction();
|
|
||||||
$xactions = $editor->applyTransactions($conpherence, $xactions);
|
|
||||||
$last_xaction = end($xactions);
|
|
||||||
$xaction_phid = $last_xaction->getPHID();
|
|
||||||
$behind = ConpherenceParticipationStatus::BEHIND;
|
|
||||||
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
|
||||||
$participants = $conpherence->getParticipants();
|
|
||||||
foreach ($participants as $phid => $participant) {
|
|
||||||
if ($phid != $user->getPHID()) {
|
|
||||||
if ($participant->getParticipationStatus() != $behind) {
|
|
||||||
$participant->setBehindTransactionPHID($xaction_phid);
|
|
||||||
}
|
|
||||||
$participant->setParticipationStatus($behind);
|
|
||||||
$participant->setDateTouched($time);
|
|
||||||
} else {
|
|
||||||
$participant->setParticipationStatus($up_to_date);
|
|
||||||
$participant->setDateTouched($time);
|
|
||||||
}
|
|
||||||
$participant->save();
|
|
||||||
}
|
|
||||||
$updated = $conpherence->saveTransaction();
|
|
||||||
break;
|
break;
|
||||||
case 'metadata':
|
case 'metadata':
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
@ -112,23 +91,25 @@ final class ConpherenceUpdateController extends
|
||||||
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
|
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
|
||||||
->setNewValue($title);
|
->setNewValue($title);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($xactions) {
|
|
||||||
$conpherence->openTransaction();
|
|
||||||
$xactions = $editor
|
|
||||||
->setContinueOnNoEffect(true)
|
|
||||||
->applyTransactions($conpherence, $xactions);
|
|
||||||
$updated = $conpherence->saveTransaction();
|
|
||||||
} else if (empty($errors)) {
|
|
||||||
$errors[] = pht(
|
|
||||||
'That was a non-update. Try cancel.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Unknown action: '.$action);
|
throw new Exception('Unknown action: '.$action);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ($xactions) {
|
||||||
|
try {
|
||||||
|
$xactions = $editor->applyTransactions($conpherence, $xactions);
|
||||||
|
$updated = true;
|
||||||
|
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||||
|
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
||||||
|
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
|
||||||
|
->setException($ex);
|
||||||
|
}
|
||||||
|
} else if (empty($errors)) {
|
||||||
|
$errors[] = pht(
|
||||||
|
'That was a non-update. Try cancel.'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($updated) {
|
if ($updated) {
|
||||||
|
|
|
@ -128,6 +128,7 @@ final class ConpherenceViewController extends
|
||||||
|
|
||||||
$form =
|
$form =
|
||||||
id(new AphrontFormView())
|
id(new AphrontFormView())
|
||||||
|
->setWorkflow(true)
|
||||||
->setAction($this->getApplicationURI('update/'.$conpherence->getID().'/'))
|
->setAction($this->getApplicationURI('update/'.$conpherence->getID().'/'))
|
||||||
->setFlexible(true)
|
->setFlexible(true)
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
|
|
|
@ -118,6 +118,27 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$editor->save();
|
$editor->save();
|
||||||
|
// fallthrough
|
||||||
|
case PhabricatorTransactions::TYPE_COMMENT:
|
||||||
|
$xaction_phid = $xaction->getPHID();
|
||||||
|
$behind = ConpherenceParticipationStatus::BEHIND;
|
||||||
|
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||||
|
$participants = $object->getParticipants();
|
||||||
|
$user = $this->getActor();
|
||||||
|
$time = time();
|
||||||
|
foreach ($participants as $phid => $participant) {
|
||||||
|
if ($phid != $user->getPHID()) {
|
||||||
|
if ($participant->getParticipationStatus() != $behind) {
|
||||||
|
$participant->setBehindTransactionPHID($xaction_phid);
|
||||||
|
}
|
||||||
|
$participant->setParticipationStatus($behind);
|
||||||
|
$participant->setDateTouched($time);
|
||||||
|
} else {
|
||||||
|
$participant->setParticipationStatus($up_to_date);
|
||||||
|
$participant->setDateTouched($time);
|
||||||
|
}
|
||||||
|
$participant->save();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||||
foreach ($xaction->getNewValue() as $participant) {
|
foreach ($xaction->getNewValue() as $participant) {
|
||||||
|
|
|
@ -363,7 +363,7 @@ final class DifferentialRevisionListController extends DifferentialController {
|
||||||
array_select_keys($handles, $params['participants']),
|
array_select_keys($handles, $params['participants']),
|
||||||
'getFullName');
|
'getFullName');
|
||||||
return id(new AphrontFormTokenizerControl())
|
return id(new AphrontFormTokenizerControl())
|
||||||
->setDatasource('/typeahead/common/allmailable/')
|
->setDatasource('/typeahead/common/accounts/')
|
||||||
->setLabel($label)
|
->setLabel($label)
|
||||||
->setName('participants')
|
->setName('participants')
|
||||||
->setValue($value);
|
->setValue($value);
|
||||||
|
|
|
@ -182,10 +182,9 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
||||||
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
||||||
$warning->appendChild(
|
$warning->appendChild(
|
||||||
pht(
|
pht(
|
||||||
'This diff is very large and affects %2$s files. Load each file '.
|
'This diff is very large and affects %s files. Load each file '.
|
||||||
'individually.',
|
'individually.',
|
||||||
$count,
|
new PhutilNumber($count)).
|
||||||
PhutilTranslator::getInstance()->formatNumber($count)).
|
|
||||||
" <strong>".
|
" <strong>".
|
||||||
phutil_tag(
|
phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
|
@ -499,7 +498,6 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
||||||
'href' => "/differential/subscribe/{$action}/{$revision_id}/",
|
'href' => "/differential/subscribe/{$action}/{$revision_id}/",
|
||||||
'name' => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
|
'name' => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
|
||||||
'instant' => true,
|
'instant' => true,
|
||||||
'sigil' => 'workflow',
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$links[] = array(
|
$links[] = array(
|
||||||
|
|
|
@ -188,6 +188,8 @@ final class DifferentialDiff extends DifferentialDAO {
|
||||||
'id' => $this->getID(),
|
'id' => $this->getID(),
|
||||||
'parent' => $this->getParentRevisionID(),
|
'parent' => $this->getParentRevisionID(),
|
||||||
'revisionID' => $this->getRevisionID(),
|
'revisionID' => $this->getRevisionID(),
|
||||||
|
'dateCreated' => $this->getDateCreated(),
|
||||||
|
'dateModified' => $this->getDateModified(),
|
||||||
'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
|
'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
|
||||||
'sourceControlPath' => $this->getSourceControlPath(),
|
'sourceControlPath' => $this->getSourceControlPath(),
|
||||||
'sourceControlSystem' => $this->getSourceControlSystem(),
|
'sourceControlSystem' => $this->getSourceControlSystem(),
|
||||||
|
|
|
@ -229,7 +229,7 @@ final class DifferentialChangesetListView extends AphrontView {
|
||||||
$template =
|
$template =
|
||||||
'<table><tr>'.
|
'<table><tr>'.
|
||||||
'<th></th><td>%s</td>'.
|
'<th></th><td>%s</td>'.
|
||||||
'<th></th><td colspan="2">%s</td>'.
|
'<th></th><td colspan="3">%s</td>'.
|
||||||
'</tr></table>';
|
'</tr></table>';
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
|
|
|
@ -52,6 +52,7 @@ final class DifferentialRevisionDetailView extends AphrontView {
|
||||||
->setName($action['name'])
|
->setName($action['name'])
|
||||||
->setHref(idx($action, 'href'))
|
->setHref(idx($action, 'href'))
|
||||||
->setWorkflow(idx($action, 'sigil') == 'workflow')
|
->setWorkflow(idx($action, 'sigil') == 'workflow')
|
||||||
|
->setRenderAsForm(!empty($action['instant']))
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setDisabled(idx($action, 'disabled', false));
|
->setDisabled(idx($action, 'disabled', false));
|
||||||
$actions->addAction($obj);
|
$actions->addAction($obj);
|
||||||
|
|
|
@ -814,9 +814,9 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
||||||
|
|
||||||
$size = strlen($data);
|
$size = strlen($data);
|
||||||
$properties->addTextContent(
|
$properties->addTextContent(
|
||||||
pht('This is a binary file. It is %2$s byte(s) in length.',
|
pht(
|
||||||
$size,
|
'This is a binary file. It is %s byte(s) in length.',
|
||||||
PhutilTranslator::getInstance()->formatNumber($size)));
|
new PhutilNumber($size)));
|
||||||
|
|
||||||
$actions = id(new PhabricatorActionListView())
|
$actions = id(new PhabricatorActionListView())
|
||||||
->setUser($this->getRequest()->getUser())
|
->setUser($this->getRequest()->getUser())
|
||||||
|
|
|
@ -116,10 +116,18 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
$content[] = $this->buildAuditTable($commit, $audit_requests);
|
$content[] = $this->buildAuditTable($commit, $audit_requests);
|
||||||
$content[] = $this->buildComments($commit);
|
$content[] = $this->buildComments($commit);
|
||||||
|
|
||||||
|
$hard_limit = 1000;
|
||||||
|
|
||||||
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
|
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
|
||||||
$drequest);
|
$drequest);
|
||||||
|
$change_query->setLimit($hard_limit + 1);
|
||||||
$changes = $change_query->loadChanges();
|
$changes = $change_query->loadChanges();
|
||||||
|
|
||||||
|
$was_limited = (count($changes) > $hard_limit);
|
||||||
|
if ($was_limited) {
|
||||||
|
$changes = array_slice($changes, 0, $hard_limit);
|
||||||
|
}
|
||||||
|
|
||||||
$content[] = $this->buildMergesTable($commit);
|
$content[] = $this->buildMergesTable($commit);
|
||||||
|
|
||||||
$owners_paths = array();
|
$owners_paths = array();
|
||||||
|
@ -151,7 +159,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
'r'.$callsign.$commit->getCommitIdentifier());
|
'r'.$callsign.$commit->getCommitIdentifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
$pane_id = null;
|
|
||||||
if ($bad_commit) {
|
if ($bad_commit) {
|
||||||
$error_panel = new AphrontErrorView();
|
$error_panel = new AphrontErrorView();
|
||||||
$error_panel->setTitle('Bad Commit');
|
$error_panel->setTitle('Bad Commit');
|
||||||
|
@ -175,11 +182,20 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
"This commit hasn't been fully parsed yet (or doesn't affect any ".
|
"This commit hasn't been fully parsed yet (or doesn't affect any ".
|
||||||
"paths).");
|
"paths).");
|
||||||
$content[] = $no_changes;
|
$content[] = $no_changes;
|
||||||
|
} else if ($was_limited) {
|
||||||
|
$huge_commit = new AphrontErrorView();
|
||||||
|
$huge_commit->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
||||||
|
$huge_commit->setTitle(pht('Enormous Commit'));
|
||||||
|
$huge_commit->appendChild(
|
||||||
|
pht(
|
||||||
|
'This commit is enormous, and affects more than %d files. '.
|
||||||
|
'Changes are not shown.',
|
||||||
|
$hard_limit));
|
||||||
|
$content[] = $huge_commit;
|
||||||
} else {
|
} else {
|
||||||
$change_panel = new AphrontPanelView();
|
$change_panel = new AphrontPanelView();
|
||||||
$change_panel->setHeader("Changes (".number_format($count).")");
|
$change_panel->setHeader("Changes (".number_format($count).")");
|
||||||
$change_panel->setID('toc');
|
$change_panel->setID('toc');
|
||||||
|
|
||||||
if ($count > self::CHANGES_LIMIT) {
|
if ($count > self::CHANGES_LIMIT) {
|
||||||
$show_all_button = phutil_tag(
|
$show_all_button = phutil_tag(
|
||||||
'a',
|
'a',
|
||||||
|
@ -296,28 +312,11 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
}
|
}
|
||||||
$change_table->setRenderingReferences($change_references);
|
$change_table->setRenderingReferences($change_references);
|
||||||
|
|
||||||
// TODO: This is pretty awkward, unify the CSS between Diffusion and
|
$content[] = $change_list->render();
|
||||||
// Differential better.
|
|
||||||
require_celerity_resource('differential-core-view-css');
|
|
||||||
$pane_id = celerity_generate_unique_node_id();
|
|
||||||
$add_comment_view = $this->renderAddCommentPanel($commit,
|
|
||||||
$audit_requests,
|
|
||||||
$pane_id);
|
|
||||||
$main_pane = phutil_render_tag(
|
|
||||||
'div',
|
|
||||||
array(
|
|
||||||
'id' => $pane_id
|
|
||||||
),
|
|
||||||
$change_list->render().
|
|
||||||
id(new PhabricatorAnchorView())
|
|
||||||
->setAnchorName('comment')
|
|
||||||
->setNavigationMarker(true)
|
|
||||||
->render().
|
|
||||||
$add_comment_view);
|
|
||||||
|
|
||||||
$content[] = $main_pane;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$content[] = $this->renderAddCommentPanel($commit, $audit_requests);
|
||||||
|
|
||||||
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
|
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
|
||||||
$short_name = DiffusionView::nameCommit(
|
$short_name = DiffusionView::nameCommit(
|
||||||
$repository,
|
$repository,
|
||||||
|
@ -577,13 +576,13 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
|
|
||||||
private function renderAddCommentPanel(
|
private function renderAddCommentPanel(
|
||||||
PhabricatorRepositoryCommit $commit,
|
PhabricatorRepositoryCommit $commit,
|
||||||
array $audit_requests,
|
array $audit_requests) {
|
||||||
$pane_id = null) {
|
|
||||||
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
|
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
|
||||||
$user = $this->getRequest()->getUser();
|
$user = $this->getRequest()->getUser();
|
||||||
|
|
||||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||||
|
|
||||||
|
$pane_id = celerity_generate_unique_node_id();
|
||||||
Javelin::initBehavior(
|
Javelin::initBehavior(
|
||||||
'differential-keyboard-navigation',
|
'differential-keyboard-navigation',
|
||||||
array(
|
array(
|
||||||
|
@ -693,14 +692,26 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
</div>
|
</div>
|
||||||
</div>';
|
</div>';
|
||||||
|
|
||||||
return
|
// TODO: This is pretty awkward, unify the CSS between Diffusion and
|
||||||
|
// Differential better.
|
||||||
|
require_celerity_resource('differential-core-view-css');
|
||||||
|
|
||||||
|
return phutil_render_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'id' => $pane_id,
|
||||||
|
),
|
||||||
phutil_render_tag(
|
phutil_render_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'differential-add-comment-panel',
|
'class' => 'differential-add-comment-panel',
|
||||||
),
|
),
|
||||||
|
id(new PhabricatorAnchorView())
|
||||||
|
->setAnchorName('comment')
|
||||||
|
->setNavigationMarker(true)
|
||||||
|
->render().
|
||||||
$panel->render().
|
$panel->render().
|
||||||
$preview_panel);
|
$preview_panel));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,6 +3,16 @@
|
||||||
final class DiffusionPathChangeQuery {
|
final class DiffusionPathChangeQuery {
|
||||||
|
|
||||||
private $request;
|
private $request;
|
||||||
|
private $limit;
|
||||||
|
|
||||||
|
public function setLimit($limit) {
|
||||||
|
$this->limit = $limit;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLimit() {
|
||||||
|
return $this->limit;
|
||||||
|
}
|
||||||
|
|
||||||
final private function __construct() {
|
final private function __construct() {
|
||||||
// <private>
|
// <private>
|
||||||
|
@ -31,20 +41,36 @@ final class DiffusionPathChangeQuery {
|
||||||
|
|
||||||
$commit = $drequest->loadCommit();
|
$commit = $drequest->loadCommit();
|
||||||
|
|
||||||
|
$conn_r = $repository->establishConnection('r');
|
||||||
|
|
||||||
|
$limit = '';
|
||||||
|
if ($this->limit) {
|
||||||
|
$limit = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'LIMIT %d',
|
||||||
|
$this->limit + 1);
|
||||||
|
}
|
||||||
|
|
||||||
$raw_changes = queryfx_all(
|
$raw_changes = queryfx_all(
|
||||||
$repository->establishConnection('r'),
|
$conn_r,
|
||||||
'SELECT c.*, p.path pathName, t.path targetPathName,
|
'SELECT c.*, p.path pathName, t.path targetPathName,
|
||||||
i.commitIdentifier targetCommitIdentifier
|
i.commitIdentifier targetCommitIdentifier
|
||||||
FROM %T c
|
FROM %T c
|
||||||
LEFT JOIN %T p ON c.pathID = p.id
|
LEFT JOIN %T p ON c.pathID = p.id
|
||||||
LEFT JOIN %T t ON c.targetPathID = t.id
|
LEFT JOIN %T t ON c.targetPathID = t.id
|
||||||
LEFT JOIN %T i ON c.targetCommitID = i.id
|
LEFT JOIN %T i ON c.targetCommitID = i.id
|
||||||
WHERE c.commitID = %d AND isDirect = 1',
|
WHERE c.commitID = %d AND isDirect = 1 %Q',
|
||||||
PhabricatorRepository::TABLE_PATHCHANGE,
|
PhabricatorRepository::TABLE_PATHCHANGE,
|
||||||
PhabricatorRepository::TABLE_PATH,
|
PhabricatorRepository::TABLE_PATH,
|
||||||
PhabricatorRepository::TABLE_PATH,
|
PhabricatorRepository::TABLE_PATH,
|
||||||
$commit->getTableName(),
|
$commit->getTableName(),
|
||||||
$commit->getID());
|
$commit->getID(),
|
||||||
|
$limit);
|
||||||
|
|
||||||
|
$limited = $this->limit && (count($raw_changes) > $this->limit);
|
||||||
|
if ($limited) {
|
||||||
|
$raw_changes = array_slice($raw_changes, 0, $this->limit);
|
||||||
|
}
|
||||||
|
|
||||||
$changes = array();
|
$changes = array();
|
||||||
|
|
||||||
|
@ -68,20 +94,22 @@ final class DiffusionPathChangeQuery {
|
||||||
$changes[$id] = $change;
|
$changes[$id] = $change;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduce the away paths by examining all the changes.
|
// Deduce the away paths by examining all the changes, if we loaded them
|
||||||
|
// all.
|
||||||
|
|
||||||
$away = array();
|
if (!$limited) {
|
||||||
foreach ($changes as $change) {
|
$away = array();
|
||||||
if ($change->getTargetPath()) {
|
foreach ($changes as $change) {
|
||||||
$away[$change->getTargetPath()][] = $change->getPath();
|
if ($change->getTargetPath()) {
|
||||||
|
$away[$change->getTargetPath()][] = $change->getPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($changes as $change) {
|
||||||
|
if (isset($away[$change->getPath()])) {
|
||||||
|
$change->setAwayPaths($away[$change->getPath()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($changes as $change) {
|
|
||||||
if (isset($away[$change->getPath()])) {
|
|
||||||
$change->setAwayPaths($away[$change->getPath()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $changes;
|
return $changes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,22 +95,21 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
||||||
|
|
||||||
$conn = $drequest->getRepository()->establishConnection('r');
|
$conn = $drequest->getRepository()->establishConnection('r');
|
||||||
|
|
||||||
$where = '';
|
$path = '/'.$drequest->getPath();
|
||||||
|
$where = (substr($path, -1) == '/'
|
||||||
|
? qsprintf($conn, 'AND path LIKE %>', $path)
|
||||||
|
: qsprintf($conn, 'AND path = %s', $path));
|
||||||
|
|
||||||
if ($drequest->getLint()) {
|
if ($drequest->getLint()) {
|
||||||
$where = qsprintf(
|
$where .= qsprintf($conn, ' AND code = %s', $drequest->getLint());
|
||||||
$conn,
|
|
||||||
'AND code = %s',
|
|
||||||
$drequest->getLint());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$like = (substr($drequest->getPath(), -1) == '/' ? 'LIKE %>' : '= %s');
|
|
||||||
return head(queryfx_one(
|
return head(queryfx_one(
|
||||||
$conn,
|
$conn,
|
||||||
'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q AND path '.$like,
|
'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q',
|
||||||
PhabricatorRepository::TABLE_LINTMESSAGE,
|
PhabricatorRepository::TABLE_LINTMESSAGE,
|
||||||
$branch->getID(),
|
$branch->getID(),
|
||||||
$where,
|
$where));
|
||||||
'/'.$drequest->getPath()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
|
|
|
@ -65,8 +65,6 @@ abstract class PhabricatorDirectoryController extends PhabricatorController {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tile_group = msort($tile_group, 'getApplicationOrder');
|
|
||||||
|
|
||||||
$is_small_tiles = ($tile_display == PhabricatorApplication::TILE_SHOW) ||
|
$is_small_tiles = ($tile_display == PhabricatorApplication::TILE_SHOW) ||
|
||||||
($tile_display == PhabricatorApplication::TILE_HIDE);
|
($tile_display == PhabricatorApplication::TILE_HIDE);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ final class PhabricatorApplicationDiviner extends PhabricatorApplication {
|
||||||
$item->setName(pht('%s Help', $application->getName()));
|
$item->setName(pht('%s Help', $application->getName()));
|
||||||
$item->setIcon('help');
|
$item->setIcon('help');
|
||||||
$item->setHref($application->getHelpURI());
|
$item->setHref($application->getHelpURI());
|
||||||
$item->setSortOrder(0.1);
|
|
||||||
$items[] = $item;
|
$items[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ final class PhabricatorImageTransformer {
|
||||||
return $dst;
|
return $dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generatePreview(PhabricatorFile $file, $size) {
|
public static function getPreviewDimensions(PhabricatorFile $file, $size) {
|
||||||
$data = $file->loadFileData();
|
$data = $file->loadFileData();
|
||||||
$src = imagecreatefromstring($data);
|
$src = imagecreatefromstring($data);
|
||||||
|
|
||||||
|
@ -128,13 +128,35 @@ final class PhabricatorImageTransformer {
|
||||||
$dx = max($size / 4, $scale * $x);
|
$dx = max($size / 4, $scale * $x);
|
||||||
$dy = max($size / 4, $scale * $y);
|
$dy = max($size / 4, $scale * $y);
|
||||||
|
|
||||||
|
$sdx = $scale * $x;
|
||||||
|
$sdy = $scale * $y;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'x' => $x,
|
||||||
|
'y' => $y,
|
||||||
|
'dx' => $dx,
|
||||||
|
'dy' => $dy,
|
||||||
|
'sdx' => $sdx,
|
||||||
|
'sdy' => $sdy
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generatePreview(PhabricatorFile $file, $size) {
|
||||||
|
$data = $file->loadFileData();
|
||||||
|
$src = imagecreatefromstring($data);
|
||||||
|
|
||||||
|
$dimensions = self::getPreviewDimensions($file, $size);
|
||||||
|
$x = $dimensions['x'];
|
||||||
|
$y = $dimensions['y'];
|
||||||
|
$dx = $dimensions['dx'];
|
||||||
|
$dy = $dimensions['dy'];
|
||||||
|
$sdx = $dimensions['sdx'];
|
||||||
|
$sdy = $dimensions['sdy'];
|
||||||
|
|
||||||
$dst = imagecreatetruecolor($dx, $dy);
|
$dst = imagecreatetruecolor($dx, $dy);
|
||||||
imagesavealpha($dst, true);
|
imagesavealpha($dst, true);
|
||||||
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
|
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
|
||||||
|
|
||||||
$sdx = $scale * $x;
|
|
||||||
$sdy = $scale * $y;
|
|
||||||
|
|
||||||
imagecopyresampled(
|
imagecopyresampled(
|
||||||
$dst,
|
$dst,
|
||||||
$src,
|
$src,
|
||||||
|
@ -153,10 +175,14 @@ final class PhabricatorImageTransformer {
|
||||||
$data = $file->loadFileData();
|
$data = $file->loadFileData();
|
||||||
$img = imagecreatefromstring($data);
|
$img = imagecreatefromstring($data);
|
||||||
$phabricator_root = dirname(phutil_get_library_root('phabricator'));
|
$phabricator_root = dirname(phutil_get_library_root('phabricator'));
|
||||||
$font_path = $phabricator_root.'/resources/font/tuffy.ttf';
|
$font_root = $phabricator_root.'/resources/font/';
|
||||||
$white = imagecolorallocate($img, 255, 255, 255);
|
$font_path = $font_root.'tuffy.ttf';
|
||||||
$black = imagecolorallocate($img, 0, 0, 0);
|
if (Filesystem::pathExists($font_root.'impact.ttf')) {
|
||||||
$border_width = 3;
|
$font_path = $font_root.'impact.ttf';
|
||||||
|
}
|
||||||
|
$text_color = imagecolorallocate($img, 255, 255, 255);
|
||||||
|
$border_color = imagecolorallocatealpha($img, 0, 0, 0, 110);
|
||||||
|
$border_width = 4;
|
||||||
$font_max = 200;
|
$font_max = 200;
|
||||||
$font_min = 5;
|
$font_min = 5;
|
||||||
for ($i = $font_max; $i > $font_min; $i--) {
|
for ($i = $font_max; $i > $font_min; $i--) {
|
||||||
|
@ -172,8 +198,8 @@ final class PhabricatorImageTransformer {
|
||||||
$i,
|
$i,
|
||||||
$x,
|
$x,
|
||||||
$y,
|
$y,
|
||||||
$white,
|
$text_color,
|
||||||
$black,
|
$border_color,
|
||||||
$border_width,
|
$border_width,
|
||||||
$font_path,
|
$font_path,
|
||||||
$upper_text);
|
$upper_text);
|
||||||
|
@ -191,8 +217,8 @@ final class PhabricatorImageTransformer {
|
||||||
$i,
|
$i,
|
||||||
$x,
|
$x,
|
||||||
$y,
|
$y,
|
||||||
$white,
|
$text_color,
|
||||||
$black,
|
$border_color,
|
||||||
$border_width,
|
$border_width,
|
||||||
$font_path,
|
$font_path,
|
||||||
$lower_text);
|
$lower_text);
|
||||||
|
|
|
@ -39,10 +39,10 @@ final class PhabricatorMacroEditController
|
||||||
if (!strlen($macro->getName())) {
|
if (!strlen($macro->getName())) {
|
||||||
$errors[] = pht('Macro name is required.');
|
$errors[] = pht('Macro name is required.');
|
||||||
$e_name = pht('Required');
|
$e_name = pht('Required');
|
||||||
} else if (!preg_match('/^[a-z0-9_-]{3,}$/', $macro->getName())) {
|
} else if (!preg_match('/^[a-z0-9:_-]{3,}$/', $macro->getName())) {
|
||||||
$errors[] = pht('Macro must be at least three characters long and '.
|
$errors[] = pht(
|
||||||
'contain only lowercase letters, digits, hyphen and '.
|
'Macro must be at least three characters long and contain only '.
|
||||||
'underscore.');
|
'lowercase letters, digits, hyphens, colons and underscores.');
|
||||||
$e_name = pht('Invalid');
|
$e_name = pht('Invalid');
|
||||||
} else {
|
} else {
|
||||||
$e_name = null;
|
$e_name = null;
|
||||||
|
|
|
@ -22,8 +22,8 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication {
|
||||||
return "\xE0\xBC\x84";
|
return "\xE0\xBC\x84";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldAppearInLaunchView() {
|
public function getApplicationGroup() {
|
||||||
return false;
|
return self::GROUP_ADMIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRoutes() {
|
public function getRoutes() {
|
||||||
|
|
|
@ -13,15 +13,7 @@ final class PhabricatorApplicationDetailViewController
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$user = $request->getUser();
|
$user = $request->getUser();
|
||||||
|
|
||||||
$selected = null;
|
$selected = PhabricatorApplication::getByClass($this->application);
|
||||||
$applications = PhabricatorApplication::getAllApplications();
|
|
||||||
|
|
||||||
foreach ($applications as $application) {
|
|
||||||
if (get_class($application) == $this->application) {
|
|
||||||
$selected = $application;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$selected) {
|
if (!$selected) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
|
@ -35,36 +27,53 @@ final class PhabricatorApplicationDetailViewController
|
||||||
->setName(pht('Applications'))
|
->setName(pht('Applications'))
|
||||||
->setHref($this->getApplicationURI()));
|
->setHref($this->getApplicationURI()));
|
||||||
|
|
||||||
$properties = $this->buildPropertyView($selected);
|
$header = id(new PhabricatorHeaderView())
|
||||||
$actions = $this->buildActionView($user, $selected);
|
->setHeader($title);
|
||||||
|
|
||||||
return $this->buildApplicationPage(
|
$status_tag = id(new PhabricatorTagView())
|
||||||
array(
|
->setType(PhabricatorTagView::TYPE_STATE);
|
||||||
$crumbs,
|
|
||||||
id(new PhabricatorHeaderView())->setHeader($title),
|
if ($selected->isInstalled()) {
|
||||||
$actions,
|
$status_tag->setName(pht('Installed'));
|
||||||
$properties,
|
$status_tag->setBackgroundColor(PhabricatorTagView::COLOR_GREEN);
|
||||||
),
|
|
||||||
array(
|
} else {
|
||||||
'title' => $title,
|
$status_tag->setName(pht('Uninstalled'));
|
||||||
'device' => true,
|
$status_tag->setBackgroundColor(PhabricatorTagView::COLOR_RED);
|
||||||
));
|
}
|
||||||
|
|
||||||
|
if ($selected->isBeta()) {
|
||||||
|
$beta_tag = id(new PhabricatorTagView())
|
||||||
|
->setType(PhabricatorTagView::TYPE_STATE)
|
||||||
|
->setName(pht('Beta'))
|
||||||
|
->setBackgroundColor(PhabricatorTagView::COLOR_GREY);
|
||||||
|
$header->addTag($beta_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$header->addTag($status_tag);
|
||||||
|
|
||||||
|
$properties = $this->buildPropertyView($selected);
|
||||||
|
$actions = $this->buildActionView($user, $selected);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$header,
|
||||||
|
$actions,
|
||||||
|
$properties,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
'device' => true,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildPropertyView(PhabricatorApplication $selected) {
|
private function buildPropertyView(PhabricatorApplication $selected) {
|
||||||
$properties = new PhabricatorPropertyListView();
|
$properties = id(new PhabricatorPropertyListView())
|
||||||
|
->addProperty(
|
||||||
if ($selected->isInstalled()) {
|
pht('Description'), $selected->getShortDescription()
|
||||||
$properties->addProperty(
|
);
|
||||||
pht('Status'), pht('Installed'));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$properties->addProperty(
|
|
||||||
pht('Status'), pht('Uninstalled'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$properties->addProperty(
|
|
||||||
pht('Description'), $selected->getShortDescription());
|
|
||||||
|
|
||||||
return $properties;
|
return $properties;
|
||||||
}
|
}
|
||||||
|
@ -72,30 +81,41 @@ final class PhabricatorApplicationDetailViewController
|
||||||
private function buildActionView(
|
private function buildActionView(
|
||||||
PhabricatorUser $user, PhabricatorApplication $selected) {
|
PhabricatorUser $user, PhabricatorApplication $selected) {
|
||||||
|
|
||||||
|
$view = id(new PhabricatorActionListView())
|
||||||
|
->setUser($user);
|
||||||
|
|
||||||
if ($selected->canUninstall()) {
|
if ($selected->canUninstall()) {
|
||||||
if ($selected->isInstalled()) {
|
if ($selected->isInstalled()) {
|
||||||
|
$view->addAction(
|
||||||
return id(new PhabricatorActionListView())
|
id(new PhabricatorActionView())
|
||||||
->setUser($user)
|
->setName(pht('Uninstall'))
|
||||||
->addAction(
|
->setIcon('delete')
|
||||||
id(new PhabricatorActionView())
|
->setWorkflow(true)
|
||||||
->setName(pht('Uninstall'))
|
->setHref(
|
||||||
->setIcon('delete')
|
|
||||||
->setHref(
|
|
||||||
$this->getApplicationURI(get_class($selected).'/uninstall/'))
|
$this->getApplicationURI(get_class($selected).'/uninstall/'))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return id(new PhabricatorActionListView())
|
$view->addAction(
|
||||||
->setUser($user)
|
id(new PhabricatorActionView())
|
||||||
->addAction(
|
->setName(pht('Install'))
|
||||||
id(new PhabricatorActionView())
|
->setIcon('new')
|
||||||
->setName(pht('Install'))
|
->setWorkflow(true)
|
||||||
->setIcon('new')
|
->setHref(
|
||||||
->setHref(
|
$this->getApplicationURI(get_class($selected).'/install/'))
|
||||||
$this->getApplicationURI(get_class($selected).'/install/'))
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('Uninstall'))
|
||||||
|
->setIcon('delete')
|
||||||
|
->setWorkflow(true)
|
||||||
|
->setDisabled(true)
|
||||||
|
->setHref(
|
||||||
|
$this->getApplicationURI(get_class($selected).'/uninstall/'))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,30 +14,51 @@ final class PhabricatorApplicationUninstallController
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$user = $request->getUser();
|
$user = $request->getUser();
|
||||||
$app_name = substr($this->application, strlen('PhabricatorApplication'));
|
|
||||||
|
$selected = PhabricatorApplication::getByClass($this->application);
|
||||||
|
|
||||||
|
if (!$selected) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$view_uri = $this->getApplicationURI('view/'.$this->application);
|
||||||
|
|
||||||
if ($request->isDialogFormPost()) {
|
if ($request->isDialogFormPost()) {
|
||||||
$this->manageApplication();
|
$this->manageApplication();
|
||||||
return id(new AphrontRedirectResponse())->setURI('/applications/');
|
return id(new AphrontRedirectResponse())->setURI($view_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$dialog = id(new AphrontDialogView())
|
||||||
|
->setUser($user)
|
||||||
|
->addCancelButton($view_uri);
|
||||||
|
|
||||||
if ($this->action == 'install') {
|
if ($this->action == 'install') {
|
||||||
|
if ($selected->canUninstall()) {
|
||||||
|
$dialog->setTitle('Confirmation')
|
||||||
|
->appendChild(
|
||||||
|
'Install '. $selected->getName(). ' application ?'
|
||||||
|
)
|
||||||
|
->addSubmitButton('Install');
|
||||||
|
|
||||||
$dialog = id(new AphrontDialogView())
|
} else {
|
||||||
->setUser($user)
|
$dialog->setTitle('Information')
|
||||||
->setTitle('Confirmation')
|
->appendChild('You cannot install a installed application.');
|
||||||
->appendChild('Install '. $app_name. ' application ?')
|
}
|
||||||
->addSubmitButton('Install')
|
|
||||||
->addCancelButton('/applications/view/'.$this->application);
|
|
||||||
} else {
|
} else {
|
||||||
$dialog = id(new AphrontDialogView())
|
if ($selected->canUninstall()) {
|
||||||
->setUser($user)
|
$dialog->setTitle('Confirmation')
|
||||||
->setTitle('Confirmation')
|
->appendChild(
|
||||||
->appendChild('Really Uninstall '. $app_name. ' application ?')
|
'Really Uninstall '. $selected->getName(). ' application ?'
|
||||||
->addSubmitButton('Uninstall')
|
)
|
||||||
->addCancelButton('/applications/view/'.$this->application);
|
->addSubmitButton('Uninstall');
|
||||||
|
} else {
|
||||||
|
$dialog->setTitle('Information')
|
||||||
|
->appendChild(
|
||||||
|
'This application cannot be uninstalled,
|
||||||
|
because it is required for Phabricator to work.'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,12 +47,24 @@ final class PhabricatorApplicationsListController
|
||||||
private function buildInstalledApplicationsList(array $applications) {
|
private function buildInstalledApplicationsList(array $applications) {
|
||||||
$list = new PhabricatorObjectItemListView();
|
$list = new PhabricatorObjectItemListView();
|
||||||
|
|
||||||
|
$applications = msort($applications, 'getName');
|
||||||
|
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
$item = id(new PhabricatorObjectItemView())
|
$item = id(new PhabricatorObjectItemView())
|
||||||
->setHeader($application->getName())
|
->setHeader($application->getName())
|
||||||
->setHref('/applications/view/'.get_class($application).'/')
|
->setHref('/applications/view/'.get_class($application).'/')
|
||||||
->addAttribute($application->getShortDescription());
|
->addAttribute($application->getShortDescription());
|
||||||
|
|
||||||
|
if (!$application->isInstalled()) {
|
||||||
|
$item->addIcon('delete', pht('Uninstalled'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($application->isBeta()) {
|
||||||
|
$item->addIcon('lint-warning', pht('Beta'));
|
||||||
|
}
|
||||||
|
|
||||||
$list->addItem($item);
|
$list->addItem($item);
|
||||||
|
|
||||||
}
|
}
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,9 +95,8 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
|
||||||
|
|
||||||
$line_count = count(explode("\n", $paste->getContent()));
|
$line_count = count(explode("\n", $paste->getContent()));
|
||||||
$line_count = pht(
|
$line_count = pht(
|
||||||
'%2$s Line(s)',
|
'%s Line(s)',
|
||||||
$line_count,
|
new PhutilNumber($line_count));
|
||||||
PhutilTranslator::getInstance()->formatNumber($line_count));
|
|
||||||
|
|
||||||
$item = id(new PhabricatorObjectItemView())
|
$item = id(new PhabricatorObjectItemView())
|
||||||
->setHeader($paste->getFullName())
|
->setHeader($paste->getFullName())
|
||||||
|
|
|
@ -58,7 +58,6 @@ final class PhabricatorApplicationPeople extends PhabricatorApplication {
|
||||||
$item = new PhabricatorMenuItemView();
|
$item = new PhabricatorMenuItemView();
|
||||||
$item->setName($user->getUsername());
|
$item->setName($user->getUsername());
|
||||||
$item->setHref('/p/'.$user->getUsername().'/');
|
$item->setHref('/p/'.$user->getUsername().'/');
|
||||||
$item->setSortOrder(0.0);
|
|
||||||
$item->addClass('phabricator-core-menu-item-profile');
|
$item->addClass('phabricator-core-menu-item-profile');
|
||||||
|
|
||||||
$classes = array(
|
$classes = array(
|
||||||
|
|
|
@ -13,19 +13,37 @@ final class PholioMockImagesView extends AphrontView {
|
||||||
throw new Exception("Call setMock() before render()!");
|
throw new Exception("Call setMock() before render()!");
|
||||||
}
|
}
|
||||||
|
|
||||||
$mockview = array();
|
|
||||||
|
|
||||||
$file = head($this->mock->getImages())->getFile();
|
|
||||||
|
|
||||||
$main_image_id = celerity_generate_unique_node_id();
|
$main_image_id = celerity_generate_unique_node_id();
|
||||||
|
require_celerity_resource('javelin-behavior-pholio-mock-view');
|
||||||
|
$config = array('mainID' => $main_image_id);
|
||||||
|
Javelin::initBehavior('pholio-mock-view', $config);
|
||||||
|
|
||||||
$main_image_tag = phutil_tag(
|
$mockview = "";
|
||||||
|
|
||||||
|
$main_image = head($this->mock->getImages());
|
||||||
|
|
||||||
|
$main_image_tag = javelin_tag(
|
||||||
'img',
|
'img',
|
||||||
array(
|
array(
|
||||||
'src' => $file->getBestURI(),
|
|
||||||
'class' => 'pholio-mock-image',
|
|
||||||
'id' => $main_image_id,
|
'id' => $main_image_id,
|
||||||
));
|
'src' => $main_image->getFile()->getBestURI(),
|
||||||
|
'sigil' => 'mock-image',
|
||||||
|
'class' => 'pholio-mock-image',
|
||||||
|
'meta' => array(
|
||||||
|
'fullSizeURI' => $main_image->getFile()->getBestURI(),
|
||||||
|
'imageID' => $main_image->getID(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$main_image_tag = javelin_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'id' => 'mock-wrapper',
|
||||||
|
'sigil' => 'mock-wrapper',
|
||||||
|
'class' => 'pholio-mock-wrapper'
|
||||||
|
),
|
||||||
|
$main_image_tag
|
||||||
|
);
|
||||||
|
|
||||||
$mockview[] = phutil_tag(
|
$mockview[] = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
|
@ -35,10 +53,6 @@ final class PholioMockImagesView extends AphrontView {
|
||||||
$main_image_tag);
|
$main_image_tag);
|
||||||
|
|
||||||
if (count($this->mock->getImages()) > 1) {
|
if (count($this->mock->getImages()) > 1) {
|
||||||
require_celerity_resource('javelin-behavior-pholio-mock-view');
|
|
||||||
$config = array('mainID' => $main_image_id);
|
|
||||||
Javelin::initBehavior('pholio-mock-view', $config);
|
|
||||||
|
|
||||||
$thumbnails = array();
|
$thumbnails = array();
|
||||||
foreach ($this->mock->getImages() as $image) {
|
foreach ($this->mock->getImages() as $image) {
|
||||||
$thumbfile = $image->getFile();
|
$thumbfile = $image->getFile();
|
||||||
|
@ -51,7 +65,7 @@ final class PholioMockImagesView extends AphrontView {
|
||||||
'class' => 'pholio-mock-carousel-thumbnail',
|
'class' => 'pholio-mock-carousel-thumbnail',
|
||||||
'meta' => array(
|
'meta' => array(
|
||||||
'fullSizeURI' => $thumbfile->getBestURI(),
|
'fullSizeURI' => $thumbfile->getBestURI(),
|
||||||
'imageID' => $image->getID(),
|
'imageID' => $image->getID()
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
$thumbnails[] = $tag;
|
$thumbnails[] = $tag;
|
||||||
|
@ -67,5 +81,4 @@ final class PholioMockImagesView extends AphrontView {
|
||||||
|
|
||||||
return $this->renderHTMLView($mockview);
|
return $this->renderHTMLView($mockview);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,11 +266,14 @@ final class PhabricatorRepositorySvnCommitChangeParserWorker
|
||||||
if (empty($raw_paths[$full_from]) &&
|
if (empty($raw_paths[$full_from]) &&
|
||||||
empty($effects[$full_from])) {
|
empty($effects[$full_from])) {
|
||||||
if ($other_type == DifferentialChangeType::TYPE_COPY_AWAY) {
|
if ($other_type == DifferentialChangeType::TYPE_COPY_AWAY) {
|
||||||
|
// Add an indirect effect for the copied file, if we
|
||||||
|
// don't already have an entry for it (e.g., a separate
|
||||||
|
// change).
|
||||||
$effects[$full_from] = array(
|
$effects[$full_from] = array(
|
||||||
'rawPath' => $full_from,
|
'rawPath' => $full_from,
|
||||||
'rawTargetPath' => null,
|
'rawTargetPath' => null,
|
||||||
'rawTargetCommit' => null,
|
'rawTargetCommit' => null,
|
||||||
'rawDirect' => true,
|
'rawDirect' => false,
|
||||||
|
|
||||||
'changeType' => $other_type,
|
'changeType' => $other_type,
|
||||||
'fileType' => $from_file_type,
|
'fileType' => $from_file_type,
|
||||||
|
|
|
@ -161,7 +161,8 @@ final class PhabricatorSearchEngineMySQL extends PhabricatorSearchEngine {
|
||||||
if (strlen($q)) {
|
if (strlen($q)) {
|
||||||
$join[] = qsprintf(
|
$join[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
"{$t_field} field ON field.phid = document.phid");
|
'%T field ON field.phid = document.phid',
|
||||||
|
$t_field);
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)',
|
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)',
|
||||||
|
|
|
@ -44,7 +44,6 @@ final class PhabricatorApplicationSettings extends PhabricatorApplication {
|
||||||
$item->setIcon('settings');
|
$item->setIcon('settings');
|
||||||
$item->setSelected($selected);
|
$item->setSelected($selected);
|
||||||
$item->setHref('/settings/');
|
$item->setHref('/settings/');
|
||||||
$item->setSortOrder(0.90);
|
|
||||||
$items[] = $item;
|
$items[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ arguments.
|
||||||
= Overview =
|
= Overview =
|
||||||
|
|
||||||
Phabricator uses a lightweight markup language called "Remarkup", similar to
|
Phabricator uses a lightweight markup language called "Remarkup", similar to
|
||||||
other lightweight markup langauges like Markdown and Wiki markup.
|
other lightweight markup languages like Markdown and Wiki markup.
|
||||||
|
|
||||||
This document describes how to format text using Remarkup.
|
This document describes how to format text using Remarkup.
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,9 @@ final class CelerityPhabricatorResourceController
|
||||||
|
|
||||||
protected function buildResourceTransformer() {
|
protected function buildResourceTransformer() {
|
||||||
$xformer = new CelerityResourceTransformer();
|
$xformer = new CelerityResourceTransformer();
|
||||||
$xformer->setMinify(PhabricatorEnv::getEnvConfig('celerity.minify'));
|
$xformer->setMinify(
|
||||||
|
!PhabricatorEnv::getEnvConfig('phabricator.developer-mode') &&
|
||||||
|
PhabricatorEnv::getEnvConfig('celerity.minify'));
|
||||||
$xformer->setCelerityMap(CelerityResourceMap::getInstance());
|
$xformer->setCelerityMap(CelerityResourceMap::getInstance());
|
||||||
return $xformer;
|
return $xformer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ abstract class CelerityResourceController extends PhabricatorController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
|
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
|
||||||
!PhabricatorEnv::getEnvConfig('celerity.force-disk-reads')) {
|
!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.
|
||||||
return $this->makeResponseCacheable(new Aphront304Response());
|
return $this->makeResponseCacheable(new Aphront304Response());
|
||||||
|
|
|
@ -74,7 +74,7 @@ final class CelerityResourceTransformer {
|
||||||
$bin = $root.'/externals/javelin/support/jsxmin/jsxmin';
|
$bin = $root.'/externals/javelin/support/jsxmin/jsxmin';
|
||||||
|
|
||||||
if (@file_exists($bin)) {
|
if (@file_exists($bin)) {
|
||||||
$future = new ExecFuture("{$bin} __DEV__:0");
|
$future = new ExecFuture('%s __DEV__:0', $bin);
|
||||||
$future->write($data);
|
$future->write($data);
|
||||||
list($err, $result) = $future->resolve();
|
list($err, $result) = $future->resolve();
|
||||||
if (!$err) {
|
if (!$err) {
|
||||||
|
|
|
@ -167,9 +167,9 @@ abstract class PhabricatorBaseEnglishTranslation
|
||||||
'%d Lint Messages',
|
'%d Lint Messages',
|
||||||
),
|
),
|
||||||
|
|
||||||
'This is a binary file. It is %2$s byte(s) in length.' => array(
|
'This is a binary file. It is %s byte(s) in length.' => array(
|
||||||
'This is a binary file. It is %2$s byte in length.',
|
'This is a binary file. It is %s byte in length.',
|
||||||
'This is a binary file. It is %2$s bytes in length.',
|
'This is a binary file. It is %s bytes in length.',
|
||||||
),
|
),
|
||||||
|
|
||||||
'%d Action(s) Have No Effect' => array(
|
'%d Action(s) Have No Effect' => array(
|
||||||
|
@ -226,9 +226,9 @@ abstract class PhabricatorBaseEnglishTranslation
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
'%2$s Line(s)' => array(
|
'%s Line(s)' => array(
|
||||||
'%2$s Line',
|
'%s Line',
|
||||||
'%2$s Lines',
|
'%s Lines',
|
||||||
),
|
),
|
||||||
|
|
||||||
"Indexing %d object(s) of type %s." => array(
|
"Indexing %d object(s) of type %s." => array(
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use javelin_tag().
|
||||||
|
*/
|
||||||
function javelin_render_tag(
|
function javelin_render_tag(
|
||||||
$tag,
|
$tag,
|
||||||
array $attributes = array(),
|
array $attributes = array(),
|
||||||
|
|
|
@ -187,9 +187,7 @@ final class PhabricatorJavelinLinter extends ArcanistLinter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newSymbolsFuture($path) {
|
private function newSymbolsFuture($path) {
|
||||||
$javelinsymbols = 'javelinsymbols';
|
$future = new ExecFuture('javelinsymbols # %s', $path);
|
||||||
|
|
||||||
$future = new ExecFuture($javelinsymbols.' # '.escapeshellarg($path));
|
|
||||||
$future->write($this->getData($path));
|
$future->write($this->getData($path));
|
||||||
return $future;
|
return $future;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,10 @@ final class PhabricatorRemarkupRuleEmbedFile
|
||||||
case 'thumb':
|
case 'thumb':
|
||||||
default:
|
default:
|
||||||
$attrs['src'] = $file->getPreview220URI();
|
$attrs['src'] = $file->getPreview220URI();
|
||||||
$attrs['width'] = '220';
|
$dimensions =
|
||||||
|
PhabricatorImageTransformer::getPreviewDimensions($file, 220);
|
||||||
|
$attrs['width'] = $dimensions['sdx'];
|
||||||
|
$attrs['height'] = $dimensions['sdy'];
|
||||||
$options['image_class'] = 'phabricator-remarkup-embed-image';
|
$options['image_class'] = 'phabricator-remarkup-embed-image';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ final class PhabricatorRemarkupRuleImageMacro
|
||||||
|
|
||||||
public function apply($text) {
|
public function apply($text) {
|
||||||
return preg_replace_callback(
|
return preg_replace_callback(
|
||||||
'@^([a-zA-Z0-9_\-]+)$@m',
|
'@^([a-zA-Z0-9:_\-]+)$@m',
|
||||||
array($this, 'markupImageMacro'),
|
array($this, 'markupImageMacro'),
|
||||||
$text);
|
$text);
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,10 @@ final class AphrontSideNavFilterView extends AphrontView {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addCustomBlock($block) {
|
public function addCustomBlock($block) {
|
||||||
$this->menu->appendChild($block);
|
$this->menu->addMenuItem(
|
||||||
|
id(new PhabricatorMenuItemView())
|
||||||
|
->setType(PhabricatorMenuItemView::TYPE_CUSTOM)
|
||||||
|
->appendChild($block));
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@ final class PhabricatorMenuItemView extends AphrontTagView {
|
||||||
const TYPE_SPACER = 'type-spacer';
|
const TYPE_SPACER = 'type-spacer';
|
||||||
const TYPE_LABEL = 'type-label';
|
const TYPE_LABEL = 'type-label';
|
||||||
const TYPE_BUTTON = 'type-button';
|
const TYPE_BUTTON = 'type-button';
|
||||||
|
const TYPE_CUSTOM = 'type-custom';
|
||||||
|
|
||||||
private $name;
|
private $name;
|
||||||
private $href;
|
private $href;
|
||||||
private $type = self::TYPE_LINK;
|
private $type = self::TYPE_LINK;
|
||||||
private $isExternal;
|
private $isExternal;
|
||||||
private $key;
|
private $key;
|
||||||
private $sortOrder = 1.0;
|
|
||||||
private $icon;
|
private $icon;
|
||||||
private $selected;
|
private $selected;
|
||||||
|
|
||||||
|
@ -88,15 +88,6 @@ final class PhabricatorMenuItemView extends AphrontTagView {
|
||||||
return $this->isExternal;
|
return $this->isExternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setSortOrder($order) {
|
|
||||||
$this->sortOrder = $order;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSortOrder() {
|
|
||||||
return $this->sortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getTagName() {
|
protected function getTagName() {
|
||||||
return $this->href ? 'a' : 'div';
|
return $this->href ? 'a' : 'div';
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ final class PhabricatorMenuView extends AphrontTagView {
|
||||||
|
|
||||||
private $items = array();
|
private $items = array();
|
||||||
|
|
||||||
|
protected function canAppendChild() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function newLabel($name) {
|
public function newLabel($name) {
|
||||||
$item = id(new PhabricatorMenuItemView())
|
$item = id(new PhabricatorMenuItemView())
|
||||||
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
||||||
|
@ -37,13 +41,83 @@ final class PhabricatorMenuView extends AphrontTagView {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addMenuItem(PhabricatorMenuItemView $item) {
|
public function addMenuItem(PhabricatorMenuItemView $item) {
|
||||||
$key = $item->getKey();
|
return $this->addMenuItemAfter(null, $item);
|
||||||
$this->items[] = $item;
|
}
|
||||||
$this->appendChild($item);
|
|
||||||
|
|
||||||
|
public function addMenuItemAfter($key, PhabricatorMenuItemView $item) {
|
||||||
|
if ($key === null) {
|
||||||
|
$this->items[] = $item;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->getItem($key)) {
|
||||||
|
throw new Exception("No such key '{$key}' to add menu item after!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach ($this->items as $other) {
|
||||||
|
$result[] = $other;
|
||||||
|
if ($other->getKey() == $key) {
|
||||||
|
$result[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->items = $result;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addMenuItemBefore($key, PhabricatorMenuItemView $item) {
|
||||||
|
if ($key === null) {
|
||||||
|
array_unshift($this->items, $item);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->requireKey($key);
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
foreach ($this->items as $other) {
|
||||||
|
if ($other->getKey() == $key) {
|
||||||
|
$result[] = $item;
|
||||||
|
}
|
||||||
|
$result[] = $other;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->items = $result;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addMenuItemToLabel($key, PhabricatorMenuItemView $item) {
|
||||||
|
$this->requireKey($key);
|
||||||
|
|
||||||
|
$other = $this->getItem($key);
|
||||||
|
if ($other->getType() != PhabricatorMenuItemView::TYPE_LABEL) {
|
||||||
|
throw new Exception("Menu item '{$key}' is not a label!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen = false;
|
||||||
|
$after = null;
|
||||||
|
foreach ($this->items as $other) {
|
||||||
|
if (!$seen) {
|
||||||
|
if ($other->getKey() == $key) {
|
||||||
|
$seen = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($other->getType() == PhabricatorMenuItemView::TYPE_LABEL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$after = $other->getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->addMenuItemAfter($after, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function requireKey($key) {
|
||||||
|
if (!$this->getItem($key)) {
|
||||||
|
throw new Exception("No menu item with key '{$key}' exists!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getItem($key) {
|
public function getItem($key) {
|
||||||
$key = (string)$key;
|
$key = (string)$key;
|
||||||
|
|
||||||
|
@ -83,4 +157,8 @@ final class PhabricatorMenuView extends AphrontTagView {
|
||||||
'class' => 'phabricator-menu-view',
|
'class' => 'phabricator-menu-view',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getTagContent() {
|
||||||
|
return $this->renderSingleView($this->items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
141
src/view/layout/__tests__/PhabricatorMenuViewTestCase.php
Normal file
141
src/view/layout/__tests__/PhabricatorMenuViewTestCase.php
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorMenuViewTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
public function testAppend() {
|
||||||
|
$menu = $this->newABCMenu();
|
||||||
|
|
||||||
|
$this->assertMenuKeys(
|
||||||
|
array(
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
),
|
||||||
|
$menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAppendAfter() {
|
||||||
|
$menu = $this->newABCMenu();
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$menu->addMenuItemAfter('x', $this->newLink('test1'));
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
$this->assertEqual(true, $caught instanceof Exception);
|
||||||
|
|
||||||
|
$menu->addMenuItemAfter('a', $this->newLink('test2'));
|
||||||
|
$menu->addMenuItemAfter(null, $this->newLink('test3'));
|
||||||
|
$menu->addMenuItemAfter('a', $this->newLink('test4'));
|
||||||
|
$menu->addMenuItemAfter('test3', $this->newLink('test5'));
|
||||||
|
|
||||||
|
$this->assertMenuKeys(
|
||||||
|
array(
|
||||||
|
'a',
|
||||||
|
'test4',
|
||||||
|
'test2',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
'test3',
|
||||||
|
'test5',
|
||||||
|
),
|
||||||
|
$menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAppendBefore() {
|
||||||
|
$menu = $this->newABCMenu();
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$menu->addMenuItemBefore('x', $this->newLink('test1'));
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
$this->assertEqual(true, $caught instanceof Exception);
|
||||||
|
|
||||||
|
$menu->addMenuItemBefore('b', $this->newLink('test2'));
|
||||||
|
$menu->addMenuItemBefore(null, $this->newLink('test3'));
|
||||||
|
$menu->addMenuItemBefore('a', $this->newLink('test4'));
|
||||||
|
$menu->addMenuItemBefore('test3', $this->newLink('test5'));
|
||||||
|
|
||||||
|
$this->assertMenuKeys(
|
||||||
|
array(
|
||||||
|
'test5',
|
||||||
|
'test3',
|
||||||
|
'test4',
|
||||||
|
'a',
|
||||||
|
'test2',
|
||||||
|
'b',
|
||||||
|
'c',
|
||||||
|
),
|
||||||
|
$menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAppendLabel() {
|
||||||
|
$menu = new PhabricatorMenuView();
|
||||||
|
$menu->addMenuItem($this->newLabel('fruit'));
|
||||||
|
$menu->addMenuItem($this->newLabel('animals'));
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$menu->addMenuItemToLabel('x', $this->newLink('test1'));
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
$this->assertEqual(true, $caught instanceof Exception);
|
||||||
|
|
||||||
|
$menu->addMenuItemToLabel('fruit', $this->newLink('apple'));
|
||||||
|
$menu->addMenuItemToLabel('fruit', $this->newLink('banana'));
|
||||||
|
|
||||||
|
$menu->addMenuItemToLabel('animals', $this->newLink('dog'));
|
||||||
|
$menu->addMenuItemToLabel('animals', $this->newLink('cat'));
|
||||||
|
|
||||||
|
$menu->addMenuItemToLabel('fruit', $this->newLink('cherry'));
|
||||||
|
|
||||||
|
$this->assertMenuKeys(
|
||||||
|
array(
|
||||||
|
'fruit',
|
||||||
|
'apple',
|
||||||
|
'banana',
|
||||||
|
'cherry',
|
||||||
|
'animals',
|
||||||
|
'dog',
|
||||||
|
'cat',
|
||||||
|
),
|
||||||
|
$menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newLink($key) {
|
||||||
|
return id(new PhabricatorMenuItemView())
|
||||||
|
->setKey($key)
|
||||||
|
->setHref('#')
|
||||||
|
->setName('Link');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newLabel($key) {
|
||||||
|
return id(new PhabricatorMenuItemView())
|
||||||
|
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
||||||
|
->setKey($key)
|
||||||
|
->setName('Label');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newABCMenu() {
|
||||||
|
$menu = new PhabricatorMenuView();
|
||||||
|
|
||||||
|
$menu->addMenuItem($this->newLink('a'));
|
||||||
|
$menu->addMenuItem($this->newLink('b'));
|
||||||
|
$menu->addMenuItem($this->newLink('c'));
|
||||||
|
|
||||||
|
return $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertMenuKeys(array $expect, PhabricatorMenuView $menu) {
|
||||||
|
$items = $menu->getItems();
|
||||||
|
$keys = mpull($items, 'getKey');
|
||||||
|
$keys = array_values($keys);
|
||||||
|
|
||||||
|
$this->assertEqual($expect, $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -83,7 +83,11 @@ class PhabricatorBarePageView extends AphrontPageView {
|
||||||
|
|
||||||
'<script type="text/javascript">'.
|
'<script type="text/javascript">'.
|
||||||
$framebust.
|
$framebust.
|
||||||
'window.__DEV__=1;'.
|
'window.__DEV__='.
|
||||||
|
(PhabricatorEnv::getEnvConfig('phabricator.developer-mode')
|
||||||
|
? '1'
|
||||||
|
: '0').
|
||||||
|
';'.
|
||||||
'</script>',
|
'</script>',
|
||||||
|
|
||||||
$response->renderResourcesOfType('css'),
|
$response->renderResourcesOfType('css'),
|
||||||
|
|
|
@ -256,7 +256,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
||||||
}
|
}
|
||||||
|
|
||||||
$developer_warning = null;
|
$developer_warning = null;
|
||||||
if (PhabricatorEnv::getEnvConfig('phabricator.show-error-callout') &&
|
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode') &&
|
||||||
DarkConsoleErrorLogPluginAPI::getErrors()) {
|
DarkConsoleErrorLogPluginAPI::getErrors()) {
|
||||||
$developer_warning = phutil_tag(
|
$developer_warning = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
|
|
|
@ -5,7 +5,6 @@ final class PhabricatorMainMenuIconView extends AphrontView {
|
||||||
private $classes = array();
|
private $classes = array();
|
||||||
private $href;
|
private $href;
|
||||||
private $name;
|
private $name;
|
||||||
private $sortOrder = 0.5;
|
|
||||||
private $workflow;
|
private $workflow;
|
||||||
private $style;
|
private $style;
|
||||||
|
|
||||||
|
@ -42,22 +41,6 @@ final class PhabricatorMainMenuIconView extends AphrontView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a float, where 0.0 is the profile item and 1.0 is the logout
|
|
||||||
* item. Normally you should pick something between the two.
|
|
||||||
*
|
|
||||||
* @param float Sort order.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public function setSortOrder($sort_order) {
|
|
||||||
$this->sortOrder = $sort_order;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSortOrder() {
|
|
||||||
return $this->sortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
$name = $this->getName();
|
$name = $this->getName();
|
||||||
$href = $this->getHref();
|
$href = $this->getHref();
|
||||||
|
|
|
@ -164,7 +164,6 @@ final class PhabricatorMainMenuView extends AphrontView {
|
||||||
$controller = $this->getController();
|
$controller = $this->getController();
|
||||||
|
|
||||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||||
$applications = msort($applications, 'getName');
|
|
||||||
|
|
||||||
$core = array();
|
$core = array();
|
||||||
$more = array();
|
$more = array();
|
||||||
|
@ -184,7 +183,7 @@ final class PhabricatorMainMenuView extends AphrontView {
|
||||||
if ($application->getApplicationGroup() == $group_core) {
|
if ($application->getApplicationGroup() == $group_core) {
|
||||||
$core[] = $item;
|
$core[] = $item;
|
||||||
} else {
|
} else {
|
||||||
$more[] = $item;
|
$more[$application->getName()] = $item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +199,9 @@ final class PhabricatorMainMenuView extends AphrontView {
|
||||||
$view->addClass('phabricator-core-menu');
|
$view->addClass('phabricator-core-menu');
|
||||||
|
|
||||||
$search = $this->renderSearch();
|
$search = $this->renderSearch();
|
||||||
$view->appendChild($search);
|
if ($search) {
|
||||||
|
$view->addMenuItem($search);
|
||||||
|
}
|
||||||
|
|
||||||
$view
|
$view
|
||||||
->newLabel(pht('Home'))
|
->newLabel(pht('Home'))
|
||||||
|
@ -235,7 +236,6 @@ final class PhabricatorMainMenuView extends AphrontView {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($actions) {
|
if ($actions) {
|
||||||
$actions = msort($actions, 'getSortOrder');
|
|
||||||
$view->addMenuItem(
|
$view->addMenuItem(
|
||||||
id(new PhabricatorMenuItemView())
|
id(new PhabricatorMenuItemView())
|
||||||
->addClass('phabricator-core-item-device')
|
->addClass('phabricator-core-item-device')
|
||||||
|
@ -260,6 +260,7 @@ final class PhabricatorMainMenuView extends AphrontView {
|
||||||
->addClass('phabricator-core-item-device')
|
->addClass('phabricator-core-item-device')
|
||||||
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
||||||
->setName(pht('More Applications')));
|
->setName(pht('More Applications')));
|
||||||
|
ksort($more);
|
||||||
foreach ($more as $item) {
|
foreach ($more as $item) {
|
||||||
$item->addClass('phabricator-core-item-device');
|
$item->addClass('phabricator-core-item-device');
|
||||||
$view->addMenuItem($item);
|
$view->addMenuItem($item);
|
||||||
|
|
|
@ -18,6 +18,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.pholio-mock-image {
|
.pholio-mock-image {
|
||||||
margin: 10px 0px;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pholio-mock-select {
|
||||||
|
border: 1px solid #FF0000;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pholio-mock-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: crosshair;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.phabricator-header-tags {
|
.phabricator-header-tags {
|
||||||
margin-left: 1em;
|
margin-left: 12px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.phabricator-header-tags .phabricator-tag-view {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,55 @@
|
||||||
* javelin-dom
|
* javelin-dom
|
||||||
* javelin-stratcom
|
* javelin-stratcom
|
||||||
* javelin-behavior-device
|
* javelin-behavior-device
|
||||||
|
* javelin-vector
|
||||||
* phabricator-tooltip
|
* phabricator-tooltip
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('differential-populate', function(config) {
|
JX.behavior('differential-populate', function(config) {
|
||||||
|
|
||||||
function onresponse(target, response) {
|
function onresponse(target_id, response) {
|
||||||
JX.DOM.replace(JX.$(target), JX.$H(response.changeset));
|
// As we populate the diff, we try to hold the document scroll position
|
||||||
|
// steady, so that, e.g., users who want to leave a comment on a diff with a
|
||||||
|
// large number of changes don't constantly have the text area scrolled off
|
||||||
|
// the bottom of the screen until the entire diff loads.
|
||||||
|
//
|
||||||
|
// There are two three major cases here:
|
||||||
|
//
|
||||||
|
// - If we're near the top of the document, never scroll.
|
||||||
|
// - If we're near the bottom of the document, always scroll.
|
||||||
|
// - Otherwise, scroll if the changes were above the midline of the
|
||||||
|
// viewport.
|
||||||
|
var target = JX.$(target_id);
|
||||||
|
|
||||||
|
var old_pos = JX.Vector.getScroll();
|
||||||
|
var old_view = JX.Vector.getViewport();
|
||||||
|
var old_dim = JX.Vector.getDocument();
|
||||||
|
|
||||||
|
// Number of pixels away from the top or bottom of the document which
|
||||||
|
// count as "nearby".
|
||||||
|
var sticky = 480;
|
||||||
|
|
||||||
|
var near_top = (old_pos.y <= sticky);
|
||||||
|
var near_bot = ((old_pos.y + old_view.y) >= (old_dim.y - sticky));
|
||||||
|
|
||||||
|
var target_pos = JX.Vector.getPos(target);
|
||||||
|
var target_dim = JX.Vector.getDim(target);
|
||||||
|
var target_mid = (target_pos.y + (target_dim.y / 2));
|
||||||
|
|
||||||
|
var view_mid = (old_pos.y + (old_view.y / 2));
|
||||||
|
var above_mid = (target_mid < view_mid);
|
||||||
|
|
||||||
|
JX.DOM.replace(target, JX.$H(response.changeset));
|
||||||
|
|
||||||
|
if (!near_top) {
|
||||||
|
if (near_bot || above_mid) {
|
||||||
|
// Figure out how much taller the document got.
|
||||||
|
var delta = (JX.Vector.getDocument().y - old_dim.y);
|
||||||
|
|
||||||
|
window.scrollTo(old_pos.x, old_pos.y + delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (response.coverage) {
|
if (response.coverage) {
|
||||||
for (var k in response.coverage) {
|
for (var k in response.coverage) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -2,8 +2,19 @@
|
||||||
* @provides javelin-behavior-pholio-mock-view
|
* @provides javelin-behavior-pholio-mock-view
|
||||||
* @requires javelin-behavior
|
* @requires javelin-behavior
|
||||||
* javelin-stratcom
|
* javelin-stratcom
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-vector
|
||||||
|
* javelin-event
|
||||||
*/
|
*/
|
||||||
JX.behavior('pholio-mock-view', function(config) {
|
JX.behavior('pholio-mock-view', function(config) {
|
||||||
|
var is_dragging = false;
|
||||||
|
var wrapper = JX.$('mock-wrapper');
|
||||||
|
var image;
|
||||||
|
var imageData;
|
||||||
|
var startPos;
|
||||||
|
var endPos;
|
||||||
|
var selection;
|
||||||
|
|
||||||
JX.Stratcom.listen(
|
JX.Stratcom.listen(
|
||||||
'click', // Listen for clicks...
|
'click', // Listen for clicks...
|
||||||
'mock-thumbnail', // ...on nodes with sigil "mock-thumbnail".
|
'mock-thumbnail', // ...on nodes with sigil "mock-thumbnail".
|
||||||
|
@ -11,7 +22,106 @@ JX.behavior('pholio-mock-view', function(config) {
|
||||||
var data = e.getNodeData('mock-thumbnail');
|
var data = e.getNodeData('mock-thumbnail');
|
||||||
|
|
||||||
var main = JX.$(config.mainID);
|
var main = JX.$(config.mainID);
|
||||||
|
JX.Stratcom.addData(
|
||||||
|
main,
|
||||||
|
{
|
||||||
|
fullSizeURI: data['fullSizeURI'],
|
||||||
|
imageID: data['imageID']
|
||||||
|
});
|
||||||
|
|
||||||
main.src = data.fullSizeURI;
|
main.src = data.fullSizeURI;
|
||||||
|
|
||||||
|
JX.DOM.setContent(wrapper,main);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function draw_rectangle(node, current, init) {
|
||||||
|
JX.$V(
|
||||||
|
Math.abs(current.x-init.x),
|
||||||
|
Math.abs(current.y-init.y))
|
||||||
|
.setDim(node);
|
||||||
|
|
||||||
|
JX.$V(
|
||||||
|
(current.x-init.x < 0) ? current.x:init.x,
|
||||||
|
(current.y-init.y < 0) ? current.y:init.y)
|
||||||
|
.setPos(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRealXY(parent, point) {
|
||||||
|
var pos = {x: (point.x - parent.x), y: (point.y - parent.y)};
|
||||||
|
|
||||||
|
if (pos.x < 0) pos.x = 0;
|
||||||
|
else if (pos.x > image.clientWidth) pos.x = image.clientWidth - 1;
|
||||||
|
|
||||||
|
if (pos.y < 0) pos.y = 0;
|
||||||
|
else if (pos.y > image.clientHeight) pos.y = image.clientHeight - 2;
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
JX.Stratcom.listen('mousedown', 'mock-wrapper', function(e) {
|
||||||
|
if (!e.isNormalMouseEvent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
image = JX.$(config.mainID);
|
||||||
|
imageData = JX.Stratcom.getData(image);
|
||||||
|
|
||||||
|
e.getRawEvent().target.draggable = false;
|
||||||
|
is_dragging = true;
|
||||||
|
|
||||||
|
startPos = getRealXY(JX.$V(wrapper),JX.$V(e));
|
||||||
|
|
||||||
|
selection = JX.$N(
|
||||||
|
'div',
|
||||||
|
{className: 'pholio-mock-select'}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
JX.$V(startPos.x,startPos.y).setPos(selection);
|
||||||
|
|
||||||
|
JX.DOM.appendContent(wrapper, selection);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
JX.enableDispatch(document.body, 'mousemove');
|
||||||
|
JX.Stratcom.listen('mousemove',null, function(e) {
|
||||||
|
if (!is_dragging) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_rectangle(selection, getRealXY(JX.$V(wrapper), JX.$V(e)), startPos);
|
||||||
|
});
|
||||||
|
|
||||||
|
JX.Stratcom.listen(
|
||||||
|
'mouseup',
|
||||||
|
null,
|
||||||
|
function(e) {
|
||||||
|
if (!is_dragging) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
is_dragging = false;
|
||||||
|
|
||||||
|
endPos = getRealXY(JX.$V(wrapper), JX.$V(e));
|
||||||
|
|
||||||
|
comment = window.prompt("Add your comment");
|
||||||
|
if (comment == null || comment == "") {
|
||||||
|
selection.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selection.title = comment;
|
||||||
|
|
||||||
|
console.log("ImageID: " + imageData['imageID'] +
|
||||||
|
", coords: (" + Math.min(startPos.x, endPos.x) + "," +
|
||||||
|
Math.min(startPos.y, endPos.y) + ") -> (" +
|
||||||
|
Math.max(startPos.x,endPos.x) + "," + Math.max(startPos.y,endPos.y) +
|
||||||
|
"), comment: " + comment);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue