Compare commits
	
		
			351 Commits
		
	
	
		
			master
			...
			blender-tw
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 436e69f1d0 | ||
| 5db4b6f01a | |||
| 5aabb434b1 | |||
| eced78a591 | |||
| fb3e6ba493 | |||
| 284204e6bb | |||
| 1df20d771c | |||
| 6fa976f749 | |||
| 8808cf8e8c | |||
| f72b9824f3 | |||
| ac94643ac7 | |||
| ce3c14919d | |||
| c9fb4c2945 | |||
| 57099f29d1 | |||
| acfdc33789 | |||
| a5efb1e8cd | |||
| e0bd65be95 | |||
| 1bcc201b0a | |||
| b2d7879162 | |||
| 775a1c2eef | |||
| bd07cea6a6 | |||
| 6139ce1841 | |||
| c38766c17f | |||
| f9637502ee | |||
|   | 0ddfe6fcfb | ||
|   | dc57652085 | ||
| 5ba0687123 | |||
| fef207bf00 | |||
| 5ef3552549 | |||
| 0f6ca4be21 | |||
| 4653348b13 | |||
| 6826284108 | |||
| c93fdca80d | |||
| 432b6d61a6 | |||
| 4c2b7d7573 | |||
| d8e25a6226 | |||
| db6f4eaab1 | |||
| 4555f18c41 | |||
| 788d8a8b98 | |||
| 55f22f74f5 | |||
| de2f7e857d | |||
| 7c869ab033 | |||
| f9c680ddc0 | |||
| 8efe191897 | |||
| 8d6f1a6128 | |||
| 81bae35d2f | |||
| fc5744c7f0 | |||
| 7aea82d526 | |||
| cb29054e66 | |||
| 0416648b39 | |||
| 01fea69352 | |||
| c7a2133639 | |||
| a162f188c2 | |||
| 2b54804c7b | |||
| a8fef30871 | |||
| 9dc4d903c7 | |||
| b8b18995c5 | |||
| 971b397133 | |||
| aee7dc94bc | |||
| feb8db1212 | |||
| f9f7f005f2 | |||
| 2321a886ae | |||
| 72858c6746 | |||
| 79ca5a3026 | |||
| 3d37998174 | |||
| ce29e40b57 | |||
| ef26d6afdc | |||
| dc84bc9391 | |||
| 321eb18cd7 | |||
| 6936b8c4f3 | |||
| 26f43ee9b8 | |||
| e19d318af9 | |||
| dfc7f764de | |||
| 331b4cc42c | |||
| 674d537f91 | |||
| a442a019d9 | |||
| 774a554405 | |||
| 90f56e07ff | |||
| 4b428ffb8a | |||
| bcd0caecd1 | |||
| 78c32afa4e | |||
| f3302fe1b9 | |||
| 7eba06c43d | |||
| 713f8e2c6a | |||
| ea0100310b | |||
| 4332bfe537 | |||
| 25ce2bdd92 | |||
| dc17ef4f7c | |||
| 8097508909 | |||
| 8addc33734 | |||
| 990136a337 | |||
| d14b204aae | |||
| 4067829d2c | |||
| 8bc4c8c485 | |||
| bf8aec2a14 | |||
| ace2787868 | |||
| 08dde1da13 | |||
| 4cde7a4b06 | |||
| 42ce8a82af | |||
| 6d6e446f6f | |||
| 26bf667fec | |||
| 18b9c9b0b1 | |||
| f78ca8833b | |||
| 19337de8f0 | |||
| c2a4fbc98c | |||
| 87c0e13efd | |||
| 3442097b31 | |||
| c7764ed1d0 | |||
| 6e39022af9 | |||
| d88716feba | |||
| 678347d836 | |||
| 890ccea846 | |||
| 405525357d | |||
| 3eee2a3311 | |||
| 55d1f0081d | |||
| ea73d9bf86 | |||
| 3eab15180c | |||
| 2210be45fe | |||
| 5bdc3dfad8 | |||
| c22440fe30 | |||
| b41844290b | |||
| 32a3440156 | |||
| 630eb3c143 | |||
| 534b628ad2 | |||
| 8a463524b4 | |||
| b50eefbd5b | |||
| 3a2ebeca11 | |||
| 95f7da61a9 | |||
| 0d04beb777 | |||
| 7ab26ff7af | |||
| 6800fc6103 | |||
| 9077c1f24f | |||
| 2327f2af6f | |||
| 41fff9c930 | |||
| cfcef40c65 | |||
| 29d158a3bc | |||
| 411cf324dd | |||
| 52230a803f | |||
| ddf6f275db | |||
| 8c65ee54d0 | |||
| d07d44fcc7 | |||
| 772d59f0ab | |||
| f7da3f88f9 | |||
| e4f6cf9993 | |||
| dd34c93656 | |||
| 320ffa2308 | |||
| b5e7136f68 | |||
| e2b7664451 | |||
| 725ad07e6f | |||
| 75cbd69d85 | |||
| a9a71ef011 | |||
| 362ba36695 | |||
| 38d687ede2 | |||
| 78cd1cf494 | |||
| 0b990d42cd | |||
| 74571de5b6 | |||
| 01ce61f6e2 | |||
| 09c9aa078d | |||
| 73441d690e | |||
| afdc21ee04 | |||
| fc317b7f28 | |||
| b3f2b7faf4 | |||
| 7ea70dcb53 | |||
| d0b90fdef7 | |||
| 3c05d31d2e | |||
| 11f5eb21af | |||
| c90665645e | |||
| fb3c71820b | |||
| e533774ac0 | |||
| 647f8de9bb | |||
| 599bf57cd8 | |||
| 50c506ea2d | |||
| 4cc7990b1a | |||
| 4ef5fdefb2 | |||
| a018837214 | |||
| e74f0e1558 | |||
| fd882b5167 | |||
| 15bda62b89 | |||
| 5e4ce96266 | |||
| dbd2f0d06f | |||
| c87e657412 | |||
| 6275e3f08f | |||
| bc2e4a2452 | |||
| ba7d9fb724 | |||
| c50ab96abb | |||
| fa68c6a0fa | |||
| d1b60fec48 | |||
| 99a839605c | |||
| 08f5b3e920 | |||
| 87e0a26d24 | |||
| 91c07e9f99 | |||
| 9b2b75e4b4 | |||
| 77c913c70a | |||
| 1a1bea527c | |||
| 46f8f6d90d | |||
| dbad3b6489 | |||
| 1821cedbc4 | |||
| 1448eafd64 | |||
| 8f4047b36a | |||
| 2683fde42a | |||
| 02522d877d | |||
| 769ffa2982 | |||
| 33eed14141 | |||
| a0bd47a012 | |||
| 8c14ef97bf | |||
| 5bdfbe8250 | |||
| f2319c9689 | |||
| c3138c6497 | |||
| 56e8308621 | |||
| cb6a8756b6 | |||
| e7242143a0 | |||
| c12898b63a | |||
| 9453f29deb | |||
| 9093816113 | |||
| 3471c1b87e | |||
| 30f0de9427 | |||
| 5278e94566 | |||
| 08b1e9d764 | |||
| 30427e0626 | |||
| 752a5b9bc3 | |||
| a5390d5461 | |||
| a02fa19d5a | |||
| f88bdcd4ff | |||
| b98f8a570c | |||
| 644070340d | |||
| 93fecfe7e9 | |||
| 53bb3ac361 | |||
| 137df8c6d3 | |||
| 9863285bf0 | |||
| a392b4a14f | |||
| d03569e908 | |||
| b1097e9c61 | |||
| 5bda6bf48b | |||
| a03cdacd9e | |||
| c3fe33f5a0 | |||
| 5d823ff1a7 | |||
| 63df9dd1f4 | |||
| 0c80c9fead | |||
| b8d42a7cc6 | |||
| 4e7dc30fea | |||
| 99c68b1091 | |||
| d48979091e | |||
| 785caa5b6c | |||
| 261d7331f4 | |||
| 28bd8409df | |||
| 47008a157c | |||
| fc1e8681ef | |||
| 17085b4379 | |||
| dd6eefcd9c | |||
| ae2b0e63d6 | |||
| bcbae097de | |||
| b59dca4eeb | |||
| 8098798fa9 | |||
| d8d0e2c99f | |||
| 214233f82f | |||
| eebe82c990 | |||
| 5b73071d64 | |||
| cd04de7f72 | |||
| cf4b1317e1 | |||
| 10e353b090 | |||
| 81bd756ec6 | |||
| 9527d8aafd | |||
| e35bc58e0e | |||
| bd0f9fd913 | |||
| 44957f7806 | |||
| 7ff26eb95d | |||
| 1f2471fa67 | |||
| 7a5588a408 | |||
| ac03ae737f | |||
| f9db95f555 | |||
| 2bc045a997 | |||
| 531b0fa529 | |||
| 6b1ed4fb9b | |||
| 645a6a9f54 | |||
| 88cd291b0f | |||
| 76e4aaf429 | |||
| 365f8ad981 | |||
| 0af08f9469 | |||
| 23ce658af7 | |||
| 0a54474f98 | |||
| 93ca9ed3a8 | |||
| d110fdccc2 | |||
| 4197d983bf | |||
| d23883b799 | |||
| 675d2ac374 | |||
| 5c86735ab7 | |||
| 566274dcac | |||
| de2814545b | |||
| 4ea26f22c4 | |||
| 458bc7f921 | |||
| 3839698e4a | |||
| b403863e62 | |||
| 246b73f922 | |||
| 6da7f462a9 | |||
| 90874b8b00 | |||
| 7e874a33fb | |||
| bbe357e5c0 | |||
| 428cdae3cf | |||
| 1c689a6b24 | |||
| 55972ef2c1 | |||
| 966687d1ab | |||
| 6a86144adf | |||
| 57765e88c0 | |||
| 60cddad047 | |||
| a77a33dc50 | |||
| 405a07e638 | |||
| 75f9ce9d77 | |||
| 259588e747 | |||
| 0e8b746fb0 | |||
| 753917c10c | |||
| 39f7364f25 | |||
| 8d7de5fcb6 | |||
| 01398f762b | |||
| e12d2efe81 | |||
| 5c7055d840 | |||
| 562c7aa126 | |||
| 5994088e50 | |||
| 9edbe22e19 | |||
| 7fabfff397 | |||
| 356a2fe0e8 | |||
| 54193ebb15 | |||
| 2b4bb9cddd | |||
| 2758907517 | |||
| a7f641eee2 | |||
| 9799ceff80 | |||
| 26e86c2171 | |||
| f6c293fb4d | |||
| 5abc5cf9f8 | |||
| 41a820fdf0 | |||
| 2c8b09da21 | |||
| a7ff526360 | |||
| 1a8148be97 | |||
| 199dd85140 | |||
| f6c44fdb03 | |||
| 1aade72cb1 | |||
| 121c9d308b | |||
| d60a47ab5b | |||
| 8af9b8796f | |||
| 7dd7673fb3 | |||
| aeaf0902e1 | |||
| 498842475c | |||
| 755177508e | |||
| 5b49fd93e2 | |||
| 968ea35b03 | |||
| 97ed90b82a | |||
| ba34aa535a | |||
| e56041da3b | |||
| b19fbc39af | |||
| 1036d419fa | |||
| 0011789443 | |||
| ff7d71fb87 | 
| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|   "phabricator.uri": "https://secure.phabricator.com/", | ||||
|   "phabricator.uri": "https://developer.blender.org/", | ||||
|   "load": ["src/"], | ||||
|   "history.immutable": false | ||||
| } | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -40,3 +40,6 @@ | ||||
| # Places for users to add custom resources. | ||||
| /resources/cows/custom/* | ||||
| /resources/figlet/custom/* | ||||
|  | ||||
| # blender migration files | ||||
| migration/dump/ | ||||
|   | ||||
							
								
								
									
										610
									
								
								migration/dedup.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,610 @@ | ||||
| <?php | ||||
|  | ||||
| $migrate_dedup_users = array(); | ||||
|  | ||||
| $migrate_dedup_users["midiclub"] = "midclub"; | ||||
| $migrate_dedup_users["trip"] = "car"; | ||||
| $migrate_dedup_users["thunderbolt"] = "thunderbolt16"; | ||||
| $migrate_dedup_users["avirnig"] = "beav"; | ||||
| $migrate_dedup_users["jeaninmontreal"] = "jeanm"; | ||||
| $migrate_dedup_users["nneo"] = "nneonneo"; | ||||
| $migrate_dedup_users["ebonyj"] = "jim"; | ||||
| $migrate_dedup_users["tnr"] = "ichem"; | ||||
| $migrate_dedup_users["archus"] = "rstehwien"; | ||||
| $migrate_dedup_users["kalysto"] = "kalar"; | ||||
| $migrate_dedup_users["eloe"] = "mouvanteloe"; | ||||
| $migrate_dedup_users["erdling"] = "eitj"; | ||||
| $migrate_dedup_users["cwinebrinner"] = "chuck_1027"; | ||||
| $migrate_dedup_users["foo"] = "mfranz"; | ||||
| $migrate_dedup_users["f00fbug"] = "viviowns"; | ||||
| $migrate_dedup_users["olavbalm"] = "olav"; | ||||
| $migrate_dedup_users["kapong"] = "el_kapong"; | ||||
| $migrate_dedup_users["thorax"] = "rofthorax"; | ||||
| $migrate_dedup_users["kramer3d"] = "asdfghytrewq"; | ||||
| $migrate_dedup_users["reevee"] = "vee"; | ||||
| $migrate_dedup_users["matzeb"] = "matzebraun"; | ||||
| $migrate_dedup_users["jmsoler"] = "jms"; | ||||
| $migrate_dedup_users["aqa"] = "angelito"; | ||||
| $migrate_dedup_users["blendorphin"] = "blenergetic"; | ||||
| $migrate_dedup_users["paolo"] = "jody"; | ||||
| $migrate_dedup_users["blendedidea"] = "chubango"; | ||||
| $migrate_dedup_users["ruiramos"] = "p3ctu5"; | ||||
| $migrate_dedup_users["head_chees"] = "vassilizaitsaf"; | ||||
| $migrate_dedup_users["kiernan"] = "rofthorax"; | ||||
| $migrate_dedup_users["olaf_arnold"] = "olafarnold"; | ||||
| $migrate_dedup_users["joseyanesaltos"] = "joseyanes"; | ||||
| $migrate_dedup_users["nicholasfrancis"] = "nicholas"; | ||||
| $migrate_dedup_users["briancary"] = "brinux"; | ||||
| $migrate_dedup_users["piotrek"] = "ccpiotr"; | ||||
| $migrate_dedup_users["slashdev"] = "colonel_panic"; | ||||
| $migrate_dedup_users["rj2004"] = "rjblender"; | ||||
| $migrate_dedup_users["jeffreymcgrew"] = "toast"; | ||||
| $migrate_dedup_users["hendersj"] = "jhenderson"; | ||||
| $migrate_dedup_users["pierpy"] = "pier"; | ||||
| $migrate_dedup_users["buschhardt"] = "ishtar"; | ||||
| $migrate_dedup_users["timlesher"] = "tim"; | ||||
| $migrate_dedup_users["tizmo"] = "timlucas"; | ||||
| $migrate_dedup_users["pprandin"] = "pashaweb"; | ||||
| $migrate_dedup_users["servivo"] = "taz"; | ||||
| $migrate_dedup_users["stabby"] = "xingo"; | ||||
| $migrate_dedup_users["jimmydietulpe"] = "jimmadietulpe"; | ||||
| $migrate_dedup_users["palmnet"] = "palmer"; | ||||
| $migrate_dedup_users["graphique-chti"] = "gcprod"; | ||||
| $migrate_dedup_users["dreamkatana1"] = "dreamkatana"; | ||||
| $migrate_dedup_users["wolverine"] = "shane"; | ||||
| $migrate_dedup_users["schwarz"] = "wene"; | ||||
| $migrate_dedup_users["justtesting"] = "levon"; | ||||
| $migrate_dedup_users["erwin_xnan"] = "erwin"; | ||||
| $migrate_dedup_users["zack"] = "krystof"; | ||||
| $migrate_dedup_users["fabinator31"] = "fab31"; | ||||
| $migrate_dedup_users["themilkman"] = "cliftonium"; | ||||
| $migrate_dedup_users["etiennel"] = "choupi"; | ||||
| $migrate_dedup_users["m_i_c_h_e_l"] = "michaels"; | ||||
| $migrate_dedup_users["techrolla"] = "muffinpeddler"; | ||||
| $migrate_dedup_users["fireflymantis"] = "twilight"; | ||||
| $migrate_dedup_users["pinucset0"] = "pinucset"; | ||||
| $migrate_dedup_users["franzrogar"] = "blender-i18n-po"; | ||||
| $migrate_dedup_users["pinucset1"] = "pinucset"; | ||||
| $migrate_dedup_users["pinucset3"] = "pinucset"; | ||||
| $migrate_dedup_users["threeddream"] = "x-axis"; | ||||
| $migrate_dedup_users["mudbot"] = "laurence"; | ||||
| $migrate_dedup_users["ligeyx"] = "ligey"; | ||||
| $migrate_dedup_users["dreas"] = "dre"; | ||||
| $migrate_dedup_users["joorvapl"] = "joorva"; | ||||
| $migrate_dedup_users["voyage"] = "xenithi"; | ||||
| $migrate_dedup_users["azter"] = "azterix"; | ||||
| $migrate_dedup_users["lapinbleu"] = "simeon"; | ||||
| $migrate_dedup_users["emmanuel_t"] = "emmanuel"; | ||||
| $migrate_dedup_users["cricket"] = "gku"; | ||||
| $migrate_dedup_users["justanumber"] = "mad4linux"; | ||||
| $migrate_dedup_users["johnnyen"] = "johnny"; | ||||
| $migrate_dedup_users["leiota"] = "marcopolo"; | ||||
| $migrate_dedup_users["blackstorm"] = "ikbendirk"; | ||||
| $migrate_dedup_users["richie"] = "ligey"; | ||||
| $migrate_dedup_users["mrcarnivore2"] = "mrcarnivore"; | ||||
| $migrate_dedup_users["muddym1nd"] = "muddymind"; | ||||
| $migrate_dedup_users["eumesmo"] = "vasco_da_gama"; | ||||
| $migrate_dedup_users["ph-kby"] = "olav"; | ||||
| $migrate_dedup_users["piotrek_"] = "ccpiotr"; | ||||
| $migrate_dedup_users["joaquin"] = "prospero"; | ||||
| $migrate_dedup_users["konrad_haenel"] = "konrad8ha"; | ||||
| $migrate_dedup_users["benstabler"] = "lightning"; | ||||
| $migrate_dedup_users["gustav"] = "draknoid"; | ||||
| $migrate_dedup_users["slikdigit"] = "bassamk"; | ||||
| $migrate_dedup_users["karlerlandsen"] = "lethalsidep"; | ||||
| $migrate_dedup_users["djwillmsoh"] = "djw1"; | ||||
| $migrate_dedup_users["aschmitz"] = "root_42"; | ||||
| $migrate_dedup_users["malfunc"] = "mcq"; | ||||
| $migrate_dedup_users["m_jack"] = "jack"; | ||||
| $migrate_dedup_users["davey"] = "madcow"; | ||||
| $migrate_dedup_users["iraytrace"] = "butler"; | ||||
| $migrate_dedup_users["duser"] = "mslechols"; | ||||
| $migrate_dedup_users["akito"] = "ton"; | ||||
| $migrate_dedup_users["jochenschmitt"] = "s4504kr"; | ||||
| $migrate_dedup_users["digikiller"] = "ace1"; | ||||
| $migrate_dedup_users["shzhc"] = "zzzz"; | ||||
| $migrate_dedup_users["cyrilbrulebois"] = "kibi"; | ||||
| $migrate_dedup_users["themushroom"] = "draknoid"; | ||||
| $migrate_dedup_users["zinkmaster"] = "mariolink"; | ||||
| $migrate_dedup_users["ashsid"] = "ash"; | ||||
| $migrate_dedup_users["novato"] = "jimenez"; | ||||
| $migrate_dedup_users["jadie_p"] = "jadie"; | ||||
| $migrate_dedup_users["neshshah007"] = "neshrai"; | ||||
| $migrate_dedup_users["ryanshow"] = "tankcoder"; | ||||
| $migrate_dedup_users["squarelinesq"] = "squareline"; | ||||
| $migrate_dedup_users["virginijus"] = "ernestas1994"; | ||||
| $migrate_dedup_users["renderdemon"] = "pnio"; | ||||
| $migrate_dedup_users["kwab"] = "kobi"; | ||||
| $migrate_dedup_users["peterjonckheere"] = "jonckheerep"; | ||||
| $migrate_dedup_users["vidar"] = "vidarino"; | ||||
| $migrate_dedup_users["sapjohannes"] = "johannesje"; | ||||
| $migrate_dedup_users["youri"] = "ayers"; | ||||
| $migrate_dedup_users["theamiman"] = "amiman"; | ||||
| $migrate_dedup_users["chrisg_ksi"] = "red_planet"; | ||||
| $migrate_dedup_users["blenderorgfan"] = "franciscosilva"; | ||||
| $migrate_dedup_users["ericd"] = "ericdrayer"; | ||||
| $migrate_dedup_users["asdfghjkl2"] = "asdfghjkl"; | ||||
| $migrate_dedup_users["quorn"] = "alienit"; | ||||
| $migrate_dedup_users["daan221"] = "phoenix221"; | ||||
| $migrate_dedup_users["user"] = "mslechols"; | ||||
| $migrate_dedup_users["pyb"] = "lomperillo"; | ||||
| $migrate_dedup_users["g56"] = "gwald"; | ||||
| $migrate_dedup_users["ausland"] = "vots"; | ||||
| $migrate_dedup_users["quillinan"] = "orinoco"; | ||||
| $migrate_dedup_users["t3tsuj1n"] = "bootsheoz"; | ||||
| $migrate_dedup_users["molflesh"] = "melflash"; | ||||
| $migrate_dedup_users["rickta59"] = "aerobicsboy"; | ||||
| $migrate_dedup_users["nolopoly"] = "lopoly"; | ||||
| $migrate_dedup_users["jeel"] = "jl57"; | ||||
| $migrate_dedup_users["brianmccumber2"] = "brianmccumber"; | ||||
| $migrate_dedup_users["mouette"] = "moutte"; | ||||
| $migrate_dedup_users["wout"] = "wonderingwout"; | ||||
| $migrate_dedup_users["fatfinger"] = "mattyc"; | ||||
| $migrate_dedup_users["nathangull"] = "nateg"; | ||||
| $migrate_dedup_users["tampadave"] = "dkmweeks"; | ||||
| $migrate_dedup_users["sausages"] = "lethargic"; | ||||
| $migrate_dedup_users["chris3d"] = "chrisbd"; | ||||
| $migrate_dedup_users["kaito"] = "ton"; | ||||
| $migrate_dedup_users["akirasan"] = "akira_b"; | ||||
| $migrate_dedup_users["fiddle"] = "rayf"; | ||||
| $migrate_dedup_users["bald"] = "manu"; | ||||
| $migrate_dedup_users["mistermelquin"] = "melquin"; | ||||
| $migrate_dedup_users["kofish"] = "kingdomoffish"; | ||||
| $migrate_dedup_users["onanj"] = "orangee"; | ||||
| $migrate_dedup_users["jasoncarrier"] = "jason"; | ||||
| $migrate_dedup_users["joelgodin"] = "jenniferblender"; | ||||
| $migrate_dedup_users["oranj"] = "orangee"; | ||||
| $migrate_dedup_users["schdeb"] = "schmidtcristian"; | ||||
| $migrate_dedup_users["bastian"] = "angerb"; | ||||
| $migrate_dedup_users["patko"] = "patco"; | ||||
| $migrate_dedup_users["arakyd666"] = "arakyd"; | ||||
| $migrate_dedup_users["karimf"] = "rim-k"; | ||||
| $migrate_dedup_users["nletwory"] = "jestertestest"; | ||||
| $migrate_dedup_users["mansoorhyder"] = "mansoor"; | ||||
| $migrate_dedup_users["rocketmagnet"] = "steventr"; | ||||
| $migrate_dedup_users["thekernal"] = "kernal"; | ||||
| $migrate_dedup_users["satom"] = "satriatomat"; | ||||
| $migrate_dedup_users["a624"] = "a623"; | ||||
| $migrate_dedup_users["jldavilacasares"] = "muki"; | ||||
| $migrate_dedup_users["segersj"] = "segers"; | ||||
| $migrate_dedup_users["hoisan49"] = "hoisan"; | ||||
| $migrate_dedup_users["j_bessette"] = "linuxpimp20"; | ||||
| $migrate_dedup_users["andreas999"] = "andreas"; | ||||
| $migrate_dedup_users["dusan"] = "dsc512"; | ||||
| $migrate_dedup_users["dx-mon"] = "mantr100"; | ||||
| $migrate_dedup_users["ilislab"] = "ids"; | ||||
| $migrate_dedup_users["rben"] = "raybenjamin"; | ||||
| $migrate_dedup_users["sphere"] = "dysonsphere"; | ||||
| $migrate_dedup_users["highlife22"] = "highlife"; | ||||
| $migrate_dedup_users["yonarw"] = "yona"; | ||||
| $migrate_dedup_users["kryptic89"] = "kryptic"; | ||||
| $migrate_dedup_users["axelp"] = "axel"; | ||||
| $migrate_dedup_users["perebalsach"] = "fog22"; | ||||
| $migrate_dedup_users["alogerson"] = "gerson"; | ||||
| $migrate_dedup_users["cotejrp"] = "cotejrp1"; | ||||
| $migrate_dedup_users["tak"] = "carlosjamesr"; | ||||
| $migrate_dedup_users["ziddy"] = "anishchandran"; | ||||
| $migrate_dedup_users["asdgz"] = "blenderiseur"; | ||||
| $migrate_dedup_users["cdated"] = "cdated257"; | ||||
| $migrate_dedup_users["jayrkalugin"] = "jayr"; | ||||
| $migrate_dedup_users["zeemzoet"] = "johannesje"; | ||||
| $migrate_dedup_users["duo"] = "ambyra"; | ||||
| $migrate_dedup_users["philb"] = "chewbacca"; | ||||
| $migrate_dedup_users["tzi"] = "izt"; | ||||
| $migrate_dedup_users["squirrelthetire"] = "squirrel-tire"; | ||||
| $migrate_dedup_users["anwyn"] = "sugarshark"; | ||||
| $migrate_dedup_users["a2z"] = "a2zaa"; | ||||
| $migrate_dedup_users["fxrex"] = "femi"; | ||||
| $migrate_dedup_users["alethewiz"] = "mfaso68"; | ||||
| $migrate_dedup_users["noseferrit"] = "billybong"; | ||||
| $migrate_dedup_users["fcali"] = "fabz"; | ||||
| $migrate_dedup_users["bbirchler"] = "bblender"; | ||||
| $migrate_dedup_users["oslosewers"] = "oslo"; | ||||
| $migrate_dedup_users["wtrsltnk"] = "wtr"; | ||||
| $migrate_dedup_users["cspohst"] = "spohst"; | ||||
| $migrate_dedup_users["warhawk08"] = "warhawk1990"; | ||||
| $migrate_dedup_users["magick_crow"] = "magickcrow"; | ||||
| $migrate_dedup_users["guillaumem"] = "guillaume"; | ||||
| $migrate_dedup_users["jwitthuhn"] = "rahu"; | ||||
| $migrate_dedup_users["fmehigan"] = "frank_me"; | ||||
| $migrate_dedup_users["ilissys"] = "ids"; | ||||
| $migrate_dedup_users["supermoaaa"] = "moaaa"; | ||||
| $migrate_dedup_users["nwmatt"] = "mhenley"; | ||||
| $migrate_dedup_users["bezel"] = "xshell"; | ||||
| $migrate_dedup_users["rebuss"] = "studioa"; | ||||
| $migrate_dedup_users["geonom"] = "geoadel"; | ||||
| $migrate_dedup_users["seldan"] = "farakon"; | ||||
| $migrate_dedup_users["kuru76"] = "kcorbin"; | ||||
| $migrate_dedup_users["kapil"] = "kapilbedarkar"; | ||||
| $migrate_dedup_users["henryiii"] = "henryschreiner"; | ||||
| $migrate_dedup_users["pateamcarl"] = "carlhuth"; | ||||
| $migrate_dedup_users["kamen"] = "bigbob1993"; | ||||
| $migrate_dedup_users["olddemon"] = "old_demon"; | ||||
| $migrate_dedup_users["draklaw2"] = "draklaw"; | ||||
| $migrate_dedup_users["ksdlee"] = "kdlee"; | ||||
| $migrate_dedup_users["xource"] = "admix"; | ||||
| $migrate_dedup_users["invertednormal"] = "smokebox46and2"; | ||||
| $migrate_dedup_users["feelgoodcomics"] = "onlygoodwin"; | ||||
| $migrate_dedup_users["glorund"] = "undolaure"; | ||||
| $migrate_dedup_users["soulofsound"] = "johnnym"; | ||||
| $migrate_dedup_users["ghigi123"] = "ghigi"; | ||||
| $migrate_dedup_users["stefanvoigthpi"] = "derstefan"; | ||||
| $migrate_dedup_users["dblenderv"] = "default"; | ||||
| $migrate_dedup_users["ophiocus"] = "oneliner"; | ||||
| $migrate_dedup_users["colleywrks"] = "colley"; | ||||
| $migrate_dedup_users["fogy"] = "fog22"; | ||||
| $migrate_dedup_users["vidarn"] = "bida70"; | ||||
| $migrate_dedup_users["kevin"] = "goblender2541"; | ||||
| $migrate_dedup_users["tisachris"] = "warflight"; | ||||
| $migrate_dedup_users["personalex2"] = "personalex"; | ||||
| $migrate_dedup_users["polypa"] = "lile"; | ||||
| $migrate_dedup_users["i4dnf"] = "emilian"; | ||||
| $migrate_dedup_users["nitalleb"] = "singleman"; | ||||
| $migrate_dedup_users["tcgodoy"] = "godoy"; | ||||
| $migrate_dedup_users["joorva_pl"] = "joorva"; | ||||
| $migrate_dedup_users["sex"] = "xest"; | ||||
| $migrate_dedup_users["chipmunk"] = "ignatz"; | ||||
| $migrate_dedup_users["schalmagne"] = "chalmagne"; | ||||
| $migrate_dedup_users["michalziulek"] = "eneida"; | ||||
| $migrate_dedup_users["jeeps"] = "jeepster"; | ||||
| $migrate_dedup_users["yain"] = "chaos"; | ||||
| $migrate_dedup_users["amadio"] = "marble"; | ||||
| $migrate_dedup_users["javiere"] = "javierchavez"; | ||||
| $migrate_dedup_users["drmzperx"] = "mzperx"; | ||||
| $migrate_dedup_users["sebastianreaser"] = "sebastian0"; | ||||
| $migrate_dedup_users["idolon"] = "spadija"; | ||||
| $migrate_dedup_users["alanhzhcn"] = "alanhzh"; | ||||
| $migrate_dedup_users["jeanc"] = "jean"; | ||||
| $migrate_dedup_users["transblue2"] = "transblue"; | ||||
| $migrate_dedup_users["cesarwilfredo"] = "cesar"; | ||||
| $migrate_dedup_users["nspyr"] = "spiderfire"; | ||||
| $migrate_dedup_users["supermegamoaaa"] = "moaaa"; | ||||
| $migrate_dedup_users["firetiger"] = "opensolution"; | ||||
| $migrate_dedup_users["skarg"] = "cados"; | ||||
| $migrate_dedup_users["realheadcrusher"] = "danigr"; | ||||
| $migrate_dedup_users["kpg"] = "nihylius"; | ||||
| $migrate_dedup_users["tft"] = "tft67"; | ||||
| $migrate_dedup_users["storabbarn"] = "morre"; | ||||
| $migrate_dedup_users["bacurau"] = "roger_roo"; | ||||
| $migrate_dedup_users["alisari"] = "parasoley"; | ||||
| $migrate_dedup_users["skiski"] = "superrom"; | ||||
| $migrate_dedup_users["halfninja"] = "nickh"; | ||||
| $migrate_dedup_users["drell_develop"] = "drellex"; | ||||
| $migrate_dedup_users["highlif3"] = "highlife"; | ||||
| $migrate_dedup_users["broggsim1"] = "broggsim"; | ||||
| $migrate_dedup_users["blendermanuci"] = "yunior88"; | ||||
| $migrate_dedup_users["jpt9"] = "jtuttle"; | ||||
| $migrate_dedup_users["farrer"] = "farpro"; | ||||
| $migrate_dedup_users["badcheez"] = "randall"; | ||||
| $migrate_dedup_users["leewj_"] = "lohns"; | ||||
| $migrate_dedup_users["martin107"] = "martinfrances"; | ||||
| $migrate_dedup_users["dedpan"] = "tberghuis"; | ||||
| $migrate_dedup_users["whiterabbit"] = "dreamscapearts"; | ||||
| $migrate_dedup_users["shuvro"] = "shuvro05"; | ||||
| $migrate_dedup_users["anichandru"] = "anishchandran"; | ||||
| $migrate_dedup_users["ncaralph"] = "ralphdoctorow"; | ||||
| $migrate_dedup_users["nlongchamps"] = "nlong"; | ||||
| $migrate_dedup_users["mawi37"] = "mawi"; | ||||
| $migrate_dedup_users["rizla"] = "jay"; | ||||
| $migrate_dedup_users["jeh"] = "mirkril"; | ||||
| $migrate_dedup_users["b-fighter"] = "fanatic"; | ||||
| $migrate_dedup_users["wasamonkey"] = "wasa"; | ||||
| $migrate_dedup_users["nicola"] = "martin45"; | ||||
| $migrate_dedup_users["method-es"] = "method"; | ||||
| $migrate_dedup_users["pablow"] = "warhole"; | ||||
| $migrate_dedup_users["alanishzh"] = "alanhzh"; | ||||
| $migrate_dedup_users["thunder947"] = "thunder"; | ||||
| $migrate_dedup_users["yijimi"] = "roler"; | ||||
| $migrate_dedup_users["jaydenb"] = "logidude"; | ||||
| $migrate_dedup_users["leandropolus"] = "leandrosz"; | ||||
| $migrate_dedup_users["ibi002"] = "ibi001"; | ||||
| $migrate_dedup_users["hakanortasoz"] = "tayfax"; | ||||
| $migrate_dedup_users["enur"] = "rune"; | ||||
| $migrate_dedup_users["dsuesse"] = "qiip"; | ||||
| $migrate_dedup_users["bipedlaboratory"] = "redmetal"; | ||||
| $migrate_dedup_users["codeyhanson"] = "codey"; | ||||
| $migrate_dedup_users["alphonso_b"] = "alfonso_b"; | ||||
| $migrate_dedup_users["surreal6"] = "carlospadial"; | ||||
| $migrate_dedup_users["kralizek"] = "kral"; | ||||
| $migrate_dedup_users["kfrechet"] = "keithfr"; | ||||
| $migrate_dedup_users["jwedlake"] = "joshwedlake"; | ||||
| $migrate_dedup_users["westie630"] = "bully"; | ||||
| $migrate_dedup_users["fictional"] = "icefire"; | ||||
| $migrate_dedup_users["zelozelos"] = "zelozelos1"; | ||||
| $migrate_dedup_users["dragonlord"] = "acuena"; | ||||
| $migrate_dedup_users["mrcheese"] = "jpeg"; | ||||
| $migrate_dedup_users["willemverwey"] = "dandandan"; | ||||
| $migrate_dedup_users["jhed"] = "anton_foy"; | ||||
| $migrate_dedup_users["treacy1077"] = "briant"; | ||||
| $migrate_dedup_users["xest"] = "xembie"; | ||||
| $migrate_dedup_users["cyphl25"] = "jmsfreezer"; | ||||
| $migrate_dedup_users["wynk"] = "wynn"; | ||||
| $migrate_dedup_users["trock2957"] = "trock"; | ||||
| $migrate_dedup_users["mr_bomb"] = "carter24"; | ||||
| $migrate_dedup_users["nikolaus"] = "tortellini"; | ||||
| $migrate_dedup_users["pegasus_001"] = "pegasus001"; | ||||
| $migrate_dedup_users["fayte"] = "fayte220"; | ||||
| $migrate_dedup_users["jagang_8"] = "jagang"; | ||||
| $migrate_dedup_users["thetwom"] = "moe"; | ||||
| $migrate_dedup_users["kevlareditor"] = "klthomas"; | ||||
| $migrate_dedup_users["damien_deom"] = "dams"; | ||||
| $migrate_dedup_users["reynantem"] = "reynante"; | ||||
| $migrate_dedup_users["hiralm01"] = "hiralm"; | ||||
| $migrate_dedup_users["radix7"] = "wyldethang"; | ||||
| $migrate_dedup_users["puppetm"] = "puppetmaster"; | ||||
| $migrate_dedup_users["vimax"] = "vimaxus"; | ||||
| $migrate_dedup_users["mozzy69"] = "lyndon"; | ||||
| $migrate_dedup_users["vitranaccad"] = "thestorm74"; | ||||
| $migrate_dedup_users["kj12345"] = "kevinjames"; | ||||
| $migrate_dedup_users["foinix"] = "mrnoodle"; | ||||
| $migrate_dedup_users["shizu"] = "sntulix"; | ||||
| $migrate_dedup_users["kukulcangod23"] = "kukulcangod"; | ||||
| $migrate_dedup_users["piliq"] = "qpblendpolis"; | ||||
| $migrate_dedup_users["benvad"] = "vbenny"; | ||||
| $migrate_dedup_users["fgribben"] = "sharkey"; | ||||
| $migrate_dedup_users["makospince"] = "mako_spince"; | ||||
| $migrate_dedup_users["ablenderuser"] = "pancakeface"; | ||||
| $migrate_dedup_users["axelphi"] = "axel"; | ||||
| $migrate_dedup_users["josiasbh"] = "josias"; | ||||
| $migrate_dedup_users["mattsix"] = "majawe"; | ||||
| $migrate_dedup_users["duarte_ramos"] = "dphantom"; | ||||
| $migrate_dedup_users["killogge"] = "muraj"; | ||||
| $migrate_dedup_users["luka"] = "omgwtfbbq"; | ||||
| $migrate_dedup_users["chromemonkey"] = "brian651msp"; | ||||
| $migrate_dedup_users["e_d_i"] = "ide"; | ||||
| $migrate_dedup_users["mrhaynesy"] = "haynesy"; | ||||
| $migrate_dedup_users["imagineering"] = "imagineer"; | ||||
| $migrate_dedup_users["virgiliovasconc"] = "virgilio"; | ||||
| $migrate_dedup_users["blenderlb57"] = "louigi"; | ||||
| $migrate_dedup_users["nick65"] = "dgnicola"; | ||||
| $migrate_dedup_users["mmatthews"] = "mamatthews"; | ||||
| $migrate_dedup_users["gerwood"] = "arilian"; | ||||
| $migrate_dedup_users["xsidmax"] = "xsi"; | ||||
| $migrate_dedup_users["mix-yag"] = "aynahsim"; | ||||
| $migrate_dedup_users["wpthomas"] = "tallguy"; | ||||
| $migrate_dedup_users["bp007"] = "bender007"; | ||||
| $migrate_dedup_users["emach"] = "eon4blender"; | ||||
| $migrate_dedup_users["alfclement"] = "alfc"; | ||||
| $migrate_dedup_users["blenderwell"] = "goplexian"; | ||||
| $migrate_dedup_users["asebastianr"] = "sebastian0"; | ||||
| $migrate_dedup_users["joepal1976"] = "joepal"; | ||||
| $migrate_dedup_users["chertov"] = "lestat"; | ||||
| $migrate_dedup_users["djela63"] = "jerominovich"; | ||||
| $migrate_dedup_users["lppinto"] = "lpciper"; | ||||
| $migrate_dedup_users["prana"] = "nomath"; | ||||
| $migrate_dedup_users["sarubadoru"] = "salvador"; | ||||
| $migrate_dedup_users["rarebit"] = "rawstar7"; | ||||
| $migrate_dedup_users["hikikamori"] = "hikkikamori"; | ||||
| $migrate_dedup_users["mrduke"] = "drop"; | ||||
| $migrate_dedup_users["deetee"] = "dertee"; | ||||
| $migrate_dedup_users["woooooah"] = "noxwell"; | ||||
| $migrate_dedup_users["macoss"] = "ossmac"; | ||||
| $migrate_dedup_users["bunnyboy212"] = "blender_buddie"; | ||||
| $migrate_dedup_users["maxgrip"] = "czarek"; | ||||
| $migrate_dedup_users["tentonman"] = "titandtat"; | ||||
| $migrate_dedup_users["jabozzo"] = "bozzo"; | ||||
| $migrate_dedup_users["reinways"] = "reinw"; | ||||
| $migrate_dedup_users["petergk"] = "nihylius"; | ||||
| $migrate_dedup_users["zphr3000"] = "zphr"; | ||||
| $migrate_dedup_users["ruivo"] = "andreh"; | ||||
| $migrate_dedup_users["kosta"] = "kgd"; | ||||
| $migrate_dedup_users["delter"] = "dertee"; | ||||
| $migrate_dedup_users["jmiller"] = "lethargic"; | ||||
| $migrate_dedup_users["dealga"] = "zeffii"; | ||||
| $migrate_dedup_users["bogey"] = "daveh"; | ||||
| $migrate_dedup_users["silencebe"] = "silence"; | ||||
| $migrate_dedup_users["temozarela"] = "gorn"; | ||||
| $migrate_dedup_users["tischite"] = "greenbutton"; | ||||
| $migrate_dedup_users["buisson"] = "dinodino"; | ||||
| $migrate_dedup_users["cfox"] = "colinfox"; | ||||
| $migrate_dedup_users["hunkadoodle"] = "hunkadoodledoo"; | ||||
| $migrate_dedup_users["jlwitthuhn"] = "rahu"; | ||||
| $migrate_dedup_users["hvfrancesco"] = "hva"; | ||||
| $migrate_dedup_users["hazim-jamal"] = "hazim1"; | ||||
| $migrate_dedup_users["aurosutru"] = "tlm"; | ||||
| $migrate_dedup_users["pierrea"] = "pier2"; | ||||
| $migrate_dedup_users["zoon"] = "zoonpolygonikon"; | ||||
| $migrate_dedup_users["gruntbatch"] = "carmichael"; | ||||
| $migrate_dedup_users["petru"] = "virusanti"; | ||||
| $migrate_dedup_users["mikeh74"] = "mikeh"; | ||||
| $migrate_dedup_users["sugoi"] = "juntunen"; | ||||
| $migrate_dedup_users["bartje"] = "bartart3d"; | ||||
| $migrate_dedup_users["yaoyansi"] = "yaoyansi2"; | ||||
| $migrate_dedup_users["rafek"] = "rafek_finearts"; | ||||
| $migrate_dedup_users["caspern"] = "caspernilsson"; | ||||
| $migrate_dedup_users["lee"] = "s_random"; | ||||
| $migrate_dedup_users["sparky"] = "mmikkelsen"; | ||||
| $migrate_dedup_users["wigglyframes"] = "rene"; | ||||
| $migrate_dedup_users["kroni"] = "kronos"; | ||||
| $migrate_dedup_users["xgl_asyliax"] = "xglasyliax"; | ||||
| $migrate_dedup_users["blendeador"] = "luisperosio"; | ||||
| $migrate_dedup_users["kaos1986"] = "kaos86"; | ||||
| $migrate_dedup_users["adamdoyle"] = "advs89"; | ||||
| $migrate_dedup_users["kahenraz"] = "mistrel"; | ||||
| $migrate_dedup_users["ccliffe"] = "cjcliffe"; | ||||
| $migrate_dedup_users["nullfied"] = "xercesblue"; | ||||
| $migrate_dedup_users["dustyghost"] = "dustbin1_uk"; | ||||
| $migrate_dedup_users["claaskuhnen"] = "cekuhnendev"; | ||||
| $migrate_dedup_users["vercingetorix"] = "diamond_rust"; | ||||
| $migrate_dedup_users["plugboy"] = "centerlaw"; | ||||
| $migrate_dedup_users["rben13"] = "raybenjamin"; | ||||
| $migrate_dedup_users["deangiberson"] = "voidptr"; | ||||
| $migrate_dedup_users["makeitwork"] = "bboybram"; | ||||
| $migrate_dedup_users["iljosli"] = "jos"; | ||||
| $migrate_dedup_users["slowan"] = "slovan"; | ||||
| $migrate_dedup_users["mooonwalkercz"] = "mooonwalker"; | ||||
| $migrate_dedup_users["tapplek"] = "tapple"; | ||||
| $migrate_dedup_users["blendmond"] = "cornix"; | ||||
| $migrate_dedup_users["giorgiomartini"] = "tweakingknobs"; | ||||
| $migrate_dedup_users["davidray"] = "deadman"; | ||||
| $migrate_dedup_users["justthisguy"] = "nyctef"; | ||||
| $migrate_dedup_users["logobar"] = "freyer"; | ||||
| $migrate_dedup_users["zooo"] = "leon_cheung"; | ||||
| $migrate_dedup_users["josiasalexandre"] = "josias"; | ||||
| $migrate_dedup_users["tsukiko_chan"] = "tsukikochan"; | ||||
| $migrate_dedup_users["akira_san"] = "akira_b"; | ||||
| $migrate_dedup_users["walberti"] = "walbertievarist"; | ||||
| $migrate_dedup_users["astro"] = "tnboma"; | ||||
| $migrate_dedup_users["mccx"] = "mcq"; | ||||
| $migrate_dedup_users["daa84"] = "daa"; | ||||
| $migrate_dedup_users["sivert3"] = "cent"; | ||||
| $migrate_dedup_users["twentyone"] = "glade"; | ||||
| $migrate_dedup_users["endi2"] = "endi"; | ||||
| $migrate_dedup_users["jamesr"] = "james_r"; | ||||
| $migrate_dedup_users["reece"] = "reecerobinson"; | ||||
| $migrate_dedup_users["dustractor"] = "bpygrams"; | ||||
| $migrate_dedup_users["pencilhead"] = "pencil-head"; | ||||
| $migrate_dedup_users["toml"] = "tomol"; | ||||
| $migrate_dedup_users["colinm"] = "tablaman"; | ||||
| $migrate_dedup_users["blendphys2"] = "blendphys"; | ||||
| $migrate_dedup_users["xgoff"] = "zzyxz"; | ||||
| $migrate_dedup_users["coleingraham"] = "coledingraham"; | ||||
| $migrate_dedup_users["danielvmacedo"] = "skul3r"; | ||||
| $migrate_dedup_users["burster"] = "przemaz"; | ||||
| $migrate_dedup_users["tung"] = "tungster"; | ||||
| $migrate_dedup_users["chessie"] = "blackcatt"; | ||||
| $migrate_dedup_users["foxdog"] = "rubbernuke"; | ||||
| $migrate_dedup_users["mordachai"] = "gus"; | ||||
| $migrate_dedup_users["chilamlai"] = "cllai"; | ||||
| $migrate_dedup_users["sliders34"] = "sliders"; | ||||
| $migrate_dedup_users["benji852"] = "benjamin852"; | ||||
| $migrate_dedup_users["bebinalpha"] = "bebin"; | ||||
| $migrate_dedup_users["mr78"] = "alexandr"; | ||||
| $migrate_dedup_users["avirillion"] = "tarion"; | ||||
| $migrate_dedup_users["matthiasro"] = "matthias_r"; | ||||
| $migrate_dedup_users["debearseax"] = "seogeniuss"; | ||||
| $migrate_dedup_users["karja"] = "trockenfrosch"; | ||||
| $migrate_dedup_users["rojuinex"] = "ifrit"; | ||||
| $migrate_dedup_users["bernardo"] = "dados"; | ||||
| $migrate_dedup_users["ddeclara"] = "decden"; | ||||
| $migrate_dedup_users["zm_sansan"] = "sansan"; | ||||
| $migrate_dedup_users["useless"] = "cortot"; | ||||
| $migrate_dedup_users["tymnclono"] = "sooccatly"; | ||||
| $migrate_dedup_users["rodrigo_b"] = "rodrigob"; | ||||
| $migrate_dedup_users["shnurui"] = "spinster"; | ||||
| $migrate_dedup_users["michalisz"] = "michaliszissiou"; | ||||
| $migrate_dedup_users["fbbdev"] = "babboide"; | ||||
| $migrate_dedup_users["tjackson"] = "tjonline"; | ||||
| $migrate_dedup_users["ramaswamy"] = "ramaswamysriram"; | ||||
| $migrate_dedup_users["allrod5"] = "rodblender"; | ||||
| $migrate_dedup_users["qcp"] = "qpblendpolis"; | ||||
| $migrate_dedup_users["ftsf"] = "thesleepless"; | ||||
| $migrate_dedup_users["umagoo2012"] = "umagoo"; | ||||
| $migrate_dedup_users["raven"] = "rune"; | ||||
| $migrate_dedup_users["hsaito"] = "integer"; | ||||
| $migrate_dedup_users["paulthegreat"] = "digitalpyro"; | ||||
| $migrate_dedup_users["capheen"] = "dval"; | ||||
| $migrate_dedup_users["rskinner"] = "rws"; | ||||
| $migrate_dedup_users["gregstein"] = "gregorein"; | ||||
| $migrate_dedup_users["matty686"] = "matty"; | ||||
| $migrate_dedup_users["selby_rowley"] = "selby"; | ||||
| $migrate_dedup_users["shembolstudio"] = "natadams8"; | ||||
| $migrate_dedup_users["grenzfrequence"] = "goatrance"; | ||||
| $migrate_dedup_users["stephan"] = "schdeffan"; | ||||
| $migrate_dedup_users["axis33"] = "dsc512"; | ||||
| $migrate_dedup_users["redandfish"] = "red-fish"; | ||||
| $migrate_dedup_users["artsapcemedia"] = "arzpace"; | ||||
| $migrate_dedup_users["artspacemedia"] = "arzpace"; | ||||
| $migrate_dedup_users["mccmcc"] = "mcq"; | ||||
| $migrate_dedup_users["seocitterx"] = "mediabuy"; | ||||
| $migrate_dedup_users["lightning_limn"] = "lightning4"; | ||||
| $migrate_dedup_users["omarlakhdar"] = "archimage"; | ||||
| $migrate_dedup_users["regeleionescu"] = "regele"; | ||||
| $migrate_dedup_users["mitchell_decker"] = "michealikruhara"; | ||||
| $migrate_dedup_users["joselebon"] = "jl57"; | ||||
| $migrate_dedup_users["simonbroggi"] = "broggsim"; | ||||
| $migrate_dedup_users["inwadnepe"] = "ceapbatatry"; | ||||
| $migrate_dedup_users["ehobjman"] = "resbsp"; | ||||
| $migrate_dedup_users["davelassanske"] = "dolby411"; | ||||
| $migrate_dedup_users["jsu"] = "jansub"; | ||||
| $migrate_dedup_users["agricola"] = "agricola1"; | ||||
| $migrate_dedup_users["bartoszek"] = "bartus"; | ||||
| $migrate_dedup_users["captainoblivion"] = "cptoblivion"; | ||||
| $migrate_dedup_users["alexmcourt"] = "personalex"; | ||||
| $migrate_dedup_users["jmsprss"] = "xonar"; | ||||
| $migrate_dedup_users["awarnock"] = "salsa"; | ||||
| $migrate_dedup_users["mcc2"] = "mcq"; | ||||
| $migrate_dedup_users["psyborg042"] = "psyborg"; | ||||
| $migrate_dedup_users["ushiproject"] = "ushi"; | ||||
| $migrate_dedup_users["mrjimmy"] = "mrjimmyos"; | ||||
| $migrate_dedup_users["thefinalcut"] = "tlousky"; | ||||
| $migrate_dedup_users["startheshadow"] = "star"; | ||||
| $migrate_dedup_users["axredneck"] = "redneck"; | ||||
| $migrate_dedup_users["phimestudio"] = "phime"; | ||||
| $migrate_dedup_users["dwatts1"] = "dlax"; | ||||
| $migrate_dedup_users["rertjoi"] = "rertjwi"; | ||||
| $migrate_dedup_users["erdjkgh"] = "rertjwi"; | ||||
| $migrate_dedup_users["libertainsrg"] = "fcougar"; | ||||
| $migrate_dedup_users["godling72"] = "dmelchio"; | ||||
| $migrate_dedup_users["myclay"] = "thenewone"; | ||||
| $migrate_dedup_users["ecaspersen"] = "ecasper"; | ||||
| $migrate_dedup_users["driewiel"] = "driesiedriewiel"; | ||||
| $migrate_dedup_users["bhupen"] = "bhupendra"; | ||||
| $migrate_dedup_users["caosdoar"] = "mailoyo"; | ||||
| $migrate_dedup_users["polyspin"] = "butler"; | ||||
| $migrate_dedup_users["qalb_al_aqrab"] = "efimpetelin"; | ||||
| $migrate_dedup_users["fdfdfdfffd"] = "fcougar"; | ||||
| $migrate_dedup_users["brianlockett"] = "macrow"; | ||||
| $migrate_dedup_users["claude"] = "coco"; | ||||
| $migrate_dedup_users["mattostgard"] = "drflail"; | ||||
| $migrate_dedup_users["cekuhnen_new"] = "cekuhnendev"; | ||||
| $migrate_dedup_users["kirill_lukyanov"] = "kirill"; | ||||
| $migrate_dedup_users["jan-eric"] = "janeric96"; | ||||
| $migrate_dedup_users["daniel_h"] = "dhoughto"; | ||||
| $migrate_dedup_users["raphaelbarros"] = "thebigheadone"; | ||||
| $migrate_dedup_users["salas"] = "tychota"; | ||||
| $migrate_dedup_users["danieljsamson"] = "techfix"; | ||||
| $migrate_dedup_users["vinagrito"] = "aechemendia"; | ||||
| $migrate_dedup_users["lin_165"] = "b1657022405"; | ||||
| $migrate_dedup_users["cwebber"] = "paroneayea"; | ||||
| $migrate_dedup_users["harolddadomo"] = "harold"; | ||||
| $migrate_dedup_users["rabidsquirrel"] = "genericusername"; | ||||
| $migrate_dedup_users["larry3"] = "lehibou"; | ||||
| $migrate_dedup_users["predoe"] = "petronius3d"; | ||||
| $migrate_dedup_users["skoo"] = "stefano"; | ||||
| $migrate_dedup_users["cabergolinety"] = "azathioprinewww"; | ||||
| $migrate_dedup_users["prestijkorsan07"] = "prestij07"; | ||||
| $migrate_dedup_users["scottpetrovic"] = "slpetrov"; | ||||
| $migrate_dedup_users["zooly76"] = "zooly"; | ||||
| $migrate_dedup_users["theoryanimation"] = "davidandrade"; | ||||
| $migrate_dedup_users["daninsky"] = "danishit"; | ||||
| $migrate_dedup_users["eyesee2013"] = "eyesee"; | ||||
| $migrate_dedup_users["megacal"] = "cmcgaugh"; | ||||
| $migrate_dedup_users["const"] = "kostas100"; | ||||
| $migrate_dedup_users["ngaudenzi"] = "puppetmaster"; | ||||
| $migrate_dedup_users["mroguski"] = "kaelthas"; | ||||
| $migrate_dedup_users["brdf"] = "origin"; | ||||
| $migrate_dedup_users["davis3d"] = "davis"; | ||||
| $migrate_dedup_users["rldigital"] = "locatelli"; | ||||
| $migrate_dedup_users["tomforsythe"] = "gallifrey77203"; | ||||
| $migrate_dedup_users["gbrnk"] = "benoe"; | ||||
| $migrate_dedup_users["arekkasprzyk"] = "kasperski"; | ||||
| $migrate_dedup_users["imbusy1"] = "imbusy"; | ||||
| $migrate_dedup_users["mfoxdoggg"] = "mfoxdogg"; | ||||
|  | ||||
| $migrate_dedup_users["knusk"] = "kanutus"; | ||||
| $migrate_dedup_users["tomekk"] = "anders211"; | ||||
| $migrate_dedup_users["kitsueb"] = "kitsu_eb"; | ||||
| $migrate_dedup_users["slugzzz"] = "tsquar3d"; | ||||
| $migrate_dedup_users["moore500"] = "mmoore500"; | ||||
| $migrate_dedup_users["verumbra"] = "sebastian0"; | ||||
| $migrate_dedup_users["blenderbug"] = "nikola"; | ||||
|  | ||||
| $migrate_dedup_users["adailtoncomp"] = "adailton"; | ||||
| $migrate_dedup_users["mchs3d"] = "abtrumpet"; | ||||
|  | ||||
| // disabled users who have tasks | ||||
| $migrate_dedup_users["sjoerd"] = "sjoerddevries"; | ||||
| $migrate_dedup_users["matali"] = "mat_ali"; | ||||
| $migrate_dedup_users["voicelesscushio"] = "None"; | ||||
| $migrate_dedup_users["bigben0328"] = "None"; | ||||
| $migrate_dedup_users["santamouse"] = "None"; | ||||
| $migrate_dedup_users["andreanckaert"] = "None"; | ||||
| $migrate_dedup_users["yesmydear"] = "None"; | ||||
| $migrate_dedup_users["spacetug"] = "None"; | ||||
| $migrate_dedup_users["omegafold"] = "None"; | ||||
|  | ||||
| // testing | ||||
| $migrate_dedup_users["blendix_rename_test_a"] = "blendix"; | ||||
| $migrate_dedup_users["blendix_rename_test_b"] = "blendix"; | ||||
|  | ||||
| Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.8 KiB | 
| Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.4 KiB | 
| @@ -9,8 +9,8 @@ return array( | ||||
|   'names' => array( | ||||
|     'conpherence.pkg.css' => '0e3cf785', | ||||
|     'conpherence.pkg.js' => '020aebcf', | ||||
|     'core.pkg.css' => 'ba768cdb', | ||||
|     'core.pkg.js' => '845355f4', | ||||
|     'core.pkg.css' => '0fbedea0', | ||||
|     'core.pkg.js' => '74ad315f', | ||||
|     'dark-console.pkg.js' => '187792c2', | ||||
|     'differential.pkg.css' => '5c459f92', | ||||
|     'differential.pkg.js' => '218fda21', | ||||
| @@ -38,9 +38,9 @@ return array( | ||||
|     'rsrc/css/aphront/typeahead.css' => '8779483d', | ||||
|     'rsrc/css/application/almanac/almanac.css' => '2e050f4f', | ||||
|     'rsrc/css/application/auth/auth.css' => 'c2f23d74', | ||||
|     'rsrc/css/application/base/main-menu-view.css' => 'bcec20f0', | ||||
|     'rsrc/css/application/base/main-menu-view.css' => 'eacf7e46', | ||||
|     'rsrc/css/application/base/notification-menu.css' => '4df1ee30', | ||||
|     'rsrc/css/application/base/phui-theme.css' => '35883b37', | ||||
|     'rsrc/css/application/base/phui-theme.css' => '63311e09', | ||||
|     'rsrc/css/application/base/standard-page-view.css' => 'a374f94c', | ||||
|     'rsrc/css/application/chatlog/chatlog.css' => 'abdc76ee', | ||||
|     'rsrc/css/application/conduit/conduit-api.css' => 'ce2cfc41', | ||||
| @@ -137,7 +137,7 @@ return array( | ||||
|     'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46', | ||||
|     'rsrc/css/phui/phui-action-list.css' => '1b0085b2', | ||||
|     'rsrc/css/phui/phui-action-panel.css' => '6c386cbf', | ||||
|     'rsrc/css/phui/phui-badge.css' => '666e25ad', | ||||
|     'rsrc/css/phui/phui-badge.css' => '96576409', | ||||
|     'rsrc/css/phui/phui-basic-nav-view.css' => '56ebd66d', | ||||
|     'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', | ||||
|     'rsrc/css/phui/phui-box.css' => '5ed3b8cb', | ||||
| @@ -154,7 +154,7 @@ return array( | ||||
|     'rsrc/css/phui/phui-document.css' => '52b748a5', | ||||
|     'rsrc/css/phui/phui-feed-story.css' => 'a0c05029', | ||||
|     'rsrc/css/phui/phui-fontkit.css' => '1ec937e5', | ||||
|     'rsrc/css/phui/phui-form-view.css' => '01b796c0', | ||||
|     'rsrc/css/phui/phui-form-view.css' => 'a8e0a1ab', | ||||
|     'rsrc/css/phui/phui-form.css' => '1f177cb7', | ||||
|     'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', | ||||
|     'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', | ||||
| @@ -187,6 +187,16 @@ return array( | ||||
|     'rsrc/css/sprite-login.css' => '18b368a6', | ||||
|     'rsrc/css/sprite-tokens.css' => 'f1896dc5', | ||||
|     'rsrc/css/syntax/syntax-default.css' => '055fc231', | ||||
|     'rsrc/custom/css/phabricator-welcome-page.css' => 'a641fcc9', | ||||
|     'rsrc/custom/image/badges/badge_devfund_bronze.png' => '0f98ccf2', | ||||
|     'rsrc/custom/image/badges/badge_devfund_diamond.png' => '196d8206', | ||||
|     'rsrc/custom/image/badges/badge_devfund_gold.png' => 'd03e1722', | ||||
|     'rsrc/custom/image/badges/badge_devfund_platinum.png' => '3e517840', | ||||
|     'rsrc/custom/image/badges/badge_devfund_silver.png' => '73594dee', | ||||
|     'rsrc/custom/image/badges/badge_devfund_titanium.png' => 'e30aa898', | ||||
|     'rsrc/custom/image/badges/badge_sprite_fright.png' => '6f4b20e6', | ||||
|     'rsrc/custom/image/badges/badge_studio.png' => 'ffbdcabb', | ||||
|     'rsrc/custom/image/blender_logo.png' => '86dc8498', | ||||
|     'rsrc/externals/d3/d3.min.js' => '9d068042', | ||||
|     'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698', | ||||
|     'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0', | ||||
| @@ -272,7 +282,7 @@ return array( | ||||
|     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '5a79f6c3', | ||||
|     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '8badee71', | ||||
|     'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '80bff3af', | ||||
|     'rsrc/favicons/favicon-16x16.png' => '4c51a03a', | ||||
|     'rsrc/favicons/favicon-16x16.png' => 'b1399751', | ||||
|     'rsrc/favicons/mask-icon.svg' => 'db699fe1', | ||||
|     'rsrc/image/BFCFDA.png' => '74b5c88b', | ||||
|     'rsrc/image/actions/edit.png' => 'fd987dff', | ||||
| @@ -490,7 +500,7 @@ return array( | ||||
|     'rsrc/js/core/behavior-more.js' => '506aa3f4', | ||||
|     'rsrc/js/core/behavior-object-selector.js' => '98ef467f', | ||||
|     'rsrc/js/core/behavior-oncopy.js' => 'da8f5259', | ||||
|     'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '54262396', | ||||
|     'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '99e30c81', | ||||
|     'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f', | ||||
|     'rsrc/js/core/behavior-redirect.js' => '407ee861', | ||||
|     'rsrc/js/core/behavior-refresh-csrf.js' => '46116c01', | ||||
| @@ -650,7 +660,7 @@ return array( | ||||
|     'javelin-behavior-phabricator-notification-example' => '29819b75', | ||||
|     'javelin-behavior-phabricator-object-selector' => '98ef467f', | ||||
|     'javelin-behavior-phabricator-oncopy' => 'da8f5259', | ||||
|     'javelin-behavior-phabricator-remarkup-assist' => '54262396', | ||||
|     'javelin-behavior-phabricator-remarkup-assist' => '99e30c81', | ||||
|     'javelin-behavior-phabricator-reveal-content' => 'b105a3a6', | ||||
|     'javelin-behavior-phabricator-search-typeahead' => '1cb7d027', | ||||
|     'javelin-behavior-phabricator-show-older-transactions' => '8b5c7d65', | ||||
| @@ -790,7 +800,7 @@ return array( | ||||
|     'phabricator-flag-css' => '2b77be8d', | ||||
|     'phabricator-keyboard-shortcut' => '1a844c06', | ||||
|     'phabricator-keyboard-shortcut-manager' => '81debc48', | ||||
|     'phabricator-main-menu-view' => 'bcec20f0', | ||||
|     'phabricator-main-menu-view' => 'eacf7e46', | ||||
|     'phabricator-nav-view-css' => '423f92cc', | ||||
|     'phabricator-notification' => 'a9b91e3f', | ||||
|     'phabricator-notification-css' => '30240bd2', | ||||
| @@ -808,6 +818,7 @@ return array( | ||||
|     'phabricator-title' => '43bc9360', | ||||
|     'phabricator-tooltip' => '83754533', | ||||
|     'phabricator-ui-example-css' => 'b4795059', | ||||
|     'phabricator-welcome-page' => 'a641fcc9', | ||||
|     'phabricator-zindex-css' => 'ac3bfcd4', | ||||
|     'phame-css' => 'bb442327', | ||||
|     'pholio-css' => '88ef5ef1', | ||||
| @@ -820,7 +831,7 @@ return array( | ||||
|     'phrequent-css' => 'bd79cc67', | ||||
|     'phriction-document-css' => '03380da0', | ||||
|     'phui-action-panel-css' => '6c386cbf', | ||||
|     'phui-badge-view-css' => '666e25ad', | ||||
|     'phui-badge-view-css' => '96576409', | ||||
|     'phui-basic-nav-view-css' => '56ebd66d', | ||||
|     'phui-big-info-view-css' => '362ad37b', | ||||
|     'phui-box-css' => '5ed3b8cb', | ||||
| @@ -846,7 +857,7 @@ return array( | ||||
|     'phui-font-icon-base-css' => '303c9b87', | ||||
|     'phui-fontkit-css' => '1ec937e5', | ||||
|     'phui-form-css' => '1f177cb7', | ||||
|     'phui-form-view-css' => '01b796c0', | ||||
|     'phui-form-view-css' => 'a8e0a1ab', | ||||
|     'phui-formation-view-css' => 'd2dec8ed', | ||||
|     'phui-head-thing-view-css' => 'd7f293df', | ||||
|     'phui-header-view-css' => '36c86a58', | ||||
| @@ -877,7 +888,7 @@ return array( | ||||
|     'phui-spacing-css' => 'b05cadc3', | ||||
|     'phui-status-list-view-css' => 'e5ff8be0', | ||||
|     'phui-tag-view-css' => '8519160a', | ||||
|     'phui-theme-css' => '35883b37', | ||||
|     'phui-theme-css' => '63311e09', | ||||
|     'phui-timeline-view-css' => '2d32d7a9', | ||||
|     'phui-two-column-view-css' => 'f96d319f', | ||||
|     'phui-workboard-color-css' => 'e86de308', | ||||
| @@ -1411,17 +1422,6 @@ return array( | ||||
|     '541f81c3' => array( | ||||
|       'javelin-install', | ||||
|     ), | ||||
|     54262396 => array( | ||||
|       'javelin-behavior', | ||||
|       'javelin-stratcom', | ||||
|       'javelin-dom', | ||||
|       'phabricator-phtize', | ||||
|       'phabricator-textareautils', | ||||
|       'javelin-workflow', | ||||
|       'javelin-vector', | ||||
|       'phuix-autocomplete', | ||||
|       'javelin-mask', | ||||
|     ), | ||||
|     '548567f6' => array( | ||||
|       'syntax-default-css', | ||||
|     ), | ||||
| @@ -1809,6 +1809,17 @@ return array( | ||||
|       'javelin-request', | ||||
|       'javelin-router', | ||||
|     ), | ||||
|     '99e30c81' => array( | ||||
|       'javelin-behavior', | ||||
|       'javelin-stratcom', | ||||
|       'javelin-dom', | ||||
|       'phabricator-phtize', | ||||
|       'phabricator-textareautils', | ||||
|       'javelin-workflow', | ||||
|       'javelin-vector', | ||||
|       'phuix-autocomplete', | ||||
|       'javelin-mask', | ||||
|     ), | ||||
|     '9aae2b66' => array( | ||||
|       'javelin-install', | ||||
|       'javelin-util', | ||||
| @@ -2034,9 +2045,6 @@ return array( | ||||
|       'phabricator-drag-and-drop-file-upload', | ||||
|       'javelin-workboard-board', | ||||
|     ), | ||||
|     'bcec20f0' => array( | ||||
|       'phui-theme-css', | ||||
|     ), | ||||
|     'c03f2fb4' => array( | ||||
|       'javelin-install', | ||||
|     ), | ||||
| @@ -2164,6 +2172,9 @@ return array( | ||||
|       'javelin-install', | ||||
|       'javelin-event', | ||||
|     ), | ||||
|     'eacf7e46' => array( | ||||
|       'phui-theme-css', | ||||
|     ), | ||||
|     'ebe83a6b' => array( | ||||
|       'javelin-install', | ||||
|     ), | ||||
|   | ||||
| @@ -7,6 +7,7 @@ $status_map = array( | ||||
|   3 => 'invalid', | ||||
|   4 => 'duplicate', | ||||
|   5 => 'spite', | ||||
|   123450 => 'archived', | ||||
| ); | ||||
|  | ||||
| $conn_w = id(new ManiphestTask())->establishConnection('w'); | ||||
|   | ||||
							
								
								
									
										172
									
								
								scripts/auth_provider/auth_provider.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,172 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
| # | ||||
| # ***** BEGIN GPL LICENSE BLOCK ***** | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or | ||||
| # modify it under the terms of the GNU General Public License | ||||
| # as published by the Free Software Foundation; either version 2 | ||||
| # of the License, or (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software Foundation, | ||||
| # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| # | ||||
| # The Original Code is Copyright (C) 2019, Blender Foundation | ||||
| # All rights reserved. | ||||
| # | ||||
| # Contributor(s): Sergey Sharybin. | ||||
| # | ||||
| # ***** END GPL LICENSE BLOCK ***** | ||||
| # | ||||
| # This scripts implements external AuthBasicProvider which can be used | ||||
| # to authentificate users using Phabricator's database. | ||||
| # | ||||
| # Example configuration: | ||||
| # | ||||
| # .htaccess file: | ||||
| # | ||||
| #   AuthType Basic | ||||
| #   AuthName "Please Enter Password" | ||||
| #   AuthBasicProvider external | ||||
| #   AuthExternal phabricator | ||||
| #   Require valid-user | ||||
| # | ||||
| # It is also required to have the following provider registered in the | ||||
| # configuration. There are two ways to do it: | ||||
| # | ||||
| #  1. Separate conf file in /etc/apache2/conf-enabled | ||||
| #     Create a file (say, authz_external_phabricator.conf) with the following | ||||
| #     content: | ||||
| # | ||||
| #       DefineExternalAuth phabricator pipe /path/to/auth_provider.php | ||||
| # | ||||
| #     This method allows to use provider in any VHOST. | ||||
| # | ||||
| #     Downside: from local tests .htaccess file picks the provider nicely, | ||||
| #     but attempts to specifu the provider in virtual host configuration | ||||
| #     resulted in issues (seems to be different initialization order between | ||||
| #     global config in those files and virtual hosts). | ||||
| # | ||||
| #  2. Define provider in the virtual host definition: | ||||
| # | ||||
| #       <IfModule mod_authnz_external.c> | ||||
| #         AddExternalAuth phabricator /path/to/auth_provider.php | ||||
| #         SetExternalAuthMethod phabricator pipe | ||||
| #       </IfModule> | ||||
| # | ||||
| #     This method allowed to use this auth provider for SVN DAV. | ||||
|  | ||||
| $IS_DEBUG = false; | ||||
| $PHABRICATOR_ROOT = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $PHABRICATOR_ROOT.'/scripts/__init_script__.php'; | ||||
|  | ||||
| function initLogging() { | ||||
|   global $IS_DEBUG; | ||||
|   global $argv; | ||||
|   for ($i = 1; $i < count($argv); ++$i) { | ||||
|     if ($argv[$i] == '--debug') { | ||||
|       $IS_DEBUG = true; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| function debugLog(string $text) { | ||||
|   global $IS_DEBUG; | ||||
|   if (!$IS_DEBUG) { | ||||
|     return; | ||||
|   } | ||||
|   print("${text}\n"); | ||||
| } | ||||
|  | ||||
| function removeSingleTrailingNewline(string $string) { | ||||
|   if ($string === '') { | ||||
|     return $string; | ||||
|   } | ||||
|   $length = strlen($string); | ||||
|   if ($string[$length - 1] == "\n") { | ||||
|     return substr($string, 0, -1); | ||||
|   } | ||||
|   return $string; | ||||
| } | ||||
|  | ||||
| class AuthRequest { | ||||
|   public $user_name; | ||||
|   public $password; | ||||
|  | ||||
|   static function fromStdin() { | ||||
|     $auth_request = new AuthRequest(); | ||||
|     $stdin = fopen('php://stdin', 'r'); | ||||
|     $auth_request->user_name = removeSingleTrailingNewline(fgets($stdin)); | ||||
|     $auth_request->password = removeSingleTrailingNewline(fgets($stdin)); | ||||
|     return $auth_request; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| function getUserForAuthRequest(AuthRequest $auth_request) { | ||||
|   if ($auth_request->user_name === '') { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   $username_or_email = $auth_request->user_name; | ||||
|  | ||||
|   $user = id(new PhabricatorUser())->loadOneWhere( | ||||
|     'username = %s', | ||||
|     $username_or_email); | ||||
|  | ||||
|   if (!$user) { | ||||
|     $user = PhabricatorUser::loadOneWithEmailAddress( | ||||
|     $username_or_email); | ||||
|   } | ||||
|  | ||||
|   return $user; | ||||
| } | ||||
|  | ||||
| function createContentSourceForAuth() { | ||||
|   return PhabricatorContentSource::newForSource( | ||||
|       PhabricatorUnitTestContentSource::SOURCECONST); | ||||
| } | ||||
|  | ||||
| function isValidAuth(AuthRequest $auth_request) { | ||||
|   debugLog("Begin authentification check for user '$auth_request->user_name'"); | ||||
|   $user = getUserForAuthRequest($auth_request); | ||||
|   if (!$user) { | ||||
|     debugLog("No PhabricatorUser() object found for requested user."); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   $content_source = createContentSourceForAuth(); | ||||
|   $envelope = new PhutilOpaqueEnvelope($auth_request->password); | ||||
|  | ||||
|   $engine = id(new PhabricatorAuthPasswordEngine()) | ||||
|       ->setViewer($user) | ||||
|       ->setContentSource($content_source) | ||||
|       ->setPasswordType(PhabricatorAuthPassword::PASSWORD_TYPE_ACCOUNT) | ||||
|       ->setObject($user); | ||||
|  | ||||
|   if (!$engine->isValidPassword($envelope)) { | ||||
|     debugLog('Engine reported invalid password.'); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   debugLog('Authentirfication passed.'); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| function main() { | ||||
|   initLogging(); | ||||
|   $auth_request = AuthRequest::fromStdin(); | ||||
|   if (!isValidAuth($auth_request)) { | ||||
|     exit(1); | ||||
|   } | ||||
|   exit(0); | ||||
| } | ||||
|  | ||||
| main(); | ||||
| ?> | ||||
							
								
								
									
										6
									
								
								scripts/gitadmin/gitx-ssh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| #!/usr/local/bin/bash | ||||
|  | ||||
| set -e | ||||
| set -u | ||||
|  | ||||
| ssh -i $SSH_KEYFILE $@ | ||||
							
								
								
									
										443
									
								
								scripts/gitadmin/rebuild_gitadmin.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,443 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
|  | ||||
| $root = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $root.'/scripts/__init_script__.php'; | ||||
|  | ||||
| function escape_name($name) { | ||||
|   return preg_replace('/[^A-Za-z0-9\-]/', '_', $name); | ||||
| } | ||||
|  | ||||
| function startswith($string, $prefix) { | ||||
|   return substr($string, 0, strlen($prefix)) == $prefix; | ||||
| } | ||||
|  | ||||
| function endswith($string, $suffix) { | ||||
|   $suffix_length = strlen($suffix); | ||||
|   return substr($string, strlen($string) - $suffix_length, | ||||
|                 $suffix_length) == $suffix; | ||||
| } | ||||
|  | ||||
| function write_ini_file($array, $file) { | ||||
|   $res = array(); | ||||
|   foreach ($array as $key => $val) { | ||||
|     if (is_array($val)) { | ||||
|       $res[] = "[$key]"; | ||||
|       foreach ($val as $skey => $sval) { | ||||
|         $res[] = "$skey = $sval"; | ||||
|       } | ||||
|       $res[] = ''; | ||||
|     } else { | ||||
|       $res[] = "$key = $val"; | ||||
|     } | ||||
|   } | ||||
|   file_put_contents($file, implode("\n", $res)); | ||||
| } | ||||
|  | ||||
| function getProjectMembersPHIDs($viewer, $project_phid) { | ||||
|   $project = id(new PhabricatorProjectQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->needMembers(true) | ||||
|     ->withPHIDs(array($project_phid)) | ||||
|     ->executeOne(); | ||||
|  | ||||
|   return $project->getMemberPHIDs(); | ||||
| } | ||||
|  | ||||
| // Get user's heys and put them to the configuration | ||||
| function handleSingleUserPHID( | ||||
|   $keydir, $viewer, $userPHID, $system_keys, &$used_keys) { | ||||
|   $user = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withPHIDs(array($userPHID)) | ||||
|     ->executeOne(); | ||||
|   if (!$user) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   if ($user->getIsDisabled()) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   $keys = id(new PhabricatorAuthSSHKey())->loadAllWhere( | ||||
|    'objectPHID = %s', | ||||
|     $user->getPHID()); | ||||
|  | ||||
|   $members = array(); | ||||
|   foreach ($keys as $key) { | ||||
|       $full_key_content = | ||||
|         $key->getKeyType().' '. | ||||
|         $key->getKeyBody().' '. | ||||
|         $key->getKeyComment()."\n"; | ||||
|  | ||||
|     if (array_key_exists($full_key_content, $system_keys)) { | ||||
|       $members[] = $system_keys[$full_key_content]; | ||||
|     } else { | ||||
|       $escaped_key_name = escape_name($key->getName()); | ||||
|       $member = 'PHAB_'.$user->getUserName(). | ||||
|         '_'.$escaped_key_name. | ||||
|         '_'.$key->getID(); | ||||
|       $members[] = $member; | ||||
|       if (!array_key_exists($member, $used_keys)) { | ||||
|         $used_keys[$member] = true; | ||||
|         file_put_contents("$keydir/$member.pub", $full_key_content); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleUsersPolicyRule( | ||||
|   $keydir, $viewer, $rule, $system_keys, &$used_keys) { | ||||
|   $members = array(); | ||||
|   foreach ($rule['value'] as $userPHID) { | ||||
|     $members = array_merge($members, | ||||
|       handleSingleUserPHID($keydir, $viewer, $userPHID, | ||||
|                            $system_keys, $used_keys)); | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleProjectsPolicyRule( | ||||
|   $keydir, $viewer, $rule, $system_keys, &$used_keys) { | ||||
|   $members = array(); | ||||
|   foreach ($rule['value'] as $projectPHID) { | ||||
|     $memberPHIDs = getProjectMembersPHIDs($viewer, $projectPHID); | ||||
|     foreach ($memberPHIDs as $userPHID) { | ||||
|       $members = array_merge($members, | ||||
|         handleSingleUserPHID($keydir, $viewer, $userPHID, | ||||
|                              $system_keys, $used_keys)); | ||||
|     } | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleProjectsAllPolicyRule( | ||||
|   $keydir, $viewer, $rule, $system_keys, &$used_keys) { | ||||
|   $is_first_project = true; | ||||
|   $allowed_members_phids = array(); | ||||
|   foreach ($rule['value'] as $project_phid) { | ||||
|     $memberPHIDs = getProjectMembersPHIDs($viewer, $project_phid); | ||||
|     if ($is_first_project) { | ||||
|       $allowed_members_phids = $memberPHIDs; | ||||
|       $is_first_project = false; | ||||
|     } else { | ||||
|       $allowed_members_phids = array_intersect( | ||||
|         $allowed_members_phids, $memberPHIDs); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   $members = array(); | ||||
|   foreach ($allowed_members_phids as $userPHID) { | ||||
|     $members = array_merge($members, | ||||
|       handleSingleUserPHID($keydir, $viewer, $userPHID, | ||||
|                            $system_keys, $used_keys)); | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleAdministratorsPolicyRule( | ||||
|   $keydir, $viewer, $rule, $system_keys, &$used_keys) { | ||||
|   $administrators = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withIsAdmin(true) | ||||
|     ->execute(); | ||||
|  | ||||
|   $members = array(); | ||||
|   foreach ($administrators as $administrator) { | ||||
|     $members = array_merge($members, | ||||
|       handleSingleUserPHID($keydir, $viewer, $administrator->getPHID(), | ||||
|                            $system_keys, $used_keys)); | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleLegalpadSingleDocument( | ||||
|   $keydir, $viewer, $document, $system_keys, &$used_keys) { | ||||
|   if ($document->getSignatureType() != | ||||
|       LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   $members = array(); | ||||
|   foreach ($document->getSignatures() as $signature) { | ||||
|     if ($signature->getSignatureType() != | ||||
|         LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL) { | ||||
|       continue; | ||||
|     } | ||||
|     $members = array_merge($members, | ||||
|       handleSingleUserPHID($keydir, $viewer, $signature->getSignerPHID(), | ||||
|                            $system_keys, $used_keys)); | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleLegalpadSignaturePolicyRule( | ||||
|   $keydir, $viewer, $rule, $system_keys, &$used_keys) { | ||||
|   $documents = id(new LegalpadDocumentQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withPHIDs($rule['value']) | ||||
|     ->needSignatures(true) | ||||
|     ->execute(); | ||||
|  | ||||
|   $members = array(); | ||||
|   foreach ($documents as $document) { | ||||
|     $members = array_merge( | ||||
|       $members, | ||||
|       handleLegalpadSingleDocument( | ||||
|         $keydir, $viewer, $document, $system_keys, $used_keys)); | ||||
|   } | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| function handleCustomPolicy( | ||||
|   $keydir, $viewer, $policy, $system_keys, &$used_keys) { | ||||
|   $members = array(); | ||||
|   $rules = $policy->getRules(); | ||||
|   foreach ($rules as $rule) { | ||||
|     // Everyone is denied by default anyway | ||||
|     if ($rule['action'] != 'allow') { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     $policy_members = array(); | ||||
|     $rule_type = $rule['rule']; | ||||
|     if ($rule_type == 'PhabricatorPolicyRuleUsers') { | ||||
|       $policy_members = handleUsersPolicyRule( | ||||
|         $keydir, $viewer, $rule, $system_keys, $used_keys); | ||||
|     } else if ($rule_type == 'PhabricatorProjectsPolicyRule') { | ||||
|       $policy_members = handleProjectsPolicyRule( | ||||
|         $keydir, $viewer, $rule, $system_keys, $used_keys); | ||||
|     } else if ($rule_type == 'PhabricatorProjectsAllPolicyRule') { | ||||
|       $policy_members = handleProjectsAllPolicyRule( | ||||
|         $keydir, $viewer, $rule, $system_keys, $used_keys); | ||||
|     } else if ($rule_type == 'PhabricatorAdministratorsPolicyRule') { | ||||
|       $policy_members = handleAdministratorsPolicyRule( | ||||
|         $keydir, $viewer, $rule, $system_keys, $used_keys); | ||||
|     } else if ($rule_type == 'PhabricatorLegalpadSignaturePolicyRule') { | ||||
|       $policy_members = handleLegalpadSignaturePolicyRule( | ||||
|         $keydir, $viewer, $rule, $system_keys, $used_keys); | ||||
|     } | ||||
|  | ||||
|     $members = array_merge($members, $policy_members); | ||||
|   } | ||||
|  | ||||
|   return $members; | ||||
| } | ||||
|  | ||||
| // Parse repository and put it's members to the config file | ||||
| function handleSingleRepository( | ||||
|   $keydir, $viewer, $repository, $all_repositories, $system_keys, | ||||
|   &$new_configuration, &$used_keys) { | ||||
|   $policies = PhabricatorPolicyQuery::loadPolicies( | ||||
|     $viewer, | ||||
|     $repository); | ||||
|  | ||||
|   $pushable = $policies[DiffusionPushCapability::CAPABILITY]; | ||||
|   $type = $pushable->getType(); | ||||
|  | ||||
|   $members = array(); | ||||
|  | ||||
|   if ($type == PhabricatorPolicyType::TYPE_PROJECT) { | ||||
|     $project = id(new PhabricatorProjectQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->needMembers(true) | ||||
|       ->withPHIDs(array($pushable->getPHID())) | ||||
|       ->executeOne(); | ||||
|  | ||||
|     $memberPHIDs = $project->getMemberPHIDs(); | ||||
|     foreach ($memberPHIDs as $memberPHID) { | ||||
|       $members = array_merge($members, | ||||
|         handleSingleUserPHID($keydir, $viewer, $memberPHID, | ||||
|                              $system_keys, $used_keys)); | ||||
|     } | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_USER) { | ||||
|     $members = handleSingleUserPHID( | ||||
|       $keydir, $viewer, $pushable->getPHID(), $system_keys, $used_keys); | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_CUSTOM) { | ||||
|     $members = handleCustomPolicy( | ||||
|       $keydir, $viewer, $pushable, $system_keys, $used_keys); | ||||
|   } else { | ||||
|     /* pass */ | ||||
|   } | ||||
|  | ||||
|   if (count($members)) { | ||||
|     $uri = $repository->getRemoteURI(); | ||||
|     $repository_name = basename($uri, '.git'); | ||||
|     $escaped_repository_name = escape_name($repository->getName()); | ||||
|     $group_name = "PHAB_${escaped_repository_name}"; | ||||
|     $values = array(); | ||||
|     $values['members'] = join(' ', $members); | ||||
|     $values['readonly'] = join(' ', $all_repositories); | ||||
|     $values['writable'] = $repository_name; | ||||
|     $new_configuration["group $group_name"] = $values; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Remove groups from previous automated configuration built | ||||
| function getCleanOldConfiguration($old_configuration) { | ||||
|   $new_configuration = array(); | ||||
|   foreach ($old_configuration as $group => $values) { | ||||
|     if (!startswith($group, 'group PHAB')) { | ||||
|       $new_configuration[$group] = $values; | ||||
|     } | ||||
|   } | ||||
|   return $new_configuration; | ||||
| } | ||||
|  | ||||
| // Get non-phab keys | ||||
| function getSystemPublicKeys($keydir) { | ||||
|   $files = scandir($keydir); | ||||
|   $system_keys = array(); | ||||
|   foreach ($files as $file) { | ||||
|     if (!startswith($file, "PHAB") && endswith($file, '.pub')) { | ||||
|       $key = file_get_contents("$keydir/$file"); | ||||
|       $system_keys[$key] = basename($file, '.pub'); | ||||
|     } | ||||
|   } | ||||
|   return $system_keys; | ||||
| } | ||||
|  | ||||
| // Remove unused public keys | ||||
| function removeUnusedPublicKeys($keydir, $used_keys) { | ||||
|   $files = scandir($keydir); | ||||
|   foreach ($files as $file) { | ||||
|     if (startswith($file, "PHAB")) { | ||||
|       $member = basename($file, '.pub'); | ||||
|       if (!array_key_exists($member, $used_keys)) { | ||||
|         unlink("$keydir/$file"); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| function rebuildConfiguration($gitosis_root) { | ||||
|   $keydir = "$gitosis_root/keydir"; | ||||
|   $configuration_file = "$gitosis_root/gitosis.conf"; | ||||
|  | ||||
|   if (!file_exists($configuration_file)) { | ||||
|     print("Not found: $configuration_file\n"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   $viewer = id(new PhabricatorUser()) | ||||
|     ->loadOneWhere('username = %s', 'sergey'); | ||||
|  | ||||
|   $old_configuration = parse_ini_file( | ||||
|     $configuration_file, true, INI_SCANNER_RAW); | ||||
|  | ||||
|   $new_configuration = getCleanOldConfiguration( | ||||
|     $old_configuration); | ||||
|  | ||||
|   // Get "system" keys to re-use if phab account uses the | ||||
|   // same public key | ||||
|   $system_keys = getSystemPublicKeys($keydir); | ||||
|  | ||||
|   // Get list of all repos which is awailable for read | ||||
|   $all_repositories = array(); | ||||
|   foreach ($old_configuration as $group => $values) { | ||||
|     if (startswith($group, 'repo')) { | ||||
|       $repository_name = substr($group, 5, strlen($group) - 5); | ||||
|       if ($repository_name == 'gitosis-admin') | ||||
|         continue; | ||||
|       $all_repositories[] = $repository_name; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Fill in new configuration and keys | ||||
|   $used_keys = array(); | ||||
|   $repositories = id(new PhabricatorRepositoryQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->execute(); | ||||
|  | ||||
|   foreach ($repositories as $repository_id => $repository) { | ||||
|     $type = $repository->getVersionControlSystem(); | ||||
|     if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { | ||||
|       handleSingleRepository( | ||||
|         $keydir, $viewer, $repository, $all_repositories, $system_keys, | ||||
|         $new_configuration, $used_keys); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   write_ini_file($new_configuration, $configuration_file); | ||||
|   removeUnusedPublicKeys($keydir, $used_keys); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| function getGitCommand($repository) { | ||||
|   $git_dir = realpath("$repository/.git"); | ||||
|   $git = "git --git-dir='$git_dir'"; | ||||
|   $git .= ' --work-tree='.realpath($repository); | ||||
|   return $git; | ||||
| } | ||||
|  | ||||
| function runGitCommand($repository, $arguments, | ||||
|                        &$output=null, &$return_var=null) { | ||||
|   $git = getGitCommand($repository); | ||||
|   $git .= " $arguments"; | ||||
|   exec($git, $output, $return_var); | ||||
|   return $return_var == 0; | ||||
| } | ||||
|  | ||||
| function runGitSshCommand($repository, $key, $arguments, | ||||
|                        &$output=null, &$return_var=null) { | ||||
|   $gitx_ssh = realpath(dirname(__FILE__) . "/gitx-ssh"); | ||||
|   $abs_key = realpath($key); | ||||
|   $git = "SSH_KEYFILE=$abs_key GIT_SSH=$gitx_ssh "; | ||||
|   $git .= getGitCommand($repository); | ||||
|   $git .= " $arguments"; | ||||
|   exec($git, $output, $return_var); | ||||
|   return $return_var == 0; | ||||
| } | ||||
|  | ||||
| function repositoryPull($repository, $key) { | ||||
|   return runGitSshCommand($repository, $key, 'pull'); | ||||
| } | ||||
|  | ||||
| function repositoryCommitAll($repository, $author, $message) { | ||||
|   if (!runGitCommand( | ||||
|     $repository, 'ls-files --other --exclude-standard', $untracked_files)) { | ||||
|     return false; | ||||
|   } | ||||
|   if (count($untracked_files)) { | ||||
|     $flat_files = join(' ', $untracked_files); | ||||
|     if (!runGitCommand($repository, "add $flat_files")) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   runGitCommand($repository, "update-index -q --refresh", $output); | ||||
|   runGitCommand($repository, "diff-index --name-only HEAD --", $output); | ||||
|   if (count($output)) { | ||||
|     return runGitCommand( | ||||
|       $repository, "commit --author='$author' -a -m '$message'", $output); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| if (count($argv) != 3) { | ||||
|   print("Usage: {$argv[0]} /path/to/gitosis-admin /path/to/id_rsa.pub\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| $gitosis_root = $argv[1]; | ||||
| $key = $argv[2]; | ||||
|  | ||||
| if (!repositoryPull($gitosis_root, $key)) { | ||||
|   print("Failed to pull changes from server.\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| if (!rebuildConfiguration($gitosis_root)) { | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| if (!repositoryCommitAll( | ||||
|   $gitosis_root, 'Rebuild Gitadmin <null@git.blender.org>', | ||||
|   'Update to correspond changes in Phabricator')) { | ||||
|   print("Failed to commit changes.\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| runGitSshCommand($gitosis_root, $key, 'push origin master'); | ||||
| ?> | ||||
							
								
								
									
										504
									
								
								scripts/gitolite/rebuild_gitolite.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,504 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
|  | ||||
| $root = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $root.'/scripts/__init_script__.php'; | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // Utilities. | ||||
| // | ||||
| // TODO(sergey): Move somewhere else. Or, evenmore ideally, use Phabricator's | ||||
| // utilities instead. | ||||
|  | ||||
| function escape_name($name) { | ||||
|   return preg_replace('/[^A-Za-z0-9]/', '_', $name); | ||||
| } | ||||
|  | ||||
| function startswith($string, $prefix) { | ||||
|   return substr($string, 0, strlen($prefix)) == $prefix; | ||||
| } | ||||
|  | ||||
| function endswith($string, $suffix) { | ||||
|   $suffix_length = strlen($suffix); | ||||
|   return substr($string, strlen($string) - $suffix_length, | ||||
|                 $suffix_length) == $suffix; | ||||
| } | ||||
|  | ||||
| function file_put_contents_if_different($file_name, $content) { | ||||
|   if (file_exists($file_name)) { | ||||
|     $current_content = file_get_contents($file_name); | ||||
|     if ($current_content == $content) { | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   file_put_contents($file_name, $content); | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // Phabricator access list traversal. | ||||
|  | ||||
| class Configuration { | ||||
|   // Phabricator user which is used as a viewer. | ||||
|   public $viewer; | ||||
|  | ||||
|   // Directory where public keys are stored. | ||||
|   // Full path. | ||||
|   protected $keys_directory; | ||||
|  | ||||
|   // Gitolite configuration file (gitolite.conf). | ||||
|   // Full path. | ||||
|   protected $config_file; | ||||
|  | ||||
|   // Indexed by key content, contains configuration user name. | ||||
|   protected $stored_keys; | ||||
|  | ||||
|   // Indexed by config user name. | ||||
|   protected $used_keys; | ||||
|  | ||||
|   // Indexed by committers variable name, contains list of users which are | ||||
|   // configured by Phabricator to be able to commit to the repository. | ||||
|   protected $committers; | ||||
|  | ||||
|   public function __construct($gitolite_root) { | ||||
|     $this->viewer = PhabricatorUser::getOmnipotentUser(); | ||||
|     $this->keys_directory = "$gitolite_root/keydir"; | ||||
|     $this->config_file = "$gitolite_root/conf/gitolite.conf"; | ||||
|  | ||||
|     $this->collectSystemPublicKeys(); | ||||
|  | ||||
|     $this->used_keys = array(); | ||||
|     $this->committers = array(); | ||||
|  | ||||
|     if (!file_exists($this->config_file)) { | ||||
|       die("Not found: $this->config_file\n"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Store given key of given user. | ||||
|   // | ||||
|   // Includes both storing public key in the file, and storing mapping between | ||||
|   // user and the key. | ||||
|   public function storeUserPublicKey($user, $key) { | ||||
|     $full_key_content = $this->getPublicKeyContent($key); | ||||
|  | ||||
|     if (array_key_exists($full_key_content, $this->stored_keys)) { | ||||
|       return $this->stored_keys[$full_key_content]; | ||||
|     } | ||||
|  | ||||
|     $config_user_name = $this->getConfigUserName($user, $key); | ||||
|  | ||||
|     if (!array_key_exists($config_user_name, $this->used_keys)) { | ||||
|       $this->used_keys[$config_user_name] = true; | ||||
|       file_put_contents_if_different("$this->keys_directory/$config_user_name.pub", | ||||
|                                      $full_key_content); | ||||
|     } | ||||
|  | ||||
|     $this->stored_keys[$full_key_content] = $config_user_name; | ||||
|  | ||||
|     return $config_user_name; | ||||
|   } | ||||
|  | ||||
|   public function setRepositoryUsers($repository, $config_user_names) { | ||||
|     $uri = $repository->getRemoteURI(); | ||||
|     $repository_name = basename($uri, '.git'); | ||||
|  | ||||
|     $variable_name = '@committers_' . escape_name(strtolower($repository_name)); | ||||
|  | ||||
|     if (array_key_exists($variable_name, $this->committers)) { | ||||
|       die("Duplicate variable mapping for repository $uri\n"); | ||||
|     } | ||||
|  | ||||
|     $this->committers[$variable_name] = $config_user_names; | ||||
|   } | ||||
|  | ||||
|   public function writeNewConfiguration() { | ||||
|     $current_config = file_get_contents($this->config_file); | ||||
|     $current_config_lines = explode("\n", $current_config); | ||||
|  | ||||
|     $new_config = ""; | ||||
|     foreach ($current_config_lines as $line) { | ||||
|       if (startswith($line, '@committers_')) { | ||||
|         $parts = explode('=', $line); | ||||
|         $variable_name = trim($parts[0]); | ||||
|         if (array_key_exists($variable_name, $this->committers)) { | ||||
|           $system_committers = $this->getNonPhabtricatorUsers($parts[1]); | ||||
|           $all_committers = array_merge( | ||||
|               $system_committers, $this->committers[$variable_name]); | ||||
|           $unique_committers = array_unique($all_committers); | ||||
|           $committers = implode(' ', $unique_committers); | ||||
|           $line = "$variable_name = $committers"; | ||||
|         } | ||||
|       } | ||||
|       $new_config .= $line . "\n"; | ||||
|     } | ||||
|  | ||||
|     file_put_contents_if_different($this->config_file, trim($new_config) . "\n"); | ||||
|   } | ||||
|  | ||||
|   protected function getNonPhabtricatorUsers($configuration_value) { | ||||
|     $system_users = array(); | ||||
|     $users = explode(' ', $configuration_value); | ||||
|     foreach ($users as $user) { | ||||
|       $user = trim($user); | ||||
|       if (empty($user)) { | ||||
|         continue; | ||||
|       } | ||||
|       if (startswith($user, 'PHAB')) { | ||||
|         continue; | ||||
|       } | ||||
|       $system_users[] = $user; | ||||
|     } | ||||
|     return $system_users; | ||||
|   } | ||||
|  | ||||
|   public function finalize() { | ||||
|     $this->removeUnusedPublicKeys(); | ||||
|   } | ||||
|  | ||||
|   // Get content of a public key to be stored in file. | ||||
|   protected function getPublicKeyContent($key) { | ||||
|     return $key->getKeyType().' '. | ||||
|            $key->getKeyBody().' '. | ||||
|            $key->getKeyComment()."\n"; | ||||
|   } | ||||
|  | ||||
|   // Get user+key name used by the Gitolite configuration. | ||||
|   protected function getConfigUserName($user, $key) { | ||||
|     $escaped_key_name = escape_name($key->getName()); | ||||
|     return 'PHAB_'.$user->getUserName(). | ||||
|            '_'.$escaped_key_name. | ||||
|            '_'.$key->getID(); | ||||
|   } | ||||
|  | ||||
|   // Get keys which are not managed by this Phabricator/Git integration script. | ||||
|   // | ||||
|   // Returns map from key content to the key file name. This is used to avoid | ||||
|   // public key duplication in the case system key is used by phabricator user. | ||||
|   protected function collectSystemPublicKeys() { | ||||
|     $files = scandir($this->keys_directory); | ||||
|     foreach ($files as $file) { | ||||
|       if (startswith($file, "PHAB")) { | ||||
|         continue; | ||||
|       } | ||||
|       if (!endswith($file, '.pub')) { | ||||
|         continue; | ||||
|       } | ||||
|       $key = file_get_contents("$this->keys_directory/$file"); | ||||
|       $file_we = basename($file, '.pub'); | ||||
|       $this->stored_keys[$key] = $file_we; | ||||
|       $this->used_keys[$file_we] = true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected function removeUnusedPublicKeys() { | ||||
|     $files = scandir($this->keys_directory); | ||||
|     foreach ($files as $file) { | ||||
|       if (!startswith($file, "PHAB")) { | ||||
|         continue; | ||||
|       } | ||||
|       $config_user_name = basename($file, '.pub'); | ||||
|       if (!array_key_exists($config_user_name, $this->used_keys)) { | ||||
|         unlink("$this->keys_directory/$file"); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  | ||||
| function getProjectMembersPHIDs($viewer, $project_phid) { | ||||
|   $project = id(new PhabricatorProjectQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->needMembers(true) | ||||
|       ->withPHIDs(array($project_phid)) | ||||
|       ->executeOne(); | ||||
|  | ||||
|   return $project->getMemberPHIDs(); | ||||
| } | ||||
|  | ||||
| // Get user's heys and put them to the configuration | ||||
| function handleSingleUserPHID($config, $userPHID) { | ||||
|   $user = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($config->viewer) | ||||
|     ->withPHIDs(array($userPHID)) | ||||
|     ->executeOne(); | ||||
|   if (!$user) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   if ($user->getIsDisabled()) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   $keys = id(new PhabricatorAuthSSHKey())->loadAllWhere( | ||||
|    'objectPHID = %s', | ||||
|     $user->getPHID()); | ||||
|  | ||||
|   $config_user_names = array(); | ||||
|   foreach ($keys as $key) { | ||||
|     $config_user_name = $config->storeUserPublicKey($user, $key); | ||||
|     $config_user_names[] = $config_user_name; | ||||
|   } | ||||
|  | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleUsersPolicyRule($config, $rule) { | ||||
|   $config_user_names = array(); | ||||
|   foreach ($rule['value'] as $userPHID) { | ||||
|     $config_user_names = array_merge($config_user_names, | ||||
|       handleSingleUserPHID($config, $userPHID)); | ||||
|   } | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleProjectsPolicyRule($config, $rule) { | ||||
|   $config_user_names = array(); | ||||
|   foreach ($rule['value'] as $projectPHID) { | ||||
|     $memberPHIDs = getProjectMembersPHIDs($config->viewer, $projectPHID); | ||||
|     foreach ($memberPHIDs as $userPHID) { | ||||
|       $config_user_names = array_merge($config_user_names, | ||||
|         handleSingleUserPHID($config, $userPHID)); | ||||
|     } | ||||
|   } | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleProjectsAllPolicyRule($config, $rule) { | ||||
|   $is_first_project = true; | ||||
|   $allowed_members_phids = array(); | ||||
|   foreach ($rule['value'] as $project_phid) { | ||||
|     $memberPHIDs = getProjectMembersPHIDs($config->viewer, $project_phid); | ||||
|     if ($is_first_project) { | ||||
|       $allowed_members_phids = $memberPHIDs; | ||||
|       $is_first_project = false; | ||||
|     } else { | ||||
|       $allowed_members_phids = array_intersect( | ||||
|         $allowed_members_phids, $memberPHIDs); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   $config_user_names = array(); | ||||
|   foreach ($allowed_members_phids as $userPHID) { | ||||
|     $config_user_names = array_merge($config_user_names, | ||||
|       handleSingleUserPHID($config, $userPHID)); | ||||
|   } | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleAdministratorsPolicyRule($config, $rule) { | ||||
|   $administrators = id(new PhabricatorPeopleQuery()) | ||||
|       ->setViewer($config->viewer) | ||||
|       ->withIsAdmin(true) | ||||
|       ->execute(); | ||||
|  | ||||
|   $config_user_names = array(); | ||||
|   foreach ($administrators as $administrator) { | ||||
|     $config_user_names = array_merge($config_user_names, | ||||
|       handleSingleUserPHID($config, $administrator->getPHID())); | ||||
|   } | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleLegalpadSingleDocument($config, $document) { | ||||
|   if ($document->getSignatureType() != | ||||
|       LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   $config_user_names = array(); | ||||
|   foreach ($document->getSignatures() as $signature) { | ||||
|     if ($signature->getSignatureType() != | ||||
|         LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL) { | ||||
|       continue; | ||||
|     } | ||||
|     $config_user_names = array_merge($config_user_names, | ||||
|       handleSingleUserPHID($config, $signature->getSignerPHID())); | ||||
|   } | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleLegalpadSignaturePolicyRule($config, $rule) { | ||||
|   $documents = id(new LegalpadDocumentQuery()) | ||||
|     ->setViewer($config->viewer) | ||||
|     ->withPHIDs($rule['value']) | ||||
|     ->needSignatures(true) | ||||
|     ->execute(); | ||||
|  | ||||
|   $config_user_names = array(); | ||||
|   foreach ($documents as $document) { | ||||
|     $config_user_names = array_merge($config_user_names, | ||||
|       handleLegalpadSingleDocument($config, $document)); | ||||
|   } | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| function handleCustomPolicy($config, $policy) { | ||||
|   $config_user_names = array(); | ||||
|   $rules = $policy->getRules(); | ||||
|   foreach ($rules as $rule) { | ||||
|     // Everyone is denied by default anyway | ||||
|     if ($rule['action'] != 'allow') { | ||||
|       continue; | ||||
|     } | ||||
|  | ||||
|     $policy_config_user_names = array(); | ||||
|     $rule_type = $rule['rule']; | ||||
|     if ($rule_type == 'PhabricatorPolicyRuleUsers') { | ||||
|       $policy_config_user_names = | ||||
|           handleUsersPolicyRule($config, $rule); | ||||
|     } else if ($rule_type == 'PhabricatorProjectsPolicyRule') { | ||||
|       $policy_config_user_names = | ||||
|           handleProjectsPolicyRule($config, $rule); | ||||
|     } else if ($rule_type == 'PhabricatorProjectsAllPolicyRule') { | ||||
|       $policy_config_user_names = | ||||
|           handleProjectsAllPolicyRule($config, $rule); | ||||
|     } else if ($rule_type == 'PhabricatorAdministratorsPolicyRule') { | ||||
|       $policy_config_user_names = | ||||
|           handleAdministratorsPolicyRule($config, $rule); | ||||
|     } else if ($rule_type == 'PhabricatorLegalpadSignaturePolicyRule') { | ||||
|       $policy_config_user_names = | ||||
|           handleLegalpadSignaturePolicyRule($config, $rule); | ||||
|     } | ||||
|  | ||||
|     $config_user_names = array_merge( | ||||
|         $config_user_names, $policy_config_user_names); | ||||
|   } | ||||
|  | ||||
|   return $config_user_names; | ||||
| } | ||||
|  | ||||
| // Parse repository and put it's members to the config file | ||||
| function handleSingleRepository($config, $repository) { | ||||
|   $policies = PhabricatorPolicyQuery::loadPolicies( | ||||
|     $config->viewer, | ||||
|     $repository); | ||||
|  | ||||
|   $pushable = $policies[DiffusionPushCapability::CAPABILITY]; | ||||
|   $type = $pushable->getType(); | ||||
|  | ||||
|   $config_user_names = array(); | ||||
|  | ||||
|   if ($type == PhabricatorPolicyType::TYPE_PROJECT) { | ||||
|     $project = id(new PhabricatorProjectQuery()) | ||||
|       ->setViewer($config->viewer) | ||||
|       ->needMembers(true) | ||||
|       ->withPHIDs(array($pushable->getPHID())) | ||||
|       ->executeOne(); | ||||
|  | ||||
|     $memberPHIDs = $project->getMemberPHIDs(); | ||||
|     foreach ($memberPHIDs as $memberPHID) { | ||||
|       $config_user_names = array_merge($config_user_names, | ||||
|           handleSingleUserPHID($config, $memberPHID)); | ||||
|     } | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_USER) { | ||||
|     $config_user_names = handleSingleUserPHID($config, $pushable->getPHID()); | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_CUSTOM) { | ||||
|     $config_user_names = handleCustomPolicy($config, $pushable); | ||||
|   } else { | ||||
|     /* pass */ | ||||
|   } | ||||
|  | ||||
|   $config->setRepositoryUsers($repository, $config_user_names); | ||||
| } | ||||
|  | ||||
| function rebuildConfiguration($gitolite_root) { | ||||
|   $config = new Configuration($gitolite_root); | ||||
|  | ||||
|   // Fill in new configuration and keys | ||||
|   $used_keys = array(); | ||||
|   $repositories = id(new PhabricatorRepositoryQuery()) | ||||
|     ->setViewer($config->viewer) | ||||
|     ->execute(); | ||||
|  | ||||
|   foreach ($repositories as $repository_id => $repository) { | ||||
|     $type = $repository->getVersionControlSystem(); | ||||
|     if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT) { | ||||
|       handleSingleRepository($config, $repository); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   $config->writeNewConfiguration(); | ||||
|  | ||||
|   $config->finalize(); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////////////////////////// | ||||
| // Repository manipulation functionality. | ||||
|  | ||||
| function getGitCommand($repository) { | ||||
|   $git_dir = realpath("$repository/.git"); | ||||
|   $git = "git --git-dir='$git_dir'"; | ||||
|   $git .= ' --work-tree='.realpath($repository); | ||||
|   return $git; | ||||
| } | ||||
|  | ||||
| function runGitCommand($repository, $arguments, | ||||
|                        &$output=null, &$return_var=null) { | ||||
|   $git = getGitCommand($repository); | ||||
|   $git .= " $arguments"; | ||||
|   exec($git, $output, $return_var); | ||||
|   return $return_var == 0; | ||||
| } | ||||
|  | ||||
| function runGitSshCommand($repository, $key, $arguments, | ||||
|                           &$output=null, &$return_var=null) { | ||||
|   $abs_key = realpath($key); | ||||
|   $git = "GIT_SSH_COMMAND=\"ssh -i $key -o IdentitiesOnly=yes\" "; | ||||
|   $git .= getGitCommand($repository); | ||||
|   $git .= " $arguments"; | ||||
|   exec($git, $output, $return_var); | ||||
|   return $return_var == 0; | ||||
| } | ||||
|  | ||||
| function repositoryPull($repository, $key) { | ||||
|   return runGitSshCommand($repository, $key, 'pull --rebase'); | ||||
| } | ||||
|  | ||||
| function repositoryCommitAll($repository, $author, $message) { | ||||
|   if (!runGitCommand( | ||||
|     $repository, 'ls-files --other --exclude-standard', $untracked_files)) { | ||||
|     return false; | ||||
|   } | ||||
|   if (count($untracked_files)) { | ||||
|     $flat_files = join(' ', $untracked_files); | ||||
|     if (!runGitCommand($repository, "add $flat_files")) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|   runGitCommand($repository, "update-index -q --refresh", $output); | ||||
|   runGitCommand($repository, "diff-index --name-only HEAD --", $output); | ||||
|   if (count($output)) { | ||||
|     return runGitCommand( | ||||
|       $repository, "commit --author='$author' -a -m '$message'", $output); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| if (count($argv) != 3) { | ||||
|   print("Usage: {$argv[0]} /path/to/gitolite-admin /path/to/id_rsa.pub\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| $gitolite_root = $argv[1]; | ||||
| $key = $argv[2]; | ||||
|  | ||||
| if (!repositoryPull($gitolite_root, $key)) { | ||||
|   print("Failed to pull changes from server.\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| if (!rebuildConfiguration($gitolite_root)) { | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| if (!repositoryCommitAll( | ||||
|   $gitolite_root, 'Rebuild Gitadmin <null@git.blender.org>', | ||||
|   'Update to correspond changes in Phabricator')) { | ||||
|   print("Failed to commit changes.\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| runGitSshCommand($gitolite_root, $key, 'push origin master'); | ||||
| ?> | ||||
| @@ -22,7 +22,7 @@ failed() { | ||||
| } | ||||
|  | ||||
| ISSUE=`cat /etc/issue` | ||||
| if [[ $ISSUE != Ubuntu* ]] | ||||
| if [[ ($ISSUE != Ubuntu*) && ($ISSUE != Debian*) ]]; | ||||
| then | ||||
|   echo "This script is intended for use on Ubuntu, but this system appears"; | ||||
|   echo "to be something else. Your results may vary."; | ||||
|   | ||||
							
								
								
									
										61
									
								
								scripts/svnauth/archived_repos.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,61 @@ | ||||
| <?php | ||||
|   $ARCHIVED_REPOS = array( | ||||
|     'abstractmesh', | ||||
|     'beast', | ||||
| #    'bf-blender', | ||||
|     'bf-docboard-es', | ||||
|     'bf-extensions', | ||||
|     'bf-funboard', | ||||
|     'bf-scripts', | ||||
| #    'bf-translations', | ||||
|     'bfct', | ||||
|     'bforge', | ||||
|     'blend-doc', | ||||
|     'blend2cs', | ||||
|     'blendedmidi', | ||||
|     'blendercad', | ||||
|     'blendxml', | ||||
|     'bzoo', | ||||
|     'docboard', | ||||
|     'drqueue', | ||||
|     'ghost', | ||||
|     'girona', | ||||
|     'guiman', | ||||
|     'lsystem', | ||||
|     'magic', | ||||
|     'makeh', | ||||
|     'mechanicblender', | ||||
|     'neverblender', | ||||
|     'news', | ||||
|     'night', | ||||
|     'nitrox', | ||||
|     'osgexport', | ||||
|     'peerrating', | ||||
|     'piovra', | ||||
|     'pyverse', | ||||
|     'qdune', | ||||
|     'scolblender', | ||||
|     'skined', | ||||
|     'smdio', | ||||
|     'soapyblender', | ||||
|     'soc-2005', | ||||
|     'soc-2006', | ||||
|     'soc-2007', | ||||
|     'soc-2008', | ||||
|     'sourceforge', | ||||
|     'spe', | ||||
|     'stats', | ||||
|     'ter2blend', | ||||
|     'torqueexporter', | ||||
|     'tuhopuu', | ||||
|     'tzuray', | ||||
|     'vectex', | ||||
|     'vectorrender', | ||||
|     'verse', | ||||
|     'vrmlimportexp', | ||||
|     'warblender', | ||||
|     'wpyre', | ||||
|     'yafray', | ||||
|     'yofrankie' | ||||
|   ); | ||||
| ?> | ||||
							
								
								
									
										292
									
								
								scripts/svnauth/rebuild_svnauth.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,292 @@ | ||||
| #!/usr/local/bin/php | ||||
| <?php | ||||
|  | ||||
| $root = dirname(dirname(dirname(__FILE__))); | ||||
| require_once $root.'/scripts/__init_script__.php'; | ||||
|  | ||||
| function getSVNRepositoryName($repository) { | ||||
|   $uri = $repository->getRemoteURI(); | ||||
|   return preg_replace( | ||||
|     '/https?\:\/\/.*?\/svnroot\/([^\/]+)\/?.*/', '$1', $uri); | ||||
| } | ||||
|  | ||||
| // Get user's heys and put them to the configuration | ||||
| function handleSingleUserPHID( | ||||
|   $viewer, $userPHID, $repository, &$namemap, &$access) { | ||||
|   $user = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withPHIDs(array($userPHID)) | ||||
|     ->executeOne(); | ||||
|   if (!$user) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if ($user->getIsDisabled()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   $user_name = $user->getUserName(); | ||||
|   if (!array_key_exists($user_name, $namemap)) { | ||||
|     $namemap[$user_name] = array('email' => $user->loadPrimaryEmailAddress(), | ||||
|                                  'name' => $user->getRealName()); | ||||
|   } | ||||
|  | ||||
|   $repository_name = getSVNRepositoryName($repository); | ||||
|  | ||||
|   $repository_rootpath = $repository_name . ':/'; | ||||
|   if (!array_key_exists($repository_rootpath, $access)) { | ||||
|     $access[$repository_rootpath]['RW'] = array(); | ||||
|     $access[$repository_rootpath]['RO'] = array(); | ||||
|   } | ||||
|   $access[$repository_rootpath]['RO'][] = $user_name; | ||||
|  | ||||
|   // Store write access settings to current subath | ||||
|   $subpath = $repository->getDetail('svn-subpath'); | ||||
|   $subpath = rtrim($subpath, '/'); | ||||
|   $repository_pathname = "$repository_name:/$subpath"; | ||||
|   if (!array_key_exists($repository_pathname, $access)) { | ||||
|     $access[$repository_pathname]['RW'] = array(); | ||||
|     $access[$repository_pathname]['RO'] = array(); | ||||
|   } | ||||
|   $access[$repository_pathname]['RW'][] = $user_name; | ||||
|  | ||||
|   // Write access to the tags | ||||
|   $tags_pathname = "$repository_name:/tags"; | ||||
|   if (!array_key_exists($tags_pathname, $access)) { | ||||
|     $access[$tags_pathname]['RW'] = array(); | ||||
|     $access[$tags_pathname]['RO'] = array(); | ||||
|   } | ||||
|   $access[$tags_pathname]['RW'][] = $user_name; | ||||
|  | ||||
|   // Write access to the branches. | ||||
|   $branches_pathname = "$repository_name:/branches"; | ||||
|   if (!array_key_exists($branches_pathname, $access)) { | ||||
|     $access[$branches_pathname]['RW'] = array(); | ||||
|     $access[$branches_pathname]['RO'] = array(); | ||||
|   } | ||||
|   $access[$branches_pathname]['RW'][] = $user_name; | ||||
| } | ||||
|  | ||||
| function getProjectMembersPHIDs($viewer, $project_phid) { | ||||
|   $project = id(new PhabricatorProjectQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->needMembers(true) | ||||
|     ->withPHIDs(array($project_phid)) | ||||
|     ->executeOne(); | ||||
|  | ||||
|   return $project->getMemberPHIDs(); | ||||
| } | ||||
|  | ||||
| function handleProjectPHID( | ||||
|   $viewer, $project_phid, $repository, &$namemap, &$access) { | ||||
|   $memberPHIDs = getProjectMembersPHIDs($viewer, $project_phid); | ||||
|   foreach ($memberPHIDs as $memberPHID) { | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $memberPHID, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleUsersPolicyRule( | ||||
|   $viewer, $rule, $repository, &$namemap, &$access) { | ||||
|   foreach ($rule['value'] as $user_phid) { | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $user_phid, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleProjectsPolicyRule( | ||||
|   $viewer, $rule, $repository, &$namemap, &$access) { | ||||
|   foreach ($rule['value'] as $project_phid) { | ||||
|     handleProjectPHID( | ||||
|       $viewer, $project_phid, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleProjectsAllPolicyRule( | ||||
|   $viewer, $rule, $repository, &$namemap, &$access) { | ||||
|   $is_first_project = true; | ||||
|   $allowed_members_phids = array(); | ||||
|   foreach ($rule['value'] as $project_phid) { | ||||
|     $memberPHIDs = getProjectMembersPHIDs($viewer, $project_phid); | ||||
|     if ($is_first_project) { | ||||
|       $allowed_members_phids = $memberPHIDs; | ||||
|       $is_first_project = false; | ||||
|     } else { | ||||
|       $allowed_members_phids = array_intersect( | ||||
|         $allowed_members_phids, $memberPHIDs); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   foreach ($allowed_members_phids as $user_phid) { | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $user_phid, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleAdministratorsPolicyRule( | ||||
|   $viewer, $rule, $repository, &$namemap, &$access) { | ||||
|   $administrators = id(new PhabricatorPeopleQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withIsAdmin(true) | ||||
|     ->execute(); | ||||
|   foreach ($administrators as $administrator) { | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $administrator->getPHID(), $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleLegalpadSingleDocument( | ||||
|   $viewer, $document, $repository, &$namemap, &$access) { | ||||
|   if ($document->getSignatureType() != | ||||
|       LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   foreach ($document->getSignatures() as $signature) { | ||||
|     if ($signature->getSignatureType() != | ||||
|         LegalpadDocument::SIGNATURE_TYPE_INDIVIDUAL) { | ||||
|       continue; | ||||
|     } | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $signature->getSignerPHID(), $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleLegalpadSignaturePolicyRule( | ||||
|   $viewer, $rule, $repository, &$namemap, &$access) { | ||||
|   $documents = id(new LegalpadDocumentQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->withPHIDs($rule['value']) | ||||
|     ->needSignatures(true) | ||||
|     ->execute(); | ||||
|  | ||||
|   foreach ($documents as $document) { | ||||
|     handleLegalpadSingleDocument( | ||||
|       $viewer, $document, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleCustomPolicyRule( | ||||
|   $viewer, $rule, $repository, &$namemap, &$access) { | ||||
|   if ($rule['action'] != PhabricatorPolicy::ACTION_ALLOW) { | ||||
|     // By default the script decides to DENY unless explicitly allowed. | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   $rule_type = $rule['rule']; | ||||
|   if ($rule_type == 'PhabricatorUsersPolicyRule') { | ||||
|     handleUsersPolicyRule( | ||||
|       $viewer, $rule, $repository, $namemap, $access); | ||||
|   } else if ($rule_type == 'PhabricatorProjectsPolicyRule') { | ||||
|     handleProjectsPolicyRule( | ||||
|       $viewer, $rule, $repository, $namemap, $access); | ||||
|   } else if ($rule_type == 'PhabricatorProjectsAllPolicyRule') { | ||||
|     handleProjectsAllPolicyRule( | ||||
|       $viewer, $rule, $repository, $namemap, $access); | ||||
|   } else if ($rule_type == 'PhabricatorAdministratorsPolicyRule') { | ||||
|     handleAdministratorsPolicyRule( | ||||
|       $viewer, $rule, $repository, $namemap, $access); | ||||
|   } else if ($rule_type == 'PhabricatorLegalpadSignaturePolicyRule') { | ||||
|     handleLegalpadSignaturePolicyRule( | ||||
|       $viewer, $rule, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function handleCustomPolicy( | ||||
|   $viewer, $policy, $repository, &$namemap, &$access) { | ||||
|   foreach ($policy->getRules() as $rule) { | ||||
|     handleCustomPolicyRule($viewer, $rule, $repository, $namemap, $access); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Parse repository and put it's members to the config file | ||||
| function handleSingleRepository( | ||||
|   $viewer, $repository, &$namemap, &$access) { | ||||
|   $policies = PhabricatorPolicyQuery::loadPolicies( | ||||
|     $viewer, | ||||
|     $repository); | ||||
|  | ||||
|   $pushable = $policies[DiffusionPushCapability::CAPABILITY]; | ||||
|   $type = phid_get_type($pushable->getPHID()); | ||||
|  | ||||
|   // Make sure repository is always available for read-only access | ||||
|   $repository_rootpath = getSVNRepositoryName($repository) . ':/'; | ||||
|   if (!array_key_exists($repository_rootpath, $access)) { | ||||
|     $access[$repository_rootpath]['RW'] = array(); | ||||
|     $access[$repository_rootpath]['RO'] = array(); | ||||
|   } | ||||
|  | ||||
|   if ($type == PhabricatorProjectProjectPHIDType::TYPECONST) { | ||||
|     handleProjectPHID( | ||||
|       $viewer, $pushable->getPHID(), $repository, $namemap, $access); | ||||
|   } else if ($type == PhabricatorPolicyType::TYPE_USER) { | ||||
|     handleSingleUserPHID( | ||||
|       $viewer, $pushable->getPHID(), $repository, $namemap, $access); | ||||
|   } else if ($type == PhabricatorPolicyPHIDTypePolicy::TYPECONST) { | ||||
|     handleCustomPolicy( | ||||
|       $viewer, $pushable, $repository, $namemap, $access); | ||||
|     /* pass */ | ||||
|   } else { | ||||
|     /* pass */ | ||||
|   } | ||||
| } | ||||
|  | ||||
| function rebuildConfiguration($what) { | ||||
|   $viewer = id(new PhabricatorUser()) | ||||
|     ->loadOneWhere('username = %s', 'sergey'); | ||||
|  | ||||
|   $repositories = id(new PhabricatorRepositoryQuery()) | ||||
|     ->setViewer($viewer) | ||||
|     ->execute(); | ||||
|  | ||||
|   $namemap = array(); | ||||
|   $access = array(); | ||||
|  | ||||
|   require_once 'archived_repos.php'; | ||||
|   foreach ($ARCHIVED_REPOS as $repository) { | ||||
|     $repository_pathname = "$repository:/"; | ||||
|     $access[$repository_pathname]['RW'] = array(); | ||||
|     $access[$repository_pathname]['RO'] = array(); | ||||
|   } | ||||
|  | ||||
|   foreach ($repositories as $repository_id => $repository) { | ||||
|     $type = $repository->getVersionControlSystem(); | ||||
|     if ($type == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { | ||||
|       handleSingleRepository( | ||||
|         $viewer, $repository, $namemap, $access); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if ($what == 'ACCESS') { | ||||
|     foreach ($access as $repository => $users) { | ||||
|       print("[$repository]\n"); | ||||
|       $rw_users = array(); | ||||
|       foreach ($users['RW'] as $user) { | ||||
|         print("$user = rw\n"); | ||||
|         $rw_users[$user] = true; | ||||
|       } | ||||
|       foreach ($users['RO'] as $user) { | ||||
|         if (!array_key_exists($user, $rw_users)) { | ||||
|           print("$user = r\n"); | ||||
|         } | ||||
|       } | ||||
|       print("anonsvn = r\n"); | ||||
|       print("* = r\n\n"); | ||||
|     } | ||||
|   } else if ($what == 'NAMEMAP') { | ||||
|     foreach ($namemap as $user => $data) { | ||||
|       print("$user\t${data['email']}\t${data['name']}\n"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| if (count($argv) != 2 || | ||||
|     ($argv[1] != 'ACCESS' && $argv[1] != 'NAMEMAP')) { | ||||
|   print("Usage: {$argv[0]} ACCESS|NAMEMAP\n"); | ||||
|   exit(1); | ||||
| } | ||||
|  | ||||
| rebuildConfiguration($argv[1]); | ||||
| ?> | ||||
| @@ -1773,6 +1773,7 @@ phutil_register_library_map(array( | ||||
|     'ManiphestReportController' => 'applications/maniphest/controller/ManiphestReportController.php', | ||||
|     'ManiphestSchemaSpec' => 'applications/maniphest/storage/ManiphestSchemaSpec.php', | ||||
|     'ManiphestSearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestSearchConduitAPIMethod.php', | ||||
|     'ManiphestSearchController' => 'applications/maniphest/controller/ManiphestSearchController.php', | ||||
|     'ManiphestStatusEmailCommand' => 'applications/maniphest/command/ManiphestStatusEmailCommand.php', | ||||
|     'ManiphestStatusSearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestStatusSearchConduitAPIMethod.php', | ||||
|     'ManiphestStatusesConfigType' => 'applications/maniphest/config/ManiphestStatusesConfigType.php', | ||||
| @@ -2165,6 +2166,7 @@ phutil_register_library_map(array( | ||||
|     'PasteSearchConduitAPIMethod' => 'applications/paste/conduit/PasteSearchConduitAPIMethod.php', | ||||
|     'PeopleBrowseUserDirectoryCapability' => 'applications/people/capability/PeopleBrowseUserDirectoryCapability.php', | ||||
|     'PeopleCreateUsersCapability' => 'applications/people/capability/PeopleCreateUsersCapability.php', | ||||
|     'PeopleDisableSpamUsersCapability' => 'applications/people/capability/PeopleDisableSpamUsersCapability.php', | ||||
|     'PeopleDisableUsersCapability' => 'applications/people/capability/PeopleDisableUsersCapability.php', | ||||
|     'PeopleHovercardEngineExtension' => 'applications/people/engineextension/PeopleHovercardEngineExtension.php', | ||||
|     'PeopleMainMenuBarExtension' => 'applications/people/engineextension/PeopleMainMenuBarExtension.php', | ||||
| @@ -2226,6 +2228,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorApplicationSearchResultView' => 'applications/search/view/PhabricatorApplicationSearchResultView.php', | ||||
|     'PhabricatorApplicationTestCase' => 'applications/base/__tests__/PhabricatorApplicationTestCase.php', | ||||
|     'PhabricatorApplicationTransaction' => 'applications/transactions/storage/PhabricatorApplicationTransaction.php', | ||||
|     'PhabricatorApplicationTransactionCannedResponsesController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCannedResponsesController.php', | ||||
|     'PhabricatorApplicationTransactionComment' => 'applications/transactions/storage/PhabricatorApplicationTransactionComment.php', | ||||
|     'PhabricatorApplicationTransactionCommentEditController' => 'applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php', | ||||
|     'PhabricatorApplicationTransactionCommentEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionCommentEditor.php', | ||||
| @@ -4165,6 +4168,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php', | ||||
|     'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php', | ||||
|     'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php', | ||||
|     'PhabricatorPeopleSpamController' => 'applications/people/controller/PhabricatorPeopleSpamController.php', | ||||
|     'PhabricatorPeopleTasksProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php', | ||||
|     'PhabricatorPeopleTestDataGenerator' => 'applications/people/lipsum/PhabricatorPeopleTestDataGenerator.php', | ||||
|     'PhabricatorPeopleTransactionQuery' => 'applications/people/query/PhabricatorPeopleTransactionQuery.php', | ||||
| @@ -4763,6 +4767,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorSettingsLogsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsLogsPanelGroup.php', | ||||
|     'PhabricatorSettingsMainController' => 'applications/settings/controller/PhabricatorSettingsMainController.php', | ||||
|     'PhabricatorSettingsPanel' => 'applications/settings/panel/PhabricatorSettingsPanel.php', | ||||
|     'PhabricatorSettingsPanelChangeUsername' => 'applications/settings/panel/PhabricatorSettingsPanelChangeUsername.php', | ||||
|     'PhabricatorSettingsPanelGroup' => 'applications/settings/panelgroup/PhabricatorSettingsPanelGroup.php', | ||||
|     'PhabricatorSettingsTimezoneController' => 'applications/settings/controller/PhabricatorSettingsTimezoneController.php', | ||||
|     'PhabricatorSetupCheck' => 'applications/config/check/PhabricatorSetupCheck.php', | ||||
| @@ -5056,6 +5061,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorUserCustomFieldNumericIndex' => 'applications/people/storage/PhabricatorUserCustomFieldNumericIndex.php', | ||||
|     'PhabricatorUserCustomFieldStringIndex' => 'applications/people/storage/PhabricatorUserCustomFieldStringIndex.php', | ||||
|     'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php', | ||||
|     'PhabricatorUserDisableSpamTransaction' => 'applications/people/xaction/PhabricatorUserDisableSpamTransaction.php', | ||||
|     'PhabricatorUserDisableTransaction' => 'applications/people/xaction/PhabricatorUserDisableTransaction.php', | ||||
|     'PhabricatorUserEditEngine' => 'applications/people/editor/PhabricatorUserEditEngine.php', | ||||
|     'PhabricatorUserEditor' => 'applications/people/editor/PhabricatorUserEditor.php', | ||||
| @@ -8025,6 +8031,7 @@ phutil_register_library_map(array( | ||||
|     'ManiphestReportController' => 'ManiphestController', | ||||
|     'ManiphestSchemaSpec' => 'PhabricatorConfigSchemaSpec', | ||||
|     'ManiphestSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', | ||||
|     'ManiphestSearchController' => 'PhabricatorApplicationSearchController', | ||||
|     'ManiphestStatusEmailCommand' => 'ManiphestEmailCommand', | ||||
|     'ManiphestStatusSearchConduitAPIMethod' => 'ManiphestConduitAPIMethod', | ||||
|     'ManiphestStatusesConfigType' => 'PhabricatorJSONConfigType', | ||||
| @@ -8469,6 +8476,7 @@ phutil_register_library_map(array( | ||||
|     'PasteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', | ||||
|     'PeopleBrowseUserDirectoryCapability' => 'PhabricatorPolicyCapability', | ||||
|     'PeopleCreateUsersCapability' => 'PhabricatorPolicyCapability', | ||||
|     'PeopleDisableSpamUsersCapability' => 'PhabricatorPolicyCapability', | ||||
|     'PeopleDisableUsersCapability' => 'PhabricatorPolicyCapability', | ||||
|     'PeopleHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', | ||||
|     'PeopleMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension', | ||||
| @@ -8538,6 +8546,7 @@ phutil_register_library_map(array( | ||||
|       'PhabricatorPolicyInterface', | ||||
|       'PhabricatorDestructibleInterface', | ||||
|     ), | ||||
|     'PhabricatorApplicationTransactionCannedResponsesController' => 'PhabricatorApplicationTransactionController', | ||||
|     'PhabricatorApplicationTransactionComment' => array( | ||||
|       'PhabricatorLiskDAO', | ||||
|       'PhabricatorMarkupInterface', | ||||
| @@ -10785,6 +10794,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController', | ||||
|     'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem', | ||||
|     'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||
|     'PhabricatorPeopleSpamController' => 'PhabricatorPeopleController', | ||||
|     'PhabricatorPeopleTasksProfileMenuItem' => 'PhabricatorProfileMenuItem', | ||||
|     'PhabricatorPeopleTestDataGenerator' => 'PhabricatorTestDataGenerator', | ||||
|     'PhabricatorPeopleTransactionQuery' => 'PhabricatorApplicationTransactionQuery', | ||||
| @@ -11519,6 +11529,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorSettingsLogsPanelGroup' => 'PhabricatorSettingsPanelGroup', | ||||
|     'PhabricatorSettingsMainController' => 'PhabricatorController', | ||||
|     'PhabricatorSettingsPanel' => 'Phobject', | ||||
|     'PhabricatorSettingsPanelChangeUsername' => 'PhabricatorSettingsPanel', | ||||
|     'PhabricatorSettingsPanelGroup' => 'Phobject', | ||||
|     'PhabricatorSettingsTimezoneController' => 'PhabricatorController', | ||||
|     'PhabricatorSetupCheck' => 'Phobject', | ||||
| @@ -11854,6 +11865,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorUserCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage', | ||||
|     'PhabricatorUserCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage', | ||||
|     'PhabricatorUserDAO' => 'PhabricatorLiskDAO', | ||||
|     'PhabricatorUserDisableSpamTransaction' => 'PhabricatorUserTransactionType', | ||||
|     'PhabricatorUserDisableTransaction' => 'PhabricatorUserTransactionType', | ||||
|     'PhabricatorUserEditEngine' => 'PhabricatorEditEngine', | ||||
|     'PhabricatorUserEditor' => 'PhabricatorEditor', | ||||
|   | ||||
| @@ -346,6 +346,14 @@ final class PhabricatorAuthRegisterController | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // blender hack | ||||
|       $root = dirname(phutil_get_library_root('phabricator')); | ||||
|       require $root.'/migration/dedup.php'; | ||||
|       if (array_key_exists($request->getStr('username'), $migrate_dedup_users)) { | ||||
|         $e_username = pht('Duplicate'); | ||||
|         $errors[] = pht('Username is already reserved.'); | ||||
|       } | ||||
|  | ||||
|       if (!$errors) { | ||||
|         if (!$is_setup) { | ||||
|           $image = $this->loadProfilePicture($account); | ||||
|   | ||||
| @@ -173,8 +173,8 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|     $dialog = id(new AphrontDialogView()) | ||||
|       ->setSubmitURI($this->getLoginURI()) | ||||
|       ->setUser($viewer) | ||||
|       ->setTitle(pht('Log In')) | ||||
|       ->addSubmitButton(pht('Log In')); | ||||
|       ->setTitle(pht('Login to developer.blender.org')) | ||||
|       ->addSubmitButton(pht('Login')); | ||||
|  | ||||
|     if ($this->shouldAllowRegistration()) { | ||||
|       $dialog->addCancelButton( | ||||
| @@ -182,6 +182,11 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|         pht('Register New Account')); | ||||
|     } | ||||
|  | ||||
|     $webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/'; | ||||
|     $dialog->addFooter( | ||||
|       phutil_safe_html( | ||||
|         FileSystem::readFile($webroot .'rsrc/custom/static/login.html'))); | ||||
|  | ||||
|     $dialog->addFooter( | ||||
|       phutil_tag( | ||||
|         'a', | ||||
| @@ -217,6 +222,28 @@ final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider { | ||||
|       $errors[] = pht('Username or password are incorrect.'); | ||||
|     } | ||||
|  | ||||
|     if (true) { | ||||
|       // blender hack | ||||
|       $root = dirname(phutil_get_library_root('phabricator')); | ||||
|       require $root.'/migration/dedup.php'; | ||||
|  | ||||
|       $missing_username = $request->getStr('username'); | ||||
|  | ||||
|       $find_user = id(new PhabricatorUser())->loadOneWhere( | ||||
|         'username = %s', | ||||
|          $missing_username); | ||||
|  | ||||
|       if (!$find_user && array_key_exists($missing_username, $migrate_dedup_users)) { | ||||
|         $errors = array(); | ||||
|         $errors[] = pht('This account was merged into account "' . | ||||
|                         $migrate_dedup_users[$missing_username] . | ||||
|                         '", because Phabricator does not support multiple accounts with the same email address. ' . | ||||
|                         'Please login with that account instead ' . | ||||
|                         '(optionally recovering your password if you forgot it). ' . | ||||
|                         'After logging in you will be able to change your username in the User Settings.'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($errors) { | ||||
|       $errors = id(new PHUIInfoView())->setErrors($errors); | ||||
|     } | ||||
|   | ||||
| @@ -125,7 +125,13 @@ final class PhabricatorAuthPassword | ||||
|     $hash = $hasher->getPasswordHashForStorage($digest); | ||||
|     $raw_hash = $hash->openEnvelope(); | ||||
|  | ||||
|     return $this->setPasswordHash($raw_hash); | ||||
|     $result = $this->setPasswordHash($raw_hash); | ||||
|     if ($result) { | ||||
|       if ($object instanceof PhabricatorUser) { | ||||
|         $object->updateHtaccessPassword($password); | ||||
|       } | ||||
|     } | ||||
|     return $result; | ||||
|   } | ||||
|  | ||||
|   public function comparePassword( | ||||
|   | ||||
| @@ -69,6 +69,31 @@ EOREMARKUP | ||||
|       'https://php.net/manual/timezones.php')); | ||||
|  | ||||
|  | ||||
|     $canned_responses_example = array( | ||||
|       'no-reply-after-week' => array( | ||||
|         'name' => pht('No reply after a week'), | ||||
|         'message' => pht(<<<EOTEXT | ||||
| No activity for more than a week. As per the tracker policy we assume | ||||
| the issue is gone and can be closed.\n\nThanks again for the report. | ||||
| If the problem persists please open a new report with the required information. | ||||
| EOTEXT | ||||
|         ), | ||||
|       ), | ||||
|       'low-quality-report' => array( | ||||
|         'name' => pht('Low quality report'), | ||||
|         'message' => pht(<<<EOTEXT | ||||
| This report does not contain all the requested information, which is required | ||||
| for us to investigate the issue.\n\nPlease submit a new report and carefully | ||||
| follow the instructions. | ||||
| EOTEXT | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|     $json = new PhutilJSON(); | ||||
|     $canned_responses_example = $json->encodeFormatted( | ||||
|       $canned_responses_example); | ||||
|  | ||||
|     return array( | ||||
|       $this->newOption('phabricator.base-uri', 'string', null) | ||||
|         ->setLocked(true) | ||||
| @@ -238,6 +263,21 @@ EOREMARKUP | ||||
|         ->setLocked(true) | ||||
|         ->setDescription( | ||||
|           pht('Customized settings for Phabricator applications.')), | ||||
|       $this->newOption('welcome.html', 'string', null) | ||||
|         ->setLocked(true) | ||||
|         ->setDescription( | ||||
|           pht('Custom HTML to show on the main Phabricator dashboard.')), | ||||
|       $this->newOption('welcome.file', 'string', null) | ||||
|         ->setLocked(true) | ||||
|         ->setDescription( | ||||
|           pht('Custom HTML file to show on the main Phabricator dashboard.')), | ||||
|       $this->newOption('diff_guidelines.file', 'string', null) | ||||
|         ->setLocked(true) | ||||
|         ->setDescription( | ||||
|           pht('Custom HTML file to show when submitting new diff.')), | ||||
|       $this->newOption('maniphest.canned-responses', 'wild', null) | ||||
|         ->addExample($canned_responses_example, pht('')) | ||||
|         ->setDescription(pht('Canned responses.')), | ||||
|       $this->newOption('phabricator.cache-namespace', 'string', 'phabricator') | ||||
|         ->setLocked(true) | ||||
|         ->setDescription(pht('Cache namespace.')), | ||||
|   | ||||
| @@ -27,6 +27,7 @@ final class PhabricatorUIConfigOptions | ||||
|       'green' => pht('Green'), | ||||
|       'indigo' => pht('Indigo'), | ||||
|       'dark' => pht('Dark'), | ||||
|       'blender' => pht('Blender'), | ||||
|     ); | ||||
|  | ||||
|     $example = <<<EOJSON | ||||
|   | ||||
| @@ -117,6 +117,9 @@ final class DifferentialGetCommitMessageConduitAPIMethod | ||||
|  | ||||
|       if (!strlen($value)) { | ||||
|         if ($is_template) { | ||||
|           if ($field_key === 'summary') { | ||||
|             continue; | ||||
|           } | ||||
|           $commit_message[] = $label.': '; | ||||
|         } | ||||
|       } else { | ||||
| @@ -127,7 +130,9 @@ final class DifferentialGetCommitMessageConduitAPIMethod | ||||
|             array("\r\n", "\r"), | ||||
|             array("\n",   "\n"), | ||||
|             $value); | ||||
|           if (strpos($value, "\n") !== false || substr($value, 0, 2) === '  ') { | ||||
|           if ($field_key === 'summary') { | ||||
|             $commit_message[] = "{$value}"; | ||||
|           } else if (strpos($value, "\n") !== false || substr($value, 0, 2) === '  ') { | ||||
|             $commit_message[] = "{$label}:\n{$value}"; | ||||
|           } else { | ||||
|             $commit_message[] = "{$label}: {$value}"; | ||||
|   | ||||
| @@ -93,6 +93,22 @@ final class DifferentialDiffCreateController extends DifferentialController { | ||||
|  | ||||
|     $cancel_uri = $this->getApplicationURI(); | ||||
|  | ||||
|     if (PhabricatorEnv::getEnvConfig('diff_guidelines.file') !== null) { | ||||
|       $webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/'; | ||||
|       $instructions = phutil_safe_html( | ||||
|         FileSystem::readFile($webroot . | ||||
|           PhabricatorEnv::getEnvConfig('diff_guidelines.file'))); | ||||
|     } else { | ||||
|       $instructions = pht( | ||||
|           'The best way to create a Differential diff is by using %s, but you '. | ||||
|           'can also just paste a diff (for example, from %s, %s or %s) into '. | ||||
|           'this box, or upload a diff file.', | ||||
|           $arcanist_link, | ||||
|           phutil_tag('tt', array(), 'svn diff'), | ||||
|           phutil_tag('tt', array(), 'git diff'), | ||||
|           phutil_tag('tt', array(), 'hg diff --git')); | ||||
|     } | ||||
|  | ||||
|     $policies = id(new PhabricatorPolicyQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->setObject($diff_object) | ||||
| @@ -134,6 +150,7 @@ final class DifferentialDiffCreateController extends DifferentialController { | ||||
|  | ||||
|     $form | ||||
|       ->setEncType('multipart/form-data') | ||||
|       ->appendInstructions($instructions) | ||||
|       ->setUser($viewer); | ||||
|  | ||||
|     if ($revision) { | ||||
|   | ||||
| @@ -42,6 +42,12 @@ final class DifferentialAuditorsCommitMessageField | ||||
|   } | ||||
|  | ||||
|   public function renderFieldValue($value) { | ||||
|     // Blender: don't include this in commit messages. | ||||
|     // | ||||
|     // NOTE: Do it at render time to match behavior of other tweaked fields | ||||
|     // (such as reviewers) which are considered non-disableable. | ||||
|     return null; | ||||
|  | ||||
|     return $this->renderHandleList($value); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -62,6 +62,12 @@ final class DifferentialReviewersCommitMessageField | ||||
|   } | ||||
|  | ||||
|   public function renderFieldValue($value) { | ||||
|     // Blender: don't include this in commit messages. | ||||
|     // | ||||
|     // NOTE: Do it at render time to allow specifying reviewers when creating | ||||
|     // a new differential revision with `arc diff`. | ||||
|     return null; | ||||
|  | ||||
|     $value = $this->inflateReviewers($value); | ||||
|  | ||||
|     $phid_list = array(); | ||||
|   | ||||
| @@ -45,6 +45,12 @@ final class DifferentialSubscribersCommitMessageField | ||||
|   } | ||||
|  | ||||
|   public function renderFieldValue($value) { | ||||
|     // Blender: don't include this in commit messages. | ||||
|     // | ||||
|     // NOTE: Do it at render time to allow specifying subsribers when creating | ||||
|     // a new differential revision with `arc diff`. | ||||
|     return null; | ||||
|  | ||||
|     return $this->renderHandleList($value); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,12 @@ final class DifferentialTagsCommitMessageField | ||||
|   } | ||||
|  | ||||
|   public function renderFieldValue($value) { | ||||
|     // Blender: don't include this in commit messages. | ||||
|     // | ||||
|     // NOTE: Do it at render time to allow specifying tags when creating | ||||
|     // a new differential revision with `arc diff`. | ||||
|     return null; | ||||
|  | ||||
|     return $this->renderHandleList($value); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -133,6 +133,7 @@ final class DifferentialRevisionSearchEngine | ||||
|       $names['authored'] = pht('Authored'); | ||||
|     } | ||||
|  | ||||
|     $names['open'] = pht('Open Revisions'); | ||||
|     $names['all'] = pht('All Revisions'); | ||||
|  | ||||
|     return $names; | ||||
| @@ -155,6 +156,9 @@ final class DifferentialRevisionSearchEngine | ||||
|       case 'authored': | ||||
|         return $query | ||||
|           ->setParameter('authorPHIDs', array($viewer->getPHID())); | ||||
|       case 'open': | ||||
|         return $query | ||||
|           ->setParameter('status', DifferentialLegacyQuery::STATUS_OPEN); | ||||
|       case 'all': | ||||
|         return $query; | ||||
|     } | ||||
|   | ||||
| @@ -284,6 +284,16 @@ final class DifferentialDiff | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Blender: auto set git commit author from user if none provided. | ||||
|     // Use username instead of email, Phabricator will recognize this | ||||
|     // and we avoid sharing private information. | ||||
|     $user = id(new PhabricatorUser()) | ||||
|       ->loadOneWhere('phid = %s', $this->authorPHID); | ||||
|     if ($user) { | ||||
|       $dict['authorName'] = $user->getRealName(); | ||||
|       $dict['authorEmail'] = $user->getUserName(); | ||||
|     } | ||||
|  | ||||
|     return $dict; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -106,9 +106,7 @@ final class DiffusionRepositoryPoliciesManagementPanel | ||||
|       pht('Editable By'), | ||||
|       $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); | ||||
|  | ||||
|     $pushable = $repository->isHosted() | ||||
|       ? $descriptions[DiffusionPushCapability::CAPABILITY] | ||||
|       : phutil_tag('em', array(), pht('Not a Hosted Repository')); | ||||
|     $pushable = $descriptions[DiffusionPushCapability::CAPABILITY]; | ||||
|     $view->addProperty(pht('Pushable By'), $pushable); | ||||
|  | ||||
|     return $this->newBox(pht('Policies'), $view); | ||||
|   | ||||
| @@ -18,6 +18,10 @@ final class PhabricatorFavoritesMainMenuBarExtension | ||||
|   public function buildMainMenus() { | ||||
|     $viewer = $this->getViewer(); | ||||
|  | ||||
|     if ($viewer->limitNonContributorUI()) { | ||||
|       return array(); | ||||
|     } | ||||
|  | ||||
|     $dropdown = $this->newDropdown($viewer); | ||||
|     if (!$dropdown) { | ||||
|       return array(); | ||||
|   | ||||
| @@ -6,6 +6,10 @@ final class PhabricatorFileDataController extends PhabricatorFileController { | ||||
|   private $key; | ||||
|   private $file; | ||||
|  | ||||
|   public function shouldAllowPublic() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function shouldRequireLogin() { | ||||
|     return false; | ||||
|   } | ||||
|   | ||||
| @@ -61,7 +61,7 @@ final class PhabricatorFile extends PhabricatorFileDAO | ||||
|  | ||||
|   protected $ttl; | ||||
|   protected $isExplicitUpload = 1; | ||||
|   protected $viewPolicy = PhabricatorPolicies::POLICY_USER; | ||||
|   protected $viewPolicy = PhabricatorPolicies::POLICY_PUBLIC; | ||||
|   protected $isPartial = 0; | ||||
|   protected $isDeleted = 0; | ||||
|  | ||||
| @@ -1570,7 +1570,7 @@ final class PhabricatorFile extends PhabricatorFileDAO | ||||
|         } | ||||
|         return $this->getViewPolicy(); | ||||
|       case PhabricatorPolicyCapability::CAN_EDIT: | ||||
|         return PhabricatorPolicies::POLICY_NOONE; | ||||
|         return PhabricatorPolicies::POLICY_ADMIN; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,19 @@ final class PhabricatorHomeProfileMenuEngine | ||||
|     return "/home/menu/{$path}"; | ||||
|   } | ||||
|  | ||||
|   private function buildWelcomePanelFromFile() { | ||||
|     $webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/'; | ||||
|     $panel = new PHUIObjectBoxView(); | ||||
|     $panel->setHeaderText('Welcome'); | ||||
|     $panel->appendChild( | ||||
|       phutil_safe_html( | ||||
|         FileSystem::readFile($webroot . | ||||
|           PhabricatorEnv::getEnvConfig('welcome.file')))); | ||||
|  | ||||
|     require_celerity_resource('phabricator-welcome-page'); | ||||
|     return $panel; | ||||
|   } | ||||
|  | ||||
|   protected function buildItemViewContent( | ||||
|     PhabricatorProfileMenuItemConfiguration $item) { | ||||
|     $viewer = $this->getViewer(); | ||||
| @@ -23,6 +36,11 @@ final class PhabricatorHomeProfileMenuEngine | ||||
|  | ||||
|     $content = parent::buildItemViewContent($item); | ||||
|  | ||||
|     if (PhabricatorEnv::getEnvConfig('welcome.file') !== null) { | ||||
|       $content = array($this->buildWelcomePanelFromFile(), | ||||
|                        $content); | ||||
|     } | ||||
|  | ||||
|     return array( | ||||
|       $content, | ||||
|       $upload, | ||||
|   | ||||
| @@ -46,6 +46,7 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication { | ||||
|     return array( | ||||
|       '/T(?P<id>[1-9]\d*)' => 'ManiphestTaskDetailController', | ||||
|       '/maniphest/' => array( | ||||
|         '(?:project/(?P<projectKey>[^/]+)/)?(?:type/(?P<taskSubtypeKey>[^/]+)/)?(?:query/(?P<queryKey>[^/]+)/)?' => 'ManiphestTaskListController', | ||||
|         $this->getQueryRoutePattern() => 'ManiphestTaskListController', | ||||
|         'report/(?:(?P<view>\w+)/)?' => 'ManiphestReportController', | ||||
|         $this->getBulkRoutePattern('bulk/') => 'ManiphestBulkEditController', | ||||
|   | ||||
| @@ -2,18 +2,137 @@ | ||||
|  | ||||
| abstract class ManiphestController extends PhabricatorController { | ||||
|  | ||||
|   protected $projectKey; | ||||
|   protected $taskSubtypeKey; | ||||
|   protected $alwaysVisibleProjects = array('BF Blender', | ||||
|                                            'BF Blender: Unconfirmed', | ||||
|                                            'BF Blender: Regressions', | ||||
|                                            'BF Blender: Next', | ||||
|                                            'Addons', | ||||
|                                            'Game Engine'); | ||||
|  | ||||
|   public function willProcessRequest(array $data) { | ||||
|     $this->projectKey = idx($data, 'projectKey'); | ||||
|     $this->taskSubtypeKey = idx($data, 'taskSubtypeKey'); | ||||
|   } | ||||
|  | ||||
|   public function buildApplicationMenu() { | ||||
|     return $this->buildSideNavView()->getMenu(); | ||||
|   } | ||||
|  | ||||
|   public function getAvailableTaskSubtypes() { | ||||
|     $config = PhabricatorEnv::getEnvConfig('maniphest.subtypes'); | ||||
|     $subtype_map = PhabricatorEditEngineSubtype::newSubtypeMap($config); | ||||
|     return $subtypes = $subtype_map->getSubtypes(); | ||||
|   } | ||||
|  | ||||
|   private function buildProjectsNavigation($nav) { | ||||
|     $user = $this->getRequest()->getUser(); | ||||
|     if (!$this->projectKey) { | ||||
|       $nav->addLabel(pht('Projects')); | ||||
|  | ||||
|       $show_item_id = celerity_generate_unique_node_id(); | ||||
|       $hide_item_id = celerity_generate_unique_node_id(); | ||||
|  | ||||
|       $show_item = id(new PHUIListItemView()) | ||||
|         ->setName(pht('Show all projects')) | ||||
|         ->setHref('#') | ||||
|         ->addSigil('reveal-content') | ||||
|         ->setID($show_item_id); | ||||
|  | ||||
|       $hide_item = id(new PHUIListItemView()) | ||||
|         ->setName(pht('Hide inactive Projects')) | ||||
|         ->setHref('#') | ||||
|         ->setStyle('display: none') | ||||
|         ->setID($hide_item_id) | ||||
|         ->addSigil('reveal-content'); | ||||
|  | ||||
|       $nav->addMenuItem($show_item); | ||||
|       $nav->addMenuItem($hide_item); | ||||
|  | ||||
|       $projects = id(new PhabricatorProjectQuery()) | ||||
|         ->setViewer($user) | ||||
|         ->execute(); | ||||
|       $project_ids = array($hide_item_id); | ||||
|  | ||||
|       foreach ($projects as $project) { | ||||
|         if ($project->isArchived()) { | ||||
|           continue; | ||||
|         } | ||||
|         $url = 'project/' . $project->getID(); | ||||
|         $name = $project->getName(); | ||||
|         $is_hide = !in_array($name, $this->alwaysVisibleProjects); | ||||
|         if ($is_hide) { | ||||
|           $label_id = celerity_generate_unique_node_id(); | ||||
|           $project_ids[] = $label_id; | ||||
|  | ||||
|           $item = id(new PHUIListItemView()) | ||||
|             ->setName($name) | ||||
|             ->setType(PHUIListItemView::TYPE_LINK) | ||||
|             ->setKey($url) | ||||
|             ->setStyle('display: none;') | ||||
|             ->setID($label_id); | ||||
|  | ||||
|           $href = clone $nav->getBaseURI(); | ||||
|           $href->setPath(rtrim($href->getPath().$url, '/').'/'); | ||||
|           $item->setHref((string)$href); | ||||
|           $nav->addMenuItem($item); | ||||
|         } else { | ||||
|           $nav->addFilter($url, pht($name)); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       Javelin::initBehavior('phabricator-reveal-content'); | ||||
|  | ||||
|       $show_item->setMetadata( | ||||
|         array( | ||||
|           'showIDs' => $project_ids, | ||||
|           'hideIDs' => array($show_item_id), | ||||
|         )); | ||||
|       $hide_item->setMetadata( | ||||
|         array( | ||||
|           'showIDs' => array($show_item_id), | ||||
|           'hideIDs' => $project_ids, | ||||
|         )); | ||||
|     } else { | ||||
|       $project = id(new PhabricatorProjectQuery()) | ||||
|         ->setViewer($user) | ||||
|         ->withIDs(array($this->projectKey)) | ||||
|         ->executeOne(); | ||||
|       if ($project) { | ||||
|         $nav->addLabel(pht($project->getName())); | ||||
|         $menu = $nav->getMenu(); | ||||
|  | ||||
|         $url = $nav->getBaseURI() . 'project/' . $this->projectKey; | ||||
|         $link = $menu->newLink(pht('All Types'), $url, ''); | ||||
|         if (!$this->taskSubtypeKey) { | ||||
|           $link->addClass('phui-list-item-selected'); | ||||
|         } | ||||
|  | ||||
|         $task_subtypes = $this->getAvailableTaskSubtypes(); | ||||
|         foreach ($task_subtypes as $key => $subtype) { | ||||
|           $url = $nav->getBaseURI() . 'project/' . $this->projectKey . '/type/' . $key . '/'; | ||||
|           $link = $menu->newLink(pht($subtype->getName()), $url, $key); | ||||
|           if ($key == $this->taskSubtypeKey) { | ||||
|             $link->addClass('phui-list-item-selected'); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public function buildSideNavView() { | ||||
|     $viewer = $this->getViewer(); | ||||
|  | ||||
|     $nav = new AphrontSideNavFilterView(); | ||||
|     $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); | ||||
|  | ||||
|     $this->buildProjectsNavigation($nav); | ||||
|  | ||||
|     id(new ManiphestTaskSearchEngine()) | ||||
|       ->setViewer($viewer) | ||||
|       ->setProjectKey($this->projectKey) | ||||
|       ->setTaskTypeKey($this->taskSubtypeKey) | ||||
|       ->addNavigationItems($nav->getMenu()); | ||||
|  | ||||
|     if ($viewer->isLoggedIn()) { | ||||
| @@ -30,6 +149,32 @@ abstract class ManiphestController extends PhabricatorController { | ||||
|   protected function buildApplicationCrumbs() { | ||||
|     $crumbs = parent::buildApplicationCrumbs(); | ||||
|  | ||||
|     if ($this->projectKey) { | ||||
|       $user = $this->getRequest()->getUser(); | ||||
|       $project = id(new PhabricatorProjectQuery()) | ||||
|         ->setViewer($user) | ||||
|         ->withIDs(array($this->projectKey)) | ||||
|         ->executeOne(); | ||||
|       if ($project) { | ||||
|         // Special for for Blender and Addons reports. | ||||
|         $form = 1; | ||||
|         if ($this->projectKey == 3) { | ||||
|           $form = 2; | ||||
|         } | ||||
|         $crumbs->addTextCrumb(pht($project->getName()), | ||||
|                               $this->taskSubtypeKey? '/maniphest/project/'.$this->projectKey: null); | ||||
|  | ||||
|         $crumbs->addAction( | ||||
|           id(new PHUIListItemView()) | ||||
|             ->setName(pht('Report Bug')) | ||||
|             ->setHref($this->getApplicationURI('task/edit/form/' . $form . '/?project='. | ||||
|                                                $project->getPHID().'&type=Bug')) | ||||
|             ->setIcon('fa-plus-square')); | ||||
|       } else { | ||||
|         throw new Exception('Unknown project was passed via the url'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     id(new ManiphestEditEngine()) | ||||
|       ->setViewer($this->getViewer()) | ||||
|       ->addActionToCrumbs($crumbs); | ||||
|   | ||||
| @@ -0,0 +1,65 @@ | ||||
| <?php | ||||
|  | ||||
| final class ManiphestSearchController | ||||
|   extends PhabricatorApplicationSearchController { | ||||
|  | ||||
|   private $projectKey; | ||||
|   private $taskSubtypeKey; | ||||
|   private $taskSubtypes; | ||||
|  | ||||
|   protected function getDescriptionForQuery($named_query, $dao_query) { | ||||
|     $title = ''; | ||||
|  | ||||
|     if ($named_query) { | ||||
|       $title = $named_query->getQueryName(); | ||||
|     } else { | ||||
|       $title = parent::getDescriptionForQuery($named_query, $dao_query); | ||||
|       if (!$title) { | ||||
|         $title = pht('Advanced Search'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $description = pht('%s (%d)', | ||||
|         $title, | ||||
|         $dao_query->getTotalTasksCount()); | ||||
|  | ||||
|     if ($this->projectKey) { | ||||
|       $user = $this->getRequest()->getUser(); | ||||
|       $project = id(new PhabricatorProjectQuery()) | ||||
|         ->setViewer($user) | ||||
|         ->withIDs(array($this->projectKey)) | ||||
|         ->executeOne(); | ||||
|       if ($project) { | ||||
|         $description .= ' from project "'.pht($project->getName()).'"'; | ||||
|       } else { | ||||
|         throw new Exception('Unknown project'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if ($this->taskSubtypeKey) { | ||||
|       $task_subtype = idx($this->taskSubtypes, $this->taskSubtypeKey); | ||||
|       if ($task_subtype) { | ||||
|         $description .= ', task type "'.pht($task_subtype->getName()).'"'; | ||||
|       } else { | ||||
|         throw new Exception('Unknown task type'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $description; | ||||
|   } | ||||
|  | ||||
|   public function setProjectKey($projectKey) { | ||||
|     $this->projectKey = $projectKey; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setTaskSubtypes($taskSubtypes) { | ||||
|     $this->taskSubtypes = $taskSubtypes; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setTaskTypeKey($taskSubtypeKey) { | ||||
|     $this->taskSubtypeKey = $taskSubtypeKey; | ||||
|     return $this; | ||||
|   } | ||||
| } | ||||
| @@ -10,14 +10,269 @@ final class ManiphestTaskListController | ||||
|   public function handleRequest(AphrontRequest $request) { | ||||
|     $querykey = $request->getURIData('queryKey'); | ||||
|  | ||||
|     $controller = id(new PhabricatorApplicationSearchController()) | ||||
|     $controller = id(new ManiphestSearchController()) | ||||
|       ->setQueryKey($querykey) | ||||
|       ->setTaskSubtypes($this->getAvailableTaskSubtypes()) | ||||
|       ->setProjectKey($this->projectKey) | ||||
|       ->setTaskTypeKey($this->taskSubtypeKey) | ||||
|       ->setSearchEngine( | ||||
|         id(new ManiphestTaskSearchEngine()) | ||||
|           ->setShowBatchControls(true)) | ||||
|           ->setShowBatchControls(true) | ||||
|           ->setProjectKey($this->projectKey) | ||||
|           ->setTaskTypeKey($this->taskSubtypeKey)) | ||||
|       ->setNavigation($this->buildSideNavView()); | ||||
|  | ||||
|     return $this->delegateToController($controller); | ||||
|   } | ||||
|  | ||||
|   public function __renderResultsList( | ||||
|     array $tasks, | ||||
|     PhabricatorSavedQuery $query) { | ||||
|     assert_instances_of($tasks, 'ManiphestTask'); | ||||
|  | ||||
|     $viewer = $this->getRequest()->getUser(); | ||||
|  | ||||
|     // If we didn't match anything, just pick up the default empty state. | ||||
|     if (!$tasks) { | ||||
|       return id(new PHUIObjectItemListView()) | ||||
|         ->setUser($viewer); | ||||
|     } | ||||
|  | ||||
|     $group_parameter = nonempty($query->getParameter('group'), 'priority'); | ||||
|     $order_parameter = nonempty($query->getParameter('order'), 'priority'); | ||||
|  | ||||
|     $handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks); | ||||
|     $groups = $this->groupTasks( | ||||
|       $tasks, | ||||
|       $group_parameter, | ||||
|       $handles); | ||||
|  | ||||
|     $can_edit_priority = $this->hasApplicationCapability( | ||||
|       ManiphestCapabilityEditPriority::CAPABILITY); | ||||
|  | ||||
|     $can_drag = ($order_parameter == 'priority') && | ||||
|                 ($can_edit_priority) && | ||||
|                 ($group_parameter == 'none' || $group_parameter == 'priority'); | ||||
|  | ||||
|     if (!$viewer->isLoggedIn()) { | ||||
|       // TODO: (T603) Eventually, we conceivably need to make each task | ||||
|       // draggable individually, since the user may be able to edit some but | ||||
|       // not others. | ||||
|       $can_drag = false; | ||||
|     } | ||||
|  | ||||
|     $result = array(); | ||||
|  | ||||
|     $can_edit = $this->hasApplicationCapability( | ||||
|       PhabricatorPolicyCapability::CAN_EDIT); | ||||
|  | ||||
|     $lists = array(); | ||||
|     foreach ($groups as $group => $list) { | ||||
|       $task_list = new ManiphestTaskListView(); | ||||
|       $task_list->setShowBatchControls($can_edit); | ||||
|       if ($can_drag) { | ||||
|         $task_list->setShowSubpriorityControls(true); | ||||
|       } | ||||
|       $task_list->setUser($viewer); | ||||
|       $task_list->setTasks($list); | ||||
|       $task_list->setHandles($handles); | ||||
|  | ||||
|       $header = javelin_tag( | ||||
|         'h1', | ||||
|         array( | ||||
|           'class' => 'maniphest-task-group-header', | ||||
|           'sigil' => 'task-group', | ||||
|           'meta'  => array( | ||||
|             'priority' => head($list)->getPriority(), | ||||
|           ), | ||||
|         ), | ||||
|         pht('%s (%s)', $group, new PhutilNumber(count($list)))); | ||||
|  | ||||
|       $lists[] = phutil_tag( | ||||
|         'div', | ||||
|         array( | ||||
|           'class' => 'maniphest-task-group' | ||||
|         ), | ||||
|         array( | ||||
|           $header, | ||||
|           $task_list, | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     if ($can_drag) { | ||||
|       Javelin::initBehavior( | ||||
|         'maniphest-subpriority-editor', | ||||
|         array( | ||||
|           'uri'   =>  '/maniphest/subpriority/', | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     return phutil_tag( | ||||
|       'div', | ||||
|       array( | ||||
|         'class' => 'maniphest-list-container', | ||||
|       ), | ||||
|       array( | ||||
|         $lists, | ||||
|         $this->renderBatchEditor($query), | ||||
|       )); | ||||
|   } | ||||
|  | ||||
|   private function __groupTasks(array $tasks, $group, array $handles) { | ||||
|     assert_instances_of($tasks, 'ManiphestTask'); | ||||
|     assert_instances_of($handles, 'PhabricatorObjectHandle'); | ||||
|  | ||||
|     $groups = $this->getTaskGrouping($tasks, $group); | ||||
|  | ||||
|     $results = array(); | ||||
|     foreach ($groups as $label_key => $tasks) { | ||||
|       $label = $this->getTaskLabelName($group, $label_key, $handles); | ||||
|       $results[$label][] = $tasks; | ||||
|     } | ||||
|     foreach ($results as $label => $task_groups) { | ||||
|       $results[$label] = array_mergev($task_groups); | ||||
|     } | ||||
|  | ||||
|     return $results; | ||||
|   } | ||||
|  | ||||
|   private function __getTaskGrouping(array $tasks, $group) { | ||||
|     switch ($group) { | ||||
|       case 'priority': | ||||
|         return mgroup($tasks, 'getPriority'); | ||||
|       case 'status': | ||||
|         return mgroup($tasks, 'getStatus'); | ||||
|       case 'assigned': | ||||
|         return mgroup($tasks, 'getOwnerPHID'); | ||||
|       case 'project': | ||||
|         return mgroup($tasks, 'getGroupByProjectPHID'); | ||||
|       default: | ||||
|         return array(pht('Tasks') => $tasks); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private function __getTaskLabelName($group, $label_key, array $handles) { | ||||
|     switch ($group) { | ||||
|       case 'priority': | ||||
|         return ManiphestTaskPriority::getTaskPriorityName($label_key); | ||||
|       case 'status': | ||||
|         return ManiphestTaskStatus::getTaskStatusFullName($label_key); | ||||
|       case 'assigned': | ||||
|         if ($label_key) { | ||||
|           return $handles[$label_key]->getFullName(); | ||||
|         } else { | ||||
|           return pht('(Not Assigned)'); | ||||
|         } | ||||
|       case 'project': | ||||
|         if ($label_key) { | ||||
|           return $handles[$label_key]->getFullName(); | ||||
|         } else { | ||||
|           return pht('(No Project)'); | ||||
|         } | ||||
|       default: | ||||
|         return pht('Tasks'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private function ___renderBatchEditor(PhabricatorSavedQuery $saved_query) { | ||||
|     $user = $this->getRequest()->getUser(); | ||||
|  | ||||
|     $batch_capability = ManiphestCapabilityBulkEdit::CAPABILITY; | ||||
|     if (!$this->hasApplicationCapability($batch_capability)) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     if (!$user->isLoggedIn()) { | ||||
|       // Don't show the batch editor or excel export for logged-out users. | ||||
|       // Technically we //could// let them export, but ehh. | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     Javelin::initBehavior( | ||||
|       'maniphest-batch-selector', | ||||
|       array( | ||||
|         'selectAll'   => 'batch-select-all', | ||||
|         'selectNone'  => 'batch-select-none', | ||||
|         'submit'      => 'batch-select-submit', | ||||
|         'status'      => 'batch-select-status-cell', | ||||
|         'idContainer' => 'batch-select-id-container', | ||||
|         'formID'      => 'batch-select-form', | ||||
|       )); | ||||
|  | ||||
|     $select_all = javelin_tag( | ||||
|       'a', | ||||
|       array( | ||||
|         'href'        => '#', | ||||
|         'mustcapture' => true, | ||||
|         'class'       => 'grey button', | ||||
|         'id'          => 'batch-select-all', | ||||
|       ), | ||||
|       pht('Select All')); | ||||
|  | ||||
|     $select_none = javelin_tag( | ||||
|       'a', | ||||
|       array( | ||||
|         'href'        => '#', | ||||
|         'mustcapture' => true, | ||||
|         'class'       => 'grey button', | ||||
|         'id'          => 'batch-select-none', | ||||
|       ), | ||||
|       pht('Clear Selection')); | ||||
|  | ||||
|     $submit = phutil_tag( | ||||
|       'button', | ||||
|       array( | ||||
|         'id'          => 'batch-select-submit', | ||||
|         'disabled'    => 'disabled', | ||||
|         'class'       => 'disabled', | ||||
|       ), | ||||
|       pht("Batch Edit Selected \xC2\xBB")); | ||||
|  | ||||
|     $export = javelin_tag( | ||||
|       'a', | ||||
|       array( | ||||
|         'href' => '/maniphest/export/'.$saved_query->getQueryKey().'/', | ||||
|         'class' => 'grey button', | ||||
|       ), | ||||
|       pht('Export to Excel')); | ||||
|  | ||||
|     $hidden = phutil_tag( | ||||
|       'div', | ||||
|       array( | ||||
|         'id' => 'batch-select-id-container', | ||||
|       ), | ||||
|       ''); | ||||
|  | ||||
|     $editor = hsprintf( | ||||
|       '<div class="maniphest-batch-editor">'. | ||||
|         '<div class="batch-editor-header">%s</div>'. | ||||
|         '<table class="maniphest-batch-editor-layout">'. | ||||
|           '<tr>'. | ||||
|             '<td>%s%s</td>'. | ||||
|             '<td>%s</td>'. | ||||
|             '<td id="batch-select-status-cell">%s</td>'. | ||||
|             '<td class="batch-select-submit-cell">%s%s</td>'. | ||||
|           '</tr>'. | ||||
|         '</table>'. | ||||
|       '</div>', | ||||
|       pht('Batch Task Editor'), | ||||
|       $select_all, | ||||
|       $select_none, | ||||
|       $export, | ||||
|       '', | ||||
|       $submit, | ||||
|       $hidden); | ||||
|  | ||||
|     $editor = phabricator_form( | ||||
|       $user, | ||||
|       array( | ||||
|         'method' => 'POST', | ||||
|         'action' => '/maniphest/batch/', | ||||
|         'id'     => 'batch-select-form', | ||||
|       ), | ||||
|       $editor); | ||||
|  | ||||
|     return $editor; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -200,7 +200,8 @@ EODOCS | ||||
|         ->setIsNullable(true) | ||||
|         ->setSingleValue($object->getOwnerPHID()) | ||||
|         ->setCommentActionLabel(pht('Assign / Claim')) | ||||
|         ->setCommentActionValue($owner_value), | ||||
|         ->setCommentActionValue($owner_value) | ||||
|         ->setCanApplyWithoutEditCapability(true), | ||||
|       id(new PhabricatorSelectEditField()) | ||||
|         ->setKey('status') | ||||
|         ->setLabel(pht('Status')) | ||||
| @@ -213,7 +214,8 @@ EODOCS | ||||
|         ->setValue($object->getStatus()) | ||||
|         ->setOptions($status_map) | ||||
|         ->setCommentActionLabel(pht('Change Status')) | ||||
|         ->setCommentActionValue($default_status), | ||||
|         ->setCommentActionValue($default_status) | ||||
|         ->setCanApplyWithoutEditCapability(true), | ||||
|       id(new PhabricatorSelectEditField()) | ||||
|         ->setKey('priority') | ||||
|         ->setLabel(pht('Priority')) | ||||
| @@ -226,6 +228,7 @@ EODOCS | ||||
|         ->setValue($object->getPriorityKeyword()) | ||||
|         ->setOptions($priority_map) | ||||
|         ->setOptionAliases($alias_map) | ||||
|         ->setCanApplyWithoutEditCapability(true) | ||||
|         ->setCommentActionLabel(pht('Change Priority')), | ||||
|     ); | ||||
|  | ||||
| @@ -350,6 +353,18 @@ EODOCS | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       // Blender: limit task reopen to contributors. | ||||
|       // | ||||
|       // NOTE: Similar to other semantic check here we don't do it in the | ||||
|       // Conduit. | ||||
|       $viewer = $this->getViewer(); | ||||
|       if (ManiphestTaskStatus::isClosedStatus($current_status) && | ||||
|           ManiphestTaskStatus::isOpenStatus($status) && | ||||
|           $viewer->limitNonContributorUI()) { | ||||
|         unset($status_map[$status]); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       // Don't allow tasks to be changed directly into "Closed, Duplicate" | ||||
|       // status. Instead, you have to merge them. See T4819. | ||||
|       if ($status == $dup_status) { | ||||
| @@ -379,6 +394,18 @@ EODOCS | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       // Blender: limit triaging to contributors. | ||||
|       // | ||||
|       // NOTE: Similar to other semantic check here we don't do it in the | ||||
|       // Conduit. | ||||
|       $viewer = $this->getViewer(); | ||||
|       if ($priority != $current_priority) { | ||||
|         if (array_search('triage', $priority_keywords[$priority]) === false && | ||||
|             $viewer->limitNonContributorUI()) { | ||||
|           continue; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       $keyword = head(idx($priority_keywords, $priority)); | ||||
|       $results[$keyword] = $priority_name; | ||||
|     } | ||||
|   | ||||
| @@ -237,7 +237,13 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { | ||||
|     return new ManiphestTask(); | ||||
|   } | ||||
|  | ||||
|   protected function loadPage() { | ||||
|   protected function buildTaskCountSelectClause(AphrontDatabaseConnection $conn) { | ||||
|     $parts = $this->buildSelectClauseParts($conn); | ||||
|     $parts[0] = qsprintf($conn, '`task`.id'); | ||||
|     return $this->formatSelectClause($conn, $parts); | ||||
|   } | ||||
|  | ||||
|   protected function getRawQueryResults($task_dao, $count_all_tasks) { | ||||
|     $task_dao = new ManiphestTask(); | ||||
|     $conn = $task_dao->establishConnection('r'); | ||||
|  | ||||
| @@ -252,18 +258,46 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     $rows = queryfx_all( | ||||
|       $conn, | ||||
|       '%Q %Q FROM %T task %Q %Q %Q %Q %Q %Q', | ||||
|       $this->buildSelectClause($conn), | ||||
|       $group_column, | ||||
|       $task_dao->getTableName(), | ||||
|       $this->buildJoinClause($conn), | ||||
|       $where, | ||||
|       $this->buildGroupClause($conn), | ||||
|       $this->buildHavingClause($conn), | ||||
|       $this->buildOrderClause($conn), | ||||
|       $this->buildLimitClause($conn)); | ||||
|     if ($count_all_tasks) { | ||||
|       $r = queryfx_all( | ||||
|         $conn, | ||||
|         '%Q %Q FROM %T task %Q %Q %Q %Q %Q', | ||||
|         $this->buildTaskCountSelectClause($conn), | ||||
|         $group_column, | ||||
|         $task_dao->getTableName(), | ||||
|         $this->buildJoinClause($conn), | ||||
|         $where, | ||||
|         $this->buildGroupClause($conn), | ||||
|         $this->buildHavingClause($conn), | ||||
|         $this->buildOrderClause($conn)); | ||||
|       return array('count' => count($r)); | ||||
|     } else { | ||||
|       return queryfx_all( | ||||
|         $conn, | ||||
|         '%Q %Q FROM %T task %Q %Q %Q %Q %Q %Q', | ||||
|         $this->buildSelectClause($conn), | ||||
|         $group_column, | ||||
|         $task_dao->getTableName(), | ||||
|         $this->buildJoinClause($conn), | ||||
|         $where, | ||||
|         $this->buildGroupClause($conn), | ||||
|         $this->buildHavingClause($conn), | ||||
|         $this->buildOrderClause($conn), | ||||
|         $this->buildLimitClause($conn)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public function getTotalTasksCount() { | ||||
|     $task_dao = new ManiphestTask(); | ||||
|  | ||||
|     $row = $this->getRawQueryResults($task_dao, true); | ||||
|     return $row['count']; | ||||
|   } | ||||
|  | ||||
|   protected function loadPage() { | ||||
|     $task_dao = new ManiphestTask(); | ||||
|  | ||||
|     $rows = $this->getRawQueryResults($task_dao, false); | ||||
|  | ||||
|     switch ($this->groupBy) { | ||||
|       case self::GROUP_PROJECT: | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
| final class ManiphestTaskSearchEngine | ||||
|   extends PhabricatorApplicationSearchEngine { | ||||
|  | ||||
|   private $projectKey; | ||||
|   private $taskSubtypeKey; | ||||
|   private $showBatchControls; | ||||
|   private $baseURI; | ||||
|   private $isBoardView; | ||||
| @@ -206,7 +208,9 @@ final class ManiphestTaskSearchEngine | ||||
|       $query->withPriorities($map['priorities']); | ||||
|     } | ||||
|  | ||||
|     if ($map['subtypes']) { | ||||
|     if ($this->taskSubtypeKey) { | ||||
|       $query->withSubtypes(array($this->taskSubtypeKey)); | ||||
|     } else if ($map['subtypes']) { | ||||
|       $query->withSubtypes($map['subtypes']); | ||||
|     } | ||||
|  | ||||
| @@ -250,6 +254,18 @@ final class ManiphestTaskSearchEngine | ||||
|       $query->withSubtaskIDs($map['subtaskIDs']); | ||||
|     } | ||||
|  | ||||
|     if ($this->projectKey) { | ||||
|       $project = id(new PhabricatorProjectQuery()) | ||||
|         ->setViewer($this->requireViewer()) | ||||
|         ->withIDs(array($this->projectKey)) | ||||
|         ->executeOne(); | ||||
|  | ||||
|       $query->withEdgeLogicPHIDs( | ||||
|         PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, | ||||
|         PhabricatorQueryConstraint::OPERATOR_AND, | ||||
|         array($project->getPHID())); | ||||
|     } | ||||
|  | ||||
|     if ($map['columnPHIDs']) { | ||||
|       $query->withColumnPHIDs($map['columnPHIDs']); | ||||
|     } | ||||
| @@ -281,23 +297,30 @@ final class ManiphestTaskSearchEngine | ||||
|  | ||||
|   protected function getURI($path) { | ||||
|     if ($this->baseURI) { | ||||
|       return $this->baseURI.$path; | ||||
|       $url = $this->baseURI; | ||||
|     } else { | ||||
|       $url = '/maniphest/'; | ||||
|     } | ||||
|     return '/maniphest/'.$path; | ||||
|     if ($this->projectKey) | ||||
|       $url .= 'project/' . $this->projectKey . '/'; | ||||
|     if ($this->taskSubtypeKey) | ||||
|       $url .= 'type/' . $this->taskSubtypeKey . '/'; | ||||
|     $url = str_replace(" ", "%20", $url); | ||||
|     return $url . $path; | ||||
|   } | ||||
|  | ||||
|   protected function getBuiltinQueryNames() { | ||||
|     $names = array(); | ||||
|  | ||||
|     $names['open'] = pht('Open Tasks'); | ||||
|     $names['all'] = pht('All Tasks'); | ||||
|  | ||||
|     if ($this->requireViewer()->isLoggedIn()) { | ||||
|       $names['assigned'] = pht('Assigned'); | ||||
|       $names['authored'] = pht('Authored'); | ||||
|       $names['subscribed'] = pht('Subscribed'); | ||||
|     } | ||||
|  | ||||
|     $names['open'] = pht('Open Tasks'); | ||||
|     $names['all'] = pht('All Tasks'); | ||||
|  | ||||
|     return $names; | ||||
|   } | ||||
|  | ||||
| @@ -310,7 +333,9 @@ final class ManiphestTaskSearchEngine | ||||
|  | ||||
|     switch ($query_key) { | ||||
|       case 'all': | ||||
|         return $query; | ||||
|         return $query | ||||
|           ->setParameter('order', 'created') | ||||
|           ->setParameter('group', 'none'); | ||||
|       case 'assigned': | ||||
|         return $query | ||||
|           ->setParameter('assignedPHIDs', array($viewer_phid)) | ||||
| @@ -358,6 +383,16 @@ final class ManiphestTaskSearchEngine | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   function setProjectKey($projectKey) { | ||||
|     $this->projectKey = $projectKey; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   function setTaskTypeKey($taskSubtypeKey) { | ||||
|     $this->taskSubtypeKey = $taskSubtypeKey; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   protected function renderResultList( | ||||
|     array $tasks, | ||||
|     PhabricatorSavedQuery $saved, | ||||
|   | ||||
| @@ -35,6 +35,7 @@ final class ManiphestTaskListView extends ManiphestView { | ||||
|     require_celerity_resource('maniphest-task-summary-css'); | ||||
|  | ||||
|     $list = new PHUIObjectItemListView(); | ||||
|     $list->addClass('maniphest-task-group'); | ||||
|  | ||||
|     if ($this->noDataString) { | ||||
|       $list->setNoDataString($this->noDataString); | ||||
| @@ -58,6 +59,11 @@ final class ManiphestTaskListView extends ManiphestView { | ||||
|         ->setHeader($task->getTitle()) | ||||
|         ->setHref('/T'.$task->getID()); | ||||
|  | ||||
|       if ($task->getAuthorPHID()) { | ||||
|         $author = $handles[$task->getAuthorPHID()]; | ||||
|         $item->addByline(pht('By: %s', $author->renderLink())); | ||||
|       } | ||||
|  | ||||
|       if ($task->getOwnerPHID()) { | ||||
|         $owner = $handles[$task->getOwnerPHID()]; | ||||
|         $item->addByline(pht('Assigned: %s', $owner->renderLink())); | ||||
| @@ -149,6 +155,10 @@ final class ManiphestTaskListView extends ManiphestView { | ||||
|       if ($assigned_phid) { | ||||
|         $phids[] = $assigned_phid; | ||||
|       } | ||||
|       $author_phid = $task->getAuthorPHID(); | ||||
|       if ($author_phid) { | ||||
|         $phids[] = $author_phid; | ||||
|       } | ||||
|       foreach ($task->getProjectPHIDs() as $project_phid) { | ||||
|         $phids[] = $project_phid; | ||||
|       } | ||||
|   | ||||
| @@ -165,4 +165,10 @@ final class ManiphestTaskOwnerTransaction | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getRequiredCapabilities( | ||||
|     $object, | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|     return PhabricatorPolicyCapability::CAN_INTERACT; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -201,4 +201,10 @@ final class ManiphestTaskPriorityTransaction | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getRequiredCapabilities( | ||||
|     $object, | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|     return PhabricatorPolicyCapability::CAN_INTERACT; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -237,4 +237,10 @@ final class ManiphestTaskStatusTransaction | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getRequiredCapabilities( | ||||
|     $object, | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|     return PhabricatorPolicyCapability::CAN_INTERACT; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -141,12 +141,14 @@ final class PhabricatorMailTarget extends Phobject { | ||||
|     $body = ''; | ||||
|  | ||||
|     if ($to_handles) { | ||||
|       $to_names = mpull($to_handles, 'getCommandLineObjectName'); | ||||
|       // $to_names = mpull($to_handles, 'getCommandLineObjectName'); | ||||
|       $to_names = mpull($to_handles, 'getFullName'); | ||||
|       $body .= "To: ".implode(', ', $to_names)."\n"; | ||||
|     } | ||||
|  | ||||
|     if ($cc_handles) { | ||||
|       $cc_names = mpull($cc_handles, 'getCommandLineObjectName'); | ||||
|       // $cc_names = mpull($cc_handles, 'getCommandLineObjectName'); | ||||
|       $cc_names = mpull($cc_handles, 'getFullName'); | ||||
|       $body .= "Cc: ".implode(', ', $cc_names)."\n"; | ||||
|     } | ||||
|  | ||||
| @@ -167,12 +169,12 @@ final class PhabricatorMailTarget extends Phobject { | ||||
|     $body = array(); | ||||
|     if ($to_handles) { | ||||
|       $body[] = phutil_tag('strong', array(), 'To: '); | ||||
|       $body[] = phutil_implode_html(', ', mpull($to_handles, 'getName')); | ||||
|       $body[] = phutil_implode_html(', ', mpull($to_handles, 'getFullName')); | ||||
|       $body[] = phutil_tag('br'); | ||||
|     } | ||||
|     if ($cc_handles) { | ||||
|       $body[] = phutil_tag('strong', array(), 'Cc: '); | ||||
|       $body[] = phutil_implode_html(', ', mpull($cc_handles, 'getName')); | ||||
|       $body[] = phutil_implode_html(', ', mpull($cc_handles, 'getFullName')); | ||||
|       $body[] = phutil_tag('br'); | ||||
|     } | ||||
|     return phutil_tag('div', array(), $body); | ||||
|   | ||||
| @@ -809,7 +809,6 @@ final class PhabricatorMetaMTAMail | ||||
|       $exceptions); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   public static function shouldMailEachRecipient() { | ||||
|     return PhabricatorEnv::getEnvConfig('metamta.one-mail-per-recipient'); | ||||
|   } | ||||
|   | ||||
| @@ -58,6 +58,7 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication { | ||||
|           => 'PhabricatorPeopleDisableController', | ||||
|         '(?P<via>disable)/(?P<id>[1-9]\d*)/' | ||||
|           => 'PhabricatorPeopleDisableController', | ||||
|         'spam/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleSpamController', | ||||
|         'empower/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleEmpowerController', | ||||
|         'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleDeleteController', | ||||
|         'rename/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleRenameController', | ||||
| @@ -99,6 +100,9 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication { | ||||
|       PeopleDisableUsersCapability::CAPABILITY => array( | ||||
|         'default' => PhabricatorPolicies::POLICY_ADMIN, | ||||
|       ), | ||||
|       PeopleDisableSpamUsersCapability::CAPABILITY => array( | ||||
|         'default' => PhabricatorPolicies::POLICY_ADMIN, | ||||
|       ), | ||||
|       PeopleBrowseUserDirectoryCapability::CAPABILITY => array(), | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -0,0 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| final class PeopleDisableSpamUsersCapability | ||||
|   extends PhabricatorPolicyCapability { | ||||
|  | ||||
|   const CAPABILITY = 'people.disable_spam.users'; | ||||
|  | ||||
|   public function getCapabilityName() { | ||||
|     return pht('Can Disable Spammers'); | ||||
|   } | ||||
|  | ||||
|   public function describeCapabilityRejection() { | ||||
|     return pht('You do not have permission to handle spam users.'); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -29,7 +29,7 @@ final class PhabricatorPeopleDeleteController | ||||
|       ->appendCommand( | ||||
|         csprintf( | ||||
|           'phabricator/ $ ./bin/remove destroy %R', | ||||
|           $user->getMonogram())) | ||||
|           $user->getMonogramForCommand())) | ||||
|       ->appendParagraph( | ||||
|         pht( | ||||
|           'Unless you have a very good reason to delete this user, consider '. | ||||
|   | ||||
| @@ -93,6 +93,10 @@ final class PhabricatorPeopleProfileManageController | ||||
|       PeopleDisableUsersCapability::CAPABILITY); | ||||
|     $can_disable = ($has_disable && !$is_self); | ||||
|  | ||||
|     $has_disable_spam_capability = $this->hasApplicationCapability( | ||||
|       PeopleDisableSpamUsersCapability::CAPABILITY); | ||||
|     $can_disable_spam = ($has_disable_spam_capability && !$is_self); | ||||
|  | ||||
|     $id = $user->getID(); | ||||
|  | ||||
|     $welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine()) | ||||
| @@ -193,6 +197,14 @@ final class PhabricatorPeopleProfileManageController | ||||
|         ->setWorkflow(true) | ||||
|         ->setHref($this->getApplicationURI('disable/'.$id.'/'))); | ||||
|  | ||||
|     $curtain->addAction( | ||||
|       id(new PhabricatorActionView()) | ||||
|         ->setIcon('fa-shield') | ||||
|         ->setName(pht('Disable as Spam')) | ||||
|         ->setDisabled(!$can_disable_spam) | ||||
|         ->setWorkflow(true) | ||||
|         ->setHref($this->getApplicationURI('spam/'.$id.'/'))); | ||||
|  | ||||
|     $curtain->addAction( | ||||
|       id(new PhabricatorActionView()) | ||||
|         ->setIcon('fa-times') | ||||
|   | ||||
| @@ -0,0 +1,90 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorPeopleSpamController | ||||
|   extends PhabricatorPeopleController { | ||||
|  | ||||
|   public function shouldRequireAdmin() { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   public function handleRequest(AphrontRequest $request) { | ||||
|     $viewer = $this->getViewer(); | ||||
|     $id = $request->getURIData('id'); | ||||
|  | ||||
|     $user = id(new PhabricatorPeopleQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withIDs(array($id)) | ||||
|       ->executeOne(); | ||||
|     if (!$user) { | ||||
|       return new Aphront404Response(); | ||||
|     } | ||||
|  | ||||
|     $this->requireApplicationCapability( | ||||
|       PeopleDisableUsersCapability::CAPABILITY); | ||||
|  | ||||
|     $actor = $viewer; | ||||
|     $done_uri = $this->getApplicationURI("manage/{$id}/"); | ||||
|  | ||||
|     if ($viewer->getPHID() == $user->getPHID()) { | ||||
|       return $this->newDialog() | ||||
|         ->setTitle(pht('Something Stays Your Hand')) | ||||
|         ->appendParagraph( | ||||
|           pht( | ||||
|             'Try as you might, you find you can not disable your own account.')) | ||||
|         ->addCancelButton($done_uri, pht('Curses!')); | ||||
|     } | ||||
|  | ||||
|     if ($request->isFormPost()) { | ||||
|       // Disable the account. | ||||
|       if (!$user->getIsDisabled()) { | ||||
|         $xactions = array(); | ||||
|         $xactions[] = id(new PhabricatorUserTransaction()) | ||||
|           ->setTransactionType(PhabricatorUserDisableTransaction::TRANSACTIONTYPE) | ||||
|           ->setNewValue(true); | ||||
|  | ||||
|         id(new PhabricatorUserTransactionEditor()) | ||||
|           ->setActor($actor) | ||||
|           ->setActingAsPHID($viewer->getPHID()) | ||||
|           ->setContentSourceFromRequest($request) | ||||
|           ->setContinueOnMissingFields(true) | ||||
|           ->setContinueOnNoEffect(true) | ||||
|           ->applyTransactions($user, $xactions); | ||||
|       } | ||||
|  | ||||
|       // Set profile info to spam and blank to everything else. | ||||
|       { | ||||
|         $xactions = array(); | ||||
|         $xactions[] = id(new PhabricatorUserTransaction()) | ||||
|         ->setTransactionType(PhabricatorUserDisableSpamTransaction::TRANSACTIONTYPE); | ||||
|  | ||||
|         id(new PhabricatorUserTransactionEditor()) | ||||
|         ->setActor($actor) | ||||
|         ->setActingAsPHID($viewer->getPHID()) | ||||
|         ->setContentSourceFromRequest($request) | ||||
|         ->setContinueOnMissingFields(true) | ||||
|         ->setContinueOnNoEffect(true) | ||||
|         ->applyTransactions($user, $xactions); | ||||
|       } | ||||
|  | ||||
|       return id(new AphrontRedirectResponse())->setURI($done_uri); | ||||
|     } | ||||
|  | ||||
|     $title = pht('Disable as Spam?'); | ||||
|     $short_title = pht('Disable as Spam'); | ||||
|  | ||||
|     $body = pht( | ||||
|       'Is %s\'s profile spam?<br />All the profile info will be erased and '. | ||||
|       'they will no longer be able to access Phabricator.', | ||||
|       phutil_tag('strong', array(), $user->getUsername())); | ||||
|  | ||||
|     $submit = pht('Disable as Spam'); | ||||
|  | ||||
|     return $this->newDialog() | ||||
|       ->setTitle($title) | ||||
|       ->setShortTitle($short_title) | ||||
|       ->appendParagraph($body) | ||||
|       ->addCancelButton($done_uri) | ||||
|       ->addSubmitButton($submit); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -134,7 +134,7 @@ final class PhabricatorMentionRemarkupRule extends PhutilRemarkupRule { | ||||
|                 font-weight: bold; | ||||
|                 padding: 0 4px;', | ||||
|             ), | ||||
|             '@'.$user->getUserName()); | ||||
|             '@'.$user->getFullName()); | ||||
|         } else { | ||||
|           if ($engine->getConfig('uri.full')) { | ||||
|             $user_href = PhabricatorEnv::getURI($user_href); | ||||
| @@ -143,7 +143,7 @@ final class PhabricatorMentionRemarkupRule extends PhutilRemarkupRule { | ||||
|           $tag = id(new PHUITagView()) | ||||
|             ->setType(PHUITagView::TYPE_PERSON) | ||||
|             ->setPHID($user->getPHID()) | ||||
|             ->setName('@'.$user->getUserName()) | ||||
|             ->setName('@'.$user->getFullName()) | ||||
|             ->setHref($user_href); | ||||
|  | ||||
|           if ($user_has_no_permission) { | ||||
|   | ||||
| @@ -258,10 +258,84 @@ final class PhabricatorUser | ||||
|       PhabricatorPeopleUserPHIDType::TYPECONST); | ||||
|   } | ||||
|  | ||||
|   public function getMonogram() { | ||||
|     return '@'.$this->getUsername(); | ||||
|   // Functions generateRadomNumber, generateChar, generateSalt and | ||||
|   // generateHtaccessPassword are adopted from FusionForge sources, | ||||
|   // which is licensed by GNU GPL license. | ||||
|   private function generateRadomNumber() { | ||||
|     mt_srand((double)microtime() * 1000000); | ||||
|     $num = mt_rand(46,122); | ||||
|     return $num; | ||||
|   } | ||||
|  | ||||
|   private function generateChar() { | ||||
|     do { | ||||
|       $num = $this->generateRadomNumber(); | ||||
|     } while ( ( $num > 57 && $num < 65 ) || ( $num > 90 && $num < 97 ) ); | ||||
|     $char = chr($num); | ||||
|     return $char; | ||||
|   } | ||||
|  | ||||
|   private function generateSalt() { | ||||
|     $a = $this->generateChar(); | ||||
|     $b = $this->generateChar(); | ||||
|     $salt = "$1$" . "$a$b"; | ||||
|     return $salt; | ||||
|   } | ||||
|  | ||||
|   private function generateHtaccessPassword($plainpw) { | ||||
|     return crypt($plainpw, $this->generateSalt()); | ||||
|   } | ||||
|  | ||||
|   public function updateHtaccessPassword(PhutilOpaqueEnvelope $password) { | ||||
|     // Get custom fields list. | ||||
|     $field_list = PhabricatorCustomField::getObjectFields( | ||||
|         $this, | ||||
|         PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS); | ||||
|     $field_list | ||||
|         ->setViewer($this) | ||||
|         ->readFieldsFromStorage($this); | ||||
|     $fields = $field_list->getFields(); | ||||
|     // Generate new hash for htaccess password. | ||||
|     $htaccess_hash = $this->generateHtaccessPassword($password->openEnvelope()); | ||||
|     // Generate transaction to update custom field. | ||||
|     $xactions = array(); | ||||
|     $xaction = $this->getCustomFieldAction( | ||||
|         $fields, | ||||
|         'std:user:htaccess_password_hash', | ||||
|         $htaccess_hash); | ||||
|     if ($xaction) { | ||||
|       $xactions[] = $xaction; | ||||
|     } | ||||
|     // Apply transactions. | ||||
|     if (count($xactions)) { | ||||
|       $editor = id(new PhabricatorUserTransactionEditor()) | ||||
|         ->setActor($this) | ||||
|         ->setContentSource($xactions[0]->getContentSource()); | ||||
|       $editor->applyTransactions($this, $xactions); | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   private function getCustomFieldAction($fields, $field_name, $value) { | ||||
|     $field = idx($fields, $field_name); | ||||
|     if (!$field) | ||||
|       return null; | ||||
|     $old_value = $field->getOldValueForApplicationTransactions(); | ||||
|     return id(new PhabricatorUserTransaction()) | ||||
|       ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) | ||||
|       ->setMetadataValue('customfield:key', $field->getFieldKey()) | ||||
|       ->setOldValue($old_value) | ||||
|       ->setNewValue($value); | ||||
|   } | ||||
|  | ||||
|   public function getMonogram() { | ||||
|     return '@'.$this->getFullname(); | ||||
|   } | ||||
|  | ||||
|    public function getMonogramForCommand() { | ||||
|     return '@'.$this->getUsername(); | ||||
|    } | ||||
|  | ||||
|   public function isLoggedIn() { | ||||
|     return !($this->getPHID() === null); | ||||
|   } | ||||
| @@ -555,7 +629,7 @@ final class PhabricatorUser | ||||
|  | ||||
|   public function getFullName() { | ||||
|     if (strlen($this->getRealName())) { | ||||
|       return $this->getUsername().' ('.$this->getRealName().')'; | ||||
|       return $this->getRealName().' ('.$this->getUsername().')'; | ||||
|     } else { | ||||
|       return $this->getUsername(); | ||||
|     } | ||||
| @@ -1016,7 +1090,7 @@ final class PhabricatorUser | ||||
|         if ($this->getIsSystemAgent() || $this->getIsMailingList()) { | ||||
|           return PhabricatorPolicies::POLICY_ADMIN; | ||||
|         } else { | ||||
|           return PhabricatorPolicies::POLICY_NOONE; | ||||
|           return PhabricatorPolicies::POLICY_ADMIN; | ||||
|         } | ||||
|     } | ||||
|   } | ||||
| @@ -1420,7 +1494,7 @@ final class PhabricatorUser | ||||
|  | ||||
|           $parts = array( | ||||
|             $this->getUsername(), | ||||
|             $envelope->openEnvelope(), | ||||
|             md5($envelope->openEnvelope()), | ||||
|             $this->getPHID(), | ||||
|             $password->getPasswordSalt(), | ||||
|           ); | ||||
| @@ -1458,5 +1532,19 @@ final class PhabricatorUser | ||||
|     return $list; | ||||
|   } | ||||
|  | ||||
|   // Blender: Limit certain workflow steps to regular contributors, making it | ||||
|   // easier to follow status of the tracker. | ||||
|   public function limitNonContributorUI() { | ||||
|     if ($this->getIsAdmin()) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     $project = id(new PhabricatorProjectQuery()) | ||||
|       ->setViewer($this) | ||||
|       ->withNames(array('Moderators')) | ||||
|       ->withMemberPHIDs(array($this->getPHID())) | ||||
|       ->executeOne(); | ||||
|  | ||||
|     return is_null($project); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorUserDisableSpamTransaction | ||||
|   extends PhabricatorUserTransactionType { | ||||
|  | ||||
|   const TRANSACTIONTYPE = 'user.disable_spam'; | ||||
|  | ||||
|   public function generateOldValue($object) { | ||||
|     $user = $object; | ||||
|     return (string)$user->getRealName(); | ||||
|   } | ||||
|  | ||||
|   public function generateNewValue($object, $value) { | ||||
|     // Deliberately not using 'spam'. | ||||
|     // This way we can use this button even for accounts that | ||||
|     // have been already manually renamed to 'spam'. | ||||
|     // Otherwise when the name clash with the existing name | ||||
|     // none of the changes happens. | ||||
|     return 'disabled_spam'; | ||||
|   } | ||||
|  | ||||
|   public function applyInternalEffects($object, $value) { | ||||
|     $user = $object; | ||||
|     $user->setRealName('spam'); | ||||
|     $profile = $user->loadUserProfile(); | ||||
|     $profile->setBlurb(''); | ||||
|     $profile->setTitle(''); | ||||
|     $profile->setIcon(''); | ||||
|  | ||||
|     $file = PhabricatorFile::loadBuiltin($user, 'profile.png'); | ||||
|     $user->setProfileImagePHID($file->getPHID()); | ||||
|   } | ||||
|  | ||||
|   public function getTitle() { | ||||
|     return pht( | ||||
|       '%s set this user as spam.', | ||||
|       $this->renderAuthor()); | ||||
|   } | ||||
|  | ||||
|   public function shouldHideForFeed() { | ||||
|     // Don't publish feed stories about handling spam. | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function validateTransactions($object, array $xactions) { | ||||
|     $errors = array(); | ||||
|  | ||||
|     foreach ($xactions as $xaction) { | ||||
|  | ||||
|       // You must have the "Can Disable Spam Users" permission to disable a user as spam. | ||||
|       $this->requireApplicationCapability( | ||||
|         PeopleDisableSpamUsersCapability::CAPABILITY); | ||||
|  | ||||
|       if ($this->getActingAsPHID() === $object->getPHID()) { | ||||
|         $errors[] = $this->newInvalidError( | ||||
|           pht('You can not disable your own account as spam.')); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $errors; | ||||
|   } | ||||
|  | ||||
|   public function getRequiredCapabilities( | ||||
|     $object, | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| @@ -374,7 +374,7 @@ final class PhabricatorObjectHandle | ||||
|   public function getLinkName() { | ||||
|     switch ($this->getType()) { | ||||
|       case PhabricatorPeopleUserPHIDType::TYPECONST: | ||||
|         $name = $this->getName(); | ||||
|         $name = $this->getFullName(); | ||||
|         break; | ||||
|       default: | ||||
|         $name = $this->getFullName(); | ||||
|   | ||||
| @@ -56,7 +56,7 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { | ||||
|         'profile/(?P<id>[1-9]\d*)/' | ||||
|           => 'PhabricatorProjectProfileController', | ||||
|         'view/(?P<id>[1-9]\d*)/' | ||||
|           => 'PhabricatorProjectViewController', | ||||
|           => 'PhabricatorProjectProfileController', | ||||
|         'picture/(?P<id>[1-9]\d*)/' | ||||
|           => 'PhabricatorProjectEditPictureController', | ||||
|         $this->getEditRoutePattern('edit/') | ||||
| @@ -125,7 +125,7 @@ final class PhabricatorProjectApplication extends PhabricatorApplication { | ||||
|           => 'PhabricatorProjectSubprojectWarningController', | ||||
|       ), | ||||
|       '/tag/' => array( | ||||
|         '(?P<slug>[^/]+)/' => 'PhabricatorProjectViewController', | ||||
|         '(?P<slug>[^/]+)/' => 'PhabricatorProjectProfileController', | ||||
|         '(?P<slug>[^/]+)/board/' => 'PhabricatorProjectBoardViewController', | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -178,8 +178,10 @@ final class PhabricatorProjectBoardViewController | ||||
|         $panel->addClass('project-panel-hidden'); | ||||
|       } | ||||
|  | ||||
|       $column_menu = $this->buildColumnMenu($project, $column); | ||||
|       $panel->addHeaderAction($column_menu); | ||||
|       if (!$viewer->limitNonContributorUI()) { | ||||
|         $column_menu = $this->buildColumnMenu($project, $column); | ||||
|         $panel->addHeaderAction($column_menu); | ||||
|       } | ||||
|  | ||||
|       if ($column->canHaveTrigger()) { | ||||
|         $trigger = $column->getTrigger(); | ||||
|   | ||||
| @@ -37,7 +37,7 @@ final class PhabricatorProjectProjectPHIDType extends PhabricatorPHIDType { | ||||
|     foreach ($handles as $phid => $handle) { | ||||
|       $project = $objects[$phid]; | ||||
|  | ||||
|       $name = $project->getDisplayName(); | ||||
|       $name = $project->getDisplayNameWithAncestorPath(); | ||||
|       $id = $project->getID(); | ||||
|       $slug = $project->getPrimarySlug(); | ||||
|  | ||||
|   | ||||
| @@ -224,6 +224,8 @@ final class PhabricatorProjectSearchEngine | ||||
|   protected function getBuiltinQueryNames() { | ||||
|     $names = array(); | ||||
|  | ||||
|     $names['active'] = pht('Active'); | ||||
|  | ||||
|     if ($this->requireViewer()->isLoggedIn()) { | ||||
|       $names['joined'] = pht('Joined'); | ||||
|     } | ||||
| @@ -232,7 +234,6 @@ final class PhabricatorProjectSearchEngine | ||||
|       $names['watching'] = pht('Watching'); | ||||
|     } | ||||
|  | ||||
|     $names['active'] = pht('Active'); | ||||
|     $names['all'] = pht('All'); | ||||
|  | ||||
|     return $names; | ||||
|   | ||||
| @@ -586,6 +586,27 @@ final class PhabricatorProject extends PhabricatorProjectDAO | ||||
|     return $name; | ||||
|   } | ||||
|  | ||||
|   public function getDisplayNameWithAncestorPath() { | ||||
|       // Figure out the ancestors for this project | ||||
|       // so that we can prepend that to the display name. | ||||
|       $ancestors = mpull(array_reverse($this->getAncestorProjects()), 'getName'); | ||||
|       $project_name_with_path =  $this->getName(); | ||||
|  | ||||
|       if ($this->isMilestone()) { | ||||
|         $project_name_with_path = pht( | ||||
|           '%s (%s)', | ||||
|           implode(' > ', $ancestors), | ||||
|           $project_name_with_path); | ||||
|       } else { | ||||
|         $ancestors[] = $project_name_with_path; | ||||
|         $project_name_with_path = implode(' > ', $ancestors); | ||||
|       } | ||||
|  | ||||
|  | ||||
|       // No pht usage, since we don't have translatable items. | ||||
|       return $project_name_with_path; | ||||
|   } | ||||
|  | ||||
|   public function getDisplayIconKey() { | ||||
|     if ($this->isMilestone()) { | ||||
|       $key = PhabricatorProjectIconSet::getMilestoneIconKey(); | ||||
|   | ||||
| @@ -123,7 +123,7 @@ final class PhabricatorProjectDatasource | ||||
|  | ||||
|       $proj_result = id(new PhabricatorTypeaheadResult()) | ||||
|         ->setName($all_strings) | ||||
|         ->setDisplayName($proj->getDisplayName()) | ||||
|         ->setDisplayName($proj->getDisplayNameWithAncestorPath()) | ||||
|         ->setDisplayType($proj->getDisplayIconName()) | ||||
|         ->setURI($proj->getURI()) | ||||
|         ->setPHID($phid) | ||||
|   | ||||
| @@ -162,14 +162,14 @@ final class PhabricatorRepositoryQuery | ||||
|  | ||||
|   public function getBuiltinOrders() { | ||||
|     return array( | ||||
|       'committed' => array( | ||||
|         'vector' => array('committed', 'id'), | ||||
|         'name' => pht('Most Recent Commit'), | ||||
|       ), | ||||
|       'name' => array( | ||||
|         'vector' => array('name', 'id'), | ||||
|         'name' => pht('Name'), | ||||
|       ), | ||||
|       'committed' => array( | ||||
|         'vector' => array('committed', 'id'), | ||||
|         'name' => pht('Most Recent Commit'), | ||||
|       ), | ||||
|       'callsign' => array( | ||||
|         'vector' => array('callsign'), | ||||
|         'name' => pht('Callsign'), | ||||
|   | ||||
| @@ -140,6 +140,24 @@ final class PhabricatorRepositorySearchEngine | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   private function getOrderOptions() { | ||||
|     return array( | ||||
|       'name' => pht('Name'), | ||||
|       'committed' => pht('Most Recent Commit'), | ||||
|       'callsign' => pht('Callsign'), | ||||
|       'created' => pht('Date Created'), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   private function getOrderValues() { | ||||
|     return array( | ||||
|       'name' => PhabricatorRepositoryQuery::ORDER_NAME, | ||||
|       'committed' => PhabricatorRepositoryQuery::ORDER_COMMITTED, | ||||
|       'callsign' => PhabricatorRepositoryQuery::ORDER_CALLSIGN, | ||||
|       'created' => PhabricatorRepositoryQuery::ORDER_CREATED, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   private function getHostedOptions() { | ||||
|     return array( | ||||
|       '' => pht('Hosted and Remote Repositories'), | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorApplicationSearchController | ||||
| /*final*/ class PhabricatorApplicationSearchController | ||||
|   extends PhabricatorSearchBaseController { | ||||
|  | ||||
|   private $searchEngine; | ||||
| @@ -92,6 +92,10 @@ final class PhabricatorApplicationSearchController | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected function getDescriptionForQuery($named_query, $dao_query) { | ||||
|     return ''; | ||||
|   } | ||||
|  | ||||
|   private function processSearchRequest() { | ||||
|     $parent = $this->getDelegatingController(); | ||||
|     $request = $this->getRequest(); | ||||
| @@ -214,6 +218,7 @@ final class PhabricatorApplicationSearchController | ||||
|       $title = pht('Advanced Search'); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     $header = id(new PHUIHeaderView()) | ||||
|       ->setHeader($title) | ||||
|       ->setProfileHeader(true); | ||||
| @@ -255,6 +260,13 @@ final class PhabricatorApplicationSearchController | ||||
|  | ||||
|         $objects = $engine->executeQuery($query, $pager); | ||||
|  | ||||
|         $description_query = $engine->buildQueryFromSavedQuery($saved_query); | ||||
|         $description_query->setViewer($this->getRequest()->getUser()); | ||||
|         $description = $this->getDescriptionForQuery($named_query, $description_query); | ||||
|         if ($description) { | ||||
|           $header->setHeader($description); | ||||
|         } | ||||
|  | ||||
|         $force_nux = $request->getBool('nux'); | ||||
|         if (!$objects || $force_nux) { | ||||
|           $nux_view = $this->renderNewUserView($engine, $force_nux); | ||||
|   | ||||
| @@ -95,7 +95,7 @@ final class PhabricatorSettingsMainController | ||||
|  | ||||
|     $this->preferences = $preferences; | ||||
|  | ||||
|     $panels = $this->buildPanels($preferences); | ||||
|     $panels = $this->buildPanels($preferences, true); | ||||
|     $nav = $this->renderSideNav($panels); | ||||
|  | ||||
|     $key = $nav->selectFilter($key, head($panels)->getPanelKey()); | ||||
| @@ -136,7 +136,7 @@ final class PhabricatorSettingsMainController | ||||
|       ->appendChild($view); | ||||
|   } | ||||
|  | ||||
|   private function buildPanels(PhabricatorUserPreferences $preferences) { | ||||
|   private function buildPanels(PhabricatorUserPreferences $preferences, $hide_disabled) { | ||||
|     $viewer = $this->getViewer(); | ||||
|     $panels = PhabricatorSettingsPanel::getAllDisplayPanels(); | ||||
|  | ||||
| @@ -154,6 +154,10 @@ final class PhabricatorSettingsMainController | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       if ($hide_disabled && !$panel->isEnabledForUser($viewer)) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       if ($this->isTemplate()) { | ||||
|         if (!$panel->isTemplatePanel()) { | ||||
|           continue; | ||||
| @@ -221,7 +225,7 @@ final class PhabricatorSettingsMainController | ||||
|  | ||||
|   public function buildApplicationMenu() { | ||||
|     if ($this->preferences) { | ||||
|       $panels = $this->buildPanels($this->preferences); | ||||
|       $panels = $this->buildPanels($this->preferences, false); | ||||
|       return $this->renderSideNav($panels)->getMenu(); | ||||
|     } | ||||
|     return parent::buildApplicationMenu(); | ||||
|   | ||||
| @@ -163,6 +163,9 @@ abstract class PhabricatorSettingsPanel extends Phobject { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function isEnabledForUser($user) { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Return true if this panel is available to users while editing their own | ||||
|   | ||||
| @@ -0,0 +1,99 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorSettingsPanelChangeUsername | ||||
|   extends PhabricatorSettingsPanel { | ||||
|  | ||||
|   public function getPanelKey() { | ||||
|     return 'changeusername'; | ||||
|   } | ||||
|  | ||||
|   public function getPanelGroupKey() { | ||||
|     return PhabricatorSettingsAccountPanelGroup::PANELGROUPKEY; | ||||
|   } | ||||
|  | ||||
|   public function getPanelName() { | ||||
|     return pht('Change Username'); | ||||
|   } | ||||
|  | ||||
|   public function getAlternatives($user) { | ||||
|     $root = dirname(phutil_get_library_root('phabricator')); | ||||
|     require $root.'/migration/dedup.php'; | ||||
|  | ||||
|     $alternatives = array(); | ||||
|  | ||||
|     foreach($migrate_dedup_users as $from_user => $to_user) { | ||||
|       if($from_user == $user->getUserName()) { | ||||
|         $alternatives[$to_user] = $to_user; | ||||
|       } | ||||
|       else if($to_user == $user->getUserName()) { | ||||
|         $alternatives[$from_user] = $from_user; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $alternatives; | ||||
|   } | ||||
|  | ||||
|   public function isEnabledForUser($user) { | ||||
|     return count($this->getAlternatives($user)) > 0; | ||||
|   } | ||||
|  | ||||
|   public function processRequest(AphrontRequest $request) { | ||||
|     $user = $request->getUser(); | ||||
|     $alternatives = $this->getAlternatives($user); | ||||
|     $new_username = $request->getStr("new_username"); | ||||
|  | ||||
|     if ($request->isFormPost()) { | ||||
|       if (array_key_exists($new_username, $alternatives)) { | ||||
|         id(new PhabricatorUserEditor()) | ||||
|           ->setActor($user) | ||||
|           ->changeUsername($user, $new_username); | ||||
|  | ||||
|         return id(new AphrontRedirectResponse()) | ||||
|           ->setURI($this->getPanelURI('?saved=true')); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $instructions = pht( | ||||
|       "Multiple of your accounts were merged into one because because Phabricator does not " . | ||||
|       "support multiple accounts with the same email address. Here you can change your " . | ||||
|       "account name your preferred one. For security purposes you must also enter choose " . | ||||
|       "your password again (can be the same or new), for this you will receive an email.\n\n" . | ||||
|       "Current username: **" . $user->getUserName() . "**."); | ||||
|  | ||||
|     $form = id(new AphrontFormView()) | ||||
|       ->setUser($user) | ||||
|       ->appendRemarkupInstructions($instructions) | ||||
|       ->appendChild( | ||||
|         id(new AphrontFormSelectControl()) | ||||
|         ->setLabel(pht('Other Username')) | ||||
|         ->setName("new_username") | ||||
|         ->setValue($user->getUserName()) | ||||
|         ->setOptions($alternatives)) | ||||
|       ->appendChild( | ||||
|         id(new AphrontFormSubmitControl()) | ||||
|           ->setValue(pht('Change Username'))); | ||||
|  | ||||
|     $error_view = null; | ||||
|  | ||||
|     if ($request->getBool('saved')) { | ||||
|       $error_view = id(new AphrontErrorView()) | ||||
|         ->setTitle(pht('Username changed')) | ||||
|         ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) | ||||
|         ->setErrors(array(pht('Your username has been changed, check your mail inbox for instructions on how to reset your password.'))); | ||||
|     } | ||||
|  | ||||
|     $errors = array(); | ||||
|     if ($error_view) { | ||||
|       $errors[] = $error_view; | ||||
|     } | ||||
|     $form_box = id(new PHUIObjectBoxView()) | ||||
|       ->setHeaderText(pht('Change Username')) | ||||
|       ->setFormErrors($errors) | ||||
|       ->setForm($form); | ||||
|  | ||||
|     return array( | ||||
|       $form_box, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -35,6 +35,8 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication { | ||||
|           => 'PhabricatorApplicationTransactionValueController', | ||||
|         'remarkuppreview/' | ||||
|           => 'PhabricatorApplicationTransactionRemarkupPreviewController', | ||||
|           'cannedresponses/' | ||||
|           => 'PhabricatorApplicationTransactionCannedResponsesController', | ||||
|         'editengine/' => array( | ||||
|           $this->getQueryRoutePattern() | ||||
|             => 'PhabricatorEditEngineListController', | ||||
|   | ||||
| @@ -254,12 +254,54 @@ EOREMARKUP | ||||
|                 $type = 'projects'; | ||||
|                 $fields = $this->newEdgeTransactionFields($xaction); | ||||
|                 break; | ||||
|               case ManiphestTaskHasDuplicateTaskEdgeType::EDGECONST: | ||||
|                 $type = 'mergedfrom'; | ||||
|                 $fields = $this->newEdgeTransactionFields($xaction); | ||||
|                 break; | ||||
|               case ManiphestTaskIsDuplicateOfTaskEdgeType::EDGECONST: | ||||
|                 $type = 'mergedinto'; | ||||
|                 $fields = $this->newEdgeTransactionFields($xaction); | ||||
|                 break; | ||||
|             } | ||||
|             break; | ||||
|           case PhabricatorTransactions::TYPE_SUBSCRIBERS: | ||||
|             $type = 'subscribers'; | ||||
|             $fields = $this->newEdgeTransactionFields($xaction); | ||||
|             break; | ||||
|           case PhabricatorTransactions::TYPE_SUBTYPE: | ||||
|             $type = 'subtype'; | ||||
|             $fields = array( | ||||
|               'old' => $xaction->getOldValue(), | ||||
|               'new' => $xaction->getNewValue(), | ||||
|             ); | ||||
|             break; | ||||
|           // Following two aren't strictly needed, but gives output | ||||
|           // that gives actual content to the related 'type' and | ||||
|           // 'fields' entries for the transactions. These would | ||||
|           // otherwise be 'null' and '{}' respectively. Note that | ||||
|           // the 'edge:type' transactions for 'mergedinto' and | ||||
|           // 'mergedfrom' also give the related data. | ||||
|           case ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE: | ||||
|             $type = ManiphestTaskMergedIntoTransaction::TRANSACTIONTYPE; | ||||
|             $fields = array( | ||||
|               'old' => $xaction->getOldValue(), | ||||
|               'new' => $xaction->getNewValue(), | ||||
|             ); | ||||
|             break; | ||||
|           case ManiphestTaskMergedFromTransaction::TRANSACTIONTYPE: | ||||
|             $type = ManiphestTaskMergedFromTransaction::TRANSACTIONTYPE; | ||||
|             $fields = array( | ||||
|               'old' => $xaction->getOldValue(), | ||||
|               'new' => $xaction->getNewValue(), | ||||
|             ); | ||||
|             break; | ||||
|           case DifferentialTransaction::TYPE_ACTION: | ||||
|             $type = DifferentialTransaction::TYPE_ACTION; | ||||
|             $fields = array( | ||||
|               'old' => $xaction->getOldValue(), | ||||
|               'new' => $xaction->getNewValue(), | ||||
|             ); | ||||
|             break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorApplicationTransactionCannedResponsesController | ||||
|   extends PhabricatorApplicationTransactionController { | ||||
|  | ||||
|   public function shouldAllowPublic() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function handleRequest(AphrontRequest $request) { | ||||
|     $viewer = $request->getViewer(); | ||||
|     $reply = $request->getStr('reply'); | ||||
|  | ||||
|     if ($request->isDialogFormPost()) { | ||||
|       $result = array( | ||||
|         'reply' => $reply, | ||||
|       ); | ||||
|       return id(new AphrontAjaxResponse())->setContent($result); | ||||
|     } | ||||
|  | ||||
|     $responses = json_decode($request->getStr('responses')); | ||||
|  | ||||
|     $view = id(new AphrontFormView()) | ||||
|       ->setUser($viewer) | ||||
|       ->appendControl( | ||||
|           id(new AphrontFormSelectControl()) | ||||
|             ->setName('reply') | ||||
|             ->setOptions($this->getCannedResponsesMap($responses))); | ||||
|  | ||||
|     $dialog = id(new AphrontDialogView()) | ||||
|       ->setUser($viewer) | ||||
|       ->setTitle(pht('Canned Responses')) | ||||
|       ->appendForm($view) | ||||
|       ->addCancelButton('/') | ||||
|       ->addSubmitButton(pht('Add Text')); | ||||
|  | ||||
|     return id(new AphrontDialogResponse())->setDialog($dialog); | ||||
|   } | ||||
|  | ||||
|   private static function getCannedResponsesMap($responses) { | ||||
|     $map = array(); | ||||
|     foreach ($responses as $key => $value) { | ||||
|       $map[$value->{'message'}] = $value->{'name'}; | ||||
|     } | ||||
|     return $map; | ||||
|   } | ||||
| } | ||||
| @@ -2697,6 +2697,8 @@ abstract class PhabricatorApplicationTransactionEditor | ||||
|       } | ||||
|     } | ||||
|  | ||||
| /* TODO(sergey): Commented for now, because otherwise we can't enable editing of | ||||
|                  the tasks to moderators only. | ||||
|     if ($this->getIsNewObject()) { | ||||
|       if (!$xactions) { | ||||
|         $has_capability = PhabricatorPolicyFilter::hasCapability( | ||||
| @@ -2716,6 +2718,7 @@ abstract class PhabricatorApplicationTransactionEditor | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| */ | ||||
|  | ||||
|     return $errors; | ||||
|   } | ||||
|   | ||||
| @@ -168,6 +168,13 @@ class PhabricatorApplicationTransactionView extends AphrontView { | ||||
|  | ||||
|       $group_event = null; | ||||
|       foreach ($group as $xaction) { | ||||
|         if ($xaction->getTransactionType() == 'core:customfield') { | ||||
|           /* TODO(sergey): Make it more general approach. */ | ||||
|           $field = $xaction->getMetadataValue('customfield:key', ''); | ||||
|           if ($field == 'std:user:htaccess_password_hash') { | ||||
|             continue; | ||||
|           } | ||||
|         } | ||||
|         $event = $this->renderEvent($xaction, $group); | ||||
|         $event->setHideByDefault($hide_by_default); | ||||
|         if (!$group_event) { | ||||
| @@ -183,8 +190,9 @@ class PhabricatorApplicationTransactionView extends AphrontView { | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       $events[] = $group_event; | ||||
|  | ||||
|       if ($group_event) { | ||||
|         $events[] = $group_event; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $events; | ||||
|   | ||||
							
								
								
									
										191
									
								
								src/extensions/abuse/SecureShieldsUpAction.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,191 @@ | ||||
| <?php | ||||
|  | ||||
| final class SecureShieldsUpAction | ||||
|   extends HeraldAction { | ||||
|  | ||||
|   const ACTIONCONST = 'secure.shields-up'; | ||||
|  | ||||
|   const DO_SHIELD = 'do.secure.shields'; | ||||
|  | ||||
|   public function getHeraldActionName() { | ||||
|     return pht('Activate Advanced Cyber Defenses'); | ||||
|   } | ||||
|  | ||||
|   public function getActionGroupKey() { | ||||
|     return HeraldUtilityActionGroup::ACTIONGROUPKEY; | ||||
|   } | ||||
|  | ||||
|   public function supportsObject($object) { | ||||
|     return ($object instanceof ManiphestTask); | ||||
|   } | ||||
|  | ||||
|   public function supportsRuleType($rule_type) { | ||||
|     return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL); | ||||
|   } | ||||
|  | ||||
|   public function applyEffect($object, HeraldEffect $effect) { | ||||
|     // This is super janky but we don't currently get a reliable acting user. | ||||
|     $last_actor_row = queryfx_one( | ||||
|       $object->establishConnection('r'), | ||||
|       'SELECT authorPHID FROM %T WHERE objectPHID = %s ORDER BY id DESC | ||||
|         LIMIT 1', | ||||
|       id(new ManiphestTransaction())->getTableName(), | ||||
|       $object->getPHID()); | ||||
|     if (!$last_actor_row) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     $actor = id(new PhabricatorPeopleQuery()) | ||||
|       ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
|       ->withPHIDs(array($last_actor_row['authorPHID'])) | ||||
|       ->executeOne(); | ||||
|     if (!$actor) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if ($this->isFriendlyUser($actor)) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (!$this->isHostileObject($object)) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     $this->quarantineUser($actor); | ||||
|     $this->quarantineObject($object); | ||||
|  | ||||
|     $this->logEffect(self::DO_SHIELD); | ||||
|   } | ||||
|  | ||||
|   public function getHeraldActionStandardType() { | ||||
|     return self::STANDARD_NONE; | ||||
|   } | ||||
|  | ||||
|   public function renderActionDescription($value) { | ||||
|     return pht('Shields up.'); | ||||
|   } | ||||
|  | ||||
|   protected function getActionEffectMap() { | ||||
|     return array( | ||||
|       self::DO_SHIELD => array( | ||||
|         'icon' => 'fa-umbrella', | ||||
|         'color' => 'indigo', | ||||
|         'name' => pht('Shields Up'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   protected function renderActionEffectDescription($type, $data) { | ||||
|     switch ($type) { | ||||
|       case self::DO_SHIELD: | ||||
|         return pht('Shields up.'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private function isFriendlyUser(PhabricatorUser $user) { | ||||
|     if ($user->getIsAdmin()) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   private function isHostileObject($object) { | ||||
|     $content = array(); | ||||
|  | ||||
|     if ($object instanceof ManiphestTask) { | ||||
|       $content[] = $object->getTitle(); | ||||
|       $content[] = $object->getDescription(); | ||||
|     } | ||||
|  | ||||
|     $content = implode("\n\n", $content); | ||||
|  | ||||
|     $patterns = array(); | ||||
|  | ||||
|     // Phone numbers that we'll reject. | ||||
|     $numbers = array( | ||||
|       '8443133901', | ||||
|       '8007909186', | ||||
|       '800059007', | ||||
|       '8008101018', | ||||
|       '8002044122', | ||||
|       '8007992667', | ||||
|       '8557092847', | ||||
|       '8007789936', | ||||
|       '8000903859', | ||||
|       '8000314244', | ||||
|       '8008057863', | ||||
|     ); | ||||
|  | ||||
|     if (self::matchPhoneNumbers($numbers, $content)) { | ||||
|       return true; | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   public static function matchPhoneNumbers(array $numbers, $content) { | ||||
|     $swap = array( | ||||
|       'o' => '0', | ||||
|       'O' => '0', | ||||
|       '@' => '0', | ||||
|       '()' => '0', | ||||
|  | ||||
|       'i' => '1', | ||||
|       'I' => '1', | ||||
|       '|' => '1', | ||||
|       'l' => '1', | ||||
|     ); | ||||
|  | ||||
|     $content = str_replace( | ||||
|       array_keys($swap), | ||||
|       array_values($swap), | ||||
|       $content); | ||||
|  | ||||
|     foreach ($numbers as $number) { | ||||
|       $regex = array(); | ||||
|       for ($ii = 0; $ii < strlen($number); $ii++) { | ||||
|         $regex[] = $number[$ii]; | ||||
|       } | ||||
|       // Reject all variants of the number with other random punctuation or | ||||
|       // spaces between the digits. | ||||
|       $regex = implode('[^\\d]{0,6}', $regex); | ||||
|       $patterns[] = '/'.$regex.'/'; | ||||
|     } | ||||
|  | ||||
|     foreach ($patterns as $pattern) { | ||||
|       if (preg_match($pattern, $content)) { | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   private function quarantineUser(PhabricatorUser $user) { | ||||
|     // For now, just log the user out of all their sessions so it's not a big | ||||
|     // deal if we hit a friendly user by accident. We could make this more | ||||
|     // extreme in the future. | ||||
|  | ||||
|     $sessions = id(new PhabricatorAuthSessionQuery()) | ||||
|       ->setViewer($user) | ||||
|       ->withIdentityPHIDs(array($user->getPHID())) | ||||
|       ->execute(); | ||||
|     foreach ($sessions as $session) { | ||||
|       $session->delete(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private function quarantineObject($object) { | ||||
|     $title = $object->getTitle(); | ||||
|     $new_title = '<QUARANTINED> '.$title; | ||||
|  | ||||
|     $object | ||||
|       ->setTitle($new_title) | ||||
|       ->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN) | ||||
|       ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN) | ||||
|       ->save(); | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										38
									
								
								src/extensions/abuse/__tests__/SecureSheldsUpTestCase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| final class SecureShieldsUpTestCase extends PhabricatorTestCase { | ||||
|  | ||||
|   public function testPhoneNumberDetection() { | ||||
|     $numbers = array( | ||||
|       '8002044122', | ||||
|     ); | ||||
|  | ||||
|     $tests = array( | ||||
|       '8002044122' => true, | ||||
|       '1 (800) 204.4122' => true, | ||||
|       '80012044122' => false, | ||||
|  | ||||
|       '8OO2o44I22' => true, | ||||
|  | ||||
|       // Does not contain the number. | ||||
|       'Pulse Rifle' => false, | ||||
|  | ||||
|       // Currently, we give up after 6 characters without finding the next | ||||
|       // digit. | ||||
|       '800........204.4122' => false, | ||||
|  | ||||
|       // We aren't wizards, but users aren't either. | ||||
|       'eight hundred, then dial two zero 4, then 41 and finally twenty two' | ||||
|         => false, | ||||
|     ); | ||||
|  | ||||
|     foreach ($tests as $input => $expect) { | ||||
|       $actual = SecureShieldsUpAction::matchPhoneNumbers($numbers, $input); | ||||
|       $this->assertEqual( | ||||
|         $expect, | ||||
|         $actual, | ||||
|         pht('Detection of phone numbers in: %s', $input)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorApplicationBuildbot extends PhabricatorApplication { | ||||
|  | ||||
|   public function getName() { | ||||
|     return pht('Buildbot'); | ||||
|   } | ||||
|  | ||||
|   public function isPinnedByDefault(PhabricatorUser $viewer) { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getBaseURI() { | ||||
|     return 'https://builder.blender.org/download/'; | ||||
|   } | ||||
|  | ||||
|   public function getFontIcon() { | ||||
|     return 'fa-sun-o'; | ||||
|   } | ||||
|  | ||||
|   public function getShortDescription() { | ||||
|     return 'Automated Builds'; | ||||
|   } | ||||
|  | ||||
|   public function getTitleGlyph() { | ||||
|     return "\xE2\x9C\x94"; | ||||
|   } | ||||
|  | ||||
|   public function getFlavorText() { | ||||
|     return pht('Automated Blender builds by Buildbot.'); | ||||
|   } | ||||
|  | ||||
|   public function getApplicationGroup() { | ||||
|     return self::GROUP_CORE; | ||||
|   } | ||||
|  | ||||
|   public function getRoutes() { | ||||
|     return array( | ||||
|       '/buildbot/' => array( | ||||
|         '(?:query/(?P<queryKey>[^/]+)/)?' | ||||
|           => 'PhabricatorBuildbotController', | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @group buildbot | ||||
|  */ | ||||
| final class PhabricatorBuildbotController extends PhabricatorController { | ||||
|  | ||||
|   public function shouldAllowPublic() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function processRequest() { | ||||
|  | ||||
|     $html = ' | ||||
|       <script type="text/javascript"> | ||||
|         window.location = "https://builder.blender.org/download"; | ||||
|       </script> | ||||
|       See <a href="https://builder.blender.org/download">builder.blender.org</a>.'; | ||||
|  | ||||
|     $panel = new PHUIObjectBoxView(); | ||||
|     $panel->appendChild(phutil_safe_html($html)); | ||||
|  | ||||
|     $content = array( | ||||
|       $panel | ||||
|     ); | ||||
|  | ||||
|     $crumbs = $this->buildApplicationCrumbs(); | ||||
|  | ||||
|     return $this->buildApplicationPage( | ||||
|       array( | ||||
|          $crumbs, | ||||
|          $panel, | ||||
|       ), | ||||
|       array( | ||||
|         'title' => 'Buildbot', | ||||
|         'device' => true, | ||||
|       )); | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorApplicationDocumentation extends PhabricatorApplication { | ||||
|  | ||||
|   public function getName() { | ||||
|     return pht('Documentation'); | ||||
|   } | ||||
|  | ||||
|   public function isPinnedByDefault(PhabricatorUser $viewer) { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getBaseURI() { | ||||
|     return 'http://wiki.blender.org/index.php/Dev:Contents'; | ||||
|   } | ||||
|  | ||||
|   public function getFontIcon() { | ||||
|     return 'fa-book'; | ||||
|   } | ||||
|  | ||||
|   public function getShortDescription() { | ||||
|     return 'Developer Wiki'; | ||||
|   } | ||||
|  | ||||
|   public function getTitleGlyph() { | ||||
|     return "\xE2\x9C\x94"; | ||||
|   } | ||||
|  | ||||
|   public function getFlavorText() { | ||||
|     return pht('Developer wiki documentation.'); | ||||
|   } | ||||
|  | ||||
|   public function getApplicationGroup() { | ||||
|     return self::GROUP_CORE; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -18,6 +18,7 @@ abstract class PhabricatorStandardCustomField | ||||
|   private $isCopyable; | ||||
|   private $hasStorageValue; | ||||
|   private $isBuiltin; | ||||
|   private $hidden; | ||||
|   private $isEnabled = true; | ||||
|  | ||||
|   abstract public function getFieldType(); | ||||
| @@ -109,6 +110,11 @@ abstract class PhabricatorStandardCustomField | ||||
|     return $this->isBuiltin; | ||||
|   } | ||||
|  | ||||
|   public function setHidden($hidden) { | ||||
|     $this->hidden = $hidden; | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function setFieldConfig(array $config) { | ||||
|     foreach ($config as $key => $value) { | ||||
|       switch ($key) { | ||||
| @@ -133,6 +139,8 @@ abstract class PhabricatorStandardCustomField | ||||
|         case 'default': | ||||
|           $this->setFieldValue($value); | ||||
|           break; | ||||
|         case 'hidden': | ||||
|           $this->setHidden($value); | ||||
|         case 'copy': | ||||
|           $this->setIsCopyable($value); | ||||
|           break; | ||||
| @@ -259,10 +267,16 @@ abstract class PhabricatorStandardCustomField | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInApplicationTransactions() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInEditView() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->getFieldConfigValue('edit', true); | ||||
|   } | ||||
|  | ||||
| @@ -297,6 +311,9 @@ abstract class PhabricatorStandardCustomField | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInPropertyView() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->getFieldConfigValue('view', true); | ||||
|   } | ||||
|  | ||||
| @@ -308,6 +325,9 @@ abstract class PhabricatorStandardCustomField | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInApplicationSearch() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->getFieldConfigValue('search', false); | ||||
|   } | ||||
|  | ||||
| @@ -394,6 +414,10 @@ abstract class PhabricatorStandardCustomField | ||||
|  | ||||
|   public function getApplicationTransactionTitle( | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|     if ($this->hidden) { | ||||
|       return pht(""); | ||||
|     } | ||||
|  | ||||
|     $author_phid = $xaction->getAuthorPHID(); | ||||
|     $old = $xaction->getOldValue(); | ||||
|     $new = $xaction->getNewValue(); | ||||
| @@ -421,6 +445,9 @@ abstract class PhabricatorStandardCustomField | ||||
|  | ||||
|   public function getApplicationTransactionTitleForFeed( | ||||
|     PhabricatorApplicationTransaction $xaction) { | ||||
|     if ($this->hidden) { | ||||
|         return pht(""); | ||||
|     } | ||||
|  | ||||
|     $author_phid = $xaction->getAuthorPHID(); | ||||
|     $object_phid = $xaction->getObjectPHID(); | ||||
| @@ -453,15 +480,29 @@ abstract class PhabricatorStandardCustomField | ||||
|   } | ||||
|  | ||||
|   public function getHeraldFieldValue() { | ||||
|     if ($this->hidden) { | ||||
|       return pht(""); | ||||
|     } | ||||
|     return $this->getFieldValue(); | ||||
|   } | ||||
|  | ||||
|   public function shouldEnableForRole($role) { | ||||
|     if ($this->hidden) { | ||||
|       return $role == PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS || | ||||
|              $role == PhabricatorCustomField::ROLE_STORAGE; | ||||
|     } | ||||
|     return parent::shouldEnableForRole($role); | ||||
|   } | ||||
|  | ||||
|   public function getFieldControlID($key = null) { | ||||
|     $key = coalesce($key, $this->getRawStandardFieldKey()); | ||||
|     return 'std:control:'.$key; | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInGlobalSearch() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return $this->getFieldConfigValue('fulltext', false); | ||||
|   } | ||||
|  | ||||
| @@ -491,10 +532,16 @@ abstract class PhabricatorStandardCustomField | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInConduitTransactions() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function shouldAppearInConduitDictionary() { | ||||
|     if ($this->hidden) { | ||||
|       return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
| @@ -507,6 +554,9 @@ abstract class PhabricatorStandardCustomField | ||||
|   } | ||||
|  | ||||
|   public function getConduitDictionaryValue() { | ||||
|     if ($this->hidden) { | ||||
|       return pht(""); | ||||
|     } | ||||
|     return $this->getFieldValue(); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -26,6 +26,15 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl { | ||||
|     return $this->canPin; | ||||
|   } | ||||
|  | ||||
|   public function getCannedResponses() { | ||||
|     return PhabricatorEnv::getEnvConfig('maniphest.canned-responses'); | ||||
|   } | ||||
|  | ||||
|   public function getCanCannedResponses() { | ||||
|     $canned_responses = $this->getCannedResponses(); | ||||
|     return $canned_responses != null; | ||||
|   } | ||||
|  | ||||
|   public function setSendOnEnter($soe) { | ||||
|     $this->sendOnEnter = $soe; | ||||
|     return $this; | ||||
| @@ -89,6 +98,7 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl { | ||||
|           'URL' => pht('URL'), | ||||
|           'key-help' => pht('Pin or unpin the comment form.'), | ||||
|         ), | ||||
|         'cannedResponses' => $this->getCannedResponses(), | ||||
|         'canPin' => $this->getCanPin(), | ||||
|         'disabled' => $this->getDisabled(), | ||||
|         'sendOnEnter' => $this->getSendOnEnter(), | ||||
| @@ -239,6 +249,13 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     if ($this->getCanCannedResponses()) { | ||||
|       $mode_actions['fa-reply'] = array( | ||||
|         'tip' => pht('Canned Responses'), | ||||
|         'align' => 'right', | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     if ($mode_actions) { | ||||
|       $actions += $mode_actions; | ||||
|     } | ||||
| @@ -353,6 +370,12 @@ final class PhabricatorRemarkupControl extends AphrontFormTextAreaControl { | ||||
|       array( | ||||
|         $buttons, | ||||
|         parent::renderInput(), | ||||
|         javelin_tag( | ||||
|           'div', | ||||
|           array( | ||||
|             'class' => 'aphront-form-caption', | ||||
|           ), | ||||
|           'Please fill in all requested information. Use drag and drop to attach files.'), | ||||
|       )); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -77,6 +77,14 @@ final class PHUIPropertyListView extends AphrontView { | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function addCustomTextContent($content) { | ||||
|     $this->parts[] = array( | ||||
|       'type'    => 'custom-text', | ||||
|       'content' => $content, | ||||
|     ); | ||||
|     return $this; | ||||
|   } | ||||
|  | ||||
|   public function addTextContent($content) { | ||||
|     $this->parts[] = array( | ||||
|       'type'    => 'text', | ||||
| @@ -162,6 +170,9 @@ final class PHUIPropertyListView extends AphrontView { | ||||
|         case 'section': | ||||
|           $items[] = $this->renderSectionPart($part); | ||||
|           break; | ||||
|         case 'custom-text': | ||||
|           $items[] = $this->renderPropertyPart($part, true); | ||||
|           break; | ||||
|         case 'text': | ||||
|         case 'image': | ||||
|           $items[] = $this->renderTextPart($part); | ||||
| @@ -186,8 +197,18 @@ final class PHUIPropertyListView extends AphrontView { | ||||
|       )); | ||||
|   } | ||||
|  | ||||
|   private function renderPropertyPart(array $part) { | ||||
|   private function renderPropertyPart(array $part, $customtext = false) { | ||||
|     $items = array(); | ||||
|     if($customtext) { | ||||
|       $items[] = phutil_tag( | ||||
|         'div', | ||||
|         array( | ||||
|           'class' => 'phui-property-list-text-content', | ||||
|         ), | ||||
|         $part['content']); | ||||
|     } | ||||
|     else { | ||||
|  | ||||
|     foreach ($part['list'] as $spec) { | ||||
|       $key = $spec['key']; | ||||
|       $value = $spec['value']; | ||||
| @@ -210,6 +231,7 @@ final class PHUIPropertyListView extends AphrontView { | ||||
|         ), | ||||
|         array($value, ' ')); | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     $stacked = ''; | ||||
|     if ($this->stacked) { | ||||
|   | ||||
| @@ -16,7 +16,8 @@ | ||||
| } | ||||
|  | ||||
| .phabricator-main-menu-background { | ||||
|   min-height: 44px; | ||||
|   min-height: 46px; | ||||
|   padding-top: 2px; | ||||
| } | ||||
|  | ||||
| .device-desktop .phabricator-main-menu { | ||||
| @@ -48,13 +49,13 @@ | ||||
| } | ||||
|  | ||||
| .phabricator-main-menu-eye { | ||||
|   margin: 2px 0; | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
|   margin: 8px 0 2px 10px; | ||||
|   width: 101px; | ||||
|   height: 30px; | ||||
|   float: left; | ||||
|   display: block; | ||||
|   background-image: url(/rsrc/image/logo/light-eye.png); | ||||
|   background-size: 40px 40px; | ||||
|   background-image: url(/rsrc/custom/image/blender_logo.png); | ||||
|   background-size: 101px 30px; | ||||
| } | ||||
|  | ||||
| .device-desktop .phabricator-main-menu-brand:hover { | ||||
| @@ -166,7 +167,7 @@ | ||||
|   border: none; | ||||
|   background-color: {$page.content}; | ||||
|   height: 28px; | ||||
|   padding: 3px 28px 3px 48px; | ||||
|   padding: 3px 28px 3px 44px; | ||||
|   float: left; | ||||
|   width: 280px; | ||||
| } | ||||
|   | ||||
| @@ -28,3 +28,7 @@ | ||||
|   background: #124A1B; | ||||
| } | ||||
|  | ||||
|  | ||||
| .phui-theme-blender .phabricator-main-menu-background { | ||||
|   background: #424246; | ||||
| } | ||||
|   | ||||
| @@ -195,7 +195,11 @@ | ||||
| } | ||||
|  | ||||
| .phui-badge-mini-white { | ||||
|   background-color: {$lightblueborder}; | ||||
|   /* Custom Blender ID badges need mini-badges without any background. | ||||
|    * Because badges were not used at developer.blender.org at all, | ||||
|    * this seemed like an acceptible compromise for achieving good looking custom badges. | ||||
|    * */ | ||||
|   background-color: transparent; | ||||
| } | ||||
|  | ||||
| .phui-badge-mini-green { | ||||
| @@ -217,3 +221,88 @@ | ||||
| .phui-badge-mini-yellow { | ||||
|   background-color: {$sh-yellowicon}; | ||||
| } | ||||
|  | ||||
| /* Badges integration with Blender ID */ | ||||
| .badge-devfund-bronze { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_devfund_bronze.png); | ||||
| } | ||||
|  | ||||
| .badge-devfund-gold { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_devfund_gold.png); | ||||
| } | ||||
|  | ||||
| .badge-devfund-silver { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_devfund_silver.png); | ||||
| } | ||||
|  | ||||
| .badge-devfund-titanium { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_devfund_titanium.png); | ||||
| } | ||||
|  | ||||
| .badge-devfund-platinum { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_devfund_platinum.png); | ||||
| } | ||||
|  | ||||
| .badge-devfund-diamond { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_devfund_diamond.png); | ||||
| } | ||||
|  | ||||
| .badge-cloud-subscriber { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_studio.png); | ||||
| } | ||||
|  | ||||
| .badge-sprite-fright { | ||||
|   background-image: url(/rsrc/custom/image/badges/badge_sprite_fright.png); | ||||
| } | ||||
| .badge-devfund-bronze, | ||||
| .badge-devfund-gold, | ||||
| .badge-devfund-silver, | ||||
| .badge-devfund-titanium, | ||||
| .badge-devfund-platinum, | ||||
| .badge-devfund-diamond, | ||||
| .badge-cloud-subscriber, | ||||
| .badge-sprite-fright { | ||||
|   box-sizing: border-box; | ||||
|   background-position: center; | ||||
|   background-size: cover; | ||||
|   background-repeat: no-repeat; | ||||
|   width: 2em; | ||||
|   height: 2em; | ||||
| } | ||||
|  | ||||
| /* When shown under avatars in comments */ | ||||
| .phui-timeline-badges .badge-devfund-bronze, | ||||
| .phui-timeline-badges .badge-devfund-gold, | ||||
| .phui-timeline-badges .badge-devfund-silver, | ||||
| .phui-timeline-badges .badge-devfund-titanium, | ||||
| .phui-timeline-badges .badge-devfund-platinum, | ||||
| .phui-timeline-badges .badge-devfund-diamond, | ||||
| .phui-timeline-badges .badge-cloud-subscriber, | ||||
| .phui-timeline-badges .badge-sprite-fright { | ||||
|   display: block; | ||||
|   margin-top: 4px !important; | ||||
|   margin-right: auto; | ||||
|   margin-left: auto; | ||||
| } | ||||
|  | ||||
| /* When shown in "Award Badge" popup */ | ||||
| .jx-tokenizer-token .badge-devfund-bronze, | ||||
| .jx-tokenizer-token .badge-devfund-gold, | ||||
| .jx-tokenizer-token .badge-devfund-silver, | ||||
| .jx-tokenizer-token .badge-devfund-titanium, | ||||
| .jx-tokenizer-token .badge-devfund-platinum, | ||||
| .jx-tokenizer-token .badge-devfund-diamond, | ||||
| .jx-tokenizer-token .badge-cloud-subscriber, | ||||
| .jx-tokenizer-token .badge-sprite-fright, | ||||
| .jx-result .badge-devfund-bronze, | ||||
| .jx-result .badge-devfund-gold, | ||||
| .jx-result .badge-devfund-silver, | ||||
| .jx-result .badge-devfund-titanium, | ||||
| .jx-result .badge-devfund-platinum, | ||||
| .jx-result .badge-devfund-diamond, | ||||
| .jx-result .badge-cloud-subscriber, | ||||
| .jx-result .badge-sprite-fright { | ||||
|   width: 1em; | ||||
|   height: 1em; | ||||
|   margin-bottom: -2px; | ||||
| } | ||||
|   | ||||
| @@ -117,7 +117,7 @@ | ||||
|   display: block; | ||||
|   width: 100%; | ||||
|   box-sizing: border-box; | ||||
|   height: 12em; | ||||
|   height: 20em; | ||||
| } | ||||
|  | ||||
| .aphront-form-control { | ||||
|   | ||||
							
								
								
									
										74
									
								
								webroot/rsrc/custom/css/phabricator-welcome-page.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,74 @@ | ||||
| /** | ||||
|  * @provides phabricator-welcome-page | ||||
|  */ | ||||
|  | ||||
|  | ||||
| .project-list { | ||||
|   padding: 0; | ||||
|   background: #FFF; | ||||
|    | ||||
|   border: none; | ||||
|   background: none; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .project-list .aphront-panel-header { | ||||
|   display: none; | ||||
| } | ||||
|  | ||||
| .project-item { | ||||
|   float: left; | ||||
|   text-align: center; | ||||
|   background: #FFF; | ||||
|   height: 145px; | ||||
|   border: 1px solid #C7CCD9; | ||||
|   border-bottom: 1px solid #A1A6B0; | ||||
| } | ||||
|  | ||||
| .project-item h2 { | ||||
|   width: 100%; | ||||
|   padding: 3px 0px; | ||||
|   color: #333; | ||||
|   font-size: 13pt; | ||||
|   font-weight: normal; | ||||
|   margin-bottom: 8px; | ||||
|   border-bottom: 1px solid #CCC; | ||||
| } | ||||
|  | ||||
| .project-item.span8 h2 { | ||||
|   width: 100%; | ||||
|   padding: 0; | ||||
|   color: #333; | ||||
|   font-size: 13pt; | ||||
|   font-weight: normal; | ||||
|   margin: 0; | ||||
|   border-bottom: 0; | ||||
|   height: auto; | ||||
| } | ||||
|  | ||||
| .project-item li { | ||||
|   margin-bottom: 5px; | ||||
| } | ||||
|  | ||||
| .project-item.span4 { | ||||
|   width: 24%; | ||||
|   margin-right: 1%; | ||||
| } | ||||
|  | ||||
| .project-item.span8 { | ||||
|   float: left; | ||||
|   padding: 8px 0px; | ||||
|   width: 49%; | ||||
|   height: 129px; | ||||
| } | ||||
|  | ||||
| @media all and (max-width: 800px) { | ||||
|   .project-item.span8 { | ||||
|     width: 48%; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .project-item.span8 ul { | ||||
|   float: left; | ||||
|   width: 50%; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_devfund_bronze.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_devfund_diamond.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_devfund_gold.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 16 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_devfund_platinum.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_devfund_silver.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 15 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_devfund_titanium.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_sprite_fright.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 26 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/badges/badge_studio.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								webroot/rsrc/custom/image/blender_logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 61 KiB | 
							
								
								
									
										27
									
								
								webroot/rsrc/custom/static/diff_guidelines.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | ||||
| <h2>Documentation</h2> | ||||
| <br/> | ||||
| <ul> | ||||
| <li><a href="https://wiki.blender.org/index.php/Dev:Doc/Process/Contributing_Code">Contributing Code</a>: how to get your code included.</li> | ||||
| <li><a href="https://wiki.blender.org/index.php/Dev:Doc/Process/Addons">Contributing Addons</a>: how to get your addon included.</li> | ||||
| <li><a href="https://wiki.blender.org/index.php/Dev:Doc/Tools/Code_Review">Code Review</a>: how to use Differential for code review effectively.</li> | ||||
| </ul> | ||||
|  | ||||
| <br/> | ||||
|  | ||||
| <h2>License</h2> | ||||
| <br/> | ||||
| <p>By submitting code here, <b>you agree that the code is (compatible with) GNU GPL v2 or later.</b><br/> | ||||
| If you choose for a compatible non-GPL license, notify it in the patch.</b></p> | ||||
|  | ||||
| <br> | ||||
|  | ||||
| <h2>Create a Diff</h2> | ||||
| <br/> | ||||
|  | ||||
| <ul> | ||||
| <li>Submit patches in svn diff or git diff formats.</li> | ||||
| <li>Either copy-paste diff content into the text field below, or attach it as file.</li> | ||||
| <li>You will be able to add a title, description and reviewers in the next steps.</li> | ||||
| <li>If you need to create diffs often, consider setting up <a href="https://wiki.blender.org/index.php/Dev:Doc/Tools/Code_Review#Use_Arcanist">Arcanist</a> to send code directly from git branches.</li> | ||||
| </ul> | ||||
|  | ||||
							
								
								
									
										1
									
								
								webroot/rsrc/custom/static/login.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| <p style="margin: 12pt 0;">Welcome to the Blender developer website!</p> | ||||
							
								
								
									
										50
									
								
								webroot/rsrc/custom/static/welcome.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | ||||
| <div style="background: #f7f7f7; padding: 0 6pt;"> | ||||
|  | ||||
| <ul> | ||||
|  | ||||
| <li> | ||||
| <div class="project-item span4" style="min-width: 120pt; margin: 6pt 1% 6pt 0; padding-bottom: 6pt;"> | ||||
|   <h2><a href="/project/view/2/">BF Blender</a></h2> | ||||
|   <ul> | ||||
|     <li><a href="/maniphest/project/2/">Browse Tasks</a></li> | ||||
|     <li><a class="button grey" href="/maniphest/task/edit/form/1">Report Bug »</a></li> | ||||
|     <li><a href="/differential/query/open/">Code Reviews</a></li> | ||||
|     <li><a class="button grey" href="/differential/diff/create/">Submit Code »</a></li> | ||||
|   </ul> | ||||
| </div> | ||||
| </li> | ||||
|  | ||||
| <li> | ||||
| <div class="project-item span4" style="min-width: 120pt; margin: 6pt 1% 6pt 0; padding-bottom: 6pt;"> | ||||
|   <h2><a href="/project/view/3/">Addons</a></h2> | ||||
|   <ul> | ||||
|     <li><a href="/maniphest/project/3/">Browse Tasks</a></li> | ||||
|     <li><a class="button grey" href="/maniphest/task/edit/form/2">Report Bug »</a></li> | ||||
|     <li><a href="/maniphest/project/3/type/patch/">Browse New Addons</a></li> | ||||
|     <li><a class="button grey" href="/maniphest/task/edit/form/3">Submit Addon »</a></li> | ||||
|   </ul> | ||||
| </div> | ||||
| </li> | ||||
|  | ||||
| <li> | ||||
| <div class="project-item span8" style="min-width: 180pt; margin: 6pt 0; padding-bottom: 12pt;"> | ||||
|   <ul> | ||||
|     <li><a href="/project/view/72/">Attract</a></li> | ||||
|     <li><a href="/project/view/26/">Cycles</a></li> | ||||
|     <li><a href="/project/view/58/">Flamenco</a></li> | ||||
|     <li><a href="/project/view/53/">Documentation</a></li> | ||||
|   </ul> | ||||
|   <ul> | ||||
|     <li><a href="/project/view/4/">Translations</a></li> | ||||
|     <li><a href="/project/view/12/">User Interface</a></li> | ||||
|     <li><a href="/project/view/34/">Quick Hacks</a></li> | ||||
|     <li><a href="/project/view/31/">Websites</a></li> | ||||
|   </ul> | ||||
|   <h2><a href="/project/query/active/">... More Projects</a></h2> | ||||
| </div> | ||||
| </li> | ||||
|  | ||||
| </ul> | ||||
|  | ||||
| <div style="clear:both;" ></div> | ||||
| </div> | ||||
| Before Width: | Height: | Size: 710 B After Width: | Height: | Size: 977 B |