1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Merge branch 'master' of github.com:facebook/phabricator

This commit is contained in:
Natthu Bharambe 2012-02-24 15:29:04 -08:00
commit a22827865f
181 changed files with 6816 additions and 695 deletions

3
.gitignore vendored
View file

@ -11,3 +11,6 @@
# NetBeans project files
/nbproject/
# Arcanist scratch directory
/.arc

View file

@ -329,7 +329,7 @@ return array(
'account.minimum-password-length' => 8,
// -- Facebook ------------------------------------------------------------ //
// -- Facebook OAuth -------------------------------------------------------- //
// Can users use Facebook credentials to login to Phabricator?
'facebook.auth-enabled' => false,
@ -348,7 +348,7 @@ return array(
'facebook.application-secret' => null,
// -- GitHub ---------------------------------------------------------------- //
// -- GitHub OAuth ---------------------------------------------------------- //
// Can users use GitHub credentials to login to Phabricator?
'github.auth-enabled' => false,
@ -367,7 +367,7 @@ return array(
'github.application-secret' => null,
// -- Google ---------------------------------------------------------------- //
// -- Google OAuth ---------------------------------------------------------- //
// Can users use Google credentials to login to Phabricator?
'google.auth-enabled' => false,
@ -385,6 +385,30 @@ return array(
// The Google "Client Secret" to use for Google API access.
'google.application-secret' => null,
// -- Phabricator OAuth ----------------------------------------------------- //
// Meta-town -- Phabricator is itself an OAuth Provider
// TODO -- T887 -- make this support multiple Phabricator instances!
// The URI of the Phabricator instance to use as an OAuth server.
'phabricator.oauth-uri' => null,
// Can users use Phabricator credentials to login to Phabricator?
'phabricator.auth-enabled' => false,
// Can users use Phabricator credentials to create new Phabricator accounts?
'phabricator.registration-enabled' => true,
// Are Phabricator accounts permanently linked to Phabricator accounts, or can
// the user unlink them?
'phabricator.auth-permanent' => false,
// The Phabricator "Client ID" to use for Phabricator API access.
'phabricator.application-id' => null,
// The Phabricator "Client Secret" to use for Phabricator API access.
'phabricator.application-secret' => null,
// -- Recaptcha ------------------------------------------------------------- //
// Is Recaptcha enabled? If disabled, captchas will not appear. You should
@ -623,6 +647,10 @@ return array(
// Adding Custom Fields" in the documentation for more information.
'maniphest.custom-task-extensions-class' => 'ManiphestDefaultTaskExtensions',
// -- Phriction ------------------------------------------------------------- //
'phriction.enabled' => true,
// -- Remarkup -------------------------------------------------------------- //
// If you enable this, linked YouTube videos will be embeded inline. This has

2
externals/javelin vendored

@ -1 +1 @@
Subproject commit f25470da2c98cd37fbac435a56128b1d465f7667
Subproject commit 14f4218c5b7d50bb0f4144d937e482cdb98f6cdb

10
externals/raphael/g.raphael.js vendored Normal file

File diff suppressed because one or more lines are too long

10
externals/raphael/g.raphael.line.js vendored Normal file
View file

@ -0,0 +1,10 @@
/**
* @provides raphael-g-line
*/
/*!
* g.Raphael 0.5 - Charting library, based on Raphaël
*
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*/
(function(){function a(g,n){var f=g.length/n,h=0,e=f,m=0,i=[];while(h<g.length){e--;if(e<0){m+=g[h]*(1+e);i.push(m/f);m=g[h++]*-e;e+=f}else{m+=g[h++]}}return i}function d(f,e,p,n,k,j){var h=(p-f)/2,g=(k-p)/2,q=Math.atan((p-f)/Math.abs(n-e)),o=Math.atan((k-p)/Math.abs(n-j));q=e<n?Math.PI-q:q;o=j<n?Math.PI-o:o;var i=Math.PI/2-((q+o)%(Math.PI*2))/2,s=h*Math.sin(i+q),m=h*Math.cos(i+q),r=g*Math.sin(i+o),l=g*Math.cos(i+o);return{x1:p-s,y1:n+m,x2:p+r,y2:n+l}}function b(f,P,O,e,h,A,z,J){var s=this;J=J||{};if(!f.raphael.is(A[0],"array")){A=[A]}if(!f.raphael.is(z[0],"array")){z=[z]}var q=J.gutter||10,B=Math.max(A[0].length,z[0].length),t=J.symbol||"",S=J.colors||s.colors,v=null,p=null,ad=f.set(),T=[];for(var ac=0,L=z.length;ac<L;ac++){B=Math.max(B,z[ac].length)}var ae=f.set();for(ac=0,L=z.length;ac<L;ac++){if(J.shade){ae.push(f.path().attr({stroke:"none",fill:S[ac],opacity:J.nostroke?1:0.3}))}if(z[ac].length>e-2*q){z[ac]=a(z[ac],e-2*q);B=e-2*q}if(A[ac]&&A[ac].length>e-2*q){A[ac]=a(A[ac],e-2*q)}}var W=Array.prototype.concat.apply([],A),U=Array.prototype.concat.apply([],z),u=s.snapEnds(Math.min.apply(Math,W),Math.max.apply(Math,W),A[0].length-1),E=u.from,o=u.to,N=s.snapEnds(Math.min.apply(Math,U),Math.max.apply(Math,U),z[0].length-1),C=N.from,n=N.to,Z=(e-q*2)/((o-E)||1),V=(h-q*2)/((n-C)||1);var G=f.set();if(J.axis){var m=(J.axis+"").split(/[,\s]+/);+m[0]&&G.push(s.axis(P+q,O+q,e-2*q,E,o,J.axisxstep||Math.floor((e-2*q)/20),2,f));+m[1]&&G.push(s.axis(P+e-q,O+h-q,h-2*q,C,n,J.axisystep||Math.floor((h-2*q)/20),3,f));+m[2]&&G.push(s.axis(P+q,O+h-q,e-2*q,E,o,J.axisxstep||Math.floor((e-2*q)/20),0,f));+m[3]&&G.push(s.axis(P+q,O+h-q,h-2*q,C,n,J.axisystep||Math.floor((h-2*q)/20),1,f))}var M=f.set(),aa=f.set(),r;for(ac=0,L=z.length;ac<L;ac++){if(!J.nostroke){M.push(r=f.path().attr({stroke:S[ac],"stroke-width":J.width||2,"stroke-linejoin":"round","stroke-linecap":"round","stroke-dasharray":J.dash||""}))}var g=Raphael.is(t,"array")?t[ac]:t,H=f.set();T=[];for(var ab=0,w=z[ac].length;ab<w;ab++){var l=P+q+((A[ac]||A[0])[ab]-E)*Z,k=O+h-q-(z[ac][ab]-C)*V;(Raphael.is(g,"array")?g[ab]:g)&&H.push(f[Raphael.is(g,"array")?g[ab]:g](l,k,(J.width||2)*3).attr({fill:S[ac],stroke:"none"}));if(J.smooth){if(ab&&ab!=w-1){var R=P+q+((A[ac]||A[0])[ab-1]-E)*Z,F=O+h-q-(z[ac][ab-1]-C)*V,Q=P+q+((A[ac]||A[0])[ab+1]-E)*Z,D=O+h-q-(z[ac][ab+1]-C)*V,af=d(R,F,l,k,Q,D);T=T.concat([af.x1,af.y1,l,k,af.x2,af.y2])}if(!ab){T=["M",l,k,"C",l,k]}}else{T=T.concat([ab?"L":"M",l,k])}}if(J.smooth){T=T.concat([l,k,l,k])}aa.push(H);if(J.shade){ae[ac].attr({path:T.concat(["L",l,O+h-q,"L",P+q+((A[ac]||A[0])[0]-E)*Z,O+h-q,"z"]).join(",")})}!J.nostroke&&r.attr({path:T.join(",")})}function K(an){var ak=[];for(var al=0,ap=A.length;al<ap;al++){ak=ak.concat(A[al])}ak.sort();var aq=[],ah=[];for(al=0,ap=ak.length;al<ap;al++){ak[al]!=ak[al-1]&&aq.push(ak[al])&&ah.push(P+q+(ak[al]-E)*Z)}ak=aq;ap=ak.length;var ag=an||f.set();for(al=0;al<ap;al++){var Y=ah[al]-(ah[al]-(ah[al-1]||P))/2,ao=((ah[al+1]||P+e)-ah[al])/2+(ah[al]-(ah[al-1]||P))/2,x;an?(x={}):ag.push(x=f.rect(Y-1,O,Math.max(ao+1,1),h).attr({stroke:"none",fill:"#000",opacity:0}));x.values=[];x.symbols=f.set();x.y=[];x.x=ah[al];x.axis=ak[al];for(var aj=0,am=z.length;aj<am;aj++){aq=A[aj]||A[0];for(var ai=0,y=aq.length;ai<y;ai++){if(aq[ai]==ak[al]){x.values.push(z[aj][ai]);x.y.push(O+h-q-(z[aj][ai]-C)*V);x.symbols.push(ad.symbols[aj][ai])}}}an&&an.call(x)}!an&&(v=ag)}function I(al){var ah=al||f.set(),x;for(var aj=0,an=z.length;aj<an;aj++){for(var ai=0,ak=z[aj].length;ai<ak;ai++){var ag=P+q+((A[aj]||A[0])[ai]-E)*Z,am=P+q+((A[aj]||A[0])[ai?ai-1:1]-E)*Z,y=O+h-q-(z[aj][ai]-C)*V;al?(x={}):ah.push(x=f.circle(ag,y,Math.abs(am-ag)/2).attr({stroke:"none",fill:"#000",opacity:0}));x.x=ag;x.y=y;x.value=z[aj][ai];x.line=ad.lines[aj];x.shade=ad.shades[aj];x.symbol=ad.symbols[aj][ai];x.symbols=ad.symbols[aj];x.axis=(A[aj]||A[0])[ai];al&&al.call(x)}}!al&&(p=ah)}ad.push(M,ae,aa,G,v,p);ad.lines=M;ad.shades=ae;ad.symbols=aa;ad.axis=G;ad.hoverColumn=function(j,i){!v&&K();v.mouseover(j).mouseout(i);return this};ad.clickColumn=function(i){!v&&K();v.click(i);return this};ad.hrefColumn=function(Y){var ag=f.raphael.is(arguments[0],"array")?arguments[0]:arguments;if(!(arguments.length-1)&&typeof Y=="object"){for(var j in Y){for(var y=0,X=v.length;y<X;y++){if(v[y].axis==j){v[y].attr("href",Y[j])}}}}!v&&K();for(y=0,X=ag.length;y<X;y++){v[y]&&v[y].attr("href",ag[y])}return this};ad.hover=function(j,i){!p&&I();p.mouseover(j).mouseout(i);return this};ad.click=function(i){!p&&I();p.click(i);return this};ad.each=function(i){I(i);return this};ad.eachColumn=function(i){K(i);return this};return ad}var c=function(){};c.prototype=Raphael.g;b.prototype=new c;Raphael.fn.linechart=function(f,k,g,e,j,i,h){return new b(this,f,k,g,e,j,i,h)}})();

11
externals/raphael/raphael.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -9,6 +9,7 @@
"PhabricatorIRCProtocolHandler",
"PhabricatorIRCObjectNameHandler",
"PhabricatorIRCLogHandler",
"PhabricatorIRCWhatsNewHandler",
"PhabricatorIRCDifferentialNotificationHandler"
],

View file

@ -0,0 +1,51 @@
CREATE DATABASE IF NOT EXISTS `phabricator_oauth_server`;
CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthserverclient` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varchar(64) BINARY NOT NULL,
`name` varchar(255) NOT NULL,
`secret` varchar(32) NOT NULL,
`redirectURI` varchar(255) NOT NULL,
`creatorPHID` varchar(64) BINARY NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `phid` (`phid`)
) ENGINE=InnoDB;
CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthclientauthorization` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varchar(64) BINARY NOT NULL,
`userPHID` varchar(64) BINARY NOT NULL,
`clientPHID` varchar(64) BINARY NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `phid` (`phid`),
UNIQUE KEY `userPHID` (`userPHID`,`clientPHID`)
) ENGINE=InnoDB;
CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthserverauthorizationcode` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`code` varchar(32) NOT NULL,
`clientPHID` varchar(64) BINARY NOT NULL,
`clientSecret` varchar(32) NOT NULL,
`userPHID` varchar(64) BINARY NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB;
CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthserveraccesstoken` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`token` varchar(32) NOT NULL,
`userPHID` varchar(64) BINARY NOT NULL,
`clientPHID` varchar(64) BINARY NOT NULL,
`dateExpires` int(10) unsigned NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `token` (`token`)
) ENGINE=InnoDB;

View file

@ -0,0 +1,6 @@
ALTER TABLE `phabricator_oauth_server`.`oauth_server_oauthclientauthorization`
ADD `scope` text NOT NULL;
ALTER TABLE `phabricator_oauth_server`.`oauth_server_oauthserveraccesstoken`
DROP `dateExpires`;

View file

@ -0,0 +1,3 @@
ALTER TABLE `phabricator_oauth_server`.`oauth_server_oauthserverclient`
ADD KEY `creatorPHID` (`creatorPHID`)

View file

@ -213,7 +213,7 @@ celerity_register_resource_map(array(
),
'differential-revision-comment-css' =>
array(
'uri' => '/res/be9a95b4/rsrc/css/application/differential/revision-comment.css',
'uri' => '/res/7a0002f1/rsrc/css/application/differential/revision-comment.css',
'type' => 'css',
'requires' =>
array(
@ -258,7 +258,7 @@ celerity_register_resource_map(array(
),
'diffusion-commit-view-css' =>
array(
'uri' => '/res/bc39d876/rsrc/css/application/diffusion/commit-view.css',
'uri' => '/res/d486f79a/rsrc/css/application/diffusion/commit-view.css',
'type' => 'css',
'requires' =>
array(
@ -361,13 +361,14 @@ celerity_register_resource_map(array(
),
'javelin-behavior-aphront-drag-and-drop-textarea' =>
array(
'uri' => '/res/fa7527f9/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
'uri' => '/res/65980508/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'phabricator-drag-and-drop-file-upload',
3 => 'phabricator-paste-file-upload',
),
'disk' => '/rsrc/js/application/core/behavior-drag-and-drop-textarea.js',
),
@ -383,6 +384,19 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/core/behavior-form.js',
),
'javelin-behavior-buoyant' =>
array(
'uri' => '/res/e7581db1/rsrc/js/application/core/behavior-buoyant.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-stratcom',
2 => 'javelin-vector',
3 => 'javelin-dom',
),
'disk' => '/rsrc/js/application/core/behavior-buoyant.js',
),
'javelin-behavior-countdown-timer' =>
array(
'uri' => '/res/5ee9cb13/rsrc/js/application/countdown/timer.js',
@ -616,6 +630,36 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/herald/herald-rule-editor.js',
),
'javelin-behavior-maniphest-batch-editor' =>
array(
'uri' => '/res/d7b7f061/rsrc/js/application/maniphest/behavior-batch-editor.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-util',
3 => 'phabricator-prefab',
4 => 'multirow-row-manager',
5 => 'javelin-tokenizer',
6 => 'javelin-typeahead-preloaded-source',
7 => 'javelin-typeahead',
8 => 'javelin-json',
),
'disk' => '/rsrc/js/application/maniphest/behavior-batch-editor.js',
),
'javelin-behavior-maniphest-batch-selector' =>
array(
'uri' => '/res/398cf8d7/rsrc/js/application/maniphest/behavior-batch-selector.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-behavior',
1 => 'javelin-dom',
2 => 'javelin-stratcom',
),
'disk' => '/rsrc/js/application/maniphest/behavior-batch-selector.js',
),
'javelin-behavior-maniphest-description-preview' =>
array(
'uri' => '/res/8acd6f07/rsrc/js/application/maniphest/behavior-task-preview.js',
@ -858,7 +902,7 @@ celerity_register_resource_map(array(
),
'javelin-dom' =>
array(
'uri' => '/res/b2e8a5b6/rsrc/js/javelin/lib/DOM.js',
'uri' => '/res/4c86aaeb/rsrc/js/javelin/lib/DOM.js',
'type' => 'js',
'requires' =>
array(
@ -941,7 +985,7 @@ celerity_register_resource_map(array(
),
'javelin-magical-init' =>
array(
'uri' => '/res/0e72d59b/rsrc/js/javelin/core/init.js',
'uri' => '/res/caa86a45/rsrc/js/javelin/core/init.js',
'type' => 'js',
'requires' =>
array(
@ -1012,7 +1056,7 @@ celerity_register_resource_map(array(
),
'javelin-request' =>
array(
'uri' => '/res/b3257b7d/rsrc/js/javelin/lib/Request.js',
'uri' => '/res/6ccc1d5a/rsrc/js/javelin/lib/Request.js',
'type' => 'js',
'requires' =>
array(
@ -1040,7 +1084,7 @@ celerity_register_resource_map(array(
),
'javelin-stratcom' =>
array(
'uri' => '/res/d7a3d1e9/rsrc/js/javelin/core/Stratcom.js',
'uri' => '/res/3afdac66/rsrc/js/javelin/core/Stratcom.js',
'type' => 'js',
'requires' =>
array(
@ -1053,7 +1097,7 @@ celerity_register_resource_map(array(
),
'javelin-tokenizer' =>
array(
'uri' => '/res/1b1c2148/rsrc/js/javelin/lib/control/tokenizer/Tokenizer.js',
'uri' => '/res/2b91543e/rsrc/js/javelin/lib/control/tokenizer/Tokenizer.js',
'type' => 'js',
'requires' =>
array(
@ -1129,7 +1173,7 @@ celerity_register_resource_map(array(
),
'javelin-typeahead-source' =>
array(
'uri' => '/res/8606f519/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadSource.js',
'uri' => '/res/e99c0c1d/rsrc/js/javelin/lib/control/typeahead/source/TypeaheadSource.js',
'type' => 'js',
'requires' =>
array(
@ -1163,7 +1207,7 @@ celerity_register_resource_map(array(
),
'javelin-vector' =>
array(
'uri' => '/res/50535cb8/rsrc/js/javelin/lib/Vector.js',
'uri' => '/res/f240bdb3/rsrc/js/javelin/lib/Vector.js',
'type' => 'js',
'requires' =>
array(
@ -1252,6 +1296,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/maniphest/task-detail.css',
),
'maniphest-batch-editor' =>
array(
'uri' => '/res/fb15d744/rsrc/css/application/maniphest/batch-editor.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/maniphest/batch-editor.css',
),
'maniphest-task-edit-css' =>
array(
'uri' => '/res/68c7863e/rsrc/css/application/maniphest/task-edit.css',
@ -1263,7 +1316,7 @@ celerity_register_resource_map(array(
),
'maniphest-task-summary-css' =>
array(
'uri' => '/res/14cb4b5d/rsrc/css/application/maniphest/task-summary.css',
'uri' => '/res/7c52d502/rsrc/css/application/maniphest/task-summary.css',
'type' => 'css',
'requires' =>
array(
@ -1272,7 +1325,7 @@ celerity_register_resource_map(array(
),
'maniphest-transaction-detail-css' =>
array(
'uri' => '/res/552bb734/rsrc/css/application/maniphest/transaction-detail.css',
'uri' => '/res/24e5862f/rsrc/css/application/maniphest/transaction-detail.css',
'type' => 'css',
'requires' =>
array(
@ -1332,7 +1385,7 @@ celerity_register_resource_map(array(
),
'phabricator-chatlog-css' =>
array(
'uri' => '/res/a5aa9eef/rsrc/css/application/chatlog/chatlog.css',
'uri' => '/res/f674f526/rsrc/css/application/chatlog/chatlog.css',
'type' => 'css',
'requires' =>
array(
@ -1478,6 +1531,20 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/objectselector/object-selector.css',
),
'phabricator-paste-file-upload' =>
array(
'uri' => '/res/cdc939bd/rsrc/js/application/core/PasteFileUpload.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-install',
1 => 'javelin-util',
2 => 'javelin-request',
3 => 'javelin-dom',
4 => 'javelin-uri',
),
'disk' => '/rsrc/js/application/core/PasteFileUpload.js',
),
'phabricator-prefab' =>
array(
'uri' => '/res/5784a112/rsrc/js/application/core/Prefab.js',
@ -1510,24 +1577,13 @@ celerity_register_resource_map(array(
),
'phabricator-remarkup-css' =>
array(
'uri' => '/res/39f358b8/rsrc/css/core/remarkup.css',
'uri' => '/res/02e0f11a/rsrc/css/core/remarkup.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/remarkup.css',
),
0 =>
array(
'uri' => '/res/b6096fdd/rsrc/js/javelin/lib/__tests__/URI.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-uri',
1 => 'javelin-php-serializer',
),
'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
),
'phabricator-search-results-css' =>
array(
'uri' => '/res/f8a86e27/rsrc/css/application/search/search-results.css',
@ -1558,15 +1614,35 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/slowvote/slowvote.css',
),
0 =>
array(
'uri' => '/res/b6096fdd/rsrc/js/javelin/lib/__tests__/URI.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-uri',
1 => 'javelin-php-serializer',
),
'disk' => '/rsrc/js/javelin/lib/__tests__/URI.js',
),
'phabricator-standard-page-view' =>
array(
'uri' => '/res/ec5acaed/rsrc/css/application/base/standard-page-view.css',
'uri' => '/res/7e09bbfc/rsrc/css/application/base/standard-page-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/application/base/standard-page-view.css',
),
'phabricator-transaction-view-css' =>
array(
'uri' => '/res/731959fb/rsrc/css/aphront/transaction.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/aphront/transaction.css',
),
'phabricator-ui-example-css' =>
array(
'uri' => '/res/0cef078b/rsrc/css/application/uiexample/example.css',
@ -1736,6 +1812,33 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/projects/project-edit.css',
),
'raphael-core' =>
array(
'uri' => '/res/bae05d27/rsrc/js/raphael/raphael.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/raphael/raphael.js',
),
'raphael-g' =>
array(
'uri' => '/res/8bbdbea8/rsrc/js/raphael/g.raphael.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/raphael/g.raphael.js',
),
'raphael-g-line' =>
array(
'uri' => '/res/54504ae4/rsrc/js/raphael/g.raphael.line.js',
'type' => 'js',
'requires' =>
array(
),
'disk' => '/rsrc/js/raphael/g.raphael.line.js',
),
'syntax-highlighting-css' =>
array(
'uri' => '/res/5669beb6/rsrc/css/core/syntax.css',
@ -1748,17 +1851,70 @@ celerity_register_resource_map(array(
), array(
'packages' =>
array(
'03ef179e' =>
'080edee4' =>
array(
'name' => 'typeahead.pkg.js',
'symbols' =>
array(
0 => 'javelin-typeahead',
1 => 'javelin-typeahead-normalizer',
2 => 'javelin-typeahead-source',
3 => 'javelin-typeahead-preloaded-source',
4 => 'javelin-typeahead-ondemand-source',
5 => 'javelin-tokenizer',
6 => 'javelin-behavior-aphront-basic-tokenizer',
),
'uri' => '/res/pkg/080edee4/typeahead.pkg.js',
'type' => 'js',
),
'46547a92' =>
array(
'name' => 'core.pkg.js',
'symbols' =>
array(
0 => 'javelin-mask',
1 => 'javelin-workflow',
2 => 'javelin-behavior-workflow',
3 => 'javelin-behavior-aphront-form-disable-on-submit',
4 => 'phabricator-keyboard-shortcut-manager',
5 => 'phabricator-keyboard-shortcut',
6 => 'javelin-behavior-phabricator-keyboard-shortcuts',
7 => 'javelin-behavior-refresh-csrf',
8 => 'javelin-behavior-phabricator-watch-anchor',
),
'uri' => '/res/pkg/46547a92/core.pkg.js',
'type' => 'js',
),
'4fbae2af' =>
array(
'name' => 'javelin.pkg.js',
'symbols' =>
array(
0 => 'javelin-util',
1 => 'javelin-install',
2 => 'javelin-event',
3 => 'javelin-stratcom',
4 => 'javelin-behavior',
5 => 'javelin-request',
6 => 'javelin-vector',
7 => 'javelin-dom',
8 => 'javelin-json',
9 => 'javelin-uri',
),
'uri' => '/res/pkg/4fbae2af/javelin.pkg.js',
'type' => 'js',
),
'61f9d480' =>
array(
'name' => 'diffusion.pkg.css',
'symbols' =>
array(
0 => 'diffusion-commit-view-css',
),
'uri' => '/res/pkg/03ef179e/diffusion.pkg.css',
'uri' => '/res/pkg/61f9d480/diffusion.pkg.css',
'type' => 'css',
),
'2729c1c6' =>
'6a6def05' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@ -1779,44 +1935,10 @@ celerity_register_resource_map(array(
13 => 'phabricator-remarkup-css',
14 => 'syntax-highlighting-css',
),
'uri' => '/res/pkg/2729c1c6/core.pkg.css',
'uri' => '/res/pkg/6a6def05/core.pkg.css',
'type' => 'css',
),
'46547a92' =>
array(
'name' => 'core.pkg.js',
'symbols' =>
array(
0 => 'javelin-mask',
1 => 'javelin-workflow',
2 => 'javelin-behavior-workflow',
3 => 'javelin-behavior-aphront-form-disable-on-submit',
4 => 'phabricator-keyboard-shortcut-manager',
5 => 'phabricator-keyboard-shortcut',
6 => 'javelin-behavior-phabricator-keyboard-shortcuts',
7 => 'javelin-behavior-refresh-csrf',
8 => 'javelin-behavior-phabricator-watch-anchor',
),
'uri' => '/res/pkg/46547a92/core.pkg.js',
'type' => 'js',
),
'540effd7' =>
array(
'name' => 'typeahead.pkg.js',
'symbols' =>
array(
0 => 'javelin-typeahead',
1 => 'javelin-typeahead-normalizer',
2 => 'javelin-typeahead-source',
3 => 'javelin-typeahead-preloaded-source',
4 => 'javelin-typeahead-ondemand-source',
5 => 'javelin-tokenizer',
6 => 'javelin-behavior-aphront-basic-tokenizer',
),
'uri' => '/res/pkg/540effd7/typeahead.pkg.js',
'type' => 'js',
),
'80580cea' =>
'8152415f' =>
array(
'name' => 'differential.pkg.css',
'symbols' =>
@ -1834,29 +1956,10 @@ celerity_register_resource_map(array(
10 => 'phabricator-content-source-view-css',
11 => 'differential-local-commits-view-css',
),
'uri' => '/res/pkg/80580cea/differential.pkg.css',
'uri' => '/res/pkg/8152415f/differential.pkg.css',
'type' => 'css',
),
'b164acea' =>
array(
'name' => 'javelin.pkg.js',
'symbols' =>
array(
0 => 'javelin-util',
1 => 'javelin-install',
2 => 'javelin-event',
3 => 'javelin-stratcom',
4 => 'javelin-behavior',
5 => 'javelin-request',
6 => 'javelin-vector',
7 => 'javelin-dom',
8 => 'javelin-json',
9 => 'javelin-uri',
),
'uri' => '/res/pkg/b164acea/javelin.pkg.js',
'type' => 'js',
),
'ef420ead' =>
'c8aaade8' =>
array(
'name' => 'differential.pkg.js',
'symbols' =>
@ -1877,80 +1980,80 @@ celerity_register_resource_map(array(
13 => 'javelin-behavior-phabricator-object-selector',
14 => 'differential-inline-comment-editor',
),
'uri' => '/res/pkg/ef420ead/differential.pkg.js',
'uri' => '/res/pkg/c8aaade8/differential.pkg.js',
'type' => 'js',
),
),
'reverse' =>
array(
'aphront-crumbs-view-css' => '2729c1c6',
'aphront-dialog-view-css' => '2729c1c6',
'aphront-form-view-css' => '2729c1c6',
'aphront-headsup-action-list-view-css' => '80580cea',
'aphront-list-filter-view-css' => '2729c1c6',
'aphront-panel-view-css' => '2729c1c6',
'aphront-side-nav-view-css' => '2729c1c6',
'aphront-table-view-css' => '2729c1c6',
'aphront-tokenizer-control-css' => '2729c1c6',
'aphront-typeahead-control-css' => '2729c1c6',
'differential-changeset-view-css' => '80580cea',
'differential-core-view-css' => '80580cea',
'differential-inline-comment-editor' => 'ef420ead',
'differential-local-commits-view-css' => '80580cea',
'differential-revision-add-comment-css' => '80580cea',
'differential-revision-comment-css' => '80580cea',
'differential-revision-comment-list-css' => '80580cea',
'differential-revision-detail-css' => '80580cea',
'differential-revision-history-css' => '80580cea',
'differential-table-of-contents-css' => '80580cea',
'diffusion-commit-view-css' => '03ef179e',
'javelin-behavior' => 'b164acea',
'javelin-behavior-aphront-basic-tokenizer' => '540effd7',
'javelin-behavior-aphront-drag-and-drop' => 'ef420ead',
'javelin-behavior-aphront-drag-and-drop-textarea' => 'ef420ead',
'aphront-crumbs-view-css' => '6a6def05',
'aphront-dialog-view-css' => '6a6def05',
'aphront-form-view-css' => '6a6def05',
'aphront-headsup-action-list-view-css' => '8152415f',
'aphront-list-filter-view-css' => '6a6def05',
'aphront-panel-view-css' => '6a6def05',
'aphront-side-nav-view-css' => '6a6def05',
'aphront-table-view-css' => '6a6def05',
'aphront-tokenizer-control-css' => '6a6def05',
'aphront-typeahead-control-css' => '6a6def05',
'differential-changeset-view-css' => '8152415f',
'differential-core-view-css' => '8152415f',
'differential-inline-comment-editor' => 'c8aaade8',
'differential-local-commits-view-css' => '8152415f',
'differential-revision-add-comment-css' => '8152415f',
'differential-revision-comment-css' => '8152415f',
'differential-revision-comment-list-css' => '8152415f',
'differential-revision-detail-css' => '8152415f',
'differential-revision-history-css' => '8152415f',
'differential-table-of-contents-css' => '8152415f',
'diffusion-commit-view-css' => '61f9d480',
'javelin-behavior' => '4fbae2af',
'javelin-behavior-aphront-basic-tokenizer' => '080edee4',
'javelin-behavior-aphront-drag-and-drop' => 'c8aaade8',
'javelin-behavior-aphront-drag-and-drop-textarea' => 'c8aaade8',
'javelin-behavior-aphront-form-disable-on-submit' => '46547a92',
'javelin-behavior-differential-accept-with-errors' => 'ef420ead',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'ef420ead',
'javelin-behavior-differential-comment-jump' => 'ef420ead',
'javelin-behavior-differential-diff-radios' => 'ef420ead',
'javelin-behavior-differential-edit-inline-comments' => 'ef420ead',
'javelin-behavior-differential-feedback-preview' => 'ef420ead',
'javelin-behavior-differential-keyboard-navigation' => 'ef420ead',
'javelin-behavior-differential-populate' => 'ef420ead',
'javelin-behavior-differential-show-more' => 'ef420ead',
'javelin-behavior-differential-accept-with-errors' => 'c8aaade8',
'javelin-behavior-differential-add-reviewers-and-ccs' => 'c8aaade8',
'javelin-behavior-differential-comment-jump' => 'c8aaade8',
'javelin-behavior-differential-diff-radios' => 'c8aaade8',
'javelin-behavior-differential-edit-inline-comments' => 'c8aaade8',
'javelin-behavior-differential-feedback-preview' => 'c8aaade8',
'javelin-behavior-differential-keyboard-navigation' => 'c8aaade8',
'javelin-behavior-differential-populate' => 'c8aaade8',
'javelin-behavior-differential-show-more' => 'c8aaade8',
'javelin-behavior-phabricator-keyboard-shortcuts' => '46547a92',
'javelin-behavior-phabricator-object-selector' => 'ef420ead',
'javelin-behavior-phabricator-object-selector' => 'c8aaade8',
'javelin-behavior-phabricator-watch-anchor' => '46547a92',
'javelin-behavior-refresh-csrf' => '46547a92',
'javelin-behavior-workflow' => '46547a92',
'javelin-dom' => 'b164acea',
'javelin-event' => 'b164acea',
'javelin-install' => 'b164acea',
'javelin-json' => 'b164acea',
'javelin-dom' => '4fbae2af',
'javelin-event' => '4fbae2af',
'javelin-install' => '4fbae2af',
'javelin-json' => '4fbae2af',
'javelin-mask' => '46547a92',
'javelin-request' => 'b164acea',
'javelin-stratcom' => 'b164acea',
'javelin-tokenizer' => '540effd7',
'javelin-typeahead' => '540effd7',
'javelin-typeahead-normalizer' => '540effd7',
'javelin-typeahead-ondemand-source' => '540effd7',
'javelin-typeahead-preloaded-source' => '540effd7',
'javelin-typeahead-source' => '540effd7',
'javelin-uri' => 'b164acea',
'javelin-util' => 'b164acea',
'javelin-vector' => 'b164acea',
'javelin-request' => '4fbae2af',
'javelin-stratcom' => '4fbae2af',
'javelin-tokenizer' => '080edee4',
'javelin-typeahead' => '080edee4',
'javelin-typeahead-normalizer' => '080edee4',
'javelin-typeahead-ondemand-source' => '080edee4',
'javelin-typeahead-preloaded-source' => '080edee4',
'javelin-typeahead-source' => '080edee4',
'javelin-uri' => '4fbae2af',
'javelin-util' => '4fbae2af',
'javelin-vector' => '4fbae2af',
'javelin-workflow' => '46547a92',
'phabricator-content-source-view-css' => '80580cea',
'phabricator-core-buttons-css' => '2729c1c6',
'phabricator-core-css' => '2729c1c6',
'phabricator-directory-css' => '2729c1c6',
'phabricator-drag-and-drop-file-upload' => 'ef420ead',
'phabricator-content-source-view-css' => '8152415f',
'phabricator-core-buttons-css' => '6a6def05',
'phabricator-core-css' => '6a6def05',
'phabricator-directory-css' => '6a6def05',
'phabricator-drag-and-drop-file-upload' => 'c8aaade8',
'phabricator-keyboard-shortcut' => '46547a92',
'phabricator-keyboard-shortcut-manager' => '46547a92',
'phabricator-object-selector-css' => '80580cea',
'phabricator-remarkup-css' => '2729c1c6',
'phabricator-shaped-request' => 'ef420ead',
'phabricator-standard-page-view' => '2729c1c6',
'syntax-highlighting-css' => '2729c1c6',
'phabricator-object-selector-css' => '8152415f',
'phabricator-remarkup-css' => '6a6def05',
'phabricator-shaped-request' => 'c8aaade8',
'phabricator-standard-page-view' => '6a6def05',
'syntax-highlighting-css' => '6a6def05',
),
));

View file

@ -204,6 +204,8 @@ phutil_register_library_map(array(
'DifferentialCommitsFieldSpecification' => 'applications/differential/field/specification/commits',
'DifferentialController' => 'applications/differential/controller/base',
'DifferentialDAO' => 'applications/differential/storage/base',
'DifferentialDateCreatedFieldSpecification' => 'applications/differential/field/specification/datecreated',
'DifferentialDateModifiedFieldSpecification' => 'applications/differential/field/specification/datemodified',
'DifferentialDefaultFieldSelector' => 'applications/differential/field/selector/default',
'DifferentialDependenciesFieldSpecification' => 'applications/differential/field/specification/dependencies',
'DifferentialDiff' => 'applications/differential/storage/diff',
@ -256,6 +258,8 @@ phutil_register_library_map(array(
'DifferentialRevisionListData' => 'applications/differential/data/revisionlist',
'DifferentialRevisionListView' => 'applications/differential/view/revisionlist',
'DifferentialRevisionQuery' => 'applications/differential/query/revision',
'DifferentialRevisionStatsController' => 'applications/differential/controller/revisionstats',
'DifferentialRevisionStatsView' => 'applications/differential/view/revisionstats',
'DifferentialRevisionStatusFieldSpecification' => 'applications/differential/field/specification/revisionstatus',
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/revisionupdatehistory',
'DifferentialRevisionViewController' => 'applications/differential/controller/revisionview',
@ -275,6 +279,8 @@ phutil_register_library_map(array(
'DiffusionBrowseQuery' => 'applications/diffusion/query/browse/base',
'DiffusionBrowseTableView' => 'applications/diffusion/view/browsetable',
'DiffusionChangeController' => 'applications/diffusion/controller/change',
'DiffusionCommentListView' => 'applications/diffusion/view/commentlist',
'DiffusionCommentView' => 'applications/diffusion/view/comment',
'DiffusionCommitChangeTableView' => 'applications/diffusion/view/commitchangetable',
'DiffusionCommitController' => 'applications/diffusion/controller/commit',
'DiffusionCommitListController' => 'applications/diffusion/controller/commitlist',
@ -399,6 +405,7 @@ phutil_register_library_map(array(
'ManiphestAuxiliaryFieldSpecification' => 'applications/maniphest/auxiliaryfield/base',
'ManiphestAuxiliaryFieldTypeException' => 'applications/maniphest/auxiliaryfield/typeexception',
'ManiphestAuxiliaryFieldValidationException' => 'applications/maniphest/auxiliaryfield/validationexception',
'ManiphestBatchEditController' => 'applications/maniphest/controller/batch',
'ManiphestConstants' => 'applications/maniphest/constants/base',
'ManiphestController' => 'applications/maniphest/controller/base',
'ManiphestDAO' => 'applications/maniphest/storage/base',
@ -430,12 +437,19 @@ phutil_register_library_map(array(
'ManiphestTransactionSaveController' => 'applications/maniphest/controller/transactionsave',
'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype',
'ManiphestView' => 'applications/maniphest/view/base',
'MetaMTAConstants' => 'applications/metamta/constants/base',
'MetaMTANotificationType' => 'applications/metamta/constants/notificationtype',
'Phabricator404Controller' => 'applications/base/controller/404',
'PhabricatorAuditActionConstants' => 'applications/audit/constants/action',
'PhabricatorAuditAddCommentController' => 'applications/audit/controller/addcomment',
'PhabricatorAuditComment' => 'applications/audit/storage/auditcomment',
'PhabricatorAuditCommentEditor' => 'applications/audit/editor/comment',
'PhabricatorAuditController' => 'applications/audit/controller/base',
'PhabricatorAuditDAO' => 'applications/audit/storage/base',
'PhabricatorAuditEditController' => 'applications/audit/controller/edit',
'PhabricatorAuditListController' => 'applications/audit/controller/list',
'PhabricatorAuditListView' => 'applications/audit/view/list',
'PhabricatorAuditQuery' => 'applications/audit/query/audit',
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
'PhabricatorAuthController' => 'applications/auth/controller/base',
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
@ -559,6 +573,7 @@ phutil_register_library_map(array(
'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message',
'PhabricatorIRCObjectNameHandler' => 'infrastructure/daemon/irc/handler/objectname',
'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/irc/handler/protocol',
'PhabricatorIRCWhatsNewHandler' => 'infrastructure/daemon/irc/handler/whatsnew',
'PhabricatorImageTransformer' => 'applications/files/transform',
'PhabricatorInfrastructureTestCase' => 'infrastructure/__tests__',
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin',
@ -595,6 +610,16 @@ phutil_register_library_map(array(
'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/sendgridreceive',
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/mysql',
'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/clientauthorization',
'PhabricatorOAuthClientAuthorizationBaseController' => 'applications/oauthserver/controller/clientauthorization/base',
'PhabricatorOAuthClientAuthorizationDeleteController' => 'applications/oauthserver/controller/clientauthorization/delete',
'PhabricatorOAuthClientAuthorizationEditController' => 'applications/oauthserver/controller/clientauthorization/edit',
'PhabricatorOAuthClientAuthorizationListController' => 'applications/oauthserver/controller/clientauthorization/list',
'PhabricatorOAuthClientBaseController' => 'applications/oauthserver/controller/client/base',
'PhabricatorOAuthClientDeleteController' => 'applications/oauthserver/controller/client/delete',
'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/edit',
'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/list',
'PhabricatorOAuthClientViewController' => 'applications/oauthserver/controller/client/view',
'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default',
'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics',
'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure',
@ -603,7 +628,19 @@ phutil_register_library_map(array(
'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook',
'PhabricatorOAuthProviderGitHub' => 'applications/auth/oauth/provider/github',
'PhabricatorOAuthProviderGoogle' => 'applications/auth/oauth/provider/google',
'PhabricatorOAuthProviderPhabricator' => 'applications/auth/oauth/provider/phabricator',
'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base',
'PhabricatorOAuthResponse' => 'applications/oauthserver/response',
'PhabricatorOAuthServer' => 'applications/oauthserver/server',
'PhabricatorOAuthServerAccessToken' => 'applications/oauthserver/storage/accesstoken',
'PhabricatorOAuthServerAuthController' => 'applications/oauthserver/controller/auth',
'PhabricatorOAuthServerAuthorizationCode' => 'applications/oauthserver/storage/authorizationcode',
'PhabricatorOAuthServerClient' => 'applications/oauthserver/storage/client',
'PhabricatorOAuthServerController' => 'applications/oauthserver/controller/base',
'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/base',
'PhabricatorOAuthServerScope' => 'applications/oauthserver/scope',
'PhabricatorOAuthServerTestController' => 'applications/oauthserver/controller/test',
'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/token',
'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink',
'PhabricatorObjectAttachmentEditor' => 'applications/search/editor/attach',
'PhabricatorObjectGraph' => 'applications/phid/graph',
@ -653,6 +690,7 @@ phutil_register_library_map(array(
'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit',
'PhabricatorProjectQuery' => 'applications/project/query/project',
'PhabricatorProjectQueryUtil' => 'applications/project/query/util',
'PhabricatorProjectStatus' => 'applications/project/constants/status',
'PhabricatorProjectSubproject' => 'applications/project/storage/subproject',
'PhabricatorProjectTransaction' => 'applications/project/storage/transaction',
@ -760,6 +798,7 @@ phutil_register_library_map(array(
'PhabricatorTimelineEventData' => 'infrastructure/daemon/timeline/storage/eventdata',
'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/iterator',
'PhabricatorTimer' => 'applications/countdown/storage/timer',
'PhabricatorTransactionView' => 'view/layout/transaction',
'PhabricatorTransformedFile' => 'applications/files/storage/transformed',
'PhabricatorTrivialTestCase' => 'infrastructure/testing/testcase/__tests__',
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/common',
@ -1020,6 +1059,8 @@ phutil_register_library_map(array(
'DifferentialCommitsFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialController' => 'PhabricatorController',
'DifferentialDAO' => 'PhabricatorLiskDAO',
'DifferentialDateCreatedFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialDateModifiedFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialDefaultFieldSelector' => 'DifferentialFieldSelector',
'DifferentialDependenciesFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialDiff' => 'DifferentialDAO',
@ -1058,6 +1099,8 @@ phutil_register_library_map(array(
'DifferentialRevisionIDFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialRevisionListController' => 'DifferentialController',
'DifferentialRevisionListView' => 'AphrontView',
'DifferentialRevisionStatsController' => 'DifferentialController',
'DifferentialRevisionStatsView' => 'AphrontView',
'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification',
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
'DifferentialRevisionViewController' => 'DifferentialController',
@ -1071,6 +1114,8 @@ phutil_register_library_map(array(
'DiffusionBrowseFileController' => 'DiffusionController',
'DiffusionBrowseTableView' => 'DiffusionView',
'DiffusionChangeController' => 'DiffusionController',
'DiffusionCommentListView' => 'AphrontView',
'DiffusionCommentView' => 'AphrontView',
'DiffusionCommitChangeTableView' => 'DiffusionView',
'DiffusionCommitController' => 'DiffusionController',
'DiffusionCommitListController' => 'DiffusionController',
@ -1157,6 +1202,7 @@ phutil_register_library_map(array(
'LiskIsolationTestDAO' => 'LiskDAO',
'ManiphestAction' => 'PhrictionConstants',
'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
'ManiphestBatchEditController' => 'ManiphestController',
'ManiphestController' => 'PhabricatorController',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
@ -1184,11 +1230,15 @@ phutil_register_library_map(array(
'ManiphestTransactionSaveController' => 'ManiphestController',
'ManiphestTransactionType' => 'ManiphestConstants',
'ManiphestView' => 'AphrontView',
'MetaMTANotificationType' => 'MetaMTAConstants',
'Phabricator404Controller' => 'PhabricatorController',
'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController',
'PhabricatorAuditComment' => 'PhabricatorAuditDAO',
'PhabricatorAuditController' => 'PhabricatorController',
'PhabricatorAuditDAO' => 'PhabricatorLiskDAO',
'PhabricatorAuditEditController' => 'PhabricatorAuditController',
'PhabricatorAuditListController' => 'PhabricatorAuditController',
'PhabricatorAuditListView' => 'AphrontView',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
'PhabricatorCalendarController' => 'PhabricatorController',
@ -1291,6 +1341,7 @@ phutil_register_library_map(array(
'PhabricatorIRCLogHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCProtocolHandler' => 'PhabricatorIRCHandler',
'PhabricatorIRCWhatsNewHandler' => 'PhabricatorIRCHandler',
'PhabricatorInfrastructureTestCase' => 'PhabricatorTestCase',
'PhabricatorJavelinLinter' => 'ArcanistLinter',
'PhabricatorLintEngine' => 'PhutilLintEngine',
@ -1321,6 +1372,16 @@ phutil_register_library_map(array(
'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorOAuthClientAuthorization' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthClientAuthorizationBaseController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthClientAuthorizationDeleteController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationEditController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientAuthorizationListController' => 'PhabricatorOAuthClientAuthorizationBaseController',
'PhabricatorOAuthClientBaseController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthClientDeleteController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthClientViewController' => 'PhabricatorOAuthClientBaseController',
'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController',
'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController',
'PhabricatorOAuthFailureView' => 'AphrontView',
@ -1328,7 +1389,17 @@ phutil_register_library_map(array(
'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderGitHub' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderGoogle' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthProviderPhabricator' => 'PhabricatorOAuthProvider',
'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController',
'PhabricatorOAuthResponse' => 'AphrontResponse',
'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerAuthController' => 'PhabricatorAuthController',
'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerClient' => 'PhabricatorOAuthServerDAO',
'PhabricatorOAuthServerController' => 'PhabricatorController',
'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO',
'PhabricatorOAuthServerTestController' => 'PhabricatorOAuthServerController',
'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController',
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorObjectGraph' => 'AbstractDirectedGraph',
'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants',
@ -1460,6 +1531,7 @@ phutil_register_library_map(array(
'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO',
'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO',
'PhabricatorTimer' => 'PhabricatorCountdownDAO',
'PhabricatorTransactionView' => 'AphrontView',
'PhabricatorTransformedFile' => 'PhabricatorFileDAO',
'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',

View file

@ -32,8 +32,13 @@ class AphrontDefaultApplicationConfiguration
public function getURIMap() {
return $this->getResourceURIMapRules() + array(
'/(?:(?P<filter>(?:feed|jump))/)?$' =>
'/(?:(?P<filter>jump)/)?$' =>
'PhabricatorDirectoryMainController',
'/(?:(?P<filter>feed)/)' => array(
'public/$' => 'PhabricatorFeedPublicStreamController',
'(?:(?P<subfilter>[^/]+)/)?$' =>
'PhabricatorDirectoryMainController',
),
'/directory/' => array(
'(?P<id>\d+)/$'
=> 'PhabricatorDirectoryCategoryViewController',
@ -96,6 +101,7 @@ class AphrontDefaultApplicationConfiguration
'/differential/' => array(
'$' => 'DifferentialRevisionListController',
'filter/(?P<filter>\w+)/$' => 'DifferentialRevisionListController',
'stats/(?P<filter>\w+)/$' => 'DifferentialRevisionStatsController',
'diff/' => array(
'(?P<id>\d+)/$' => 'DifferentialDiffViewController',
'create/$' => 'DifferentialDiffCreateController',
@ -151,6 +157,27 @@ class AphrontDefaultApplicationConfiguration
),
),
'/oauthserver/' => array(
'auth/' => 'PhabricatorOAuthServerAuthController',
'test/' => 'PhabricatorOAuthServerTestController',
'token/' => 'PhabricatorOAuthServerTokenController',
'clientauthorization/' => array(
'$' => 'PhabricatorOAuthClientAuthorizationListController',
'delete/(?P<phid>[^/]+)/' =>
'PhabricatorOAuthClientAuthorizationDeleteController',
'edit/(?P<phid>[^/]+)/' =>
'PhabricatorOAuthClientAuthorizationEditController',
),
'client/' => array(
'$' => 'PhabricatorOAuthClientListController',
'create/$' => 'PhabricatorOAuthClientEditController',
'delete/(?P<phid>[^/]+)/$' =>
'PhabricatorOAuthClientDeleteController',
'edit/(?P<phid>[^/]+)/$' => 'PhabricatorOAuthClientEditController',
'view/(?P<phid>[^/]+)/$' => 'PhabricatorOAuthClientViewController',
),
),
'/xhprof/' => array(
'profile/(?P<phid>[^/]+)/$' => 'PhabricatorXHProfProfileController',
),
@ -165,6 +192,7 @@ class AphrontDefaultApplicationConfiguration
'$' => 'ManiphestTaskListController',
'view/(?P<view>\w+)/$' => 'ManiphestTaskListController',
'report/(?:(?P<view>\w+)/)?$' => 'ManiphestReportController',
'batch/$' => 'ManiphestBatchEditController',
'task/' => array(
'create/$' => 'ManiphestTaskEditController',
'edit/(?P<id>\d+)/$' => 'ManiphestTaskEditController',
@ -318,8 +346,11 @@ class AphrontDefaultApplicationConfiguration
),
'/audit/' => array(
'$' => 'PhabricatorAuditEditController',
'$' => 'PhabricatorAuditListController',
'view/(?P<filter>[^/]+)/$' => 'PhabricatorAuditListController',
'edit/$' => 'PhabricatorAuditEditController',
'addcomment/$' => 'PhabricatorAuditAddCommentController',
),
'/xhpast/' => array(
@ -359,8 +390,6 @@ class AphrontDefaultApplicationConfiguration
=> 'PhabricatorCountdownDeleteController'
),
'/feed/public/$' => 'PhabricatorFeedPublicStreamController',
'/V(?P<id>\d+)$' => 'PhabricatorSlowvotePollController',
'/vote/' => array(
'(?:view/(?P<view>\w+)/)?$' => 'PhabricatorSlowvoteListController',

View file

@ -21,15 +21,28 @@
*/
class Aphront403Response extends AphrontWebpageResponse {
private $forbiddenText;
public function setForbiddenText($text) {
$this->forbiddenText = $text;
return $this;
}
private function getForbiddenText() {
return $this->forbiddenText;
}
public function getHTTPResponseCode() {
return 403;
}
public function buildResponseString() {
$forbidden_text = $this->getForbiddenText();
if (!$forbidden_text) {
$forbidden_text =
'You do not have privileges to access the requested page.';
}
$failure = new AphrontRequestFailureView();
$failure->setHeader('403 Forbidden');
$failure->appendChild(
'<p>You do not have privileges to access the requested page.</p>');
$failure->appendChild('<p>'.$forbidden_text.'</p>');
$view = new PhabricatorStandardPageView();
$view->setTitle('403 Forbidden');

View file

@ -35,9 +35,8 @@ final class AphrontAjaxResponse extends AphrontResponse {
$this->content,
$this->error);
return $this->encodeJSONForHTTPResponse(
$object,
$use_javelin_shield = true);
$response_json = $this->encodeJSONForHTTPResponse($object);
return $this->addJSONShield($response_json, $use_javelin_shield = true);
}
public function getHeaders() {

View file

@ -70,9 +70,7 @@ abstract class AphrontResponse {
return $this;
}
protected function encodeJSONForHTTPResponse(
array $object,
$use_javelin_shield) {
protected function encodeJSONForHTTPResponse(array $object) {
$response = json_encode($object);
@ -84,6 +82,11 @@ abstract class AphrontResponse {
array('\u003c', '\u003e'),
$response);
return $response;
}
protected function addJSONShield($json_response, $use_javelin_shield) {
// Add a shield to prevent "JSON Hijacking" attacks where an attacker
// requests a JSON response using a normal <script /> tag and then uses
// Object.prototype.__defineSetter__() or similar to read response data.
@ -96,7 +99,7 @@ abstract class AphrontResponse {
? 'for (;;);'
: 'for(;;);';
$response = $shield.$response;
$response = $shield.$json_response;
return $response;
}

View file

@ -29,10 +29,8 @@ final class AphrontJSONResponse extends AphrontResponse {
}
public function buildResponseString() {
$response = $this->encodeJSONForHTTPResponse(
$this->content,
$use_javelin_shield = false);
return $response;
$response = $this->encodeJSONForHTTPResponse($this->content);
return $this->addJSONShield($response, $use_javelin_shield = false);
}
public function getHeaders() {

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,13 +20,13 @@ class PhabricatorAuditActionConstants {
const CONCERN = 'concern';
const ACCEPT = 'accept';
// TODO: enable comment
//const COMMENT = 'comment';
const COMMENT = 'comment';
public static function getActionNameMap() {
static $map = array(
self::CONCERN => 'Have Concern',
self::ACCEPT => 'Accept',
self::COMMENT => 'Comment',
self::CONCERN => 'Raise Concern',
self::ACCEPT => 'Accept Commit',
);
return $map;

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
* limitations under the License.
*/
class PhabricatorAuditStatusConstants {
final class PhabricatorAuditStatusConstants {
const NONE = '';
const AUDIT_NOT_REQUIRED = 'audit-not-required';
@ -26,14 +26,18 @@ class PhabricatorAuditStatusConstants {
public static function getStatusNameMap() {
static $map = array(
self::NONE => 'Not Apply',
self::AUDIT_NOT_REQUIRED => 'Audit Not Required',
self::AUDIT_REQUIRED => 'Audit Required',
self::CONCERNED => 'Concerned',
self::ACCEPTED => 'Accepted',
self::NONE => 'Not Applicable',
self::AUDIT_NOT_REQUIRED => 'Audit Not Required',
self::AUDIT_REQUIRED => 'Audit Required',
self::CONCERNED => 'Concern Raised',
self::ACCEPTED => 'Accepted',
);
return $map;
}
public static function getStatusName($code) {
return idx(self::getStatusNameMap(), $code, 'Unknown');
}
}

View file

@ -6,5 +6,7 @@
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditStatusConstants.php');

View file

@ -0,0 +1,54 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorAuditAddCommentController
extends PhabricatorAuditController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if (!$request->isFormPost()) {
return new Aphront403Response();
}
$commit_phid = $request->getStr('commit');
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
'phid = %s',
$commit_phid);
if (!$commit) {
return new Aphront404Response();
}
$comment = id(new PhabricatorAuditComment())
->setAction($request->getStr('action'))
->setContent($request->getStr('content'));
id(new PhabricatorAuditCommentEditor($commit))
->setUser($user)
->addComment($comment);
$phids = array($commit_phid);
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$uri = $handles[$commit_phid]->getURI();
return id(new AphrontRedirectResponse())->setURI($uri);
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/403');
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/audit/controller/base');
phutil_require_module('phabricator', 'applications/audit/editor/comment');
phutil_require_module('phabricator', 'applications/audit/storage/auditcomment');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/repository/storage/commit');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditAddCommentController.php');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -228,53 +228,21 @@ class PhabricatorAuditEditController extends PhabricatorAuditController {
private function saveAuditComments() {
$action = $this->request->getStr('action');
$status_map = PhabricatorAuditActionConstants::getStatusNameMap();
$status = idx($status_map, $action, null);
if ($status === null) {
return $this->buildStandardPageResponse(
id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_ERROR)
->setTitle("Action {$action} is invalid."),
array(
'title' => 'Audit a Commit',
));
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
'phid = %s',
$this->commitPHID);
if (!$commit) {
throw new Exception("No such commit!");
}
id(new PhabricatorAuditComment())
->setActorPHID($this->user->getPHID())
->setTargetPHID($this->commitPHID)
$comment = id(new PhabricatorAuditComment())
->setAction($action)
->setContent($this->request->getStr('comments'))
->save();
->setContent($this->request->getStr('comments'));
// Update the audit status for all the relationships <commit, package>
// where the package is owned by the user. When a user owns several
// packages and a commit touches all of them,It should be good enough for
// the user to approve it once to get all the relationships automatically
// updated.
$owned_packages = id(new PhabricatorOwnersOwner())->loadAllWhere(
'userPHID = %s',
$this->user->getPHID());
$owned_package_ids = mpull($owned_packages, 'getPackageID');
$conn_r = id(new PhabricatorOwnersPackage())->establishConnection('r');
$owned_package_phids = queryfx_all(
$conn_r,
'SELECT `phid` FROM %T WHERE id IN (%Ld)',
id(new PhabricatorOwnersPackage())->getTableName(),
$owned_package_ids);
$owned_package_phids = ipull($owned_package_phids, 'phid');
$relationships = id(new PhabricatorOwnersPackageCommitRelationship())
->loadAllWhere(
'commitPHID = %s AND packagePHID IN (%Ls)',
$this->commitPHID,
$owned_package_phids);
foreach ($relationships as $relationship) {
$relationship->setAuditStatus($status);
$relationship->save();
}
$editor = id(new PhabricatorAuditCommentEditor($commit))
->setUser($this->user)
->addComment($comment);
return id(new AphrontRedirectResponse())
->setURI(sprintf('/audit/edit/?c-phid=%s&p-phid=%s',

View file

@ -11,12 +11,14 @@ phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/audit/constants/action');
phutil_require_module('phabricator', 'applications/audit/constants/status');
phutil_require_module('phabricator', 'applications/audit/controller/base');
phutil_require_module('phabricator', 'applications/audit/editor/comment');
phutil_require_module('phabricator', 'applications/audit/storage/auditcomment');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
phutil_require_module('phabricator', 'applications/owners/storage/owner');
phutil_require_module('phabricator', 'applications/owners/storage/package');
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/repository/storage/commit');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/divider');

View file

@ -0,0 +1,68 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorAuditListController extends PhabricatorAuditController {
private $filter;
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
public function processRequest() {
$request = $this->getRequest();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/audit/view/'));
$nav->addLabel('Audits');
$nav->addFilter('all', 'All');
$this->filter = $nav->selectFilter($this->filter, 'all');
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'offset');
$query = new PhabricatorAuditQuery();
$query->setOffset($pager->getOffset());
$query->setLimit($pager->getPageSize() + 1);
$audits = $query->execute();
$audits = $pager->sliceResults($audits);
$view = new PhabricatorAuditListView();
$view->setAudits($audits);
$phids = $view->getRequiredHandlePHIDs();
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view->setHandles($handles);
$panel = new AphrontPanelView();
$panel->appendChild($view);
$panel->setHeader('Audits');
$nav->appendChild($panel);
$nav->appendChild($pager);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Audits',
));
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/audit/controller/base');
phutil_require_module('phabricator', 'applications/audit/query/audit');
phutil_require_module('phabricator', 'applications/audit/view/list');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditListController.php');

View file

@ -0,0 +1,115 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorAuditCommentEditor {
private $commit;
private $user;
public function __construct(PhabricatorRepositoryCommit $commit) {
$this->commit = $commit;
return $this;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function addComment(PhabricatorAuditComment $comment) {
$commit = $this->commit;
$user = $this->user;
$comment
->setActorPHID($user->getPHID())
->setTargetPHID($commit->getPHID())
->save();
// When a user submits an audit comment, we update all the audit requests
// they have authority over to reflect the most recent status. The general
// idea here is that if audit has triggered for, e.g., several packages, but
// a user owns all of them, they can clear the audit requirement in one go
// without auditing the commit for each trigger.
$audit_phids = self::loadAuditPHIDsForUser($this->user);
$audit_phids = array_fill_keys($audit_phids, true);
$relationships = id(new PhabricatorOwnersPackageCommitRelationship())
->loadAllWhere(
'commitPHID = %s',
$commit->getPHID());
$action = $comment->getAction();
$status_map = PhabricatorAuditActionConstants::getStatusNameMap();
$status = idx($status_map, $action, null);
// Status may be empty for updates which don't affect status, like
// "comment".
if ($status) {
foreach ($relationships as $relationship) {
if (empty($audit_phids[$relationship->getPackagePHID()])) {
continue;
}
$relationship->setAuditStatus($status);
$relationship->save();
}
}
// TODO: News feed.
// TODO: Search index.
// TODO: Email.
}
/**
* Load the PHIDs for all objects the user has the authority to act as an
* audit for. This includes themselves, and any packages they are an owner
* of.
*/
public static function loadAuditPHIDsForUser(PhabricatorUser $user) {
$phids = array();
// The user can audit on their own behalf.
$phids[$user->getPHID()] = true;
// The user can audit on behalf of all packages they own.
$owned_packages = id(new PhabricatorOwnersOwner())->loadAllWhere(
'userPHID = %s',
$user->getPHID());
if ($owned_packages) {
$packages = id(new PhabricatorOwnersPackage())->loadAllWhere(
'id IN (%Ld)',
mpull($owned_packages, 'getPackageID'));
foreach (mpull($packages, 'getPHID') as $phid) {
$phids[$phid] = true;
}
}
// The user can audit on behalf of all projects they are a member of.
$query = new PhabricatorProjectQuery();
$query->setMembers(array($user->getPHID));
$projects = $query->execute();
foreach ($projects as $project) {
$phids[$project->getPHID()] = true;
}
return array_keys($phids);
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/audit/constants/action');
phutil_require_module('phabricator', 'applications/owners/storage/owner');
phutil_require_module('phabricator', 'applications/owners/storage/package');
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
phutil_require_module('phabricator', 'applications/project/query/project');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditCommentEditor.php');

View file

@ -0,0 +1,91 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorAuditQuery {
private $offset;
private $limit;
private $commitPHIDs;
public function withCommitPHIDs(array $commit_phids) {
$this->commitPHIDs = $commit_phids;
return $this;
}
public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
public function setLimit($limit) {
$this->limit = $limit;
return $this;
}
public function execute() {
$table = new PhabricatorOwnersPackageCommitRelationship();
$conn_r = $table->establishConnection('r');
$where = $this->buildWhereClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q',
$table->getTableName(),
$where,
$limit);
$audits = $table->loadAllFromArray($data);
return $audits;
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->commitPHIDs) {
$where[] = qsprintf(
$conn_r,
'commitPHID IN (%Ls)',
$this->commitPHIDs);
}
if ($where) {
$where = 'WHERE ('.implode(') AND (', $where).')';
} else {
$where = '';
}
return $where;
}
private function buildLimitClause($conn_r) {
if ($this->limit && $this->offset) {
return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, $this->limit);
} else if ($this->limit) {
return qsprintf($conn_r, 'LIMIT %d', $this->limit);
} else if ($this->offset) {
return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, PHP_INT_MAX);
} else {
return '';
}
}
}

View file

@ -0,0 +1,14 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_source('PhabricatorAuditQuery.php');

View file

@ -0,0 +1,102 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorAuditListView extends AphrontView {
private $audits;
private $handles;
public function setAudits(array $audits) {
$this->audits = $audits;
return $this;
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
public function getRequiredHandlePHIDs() {
$phids = array();
foreach ($this->audits as $audit) {
$phids[$audit->getCommitPHID()] = true;
$phids[$audit->getPackagePHID()] = true;
}
return array_keys($phids);
}
private function getHandle($phid) {
$handle = idx($this->handles, $phid);
if (!$handle) {
throw new Exception("No handle for '{$phid}'!");
}
return $handle;
}
public function render() {
$last = null;
$rows = array();
foreach ($this->audits as $audit) {
$commit_phid = $audit->getCommitPHID();
if ($last == $commit_phid) {
$commit_name = null;
} else {
$commit_name = $this->getHandle($commit_phid)->renderLink();
$last = $commit_phid;
}
$reasons = $audit->getAuditReasons();
foreach ($reasons as $key => $reason) {
$reasons[$key] = phutil_escape_html($reason);
}
$reasons = implode('<br />', $reasons);
$status_code = $audit->getAuditStatus();
$status = PhabricatorAuditStatusConstants::getStatusName($status_code);
$auditor_handle = $this->getHandle($audit->getPackagePHID());
$rows[] = array(
$commit_name,
$auditor_handle->renderLink(),
phutil_escape_html($status),
$reasons,
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Commit',
'Auditor',
'Status',
'Details',
));
$table->setColumnClasses(
array(
'pri',
'',
'',
'wide',
));
return $table->render();
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/audit/constants/status');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditListView.php');

View file

@ -21,6 +21,7 @@ abstract class PhabricatorOAuthProvider {
const PROVIDER_FACEBOOK = 'facebook';
const PROVIDER_GITHUB = 'github';
const PROVIDER_GOOGLE = 'google';
const PROVIDER_PHABRICATOR = 'phabricator';
private $accessToken;
@ -108,6 +109,9 @@ abstract class PhabricatorOAuthProvider {
case self::PROVIDER_GOOGLE:
$class = 'PhabricatorOAuthProviderGoogle';
break;
case self::PROVIDER_PHABRICATOR:
$class = 'PhabricatorOAuthProviderPhabricator';
break;
default:
throw new Exception('Unknown OAuth provider.');
}
@ -120,6 +124,7 @@ abstract class PhabricatorOAuthProvider {
self::PROVIDER_FACEBOOK,
self::PROVIDER_GITHUB,
self::PROVIDER_GOOGLE,
self::PROVIDER_PHABRICATOR,
);
$providers = array();
foreach ($all as $provider) {

View file

@ -0,0 +1,132 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorOAuthProviderPhabricator
extends PhabricatorOAuthProvider {
private $userData;
public function decodeTokenResponse($response) {
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
throw new Exception('Invalid token response.');
}
return $decoded;
}
public function getProviderKey() {
return self::PROVIDER_PHABRICATOR;
}
public function getProviderName() {
return 'Phabricator';
}
public function isProviderEnabled() {
return PhabricatorEnv::getEnvConfig('phabricator.auth-enabled');
}
public function isProviderLinkPermanent() {
return PhabricatorEnv::getEnvConfig('phabricator.auth-permanent');
}
public function isProviderRegistrationEnabled() {
return PhabricatorEnv::getEnvConfig('phabricator.registration-enabled');
}
public function getClientID() {
return PhabricatorEnv::getEnvConfig('phabricator.application-id');
}
public function renderGetClientIDHelp() {
return null;
}
public function getClientSecret() {
return PhabricatorEnv::getEnvConfig('phabricator.application-secret');
}
public function renderGetClientSecretHelp() {
return null;
}
public function getAuthURI() {
return $this->getURI('/oauthserver/auth/');
}
public function getTestURIs() {
return array(
$this->getURI('/'),
$this->getURI('/api/user.whoami/')
);
}
public function getTokenURI() {
return $this->getURI('/oauthserver/token/');
}
public function getUserInfoURI() {
return $this->getURI('/api/user.whoami');
}
public function getMinimumScope() {
return 'email';
}
public function setUserData($data) {
// need to strip the javascript shield from conduit
$data = substr($data, 8);
$data = json_decode($data, true);
if (!is_array($data)) {
throw new Exception('Invalid user data.');
}
$this->userData = $data['result'];
return $this;
}
public function retrieveUserID() {
return $this->userData['phid'];
}
public function retrieveUserEmail() {
return $this->userData['email'];
}
public function retrieveUserAccountName() {
return $this->userData['userName'];
}
public function retrieveUserProfileImage() {
$uri = $this->userData['image'];
return @file_get_contents($uri);
}
public function retrieveUserAccountURI() {
return $this->userData['uri'];
}
public function retrieveUserRealName() {
return $this->userData['realName'];
}
private function getURI($path) {
return
rtrim(PhabricatorEnv::getEnvConfig('phabricator.oauth-uri'), '/') .
$path;
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/auth/oauth/provider/base');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_source('PhabricatorOAuthProviderPhabricator.php');

View file

@ -114,6 +114,7 @@ class PhabricatorConduitAPIController
$allow_unguarded_writes = false;
$auth_error = null;
if ($method_handler->shouldRequireAuthentication()) {
$metadata['scope'] = $method_handler->getRequiredScope();
$auth_error = $this->authenticateUser($api_request, $metadata);
// If we've explicitly authenticated the user here and either done
// CSRF validation or are using a non-web authentication mechanism.
@ -247,6 +248,48 @@ class PhabricatorConduitAPIController
return null;
}
// handle oauth
// TODO - T897 (make error codes for OAuth more correct to spec)
// and T891 (strip shield from Conduit response)
$access_token = $request->getStr('access_token');
$method_scope = $metadata['scope'];
if ($access_token &&
$method_scope != PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE) {
$token = id(new PhabricatorOAuthServerAccessToken())
->loadOneWhere('token = %s',
$access_token);
if (!$token) {
return array(
'ERR-INVALID-AUTH',
'Access token does not exist.',
);
}
$oauth_server = new PhabricatorOAuthServer();
$valid = $oauth_server->validateAccessToken($token,
$method_scope);
if (!$valid) {
return array(
'ERR-INVALID-AUTH',
'Access token is invalid.',
);
}
// valid token, so let's log in the user!
$user_phid = $token->getUserPHID();
$user = id(new PhabricatorUser())
->loadOneWhere('phid = %s',
$user_phid);
if (!$user) {
return array(
'ERR-INVALID-AUTH',
'Access token is for invalid user.',
);
}
$api_request->setUser($user);
return null;
}
// Handle sessionless auth. TOOD: This is super messy.
if (isset($metadata['authUser'])) {
$user = id(new PhabricatorUser())->loadOneWhere(

View file

@ -13,6 +13,9 @@ phutil_require_module('phabricator', 'applications/conduit/method/base');
phutil_require_module('phabricator', 'applications/conduit/protocol/request');
phutil_require_module('phabricator', 'applications/conduit/protocol/response');
phutil_require_module('phabricator', 'applications/conduit/storage/methodcalllog');
phutil_require_module('phabricator', 'applications/oauthserver/scope');
phutil_require_module('phabricator', 'applications/oauthserver/server');
phutil_require_module('phabricator', 'applications/oauthserver/storage/accesstoken');
phutil_require_module('phabricator', 'applications/people/storage/user');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/control/table');

View file

@ -35,6 +35,11 @@ abstract class ConduitAPIMethod {
return idx($this->defineErrorTypes(), $error_code, 'Unknown Error');
}
public function getRequiredScope() {
// by default, conduit methods are not accessible via OAuth
return PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE;
}
public function executeMethod(ConduitAPIRequest $request) {
return $this->execute($request);
}

View file

@ -6,6 +6,7 @@
phutil_require_module('phabricator', 'applications/oauthserver/scope');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'parser/uri');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,10 +22,21 @@
abstract class ConduitAPI_user_Method extends ConduitAPIMethod {
protected function buildUserInformationDictionary(PhabricatorUser $user) {
$src_phid = $user->getProfileImagePHID();
$file = id(new PhabricatorFile())->loadOneWhere('phid = %s', $src_phid);
if ($file) {
$picture = $file->getBestURI();
} else {
$picture = null;
}
return array(
'phid' => $user->getPHID(),
'userName' => $user->getUserName(),
'realName' => $user->getRealName(),
'email' => $user->getEmail(),
'image' => $picture,
'uri' => PhabricatorEnv::getURI('/p/'.$user->getUsername().'/'),
);
}

View file

@ -7,6 +7,10 @@
phutil_require_module('phabricator', 'applications/conduit/method/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'utils');
phutil_require_source('ConduitAPI_user_Method.php');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -40,6 +40,10 @@ final class ConduitAPI_user_whoami_Method
);
}
public function getRequiredScope() {
return PhabricatorOAuthServerScope::SCOPE_WHOAMI;
}
protected function execute(ConduitAPIRequest $request) {
return $this->buildUserInformationDictionary($request->getUser());
}

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/conduit/method/user/base');
phutil_require_module('phabricator', 'applications/oauthserver/scope');
phutil_require_source('ConduitAPI_user_whoami_Method.php');

View file

@ -366,6 +366,10 @@ class DifferentialRevisionListController extends DifferentialController {
private function buildViews($filter, $user_phid, array $revisions) {
$user = $this->getRequest()->getUser();
$template = id(new DifferentialRevisionListView())
->setUser($user)
->setFields(DifferentialRevisionListView::getDefaultFields());
$views = array();
switch ($filter) {
case 'active':
@ -373,18 +377,16 @@ class DifferentialRevisionListController extends DifferentialController {
$revisions,
$user_phid);
$view = id(new DifferentialRevisionListView())
$view = id(clone $template)
->setRevisions($active)
->setUser($user)
->setNoDataString("You have no active revisions requiring action.");
$views[] = array(
'title' => 'Action Required',
'view' => $view,
);
$view = id(new DifferentialRevisionListView())
$view = id(clone $template)
->setRevisions($waiting)
->setUser($user)
->setNoDataString("You have no active revisions waiting on others.");
$views[] = array(
'title' => 'Waiting On Others',
@ -401,9 +403,8 @@ class DifferentialRevisionListController extends DifferentialController {
'subscribed' => 'Revisions by Subscriber',
'all' => 'Revisions',
);
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setUser($user);
$view = id(clone $template)
->setRevisions($revisions);
$views[] = array(
'title' => idx($titles, $filter),
'view' => $view,
@ -412,6 +413,7 @@ class DifferentialRevisionListController extends DifferentialController {
default:
throw new Exception("Unknown filter '{$filter}'!");
}
return $views;
}

View file

@ -0,0 +1,170 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class DifferentialRevisionStatsController extends DifferentialController {
private $filter;
public function shouldRequireLogin() {
return true;
}
private function loadRevisions($phid) {
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT revisions.* FROM %T revisions ' .
'JOIN %T comments ON comments.revisionID = revisions.id ' .
'JOIN (' .
' SELECT revisionID FROM %T WHERE objectPHID = %s ' .
' UNION ALL ' .
' SELECT id from differential_revision WHERE authorPHID = %s) rel ' .
'ON (comments.revisionID = rel.revisionID)' .
'WHERE comments.action = %s' .
'AND comments.authorPHID = %s',
$table->getTableName(),
id(new DifferentialComment())->getTableName(),
DifferentialRevision::RELATIONSHIP_TABLE,
$phid,
$phid,
$this->filter,
$phid
);
return $table->loadAllFromArray($rows);
}
private function loadComments($phid) {
$table = new DifferentialComment();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT comments.* FROM %T comments ' .
'JOIN (' .
' SELECT revisionID FROM %T WHERE objectPHID = %s ' .
' UNION ALL ' .
' SELECT id from differential_revision WHERE authorPHID = %s) rel ' .
'ON (comments.revisionID = rel.revisionID)' .
'WHERE comments.action = %s' .
'AND comments.authorPHID = %s',
$table->getTableName(),
DifferentialRevision::RELATIONSHIP_TABLE,
$phid,
$phid,
$this->filter,
$phid
);
return $table->loadAllFromArray($rows);
}
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
$phid_arr = $request->getArr('view_user');
$view_target = head($phid_arr);
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI()->alter('phid', $view_target));
}
$params = array_filter(
array(
'phid' => $request->getStr('phid'),
));
// Fill in the defaults we'll actually use for calculations if any
// parameters are missing.
$params += array(
'phid' => $user->getPHID(),
);
$side_nav = new AphrontSideNavFilterView();
$side_nav->setBaseURI(id(new PhutilURI('/differential/stats/'))
->alter('phid', $params['phid']));
foreach (array(
DifferentialAction::ACTION_COMMIT,
DifferentialAction::ACTION_ACCEPT,
DifferentialAction::ACTION_REJECT,
DifferentialAction::ACTION_UPDATE,
DifferentialAction::ACTION_COMMENT,
) as $action) {
$verb = ucfirst(DifferentialAction::getActionPastTenseVerb($action));
$side_nav->addFilter($action, $verb);
}
$this->filter =
$side_nav->selectFilter($this->filter,
DifferentialAction::ACTION_COMMIT);
$panels = array();
$handles = id(new PhabricatorObjectHandleData(array($params['phid'])))
->loadHandles();
$filter_form = id(new AphrontFormView())
->setAction('/differential/stats/'.$this->filter.'/')
->setUser($user);
$filter_form->appendChild(
$this->renderControl($params['phid'], $handles));
$filter_form->appendChild(id(new AphrontFormSubmitControl())
->setValue('Filter Revisions'));
$side_nav->appendChild($filter_form);
$comments = $this->loadComments($params['phid']);
$revisions = $this->loadRevisions($params['phid']);
$panel = new AphrontPanelView();
$panel->setHeader('Differential rate analysis');
$panel->appendChild(
id(new DifferentialRevisionStatsView())
->setComments($comments)
->setRevisions($revisions)
->setUser($user));
$panels[] = $panel;
foreach ($panels as $panel) {
$side_nav->appendChild($panel);
}
return $this->buildStandardPageResponse(
$side_nav,
array(
'title' => 'Differential statistics',
));
}
private function renderControl($view_phid, $handles) {
$value = array();
if ($view_phid) {
$value = array(
$view_phid => $handles[$view_phid]->getFullName(),
);
}
return id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/users/')
->setLabel('View User')
->setName('view_user')
->setValue($value)
->setLimit(1);
}
}

View file

@ -0,0 +1,27 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/differential/constants/action');
phutil_require_module('phabricator', 'applications/differential/controller/base');
phutil_require_module('phabricator', 'applications/differential/storage/comment');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
phutil_require_module('phabricator', 'applications/differential/view/revisionstats');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/tokenizer');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');
phutil_require_source('DifferentialRevisionStatsController.php');

View file

@ -663,7 +663,11 @@ class DifferentialRevisionEditor {
->setAuthorPHID($this->getActorPHID())
->setRevisionID($revision_id)
->setContent($this->getComments())
->setAction('update');
->setAction(DifferentialAction::ACTION_UPDATE)
->setMetadata(
array(
DifferentialComment::METADATA_DIFF_ID => $this->getDiff()->getID(),
));
if ($this->contentSource) {
$comment->setContentSource($this->contentSource);

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,4 +30,8 @@ abstract class DifferentialFieldSelector {
abstract public function getFieldSpecifications();
public function sortFieldsForRevisionList(array $fields) {
return $fields;
}
}

View file

@ -53,9 +53,33 @@ final class DifferentialDefaultFieldSelector
new DifferentialApplyPatchFieldSpecification(),
new DifferentialRevisionIDFieldSpecification(),
new DifferentialGitSVNIDFieldSpecification(),
new DifferentialDateModifiedFieldSpecification(),
new DifferentialDateCreatedFieldSpecification(),
));
return $fields;
}
public function sortFieldsForRevisionList(array $fields) {
$map = array();
foreach ($fields as $field) {
$map[get_class($field)] = $field;
}
$map = array_select_keys(
$map,
array(
'DifferentialRevisionIDFieldSpecification',
'DifferentialTitleFieldSpecification',
'DifferentialRevisionStatusFieldSpecification',
'DifferentialAuthorFieldSpecification',
'DifferentialReviewersFieldSpecification',
'DifferentialDateModifiedFieldSpecification',
'DifferentialDateCreatedFieldSpecification',
)) + $map;
return array_values($map);
}
}

View file

@ -13,6 +13,8 @@ phutil_require_module('phabricator', 'applications/differential/field/specificat
phutil_require_module('phabricator', 'applications/differential/field/specification/branch');
phutil_require_module('phabricator', 'applications/differential/field/specification/ccs');
phutil_require_module('phabricator', 'applications/differential/field/specification/commits');
phutil_require_module('phabricator', 'applications/differential/field/specification/datecreated');
phutil_require_module('phabricator', 'applications/differential/field/specification/datemodified');
phutil_require_module('phabricator', 'applications/differential/field/specification/dependencies');
phutil_require_module('phabricator', 'applications/differential/field/specification/gitsvnid');
phutil_require_module('phabricator', 'applications/differential/field/specification/host');
@ -29,5 +31,7 @@ phutil_require_module('phabricator', 'applications/differential/field/specificat
phutil_require_module('phabricator', 'applications/differential/field/specification/unit');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'utils');
phutil_require_source('DifferentialDefaultFieldSelector.php');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -43,4 +43,21 @@ final class DifferentialAuthorFieldSpecification
return $revision->getAuthorPHID();
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Author';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return $this->getHandle($revision->getAuthorPHID())->renderLink();
}
public function getRequiredHandlePHIDsForRevisionList(
DifferentialRevision $revision) {
return array($revision->getAuthorPHID());
}
}

View file

@ -27,6 +27,7 @@
* @task storage Field Storage
* @task edit Extending the Revision Edit Interface
* @task view Extending the Revision View Interface
* @task list Extending the Revision List Interface
* @task conduit Extending the Conduit View Interface
* @task commit Extending Commit Messages
* @task load Loading Additional Data
@ -264,6 +265,59 @@ abstract class DifferentialFieldSpecification {
}
/* -( Extending the Revision List Interface )------------------------------ */
/**
* Determine if this field should appear in the table on the revision list
* interface.
*
* @return bool True if this field should appear in the table.
*
* @task list
*/
public function shouldAppearOnRevisionList() {
return false;
}
/**
* Return a column header for revision list tables.
*
* @return string Column header.
*
* @task list
*/
public function renderHeaderForRevisionList() {
throw new DifferentialFieldSpecificationIncompleteException($this);
}
/**
* Optionally, return a column class for revision list tables.
*
* @return string CSS class for table cells.
*
* @task list
*/
public function getColumnClassForRevisionList() {
return null;
}
/**
* Return a table cell value for revision list tables.
*
* @param DifferentialRevision The revision to render a value for.
* @return string Table cell value.
*
* @task list
*/
public function renderValueForRevisionList(DifferentialRevision $revision) {
throw new DifferentialFieldSpecificationIncompleteException($this);
}
/* -( Extending the Conduit Interface )------------------------------------ */
@ -483,6 +537,7 @@ abstract class DifferentialFieldSpecification {
return array();
}
/**
* Specify which @{class:PhabricatorObjectHandles} need to be loaded for your
* field to render correctly on the view interface.
@ -498,6 +553,25 @@ abstract class DifferentialFieldSpecification {
return $this->getRequiredHandlePHIDs();
}
/**
* Specify which @{class:PhabricatorObjectHandles} need to be loaded for your
* field to render correctly on the list interface.
*
* This is a more specific version of @{method:getRequiredHandlePHIDs} which
* can be overridden to improve field performance by loading only data you
* need.
*
* @param DifferentialRevision The revision to pull PHIDs for.
* @return list List of PHIDs to load handles for.
* @task load
*/
public function getRequiredHandlePHIDsForRevisionList(
DifferentialRevision $revision) {
return array();
}
/**
* Specify which @{class:PhabricatorObjectHandles} need to be loaded for your
* field to render correctly on the edit interface.

View file

@ -0,0 +1,38 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialDateCreatedFieldSpecification
extends DifferentialFieldSpecification {
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Created';
}
public function getColumnClassForRevisionList() {
return 'right';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return phabricator_date($revision->getDateCreated(), $this->getUser());
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/differential/field/specification/base');
phutil_require_module('phabricator', 'view/utils');
phutil_require_source('DifferentialDateCreatedFieldSpecification.php');

View file

@ -0,0 +1,38 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialDateModifiedFieldSpecification
extends DifferentialFieldSpecification {
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Updated';
}
public function getColumnClassForRevisionList() {
return 'right';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return phabricator_datetime($revision->getDateModified(), $this->getUser());
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/differential/field/specification/base');
phutil_require_module('phabricator', 'view/utils');
phutil_require_source('DifferentialDateModifiedFieldSpecification.php');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,4 +32,20 @@ final class DifferentialLinesFieldSpecification
return phutil_escape_html(number_format($diff->getLineCount()));
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Lines';
}
public function getColumnClassForRevisionList() {
return 'n';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return number_format($revision->getLineCount());
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -140,4 +140,36 @@ final class DifferentialReviewersFieldSpecification
return $this->parseCommitMessageUserList($value);
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Reviewers';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
$reviewer_phids = $revision->getReviewers();
if ($reviewer_phids) {
$first = reset($reviewer_phids);
if (count($reviewer_phids) > 1) {
$suffix = ' (+'.(count($reviewer_phids) - 1).')';
} else {
$suffix = null;
}
return $this->getHandle($first)->renderLink().$suffix;
} else {
return '<em>None</em>';
}
}
public function getRequiredHandlePHIDsForRevisionList(
DifferentialRevision $revision) {
$reviewer_phids = $revision->getReviewers();
if ($reviewer_phids) {
return array(reset($reviewer_phids));
}
return array();
}
}

View file

@ -92,4 +92,16 @@ final class DifferentialRevisionIDFieldSpecification
return null;
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'ID';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return 'D'.$revision->getID();
}
}

View file

@ -54,4 +54,17 @@ final class DifferentialRevisionStatusFieldSpecification
return '<strong>'.$status.'</strong>'.$next_step;
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Status';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return ArcanistDifferentialRevisionStatus::getNameForRevisionStatus(
$revision->getStatus());
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -86,4 +86,25 @@ final class DifferentialTitleFieldSpecification
return preg_replace('/\s*\n\s*/', ' ', $value);
}
public function shouldAppearOnRevisionList() {
return true;
}
public function renderHeaderForRevisionList() {
return 'Revision';
}
public function getColumnClassForRevisionList() {
return 'wide pri';
}
public function renderValueForRevisionList(DifferentialRevision $revision) {
return phutil_render_tag(
'a',
array(
'href' => '/D'.$revision->getID(),
),
phutil_escape_html($revision->getTitle()));
}
}

View file

@ -10,6 +10,7 @@ phutil_require_module('phabricator', 'applications/differential/field/exception/
phutil_require_module('phabricator', 'applications/differential/field/specification/base');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -103,6 +103,11 @@ abstract class DifferentialMail {
$template->setIsBulk(true);
$template->setRelatedPHID($this->getRevision()->getPHID());
$mailtags = $this->getMailTags();
if ($mailtags) {
$template->setMailTags($mailtags);
}
$phids = array();
foreach ($to_phids as $phid) {
$phids[$phid] = true;
@ -134,6 +139,10 @@ abstract class DifferentialMail {
}
}
protected function getMailTags() {
return array();
}
protected function getSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix');
}

View file

@ -44,6 +44,35 @@ class DifferentialCommentMail extends DifferentialMail {
}
protected function getMailTags() {
$comment = $this->getComment();
$action = $comment->getAction();
$tags = array();
switch ($action) {
case DifferentialAction::ACTION_ADDCCS:
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_CC;
break;
case DifferentialAction::ACTION_COMMIT:
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMITTED;
break;
}
if (strlen(trim($comment->getContent()))) {
switch ($action) {
case DifferentialAction::ACTION_COMMIT:
// Commit comments are auto-generated and not especially interesting,
// so don't tag them as having a comment.
break;
default:
$tags[] = MetaMTANotificationType::TYPE_DIFFERENTIAL_COMMENT;
break;
}
}
return $tags;
}
protected function renderSubject() {
$verb = ucwords($this->getVerb());
$revision = $this->getRevision();

View file

@ -11,6 +11,7 @@ phutil_require_module('arcanist', 'differential/constants/revisionstatus');
phutil_require_module('phabricator', 'applications/differential/constants/action');
phutil_require_module('phabricator', 'applications/differential/mail/base');
phutil_require_module('phabricator', 'applications/differential/storage/comment');
phutil_require_module('phabricator', 'applications/metamta/constants/notificationtype');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/env');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,7 @@ class DifferentialComment extends DifferentialDAO {
const METADATA_ADDED_REVIEWERS = 'added-reviewers';
const METADATA_ADDED_CCS = 'added-ccs';
const METADATA_DIFF_ID = 'diff-id';
protected $authorPHID;
protected $revisionID;

View file

@ -96,6 +96,10 @@ class DifferentialChangesetDetailView extends AphrontView {
}
$display_filename = $changeset->getDisplayFilename();
$buoyant_begin = $this->renderBuoyant($display_filename);
$buoyant_end = $this->renderBuoyant(null);
$output = javelin_render_tag(
'div',
array(
@ -109,6 +113,7 @@ class DifferentialChangesetDetailView extends AphrontView {
'class' => $class,
'id' => $id,
),
$buoyant_begin.
phutil_render_tag(
'a',
array(
@ -118,9 +123,29 @@ class DifferentialChangesetDetailView extends AphrontView {
$buttons.
'<h1>'.phutil_escape_html($display_filename).'</h1>'.
'<div style="clear: both;"></div>'.
$this->renderChildren());
$this->renderChildren().
$buoyant_end);
return $output;
}
private function renderBuoyant($text) {
return javelin_render_tag(
'div',
array(
'sigil' => 'buoyant',
'meta' => array(
'text' => $text,
),
'style' => ($text === null)
// Current CSS spacing rules cause the "end" anchor to appear too
// late in the display document. Shift it up a bit so we drop the
// buoyant header sooner. This reduces confusion when using keystroke
// navigation.
? 'bottom: 60px; position: absolute;'
: null,
),
'');
}
}

View file

@ -29,7 +29,7 @@ class DifferentialChangesetListView extends AphrontView {
private $symbolIndexes = array();
private $repository;
private $diff;
private $vsMap;
private $vsMap = array();
public function setChangesets($changesets) {
$this->changesets = $changesets;
@ -106,6 +106,8 @@ class DifferentialChangesetListView extends AphrontView {
array());
}
Javelin::initBehavior('buoyant', array());
$output = array();
$mapping = array();
$repository = $this->repository;

View file

@ -218,8 +218,10 @@ final class DifferentialInlineCommentView extends AphrontView {
'<span class="differential-inline-comment-line">'.$line.'</span>'.
phutil_escape_html($author).
'</div>'.
'<div class="phabricator-remarkup">'.
$content.
'<div class="differential-inline-comment-content">'.
'<div class="phabricator-remarkup">'.
$content.
'</div>'.
'</div>');
return $this->scaffoldMarkup($markup);

View file

@ -95,46 +95,9 @@ final class DifferentialRevisionCommentView extends AphrontView {
$action_class = 'differential-comment-action-'.$action;
if ($this->preview) {
$date = 'COMMENT PREVIEW';
} else {
$date = phabricator_datetime($comment->getDateCreated(), $this->user);
}
$info = array();
$content_source = new PhabricatorContentSourceView();
$content_source->setContentSource($comment->getContentSource());
$content_source->setUser($this->user);
$info[] = $content_source->render();
$info[] = $date;
$comment_anchor = null;
$anchor_name = $this->anchorName;
if ($anchor_name != '' && !$this->preview) {
Javelin::initBehavior('phabricator-watch-anchor');
$info[] = phutil_render_tag(
'a',
array(
'name' => $anchor_name,
'id' => $anchor_name,
'href' => '#'.$anchor_name,
),
'D'.$comment->getRevisionID().'#'.$anchor_name);
$comment_anchor = 'anchor-'.$anchor_name;
}
$info = implode(' &middot; ', array_filter($info));
$author = $this->handles[$comment->getAuthorPHID()];
$author_link = $author->renderLink();
$verb = DifferentialAction::getActionPastTenseVerb($comment->getAction());
$verb = phutil_escape_html($verb);
$content = $comment->getContent();
$head_content = null;
$hide_comments = true;
if (strlen(rtrim($content))) {
$hide_comments = false;
@ -157,8 +120,6 @@ final class DifferentialRevisionCommentView extends AphrontView {
'</div>';
}
$title = "{$author_link} {$verb} this revision.";
if ($this->inlines) {
$hide_comments = false;
$inline_render = array();
@ -282,63 +243,93 @@ final class DifferentialRevisionCommentView extends AphrontView {
$inline_render = null;
}
$background = null;
$uri = $author->getImageURI();
if ($uri) {
$background = "background-image: url('{$uri}');";
}
$author = $this->handles[$comment->getAuthorPHID()];
$author_link = $author->renderLink();
$metadata_blocks = array();
$metadata = $comment->getMetadata();
$added_reviewers = idx(
$metadata,
DifferentialComment::METADATA_ADDED_REVIEWERS);
if ($added_reviewers) {
$reviewers = 'Added reviewers: '.$this->renderHandleList(
$added_reviewers);
$metadata_blocks[] = $reviewers;
}
$added_ccs = idx(
$metadata,
DifferentialComment::METADATA_ADDED_CCS);
$verb = DifferentialAction::getActionPastTenseVerb($comment->getAction());
$verb = phutil_escape_html($verb);
$actions = array();
switch ($comment->getAction()) {
case DifferentialAction::ACTION_ADDCCS:
$actions[] = "{$author_link} added CCs: ".
$this->renderHandleList($added_ccs).".";
$added_ccs = null;
break;
case DifferentialAction::ACTION_ADDREVIEWERS:
$actions[] = "{$author_link} added reviewers: ".
$this->renderHandleList($added_reviewers).".";
$added_reviewers = null;
break;
case DifferentialAction::ACTION_UPDATE:
$diff_id = idx($metadata, DifferentialComment::METADATA_DIFF_ID);
if ($diff_id) {
$diff_link = phutil_render_tag(
'a',
array(
'href' => '/D'.$comment->getRevisionID().'?id='.$diff_id,
),
'Diff #'.phutil_escape_html($diff_id));
$actions[] = "{$author_link} updated this revision to {$diff_link}.";
} else {
$actions[] = "{$author_link} {$verb} this revision.";
}
break;
default:
$actions[] = "{$author_link} {$verb} this revision.";
break;
}
if ($added_reviewers) {
$actions[] = "{$author_link} added reviewers: ".
$this->renderHandleList($added_reviewers).".";
}
if ($added_ccs) {
$ccs = 'Added CCs: '.$this->renderHandleList($added_ccs);
$metadata_blocks[] = $ccs;
$actions[] = "{$author_link} added CCs: ".
$this->renderHandleList($added_ccs).".";
}
if ($metadata_blocks) {
$hide_comments = false;
$metadata_blocks =
'<div class="differential-comment-metadata">'.
implode("\n", $metadata_blocks).
'</div>';
foreach ($actions as $key => $action) {
$actions[$key] = '<div>'.$action.'</div>';
}
$xaction_view = id(new PhabricatorTransactionView())
->setUser($this->user)
->setImageURI($author->getImageURI())
->setContentSource($comment->getContentSource())
->addClass($action_class)
->setActions($actions);
if ($this->preview) {
$xaction_view->setIsPreview($this->preview);
} else {
$metadata_blocks = null;
$xaction_view->setEpoch($comment->getDateCreated());
if ($this->anchorName) {
$anchor_name = $this->anchorName;
$anchor_text = 'D'.$comment->getRevisionID().'#'.$anchor_name;
$xaction_view->setAnchor($anchor_name, $anchor_text);
}
}
$hide_comments_class = ($hide_comments ? 'hide' : '');
return phutil_render_tag(
'div',
array(
'class' => "differential-comment",
'id' => $comment_anchor,
'style' => $background,
),
'<div class="differential-comment-detail '.$action_class.'">'.
'<div class="differential-comment-header">'.
'<span class="differential-comment-info">'.$info.'</span>'.
'<span class="differential-comment-title">'.$title.'</span>'.
if (!$hide_comments) {
$xaction_view->appendChild(
'<div class="differential-comment-core">'.
$content.
'</div>'.
'<div class="differential-comment-content '.$hide_comments_class.'">'.
$head_content.
$metadata_blocks.
'<div class="differential-comment-core">'.
$content.
'</div>'.
$inline_render.
'</div>'.
'</div>');
$inline_render);
}
return $xaction_view->render();
}
private function renderHandleList(array $phids) {

View file

@ -9,11 +9,9 @@
phutil_require_module('phabricator', 'aphront/writeguard');
phutil_require_module('phabricator', 'applications/differential/constants/action');
phutil_require_module('phabricator', 'applications/differential/storage/comment');
phutil_require_module('phabricator', 'applications/metamta/contentsource/view');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phabricator', 'view/layout/transaction');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -25,6 +25,12 @@ final class DifferentialRevisionListView extends AphrontView {
private $handles;
private $user;
private $noDataString;
private $fields;
public function setFields(array $fields) {
$this->fields = $fields;
return $this;
}
public function setRevisions(array $revisions) {
$this->revisions = $revisions;
@ -33,14 +39,12 @@ final class DifferentialRevisionListView extends AphrontView {
public function getRequiredHandlePHIDs() {
$phids = array();
foreach ($this->revisions as $revision) {
$phids[] = $revision->getAuthorPHID();
$reviewers = $revision->getReviewers();
if ($reviewers) {
$phids[] = head($reviewers);
foreach ($this->fields as $field) {
foreach ($this->revisions as $revision) {
$phids[] = $field->getRequiredHandlePHIDsForRevisionList($revision);
}
}
return $phids;
return array_mergev($phids);
}
public function setHandles(array $handles) {
@ -65,67 +69,30 @@ final class DifferentialRevisionListView extends AphrontView {
throw new Exception("Call setUser() before render()!");
}
foreach ($this->fields as $field) {
$field->setUser($this->user);
$field->setHandles($this->handles);
}
$rows = array();
foreach ($this->revisions as $revision) {
$status = $revision->getStatus();
$status =
ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status);
$reviewer_phids = $revision->getReviewers();
if ($reviewer_phids) {
$first = reset($reviewer_phids);
if (count($reviewer_phids) > 1) {
$suffix = ' (+'.(count($reviewer_phids) - 1).')';
} else {
$suffix = null;
}
$reviewers = $this->handles[$first]->renderLink().$suffix;
} else {
$reviewers = '<em>None</em>';
$row = array();
foreach ($this->fields as $field) {
$row[] = $field->renderValueForRevisionList($revision);
}
$rows[] = $row;
}
$link = phutil_render_tag(
'a',
array(
'href' => '/D'.$revision->getID(),
),
phutil_escape_html($revision->getTitle()));
$rows[] = array(
'D'.$revision->getID(),
$link,
phutil_escape_html($status),
number_format($revision->getLineCount()),
$this->handles[$revision->getAuthorPHID()]->renderLink(),
$reviewers,
phabricator_datetime($revision->getDateModified(), $user),
phabricator_date($revision->getDateCreated(), $user),
);
$headers = array();
$classes = array();
foreach ($this->fields as $field) {
$headers[] = $field->renderHeaderForRevisionList();
$classes[] = $field->getColumnClassForRevisionList();
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'ID',
'Revision',
'Status',
'Lines',
'Author',
'Reviewers',
'Modified',
'Created',
));
$table->setColumnClasses(
array(
null,
'wide pri',
null,
'n',
null,
null,
'right',
'right',
));
$table->setHeaders($headers);
$table->setColumnClasses($classes);
if ($this->noDataString) {
$table->setNoDataString($this->noDataString);
@ -134,4 +101,22 @@ final class DifferentialRevisionListView extends AphrontView {
return $table->render();
}
public static function getDefaultFields() {
$selector = DifferentialFieldSelector::newSelector();
$fields = $selector->getFieldSpecifications();
foreach ($fields as $key => $field) {
if (!$field->shouldAppearOnRevisionList()) {
unset($fields[$key]);
}
}
if (!$fields) {
throw new Exception(
"Phabricator configuration has no fields that appear on the list ".
"interface!");
}
return $selector->sortFieldsForRevisionList($fields);
}
}

View file

@ -6,13 +6,10 @@
phutil_require_module('arcanist', 'differential/constants/revisionstatus');
phutil_require_module('phabricator', 'applications/differential/field/selector/base');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -0,0 +1,152 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Render some distracting statistics on revisions
*/
final class DifferentialRevisionStatsView extends AphrontView {
private $comments;
private $revisions;
private $user;
public function setRevisions(array $revisions) {
$this->revisions = $revisions;
return $this;
}
public function setComments(array $comments) {
$this->comments = $comments;
return $this;
}
public function setUser($user) {
$this->user = $user;
return $this;
}
public function render() {
$user = $this->user;
if (!$user) {
throw new Exception("Call setUser() before render()!");
}
$id_to_revision_map = array();
foreach ($this->revisions as $rev) {
$id_to_revision_map[$rev->getID()] = $rev;
}
$revisions_seen = array();
$dates = array();
$counts = array();
$lines = array();
$boosts = array();
$days_with_diffs = array();
$count_active = array();
$now = time();
$row_array = array();
foreach (array(
'1 week', '2 weeks', '3 weeks',
'1 month', '2 months', '3 months', '6 months', '9 months',
'1 year', '18 months',
'2 years', '3 years', '4 years', '5 years',
) as $age) {
$dates[$age] = strtotime($age . ' ago');
$counts[$age] = 0;
$lines[$age] = 0;
$count_active[$age] = 0;
}
foreach ($this->comments as $comment) {
$rev_date = $comment->getDateCreated();
$day = phabricator_date($rev_date, $user);
$old_daycount = idx($days_with_diffs, $day, 0);
$days_with_diffs[$day] = $old_daycount + 1;
$rev_id = $comment->getRevisionID();
if (idx($revisions_seen, $rev_id)) {
continue;
}
$rev = $id_to_revision_map[$rev_id];
$revisions_seen[$rev_id] = true;
foreach ($dates as $age => $cutoff) {
if ($cutoff > $rev_date) {
continue;
}
if ($rev) {
$lines[$age] += $rev->getLineCount();
}
$counts[$age]++;
if (!$old_daycount) {
$count_active[$age]++;
}
}
}
$old_count = 0;
foreach ($dates as $age => $cutoff) {
$weeks = ($now - $cutoff + 0.) / (7 * 60 * 60 * 24);
if ($old_count == $counts[$age]) {
continue;
}
$old_count = $counts[$age];
$row_array[$age] = array(
'Revisions per week' => number_format($counts[$age] / $weeks, 2),
'Lines per week' => number_format($lines[$age] / $weeks, 1),
'Active days per week' =>
number_format($count_active[$age] / $weeks, 1),
'Revisions' => number_format($counts[$age]),
'Lines' => number_format($lines[$age]),
'Lines per diff' => number_format($lines[$age] /
($counts[$age] + 0.0001)),
'Active days' => number_format($count_active[$age]),
);
}
$rows = array();
$row_names = array_keys(head($row_array));
foreach ($row_names as $row_name) {
$rows[] = array($row_name);
}
foreach (array_keys($dates) as $age) {
$i = 0;
foreach ($row_names as $row_name) {
$rows[$i][] = idx(idx($row_array, $age), $row_name, '-');
++$i;
}
}
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'wide pri',
));
$table->setHeaders(
array_merge(
array(
'Metric',
),
array_keys($dates)));
return $table->render();
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'utils');
phutil_require_source('DifferentialRevisionStatsView.php');

View file

@ -143,6 +143,7 @@ abstract class DiffusionController extends PhabricatorController {
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setFields(DifferentialRevisionListView::getDefaultFields())
->setUser($this->getRequest()->getUser());
$phids = $view->getRequiredHandlePHIDs();

View file

@ -86,6 +86,9 @@ class DiffusionCommitController extends DiffusionController {
$content[] = $detail_panel;
}
$content[] = $this->buildAuditTable($commit);
$content[] = $this->buildComments($commit);
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$changes = $change_query->loadChanges();
@ -213,6 +216,8 @@ class DiffusionCommitController extends DiffusionController {
$content[] = $change_list;
}
$content[] = $this->buildAddCommentView($commit);
return $this->buildStandardPageResponse(
$content,
array(
@ -290,4 +295,78 @@ class DiffusionCommitController extends DiffusionController {
'</table>';
}
private function buildAuditTable($commit) {
$query = new PhabricatorAuditQuery();
$query->withCommitPHIDs(array($commit->getPHID()));
$audits = $query->execute();
$view = new PhabricatorAuditListView();
$view->setAudits($audits);
$phids = $view->getRequiredHandlePHIDs();
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view->setHandles($handles);
$panel = new AphrontPanelView();
$panel->setHeader('Audits');
$panel->appendChild($view);
return $panel;
}
private function buildComments($commit) {
$user = $this->getRequest()->getUser();
$comments = id(new PhabricatorAuditComment())->loadAllWhere(
'targetPHID = %s ORDER BY dateCreated ASC',
$commit->getPHID());
$view = new DiffusionCommentListView();
$view->setUser($user);
$view->setComments($comments);
$phids = $view->getRequiredHandlePHIDs();
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
$view->setHandles($handles);
return $view;
}
private function buildAddCommentView($commit) {
$user = $this->getRequest()->getUser();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/audit/addcomment/')
->addHiddenInput('commit', $commit->getPHID())
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Action')
->setName('action')
->setOptions(PhabricatorAuditActionConstants::getActionNameMap()))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Comments')
->setName('content')
->setCaption(phutil_render_tag(
'a',
array(
'href' => PhabricatorEnv::getDoclink(
'article/Remarkup_Reference.html'),
'tabindex' => '-1',
'target' => '_blank',
),
'Formatting Reference')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue($is_serious ? 'Submit' : 'Cook the Books'));
$panel = new AphrontPanelView();
$panel->setHeader($is_serious ? 'Audit Commit' : 'Creative Accounting');
$panel->appendChild($form);
return $panel;
}
}

View file

@ -6,19 +6,29 @@
phutil_require_module('phabricator', 'applications/audit/constants/action');
phutil_require_module('phabricator', 'applications/audit/query/audit');
phutil_require_module('phabricator', 'applications/audit/storage/auditcomment');
phutil_require_module('phabricator', 'applications/audit/view/list');
phutil_require_module('phabricator', 'applications/differential/constants/changetype');
phutil_require_module('phabricator', 'applications/differential/view/changesetlistview');
phutil_require_module('phabricator', 'applications/diffusion/controller/base');
phutil_require_module('phabricator', 'applications/diffusion/data/pathchange');
phutil_require_module('phabricator', 'applications/diffusion/query/contains/base');
phutil_require_module('phabricator', 'applications/diffusion/query/pathchange/base');
phutil_require_module('phabricator', 'applications/diffusion/view/commentlist');
phutil_require_module('phabricator', 'applications/diffusion/view/commitchangetable');
phutil_require_module('phabricator', 'applications/markup/engine');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/storage/repository');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');

View file

@ -0,0 +1,132 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DiffusionCommentView extends AphrontView {
private $user;
private $comment;
private $commentNumber;
private $handles;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setComment(PhabricatorAuditComment $comment) {
$this->comment = $comment;
return $this;
}
public function setCommentNumber($comment_number) {
$this->commentNumber = $comment_number;
return $this;
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
private function getHandle($phid) {
if (empty($this->handles[$phid])) {
throw new Exception("Unloaded handle '{$phid}'!");
}
return $this->handles[$phid];
}
public function render() {
$comment = $this->comment;
$author = $this->getHandle($comment->getActorPHID());
$author_link = $author->renderLink();
$actions = $this->renderActions();
$content = $this->renderContent();
$classes = $this->renderClasses();
$xaction_view = id(new PhabricatorTransactionView())
->setUser($this->user)
->setImageURI($author->getImageURI())
->setActions($actions)
->setAnchor('comment-'.$this->commentNumber, '#'.$this->commentNumber)
->setEpoch($comment->getDateCreated())
->appendChild($content);
foreach ($classes as $class) {
$xaction_view->addClass($class);
}
return $xaction_view;
}
private function renderActions() {
$comment = $this->comment;
$author = $this->getHandle($comment->getActorPHID());
$author_link = $author->renderLink();
$actions = array();
switch ($comment->getAction()) {
case PhabricatorAuditActionConstants::ACCEPT:
$actions[] = "{$author_link} accepted this commit.";
break;
case PhabricatorAuditActionConstants::CONCERN:
$actions[] = "{$author_link} raised concerns with this commit.";
break;
case PhabricatorAuditActionConstants::COMMENT:
default:
$actions[] = "{$author_link} commented on this commit.";
break;
}
foreach ($actions as $key => $action) {
$actions[$key] = '<div>'.$action.'</div>';
}
return $actions;
}
private function renderContent() {
$comment = $this->comment;
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
return
'<div class="phabricator-remarkup">'.
$engine->markupText($comment->getContent()).
'</div>';
}
private function renderClasses() {
$comment = $this->comment;
$classes = array();
switch ($comment->getAction()) {
case PhabricatorAuditActionConstants::ACCEPT:
$classes[] = 'audit-accept';
break;
case PhabricatorAuditActionConstants::CONCERN:
$classes[] = 'audit-concern';
break;
}
return $classes;
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/audit/constants/action');
phutil_require_module('phabricator', 'applications/markup/engine');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/layout/transaction');
phutil_require_module('phutil', 'utils');
phutil_require_source('DiffusionCommentView.php');

View file

@ -0,0 +1,70 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DiffusionCommentListView extends AphrontView {
private $user;
private $comments;
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
public function setComments(array $comments) {
$this->comments = $comments;
return $this;
}
public function getRequiredHandlePHIDs() {
$phids = array();
foreach ($this->comments as $comment) {
$phids[$comment->getActorPHID()] = true;
}
return array_keys($phids);
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
public function render() {
$num = 1;
$comments = array();
foreach ($this->comments as $comment) {
$view = id(new DiffusionCommentView())
->setComment($comment)
->setCommentNumber($num)
->setHandles($this->handles)
->setUser($this->user);
$comments[] = $view->render();
++$num;
}
return
'<div class="diffusion-comment-list">'.
$this->renderSingleView($comments).
'</div>';
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/diffusion/view/comment');
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('DiffusionCommentListView.php');

View file

@ -20,9 +20,11 @@ class PhabricatorDirectoryMainController
extends PhabricatorDirectoryController {
private $filter;
private $subfilter;
public function willProcessRequest(array $data) {
$this->filter = idx($data, 'filter');
$this->subfilter = idx($data, 'subfilter');
}
public function shouldRequireAdmin() {
@ -62,13 +64,17 @@ class PhabricatorDirectoryMainController
}
private function buildMainResponse($nav, $projects) {
$unbreak_panel = $this->buildUnbreakNowPanel();
$triage_panel = $this->buildNeedsTriagePanel($projects);
if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$unbreak_panel = $this->buildUnbreakNowPanel();
$triage_panel = $this->buildNeedsTriagePanel($projects);
$tasks_panel = $this->buildTasksPanel();
} else {
$unbreak_panel = null;
$triage_panel = null;
$tasks_panel = null;
}
$jump_panel = $this->buildJumpPanel();
$revision_panel = $this->buildRevisionPanel();
$tasks_panel = $this->buildTasksPanel();
$feed_view = $this->buildFeedView($projects, $is_full = false);
$content = array(
$unbreak_panel,
@ -76,7 +82,6 @@ class PhabricatorDirectoryMainController
$jump_panel,
$revision_panel,
$tasks_panel,
$feed_view,
);
$nav->appendChild($content);
@ -103,17 +108,16 @@ class PhabricatorDirectoryMainController
'/^d$/i' => 'uri:/differential/',
'/^r$/i' => 'uri:/diffusion/',
'/^t$/i' => 'uri:/maniphest/',
'/^p$/i' => 'uri:/project/',
'/^u$/i' => 'uri:/people/',
'/r([A-Z]+)$/' => 'repository',
'/r([A-Z]+)(\S+)$/' => 'commit',
'/^d(\d+)$/i' => 'revision',
'/^t(\d+)$/i' => 'task',
// TODO: '/^p$/i' => 'uri:/projects/',
// TODO: '/^u$/i' => 'uri:/people/',
// TODO: '/^p\s+(\S+)$/i' => 'project',
// TODO: '/^u\s+(\S+)$/i' => 'user',
// TODO: '/^task:\s+(\S+)/i' => 'create-task',
// TODO: '/^(?:s|symbol)\s+(\S+)/i' => 'find-symbol',
'/^p\s+(.+)$/i' => 'project',
'/^u\s+(\S+)$/i' => 'user',
'/^task:\s*(.+)/i' => 'create-task',
'/^(?:s|symbol)\s+(\S+)/i' => 'find-symbol',
);
@ -137,6 +141,26 @@ class PhabricatorDirectoryMainController
case 'task':
return id(new AphrontRedirectResponse())
->setURI('/T'.$matches[1]);
case 'user':
return id(new AphrontRedirectResponse())
->setURI('/p/'.$matches[1].'/');
case 'project':
$project = PhabricatorProjectQueryUtil
::findCloselyNamedProject($matches[1]);
if ($project) {
return id(new AphrontRedirectResponse())
->setURI('/project/view/'.$project->getID().'/');
} else {
$jump = $matches[1];
}
break;
case 'find-symbol':
return id(new AphrontRedirectResponse())
->setURI('/diffusion/symbol/'.$matches[1].'/?jump=true');
case 'create-task':
return id(new AphrontRedirectResponse())
->setURI('/maniphest/task/create/?title='
.phutil_escape_uri($matches[1]));
default:
throw new Exception("Unknown jump effect '{$effect}'!");
}
@ -162,8 +186,29 @@ class PhabricatorDirectoryMainController
}
private function buildFeedResponse($nav, $projects) {
$view = $this->buildFeedView($projects, $is_full = true);
$nav->appendChild($view);
$subnav = new AphrontSideNavFilterView();
$subnav->setBaseURI(new PhutilURI('/feed/'));
$subnav->addFilter('all', 'All Activity', '/feed/');
$subnav->addFilter('projects', 'My Projects');
$filter = $subnav->selectFilter($this->subfilter, 'all');
switch ($filter) {
case 'all':
$phids = array();
break;
case 'projects':
$phids = mpull($projects, 'getPHID');
break;
}
$view = $this->buildFeedView($phids);
$subnav->appendChild($view);
$nav->appendChild($subnav);
return $this->buildStandardPageResponse(
$nav,
array(
@ -286,8 +331,11 @@ class PhabricatorDirectoryMainController
"View Active Revisions \xC2\xBB"));
if ($active) {
$fields =
$revision_view = id(new DifferentialRevisionListView())
->setRevisions($active)
->setFields(DifferentialRevisionListView::getDefaultFields())
->setUser($user);
$phids = array_merge(
array($user_phid),
@ -362,16 +410,15 @@ class PhabricatorDirectoryMainController
return $view;
}
private function buildFeedView(array $projects, $is_full) {
private function buildFeedView(array $phids) {
$request = $this->getRequest();
$user = $request->getUser();
$user_phid = $user->getPHID();
$feed_query = new PhabricatorFeedQuery();
$feed_query->setFilterPHIDs(
array_merge(
array($user_phid),
mpull($projects, 'getPHID')));
if ($phids) {
$feed_query->setFilterPHIDs($phids);
}
// TODO: All this limit stuff should probably be consolidated into the
// feed query?
@ -379,13 +426,9 @@ class PhabricatorDirectoryMainController
$old_link = null;
$new_link = null;
if ($is_full) {
$feed_query->setAfter($request->getStr('after'));
$feed_query->setBefore($request->getStr('before'));
$limit = 500;
} else {
$limit = 100;
}
$feed_query->setAfter($request->getStr('after'));
$feed_query->setBefore($request->getStr('before'));
$limit = 500;
// Grab one more story than we intend to display so we can figure out
// if we need to render an "Older Posts" link or not (with reasonable
@ -394,12 +437,8 @@ class PhabricatorDirectoryMainController
$feed = $feed_query->execute();
$extra_row = (count($feed) == $limit + 1);
if ($is_full) {
$have_new = ($request->getStr('before')) ||
($request->getStr('after') && $extra_row);
} else {
$have_new = false;
}
$have_new = ($request->getStr('before')) ||
($request->getStr('after') && $extra_row);
$have_old = ($request->getStr('after')) ||
($request->getStr('before') && $extra_row) ||
@ -412,7 +451,7 @@ class PhabricatorDirectoryMainController
$old_link = phutil_render_tag(
'a',
array(
'href' => '/feed/?before='.end($feed)->getChronologicalKey(),
'href' => '?before='.end($feed)->getChronologicalKey(),
'class' => 'phabricator-feed-older-link',
),
"Older Stories \xC2\xBB");
@ -421,7 +460,7 @@ class PhabricatorDirectoryMainController
$new_link = phutil_render_tag(
'a',
array(
'href' => '/feed/?after='.reset($feed)->getChronologicalKey(),
'href' => '?after='.reset($feed)->getChronologicalKey(),
'class' => 'phabricator-feed-newer-link',
),
"\xC2\xAB Newer Stories");
@ -466,7 +505,7 @@ class PhabricatorDirectoryMainController
array(
'href' => $doc_href,
),
'Jump Nav Use Guide');
'Jump Nav User Guide');
$jump_input = phutil_render_tag(
'input',
@ -494,13 +533,17 @@ class PhabricatorDirectoryMainController
),
$jump_input));
$nav_buttons = array(
'/maniphest/task/create/' => 'Create a Task',
'/file/' => 'Upload a File',
'/paste/' => 'Create Paste',
'/w/' => 'Browse Wiki',
'/diffusion/' => 'Browse Code',
);
$nav_buttons = array();
if (PhabricatorEnv::getEnvConfig('maniphest.enabled')) {
$nav_buttons['/maniphest/task/create/'] = 'Create a Task';
}
$nav_buttons['/file/'] = 'Upload a File';
$nav_buttons['/paste/'] = 'Create Paste';
if (PhabricatorEnv::getEnvConfig('phriction.enabled')) {
$nav_buttons['/w/'] = 'Browse Wiki';
}
$nav_buttons['/diffusion/'] = 'Browse Code';
$panel->appendChild('<div class="phabricator-jump-nav-buttons">');
foreach ($nav_buttons as $uri => $name) {

View file

@ -18,6 +18,7 @@ phutil_require_module('phabricator', 'applications/maniphest/query');
phutil_require_module('phabricator', 'applications/maniphest/view/tasklist');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/project/query/project');
phutil_require_module('phabricator', 'applications/project/query/util');
phutil_require_module('phabricator', 'applications/search/storage/query');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/env');
@ -25,8 +26,10 @@ phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/layout/minipanel');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -237,6 +237,7 @@ class PhabricatorFileListController extends PhabricatorFileController {
$rowc[] = '';
}
$name = $file->getName();
$rows[] = array(
phutil_escape_html('F'.$file->getID()),
$file->getAuthorPHID()
@ -247,7 +248,7 @@ class PhabricatorFileListController extends PhabricatorFileController {
array(
'href' => $file->getBestURI(),
),
phutil_escape_html($file->getName())),
($name != '' ? phutil_escape_html($name) : '<em>no name</em>')),
phutil_escape_html(number_format($file->getByteSize()).' bytes'),
phutil_render_tag(
'a',

View file

@ -0,0 +1,197 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
final class ManiphestBatchEditController extends ManiphestController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$task_ids = $request->getArr('batch');
$tasks = id(new ManiphestTask())->loadAllWhere(
'id IN (%Ld)',
$task_ids);
$actions = $request->getStr('actions');
if ($actions) {
$actions = json_decode($actions, true);
}
if ($request->isFormPost() && is_array($actions)) {
foreach ($tasks as $task) {
$xactions = $this->buildTransactions($actions, $task);
if ($xactions) {
$editor = new ManiphestTransactionEditor();
$editor->applyTransactions($task, $xactions);
}
}
return id(new AphrontRedirectResponse())
->setURI('/maniphest/');
}
$panel = new AphrontPanelView();
$panel->setHeader('Maniphest Batch Editor');
$handle_phids = mpull($tasks, 'getOwnerPHID');
$handles = id(new PhabricatorObjectHandleData($handle_phids))
->loadHandles();
$list = new ManiphestTaskListView();
$list->setTasks($tasks);
$list->setUser($user);
$list->setHandles($handles);
$template = new AphrontTokenizerTemplateView();
$template = $template->render();
require_celerity_resource('maniphest-batch-editor');
Javelin::initBehavior(
'maniphest-batch-editor',
array(
'root' => 'maniphest-batch-edit-form',
'tokenizerTemplate' => $template,
'sources' => array(
'project' => '/typeahead/common/projects/',
),
'input' => 'batch-form-actions',
));
$form = new AphrontFormView();
$form->setUser($user);
$form->setID('maniphest-batch-edit-form');
foreach ($tasks as $task) {
$form->appendChild(
phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'batch[]',
'value' => $task->getID(),
),
null));
}
$form->appendChild(
phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'actions',
'id' => 'batch-form-actions',
),
null));
$form->appendChild('<p>These tasks will be edited:</p>');
$form->appendChild($list);
$form->appendChild(
'<h1>Actions</h1>'.
'<div class="aphront-form-inset">'.
'<div style="float: right;">'.
javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'add-action',
'mustcapture' => true,
),
'Add Another Action').
'</div>'.
'<div style="clear: both;"></div>'.
javelin_render_tag(
'table',
array(
'sigil' => 'maniphest-batch-actions',
'class' => 'maniphest-batch-actions-table',
),
'').
'</div>')
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Update Tasks')
->addCancelButton('/maniphest/', 'Done'));
$panel->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Batch Editor',
));
}
private function buildTransactions($actions, ManiphestTask $task) {
$template = new ManiphestTransaction();
$template->setAuthorPHID($this->getRequest()->getUser()->getPHID());
// TODO: Set content source to "batch edit".
$xactions = array();
foreach ($actions as $action) {
$value = $action['value'];
switch ($action['action']) {
case 'add_project':
case 'remove_project':
$is_remove = ($action['action'] == 'remove_project');
$current = array_fill_keys($task->getProjectPHIDs(), true);
$value = array_fill_keys($value, true);
$new = $current;
$did_something = false;
if ($is_remove) {
foreach ($value as $phid => $ignored) {
if (isset($new[$phid])) {
unset($new[$phid]);
$did_something = true;
}
}
} else {
foreach ($value as $phid => $ignored) {
if (empty($new[$phid])) {
$new[$phid] = true;
$did_something = true;
}
}
}
if (!$did_something) {
break;
}
$new = array_keys($new);
$xaction = clone $template;
$xaction->setTransactionType(ManiphestTransactionType::TYPE_PROJECTS);
$xaction->setNewValue($new);
$xactions[] = $xaction;
break;
}
}
return $xactions;
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
phutil_require_module('phabricator', 'applications/maniphest/view/tasklist');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/control/tokenizer');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('ManiphestBatchEditController.php');

View file

@ -222,20 +222,35 @@ class ManiphestTaskListController extends ManiphestController {
"Displaying tasks {$cur} - {$max} of {$tot}.".
'</div>');
$selector = new AphrontNullView();
foreach ($tasks as $group => $list) {
$task_list = new ManiphestTaskListView();
$task_list->setShowBatchControls(true);
$task_list->setUser($user);
$task_list->setTasks($list);
$task_list->setHandles($handles);
$count = number_format(count($list));
$nav->appendChild(
$selector->appendChild(
'<h1 class="maniphest-task-group-header">'.
phutil_escape_html($group).' ('.$count.')'.
'</h1>');
$nav->appendChild($task_list);
$selector->appendChild($task_list);
}
$selector->appendChild($this->renderBatchEditor());
$selector = phabricator_render_form(
$user,
array(
'method' => 'POST',
'action' => '/maniphest/batch/',
),
$selector->render());
$nav->appendChild($selector);
$nav->appendChild($pager);
}
@ -479,4 +494,61 @@ class ManiphestTaskListController extends ManiphestController {
return implode("\n", $links);
}
private function renderBatchEditor() {
Javelin::initBehavior(
'maniphest-batch-selector',
array(
'selectAll' => 'batch-select-all',
'selectNone' => 'batch-select-none',
'submit' => 'batch-select-submit',
'status' => 'batch-select-status-cell',
));
$select_all = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'class' => 'grey button',
'id' => 'batch-select-all',
),
'Select All');
$select_none = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'class' => 'grey button',
'id' => 'batch-select-none',
),
'Clear Selection');
$submit = phutil_render_tag(
'button',
array(
'id' => 'batch-select-submit',
'disabled' => 'disabled',
'class' => 'disabled',
),
'Batch Edit Selected Tasks &raquo;');
return
'<div class="maniphest-batch-editor">'.
'<div class="batch-editor-header">Batch Task Editor</div>'.
'<table class="maniphest-batch-editor-layout">'.
'<tr>'.
'<td>'.
$select_all.
$select_none.
'</td>'.
'<td id="batch-select-status-cell">'.
'0 Selected Tasks'.
'</td>'.
'<td class="batch-select-submit-cell">'.$submit.'</td>'.
'</tr>'.
'</table>'.
'</table>';
}
}

View file

@ -14,6 +14,8 @@ phutil_require_module('phabricator', 'applications/maniphest/query');
phutil_require_module('phabricator', 'applications/maniphest/view/tasklist');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
@ -22,6 +24,7 @@ phutil_require_module('phabricator', 'view/form/control/togglebuttons');
phutil_require_module('phabricator', 'view/form/control/tokenizer');
phutil_require_module('phabricator', 'view/layout/listfilter');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phabricator', 'view/null');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'parser/uri');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -253,6 +253,8 @@ class ManiphestTransactionEditor {
$prefix = $this->getSubjectPrefix();
$subject = trim("{$prefix} [{$action}] T{$task_id}: {$title}");
$mailtags = $this->getMailTags($transactions);
$template = id(new PhabricatorMetaMTAMail())
->setSubject($subject)
->setFrom($transaction->getAuthorPHID())
@ -261,6 +263,7 @@ class ManiphestTransactionEditor {
->setThreadID($thread_id, $is_create)
->setRelatedPHID($task->getPHID())
->setIsBulk(true)
->setMailTags($mailtags)
->setBody($body);
$mails = $reply_handler->multiplexMail(
@ -349,4 +352,30 @@ class ManiphestTransactionEditor {
return $is_create;
}
private function getMailTags(array $transactions) {
$tags = array();
foreach ($transactions as $xaction) {
switch ($xaction->getTransactionType()) {
case ManiphestTransactionType::TYPE_CCS:
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_CC;
break;
case ManiphestTransactionType::TYPE_PROJECTS:
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS;
break;
case ManiphestTransactionType::TYPE_PRIORITY:
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY;
break;
default:
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER;
break;
}
if ($xaction->hasComments()) {
$tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT;
}
}
return array_unique($tags);
}
}

View file

@ -12,6 +12,7 @@ phutil_require_module('phabricator', 'applications/maniphest/constants/action');
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
phutil_require_module('phabricator', 'applications/maniphest/view/transactiondetail');
phutil_require_module('phabricator', 'applications/metamta/constants/notificationtype');
phutil_require_module('phabricator', 'applications/metamta/storage/mail');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'applications/search/index/indexer/maniphest');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ class ManiphestTaskListView extends ManiphestView {
private $tasks;
private $handles;
private $user;
private $showBatchControls;
public function setTasks(array $tasks) {
$this->tasks = $tasks;
@ -40,12 +41,18 @@ class ManiphestTaskListView extends ManiphestView {
return $this;
}
public function setShowBatchControls($show_batch_controls) {
$this->showBatchControls = $show_batch_controls;
return $this;
}
public function render() {
$views = array();
foreach ($this->tasks as $task) {
$view = new ManiphestTaskSummaryView();
$view->setTask($task);
$view->setShowBatchControls($this->showBatchControls);
$view->setUser($this->user);
$view->setHandles($this->handles);
$views[] = $view->render();

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ class ManiphestTaskSummaryView extends ManiphestView {
private $task;
private $handles;
private $user;
private $showBatchControls;
public function setTask(ManiphestTask $task) {
$this->task = $task;
@ -40,6 +41,11 @@ class ManiphestTaskSummaryView extends ManiphestView {
return $this;
}
public function setShowBatchControls($show_batch_controls) {
$this->showBatchControls = $show_batch_controls;
return $this;
}
public function render() {
if (!$this->user) {
@ -63,36 +69,58 @@ class ManiphestTaskSummaryView extends ManiphestView {
$pri_class = idx($classes, $task->getPriority());
$status_map = ManiphestTaskStatus::getTaskStatusMap();
return
'<table class="maniphest-task-summary">'.
'<tr>'.
'<td class="maniphest-task-number '.$pri_class.'">'.
'T'.$task->getID().
'</td>'.
'<td class="maniphest-task-status">'.
idx($status_map, $task->getStatus(), 'Unknown').
'</td>'.
'<td class="maniphest-task-owner">'.
($task->getOwnerPHID()
? $handles[$task->getOwnerPHID()]->renderLink()
: '<em>None</em>').
'</td>'.
'<td class="maniphest-task-name">'.
phutil_render_tag(
'a',
array(
'href' => '/T'.$task->getID(),
),
phutil_escape_html($task->getTitle())).
'</td>'.
'<td class="maniphest-task-priority">'.
ManiphestTaskPriority::getTaskPriorityName($task->getPriority()).
'</td>'.
'<td class="maniphest-task-updated">'.
phabricator_datetime($task->getDateModified(), $this->user).
'</td>'.
'</tr>'.
'</table>';
$batch = null;
if ($this->showBatchControls) {
$batch =
'<td class="maniphest-task-batch">'.
javelin_render_tag(
'input',
array(
'type' => 'checkbox',
'name' => 'batch[]',
'value' => $task->getID(),
'sigil' => 'maniphest-batch',
),
null).
'</td>';
}
return javelin_render_tag(
'table',
array(
'class' => 'maniphest-task-summary',
'sigil' => 'maniphest-task',
),
'<tr>'.
'<td class="maniphest-task-handle '.$pri_class.'">'.
'</td>'.
$batch.
'<td class="maniphest-task-number">'.
'T'.$task->getID().
'</td>'.
'<td class="maniphest-task-status">'.
idx($status_map, $task->getStatus(), 'Unknown').
'</td>'.
'<td class="maniphest-task-owner">'.
($task->getOwnerPHID()
? $handles[$task->getOwnerPHID()]->renderLink()
: '<em>None</em>').
'</td>'.
'<td class="maniphest-task-name">'.
phutil_render_tag(
'a',
array(
'href' => '/T'.$task->getID(),
),
phutil_escape_html($task->getTitle())).
'</td>'.
'<td class="maniphest-task-priority">'.
ManiphestTaskPriority::getTaskPriorityName($task->getPriority()).
'</td>'.
'<td class="maniphest-task-updated">'.
phabricator_datetime($task->getDateModified(), $this->user).
'</td>'.
'</tr>');
}
}

View file

@ -10,6 +10,7 @@ phutil_require_module('phabricator', 'applications/maniphest/constants/priority'
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/view/base');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');

View file

@ -173,14 +173,11 @@ class ManiphestTransactionDetailView extends ManiphestView {
),
$author->renderLink().' '.$desc.'.'.$full_summary);
}
$descs = implode("\n", $descs);
if ($this->getRenderSummaryOnly()) {
return $descs;
return implode("\n", $descs);
}
$more_classes = implode(' ', $more_classes);
if ($comment_transaction && $comment_transaction->hasComments()) {
$comments = $comment_transaction->getCache();
if (!strlen($comments)) {
@ -203,58 +200,33 @@ class ManiphestTransactionDetailView extends ManiphestView {
$comment_block = null;
}
if ($this->preview) {
$timestamp = 'COMMENT PREVIEW';
} else {
$timestamp = phabricator_datetime(
$transaction->getDateCreated(),
$this->user);
}
$info = array();
$source_transaction = nonempty($comment_transaction, $any_transaction);
$content_source = new PhabricatorContentSourceView();
$content_source->setContentSource($source_transaction->getContentSource());
$content_source->setUser($this->user);
$info[] = $content_source->render();
$info[] = $timestamp;
$xaction_view = id(new PhabricatorTransactionView())
->setUser($this->user)
->setImageURI($author->getImageURI())
->setContentSource($source_transaction->getContentSource())
->setActions($descs);
$comment_anchor = null;
$num = $this->commentNumber;
if ($num && !$this->preview) {
Javelin::initBehavior('phabricator-watch-anchor');
$anchor_name = 'comment-'.$num;
$info[] = javelin_render_tag(
'a',
array(
'name' => $anchor_name,
'id' => $anchor_name,
'href' => '#'.$anchor_name,
),
'T'.$any_transaction->getTaskID().'#'.$anchor_name);
$comment_anchor = 'anchor-'.$anchor_name;
foreach ($more_classes as $class) {
$xaction_view->addClass($class);
}
$info = implode(' &middot; ', array_filter($info));
if ($this->preview) {
$xaction_view->setIsPreview($this->preview);
} else {
$xaction_view->setEpoch($any_transaction->getDateCreated());
if ($this->commentNumber) {
$anchor_name = 'comment-'.$this->commentNumber;
$anchor_text = 'T'.$any_transaction->getTaskID().'#'.$anchor_name;
return phutil_render_tag(
'div',
array(
'class' => "maniphest-transaction-detail-container",
'style' => "background-image: url('".$author->getImageURI()."')",
'id' => $comment_anchor,
),
'<div class="maniphest-transaction-detail-view '.$more_classes.'">'.
'<div class="maniphest-transaction-header">'.
'<div class="maniphest-transaction-timestamp">'.
$info.
'</div>'.
$descs.
'</div>'.
$comment_block.
'</div>');
$xaction_view->setAnchor($anchor_name, $anchor_text);
}
}
$xaction_view->appendChild($comment_block);
return $xaction_view->render();
}
private function renderSupplementalInfoForEmail($transaction) {

View file

@ -12,14 +12,13 @@ phutil_require_module('phabricator', 'applications/maniphest/constants/priority'
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
phutil_require_module('phabricator', 'applications/maniphest/view/base');
phutil_require_module('phabricator', 'applications/metamta/contentsource/view');
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/diff/engine');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phabricator', 'view/layout/transaction');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -59,6 +59,11 @@ class PhabricatorMarkupEngine {
));
}
public static function newDiffusionMarkupEngine(array $options = array()) {
return self::newMarkupEngine(array(
));
}
public static function newProfileMarkupEngine() {
return self::newMarkupEngine(array(
));

View file

@ -0,0 +1,21 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
abstract class MetaMTAConstants {
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('MetaMTAConstants.php');

View file

@ -0,0 +1,32 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class MetaMTANotificationType
extends MetaMTAConstants {
const TYPE_DIFFERENTIAL_COMMITTED = 'differential-committed';
const TYPE_DIFFERENTIAL_CC = 'differential-cc';
const TYPE_DIFFERENTIAL_COMMENT = 'differential-comment';
const TYPE_MANIPHEST_PROJECTS = 'maniphest-projects';
const TYPE_MANIPHEST_PRIORITY = 'maniphest-priority';
const TYPE_MANIPHEST_CC = 'maniphest-cc';
const TYPE_MANIPHEST_OTHER = 'maniphest-other';
const TYPE_MANIPHEST_COMMENT = 'maniphest-comment';
}

Some files were not shown because too many files have changed in this diff Show more