Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2895417
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
102 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/docs/contributor/javascript_coding_standards.diviner b/src/docs/contributor/javascript_coding_standards.diviner
index 83772918c3..7bff8a3bdf 100644
--- a/src/docs/contributor/javascript_coding_standards.diviner
+++ b/src/docs/contributor/javascript_coding_standards.diviner
@@ -1,140 +1,140 @@
@title Javascript Coding Standards
@group standards
This document describes Javascript coding standards for Phabricator and Javelin.
= Overview =
This document outlines technical and style guidelines which are followed in
Phabricator and Javelin. Contributors should also follow these guidelines. Many
of these guidelines are automatically enforced by lint.
These guidelines are essentially identical to the Facebook guidelines, since I
basically copy-pasted them. If you are already familiar with the Facebook
guidelines, you can probably get away with skimming this document.
= Spaces, Linebreaks and Indentation =
- Use two spaces for indentation. Don't use literal tab characters.
- Use Unix linebreaks ("\n"), not MSDOS ("\r\n") or OS9 ("\r").
- Use K&R style braces and spacing.
- Put a space after control keywords like ##if## and ##for##.
- Put a space after commas in argument lists.
- Put space around operators like ##=##, ##<##, etc.
- Don't put spaces after function names.
- Parentheses should hug their contents.
- Generally, prefer to wrap code at 80 columns.
= Case and Capitalization =
The Javascript language unambiguously dictates casing/naming rules; follow those
rules.
- Name variables using ##lowercase_with_underscores##.
- Name classes using ##UpperCamelCase##.
- Name methods and properties using ##lowerCamelCase##.
- Name global functions using ##lowerCamelCase##. Avoid defining global
functions.
- Name constants using ##UPPERCASE##.
- Write ##true##, ##false##, and ##null## in lowercase.
- "Internal" methods and properties should be prefixed with an underscore.
For more information about what "internal" means, see
**Leading Underscores**, below.
= Comments =
- Strongly prefer ##//## comments for making comments inside the bodies of
functions and methods (this lets someone easily comment out a block of code
while debugging later).
= Javascript Language =
- Use ##[]## and ##{}##, not ##new Array## and ##new Object##.
- When creating an object literal, do not quote keys unless required.
= Examples =
**if/else:**
lang=js
if (x > 3) {
// ...
} else if (x === null) {
// ...
} else {
// ...
}
You should always put braces around the body of an if clause, even if it is only
one line. Note that operators like ##>## and ##===## are also surrounded by
spaces.
**for (iteration):**
lang=js
for (var ii = 0; ii < 10; ii++) {
// ...
}
Prefer ii, jj, kk, etc., as iterators, since they're easier to pick out
visually and react better to "Find Next..." in editors.
**for (enumeration):**
lang=js
for (var k in obj) {
// ...
}
Make sure you use enumeration only on Objects, not on Arrays. For more details,
see @{article:Javascript Object and Array}.
**switch:**
lang=js
switch (x) {
case 1:
// ...
break;
case 2:
if (flag) {
break;
}
break;
default:
// ...
break;
}
-##break## statements should be indented to block level. If you don't push them
+`break` statements should be indented to block level. If you don't push them
in, you end up with an inconsistent rule for conditional ##break## statements,
as in the ##2## case.
If you insist on having a "fall through" case that does not end with ##break##,
make it clear in a comment that you wrote this intentionally. For instance:
lang=js
switch (x) {
case 1:
// ...
// Fall through...
case 2:
//...
break;
}
= Leading Underscores =
By convention, methods names which start with a leading underscore are
considered "internal", which (roughly) means "private". The critical difference
is that this is treated as a signal to Javascript processing scripts that a
symbol is safe to rename since it is not referenced outside the current file.
The upshot here is:
- name internal methods which shouldn't be called outside of a file's scope
with a leading underscore; and
- **never** call an internal method from another file.
If you treat them as though they were "private", you won't run into problems.
diff --git a/src/docs/contributor/php_coding_standards.diviner b/src/docs/contributor/php_coding_standards.diviner
index 93957845ce..a180a2ff3a 100644
--- a/src/docs/contributor/php_coding_standards.diviner
+++ b/src/docs/contributor/php_coding_standards.diviner
@@ -1,170 +1,170 @@
@title PHP Coding Standards
@group standards
This document describes PHP coding standards for Phabricator and related
projects (like Arcanist and libphutil).
= Overview =
This document outlines technical and style guidelines which are followed in
libphutil. Contributors should also follow these guidelines. Many of these
guidelines are automatically enforced by lint.
These guidelines are essentially identical to the Facebook guidelines, since I
basically copy-pasted them. If you are already familiar with the Facebook
guidelines, you probably don't need to read this super thoroughly.
= Spaces, Linebreaks and Indentation =
- Use two spaces for indentation. Don't use tab literal characters.
- Use Unix linebreaks ("\n"), not MSDOS ("\r\n") or OS9 ("\r").
- Use K&R style braces and spacing.
- Put a space after control keywords like ##if## and ##for##.
- Put a space after commas in argument lists.
- Put a space around operators like ##=##, ##<##, etc.
- Don't put spaces after function names.
- Parentheses should hug their contents.
- Generally, prefer to wrap code at 80 columns.
= Case and Capitalization =
- Name variables and functions using ##lowercase_with_underscores##.
- Name classes using ##UpperCamelCase##.
- Name methods and properties using ##lowerCamelCase##.
- Use uppercase for common acronyms like ID and HTML.
- Name constants using ##UPPERCASE##.
- Write ##true##, ##false## and ##null## in lowercase.
= Comments =
- Do not use "#" (shell-style) comments.
- Prefer "//" comments inside function and method bodies.
= PHP Language Style =
- Use "<?php", not the "<?" short form. Omit the closing "?>" tag.
- Prefer casts like ##(string)## to casting functions like ##strval()##.
- Prefer type checks like ##$v === null## to type functions like
##is_null()##.
- Avoid all crazy alternate forms of language constructs like "endwhile"
and "<>".
- Always put braces around conditional and loop blocks.
= PHP Language Features =
- Use PHP as a programming language, not a templating language.
- Avoid globals.
- Avoid extract().
- Avoid eval().
- Avoid variable variables.
- Prefer classes over functions.
- Prefer class constants over defines.
- Avoid naked class properties; instead, define accessors.
- Use exceptions for error conditions.
- Use type hints, use `assert_instances_of()` for arrays holding objects.
= Examples =
**if/else:**
if ($some_variable > 3) {
// ...
} else if ($some_variable === null) {
// ...
} else {
// ...
}
You should always put braces around the body of an if clause, even if it is only
one line long. Note spaces around operators and after control statements. Do not
use the "endif" construct, and write "else if" as two words.
**for:**
for ($ii = 0; $ii < 10; $ii++) {
// ...
}
Prefer $ii, $jj, $kk, etc., as iterators, since they're easier to pick out
visually and react better to "Find Next..." in editors.
**foreach:**
foreach ($map as $key => $value) {
// ...
}
**switch:**
switch ($value) {
case 1:
// ...
break;
case 2:
if ($flag) {
// ...
break;
}
break;
default:
// ...
break;
}
-##break## statements should be indented to block level.
+`break` statements should be indented to block level.
**array literals:**
$junk = array(
'nuts',
'bolts',
'refuse',
);
Use a trailing comma and put the closing parenthesis on a separate line so that
diffs which add elements to the array affect only one line.
**operators:**
$a + $b; // Put spaces around operators.
$omg.$lol; // Exception: no spaces around string concatenation.
$arr[] = $element; // Couple [] with the array when appending.
$obj = new Thing(); // Always use parens.
**function/method calls:**
// One line
eject($cargo);
// Multiline
AbstractFireFactoryFactoryEngine::promulgateConflagrationInstance(
$fuel,
$ignition_source);
**function/method definitions:**
function example_function($base_value, $additional_value) {
return $base_value + $additional_value;
}
class C {
public static function promulgateConflagrationInstance(
IFuel $fuel,
IgnitionSource $source) {
// ...
}
}
**class:**
class Dog extends Animal {
const CIRCLES_REQUIRED_TO_LIE_DOWN = 3;
private $favoriteFood = 'dirt';
public function getFavoriteFood() {
return $this->favoriteFood;
}
}
diff --git a/src/docs/contributor/using_oauthserver.diviner b/src/docs/contributor/using_oauthserver.diviner
index 9587d23410..dad9b63b68 100644
--- a/src/docs/contributor/using_oauthserver.diviner
+++ b/src/docs/contributor/using_oauthserver.diviner
@@ -1,120 +1,120 @@
@title Using the Phabricator OAuth Server
@group developer
How to use the Phabricator OAuth Server.
= Overview =
Phabricator includes an OAuth Server which supports the
-##Authorization Code Grant## flow as described in the OAuth 2.0
+`Authorization Code Grant` flow as described in the OAuth 2.0
specification:
http://tools.ietf.org/html/draft-ietf-oauth-v2-23
This functionality can allow clients to integrate with a given
Phabricator instance in a secure way with granular data access.
For example, Phabricator can be used as a central identity store for any
clients that implement OAuth 2.0.
= Vocabulary =
- **Access token** - a token which allows a client to ask for data on
behalf of a resource owner. A given client will only be able to access
data included in the scope(s) the resource owner authorized that client for.
- **Authorization code** - a short-lived code which allows an authenticated
client to ask for an access token on behalf of some resource owner.
- **Client** - this is the application or system asking for data from the
OAuth Server on behalf of the resource owner.
- **Resource owner** - this is the user the client and OAuth Server are
concerned with on a given request.
- **Scope** - this defines a specific piece of granular data a client can
or can not access on behalf of a user. For example, if authorized for the
"whoami" scope on behalf of a given resource owner, the client can get the
results of Conduit.whoami for that resource owner when authenticated with
a valid access token.
= Setup - Creating a Client =
# Visit https://phabricator.example.com/oauthserver/client/create/
# Fill out the form
# Profit
= Obtaining an Authorization Code =
POST or GET https://phabricator.example.com/oauthserver/auth/ with the
following parameters:
- Required - **client_id** - the id of the newly registered client.
- Required - **response_type** - the desired type of authorization code
response. Only code is supported at this time.
- Optional - **redirect_uri** - override the redirect_uri the client
registered. This redirect_uri must have the same fully-qualified domain
and have at least the same query parameters as the redirect_uri the client
registered, as well as have no fragments.
- Optional - **scope** - specify what scope(s) the client needs access to
in a space-delimited list.
- Optional - **state** - an opaque value the client can send to the server
for programmatic excellence. Some clients use this value to implement XSRF
protection or for debugging purposes.
If done correctly and the resource owner has not yet authorized the client
for the desired scope, then the resource owner will be presented with an
interface to authorize the client for the desired scope. The OAuth Server
will redirect to the pertinent redirect_uri with an authorization code or
an error indicating the resource owner did not authorize the client, depending.
If done correctly and the resource owner has already authorized the client for
the desired scope, then the OAuth Server will redirect to the pertinent
redirect_uri with a valid authorization code.
If there is an error, the OAuth Server will return a descriptive error
message. This error will be presented to the resource owner on the
Phabricator domain if there is reason to believe there is something fishy
with the client. For example, if there is an issue with the redirect_uri.
Otherwise, the OAuth Server will redirect to the pertinent redirect_uri
and include the pertinent error information.
= Obtaining an Access Token =
POST or GET https://phabricator.example.com/oauthserver/token/
with the following parameters:
- Required - **client_id** - the id of the client
- Required - **client_secret** - the secret of the client.
This is used to authenticate the client.
- Required - **code** - the authorization code obtained earlier.
- Required - **grant_type** - the desired type of access grant.
Only token is supported at this time.
- Optional - **redirect_uri** - should be the exact same redirect_uri as
the redirect_uri specified to obtain the authorization code. If no
redirect_uri was specified to obtain the authorization code then this
should not be specified.
If done correctly, the OAuth Server will redirect to the pertinent
redirect_uri with an access token.
If there is an error, the OAuth Server will return a descriptive error
message.
= Using an Access Token =
Simply include a query param with the key of "access_token" and the value
as the earlier obtained access token. For example:
https://phabricator.example.com/api/user.whoami?access_token=ykc7ly7vtibj334oga4fnfbuvnwz4ocp
If the token has expired or is otherwise invalid, the client will receive
an error indicating as such. In these cases, the client should re-initiate
the entire ##Authorization Code Grant## flow.
NOTE: See "Scopes" section below for more information on what data is
currently exposed through the OAuth Server.
= Scopes =
There are only two scopes supported at this time.
- **offline_access** - allows an access token to work indefinitely without
expiring.
- **whoami** - allows the client to access the results of Conduit.whoami on
behalf of the resource owner.
diff --git a/src/docs/flavor/javascript_pitfalls.diviner b/src/docs/flavor/javascript_pitfalls.diviner
index fb9af3c197..beef1b83e6 100644
--- a/src/docs/flavor/javascript_pitfalls.diviner
+++ b/src/docs/flavor/javascript_pitfalls.diviner
@@ -1,87 +1,87 @@
@title Javascript Pitfalls
@group javascript
This document discusses pitfalls and flaws in the Javascript language, and how
to avoid, work around, or at least understand them.
= Implicit Semicolons =
Javascript tries to insert semicolons if you forgot them. This is a pretty
horrible idea. Notably, it can mask syntax errors by transforming subexpressions
on their own lines into statements with no effect:
lang=js
string = "Here is a fairly long string that does not fit on one "
"line. Note that I forgot the string concatenation operators "
"so this will compile and execute with the wrong behavior. ";
Here's what ECMA262 says about this:
When, as the program is parsed ..., a token ... is encountered that is not
allowed by any production of the grammar, then a semicolon is automatically
inserted before the offending token if one or more of the following conditions
is true: ...
To protect yourself against this "feature", don't use it. Always explicitly
insert semicolons after each statement. You should also prefer to break lines in
places where insertion of a semicolon would not make the unparseable parseable,
usually after operators.
= ##with## is Bad News =
-##with## is a pretty bad feature, for this reason among others:
+`with` is a pretty bad feature, for this reason among others:
with (object) {
property = 3; // Might be on object, might be on window: who knows.
}
Avoid ##with##.
= ##arguments## is not an Array =
You can convert ##arguments## to an array using JX.$A() or similar. Note that
you can pass ##arguments## to Function.prototype.apply() without converting it.
= Object, Array, and iteration are needlessly hard =
There is essentially only one reasonable, consistent way to use these primitives
but it is not obvious. Navigate these troubled waters with
@{article:Javascript Object and Array}.
= typeof null == "object" =
This statement is true in Javascript:
typeof null == 'object'
This is pretty much a bug in the language that can never be fixed now.
= Number, String, and Boolean objects =
Like Java, Javascript has primitive versions of number, string, and boolean,
and object versions. In Java, there's some argument for this distinction. In
Javascript, it's pretty much completely worthless and the behavior of these
objects is wrong. String and Boolean in particular are essentially unusable:
lang=js
"pancake" == "pancake"; // true
new String("pancake") == new String("pancake"); // false
var b = new Boolean(false);
b; // Shows 'false' in console.
!b; // ALSO shows 'false' in console.
!b == b; // So this is true!
!!b == !b // Negate both sides and it's false! FUCK!
if (b) {
// Better fucking believe this will get executed.
}
There is no advantage to using the object forms (the primitive forms behave like
objects and can have methods and properties, and inherit from Array.prototype,
Number.prototype, etc.) and their logical behavior is at best absurd and at
worst strictly wrong.
**Never use** ##new Number()##, ##new String()## or ##new Boolean()## unless
your Javascript is God Tier and you are absolutely sure you know what you are
doing.
diff --git a/src/docs/flavor/php_pitfalls.diviner b/src/docs/flavor/php_pitfalls.diviner
index c51b459910..2b8373f8cc 100644
--- a/src/docs/flavor/php_pitfalls.diviner
+++ b/src/docs/flavor/php_pitfalls.diviner
@@ -1,301 +1,301 @@
@title PHP Pitfalls
@group php
This document discusses difficult traps and pitfalls in PHP, and how to avoid,
work around, or at least understand them.
= array_merge() in Incredibly Slow When Merging A List of Arrays =
If you merge a list of arrays like this:
COUNTEREXAMPLE
$result = array();
foreach ($list_of_lists as $one_list) {
$result = array_merge($result, $one_list);
}
...your program now has a huge runtime because it generates a large number of
intermediate arrays and copies every element it has previously seen each time
you iterate.
In a libphutil environment, you can use @{function@libphutil:array_mergev}
instead.
= var_export() Hates Baby Animals =
If you try to var_export() an object that contains recursive references, your
program will terminate. You have no chance to intercept or react to this or
otherwise stop it from happening. Avoid var_export() unless you are certain
you have only simple data. You can use print_r() or var_dump() to display
complex variables safely.
= isset(), empty() and Truthiness =
A value is "truthy" if it evaluates to true in an ##if## clause:
$value = something();
if ($value) {
// Value is truthy.
}
If a value is not truthy, it is "falsey". These values are falsey in PHP:
null // null
0 // integer
0.0 // float
"0" // string
"" // empty string
false // boolean
array() // empty array
Disregarding some bizarre edge cases, all other values are truthy. Note that
because "0" is falsey, this sort of thing (intended to prevent users from making
empty comments) is wrong in PHP:
COUNTEREXAMPLE
if ($comment_text) {
make_comment($comment_text);
}
This is wrong because it prevents users from making the comment "0". //THIS
COMMENT IS TOTALLY AWESOME AND I MAKE IT ALL THE TIME SO YOU HAD BETTER NOT
BREAK IT!!!// A better test is probably strlen().
In addition to truth tests with ##if##, PHP has two special truthiness operators
which look like functions but aren't: empty() and isset(). These operators help
deal with undeclared variables.
In PHP, there are two major cases where you get undeclared variables -- either
you directly use a variable without declaring it:
COUNTEREXAMPLE
function f() {
if ($not_declared) {
// ...
}
}
...or you index into an array with an index which may not exist:
COUNTEREXAMPLE
function f(array $mystery) {
if ($mystery['stuff']) {
// ...
}
}
When you do either of these, PHP issues a warning. Avoid these warnings by using
empty() and isset() to do tests that are safe to apply to undeclared variables.
empty() evaluates truthiness exactly opposite of if(). isset() returns true for
everything except null. This is the truth table:
VALUE if() empty() isset()
null false true false
0 false true true
0.0 false true true
"0" false true true
"" false true true
false false true true
array() false true true
EVERYTHING ELSE true false true
The value of these operators is that they accept undeclared variables and do not
issue a warning. Specifically, if you try to do this you get a warning:
COUNTEREXAMPLE
if ($not_previously_declared) { // PHP Notice: Undefined variable!
// ...
}
But these are fine:
if (empty($not_previously_declared)) { // No notice, returns true.
// ...
}
if (isset($not_previously_declared)) { // No notice, returns false.
// ...
}
So, isset() really means is_declared_and_is_set_to_something_other_than_null().
empty() really means is_falsey_or_is_not_declared(). Thus:
- If a variable is known to exist, test falsiness with if (!$v), not empty().
In particular, test for empty arrays with if (!$array). There is no reason
to ever use empty() on a declared variable.
- When you use isset() on an array key, like isset($array['key']), it will
evaluate to "false" if the key exists but has the value null! Test for index
existence with array_key_exists().
Put another way, use isset() if you want to type "if ($value !== null)" but are
testing something that may not be declared. Use empty() if you want to type
"if (!$value)" but you are testing something that may not be declared.
= usort(), uksort(), and uasort() are Slow =
This family of functions is often extremely slow for large datasets. You should
avoid them if at all possible. Instead, build an array which contains surrogate
keys that are naturally sortable with a function that uses native comparison
(e.g., sort(), asort(), ksort(), or natcasesort()). Sort this array instead, and
use it to reorder the original array.
In a libphutil environment, you can often do this easily with
@{function@libphutil:isort} or @{function@libphutil:msort}.
= array_intersect() and array_diff() are Also Slow =
These functions are much slower for even moderately large inputs than
array_intersect_key() and array_diff_key(), because they can not make the
assumption that their inputs are unique scalars as the ##key## varieties can.
Strongly prefer the ##key## varieties.
= array_uintersect() and array_udiff() are Definitely Slow Too =
These functions have the problems of both the ##usort()## family and the
-##array_diff()## family. Avoid them.
+`array_diff()` family. Avoid them.
= foreach() Does Not Create Scope =
Variables survive outside of the scope of foreach(). More problematically,
references survive outside of the scope of foreach(). This code mutates
-##$array## because the reference leaks from the first loop to the second:
+`$array` because the reference leaks from the first loop to the second:
COUNTEREXAMPLE
$array = range(1, 3);
echo implode(',', $array); // Outputs '1,2,3'
foreach ($array as &$value) {}
echo implode(',', $array); // Outputs '1,2,3'
foreach ($array as $value) {}
echo implode(',', $array); // Outputs '1,2,2'
The easiest way to avoid this is to avoid using foreach-by-reference. If you do
use it, unset the reference after the loop:
foreach ($array as &$value) {
// ...
}
unset($value);
= unserialize() is Incredibly Slow on Large Datasets =
The performance of unserialize() is nonlinear in the number of zvals you
unserialize, roughly O(N^2).
zvals approximate time
10000 5ms
100000 85ms
1000000 8,000ms
10000000 72 billion years
= call_user_func() Breaks References =
If you use call_use_func() to invoke a function which takes parameters by
reference, the variables you pass in will have their references broken and will
emerge unmodified. That is, if you have a function that takes references:
function add_one(&$v) {
$v++;
}
...and you call it with call_user_func():
COUNTEREXAMPLE
$x = 41;
call_user_func('add_one', $x);
...##$x## will not be modified. The solution is to use call_user_func_array()
and wrap the reference in an array:
$x = 41;
call_user_func_array(
'add_one',
array(&$x)); // Note '&$x'!
This will work as expected.
= You Can't Throw From __toString() =
If you throw from __toString(), your program will terminate uselessly and you
won't get the exception.
= An Object Can Have Any Scalar as a Property =
Object properties are not limited to legal variable names:
$property = '!@#$%^&*()';
$obj->$property = 'zebra';
echo $obj->$property; // Outputs 'zebra'.
So, don't make assumptions about property names.
= There is an (object) Cast =
You can cast a dictionary into an object.
$obj = (object)array('flavor' => 'coconut');
echo $obj->flavor; // Outputs 'coconut'.
echo get_class($obj); // Outputs 'stdClass'.
This is occasionally useful, mostly to force an object to become a Javascript
dictionary (vs a list) when passed to json_encode().
= Invoking "new" With an Argument Vector is Really Hard =
If you have some ##$class_name## and some ##$argv## of constructor
arguments and you want to do this:
new $class_name($argv[0], $argv[1], ...);
...you'll probably invent a very interesting, very novel solution that is very
wrong. In a libphutil environment, solve this problem with
@{function@libphutil:newv}. Elsewhere, copy newv()'s implementation.
= Equality is not Transitive =
This isn't terribly surprising since equality isn't transitive in a lot of
languages, but the == operator is not transitive:
$a = ''; $b = 0; $c = '0a';
$a == $b; // true
$b == $c; // true
$c == $a; // false!
When either operand is an integer, the other operand is cast to an integer
before comparison. Avoid this and similar pitfalls by using the === operator,
which is transitive.
= All 676 Letters in the Alphabet =
This doesn't do what you'd expect it to do in C:
for ($c = 'a'; $c <= 'z'; $c++) {
// ...
}
This is because the successor to 'z' is 'aa', which is "less than" 'z'. The
loop will run for ~700 iterations until it reaches 'zz' and terminates. That is,
-##$c## will take on these values:
+`$c` will take on these values:
a
b
...
y
z
aa // loop continues because 'aa' <= 'z'
ab
...
mf
mg
...
zw
zx
zy
zz // loop now terminates because 'zz' > 'z'
Instead, use this loop:
foreach (range('a', 'z') as $c) {
// ...
}
diff --git a/src/docs/tech/celerity.diviner b/src/docs/tech/celerity.diviner
index d864f1b437..91a53d6e8d 100644
--- a/src/docs/tech/celerity.diviner
+++ b/src/docs/tech/celerity.diviner
@@ -1,62 +1,62 @@
@title Celerity Technical Documentation
@group celerity
Technical overview of the Celerity system.
= Overview =
Celerity is a static resource (CSS and JS) management system, which handles:
- Keeping track of which resources a page needs.
- Generating URIs for the browser to access resources.
- Managing dependencies between resources.
- Packaging resources into fewer HTTP requests for performance.
- Preprocessing resources (e.g., stripping comments and whitespace).
- Delivering resources and managing resource cache lifetimes.
- Interfacing with the client to manage resources.
Celerity is an outgrowth of the //Haste// system at Facebook. You can find more
information about Celerity here:
- @{article:Things You Should Do Soon: Static Resources} describes the history
and context of the system and the problems it solves.
- @{article:Adding New CSS and JS} provides a developer guide to using
Celerity.
= Class Relationships =
Celerity's primary API is @{function:require_celerity_resource}, which marks a
resource for inclusion when a response is rendered (e.g., when the HTML page is
generated, or when the response to an Ajax request is built). For instance, if
you use a CSS class like "widget-view", you must ensure the appropriate CSS is
included by calling ##require_celerity_resource('widget-view-css')## (or
similar), at your use site.
This function uses @{class:CelerityAPI} to access the active
@{class:CelerityStaticResourceResponse} and tells it that it needs to include
the resource later, when the response actually gets built. (This layer of
indirection provides future-proofing against certain complex situations Facebook
eventually encountered).
When the time comes to render the response, the page renderer uses
@{class:CelerityAPI} to access the active
@{class:CelerityStaticResourceResponse} and requests that it render out
appropriate references to CSS and JS resources. It uses
@{class:CelerityResourceMap} to determine the dependencies for the requested
resources (so you only have to explicitly include what you're actually using,
and not all of its dependencies) and any packaging rules (so it may be able to
generate fewer resource requests, improving performance). It then generates
-##<script />## and ##<link />## references to these resources.
+`<script />` and `<link />` references to these resources.
These references point at ##/res/## URIs, which are handled by
@{class:CelerityResourceController}. It responds to these requests and delivers
the relevant resources and packages, managing cache lifetimes and handling any
neessary preprocessing. It uses @{class:CelerityResourceMap} to locate resources
and read packaging rules.
The dependency and packaging maps are generated by ##bin/celerity map##,
which updates ##resources/celerity/map.php##.
@{class:CelerityStaticResourceResponse} also manages some Javelin information,
and @{function:celerity_generate_unique_node_id} uses this metadata to provide
a better uniqueness guarantee when generating unique node IDs.
diff --git a/src/docs/tech/chatbot.diviner b/src/docs/tech/chatbot.diviner
index 263f9433b4..e37cedf8cf 100644
--- a/src/docs/tech/chatbot.diviner
+++ b/src/docs/tech/chatbot.diviner
@@ -1,89 +1,89 @@
@title Chat Bot Technical Documentation
@group bot
Configuring and extending the chat bot.
= Overview =
Phabricator includes a simple chat bot daemon, which is primarily intended as
an example of how you can write an external script that interfaces with
Phabricator over Conduit and does some kind of useful work. If you use IRC or
another supported chat protocol, you can also have the bot hang out in your
channel.
NOTE: The chat bot is somewhat experimental and not very mature.
= Configuring the Bot =
The bot reads a JSON configuration file. You can find an example in:
resources/chatbot/example_config.json
These are the configuration values it reads:
- ##server## String, required, the server to connect to.
- ##port## Int, optional, the port to connect to (defaults to 6667).
- ##ssl## Bool, optional, whether to connect via SSL or not (defaults to
false).
- ##nick## String, nickname to use.
- ##user## String, optional, username to use (defaults to ##nick##).
- ##pass## String, optional, password for server.
- ##nickpass## String, optional, password for NickServ.
- ##join## Array, list of channels to join.
- ##handlers## Array, list of handlers to run. These are like plugins for the
bot.
- ##conduit.uri##, ##conduit.user##, ##conduit.cert## Conduit configuration,
see below.
- ##notification.channels## Notification configuration, see below.
= Handlers =
You specify a list of "handlers", which are basically plugins or modules for
the bot. These are the default handlers available:
- @{class:PhabricatorBotObjectNameHandler} This handler looks for users
mentioning Phabricator objects like "T123" and "D345" in chat, looks them
up, and says their name with a link to the object. Requires conduit.
- @{class:PhabricatorBotFeedNotificationHandler} This handler posts
notifications about changes to revisions to the channels listed in
##notification.channels##.
- @{class:PhabricatorBotLogHandler} This handler records chatlogs which can
be browsed in the Phabricator web interface.
- @{class:PhabricatorBotSymbolHandler} This handler posts responses to lookups
for symbols in Diffusion
- @{class:PhabricatorBotMacroHandler} This handler looks for users mentioning
macros, if found will convert image to ASCII and output in chat. Configure
with ##macro.size## and ##macro.aspect##
You can also write your own handlers, by extending
@{class:PhabricatorBotHandler}.
= Conduit =
Some handlers (e.g., @{class:PhabricatorBotObjectNameHandler}) need to read data
from Phabricator over Conduit, Phabricator's HTTP API. You can use this method
to allow other scripts or programs to access Phabricator's data from different
servers and in different languages.
To allow the bot to access Conduit, you need to create a user that it can login
with. To do this, login to Phabricator as an administrator and go to
-##People -> Create New Account##. Create a new account and flag them as a
+`People -> Create New Account`. Create a new account and flag them as a
"Bot/Script". Then in your configuration file, set these parameters:
- ##conduit.uri## The URI for your Phabricator install, like
##http://phabricator.example.com/##
- ##conduit.user## The username your bot should login to Phabricator with --
whatever you selected above, like ##phabot##.
- ##conduit.cert## The user's certificate, from the "Conduit Certificate" tab
in the user's administrative view.
Now the bot should be able to connect to Phabricator via Conduit.
= Starting the Bot =
The bot is a Phabricator daemon, so start it with ##phd##:
./bin/phd launch phabricatorbot <absolute_path_to_config_file>
If you have issues you can try ##debug## instead of ##launch##, see
@{article:Managing Daemons with phd} for more information.
diff --git a/src/docs/tech/conduit.diviner b/src/docs/tech/conduit.diviner
index b64f086c02..12af1d6175 100644
--- a/src/docs/tech/conduit.diviner
+++ b/src/docs/tech/conduit.diviner
@@ -1,57 +1,57 @@
@title Conduit Technical Documentation
@group conduit
Technical overview of the Conduit API.
= Overview =
Conduit is an informal mechanism for transferring ad-hoc JSON blobs around on
the internet.
Theoretically, it provides an API to Phabricator so external scripts (including
scripts written in other languages) can interface with the applications in the
Phabricator suite. It technically does this, sort of, but it is unstable and
incomplete so you should keep your expectations very low if you choose to build
things on top of it.
NOTE: Hopefully, this should improve over time, but making Conduit more robust
isn't currently a major project priority because there isn't much demand for it
outside of internal scripts. If you want to use Conduit to build things on top
of Phabricator, let us know so we can adjust priorities.
Conduit provides an authenticated HTTP API for Phabricator. It is informal and
extremely simple: you post a JSON blob and you get a JSON blob back. You can
access Conduit in PHP with @{class@libphutil:ConduitClient}, or in any language
by executing ##arc call-conduit method## (see ##arc help call-conduit## for
more information). You can see and test available methods at ##/conduit/## in
the web interface.
Arcanist is implemented using Conduit, and @{class:PhabricatorIRCBot} is
intended as a practical example of how to write a program which interfaces with
Phabricator over Conduit.
= Class Relationships =
The primary Conduit workflow is exposed at ##/api/##, which routes to
@{class:PhabricatorConduitAPIController}. This controller builds a
@{class:ConduitAPIRequest} representing authentication information and POST
parameters, instantiates an appropriate subclass of @{class:ConduitAPIMethod},
and passes the request to it. Subclasses of @{class:ConduitAPIMethod} implement
the actual methods which Conduit exposes.
Conduit calls which fail throw @{class:ConduitException}, which the controller
handles.
There is a web interface for viewing and testing Conduit called the "Conduit
Console", implemented by @{class:PhabricatorConduitConsoleController} at
-##/conduit/##.
+`/conduit/`.
A log of connections and calls is stored by
@{class:PhabricatorConduitConnectionLog} and
@{class:PhabricatorConduitMethodCallLog}, and can be accessed on the web via
@{class:PhabricatorConduitLogController} at ##/conduit/log/##.
Conduit provides a token-based handshake mechanism used by
-##arc install-certificate## at ##/conduit/token/##, implemented by
+`arc install-certificate` at `/conduit/token/`, implemented by
@{class:PhabricatorConduitTokenController} which stores generated tokens using
@{class:PhabricatorConduitCertificateToken}.
diff --git a/src/docs/tech/files.diviner b/src/docs/tech/files.diviner
index a7fccfd982..b0708d4aac 100644
--- a/src/docs/tech/files.diviner
+++ b/src/docs/tech/files.diviner
@@ -1,39 +1,39 @@
@title File Storage Technical Documentation
@group filestorage
Phabricator file storage details.
= Overview =
Phabricator has a simple, general-purpose file storage system with configurable
storage backends that allows you to choose where files are stored. For a user
guide, see @{article:Configuring File Storage}.
= Class Relationships =
@{class:PhabricatorFile} holds file metadata (name, author, phid), including an
identifier for a @{class:PhabricatorFileStorageEngine} where the actual file
data is stored, and a data handle which identifies the data within that storage
engine.
When writing data, a @{class:PhabricatorFileStorageEngineSelector} is
instantiated (by default, @{class:PhabricatorDefaultFileStorageEngineSelector},
but you can change this by setting the ##storage.engine-selector## key in your
configuration). The selector returns a list of satisfactory
@{class:PhabricatorFileStorageEngine}s, in order of preference.
For instance, suppose the user is uploading a picture. The upload pipeline would
instantiate the configured selector, which might return a
@{class:PhabricatorMySQLFileStorageEngine} and a
@{class:PhabricatorLocalDiskFileStorageEngine}, indicating that the picture may
be stored in either storage engine but MySQL is preferred. If a given storage
engine fails to perform the write, it will fall back to the next engine.
= Adding New Storage Engines =
To add a new storage engine, extend @{class:PhabricatorFileStorageEngine}. In
order to make files actually get written to it, you also need to extend
@{class:PhabricatorFileStorageEngineSelector}, provide an implementation which
selects your storage engine for whatever files you want to store there, and then
configure Phabricator to use your selector by setting
-##storage.engine-selector##.
+`storage.engine-selector`.
diff --git a/src/docs/user/configuration/configuring_accounts_and_registration.diviner b/src/docs/user/configuration/configuring_accounts_and_registration.diviner
index 35104b1858..8a4c59b193 100644
--- a/src/docs/user/configuration/configuring_accounts_and_registration.diviner
+++ b/src/docs/user/configuration/configuring_accounts_and_registration.diviner
@@ -1,69 +1,69 @@
@title Configuring Accounts and Registration
@group config
Describes how to configure user access to Phabricator.
= Overview =
Phabricator supports a number of login systems. You can enable or disable these
systems to configure who can register for and access your install, and how users
with existing accounts can login.
Methods of logging in are called **Authentication Providers**. For example,
there is a "Username/Password" authentication provider available, which allows
users to log in with a traditional username and password. Other providers
support logging in with other credentials. For example:
- **Username/Password:** Users use a username and password to log in or
register.
- **LDAP:** Users use LDAP credentials to log in or register.
- **OAuth:** Users use accounts on a supported OAuth2 provider (like
GitHub, Facebook, or Google) to log in or register.
- **Other Providers:** More providers are available, and Phabricator
can be extended with custom providers. See the "Auth" application for
a list of available providers.
By default, no providers are enabled. You must use the "Auth" application to
add one or more providers after you complete the installation process.
After you add a provider, you can link it to existing accounts (for example,
associate an existing Phabricator account with a GitHub OAuth account) or users
can use it to register new accounts (assuming you enable these options).
= Recovering Administrator Accounts =
If you accidentally lock yourself out of Phabricator, you can use the `bin/auth`
script to recover access to an administrator account. To recover access, run:
phabricator/ $ ./bin/auth recover <username>
...where `<username>` is the admin account username you want to recover access
to. This will give you a link which will log you in as the specified
administrative user.
= Managing Accounts with the Web Console =
To manage accounts from the web, login as an administrator account and go to
-##/people/## or click "People" on the homepage. Provided you're an admin,
+`/people/` or click "People" on the homepage. Provided you're an admin,
you'll see options to create or edit accounts.
= Manually Creating New Accounts =
There are two ways to manually create new accounts: via the web UI using
the "People" application (this is easiest), or via the CLI using the
`accountadmin` binary (this has a few more options).
To use the CLI script, run:
phabricator/ $ ./bin/accountadmin
Some options (like setting passwords and changing certain account flags) are
only available from the CLI. You can also use this script to make a user
an administrator (if you accidentally remove your admin flag) or create an
administrative account.
= Next Steps =
Continue by:
- returning to the @{article:Configuration Guide}.
diff --git a/src/docs/user/configuration/configuring_inbound_email.diviner b/src/docs/user/configuration/configuring_inbound_email.diviner
index 48e25e499d..ba268eb7cc 100644
--- a/src/docs/user/configuration/configuring_inbound_email.diviner
+++ b/src/docs/user/configuration/configuring_inbound_email.diviner
@@ -1,252 +1,252 @@
@title Configuring Inbound Email
@group config
This document contains instructions for configuring inbound email, so users
may update Differential and Maniphest by replying to messages and create
Maniphest tasks via email.
= Preamble =
This can be extremely difficult to configure correctly. This is doubly true if
you use a local MTA.
There are a few approaches available:
| Receive Mail With | Setup | Cost | Notes |
|--------|-------|------|-------|
| Mailgun | Easy | Cheap | Recommended |
| SendGrid | Easy | Cheap | |
| Local MTA | Extremely Difficult | Free | Strongly discouraged! |
The remainder of this document walks through configuring Phabricator to
receive mail, and then configuring your chosen transport to deliver mail
to Phabricator.
= Configuring Phabricator =
By default, Phabricator uses a `noreply@phabricator.example.com` email address
as the 'From' (configurable with `metamta.default-address`) and sets
'Reply-To' to the user generating the email (e.g., by making a comment), if the
mail was generated by a user action. This means that users can reply (or
reply-all) to email to discuss changes, but the conversation won't be recorded
in Phabricator and users will not be able to take actions like claiming tasks or
requesting changes to revisions.
To change this behavior so that users can interact with objects in Phabricator
over email, set these configuration keys:
- ##metamta.differential.reply-handler-domain##: enables email replies for
Differential.
- ##metamta.maniphest.reply-handler-domain##: enables email replies for
Maniphest.
Set these keys to some domain which you configure according to the instructions
below, e.g. `phabricator.example.com`. You can set these both to the same
domain, and will generally want to. Once you set these keys, emails will use a
'Reply-To' like `T123+273+af310f9220ad@example.com`, which -- when
configured correctly, according to the instructions below -- will parse incoming
email and allow users to interact with Maniphest tasks and Differential
revisions over email.
If you don't want Phabricator to take up an entire domain (or subdomain) you
can configure a general prefix so you can use a single mailbox to receive mail
on. To make use of this set `metamta.single-reply-handler-prefix` to the
prefix of your choice, and Phabricator will prepend this to the 'Reply-To'
mail address. This works because everything up to the first (optional) '+'
character in an email-address is considered the receiver, and everything
after is essentially ignored.
You can also set up a task creation email address, like `bugs@example.com`,
which will create a Maniphest task out of any email which is set to it. To do
this, set `metamta.maniphest.public-create-email` in your configuration. This
has some mild security implications, see below.
= Security =
The email reply channel is "somewhat" authenticated. Each reply-to address is
unique to the recipient and includes a hash of user information and a unique
object ID, so it can only be used to update that object and only be used to act
on behalf of the recipient.
However, if an address is leaked (which is fairly easy -- for instance,
forwarding an email will leak a live reply address, or a user might take a
screenshot), //anyone// who can send mail to your reply-to domain may interact
with the object the email relates to as the user who leaked the mail. Because
the authentication around email has this weakness, some actions (like accepting
revisions) are not permitted over email.
This implementation is an attempt to balance utility and security, but makes
some sacrifices on both sides to achieve it because of the difficulty of
authenticating senders in the general case (e.g., where you are an open source
project and need to interact with users whose email accounts you have no control
over).
If you leak a bunch of reply-to addresses by accident, you can change
`phabricator.mail-key` in your configuration to invalidate all the old hashes.
You can also set `metamta.public-replies`, which will change how Phabricator
delivers email. Instead of sending each recipient a unique mail with a personal
reply-to address, it will send a single email to everyone with a public reply-to
address. This decreases security because anyone who can spoof a "From" address
can act as another user, but increases convenience if you use mailing lists and,
practically, is a reasonable setting for many installs. The reply-to address
will still contain a hash unique to the object it represents, so users who have
not received an email about an object can not blindly interact with it.
If you enable `metamta.maniphest.public-create-email`, that address also uses
the weaker "From" authentication mechanism.
NOTE: Phabricator does not currently attempt to verify "From" addresses because
this is technically complex, seems unreasonably difficult in the general case,
and no installs have had a need for it yet. If you have a specific case where a
reasonable mechanism exists to provide sender verification (e.g., DKIM
signatures are sufficient to authenticate the sender under your configuration,
or you are willing to require all users to sign their email), file a feature
request.
= Testing and Debugging Inbound Email =
You can use the `bin/mail` utility to test and review inbound mail. This can
help you determine if mail is being delivered to Phabricator or not:
phabricator/ $ ./bin/mail list-inbound # List inbound messages.
phabricator/ $ ./bin-mail show-inbound # Show details about a message.
You can also test receiving mail, but note that this just simulates receiving
the mail and doesn't send any information over the network. It is
primarily aimed at developing email handlers: it will still work properly
if your inbound email configuration is incorrect or even disabled.
phabricator/ $ ./bin/mail receive-test # Receive test message.
Run `bin/mail help <command>` for detailed help on using these commands.
= Mailgun Setup =
To use Mailgun, you need a Mailgun account. You can sign up at
<http://www.mailgun.com>. Provided you have such an account, configure it
like this:
- Configure a mail domain according to Mailgun's instructions.
- Add a Mailgun route with a `catch_all()` rule which takes the action
`forward("https://phabricator.example.com/mail/mailgun/")`. Replace the
example domain with your actual domain.
= SendGrid Setup =
To use SendGrid, you need a SendGrid account with access to the "Parse API" for
inbound email. Provided you have such an account, configure it like this:
- Configure an MX record according to SendGrid's instructions, i.e. add
##phabricator.example.com MX 10 mx.sendgrid.net.## or similar.
- Go to the "Parse Incoming Emails" page on SendGrid
(<http://sendgrid.com/developer/reply>) and add the domain as the
"Hostname".
- Add the URL ##https://phabricator.example.com/mail/sendgrid/## as the "Url",
using your domain (and HTTP instead of HTTPS if you are not configured with
SSL).
- If you get an error that the hostname "can't be located or verified", it
means your MX record is either incorrectly configured or hasn't propagated
yet.
- Set ##metamta.maniphest.reply-handler-domain## and/or
##metamta.differential.reply-handler-domain## to
"##phabricator.example.com##" (whatever you configured the MX record for),
depending on whether you want to support email replies for Maniphest,
Differential, or both.
That's it! If everything is working properly you should be able to send email
to ##anything@phabricator.example.com## and it should appear in
`bin/mail list-inbound` within a few seconds.
= Local MTA: Installing Mailparse =
If you're going to run your own MTA, you need to install the PECL mailparse
extension. In theory, you can do that with:
$ sudo pecl install mailparse
You may run into an error like "needs mbstring". If so, try:
$ sudo yum install php-mbstring # or equivalent
$ sudo pecl install -n mailparse
If you get a linker error like this:
COUNTEREXAMPLE
PHP Warning: PHP Startup: Unable to load dynamic library
'/usr/lib64/php/modules/mailparse.so' - /usr/lib64/php/modules/mailparse.so:
undefined symbol: mbfl_name2no_encoding in Unknown on line 0
...you need to edit your php.ini file so that mbstring.so is loaded **before**
mailparse.so. This is not the default if you have individual files in
-##php.d/##.
+`php.d/`.
= Local MTA: Configuring Sendmail =
Before you can configure Sendmail, you need to install Mailparse. See the
section "Installing Mailparse" above.
Sendmail is very difficult to configure. First, you need to configure it for
your domain so that mail can be delivered correctly. In broad strokes, this
probably means something like this:
- add an MX record;
- make sendmail listen on external interfaces;
- open up port 25 if necessary (e.g., in your EC2 security policy);
- add your host to /etc/mail/local-host-names; and
- restart sendmail.
Now, you can actually configure sendmail to deliver to Phabricator. In
-##/etc/aliases##, add an entry like this:
+`/etc/aliases`, add an entry like this:
phabricator: "| /path/to/phabricator/scripts/mail/mail_handler.php <ENV>"
...where <ENV> is the PHABRICATOR_ENV the script should run under. Run
-##sudo newaliases##. Now you likely need to symlink this script into
-##/etc/smrsh/##:
+`sudo newaliases`. Now you likely need to symlink this script into
+`/etc/smrsh/`:
sudo ln -s /path/to/phabricator/scripts/mail/mail_handler.php /etc/smrsh/
Finally, edit ##/etc/mail/virtusertable## and add an entry like this:
@yourdomain.com phabricator@localhost
That will forward all mail to @yourdomain.com to the Phabricator processing
script. Run ##sudo /etc/mail/make## or similar and then restart sendmail with
-##sudo /etc/init.d/sendmail restart##.
+`sudo /etc/init.d/sendmail restart`.
= Local MTA: Configuring Lamson =
Before you can configure Lamson, you need to install Mailparse. See the section
"Installing Mailparse" above.
In contrast to Sendmail, Lamson is relatively easy to configure. It is fairly
minimal, and is suitable for a development or testing environment. Lamson
listens for incoming SMTP mails and passes the content directly to Phabricator.
To get started, follow the provided instructions
(<http://lamsonproject.org/docs/getting_started.html>) to set up an instance.
One likely deployment issue is that binding to port 25 requires root
privileges. Lamson is capable of starting as root then dropping privileges, but
you must supply ##-uid## and ##-gid## arguments to do so, as demonstrated by
Step 8 in Lamson's deployment tutorial (located here:
<http://lamsonproject.org/docs/deploying_oneshotblog.html>).
The Lamson handler code itself is very concise; it merely needs to pass the
content of the email to Phabricator:
import logging, subprocess
from lamson.routing import route, stateless
from lamson import view
PHABRICATOR_ROOT = "/path/to/phabricator"
PHABRICATOR_ENV = "custom/myconf"
LOGGING_ENABLED = True
@route("(address)@(host)", address=".+")
@stateless
def START(message, address=None, host=None):
if LOGGING_ENABLED:
logging.debug("%s", message.original)
process = subprocess.Popen([PHABRICATOR_ROOT + "scripts/mail/mail_handler.php",PHABRICATOR_ENV],stdin=subprocess.PIPE)
process.communicate(message.original)
diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner
index cfde51569e..a66e78e8bf 100644
--- a/src/docs/user/configuration/custom_fields.diviner
+++ b/src/docs/user/configuration/custom_fields.diviner
@@ -1,199 +1,207 @@
@title Configuring Custom Fields
@group config
How to add custom fields to applications which support them.
= Overview =
Several Phabricator applications allow the configuration of custom fields. These
fields allow you to add more information to objects, and in some cases reorder
or remove builtin fields.
For example, you could use custom fields to add an "Estimated Hours" field to
tasks, a "Lead" field to projects, or a "T-Shirt Size" field to users.
These applications currently support custom fields:
| Application | Support |
|-------------|---------|
| Maniphest | Full Support |
| Projects | Full Support |
| People | Full Support |
| Differential | Partial Support |
| Diffusion | Limited Support |
Custom fields can appear in many interfaces and support search, editing, and
other features.
= Basic Custom Fields =
To get started with custom fields, you can use configuration to select and
reorder fields and to add new simple fields.
If you don't need complicated display controls or sophisticated validation,
these simple fields should cover most use cases. They allow you to attach
things like strings, numbers, and dropdown menus to objects.
The relevant configuration settings are:
| Application | Add Fields | Select Fields |
|-------------|------------|---------------|
| Maniphest | `maniphest.custom-field-definitions` | `maniphest.fields` |
| Projects | `projects.custom-field-definitions` | `projects.fields` |
| People | `user.custom-field-definitions` | `user.fields` |
| Differential | Planned | `differential.fields` |
| Diffusion | Planned | Planned |
When adding fields, you'll specify a JSON blob like this (for example, as the
value of `maniphest.custom-field-definitions`):
{
"mycompany:estimated-hours": {
"name": "Estimated Hours",
"type": "int",
"caption": "Estimated number of hours this will take.",
"required": true
},
"mycompany:actual-hours": {
"name": "Actual Hours",
"type": "int",
"caption": "Actual number of hours this took."
},
+ "mycompany:company-jobs": {
+ "name": "Job Role",
+ "type: "select",
+ "options": {
+ "mycompany:engineer": "Engineer",
+ "mycompany:nonengineer": "Other"
+ }
+ },
"mycompany:favorite-dinosaur": {
"name": "Favorite Dinosaur",
"type": "text"
- }
+ },
}
The fields will then appear in the other config option for the application
(for example, in `maniphest.fields`) and you can enable, disable, or reorder
them.
For details on how to define a field, see the next section.
= Custom Field Configuration =
When defining custom fields using a configuration option like
`maniphest.custom-field-definitions`, these options are available:
- **name**: Display label for the field on the edit and detail interfaces.
- **description**: Optional text shown when managing the field.
- **type**: Field type. The supported field types are:
- **int**: An integer, rendered as a text field.
- **text**: A string, rendered as a text field.
- **bool**: A boolean value, rendered as a checkbox.
- - **select**: Allows the user to select from several options, rendered
- as a dropdown.
+ - **select**: Allows the user to select from several options as defined
+ by **options**, rendered as a dropdown.
- **remarkup**: A text area which allows the user to enter markup.
- **users**: A typeahead which allows multiple users to be input.
- **date**: A date/time picker.
- **header**: Renders a visual divider which you can use to group fields.
- **edit**: Show this field on the application's edit interface (this
defaults to `true`).
- **view**: Show this field on the application's view interface (this
defaults to `true`).
- **search**: Show this field on the application's search interface, allowing
users to filter objects by the field value.
- **caption**: A caption to display underneath the field (optional).
- **required**: True if the user should be required to provide a value.
- **options**: If type is set to **select**, provide options for the dropdown
as a dictionary.
- **default**: Default field value.
- **strings**: Allows you to override specific strings based on the field
type. See below.
- **instructions**: Optional block of remarkup text which will appear
above the control when rendered on the edit view.
- **placeholder**: A placeholder text that appears on text boxes. Only
supported in text, int and remarkup fields (optional).
The `strings` value supports different strings per control type. They are:
- **bool**
- **edit.checkbox** Text for the edit interface, no default.
- **view.yes** Text for the view interface, defaults to "Yes".
- **search.default** Text for the search interface, defaults to "(Any)".
- **search.require** Text for the search interface, defaults to "Require".
Some applications have specific options which only work in that application.
In **Maniphest**:
- **copy**: When a user creates a task, the UI gives them an option to
"Create Another Similar Task". Some fields from the original task are copied
into the new task, while others are not; by default, fields are not copied.
If you want this field to be copied, specify `true` for the `copy` property.
Internally, Phabricator implements some additional custom field types and
options. These are not intended for general use and are subject to abrupt
change, but are documented here for completeness:
- **Credentials**: Controls with type `credential` allow selection of a
Passphrase credential which provides `credential.provides`, and creation
of credentials of `credential.type`.
= Advanced Custom Fields =
If you want custom fields to have advanced behaviors (sophisticated rendering,
advanced validation, complicated controls, interaction with other systems, etc),
you can write a custom field as an extension and add it to Phabricator.
NOTE: This API is somewhat new and fairly large. You should expect that there
will be occasional changes to the API requiring minor updates in your code.
To do this, extend the appropriate `CustomField` class for the application you
want to add a field to:
| Application | Extend |
|-------------|---------|
| Maniphest | @{class:ManiphestCustomField} |
| Projects | @{class:PhabricatorProjectCustomField} |
| People | @{class:PhabricatorUserCustomField} |
| Differential | @{class:DifferentialCustomField} |
| Diffusion | @{class:PhabricatorCommitCustomField} |
The easiest way to get started is to drop your subclass into
`phabricator/src/extensions/`, which should make it immediately available in the
UI (if you use APC, you may need to restart your webserver). For example, this
is a simple template which adds a custom field to Maniphest:
name=ExampleManiphestCustomField.php
<?php
final class ExampleCustomField extends ManiphestCustomField {
public function getFieldKey() {
return 'example:test';
}
public function shouldAppearInPropertyView() {
return true;
}
public function renderPropertyViewLabel() {
return pht('Example Custom Field');
}
public function renderPropertyViewValue(array $handles) {
return phutil_tag(
'h1',
array(
'style' => 'color: #ff00ff',
),
pht('It worked!'));
}
}
Broadly, you can then add features by overriding more methods and implementing
them. Many of the native fields are implemented on the custom field
architecture, and it may be useful to look at them. For details on available
integrations, see the base class for your application and
@{class:PhabricatorCustomField}.
= Next Steps =
Continue by:
- learning more about extending Phabricator with custom code in
@{article:libphutil Libraries User Guide};
- or returning to the @{article: Configuration Guide}.
diff --git a/src/docs/user/installation_guide.diviner b/src/docs/user/installation_guide.diviner
index b9adeb6bcf..77e858ddd8 100644
--- a/src/docs/user/installation_guide.diviner
+++ b/src/docs/user/installation_guide.diviner
@@ -1,173 +1,173 @@
@title Installation Guide
@group intro
This document contains basic install instructions to get Phabricator up and
running.
= Installation Requirements =
You will need **a computer**. Options include:
- **A Normal Computer**: This is strongly recommended. Many installs use a VM
in EC2. Phabricator installs properly and works well on a normal computer.
- **A Shared Host**: This may work, but is not recommended. Many shared
hosting environments have restrictions which prevent some of Phabricator's
features from working. Consider using a normal computer instead.
- **A SAN Appliance, Network Router, Gaming Console, Raspberry Pi, etc.**:
Although you may be able to install Phabricator on specialized hardware, it
is unlikely to work well and will be difficult for us to support. Strongly
consider using a normal computer instead.
- **A Toaster, Car, Firearm, Thermostat, etc.**: Yes, many modern devices now
have embedded computing capability. We live in interesting times. However,
you should not install Phabricator on these devices. Instead, install it on
a normal computer.
To install the Phabricator server software, you will need an **operating
system** on your normal computer which is **not Windows**. Note that **the
command line interface //does// work on Windows**, and **you can //use//
Phabricator from any operating system with a web browser**. However, the server
software does not run on Windows. It does run on most other operating systems,
so choose one of these instead:
- **Linux**: Most installs use Linux.
- **Mac OS X**: Mac OS X is an acceptable flavor of Linux.
- **FreeBSD**: While FreeBSD is certainly not a flavor of Linux, it is a fine
operating system possessed of many desirable qualities, and Phabricator will
install and run properly on FreeBSD.
- **Solaris, etc.**: Other systems which look like Linux and quack like Linux
will generally work fine, although we may suffer a reduced ability to
support and resolve issues on unusual operating systems.
Beyond an operating system, you will need **a webserver**.
- **Apache**: Many installs use Apache + `mod_php`.
- **nginx**: Many installs use nginx + `php-fpm`.
- **lighttpd**: `lighttpd` is less popular than Apache or nginx, but it
works fine.
- **Other**: Other webservers which can run PHP are also likely to work fine,
although these installation instructions will not cover how to set them up.
- **PHP Builtin Server**: You can use the builtin PHP webserver for
development or testing, although it should not be used in production.
You will also need:
- **MySQL**: You need MySQL.
- **PHP**: You need PHP 5.2 or newer.
You'll probably also need a **domain name**. In particular, you should read this
note:
NOTE: Phabricator must be installed on an entire domain. You can not install it
to a path on an existing domain, like `example.com/phabricator/`. Instead,
install it to an entire domain or subdomain, like `phabricator.example.com`.
= Installing Required Components =
If you are installing on Ubuntu or an RedHat derivative, there are install
scripts available which should handle most of the things discussed in this
document for you:
- **RedHat Derivatives**:
<http://www.phabricator.com/rsrc/install/install_rhel-derivs.sh>
- **Ubuntu**: <http://www.phabricator.com/rsrc/install/install_ubuntu.sh>
If those work for you, you can skip directly to the
@{article:Configuration Guide}. These scripts are also available in the
-##scripts/install## directory in the project itself.
+`scripts/install` directory in the project itself.
Otherwise, here's a general description of what you need to install:
- git (usually called "git" in package management systems)
- Apache (usually "httpd" or "apache2") (or nginx)
- MySQL Server (usually "mysqld" or "mysql-server")
- PHP (usually "php")
- Required PHP extensions: mbstring, iconv, mysql (or mysqli), curl, pcntl
(these might be something like "php-mysql" or "php5-mysql")
- Optional PHP extensions: gd, apc (special instructions for APC are available
below if you have difficulty installing it), xhprof (instructions below,
you only need this if you are developing Phabricator)
If you already have LAMP setup, you've probably already got everything you need.
It may also be helpful to refer to the install scripts above, even if they don't
work for your system.
Now that you have all that stuff installed, grab Phabricator and its
dependencies:
$ cd somewhere/ # pick some install directory
somewhere/ $ git clone https://github.com/phacility/libphutil.git
somewhere/ $ git clone https://github.com/phacility/arcanist.git
somewhere/ $ git clone https://github.com/phacility/phabricator.git
= Installing APC (Optional) =
Like everything else written in PHP, Phabricator will run much faster with APC
installed. You likely need to install "pcre-devel" first:
sudo yum install pcre-devel
Then you have two options. Either install via PECL (try this first):
sudo yum install php-pear
sudo pecl install apc
**If that doesn't work**, grab the package from PECL directly and follow the
build instructions there:
http://pecl.php.net/package/APC
Installing APC is optional but **strongly recommended**, especially on
production hosts.
Once APC is installed, test that it is available by running:
php -i | grep apc
If it doesn't show up, add:
extension=apc.so
..to "/etc/php.d/apc.ini" or the "php.ini" file indicated by "php -i".
= Installing XHProf (Optional) =
XHProf is a PHP profiling tool. You don't need to install it unless you are
developing Phabricator and making performance changes.
You can install xhprof with:
$ pecl install xhprof
If you have a PEAR version prior to 1.9.3, you may run into a `phpize` failure.
If so, you can download the source and build it with:
$ cd extension/
$ phpize
$ ./configure
$ make
$ sudo make install
You may also need to add "##extension=xhprof.so##" to your php.ini.
See <https://bugs.php.net/bug.php?id=59747> for more information.
= Updating Phabricator =
Since Phabricator is under active development, you should update frequently. To
update Phabricator:
- Stop the webserver (including `php-fpm`, if you use it).
- Run `git pull` in `libphutil/`, `arcanist/` and `phabricator/`.
- Run `phabricator/bin/storage upgrade`.
- Restart the webserver (and `php-fpm`, if you stopped it earlier).
For more details, see @{article:Configuration Guide}. You can use a script
similar to this one to automate the process:
http://www.phabricator.com/rsrc/install/update_phabricator.sh
= Next Steps =
Continue by:
- configuring Phabricator with the @{article:Configuration Guide}.
diff --git a/src/docs/user/userguide/arcanist_lint_unit.diviner b/src/docs/user/userguide/arcanist_lint_unit.diviner
index 3b12de9dac..e738c42ffb 100644
--- a/src/docs/user/userguide/arcanist_lint_unit.diviner
+++ b/src/docs/user/userguide/arcanist_lint_unit.diviner
@@ -1,92 +1,92 @@
@title Arcanist User Guide: Customizing Lint, Unit Tests and Workflows
@group userguide
Explains how to build new classes to control how Arcanist behaves.
This is a configuration guide that helps you set up advanced features. If you're
just getting started, you don't need to look at this yet. Instead, start with
the @{article:Arcanist User Guide}.
= Overview =
Arcanist has some basic configuration options available in the `.arcconfig`
file (see @{article:Arcanist User Guide: Configuring a New Project}), but it
can't handle everything. If you want to customize Arcanist at a deeper level,
you need to build new classes. For instance:
- if you want to configure linters, or add new linters, you need to create a
new class which extends @{class@arcanist:ArcanistLintEngine}.
- if you want to integrate with a unit testing framework, you need to create a
new class which extends @{class@arcanist:ArcanistUnitTestEngine}.
- if you you want to change how workflows behave, or add new workflows, you
need to create a new class which extends
@{class@arcanist:ArcanistConfiguration}.
Arcanist works through a sort of dependency-injection approach. For example,
Arcanist does not run lint rules by default, but you can set `lint.engine`
in your `.arcconfig` to the name of a class which extends
@{class@arcanist:ArcanistLintEngine}. When running from inside your project,
Arcanist will load this class and call methods on it in order to run lint. To
make this work, you need to do three things:
- actually write the class;
- add the library where the class exists to your ##.arcconfig##;
- add the class name to your ##.arcconfig## as the **lint.engine**,
**unit.engine**, or **arcanist_configuration**.
= Create a libphutil Library =
If you haven't created a library for the class to live in yet, you need to do
that first. Follow the instructions in
@{article:libphutil Libraries User Guide}, then make the library loadable by
adding it to your ##.arcconfig## like this:
{
// ...
"load" : [
// ...
"/path/to/my/library", // Absolute path
"support/arcanist", // Relative path in this project
// ...
]
// ...
}
You can either specify an absolute path, or a path relative to the project root.
When you run ##arc list --trace##, you should see a message to the effect that
it has loaded your library.
For debugging or testing, you can also run Arcanist with the
-##--load-phutil-library## flag:
+`--load-phutil-library` flag:
arc --load-phutil-library=/path/to/library <command>
You can specify this flag more than once to load several libraries. Note that
if you use this flag, Arcanist will ignore any libraries listed in
-##.arcconfig##.
+`.arcconfig`.
= Use the Class =
This step is easy: just edit ##.arcconfig## to specify your class name as
the appropriate configuration value.
{
// ...
"lint.engine" : "CustomArcanistLintEngine",
// ...
}
Now, when you run Arcanist in your project, it will invoke your class when
appropriate.
For lint and unit tests, you can also use the ##--engine## flag override the
default engine:
arc lint --engine MyCustomArcanistLintEngine
This is mostly useful for debugging and testing.
= Next Steps =
- Learn how to reuse existing linters by reading
@{article:Arcanist User Guide: Customizing Existing Linters}.
diff --git a/src/docs/user/userguide/diffusion_symbols.diviner b/src/docs/user/userguide/diffusion_symbols.diviner
index 3ca381bac1..b9b96ec209 100644
--- a/src/docs/user/userguide/diffusion_symbols.diviner
+++ b/src/docs/user/userguide/diffusion_symbols.diviner
@@ -1,95 +1,95 @@
@title Diffusion User Guide: Symbol Indexes
@group userguide
Guide to configuring and using the symbol index.
= Overview =
Phabricator can maintain a symbol index, which keeps track of where classes
and functions are defined in the codebase. Once you set up indexing, you can
use the index to do things like:
- link symbol uses in Differential code reviews to their definitions
- allow you to search for symbols
- let the IRC bot answer questions like "Where is SomeClass?"
NOTE: Symbol indexing is somewhat new, and has broader support for PHP than for
other languages.
= Populating the Index =
To populate the index, you need to write a script which identifies symbols in
your codebase and set up a cronjob which pipes its output to:
./scripts/symbols/import_project_symbols.php
Phabricator includes a script which can identify symbols in PHP projects:
./scripts/symbols/generate_php_symbols.php
Phabricator also includes a script which can identify symbols in any
programming language that has classes and/or functions, and is supported by
Exuberant Ctags (http://ctags.sourceforge.net):
./scripts/symbols/generate_ctags_symbols.php
If you want to identify symbols from another language, you need to write a
script which can export them (for example, maybe by parsing a ##ctags## file).
The output format of the script should be one symbol per line:
<context> <name> <type> <lang> <line> <path>
For example:
ExampleClass exampleMethod function php 13 /src/classes/ExampleClass.php
Context is, broadly speaking, the scope or namespace where the symbol is
defined. For object-oriented languages, this is probably a class name. The
symbols with that context are class constants, methods, properties, nested
classes, etc. When printing symbols without a context (those that are defined
globally, for instance), the ##<context>## field should be empty (that is, the
line should start with a space).
Your script should enumerate all the symbols in your project, and provide paths
from the project root (where ".arcconfig" is) beginning with a "/".
You can look at ##generate_php_symbols.php## for an example of how you might
write such a script, and run this command to see its output:
$ cd phabricator/
$ find . -type f -name '*.php' | ./scripts/symbols/generate_php_symbols.php
To actually build the symbol index, pipe this data to the
-##import_project_symbols.php## script, providing the project name:
+`import_project_symbols.php` script, providing the project name:
$ ./scripts/symbols/import_project_symbols.php yourproject < symbols_data
Then just set up a cronjob to run that however often you like.
You can test that the import worked by querying for symbols using the Conduit
method ##differential.findsymbols##. Some features (like that method, and the
IRC bot integration) will start working immediately. Others will require more
configuration.
= Configuring Differential Integration =
To configure Differential integration, you need to tell Phabricator which
projects have symbol indexes you want to use, and which other projects they
should pull symbols from. To do this, go to
-##Repositories -> Arcanist Projects -> Edit## as an administrator. You need to
+`Repositories -> Arcanist Projects -> Edit` as an administrator. You need to
fill out these fields:
- **Repository**: Associate the project with a tracked repository.
- **Indexed Languages**: Fill in all the languages you've built indexes for.
- **Uses Symbols From**: If this project depends on other projects, add the
other projects which symbols should be looked for here. For example,
Phabricator lists "Arcanist" and "libphutil" because it uses classes and
functions from these projects.
Once you've configured a project, new revisions in that project will
automatically link symbols in Differential.
NOTE: Because this feature depends on the syntax highlighter, it will work
better for some languages than others. It currently works fairly well for PHP,
but your mileage may vary for other languages.
diff --git a/src/docs/user/userguide/libraries.diviner b/src/docs/user/userguide/libraries.diviner
index 053c63cf75..33f6f57b67 100644
--- a/src/docs/user/userguide/libraries.diviner
+++ b/src/docs/user/userguide/libraries.diviner
@@ -1,156 +1,156 @@
@title libphutil Libraries User Guide
@group userguide
Guide to creating and managing libphutil libraries.
= Overview =
libphutil includes a library system which organizes PHP classes and functions
into modules. Some extensions and customizations of Arcanist and Phabricator
require you to make code available to Phabricator by providing it in a libphutil
library.
For example, if you want to store files in some kind of custom storage engine,
you need to write a class which can interact with that engine and then tell
Phabricator to load it.
In general, you perform these one-time setup steps:
- Create a new directory.
- Use ##arc liberate## to initialize and name the library.
- Add a dependency on Phabricator if necessary.
- Add the library to your Phabricator config or ##.arcconfig## so it will be
loaded at runtime.
Then, to add new code, you do this:
- Write or update classes.
- Update the library metadata by running ##arc liberate## again.
= Creating a New Library =
To **create a new libphutil library**:
$ mkdir libcustom/
$ cd libcustom/
libcustom/ $ arc liberate src/
Now you'll get a prompt like this:
lang=txt
No library currently exists at that path...
The directory '/some/path/libcustom/src' does not exist.
Do you want to create it? [y/N] y
Creating new libphutil library in '/some/path/libcustom/src'.
Choose a name for the new library.
What do you want to name this library?
Choose a library name (in this case, "libcustom" would be appropriate) and it
you should get some details about the library initialization:
lang=txt
Writing '__phutil_library_init__.php' to
'/some/path/libcustom/src/__phutil_library_init__.php'...
Using library root at 'src'...
Mapping library...
Verifying library...
Finalizing library map...
OKAY Library updated.
This will write three files:
- ##src/.phutil_module_cache## This is a cache which makes "arc liberate"
faster when you run it to update the library. You can safely remove it at
any time. If you check your library into version control, you can add this
file to ignore rules (like .gitignore).
- ##src/__phutil_library_init__.php## This records the name of the library and
tells libphutil that a library exists here.
- ##src/__phutil_library_map__.php## This is a map of all the symbols
(functions and classes) in the library, which allows them to be autoloaded
at runtime and dependencies to be statically managed by "arc liberate".
= Linking with Phabricator =
If you aren't using this library with Phabricator (e.g., you are only using it
with Arcanist or are building something else on libphutil) you can skip this
step.
But, if you intend to use this library with Phabricator, you need to define its
dependency on Phabricator by creating a ##.arcconfig## file which points at
Phabricator. For example, you might write this file to
-##libcustom/.arcconfig##:
+`libcustom/.arcconfig`:
{
"project.name" : "libcustom",
"load" : [
"phabricator/src/"
]
}
For details on creating a ##.arcconfig##, see
@{article:Arcanist User Guide: Configuring a New Project}. In general, this
tells ##arc liberate## that it should look for symbols in Phabricator when
performing static analysis.
NOTE: If Phabricator isn't located next to your custom library, specify a
path which actually points to the ##phabricator/## directory.
You do not need to declare dependencies on ##arcanist## or ##libphutil##,
since ##arc liberate## automatically loads them.
Finally, edit your Phabricator config to tell it to load your library at
runtime, by adding it to ##load-libraries##:
...
'load-libraries' => array(
'libcustom' => 'libcustom/src/',
),
...
Now, Phabricator will be able to load classes from your custom library.
= Writing Classes =
To actually write classes, create a new module and put code in it:
libcustom/ $ mkdir src/example/
libcustom/ $ nano src/example/ExampleClass.php # Edit some code.
Now, run ##arc liberate## to regenerate the static resource map:
libcustom/ $ arc liberate src/
This will automatically regenerate the static map of the library.
= What You Can Extend And Invoke =
libphutil, Arcanist and Phabricator are strict about extensibility of classes
and visibility of methods and properties. Most classes are marked ##final##, and
methods have the minimum required visibility (protected or private). The goal of
this strictness is to make it clear what you can safely extend, access, and
invoke, so your code will keep working as the upstream changes.
When developing libraries to work with libphutil, Arcanist and Phabricator, you
should respect method and property visibility and extend only classes marked
-##@stable##. They are rendered with a large callout in the documentation (for
+`@stable`. They are rendered with a large callout in the documentation (for
example: @{class@libphutil:AbstractDirectedGraph}). These classes are external
interfaces intended for extension.
If you want to extend a class but it is not marked ##@stable##, here are some
approaches you can take:
- Good: If possible, use composition rather than extension to build your
feature.
- Good: Check the documentation for a better way to accomplish what you're
trying to do.
- Good: Let us know what your use case is so we can make the class tree more
flexible or configurable, or point you at the right way to do whatever
you're trying to do, or explain why we don't let you do it.
- Discouraged: Send us a patch removing "final" (or turning "protected" or
"private" into "public"). We generally will not accept these patches, unless
there's a good reason that the current behavior is wrong.
- Discouraged: Create an ad-hoc local fork and remove "final" in your copy of
the code. This will make it more difficult for you to upgrade in the future.
- Discouraged: Use Reflection to violate visibility keywords.
diff --git a/src/docs/user/userguide/mail_rules.diviner b/src/docs/user/userguide/mail_rules.diviner
index 2000d6d160..cc0860a1ae 100644
--- a/src/docs/user/userguide/mail_rules.diviner
+++ b/src/docs/user/userguide/mail_rules.diviner
@@ -1,81 +1,81 @@
@title User Guide: Managing Phabricator Email
@group userguide
How to effectively manage Phabricator email notifications.
= Overview =
Phabricator uses email as a major notification channel, but the amount of email
it sends can seem overwhelming if you're working on an active team. This
document discusses some strategies for managing email.
By far the best approach to managing mail is to **write mail rules** to
categorize mail. Essentially all modern mail clients allow you to quickly
write sophisticated rules to route, categorize, or delete email.
= Reducing Email =
You can reduce the amount of email you receive by turning off some types of
email in ##Settings -> Email Preferences##. For example, you can turn off email
produced by your own actions (like when you comment on a revision), and some
types of less-important notifications about events.
= Mail Rules =
The best approach to managing mail is to write mail rules. Simply writing rules
to move mail from Differential, Maniphest and Herald to separate folders will
vastly simplify mail management.
Phabricator also sets a large number of headers (see below) which can allow you
to write more sophisticated mail rules.
= Mail Headers =
Phabricator sends a variety of mail headers that can be useful in crafting rules
to route and manage mail.
Headers in plural contain lists. A list containing two items, ##1## and
-##15## will generally be formatted like this:
+`15` will generally be formatted like this:
X-Header: <1>, <15>
The intent is to allow you to write a rule which matches against "<1>". If you
just match against "1", you'll incorrectly match "15", but matching "<1>" will
correctly match only "<1>".
Some other headers use a single value but can be presented multiple times.
It is to support e-mail clients which are not able to create rules using regular
expressions or wildcards (namely Outlook).
The headers Phabricator adds to mail are:
- ##X-Phabricator-Sent-This-Message##: this is attached to all mail
Phabricator sends. You can use it to differentiate between email from
Phabricator and replies/forwards of Phabricator mail from human beings.
- ##X-Phabricator-To##: this is attached to all mail Phabricator sends.
It shows the PHIDs of the original "To" line, before any mutation
by the mailer configuration.
- ##X-Phabricator-Cc##: this is attached to all mail Phabricator sends.
It shows the PHIDs of the original "Cc" line, before any mutation by the
mailer configuration.
- ##X-Differential-Author##: this is attached to Differential mail and shows
the revision's author. You can use it to filter mail about your revisions
(or other users' revisions).
- ##X-Differential-Reviewer##: this is attached to Differential mail and
shows the reviewers. You can use it to filter mail about revisions you
are reviewing, versus revisions you are explicitly CC'd on or CC'd as
a result of Herald rules.
- ##X-Differential-Reviewers##: list version of the previous.
- ##X-Differential-CC##: this is attached to Differential mail and shows
the CCs on the revision.
- ##X-Differential-CCs##: list version of the previous.
- ##X-Differential-Explicit-CC##: this is attached to Differential mail and
shows the explicit CCs on the revision (those that were added by humans,
not by Herald).
- ##X-Differential-Explicit-CCs##: list version of the previous.
- ##X-Phabricator-Mail-Tags##: this is attached to some mail and has
a list of descriptors about the mail. (This is fairly new and subject
to some change.)
- ##X-Herald-Rules##: this is attached to some mail and shows Herald rule
IDs which have triggered for the object. You can use this to sort or
categorize mail that has triggered specific rules.
diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner
index 3967201738..5c76d6884b 100644
--- a/src/docs/user/userguide/remarkup.diviner
+++ b/src/docs/user/userguide/remarkup.diviner
@@ -1,563 +1,563 @@
@title Remarkup Reference
@group userguide
Explains how to make bold text; this makes your words louder so you can win
arguments.
= Overview =
Phabricator uses a lightweight markup language called "Remarkup", similar to
other lightweight markup languages like Markdown and Wiki markup.
This document describes how to format text using Remarkup.
= Quick Reference =
All the syntax is explained in more detail below, but this is a quick guide to
formatting text in Remarkup.
These are inline styles, and can be applied to most text:
**bold** //italic// ##monospaced## `monospaced` ~~deleted~~ __underlined__
D123 T123 rX123 # Link to Objects
{D123} {T123} # Link to Objects (Full Name)
{F123} # Embed Images
{M123} # Embed Pholio Mock
@username # Mention a User
#project # Mention a Project
[[wiki page]] # Link to Phriction
[[wiki page | name]] # Named link to Phriction
http://xyz/ # Link to web
[[http://xyz/ | name]] # Named link to web
[name](http://xyz/) # Alternate Link
These are block styles, and must be separated from surrounding text by
empty lines:
= Large Header =
== Smaller Header ==
## This is a Header As Well
Also a Large Header
===================
Also a Smaller Header
---------------------
> Quoted Text
Use "- " or "* " for bulleted lists, and "# " for numbered lists.
Use ``` or indent two spaces for code.
Use %%% for a literal block.
Use | ... | ... for tables.
= Basic Styling =
Format **basic text styles** like this:
**bold text**
//italic text//
##monospaced text##
`monospaced text`
~~deleted text~~
__underlined text__
Those produce **bold text**, //italic text//, ##monospaced text##,
`monospaced text`, ~~deleted text~~, and __underlined text__, respectively.
= Layout =
Make **headers** like this:
= Large Header =
== Smaller Header ==
===== Very Small Header =====
Alternate Large Header
======================
Alternate Smaller Header
------------------------
You can optionally omit the trailing `=` signs -- that is, these are the same:
== Smaller Header ==
== Smaller Header
This produces headers like the ones in this document. Make sure you have an
empty line before and after the header.
Make **lists** by beginning each item with a "-" or a "*":
lang=text
- milk
- eggs
- bread
* duck
* duck
* goose
This produces a list like this:
- milk
- eggs
- bread
(Note that you need to put a space after the "-" or "*".)
You can make numbered lists with a "#" instead of "-" or "*":
# Articuno
# Zapdos
# Moltres
You can also nest lists:
```- Body
- Head
- Arm
- Elbow
- Hand
# Thumb
# Index
# Middle
# Ring
# Pinkie
- Leg
- Knee
- Foot```
...which produces:
- Body
- Head
- Arm
- Elbow
- Hand
# Thumb
# Index
# Middle
# Ring
# Pinkie
- Leg
- Knee
- Foot
If you prefer, you can indent lists using multiple characters to show indent
depth, like this:
```- Tree
-- Branch
--- Twig```
As expected, this produces:
- Tree
-- Branch
--- Twig
You can add checkboxes to items by prefacing them with `[ ]` or `[X]`, like
this:
```
- [X] Preheat oven to 450 degrees.
- [ ] Zest 35 lemons.
```
When rendered, this produces:
- [X] Preheat oven to 450 degrees.
- [ ] Zest 35 lemons.
Make **code blocks** by indenting two spaces:
f(x, y);
You can also use three backticks to enclose the code block:
```f(x, y);
g(f);```
You can specify a language for syntax highlighting with "lang=xxx":
lang=text
lang=html
<a href="#">...</a>
This will highlight the block using a highlighter for that language, if one is
available (in most cases, this means you need to configure Pygments):
lang=html
<a href="#">...</a>
You can also use a "COUNTEREXAMPLE" header to show that a block of code is
bad and shouldn't be copied:
lang=text
COUNTEREXAMPLE
function f() {
global $$variable_variable;
}
This produces a block like this:
COUNTEREXAMPLE
function f() {
global $$variable_variable;
}
You can use ##lines=N## to limit the vertical size of a chunk of code, and
-##name=some_name.ext## to give it a name. For example, this:
+`name=some_name.ext` to give it a name. For example, this:
lang=text
lang=html, name=example.html, lines=12, counterexample
...
...produces this:
lang=html, name=example.html, lines=12, counterexample
<p>Apple</p>
<p>Apricot</p>
<p>Avocado</p>
<p>Banana</p>
<p>Bilberry</p>
<p>Blackberry</p>
<p>Blackcurrant</p>
<p>Blueberry</p>
<p>Currant</p>
<p>Cherry</p>
<p>Cherimoya</p>
<p>Clementine</p>
<p>Date</p>
<p>Damson</p>
<p>Durian</p>
<p>Eggplant</p>
<p>Elderberry</p>
<p>Feijoa</p>
<p>Gooseberry</p>
<p>Grape</p>
<p>Grapefruit</p>
<p>Guava</p>
<p>Huckleberry</p>
<p>Jackfruit</p>
<p>Jambul</p>
<p>Kiwi fruit</p>
<p>Kumquat</p>
<p>Legume</p>
<p>Lemon</p>
<p>Lime</p>
<p>Lychee</p>
<p>Mandarine</p>
<p>Mango</p>
<p>Mangostine</p>
<p>Melon</p>
You can also use "NOTE:", "WARNING:", or "IMPORTANT:" to call out an important
idea.
NOTE: Best practices in proton pack operation include not crossing the streams.
WARNING: Crossing the streams can result in total protonic reversal!
IMPORTANT: Don't cross the streams!
In addition, you can use "(NOTE)", "(WARNING)", or "(IMPORTANT)" to get the
same effect but without "(NOTE)", "(WARNING)", or "(IMPORTANT)" appearing in
the rendered result. For example
(NOTE) Dr. Egon Spengler is the best resource for additional proton pack
questions.
= Linking URIs =
URIs are automatically linked: http://phabricator.org/
If you have a URI with problematic characters in it, like
"##http://comma.org/,##", you can surround it with angle brackets:
<http://comma.org/,>
This will force the parser to consume the whole URI: <http://comma.org/,>
You can also use create named links, where you choose the displayed text. These
work within Phabricator or on the internet at large:
[[/herald/transcript/ | Herald Transcripts]]
[[http://www.boring-legal-documents.com/ | exciting legal documents]]
Markdown-style links are also supported:
[Toil](http://www.trouble.com)
= Linking to Objects =
You can link to Differential revisions, Diffusion commits and Maniphest tasks
by mentioning the name of an object:
D123 # Link to Differential revision D123
rX123 # Link to SVN commit 123 from the "X" repository
rXaf3192cd5 # Link to Git commit "af3192cd5..." from the "X" repository.
# You must specify at least 7 characters of the hash.
T123 # Link to Maniphest task T123
You can also link directly to a comment in Maniphest and Differential:
T123#4 # Link to comment #4 of T123
= Embedding Objects
You can also generate full-name references to some objects by using braces:
{D123} # Link to Differential revision D123 with the full name
{T123} # Link to Maniphest task T123 with the full name
These references will also show when an object changes state (for instance, a
task or revision is closed). Some types of objects support rich embedding.
== Linking to Project Tags
Projects can be linked to with the use of a hashtag (#). This works by default
using the name of the Project (lowercase, underscored). Additionally you
can set multiple additional hashtags by editing the Project details.
#qa, #quality_assurance
== Embedding Mocks (Pholio)
You can embed a Pholio mock by using braces to refer to it:
{M123}
By default the first four images from the mock set are displayed. This behavior
can be overridden with the **image** option. With the **image** option you can
provide one or more image IDs to display.
You can set the image (or images) to display like this:
{M123, image=12345}
{M123, image=12345 & 6789}
== Embedding Pastes
You can embed a Paste using braces:
{P123}
You can adjust the embed height with the `lines` option:
{P123, lines=15}
You can highlight specific lines with the `highlight` option:
{P123, highlight=15}
{P123, highlight="23-25, 31"}
== Embedding Images
You can embed an image or other file by using braces to refer to it:
{F123}
In most interfaces, you can drag-and-drop an image from your computer into the
text area to upload and reference it.
Some browsers (e.g. Chrome) support uploading an image data just by pasting them
from clipboard into the text area.
You can set file display options like this:
{F123, layout=left, float, size=full, alt="a duckling"}
Valid options are:
- **layout** left (default), center, right, inline, link (render a link
instead of a thumbnail for images)
- **float** If layout is set to left or right, the image will be floated so
text wraps around it.
- **size** thumb (default), full
- **name** with `layout=link` or for non-images, use this name for the link
text
- **width** Scale image to a specific width.
- **height** Scale image to a specific height.
- **alt** Provide alternate text for assistive technologies.
== Embedding Countdowns
You can embed a countdown by using braces:
{C123}
= Quoting Text =
To quote text, preface it with an ">":
> This is quoted text.
This appears like this:
> This is quoted text.
= Embedding Media =
If you set a configuration flag, you can embed media directly in text:
- **remarkup.enable-embedded-youtube**: allows you to paste in YouTube videos
and have them render inline.
This option is disabled by default because it has security and/or
silliness implications. Read the description in ##default.conf.php## before
enabling it.
= Image Macros =
You can upload image macros (More Stuff -> Macro) which will replace text
strings with the image you specify. For instance, you could upload an image of a
dancing banana to create a macro named "peanutbutterjellytime", and then any
time you type that string on a separate line it will be replaced with the image
of a dancing banana.
= Memes =
You can also use image macros in the context of memes. For example, if you
have an image macro named "grumpy", you can create a meme by doing the
following:
{meme, src = grumpy, above = toptextgoeshere, below = bottomtextgoeshere}
By default, the font used to create the text for the meme is `tuffy.ttf`. For
the more authentic feel of `impact.ttf`, you simply have to place the Impact
TrueType font in the Phabricator subfolder `/resources/font/`. If Remarkup
detects the presence of `impact.ttf`, it will automatically use it.
= Mentioning Users =
In Differential and Maniphest, you can mention another user by writing:
@username
When you submit your comment, this will add them as a CC on the revision or task
if they aren't already CC'd.
Icons
=====
You can add icons to comments using the `{icon ...}` syntax. For example:
{icon camera}
This renders: {icon camera}
You can select a color for icons:
{icon camera color=blue}
This renders: {icon camera color=blue}
For a list of available icons and colors, check the UIExamples application.
(The icons are sourced from
[[ http://fortawesome.github.io/Font-Awesome/ | FontAwesome ]], so you can also
browse the collection there.)
= Phriction Documents =
You can link to Phriction documents with a name or path:
Make sure you sign and date your [[legal/Letter of Marque and Reprisal]]!
With a pipe (##|##), you can retitle the link. Use this to mislead your
opponents:
Check out these [[legal/boring_documents/ | exciting legal documents]]!
= Literal Blocks =
To place text in a literal block use "%%%":
%%%Text that won't be processed by remarkup
[[http://www.example.com | example]]
%%%
Remarkup will not process the text inside of literal blocks (other than to
escape HTML and preserve line breaks).
= Tables =
Remarkup supports simple table syntax. For example, this:
| Fruit | Color | Price | Peel?
| ----- | ----- | ----- | -----
| Apple | red | `$0.93` | no
| Banana | yellow | `$0.19` | **YES**
...produces this:
| Fruit | Color | Price | Peel?
| ----- | ----- | ----- | -----
| Apple | red | `$0.93` | no
| Banana | yellow | `$0.19` | **YES**
Remarkup also supports a simplified HTML table syntax. For example, this:
<table>
<tr>
<th>Fruit</th>
<th>Color</th>
<th>Price</th>
<th>Peel?</th>
</tr>
<tr>
<td>Apple</td>
<td>red</td>
<td>`$0.93`</td>
<td>no</td>
</tr>
<tr>
<td>Banana</td>
<td>yellow</td>
<td>`$0.19`</td>
<td>**YES**</td>
</tr>
</table>
...produces this:
<table>
<tr>
<th>Fruit</th>
<th>Color</th>
<th>Price</th>
<th>Peel?</th>
</tr>
<tr>
<td>Apple</td>
<td>red</td>
<td>`$0.93`</td>
<td>no</td>
</tr>
<tr>
<td>Banana</td>
<td>yellow</td>
<td>`$0.19`</td>
<td>**YES**</td>
</tr>
</table>
Some general notes about this syntax:
- your tags must all be properly balanced;
- your tags must NOT include attributes (`<td>` is OK, `<td style="...">` is
not);
- you can use other Remarkup rules (like **bold**, //italics//, etc.) inside
table cells.
= Fullscreen Mode =
Remarkup editors provide a fullscreen composition mode. This can make it easier
to edit large blocks of text, or improve focus by removing distractions. You can
exit **Fullscreen** mode by clicking the button again or by pressing escape.
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jan 19 2025, 21:26 (6 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128922
Default Alt Text
(102 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment