mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 00:10:57 +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/ENVIRONMENT
|
||||
/conf/local/VERSION
|
||||
|
||||
# Impact Font
|
||||
/resources/font/impact.ttf
|
||||
|
|
|
@ -798,13 +798,11 @@ return array(
|
|||
// behalf, silencing the warning.
|
||||
'phabricator.timezone' => null,
|
||||
|
||||
// When unhandled exceptions occur, stack traces are hidden by default.
|
||||
// You can enable traces for development to make it easier to debug problems.
|
||||
'phabricator.show-stack-traces' => false,
|
||||
|
||||
// Shows an error callout if a page generated PHP errors, warnings or notices.
|
||||
// This makes it harder to miss problems while developing Phabricator.
|
||||
'phabricator.show-error-callout' => false,
|
||||
// Show stack traces when unhandled exceptions occur, force reloading of
|
||||
// static resources (skipping the cache), show an error callout if a page
|
||||
// generated PHP errors, warnings, or notices, force disk reads when
|
||||
// reloading. This option should not be enabled in production.
|
||||
'phabricator.developer-mode' => false,
|
||||
|
||||
// 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
|
||||
|
@ -1220,15 +1218,6 @@ return array(
|
|||
// unlikely that you need to modify this.
|
||||
'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
|
||||
// enable this in production, but disable it in development.
|
||||
'celerity.minify' => false,
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
return array(
|
||||
|
||||
'phabricator.developer-mode' => 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');
|
||||
|
|
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();
|
||||
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).
|
||||
*
|
||||
* @return bool
|
||||
* @task info
|
||||
*/
|
||||
isNormalClick : function() {
|
||||
if (this.getType() != 'click') {
|
||||
isNormalMouseEvent : function() {
|
||||
var supportedEvents = ['click','mouseup','mousedown'];
|
||||
|
||||
if (supportedEvents.indexOf(this.getType()) == -1) {
|
||||
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.
|
||||
* 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',
|
||||
'PhabricatorMenuItemView' => 'view/layout/PhabricatorMenuItemView.php',
|
||||
'PhabricatorMenuView' => 'view/layout/PhabricatorMenuView.php',
|
||||
'PhabricatorMenuViewTestCase' => 'view/layout/__tests__/PhabricatorMenuViewTestCase.php',
|
||||
'PhabricatorMercurialGraphStream' => 'applications/repository/daemon/PhabricatorMercurialGraphStream.php',
|
||||
'PhabricatorMetaMTAAttachment' => 'applications/metamta/storage/PhabricatorMetaMTAAttachment.php',
|
||||
'PhabricatorMetaMTAConfigOptions' => 'applications/config/option/PhabricatorMetaMTAConfigOptions.php',
|
||||
|
@ -2395,6 +2396,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMarkupCache' => 'PhabricatorCacheDAO',
|
||||
'PhabricatorMenuItemView' => 'AphrontTagView',
|
||||
'PhabricatorMenuView' => 'AphrontTagView',
|
||||
'PhabricatorMenuViewTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorMetaMTAConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorMetaMTAController' => 'PhabricatorController',
|
||||
'PhabricatorMetaMTADAO' => 'PhabricatorLiskDAO',
|
||||
|
|
|
@ -228,6 +228,26 @@ final class AphrontRequest {
|
|||
$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
|
||||
// internet for 6 hours, and then reconnect and immediately submit,
|
||||
// but give the user some indication of what happened since the workflow
|
||||
|
|
|
@ -238,7 +238,7 @@ class AphrontDefaultApplicationConfiguration
|
|||
"schema is up to date.";
|
||||
}
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) {
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
||||
$trace = $this->renderStackTrace($ex->getTrace(), $user);
|
||||
} else {
|
||||
$trace = null;
|
||||
|
|
|
@ -22,7 +22,6 @@ final class PhabricatorApplicationAuth extends PhabricatorApplication {
|
|||
$item->setIcon('power');
|
||||
$item->setWorkflow(true);
|
||||
$item->setHref('/logout/');
|
||||
$item->setSortOrder(2.0);
|
||||
$item->setSelected(($controller instanceof PhabricatorLogoutController));
|
||||
$items[] = $item;
|
||||
}
|
||||
|
|
|
@ -70,9 +70,10 @@ final class PhabricatorOAuthFailureView extends AphrontView {
|
|||
|
||||
$provider_key = $provider->getProviderKey();
|
||||
$diagnose = hsprintf(
|
||||
'<a href="/oauth/'.$provider_key.'/diagnose/" class="button green">'.
|
||||
'<a href="/oauth/%s/diagnose/" class="button green">'.
|
||||
'Diagnose %s OAuth Problems'.
|
||||
'</a>',
|
||||
$provider_key,
|
||||
$provider_name);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,9 @@ abstract class PhabricatorApplication {
|
|||
$uninstalled =
|
||||
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;
|
||||
} else {
|
||||
return true;
|
||||
|
@ -227,8 +229,21 @@ abstract class PhabricatorApplication {
|
|||
|
||||
/* -( 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())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setConcreteOnly(true)
|
||||
|
@ -241,6 +256,13 @@ abstract class PhabricatorApplication {
|
|||
$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;
|
||||
}
|
||||
|
||||
|
@ -254,31 +276,24 @@ abstract class PhabricatorApplication {
|
|||
PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications');
|
||||
|
||||
|
||||
|
||||
if (empty($applications)) {
|
||||
$classes = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setConcreteOnly(true)
|
||||
->selectAndLoadSymbols();
|
||||
|
||||
$all_applications = self::getAllApplications();
|
||||
$apps = array();
|
||||
foreach ($classes as $class) {
|
||||
|
||||
if (isset($uninstalled[$class['name']])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$app = newv($class['name'], array());
|
||||
|
||||
if (!$app->isEnabled()) {
|
||||
foreach ($all_applications as $app) {
|
||||
$class = get_class($app);
|
||||
if (isset($uninstalled[$class])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$show_beta && $app->isBeta()) {
|
||||
if (!$app->isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$apps[] = $app;
|
||||
if (!$show_beta && $app->isBeta()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$apps[] = $app;
|
||||
}
|
||||
|
||||
$applications = $apps;
|
||||
|
|
|
@ -86,48 +86,18 @@ final class PhabricatorDeveloperConfigOptions
|
|||
"data to look at eventually). In development, it may be useful to ".
|
||||
"set it to 1 in order to debug performance problems.\n\n".
|
||||
"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(
|
||||
array(
|
||||
pht('Show stack traces'),
|
||||
pht('Hide stack traces'),
|
||||
pht('Enable developer mode'),
|
||||
pht('Disable developer mode'),
|
||||
))
|
||||
->setSummary(pht("Show stack traces when unhandled exceptions occur."))
|
||||
->setDescription(
|
||||
pht(
|
||||
"When unhandled exceptions occur, stack traces are hidden by ".
|
||||
"default. You can enable traces for development to make it easier ".
|
||||
"to debug problems.")),
|
||||
$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.)")),
|
||||
->setSummary(pht("Enable verbose error reporting and disk reads."))
|
||||
->setDescription(
|
||||
pht(
|
||||
"This option enables verbose error reporting (stack traces, ".
|
||||
"error callouts) and forces disk reads of static assets on ".
|
||||
"every reload.")),
|
||||
$this->newOption('celerity.minify', 'bool', false)
|
||||
->setBoolOptions(
|
||||
array(
|
||||
|
|
|
@ -10,6 +10,15 @@ final class PhabricatorConfigResponse extends AphrontHTMLResponse {
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
$view = $this->view->render();
|
||||
|
|
|
@ -44,6 +44,7 @@ final class ConpherenceUpdateController extends
|
|||
'ip' => $request->getRemoteAddr()
|
||||
));
|
||||
$editor = id(new ConpherenceEditor())
|
||||
->setContinueOnNoEffect($request->isContinueRequest())
|
||||
->setContentSource($content_source)
|
||||
->setActor($user);
|
||||
|
||||
|
@ -55,28 +56,6 @@ final class ConpherenceUpdateController extends
|
|||
$conpherence,
|
||||
$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;
|
||||
case 'metadata':
|
||||
$xactions = array();
|
||||
|
@ -112,23 +91,25 @@ final class ConpherenceUpdateController extends
|
|||
->setTransactionType(ConpherenceTransactionType::TYPE_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;
|
||||
default:
|
||||
throw new Exception('Unknown action: '.$action);
|
||||
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) {
|
||||
|
|
|
@ -128,6 +128,7 @@ final class ConpherenceViewController extends
|
|||
|
||||
$form =
|
||||
id(new AphrontFormView())
|
||||
->setWorkflow(true)
|
||||
->setAction($this->getApplicationURI('update/'.$conpherence->getID().'/'))
|
||||
->setFlexible(true)
|
||||
->setUser($user)
|
||||
|
|
|
@ -118,6 +118,27 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
);
|
||||
}
|
||||
$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;
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
foreach ($xaction->getNewValue() as $participant) {
|
||||
|
|
|
@ -363,7 +363,7 @@ final class DifferentialRevisionListController extends DifferentialController {
|
|||
array_select_keys($handles, $params['participants']),
|
||||
'getFullName');
|
||||
return id(new AphrontFormTokenizerControl())
|
||||
->setDatasource('/typeahead/common/allmailable/')
|
||||
->setDatasource('/typeahead/common/accounts/')
|
||||
->setLabel($label)
|
||||
->setName('participants')
|
||||
->setValue($value);
|
||||
|
|
|
@ -182,10 +182,9 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
||||
$warning->appendChild(
|
||||
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.',
|
||||
$count,
|
||||
PhutilTranslator::getInstance()->formatNumber($count)).
|
||||
new PhutilNumber($count)).
|
||||
" <strong>".
|
||||
phutil_tag(
|
||||
'a',
|
||||
|
@ -499,7 +498,6 @@ final class DifferentialRevisionViewController extends DifferentialController {
|
|||
'href' => "/differential/subscribe/{$action}/{$revision_id}/",
|
||||
'name' => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
|
||||
'instant' => true,
|
||||
'sigil' => 'workflow',
|
||||
);
|
||||
} else {
|
||||
$links[] = array(
|
||||
|
|
|
@ -188,6 +188,8 @@ final class DifferentialDiff extends DifferentialDAO {
|
|||
'id' => $this->getID(),
|
||||
'parent' => $this->getParentRevisionID(),
|
||||
'revisionID' => $this->getRevisionID(),
|
||||
'dateCreated' => $this->getDateCreated(),
|
||||
'dateModified' => $this->getDateModified(),
|
||||
'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
|
||||
'sourceControlPath' => $this->getSourceControlPath(),
|
||||
'sourceControlSystem' => $this->getSourceControlSystem(),
|
||||
|
|
|
@ -229,7 +229,7 @@ final class DifferentialChangesetListView extends AphrontView {
|
|||
$template =
|
||||
'<table><tr>'.
|
||||
'<th></th><td>%s</td>'.
|
||||
'<th></th><td colspan="2">%s</td>'.
|
||||
'<th></th><td colspan="3">%s</td>'.
|
||||
'</tr></table>';
|
||||
|
||||
return array(
|
||||
|
|
|
@ -52,6 +52,7 @@ final class DifferentialRevisionDetailView extends AphrontView {
|
|||
->setName($action['name'])
|
||||
->setHref(idx($action, 'href'))
|
||||
->setWorkflow(idx($action, 'sigil') == 'workflow')
|
||||
->setRenderAsForm(!empty($action['instant']))
|
||||
->setUser($user)
|
||||
->setDisabled(idx($action, 'disabled', false));
|
||||
$actions->addAction($obj);
|
||||
|
|
|
@ -814,9 +814,9 @@ final class DiffusionBrowseFileController extends DiffusionController {
|
|||
|
||||
$size = strlen($data);
|
||||
$properties->addTextContent(
|
||||
pht('This is a binary file. It is %2$s byte(s) in length.',
|
||||
$size,
|
||||
PhutilTranslator::getInstance()->formatNumber($size)));
|
||||
pht(
|
||||
'This is a binary file. It is %s byte(s) in length.',
|
||||
new PhutilNumber($size)));
|
||||
|
||||
$actions = id(new PhabricatorActionListView())
|
||||
->setUser($this->getRequest()->getUser())
|
||||
|
|
|
@ -116,10 +116,18 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
$content[] = $this->buildAuditTable($commit, $audit_requests);
|
||||
$content[] = $this->buildComments($commit);
|
||||
|
||||
$hard_limit = 1000;
|
||||
|
||||
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
|
||||
$drequest);
|
||||
$change_query->setLimit($hard_limit + 1);
|
||||
$changes = $change_query->loadChanges();
|
||||
|
||||
$was_limited = (count($changes) > $hard_limit);
|
||||
if ($was_limited) {
|
||||
$changes = array_slice($changes, 0, $hard_limit);
|
||||
}
|
||||
|
||||
$content[] = $this->buildMergesTable($commit);
|
||||
|
||||
$owners_paths = array();
|
||||
|
@ -151,7 +159,6 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
'r'.$callsign.$commit->getCommitIdentifier());
|
||||
}
|
||||
|
||||
$pane_id = null;
|
||||
if ($bad_commit) {
|
||||
$error_panel = new AphrontErrorView();
|
||||
$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 ".
|
||||
"paths).");
|
||||
$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 {
|
||||
$change_panel = new AphrontPanelView();
|
||||
$change_panel->setHeader("Changes (".number_format($count).")");
|
||||
$change_panel->setID('toc');
|
||||
|
||||
if ($count > self::CHANGES_LIMIT) {
|
||||
$show_all_button = phutil_tag(
|
||||
'a',
|
||||
|
@ -296,28 +312,11 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
}
|
||||
$change_table->setRenderingReferences($change_references);
|
||||
|
||||
// TODO: This is pretty awkward, unify the CSS between Diffusion and
|
||||
// Differential better.
|
||||
require_celerity_resource('differential-core-view-css');
|
||||
$pane_id = celerity_generate_unique_node_id();
|
||||
$add_comment_view = $this->renderAddCommentPanel($commit,
|
||||
$audit_requests,
|
||||
$pane_id);
|
||||
$main_pane = phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => $pane_id
|
||||
),
|
||||
$change_list->render().
|
||||
id(new PhabricatorAnchorView())
|
||||
->setAnchorName('comment')
|
||||
->setNavigationMarker(true)
|
||||
->render().
|
||||
$add_comment_view);
|
||||
|
||||
$content[] = $main_pane;
|
||||
$content[] = $change_list->render();
|
||||
}
|
||||
|
||||
$content[] = $this->renderAddCommentPanel($commit, $audit_requests);
|
||||
|
||||
$commit_id = 'r'.$callsign.$commit->getCommitIdentifier();
|
||||
$short_name = DiffusionView::nameCommit(
|
||||
$repository,
|
||||
|
@ -577,13 +576,13 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
|
||||
private function renderAddCommentPanel(
|
||||
PhabricatorRepositoryCommit $commit,
|
||||
array $audit_requests,
|
||||
$pane_id = null) {
|
||||
array $audit_requests) {
|
||||
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
|
||||
$pane_id = celerity_generate_unique_node_id();
|
||||
Javelin::initBehavior(
|
||||
'differential-keyboard-navigation',
|
||||
array(
|
||||
|
@ -693,14 +692,26 @@ final class DiffusionCommitController extends DiffusionController {
|
|||
</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(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'differential-add-comment-panel',
|
||||
),
|
||||
id(new PhabricatorAnchorView())
|
||||
->setAnchorName('comment')
|
||||
->setNavigationMarker(true)
|
||||
->render().
|
||||
$panel->render().
|
||||
$preview_panel);
|
||||
$preview_panel));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,16 @@
|
|||
final class DiffusionPathChangeQuery {
|
||||
|
||||
private $request;
|
||||
private $limit;
|
||||
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLimit() {
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
final private function __construct() {
|
||||
// <private>
|
||||
|
@ -31,20 +41,36 @@ final class DiffusionPathChangeQuery {
|
|||
|
||||
$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(
|
||||
$repository->establishConnection('r'),
|
||||
$conn_r,
|
||||
'SELECT c.*, p.path pathName, t.path targetPathName,
|
||||
i.commitIdentifier targetCommitIdentifier
|
||||
FROM %T c
|
||||
LEFT JOIN %T p ON c.pathID = p.id
|
||||
LEFT JOIN %T t ON c.targetPathID = t.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_PATH,
|
||||
PhabricatorRepository::TABLE_PATH,
|
||||
$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();
|
||||
|
||||
|
@ -68,20 +94,22 @@ final class DiffusionPathChangeQuery {
|
|||
$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();
|
||||
foreach ($changes as $change) {
|
||||
if ($change->getTargetPath()) {
|
||||
$away[$change->getTargetPath()][] = $change->getPath();
|
||||
if (!$limited) {
|
||||
$away = array();
|
||||
foreach ($changes as $change) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -95,22 +95,21 @@ final class DiffusionBrowseTableView extends DiffusionView {
|
|||
|
||||
$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()) {
|
||||
$where = qsprintf(
|
||||
$conn,
|
||||
'AND code = %s',
|
||||
$drequest->getLint());
|
||||
$where .= qsprintf($conn, ' AND code = %s', $drequest->getLint());
|
||||
}
|
||||
|
||||
$like = (substr($drequest->getPath(), -1) == '/' ? 'LIKE %>' : '= %s');
|
||||
return head(queryfx_one(
|
||||
$conn,
|
||||
'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q AND path '.$like,
|
||||
'SELECT COUNT(*) FROM %T WHERE branchID = %d %Q',
|
||||
PhabricatorRepository::TABLE_LINTMESSAGE,
|
||||
$branch->getID(),
|
||||
$where,
|
||||
'/'.$drequest->getPath()));
|
||||
$where));
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
|
|
@ -65,8 +65,6 @@ abstract class PhabricatorDirectoryController extends PhabricatorController {
|
|||
continue;
|
||||
}
|
||||
|
||||
$tile_group = msort($tile_group, 'getApplicationOrder');
|
||||
|
||||
$is_small_tiles = ($tile_display == PhabricatorApplication::TILE_SHOW) ||
|
||||
($tile_display == PhabricatorApplication::TILE_HIDE);
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ final class PhabricatorApplicationDiviner extends PhabricatorApplication {
|
|||
$item->setName(pht('%s Help', $application->getName()));
|
||||
$item->setIcon('help');
|
||||
$item->setHref($application->getHelpURI());
|
||||
$item->setSortOrder(0.1);
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ final class PhabricatorImageTransformer {
|
|||
return $dst;
|
||||
}
|
||||
|
||||
private function generatePreview(PhabricatorFile $file, $size) {
|
||||
public static function getPreviewDimensions(PhabricatorFile $file, $size) {
|
||||
$data = $file->loadFileData();
|
||||
$src = imagecreatefromstring($data);
|
||||
|
||||
|
@ -128,13 +128,35 @@ final class PhabricatorImageTransformer {
|
|||
$dx = max($size / 4, $scale * $x);
|
||||
$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);
|
||||
imagesavealpha($dst, true);
|
||||
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
|
||||
|
||||
$sdx = $scale * $x;
|
||||
$sdy = $scale * $y;
|
||||
|
||||
imagecopyresampled(
|
||||
$dst,
|
||||
$src,
|
||||
|
@ -153,10 +175,14 @@ final class PhabricatorImageTransformer {
|
|||
$data = $file->loadFileData();
|
||||
$img = imagecreatefromstring($data);
|
||||
$phabricator_root = dirname(phutil_get_library_root('phabricator'));
|
||||
$font_path = $phabricator_root.'/resources/font/tuffy.ttf';
|
||||
$white = imagecolorallocate($img, 255, 255, 255);
|
||||
$black = imagecolorallocate($img, 0, 0, 0);
|
||||
$border_width = 3;
|
||||
$font_root = $phabricator_root.'/resources/font/';
|
||||
$font_path = $font_root.'tuffy.ttf';
|
||||
if (Filesystem::pathExists($font_root.'impact.ttf')) {
|
||||
$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_min = 5;
|
||||
for ($i = $font_max; $i > $font_min; $i--) {
|
||||
|
@ -172,8 +198,8 @@ final class PhabricatorImageTransformer {
|
|||
$i,
|
||||
$x,
|
||||
$y,
|
||||
$white,
|
||||
$black,
|
||||
$text_color,
|
||||
$border_color,
|
||||
$border_width,
|
||||
$font_path,
|
||||
$upper_text);
|
||||
|
@ -191,8 +217,8 @@ final class PhabricatorImageTransformer {
|
|||
$i,
|
||||
$x,
|
||||
$y,
|
||||
$white,
|
||||
$black,
|
||||
$text_color,
|
||||
$border_color,
|
||||
$border_width,
|
||||
$font_path,
|
||||
$lower_text);
|
||||
|
|
|
@ -39,10 +39,10 @@ final class PhabricatorMacroEditController
|
|||
if (!strlen($macro->getName())) {
|
||||
$errors[] = pht('Macro name is required.');
|
||||
$e_name = pht('Required');
|
||||
} else if (!preg_match('/^[a-z0-9_-]{3,}$/', $macro->getName())) {
|
||||
$errors[] = pht('Macro must be at least three characters long and '.
|
||||
'contain only lowercase letters, digits, hyphen and '.
|
||||
'underscore.');
|
||||
} else if (!preg_match('/^[a-z0-9:_-]{3,}$/', $macro->getName())) {
|
||||
$errors[] = pht(
|
||||
'Macro must be at least three characters long and contain only '.
|
||||
'lowercase letters, digits, hyphens, colons and underscores.');
|
||||
$e_name = pht('Invalid');
|
||||
} else {
|
||||
$e_name = null;
|
||||
|
|
|
@ -22,8 +22,8 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication {
|
|||
return "\xE0\xBC\x84";
|
||||
}
|
||||
|
||||
public function shouldAppearInLaunchView() {
|
||||
return false;
|
||||
public function getApplicationGroup() {
|
||||
return self::GROUP_ADMIN;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
|
|
|
@ -13,15 +13,7 @@ final class PhabricatorApplicationDetailViewController
|
|||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$selected = null;
|
||||
$applications = PhabricatorApplication::getAllApplications();
|
||||
|
||||
foreach ($applications as $application) {
|
||||
if (get_class($application) == $this->application) {
|
||||
$selected = $application;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$selected = PhabricatorApplication::getByClass($this->application);
|
||||
|
||||
if (!$selected) {
|
||||
return new Aphront404Response();
|
||||
|
@ -35,36 +27,53 @@ final class PhabricatorApplicationDetailViewController
|
|||
->setName(pht('Applications'))
|
||||
->setHref($this->getApplicationURI()));
|
||||
|
||||
$properties = $this->buildPropertyView($selected);
|
||||
$actions = $this->buildActionView($user, $selected);
|
||||
$header = id(new PhabricatorHeaderView())
|
||||
->setHeader($title);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
array(
|
||||
$crumbs,
|
||||
id(new PhabricatorHeaderView())->setHeader($title),
|
||||
$actions,
|
||||
$properties,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
));
|
||||
$status_tag = id(new PhabricatorTagView())
|
||||
->setType(PhabricatorTagView::TYPE_STATE);
|
||||
|
||||
if ($selected->isInstalled()) {
|
||||
$status_tag->setName(pht('Installed'));
|
||||
$status_tag->setBackgroundColor(PhabricatorTagView::COLOR_GREEN);
|
||||
|
||||
} else {
|
||||
$status_tag->setName(pht('Uninstalled'));
|
||||
$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) {
|
||||
$properties = new PhabricatorPropertyListView();
|
||||
|
||||
if ($selected->isInstalled()) {
|
||||
$properties->addProperty(
|
||||
pht('Status'), pht('Installed'));
|
||||
|
||||
} else {
|
||||
$properties->addProperty(
|
||||
pht('Status'), pht('Uninstalled'));
|
||||
}
|
||||
|
||||
$properties->addProperty(
|
||||
pht('Description'), $selected->getShortDescription());
|
||||
$properties = id(new PhabricatorPropertyListView())
|
||||
->addProperty(
|
||||
pht('Description'), $selected->getShortDescription()
|
||||
);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -72,30 +81,41 @@ final class PhabricatorApplicationDetailViewController
|
|||
private function buildActionView(
|
||||
PhabricatorUser $user, PhabricatorApplication $selected) {
|
||||
|
||||
$view = id(new PhabricatorActionListView())
|
||||
->setUser($user);
|
||||
|
||||
if ($selected->canUninstall()) {
|
||||
if ($selected->isInstalled()) {
|
||||
|
||||
return id(new PhabricatorActionListView())
|
||||
->setUser($user)
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Uninstall'))
|
||||
->setIcon('delete')
|
||||
->setHref(
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Uninstall'))
|
||||
->setIcon('delete')
|
||||
->setWorkflow(true)
|
||||
->setHref(
|
||||
$this->getApplicationURI(get_class($selected).'/uninstall/'))
|
||||
);
|
||||
);
|
||||
} else {
|
||||
return id(new PhabricatorActionListView())
|
||||
->setUser($user)
|
||||
->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Install'))
|
||||
->setIcon('new')
|
||||
->setHref(
|
||||
$this->getApplicationURI(get_class($selected).'/install/'))
|
||||
);
|
||||
$view->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Install'))
|
||||
->setIcon('new')
|
||||
->setWorkflow(true)
|
||||
->setHref(
|
||||
$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() {
|
||||
$request = $this->getRequest();
|
||||
$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()) {
|
||||
$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 ($selected->canUninstall()) {
|
||||
$dialog->setTitle('Confirmation')
|
||||
->appendChild(
|
||||
'Install '. $selected->getName(). ' application ?'
|
||||
)
|
||||
->addSubmitButton('Install');
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->setTitle('Confirmation')
|
||||
->appendChild('Install '. $app_name. ' application ?')
|
||||
->addSubmitButton('Install')
|
||||
->addCancelButton('/applications/view/'.$this->application);
|
||||
} else {
|
||||
$dialog->setTitle('Information')
|
||||
->appendChild('You cannot install a installed application.');
|
||||
}
|
||||
} else {
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->setTitle('Confirmation')
|
||||
->appendChild('Really Uninstall '. $app_name. ' application ?')
|
||||
->addSubmitButton('Uninstall')
|
||||
->addCancelButton('/applications/view/'.$this->application);
|
||||
if ($selected->canUninstall()) {
|
||||
$dialog->setTitle('Confirmation')
|
||||
->appendChild(
|
||||
'Really Uninstall '. $selected->getName(). ' 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,24 @@ final class PhabricatorApplicationsListController
|
|||
private function buildInstalledApplicationsList(array $applications) {
|
||||
$list = new PhabricatorObjectItemListView();
|
||||
|
||||
$applications = msort($applications, 'getName');
|
||||
|
||||
foreach ($applications as $application) {
|
||||
$item = id(new PhabricatorObjectItemView())
|
||||
->setHeader($application->getName())
|
||||
->setHref('/applications/view/'.get_class($application).'/')
|
||||
->addAttribute($application->getShortDescription());
|
||||
|
||||
if (!$application->isInstalled()) {
|
||||
$item->addIcon('delete', pht('Uninstalled'));
|
||||
}
|
||||
|
||||
if ($application->isBeta()) {
|
||||
$item->addIcon('lint-warning', pht('Beta'));
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
|
|
@ -95,9 +95,8 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
|
|||
|
||||
$line_count = count(explode("\n", $paste->getContent()));
|
||||
$line_count = pht(
|
||||
'%2$s Line(s)',
|
||||
$line_count,
|
||||
PhutilTranslator::getInstance()->formatNumber($line_count));
|
||||
'%s Line(s)',
|
||||
new PhutilNumber($line_count));
|
||||
|
||||
$item = id(new PhabricatorObjectItemView())
|
||||
->setHeader($paste->getFullName())
|
||||
|
|
|
@ -58,7 +58,6 @@ final class PhabricatorApplicationPeople extends PhabricatorApplication {
|
|||
$item = new PhabricatorMenuItemView();
|
||||
$item->setName($user->getUsername());
|
||||
$item->setHref('/p/'.$user->getUsername().'/');
|
||||
$item->setSortOrder(0.0);
|
||||
$item->addClass('phabricator-core-menu-item-profile');
|
||||
|
||||
$classes = array(
|
||||
|
|
|
@ -13,19 +13,37 @@ final class PholioMockImagesView extends AphrontView {
|
|||
throw new Exception("Call setMock() before render()!");
|
||||
}
|
||||
|
||||
$mockview = array();
|
||||
|
||||
$file = head($this->mock->getImages())->getFile();
|
||||
|
||||
$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',
|
||||
array(
|
||||
'src' => $file->getBestURI(),
|
||||
'class' => 'pholio-mock-image',
|
||||
'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(
|
||||
'div',
|
||||
|
@ -35,10 +53,6 @@ final class PholioMockImagesView extends AphrontView {
|
|||
$main_image_tag);
|
||||
|
||||
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();
|
||||
foreach ($this->mock->getImages() as $image) {
|
||||
$thumbfile = $image->getFile();
|
||||
|
@ -51,7 +65,7 @@ final class PholioMockImagesView extends AphrontView {
|
|||
'class' => 'pholio-mock-carousel-thumbnail',
|
||||
'meta' => array(
|
||||
'fullSizeURI' => $thumbfile->getBestURI(),
|
||||
'imageID' => $image->getID(),
|
||||
'imageID' => $image->getID()
|
||||
),
|
||||
));
|
||||
$thumbnails[] = $tag;
|
||||
|
@ -67,5 +81,4 @@ final class PholioMockImagesView extends AphrontView {
|
|||
|
||||
return $this->renderHTMLView($mockview);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -266,11 +266,14 @@ final class PhabricatorRepositorySvnCommitChangeParserWorker
|
|||
if (empty($raw_paths[$full_from]) &&
|
||||
empty($effects[$full_from])) {
|
||||
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(
|
||||
'rawPath' => $full_from,
|
||||
'rawTargetPath' => null,
|
||||
'rawTargetCommit' => null,
|
||||
'rawDirect' => true,
|
||||
'rawDirect' => false,
|
||||
|
||||
'changeType' => $other_type,
|
||||
'fileType' => $from_file_type,
|
||||
|
|
|
@ -161,7 +161,8 @@ final class PhabricatorSearchEngineMySQL extends PhabricatorSearchEngine {
|
|||
if (strlen($q)) {
|
||||
$join[] = qsprintf(
|
||||
$conn_r,
|
||||
"{$t_field} field ON field.phid = document.phid");
|
||||
'%T field ON field.phid = document.phid',
|
||||
$t_field);
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)',
|
||||
|
|
|
@ -44,7 +44,6 @@ final class PhabricatorApplicationSettings extends PhabricatorApplication {
|
|||
$item->setIcon('settings');
|
||||
$item->setSelected($selected);
|
||||
$item->setHref('/settings/');
|
||||
$item->setSortOrder(0.90);
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ arguments.
|
|||
= Overview =
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -35,7 +35,9 @@ final class CelerityPhabricatorResourceController
|
|||
|
||||
protected function buildResourceTransformer() {
|
||||
$xformer = new CelerityResourceTransformer();
|
||||
$xformer->setMinify(PhabricatorEnv::getEnvConfig('celerity.minify'));
|
||||
$xformer->setMinify(
|
||||
!PhabricatorEnv::getEnvConfig('phabricator.developer-mode') &&
|
||||
PhabricatorEnv::getEnvConfig('celerity.minify'));
|
||||
$xformer->setCelerityMap(CelerityResourceMap::getInstance());
|
||||
return $xformer;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ abstract class CelerityResourceController extends PhabricatorController {
|
|||
}
|
||||
|
||||
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
|
||||
// field since we never change what resource is served by a given URI.
|
||||
return $this->makeResponseCacheable(new Aphront304Response());
|
||||
|
|
|
@ -74,7 +74,7 @@ final class CelerityResourceTransformer {
|
|||
$bin = $root.'/externals/javelin/support/jsxmin/jsxmin';
|
||||
|
||||
if (@file_exists($bin)) {
|
||||
$future = new ExecFuture("{$bin} __DEV__:0");
|
||||
$future = new ExecFuture('%s __DEV__:0', $bin);
|
||||
$future->write($data);
|
||||
list($err, $result) = $future->resolve();
|
||||
if (!$err) {
|
||||
|
|
|
@ -167,9 +167,9 @@ abstract class PhabricatorBaseEnglishTranslation
|
|||
'%d Lint Messages',
|
||||
),
|
||||
|
||||
'This is a binary file. It is %2$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 %2$s bytes in length.',
|
||||
'This is a binary file. It is %s byte(s) in length.' => array(
|
||||
'This is a binary file. It is %s byte in length.',
|
||||
'This is a binary file. It is %s bytes in length.',
|
||||
),
|
||||
|
||||
'%d Action(s) Have No Effect' => array(
|
||||
|
@ -226,9 +226,9 @@ abstract class PhabricatorBaseEnglishTranslation
|
|||
),
|
||||
),
|
||||
|
||||
'%2$s Line(s)' => array(
|
||||
'%2$s Line',
|
||||
'%2$s Lines',
|
||||
'%s Line(s)' => array(
|
||||
'%s Line',
|
||||
'%s Lines',
|
||||
),
|
||||
|
||||
"Indexing %d object(s) of type %s." => array(
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @deprecated Use javelin_tag().
|
||||
*/
|
||||
function javelin_render_tag(
|
||||
$tag,
|
||||
array $attributes = array(),
|
||||
|
|
|
@ -187,9 +187,7 @@ final class PhabricatorJavelinLinter extends ArcanistLinter {
|
|||
}
|
||||
|
||||
private function newSymbolsFuture($path) {
|
||||
$javelinsymbols = 'javelinsymbols';
|
||||
|
||||
$future = new ExecFuture($javelinsymbols.' # '.escapeshellarg($path));
|
||||
$future = new ExecFuture('javelinsymbols # %s', $path);
|
||||
$future->write($this->getData($path));
|
||||
return $future;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,10 @@ final class PhabricatorRemarkupRuleEmbedFile
|
|||
case 'thumb':
|
||||
default:
|
||||
$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';
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ final class PhabricatorRemarkupRuleImageMacro
|
|||
|
||||
public function apply($text) {
|
||||
return preg_replace_callback(
|
||||
'@^([a-zA-Z0-9_\-]+)$@m',
|
||||
'@^([a-zA-Z0-9:_\-]+)$@m',
|
||||
array($this, 'markupImageMacro'),
|
||||
$text);
|
||||
}
|
||||
|
|
|
@ -121,7 +121,10 @@ final class AphrontSideNavFilterView extends AphrontView {
|
|||
}
|
||||
|
||||
public function addCustomBlock($block) {
|
||||
$this->menu->appendChild($block);
|
||||
$this->menu->addMenuItem(
|
||||
id(new PhabricatorMenuItemView())
|
||||
->setType(PhabricatorMenuItemView::TYPE_CUSTOM)
|
||||
->appendChild($block));
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@ final class PhabricatorMenuItemView extends AphrontTagView {
|
|||
const TYPE_SPACER = 'type-spacer';
|
||||
const TYPE_LABEL = 'type-label';
|
||||
const TYPE_BUTTON = 'type-button';
|
||||
const TYPE_CUSTOM = 'type-custom';
|
||||
|
||||
private $name;
|
||||
private $href;
|
||||
private $type = self::TYPE_LINK;
|
||||
private $isExternal;
|
||||
private $key;
|
||||
private $sortOrder = 1.0;
|
||||
private $icon;
|
||||
private $selected;
|
||||
|
||||
|
@ -88,15 +88,6 @@ final class PhabricatorMenuItemView extends AphrontTagView {
|
|||
return $this->isExternal;
|
||||
}
|
||||
|
||||
public function setSortOrder($order) {
|
||||
$this->sortOrder = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSortOrder() {
|
||||
return $this->sortOrder;
|
||||
}
|
||||
|
||||
protected function getTagName() {
|
||||
return $this->href ? 'a' : 'div';
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ final class PhabricatorMenuView extends AphrontTagView {
|
|||
|
||||
private $items = array();
|
||||
|
||||
protected function canAppendChild() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function newLabel($name) {
|
||||
$item = id(new PhabricatorMenuItemView())
|
||||
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
||||
|
@ -37,13 +41,83 @@ final class PhabricatorMenuView extends AphrontTagView {
|
|||
}
|
||||
|
||||
public function addMenuItem(PhabricatorMenuItemView $item) {
|
||||
$key = $item->getKey();
|
||||
$this->items[] = $item;
|
||||
$this->appendChild($item);
|
||||
return $this->addMenuItemAfter(null, $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;
|
||||
}
|
||||
|
||||
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) {
|
||||
$key = (string)$key;
|
||||
|
||||
|
@ -83,4 +157,8 @@ final class PhabricatorMenuView extends AphrontTagView {
|
|||
'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">'.
|
||||
$framebust.
|
||||
'window.__DEV__=1;'.
|
||||
'window.__DEV__='.
|
||||
(PhabricatorEnv::getEnvConfig('phabricator.developer-mode')
|
||||
? '1'
|
||||
: '0').
|
||||
';'.
|
||||
'</script>',
|
||||
|
||||
$response->renderResourcesOfType('css'),
|
||||
|
|
|
@ -256,7 +256,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
|||
}
|
||||
|
||||
$developer_warning = null;
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.show-error-callout') &&
|
||||
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode') &&
|
||||
DarkConsoleErrorLogPluginAPI::getErrors()) {
|
||||
$developer_warning = phutil_tag(
|
||||
'div',
|
||||
|
|
|
@ -5,7 +5,6 @@ final class PhabricatorMainMenuIconView extends AphrontView {
|
|||
private $classes = array();
|
||||
private $href;
|
||||
private $name;
|
||||
private $sortOrder = 0.5;
|
||||
private $workflow;
|
||||
private $style;
|
||||
|
||||
|
@ -42,22 +41,6 @@ final class PhabricatorMainMenuIconView extends AphrontView {
|
|||
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() {
|
||||
$name = $this->getName();
|
||||
$href = $this->getHref();
|
||||
|
|
|
@ -164,7 +164,6 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
$controller = $this->getController();
|
||||
|
||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||
$applications = msort($applications, 'getName');
|
||||
|
||||
$core = array();
|
||||
$more = array();
|
||||
|
@ -184,7 +183,7 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
if ($application->getApplicationGroup() == $group_core) {
|
||||
$core[] = $item;
|
||||
} else {
|
||||
$more[] = $item;
|
||||
$more[$application->getName()] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +199,9 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
$view->addClass('phabricator-core-menu');
|
||||
|
||||
$search = $this->renderSearch();
|
||||
$view->appendChild($search);
|
||||
if ($search) {
|
||||
$view->addMenuItem($search);
|
||||
}
|
||||
|
||||
$view
|
||||
->newLabel(pht('Home'))
|
||||
|
@ -235,7 +236,6 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
}
|
||||
|
||||
if ($actions) {
|
||||
$actions = msort($actions, 'getSortOrder');
|
||||
$view->addMenuItem(
|
||||
id(new PhabricatorMenuItemView())
|
||||
->addClass('phabricator-core-item-device')
|
||||
|
@ -260,6 +260,7 @@ final class PhabricatorMainMenuView extends AphrontView {
|
|||
->addClass('phabricator-core-item-device')
|
||||
->setType(PhabricatorMenuItemView::TYPE_LABEL)
|
||||
->setName(pht('More Applications')));
|
||||
ksort($more);
|
||||
foreach ($more as $item) {
|
||||
$item->addClass('phabricator-core-item-device');
|
||||
$view->addMenuItem($item);
|
||||
|
|
|
@ -18,6 +18,19 @@
|
|||
}
|
||||
|
||||
.pholio-mock-image {
|
||||
margin: 10px 0px;
|
||||
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 {
|
||||
margin-left: 1em;
|
||||
margin-left: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.phabricator-header-tags .phabricator-tag-view {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
|
|
@ -6,13 +6,55 @@
|
|||
* javelin-dom
|
||||
* javelin-stratcom
|
||||
* javelin-behavior-device
|
||||
* javelin-vector
|
||||
* phabricator-tooltip
|
||||
*/
|
||||
|
||||
JX.behavior('differential-populate', function(config) {
|
||||
|
||||
function onresponse(target, response) {
|
||||
JX.DOM.replace(JX.$(target), JX.$H(response.changeset));
|
||||
function onresponse(target_id, response) {
|
||||
// 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) {
|
||||
for (var k in response.coverage) {
|
||||
try {
|
||||
|
|
|
@ -2,8 +2,19 @@
|
|||
* @provides javelin-behavior-pholio-mock-view
|
||||
* @requires javelin-behavior
|
||||
* javelin-stratcom
|
||||
* javelin-dom
|
||||
* javelin-vector
|
||||
* javelin-event
|
||||
*/
|
||||
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(
|
||||
'click', // Listen for clicks...
|
||||
'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 main = JX.$(config.mainID);
|
||||
JX.Stratcom.addData(
|
||||
main,
|
||||
{
|
||||
fullSizeURI: data['fullSizeURI'],
|
||||
imageID: data['imageID']
|
||||
});
|
||||
|
||||
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