Add an icon+background selector for project images
Summary: Makes it easy to choose distinctive icons for projects.
Test Plan:
{F71018}
{F71020}
{F71019}
{F71021}
Reviewers: btrahan, chad
Reviewed By: chad
CC: chad, aran
Differential Revision: https://secure.phabricator.com/D7333
			
			
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								resources/builtin/project.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/builtin/project.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.0 KiB  | 
@@ -1,284 +1,284 @@
 | 
			
		||||
{
 | 
			
		||||
  "version" : 1,
 | 
			
		||||
  "sprites" : {
 | 
			
		||||
    "projects_8ball"         : {
 | 
			
		||||
      "name" : "projects_8ball",
 | 
			
		||||
      "rule" : ".projects_8ball",
 | 
			
		||||
    "projects-8ball"         : {
 | 
			
		||||
      "name" : "projects-8ball",
 | 
			
		||||
      "rule" : ".projects-8ball",
 | 
			
		||||
      "hash" : "1571c4d51926d3af7711b825c5816e2e"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_alien"         : {
 | 
			
		||||
      "name" : "projects_alien",
 | 
			
		||||
      "rule" : ".projects_alien",
 | 
			
		||||
    "projects-alien"         : {
 | 
			
		||||
      "name" : "projects-alien",
 | 
			
		||||
      "rule" : ".projects-alien",
 | 
			
		||||
      "hash" : "384f920ae335dca04edaf29663d3a074"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_annouce"       : {
 | 
			
		||||
      "name" : "projects_annouce",
 | 
			
		||||
      "rule" : ".projects_annouce",
 | 
			
		||||
      "hash" : "38abd2ff32e7c145e44c020ee4e6f2f1"
 | 
			
		||||
    "projects-announce"      : {
 | 
			
		||||
      "name" : "projects-announce",
 | 
			
		||||
      "rule" : ".projects-announce",
 | 
			
		||||
      "hash" : "94329cedd509fc27a6fb577927581118"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_art"           : {
 | 
			
		||||
      "name" : "projects_art",
 | 
			
		||||
      "rule" : ".projects_art",
 | 
			
		||||
    "projects-art"           : {
 | 
			
		||||
      "name" : "projects-art",
 | 
			
		||||
      "rule" : ".projects-art",
 | 
			
		||||
      "hash" : "85c545e5130f00ff1b93c0af0d540974"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_award"         : {
 | 
			
		||||
      "name" : "projects_award",
 | 
			
		||||
      "rule" : ".projects_award",
 | 
			
		||||
    "projects-award"         : {
 | 
			
		||||
      "name" : "projects-award",
 | 
			
		||||
      "rule" : ".projects-award",
 | 
			
		||||
      "hash" : "fad6d89e4938e16f22f3c9db7cf5d696"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_bacon"         : {
 | 
			
		||||
      "name" : "projects_bacon",
 | 
			
		||||
      "rule" : ".projects_bacon",
 | 
			
		||||
    "projects-bacon"         : {
 | 
			
		||||
      "name" : "projects-bacon",
 | 
			
		||||
      "rule" : ".projects-bacon",
 | 
			
		||||
      "hash" : "f6300cdfa5a96a223f53f13dd0d3acc3"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_bandaid"       : {
 | 
			
		||||
      "name" : "projects_bandaid",
 | 
			
		||||
      "rule" : ".projects_bandaid",
 | 
			
		||||
    "projects-bandaid"       : {
 | 
			
		||||
      "name" : "projects-bandaid",
 | 
			
		||||
      "rule" : ".projects-bandaid",
 | 
			
		||||
      "hash" : "c463dffa161997277fc6697155f4085b"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_beer"          : {
 | 
			
		||||
      "name" : "projects_beer",
 | 
			
		||||
      "rule" : ".projects_beer",
 | 
			
		||||
    "projects-beer"          : {
 | 
			
		||||
      "name" : "projects-beer",
 | 
			
		||||
      "rule" : ".projects-beer",
 | 
			
		||||
      "hash" : "81c7580f322d9fb40c77db56cd92d61d"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_bomb"          : {
 | 
			
		||||
      "name" : "projects_bomb",
 | 
			
		||||
      "rule" : ".projects_bomb",
 | 
			
		||||
    "projects-bomb"          : {
 | 
			
		||||
      "name" : "projects-bomb",
 | 
			
		||||
      "rule" : ".projects-bomb",
 | 
			
		||||
      "hash" : "1123da7cc56313891c9979b004cc02f7"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_briefcase"     : {
 | 
			
		||||
      "name" : "projects_briefcase",
 | 
			
		||||
      "rule" : ".projects_briefcase",
 | 
			
		||||
    "projects-briefcase"     : {
 | 
			
		||||
      "name" : "projects-briefcase",
 | 
			
		||||
      "rule" : ".projects-briefcase",
 | 
			
		||||
      "hash" : "9b4b413ddb250ce1d3fbe18a5a5698cd"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_bug"           : {
 | 
			
		||||
      "name" : "projects_bug",
 | 
			
		||||
      "rule" : ".projects_bug",
 | 
			
		||||
    "projects-bug"           : {
 | 
			
		||||
      "name" : "projects-bug",
 | 
			
		||||
      "rule" : ".projects-bug",
 | 
			
		||||
      "hash" : "9678702aed00c4779759ebbdfe97fe48"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_calendar"      : {
 | 
			
		||||
      "name" : "projects_calendar",
 | 
			
		||||
      "rule" : ".projects_calendar",
 | 
			
		||||
    "projects-calendar"      : {
 | 
			
		||||
      "name" : "projects-calendar",
 | 
			
		||||
      "rule" : ".projects-calendar",
 | 
			
		||||
      "hash" : "e7dc5d1b11fc55ed239fcbfe527ed0e7"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_cloud"         : {
 | 
			
		||||
      "name" : "projects_cloud",
 | 
			
		||||
      "rule" : ".projects_cloud",
 | 
			
		||||
    "projects-cloud"         : {
 | 
			
		||||
      "name" : "projects-cloud",
 | 
			
		||||
      "rule" : ".projects-cloud",
 | 
			
		||||
      "hash" : "d38bf58580b3c36fbd3149a13f7d0e5e"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_coffee"        : {
 | 
			
		||||
      "name" : "projects_coffee",
 | 
			
		||||
      "rule" : ".projects_coffee",
 | 
			
		||||
    "projects-coffee"        : {
 | 
			
		||||
      "name" : "projects-coffee",
 | 
			
		||||
      "rule" : ".projects-coffee",
 | 
			
		||||
      "hash" : "a9c10862139d8e7f56c9f892496f9666"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_creditcard"    : {
 | 
			
		||||
      "name" : "projects_creditcard",
 | 
			
		||||
      "rule" : ".projects_creditcard",
 | 
			
		||||
    "projects-creditcard"    : {
 | 
			
		||||
      "name" : "projects-creditcard",
 | 
			
		||||
      "rule" : ".projects-creditcard",
 | 
			
		||||
      "hash" : "db2c179cb4935da8b9950ac30da8c0d1"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_death"         : {
 | 
			
		||||
      "name" : "projects_death",
 | 
			
		||||
      "rule" : ".projects_death",
 | 
			
		||||
    "projects-death"         : {
 | 
			
		||||
      "name" : "projects-death",
 | 
			
		||||
      "rule" : ".projects-death",
 | 
			
		||||
      "hash" : "cdea72dfdcb3fc64873b9fff78addb3c"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_desktop"       : {
 | 
			
		||||
      "name" : "projects_desktop",
 | 
			
		||||
      "rule" : ".projects_desktop",
 | 
			
		||||
    "projects-desktop"       : {
 | 
			
		||||
      "name" : "projects-desktop",
 | 
			
		||||
      "rule" : ".projects-desktop",
 | 
			
		||||
      "hash" : "19d2ef34e3dd53615cdad91eb987d6fe"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_dropbox"       : {
 | 
			
		||||
      "name" : "projects_dropbox",
 | 
			
		||||
      "rule" : ".projects_dropbox",
 | 
			
		||||
    "projects-dropbox"       : {
 | 
			
		||||
      "name" : "projects-dropbox",
 | 
			
		||||
      "rule" : ".projects-dropbox",
 | 
			
		||||
      "hash" : "10231bf468769b96ed40cf983abfa269"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_education"     : {
 | 
			
		||||
      "name" : "projects_education",
 | 
			
		||||
      "rule" : ".projects_education",
 | 
			
		||||
    "projects-education"     : {
 | 
			
		||||
      "name" : "projects-education",
 | 
			
		||||
      "rule" : ".projects-education",
 | 
			
		||||
      "hash" : "ce3d0ca75d519b2ac427a690d30475f8"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_experimental"  : {
 | 
			
		||||
      "name" : "projects_experimental",
 | 
			
		||||
      "rule" : ".projects_experimental",
 | 
			
		||||
    "projects-experimental"  : {
 | 
			
		||||
      "name" : "projects-experimental",
 | 
			
		||||
      "rule" : ".projects-experimental",
 | 
			
		||||
      "hash" : "311ef712f8daca057c20c8fd78fa77ce"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_facebook"      : {
 | 
			
		||||
      "name" : "projects_facebook",
 | 
			
		||||
      "rule" : ".projects_facebook",
 | 
			
		||||
    "projects-facebook"      : {
 | 
			
		||||
      "name" : "projects-facebook",
 | 
			
		||||
      "rule" : ".projects-facebook",
 | 
			
		||||
      "hash" : "16581191e4ce9e0115d447b479c886cb"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_facility"      : {
 | 
			
		||||
      "name" : "projects_facility",
 | 
			
		||||
      "rule" : ".projects_facility",
 | 
			
		||||
    "projects-facility"      : {
 | 
			
		||||
      "name" : "projects-facility",
 | 
			
		||||
      "rule" : ".projects-facility",
 | 
			
		||||
      "hash" : "d8893f9d2b75ec047b6f3898a386055c"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_film"          : {
 | 
			
		||||
      "name" : "projects_film",
 | 
			
		||||
      "rule" : ".projects_film",
 | 
			
		||||
    "projects-film"          : {
 | 
			
		||||
      "name" : "projects-film",
 | 
			
		||||
      "rule" : ".projects-film",
 | 
			
		||||
      "hash" : "57497050fa09ba1533d981a9c1550ba9"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_forked"        : {
 | 
			
		||||
      "name" : "projects_forked",
 | 
			
		||||
      "rule" : ".projects_forked",
 | 
			
		||||
    "projects-forked"        : {
 | 
			
		||||
      "name" : "projects-forked",
 | 
			
		||||
      "rule" : ".projects-forked",
 | 
			
		||||
      "hash" : "f575428e1079981840297bd444e51c43"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_games"         : {
 | 
			
		||||
      "name" : "projects_games",
 | 
			
		||||
      "rule" : ".projects_games",
 | 
			
		||||
    "projects-games"         : {
 | 
			
		||||
      "name" : "projects-games",
 | 
			
		||||
      "rule" : ".projects-games",
 | 
			
		||||
      "hash" : "b802cff3e76051675b37165bd9702088"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_ghost"         : {
 | 
			
		||||
      "name" : "projects_ghost",
 | 
			
		||||
      "rule" : ".projects_ghost",
 | 
			
		||||
    "projects-ghost"         : {
 | 
			
		||||
      "name" : "projects-ghost",
 | 
			
		||||
      "rule" : ".projects-ghost",
 | 
			
		||||
      "hash" : "7c8622cad29bddc5179f6a6d5f15fbe9"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_gift"          : {
 | 
			
		||||
      "name" : "projects_gift",
 | 
			
		||||
      "rule" : ".projects_gift",
 | 
			
		||||
    "projects-gift"          : {
 | 
			
		||||
      "name" : "projects-gift",
 | 
			
		||||
      "rule" : ".projects-gift",
 | 
			
		||||
      "hash" : "f2ca678906a6806f421b60abddaa6cae"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_globe"         : {
 | 
			
		||||
      "name" : "projects_globe",
 | 
			
		||||
      "rule" : ".projects_globe",
 | 
			
		||||
    "projects-globe"         : {
 | 
			
		||||
      "name" : "projects-globe",
 | 
			
		||||
      "rule" : ".projects-globe",
 | 
			
		||||
      "hash" : "87515a83cc0c840804aca594677d1eae"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_golf"          : {
 | 
			
		||||
      "name" : "projects_golf",
 | 
			
		||||
      "rule" : ".projects_golf",
 | 
			
		||||
    "projects-golf"          : {
 | 
			
		||||
      "name" : "projects-golf",
 | 
			
		||||
      "rule" : ".projects-golf",
 | 
			
		||||
      "hash" : "1ee7556fab3d46d925deb00322dad858"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_heart"         : {
 | 
			
		||||
      "name" : "projects_heart",
 | 
			
		||||
      "rule" : ".projects_heart",
 | 
			
		||||
    "projects-heart"         : {
 | 
			
		||||
      "name" : "projects-heart",
 | 
			
		||||
      "rule" : ".projects-heart",
 | 
			
		||||
      "hash" : "3da64839e37ee245333017d0a310cc2e"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_intergalactic" : {
 | 
			
		||||
      "name" : "projects_intergalactic",
 | 
			
		||||
      "rule" : ".projects_intergalactic",
 | 
			
		||||
    "projects-intergalactic" : {
 | 
			
		||||
      "name" : "projects-intergalactic",
 | 
			
		||||
      "rule" : ".projects-intergalactic",
 | 
			
		||||
      "hash" : "94dca756cb267bdb4e0ed58467320780"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_lock"          : {
 | 
			
		||||
      "name" : "projects_lock",
 | 
			
		||||
      "rule" : ".projects_lock",
 | 
			
		||||
    "projects-lock"          : {
 | 
			
		||||
      "name" : "projects-lock",
 | 
			
		||||
      "rule" : ".projects-lock",
 | 
			
		||||
      "hash" : "9d4c8ad3a4ac4163f284461da7df2763"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_mail"          : {
 | 
			
		||||
      "name" : "projects_mail",
 | 
			
		||||
      "rule" : ".projects_mail",
 | 
			
		||||
    "projects-mail"          : {
 | 
			
		||||
      "name" : "projects-mail",
 | 
			
		||||
      "rule" : ".projects-mail",
 | 
			
		||||
      "hash" : "963f5ce26c6caf86e72d754f7b6e8865"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_martini"       : {
 | 
			
		||||
      "name" : "projects_martini",
 | 
			
		||||
      "rule" : ".projects_martini",
 | 
			
		||||
    "projects-martini"       : {
 | 
			
		||||
      "name" : "projects-martini",
 | 
			
		||||
      "rule" : ".projects-martini",
 | 
			
		||||
      "hash" : "24d4d5fb5c334621ece4c35a9196471e"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_medical"       : {
 | 
			
		||||
      "name" : "projects_medical",
 | 
			
		||||
      "rule" : ".projects_medical",
 | 
			
		||||
    "projects-medical"       : {
 | 
			
		||||
      "name" : "projects-medical",
 | 
			
		||||
      "rule" : ".projects-medical",
 | 
			
		||||
      "hash" : "e0cb3ef5557321d166e8eb49c10d3599"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_mobile"        : {
 | 
			
		||||
      "name" : "projects_mobile",
 | 
			
		||||
      "rule" : ".projects_mobile",
 | 
			
		||||
    "projects-mobile"        : {
 | 
			
		||||
      "name" : "projects-mobile",
 | 
			
		||||
      "rule" : ".projects-mobile",
 | 
			
		||||
      "hash" : "37dec95d1a4a937743d52acac319c3b6"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_music"         : {
 | 
			
		||||
      "name" : "projects_music",
 | 
			
		||||
      "rule" : ".projects_music",
 | 
			
		||||
    "projects-music"         : {
 | 
			
		||||
      "name" : "projects-music",
 | 
			
		||||
      "rule" : ".projects-music",
 | 
			
		||||
      "hash" : "e7a814194685ac25be0db05b04074607"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_news"          : {
 | 
			
		||||
      "name" : "projects_news",
 | 
			
		||||
      "rule" : ".projects_news",
 | 
			
		||||
    "projects-news"          : {
 | 
			
		||||
      "name" : "projects-news",
 | 
			
		||||
      "rule" : ".projects-news",
 | 
			
		||||
      "hash" : "6861f3ee827d09b0592166514f4941e8"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_orgchart"      : {
 | 
			
		||||
      "name" : "projects_orgchart",
 | 
			
		||||
      "rule" : ".projects_orgchart",
 | 
			
		||||
    "projects-orgchart"      : {
 | 
			
		||||
      "name" : "projects-orgchart",
 | 
			
		||||
      "rule" : ".projects-orgchart",
 | 
			
		||||
      "hash" : "20c51c59788fb2bc8184fdd5687d33dc"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_peoples"       : {
 | 
			
		||||
      "name" : "projects_peoples",
 | 
			
		||||
      "rule" : ".projects_peoples",
 | 
			
		||||
    "projects-peoples"       : {
 | 
			
		||||
      "name" : "projects-peoples",
 | 
			
		||||
      "rule" : ".projects-peoples",
 | 
			
		||||
      "hash" : "c949ba6d09e68317a9a11482e75e5140"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_piechart"      : {
 | 
			
		||||
      "name" : "projects_piechart",
 | 
			
		||||
      "rule" : ".projects_piechart",
 | 
			
		||||
    "projects-piechart"      : {
 | 
			
		||||
      "name" : "projects-piechart",
 | 
			
		||||
      "rule" : ".projects-piechart",
 | 
			
		||||
      "hash" : "051138560e30982a029aa5e4ea87bc17"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_poison"        : {
 | 
			
		||||
      "name" : "projects_poison",
 | 
			
		||||
      "rule" : ".projects_poison",
 | 
			
		||||
    "projects-poison"        : {
 | 
			
		||||
      "name" : "projects-poison",
 | 
			
		||||
      "rule" : ".projects-poison",
 | 
			
		||||
      "hash" : "56ddafd138e421f198b9cb38e5dc7455"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_putabirdonit"  : {
 | 
			
		||||
      "name" : "projects_putabirdonit",
 | 
			
		||||
      "rule" : ".projects_putabirdonit",
 | 
			
		||||
    "projects-putabirdonit"  : {
 | 
			
		||||
      "name" : "projects-putabirdonit",
 | 
			
		||||
      "rule" : ".projects-putabirdonit",
 | 
			
		||||
      "hash" : "ee298fff82c34341b986a3e1b77bea11"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_radiate"       : {
 | 
			
		||||
      "name" : "projects_radiate",
 | 
			
		||||
      "rule" : ".projects_radiate",
 | 
			
		||||
    "projects-radiate"       : {
 | 
			
		||||
      "name" : "projects-radiate",
 | 
			
		||||
      "rule" : ".projects-radiate",
 | 
			
		||||
      "hash" : "9cfb918089b3de8506a5d270a119052c"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_savings"       : {
 | 
			
		||||
      "name" : "projects_savings",
 | 
			
		||||
      "rule" : ".projects_savings",
 | 
			
		||||
    "projects-savings"       : {
 | 
			
		||||
      "name" : "projects-savings",
 | 
			
		||||
      "rule" : ".projects-savings",
 | 
			
		||||
      "hash" : "9e92bc5e64f79d2f4842ac24a8b57fcb"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_search"        : {
 | 
			
		||||
      "name" : "projects_search",
 | 
			
		||||
      "rule" : ".projects_search",
 | 
			
		||||
    "projects-search"        : {
 | 
			
		||||
      "name" : "projects-search",
 | 
			
		||||
      "rule" : ".projects-search",
 | 
			
		||||
      "hash" : "a42c1c31f2929838b0f181f417c0b6a4"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_shield"        : {
 | 
			
		||||
      "name" : "projects_shield",
 | 
			
		||||
      "rule" : ".projects_shield",
 | 
			
		||||
    "projects-shield"        : {
 | 
			
		||||
      "name" : "projects-shield",
 | 
			
		||||
      "rule" : ".projects-shield",
 | 
			
		||||
      "hash" : "40c6e1bec7c07c165668ac45c218847a"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_speed"         : {
 | 
			
		||||
      "name" : "projects_speed",
 | 
			
		||||
      "rule" : ".projects_speed",
 | 
			
		||||
    "projects-speed"         : {
 | 
			
		||||
      "name" : "projects-speed",
 | 
			
		||||
      "rule" : ".projects-speed",
 | 
			
		||||
      "hash" : "2b70c194d07f5a9d95abc51d84fb22ed"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_sprint"        : {
 | 
			
		||||
      "name" : "projects_sprint",
 | 
			
		||||
      "rule" : ".projects_sprint",
 | 
			
		||||
    "projects-sprint"        : {
 | 
			
		||||
      "name" : "projects-sprint",
 | 
			
		||||
      "rule" : ".projects-sprint",
 | 
			
		||||
      "hash" : "655ef9a3043eab23eac1da21baeb36b3"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_star"          : {
 | 
			
		||||
      "name" : "projects_star",
 | 
			
		||||
      "rule" : ".projects_star",
 | 
			
		||||
    "projects-star"          : {
 | 
			
		||||
      "name" : "projects-star",
 | 
			
		||||
      "rule" : ".projects-star",
 | 
			
		||||
      "hash" : "a46e3c18f68bc13a65b410496e27b5d7"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_storage"       : {
 | 
			
		||||
      "name" : "projects_storage",
 | 
			
		||||
      "rule" : ".projects_storage",
 | 
			
		||||
    "projects-storage"       : {
 | 
			
		||||
      "name" : "projects-storage",
 | 
			
		||||
      "rule" : ".projects-storage",
 | 
			
		||||
      "hash" : "bb19baa77bb7596f43f77e5dbbddb006"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_tablet"        : {
 | 
			
		||||
      "name" : "projects_tablet",
 | 
			
		||||
      "rule" : ".projects_tablet",
 | 
			
		||||
    "projects-tablet"        : {
 | 
			
		||||
      "name" : "projects-tablet",
 | 
			
		||||
      "rule" : ".projects-tablet",
 | 
			
		||||
      "hash" : "830dcf6637288ca122c8f5034cae3769"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_travel"        : {
 | 
			
		||||
      "name" : "projects_travel",
 | 
			
		||||
      "rule" : ".projects_travel",
 | 
			
		||||
    "projects-travel"        : {
 | 
			
		||||
      "name" : "projects-travel",
 | 
			
		||||
      "rule" : ".projects-travel",
 | 
			
		||||
      "hash" : "86ec4dcd025879a43435b101fd542a1b"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_twitter"       : {
 | 
			
		||||
      "name" : "projects_twitter",
 | 
			
		||||
      "rule" : ".projects_twitter",
 | 
			
		||||
    "projects-twitter"       : {
 | 
			
		||||
      "name" : "projects-twitter",
 | 
			
		||||
      "rule" : ".projects-twitter",
 | 
			
		||||
      "hash" : "75b8680dd1e4ecce4ca3a39c87e1ed80"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_warning"       : {
 | 
			
		||||
      "name" : "projects_warning",
 | 
			
		||||
      "rule" : ".projects_warning",
 | 
			
		||||
    "projects-warning"       : {
 | 
			
		||||
      "name" : "projects-warning",
 | 
			
		||||
      "rule" : ".projects-warning",
 | 
			
		||||
      "hash" : "3ac48b6f963675e1f4bb4ac75aad936f"
 | 
			
		||||
    },
 | 
			
		||||
    "projects_whale"         : {
 | 
			
		||||
      "name" : "projects_whale",
 | 
			
		||||
      "rule" : ".projects_whale",
 | 
			
		||||
    "projects-whale"         : {
 | 
			
		||||
      "name" : "projects-whale",
 | 
			
		||||
      "rule" : ".projects-whale",
 | 
			
		||||
      "hash" : "569b584c7e80a0a9b965280abd27c723"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB  | 
| 
		 Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB  | 
@@ -1791,6 +1791,18 @@ celerity_register_resource_map(array(
 | 
			
		||||
    ),
 | 
			
		||||
    'disk' => '/rsrc/js/core/behavior-history-install.js',
 | 
			
		||||
  ),
 | 
			
		||||
  'javelin-behavior-icon-composer' =>
 | 
			
		||||
  array(
 | 
			
		||||
    'uri' => '/res/0be5c462/rsrc/js/application/files/behavior-icon-composer.js',
 | 
			
		||||
    'type' => 'js',
 | 
			
		||||
    'requires' =>
 | 
			
		||||
    array(
 | 
			
		||||
      0 => 'javelin-behavior',
 | 
			
		||||
      1 => 'javelin-dom',
 | 
			
		||||
      2 => 'javelin-stratcom',
 | 
			
		||||
    ),
 | 
			
		||||
    'disk' => '/rsrc/js/application/files/behavior-icon-composer.js',
 | 
			
		||||
  ),
 | 
			
		||||
  'javelin-behavior-konami' =>
 | 
			
		||||
  array(
 | 
			
		||||
    'uri' => '/res/b7bb7c24/rsrc/js/core/behavior-konami.js',
 | 
			
		||||
@@ -1802,6 +1814,18 @@ celerity_register_resource_map(array(
 | 
			
		||||
    ),
 | 
			
		||||
    'disk' => '/rsrc/js/core/behavior-konami.js',
 | 
			
		||||
  ),
 | 
			
		||||
  'javelin-behavior-launch-icon-composer' =>
 | 
			
		||||
  array(
 | 
			
		||||
    'uri' => '/res/202488ac/rsrc/js/application/files/behavior-launch-icon-composer.js',
 | 
			
		||||
    'type' => 'js',
 | 
			
		||||
    'requires' =>
 | 
			
		||||
    array(
 | 
			
		||||
      0 => 'javelin-behavior',
 | 
			
		||||
      1 => 'javelin-dom',
 | 
			
		||||
      2 => 'javelin-workflow',
 | 
			
		||||
    ),
 | 
			
		||||
    'disk' => '/rsrc/js/application/files/behavior-launch-icon-composer.js',
 | 
			
		||||
  ),
 | 
			
		||||
  'javelin-behavior-lightbox-attachments' =>
 | 
			
		||||
  array(
 | 
			
		||||
    'uri' => '/res/72b4d3a8/rsrc/js/core/behavior-lightbox-attachments.js',
 | 
			
		||||
@@ -3062,7 +3086,7 @@ celerity_register_resource_map(array(
 | 
			
		||||
  ),
 | 
			
		||||
  'people-profile-css' =>
 | 
			
		||||
  array(
 | 
			
		||||
    'uri' => '/res/d50d9502/rsrc/css/application/people/people-profile.css',
 | 
			
		||||
    'uri' => '/res/f1da102e/rsrc/css/application/people/people-profile.css',
 | 
			
		||||
    'type' => 'css',
 | 
			
		||||
    'requires' =>
 | 
			
		||||
    array(
 | 
			
		||||
@@ -4227,7 +4251,7 @@ celerity_register_resource_map(array(
 | 
			
		||||
  ),
 | 
			
		||||
  'sprite-projects-css' =>
 | 
			
		||||
  array(
 | 
			
		||||
    'uri' => '/res/3ff34b69/rsrc/css/sprite-projects.css',
 | 
			
		||||
    'uri' => '/res/40eacbfb/rsrc/css/sprite-projects.css',
 | 
			
		||||
    'type' => 'css',
 | 
			
		||||
    'requires' =>
 | 
			
		||||
    array(
 | 
			
		||||
 
 | 
			
		||||
@@ -1209,6 +1209,7 @@ phutil_register_library_map(array(
 | 
			
		||||
    'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php',
 | 
			
		||||
    'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php',
 | 
			
		||||
    'PhabricatorFileCommentController' => 'applications/files/controller/PhabricatorFileCommentController.php',
 | 
			
		||||
    'PhabricatorFileComposeController' => 'applications/files/controller/PhabricatorFileComposeController.php',
 | 
			
		||||
    'PhabricatorFileController' => 'applications/files/controller/PhabricatorFileController.php',
 | 
			
		||||
    'PhabricatorFileDAO' => 'applications/files/storage/PhabricatorFileDAO.php',
 | 
			
		||||
    'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php',
 | 
			
		||||
@@ -1523,6 +1524,7 @@ phutil_register_library_map(array(
 | 
			
		||||
    'PhabricatorProjectProfile' => 'applications/project/storage/PhabricatorProjectProfile.php',
 | 
			
		||||
    'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
 | 
			
		||||
    'PhabricatorProjectProfileEditController' => 'applications/project/controller/PhabricatorProjectProfileEditController.php',
 | 
			
		||||
    'PhabricatorProjectProfilePictureController' => 'applications/project/controller/PhabricatorProjectProfilePictureController.php',
 | 
			
		||||
    'PhabricatorProjectQuery' => 'applications/project/query/PhabricatorProjectQuery.php',
 | 
			
		||||
    'PhabricatorProjectSearchEngine' => 'applications/project/query/PhabricatorProjectSearchEngine.php',
 | 
			
		||||
    'PhabricatorProjectSearchIndexer' => 'applications/project/search/PhabricatorProjectSearchIndexer.php',
 | 
			
		||||
@@ -3397,6 +3399,7 @@ phutil_register_library_map(array(
 | 
			
		||||
      3 => 'PhabricatorPolicyInterface',
 | 
			
		||||
    ),
 | 
			
		||||
    'PhabricatorFileCommentController' => 'PhabricatorFileController',
 | 
			
		||||
    'PhabricatorFileComposeController' => 'PhabricatorFileController',
 | 
			
		||||
    'PhabricatorFileController' => 'PhabricatorController',
 | 
			
		||||
    'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
 | 
			
		||||
    'PhabricatorFileDataController' => 'PhabricatorFileController',
 | 
			
		||||
@@ -3739,6 +3742,7 @@ phutil_register_library_map(array(
 | 
			
		||||
    'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
 | 
			
		||||
    'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
 | 
			
		||||
    'PhabricatorProjectProfileEditController' => 'PhabricatorProjectController',
 | 
			
		||||
    'PhabricatorProjectProfilePictureController' => 'PhabricatorProjectController',
 | 
			
		||||
    'PhabricatorProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
 | 
			
		||||
    'PhabricatorProjectSearchEngine' => 'PhabricatorApplicationSearchEngine',
 | 
			
		||||
    'PhabricatorProjectSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,7 @@ final class PhabricatorApplicationFiles extends PhabricatorApplication {
 | 
			
		||||
        '(query/(?P<key>[^/]+)/)?' => 'PhabricatorFileListController',
 | 
			
		||||
        'upload/' => 'PhabricatorFileUploadController',
 | 
			
		||||
        'dropupload/' => 'PhabricatorFileDropUploadController',
 | 
			
		||||
        'compose/' => 'PhabricatorFileComposeController',
 | 
			
		||||
        'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorFileCommentController',
 | 
			
		||||
        'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
 | 
			
		||||
        'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,249 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
final class PhabricatorFileComposeController
 | 
			
		||||
  extends PhabricatorFileController {
 | 
			
		||||
 | 
			
		||||
  public function processRequest() {
 | 
			
		||||
    $request = $this->getRequest();
 | 
			
		||||
    $viewer = $request->getUser();
 | 
			
		||||
 | 
			
		||||
    $colors = array(
 | 
			
		||||
      'red' => pht('Verbillion'),
 | 
			
		||||
      'orange' => pht('Navel Orange'),
 | 
			
		||||
      'yellow' => pht('Prim Goldenrod'),
 | 
			
		||||
      'green' => pht('Lustrous Verdant'),
 | 
			
		||||
      'blue' => pht('Tropical Deep'),
 | 
			
		||||
      'sky' => pht('Wide Open Sky'),
 | 
			
		||||
      'indigo' => pht('Pleated Khaki'),
 | 
			
		||||
      'violet' => pht('Aged Merlot'),
 | 
			
		||||
      'charcoal' => pht('Gemstone'),
 | 
			
		||||
      'backdrop' => pht('Driven Snow'),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    $manifest = PHUIIconView::getSheetManifest(PHUIIconView::SPRITE_PROJECTS);
 | 
			
		||||
 | 
			
		||||
    if ($request->isFormPost()) {
 | 
			
		||||
      $icon = $request->getStr('icon');
 | 
			
		||||
      $color = $request->getStr('color');
 | 
			
		||||
 | 
			
		||||
      if (isset($colors[$color]) && isset($manifest['projects-'.$icon])) {
 | 
			
		||||
        $root = dirname(phutil_get_library_root('phabricator'));
 | 
			
		||||
        $icon_file = $root.'/resources/sprite/projects_1x/'.$icon.'.png';
 | 
			
		||||
        $icon_data = Filesystem::readFile($icon_file);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $data = $this->composeImage($color, $icon_data);
 | 
			
		||||
 | 
			
		||||
        $file = PhabricatorFile::buildFromFileDataOrHash(
 | 
			
		||||
          $data,
 | 
			
		||||
          array(
 | 
			
		||||
            'name' => 'project.png',
 | 
			
		||||
          ));
 | 
			
		||||
 | 
			
		||||
        $content = array(
 | 
			
		||||
          'phid' => $file->getPHID(),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return id(new AphrontAjaxResponse())->setContent($content);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $value_color = head_key($colors);
 | 
			
		||||
    $value_icon = head_key($manifest);
 | 
			
		||||
    $value_icon = substr($value_icon, strlen('projects-'));
 | 
			
		||||
 | 
			
		||||
    require_celerity_resource('people-profile-css');
 | 
			
		||||
 | 
			
		||||
    $buttons = array();
 | 
			
		||||
    foreach ($colors as $color => $name) {
 | 
			
		||||
      $buttons[] = javelin_tag(
 | 
			
		||||
        'button',
 | 
			
		||||
        array(
 | 
			
		||||
          'class' => 'grey profile-image-button',
 | 
			
		||||
          'sigil' => 'has-tooltip compose-select-color',
 | 
			
		||||
          'style' => 'margin: 0 8px 8px 0',
 | 
			
		||||
          'meta' => array(
 | 
			
		||||
            'color' => $color,
 | 
			
		||||
            'tip' => $name,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        id(new PHUIIconView())
 | 
			
		||||
          ->addClass('compose-background-'.$color));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $icons = array();
 | 
			
		||||
 | 
			
		||||
    $icon_quips = array(
 | 
			
		||||
      '8ball' => pht('Take a Risk'),
 | 
			
		||||
      'alien' => pht('Foreign Interface'),
 | 
			
		||||
      'announce' => pht('Louder is Better'),
 | 
			
		||||
      'art' => pht('Unique Snowflake'),
 | 
			
		||||
      'award' => pht('Shooting Star'),
 | 
			
		||||
      'bacon' => pht('Healthy Vegetables'),
 | 
			
		||||
      'bandaid' => pht('Durable Infrastructure'),
 | 
			
		||||
      'beer' => pht('Healthy Vegetable Juice'),
 | 
			
		||||
      'bomb' => pht('Imminent Success'),
 | 
			
		||||
      'briefcase' => pht('Adventure Pack'),
 | 
			
		||||
      'bug' => pht('Costumed Egg'),
 | 
			
		||||
      'calendar' => pht('Everyone Loves Meetings'),
 | 
			
		||||
      'cloud' => pht('Water Cycle'),
 | 
			
		||||
      'coffee' => pht('Half-Whip Nonfat Soy Latte'),
 | 
			
		||||
      'creditcard' => pht('Expense It'),
 | 
			
		||||
      'death' => pht('Calcium Promotes Bone Health'),
 | 
			
		||||
      'desktop' => pht('Magical Portal'),
 | 
			
		||||
      'dropbox' => pht('Cardboard Box'),
 | 
			
		||||
      'education' => pht('Debt'),
 | 
			
		||||
      'experimental' => pht('CAUTION: Dangerous Chemicals'),
 | 
			
		||||
      'facebook' => pht('Popular Social Network'),
 | 
			
		||||
      'facility' => pht('Pollution Solves Problems'),
 | 
			
		||||
      'film' => pht('Actual Physical Film'),
 | 
			
		||||
      'forked' => pht('You Can\'t Eat Soup'),
 | 
			
		||||
      'games' => pht('Serious Business'),
 | 
			
		||||
      'ghost' => pht('Haunted'),
 | 
			
		||||
      'gift' => pht('Surprise!'),
 | 
			
		||||
      'globe' => pht('Scanner Sweep'),
 | 
			
		||||
      'golf' => pht('Business Meeting'),
 | 
			
		||||
      'heart' => pht('Undergoing a Major Surgery'),
 | 
			
		||||
      'intergalactic' => pht('Jupiter'),
 | 
			
		||||
      'lock' => pht('Extremely Secret'),
 | 
			
		||||
      'mail' => pht('Oragami'),
 | 
			
		||||
      'martini' => pht('Healthy Olive Drink'),
 | 
			
		||||
      'medical' => pht('Medic!'),
 | 
			
		||||
      'mobile' => pht('Cellular Telephone'),
 | 
			
		||||
      'music' => pht("\xE2\x99\xAB"),
 | 
			
		||||
      'news' => pht('Actual Physical Newspaper'),
 | 
			
		||||
      'orgchart' => pht('It\'s Good to be King'),
 | 
			
		||||
      'peoples' => pht('Angel and Devil'),
 | 
			
		||||
      'piechart' => pht('Actual Physical Pie'),
 | 
			
		||||
      'poison' => pht('Healthy Bone Juice'),
 | 
			
		||||
      'putabirdonit' => pht('Put a Bird On It'),
 | 
			
		||||
      'radiate' => pht('Radiant Beauty'),
 | 
			
		||||
      'savings' => pht('Oink Oink'),
 | 
			
		||||
      'search' => pht('Sleuthing'),
 | 
			
		||||
      'shield' => pht('Royal Crest'),
 | 
			
		||||
      'speed' => pht('Slow and Steady'),
 | 
			
		||||
      'sprint' => pht('Fire Exit'),
 | 
			
		||||
      'star' => pht('The More You Know'),
 | 
			
		||||
      'storage' => pht('Stack of Pancakes'),
 | 
			
		||||
      'tablet' => pht('Cellular Telephone For Giants'),
 | 
			
		||||
      'travel' => pht('Pretty Clearly an Airplane'),
 | 
			
		||||
      'twitter' => pht('Bird Stencil'),
 | 
			
		||||
      'warning' => pht('No Caution Required, Everything Looks Safe'),
 | 
			
		||||
      'whale' => pht('Friendly Walrus'),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    foreach ($manifest as $icon => $spec) {
 | 
			
		||||
      $icon = substr($icon, strlen('projects-'));
 | 
			
		||||
 | 
			
		||||
      $icons[] = javelin_tag(
 | 
			
		||||
        'button',
 | 
			
		||||
        array(
 | 
			
		||||
          'class' => 'grey profile-image-button',
 | 
			
		||||
          'sigil' => 'has-tooltip compose-select-icon',
 | 
			
		||||
          'style' => 'margin: 0 8px 8px 0',
 | 
			
		||||
          'meta' => array(
 | 
			
		||||
            'icon' => $icon,
 | 
			
		||||
            'tip' => idx($icon_quips, $icon, $icon),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        id(new PHUIIconView())
 | 
			
		||||
          ->setSpriteIcon($icon)
 | 
			
		||||
          ->setSpriteSheet(PHUIIconView::SPRITE_PROJECTS));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $dialog_id = celerity_generate_unique_node_id();
 | 
			
		||||
    $color_input_id = celerity_generate_unique_node_id();;
 | 
			
		||||
    $icon_input_id = celerity_generate_unique_node_id();
 | 
			
		||||
    $preview_id = celerity_generate_unique_node_id();
 | 
			
		||||
 | 
			
		||||
    $preview = id(new PHUIIconView())
 | 
			
		||||
      ->setID($preview_id)
 | 
			
		||||
      ->addClass('compose-background-'.$value_color)
 | 
			
		||||
      ->setSpriteIcon($value_icon)
 | 
			
		||||
      ->setSpriteSheet(PHUIIconView::SPRITE_PROJECTS);
 | 
			
		||||
 | 
			
		||||
    $color_input = javelin_tag(
 | 
			
		||||
      'input',
 | 
			
		||||
      array(
 | 
			
		||||
        'type' => 'hidden',
 | 
			
		||||
        'name' => 'color',
 | 
			
		||||
        'value' => $value_color,
 | 
			
		||||
        'id' => $color_input_id,
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    $icon_input = javelin_tag(
 | 
			
		||||
      'input',
 | 
			
		||||
      array(
 | 
			
		||||
        'type' => 'hidden',
 | 
			
		||||
        'name' => 'icon',
 | 
			
		||||
        'value' => $value_icon,
 | 
			
		||||
        'id' => $icon_input_id,
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    Javelin::initBehavior('phabricator-tooltips');
 | 
			
		||||
    Javelin::initBehavior(
 | 
			
		||||
      'icon-composer',
 | 
			
		||||
      array(
 | 
			
		||||
        'dialogID' => $dialog_id,
 | 
			
		||||
        'colorInputID' => $color_input_id,
 | 
			
		||||
        'iconInputID' => $icon_input_id,
 | 
			
		||||
        'previewID' => $preview_id,
 | 
			
		||||
        'defaultColor' => $value_color,
 | 
			
		||||
        'defaultIcon' => $value_icon,
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    $dialog = id(new AphrontDialogView())
 | 
			
		||||
      ->setUser($viewer)
 | 
			
		||||
      ->setFormID($dialog_id)
 | 
			
		||||
      ->setClass('compose-dialog')
 | 
			
		||||
      ->setTitle(pht('Compose Image'))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        phutil_tag(
 | 
			
		||||
          'div',
 | 
			
		||||
          array(
 | 
			
		||||
            'class' => 'compose-header',
 | 
			
		||||
          ),
 | 
			
		||||
          pht('Choose Background Color')))
 | 
			
		||||
      ->appendChild($buttons)
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        phutil_tag(
 | 
			
		||||
          'div',
 | 
			
		||||
          array(
 | 
			
		||||
            'class' => 'compose-header',
 | 
			
		||||
          ),
 | 
			
		||||
          pht('Choose Icon')))
 | 
			
		||||
      ->appendChild($icons)
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        phutil_tag(
 | 
			
		||||
          'div',
 | 
			
		||||
          array(
 | 
			
		||||
            'class' => 'compose-header',
 | 
			
		||||
          ),
 | 
			
		||||
          pht('Preview')))
 | 
			
		||||
      ->appendChild($preview)
 | 
			
		||||
      ->appendChild($color_input)
 | 
			
		||||
      ->appendChild($icon_input)
 | 
			
		||||
      ->addCancelButton('/')
 | 
			
		||||
      ->addSubmitButton(pht('Save Image'));
 | 
			
		||||
 | 
			
		||||
    return id(new AphrontDialogResponse())->setDialog($dialog);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private function composeImage($color, $icon_data) {
 | 
			
		||||
    $icon_img = imagecreatefromstring($icon_data);
 | 
			
		||||
 | 
			
		||||
    $map = CelerityResourceTransformer::getCSSVariableMap();
 | 
			
		||||
    $color_string = idx($map, $color, '#ff00ff');
 | 
			
		||||
    $color_const = hexdec(trim($color_string, '#'));
 | 
			
		||||
 | 
			
		||||
    $canvas = imagecreatetruecolor(50, 50);
 | 
			
		||||
    imagefill($canvas, 0, 0, $color_const);
 | 
			
		||||
 | 
			
		||||
    imagecopy($canvas, $icon_img, 0, 0, 0, 0, 50, 50);
 | 
			
		||||
 | 
			
		||||
    return PhabricatorImageTransformer::saveImageDataInAnyFormat(
 | 
			
		||||
      $canvas,
 | 
			
		||||
      'image/png');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -42,6 +42,8 @@ final class PhabricatorApplicationProject extends PhabricatorApplication {
 | 
			
		||||
          => 'PhabricatorProjectMembersEditController',
 | 
			
		||||
        'view/(?P<id>[1-9]\d*)/(?:(?P<page>\w+)/)?'
 | 
			
		||||
          => 'PhabricatorProjectProfileController',
 | 
			
		||||
        'picture/(?P<id>[1-9]\d*)/' =>
 | 
			
		||||
          'PhabricatorProjectProfilePictureController',
 | 
			
		||||
        'create/' => 'PhabricatorProjectCreateController',
 | 
			
		||||
        'update/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
 | 
			
		||||
          => 'PhabricatorProjectUpdateController',
 | 
			
		||||
 
 | 
			
		||||
@@ -216,6 +216,14 @@ final class PhabricatorProjectProfileController
 | 
			
		||||
        ->setDisabled(!$can_edit)
 | 
			
		||||
        ->setWorkflow(!$can_edit));
 | 
			
		||||
 | 
			
		||||
    $view->addAction(
 | 
			
		||||
      id(new PhabricatorActionView())
 | 
			
		||||
        ->setName(pht('Edit Picture'))
 | 
			
		||||
        ->setIcon('image')
 | 
			
		||||
        ->setHref($this->getApplicationURI("picture/{$id}/"))
 | 
			
		||||
        ->setDisabled(!$can_edit)
 | 
			
		||||
        ->setWorkflow(!$can_edit));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    $action = null;
 | 
			
		||||
    if (!$project->isUserMember($viewer->getPHID())) {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,14 +29,9 @@ final class PhabricatorProjectProfileEditController
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $profile = $project->getProfile();
 | 
			
		||||
    $img_src = $profile->getProfileImageURI();
 | 
			
		||||
 | 
			
		||||
    $options = PhabricatorProjectStatus::getStatusMap();
 | 
			
		||||
 | 
			
		||||
    $supported_formats = PhabricatorFile::getTransformableImageFormats();
 | 
			
		||||
 | 
			
		||||
    $e_name = true;
 | 
			
		||||
    $e_image = null;
 | 
			
		||||
 | 
			
		||||
    $errors = array();
 | 
			
		||||
    if ($request->isFormPost()) {
 | 
			
		||||
@@ -89,37 +84,6 @@ final class PhabricatorProjectProfileEditController
 | 
			
		||||
        $e_name = null;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $default_image = $request->getExists('default_image');
 | 
			
		||||
      if ($default_image) {
 | 
			
		||||
        $profile->setProfileImagePHID(null);
 | 
			
		||||
      } else if (!empty($_FILES['image'])) {
 | 
			
		||||
        $err = idx($_FILES['image'], 'error');
 | 
			
		||||
        if ($err != UPLOAD_ERR_NO_FILE) {
 | 
			
		||||
          $file = PhabricatorFile::newFromPHPUpload(
 | 
			
		||||
            $_FILES['image'],
 | 
			
		||||
            array(
 | 
			
		||||
              'authorPHID' => $user->getPHID(),
 | 
			
		||||
            ));
 | 
			
		||||
          $okay = $file->isTransformableImage();
 | 
			
		||||
          if ($okay) {
 | 
			
		||||
            $xformer = new PhabricatorImageTransformer();
 | 
			
		||||
            $xformed = $xformer->executeThumbTransform(
 | 
			
		||||
              $file,
 | 
			
		||||
              $x = 50,
 | 
			
		||||
              $y = 50);
 | 
			
		||||
 | 
			
		||||
            $profile->setProfileImagePHID($xformed->getPHID());
 | 
			
		||||
            $xformed->attachToObject($user, $project->getPHID());
 | 
			
		||||
 | 
			
		||||
          } else {
 | 
			
		||||
            $e_image = pht('Not Supported');
 | 
			
		||||
            $errors[] =
 | 
			
		||||
              pht('This server only supports these image formats:').' '.
 | 
			
		||||
              implode(', ', $supported_formats).'.';
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!$errors) {
 | 
			
		||||
        $project->save();
 | 
			
		||||
        $profile->setProjectPHID($project->getPHID());
 | 
			
		||||
@@ -150,7 +114,6 @@ final class PhabricatorProjectProfileEditController
 | 
			
		||||
      ->setID('project-edit-form')
 | 
			
		||||
      ->setUser($user)
 | 
			
		||||
      ->setAction($action)
 | 
			
		||||
      ->setEncType('multipart/form-data')
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormTextControl())
 | 
			
		||||
          ->setLabel(pht('Name'))
 | 
			
		||||
@@ -198,22 +161,6 @@ final class PhabricatorProjectProfileEditController
 | 
			
		||||
          ->setPolicyObject($project)
 | 
			
		||||
          ->setPolicies($policies)
 | 
			
		||||
          ->setCapability(PhabricatorPolicyCapability::CAN_JOIN))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormMarkupControl())
 | 
			
		||||
          ->setLabel(pht('Profile Image'))
 | 
			
		||||
          ->setValue(
 | 
			
		||||
            phutil_tag(
 | 
			
		||||
              'img',
 | 
			
		||||
              array(
 | 
			
		||||
                'src' => $img_src,
 | 
			
		||||
              ))))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormImageControl())
 | 
			
		||||
          ->setLabel(pht('Change Image'))
 | 
			
		||||
          ->setName('image')
 | 
			
		||||
          ->setError($e_image)
 | 
			
		||||
          ->setCaption(
 | 
			
		||||
            pht('Supported formats:').' '.implode(', ', $supported_formats)))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormSubmitControl())
 | 
			
		||||
          ->addCancelButton('/project/view/'.$project->getID().'/')
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,274 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
final class PhabricatorProjectProfilePictureController
 | 
			
		||||
  extends PhabricatorProjectController {
 | 
			
		||||
 | 
			
		||||
  private $id;
 | 
			
		||||
 | 
			
		||||
  public function willProcessRequest(array $data) {
 | 
			
		||||
    $this->id = $data['id'];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function processRequest() {
 | 
			
		||||
    $request = $this->getRequest();
 | 
			
		||||
    $viewer = $request->getUser();
 | 
			
		||||
 | 
			
		||||
    $project = id(new PhabricatorProjectQuery())
 | 
			
		||||
      ->setViewer($viewer)
 | 
			
		||||
      ->withIDs(array($this->id))
 | 
			
		||||
      ->needProfiles(true)
 | 
			
		||||
      ->requireCapabilities(
 | 
			
		||||
        array(
 | 
			
		||||
          PhabricatorPolicyCapability::CAN_VIEW,
 | 
			
		||||
          PhabricatorPolicyCapability::CAN_EDIT,
 | 
			
		||||
        ))
 | 
			
		||||
      ->executeOne();
 | 
			
		||||
    if (!$project) {
 | 
			
		||||
      return new Aphront404Response();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $project_uri = $this->getApplicationURI('view/'.$project->getID().'/');
 | 
			
		||||
 | 
			
		||||
    $supported_formats = PhabricatorFile::getTransformableImageFormats();
 | 
			
		||||
    $e_file = true;
 | 
			
		||||
    $errors = array();
 | 
			
		||||
 | 
			
		||||
    if ($request->isFormPost()) {
 | 
			
		||||
      $phid = $request->getStr('phid');
 | 
			
		||||
      $is_default = false;
 | 
			
		||||
      if ($phid == PhabricatorPHIDConstants::PHID_VOID) {
 | 
			
		||||
        $phid = null;
 | 
			
		||||
        $is_default = true;
 | 
			
		||||
      } else if ($phid) {
 | 
			
		||||
        $file = id(new PhabricatorFileQuery())
 | 
			
		||||
          ->setViewer($viewer)
 | 
			
		||||
          ->withPHIDs(array($phid))
 | 
			
		||||
          ->executeOne();
 | 
			
		||||
      } else {
 | 
			
		||||
        if ($request->getFileExists('picture')) {
 | 
			
		||||
          $file = PhabricatorFile::newFromPHPUpload(
 | 
			
		||||
            $_FILES['picture'],
 | 
			
		||||
            array(
 | 
			
		||||
              'authorPHID' => $viewer->getPHID(),
 | 
			
		||||
            ));
 | 
			
		||||
        } else {
 | 
			
		||||
          $e_file = pht('Required');
 | 
			
		||||
          $errors[] = pht(
 | 
			
		||||
            'You must choose a file when uploading a new project picture.');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!$errors && !$is_default) {
 | 
			
		||||
        if (!$file->isTransformableImage()) {
 | 
			
		||||
          $e_file = pht('Not Supported');
 | 
			
		||||
          $errors[] = pht(
 | 
			
		||||
            'This server only supports these image formats: %s.',
 | 
			
		||||
            implode(', ', $supported_formats));
 | 
			
		||||
        } else {
 | 
			
		||||
          $xformer = new PhabricatorImageTransformer();
 | 
			
		||||
          $xformed = $xformer->executeProfileTransform(
 | 
			
		||||
            $file,
 | 
			
		||||
            $width = 50,
 | 
			
		||||
            $min_height = 50,
 | 
			
		||||
            $max_height = 50);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!$errors) {
 | 
			
		||||
        $profile = $project->getProfile();
 | 
			
		||||
        if ($is_default) {
 | 
			
		||||
          $profile->setProfileImagePHID(null);
 | 
			
		||||
        } else {
 | 
			
		||||
          $profile->setProfileImagePHID($xformed->getPHID());
 | 
			
		||||
          $xformed->attachToObject($viewer, $project->getPHID());
 | 
			
		||||
        }
 | 
			
		||||
        $profile->save();
 | 
			
		||||
        return id(new AphrontRedirectResponse())->setURI($project_uri);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $title = pht('Edit Project Picture');
 | 
			
		||||
    $crumbs = $this->buildApplicationCrumbs();
 | 
			
		||||
    $crumbs->addCrumb(
 | 
			
		||||
      id(new PhabricatorCrumbView())
 | 
			
		||||
        ->setName($project->getName())
 | 
			
		||||
        ->setHref($project_uri));
 | 
			
		||||
    $crumbs->addCrumb(
 | 
			
		||||
      id(new PhabricatorCrumbView())
 | 
			
		||||
        ->setName($title));
 | 
			
		||||
 | 
			
		||||
    $form = id(new PHUIFormLayoutView())
 | 
			
		||||
      ->setUser($viewer);
 | 
			
		||||
 | 
			
		||||
    $default_image = PhabricatorFile::loadBuiltin($viewer, 'project.png');
 | 
			
		||||
 | 
			
		||||
    $images = array();
 | 
			
		||||
 | 
			
		||||
    $current = $project->getProfile()->getProfileImagePHID();
 | 
			
		||||
    $has_current = false;
 | 
			
		||||
    if ($current) {
 | 
			
		||||
      $files = id(new PhabricatorFileQuery())
 | 
			
		||||
        ->setViewer($viewer)
 | 
			
		||||
        ->withPHIDs(array($current))
 | 
			
		||||
        ->execute();
 | 
			
		||||
      if ($files) {
 | 
			
		||||
        $file = head($files);
 | 
			
		||||
        if ($file->isTransformableImage()) {
 | 
			
		||||
          $has_current = true;
 | 
			
		||||
          $images[$current] = array(
 | 
			
		||||
            'uri' => $file->getBestURI(),
 | 
			
		||||
            'tip' => pht('Current Picture'),
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $images[PhabricatorPHIDConstants::PHID_VOID] = array(
 | 
			
		||||
      'uri' => $default_image->getBestURI(),
 | 
			
		||||
      'tip' => pht('Default Picture'),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    require_celerity_resource('people-profile-css');
 | 
			
		||||
    Javelin::initBehavior('phabricator-tooltips', array());
 | 
			
		||||
 | 
			
		||||
    $buttons = array();
 | 
			
		||||
    foreach ($images as $phid => $spec) {
 | 
			
		||||
      $button = javelin_tag(
 | 
			
		||||
        'button',
 | 
			
		||||
        array(
 | 
			
		||||
          'class' => 'grey profile-image-button',
 | 
			
		||||
          'sigil' => 'has-tooltip',
 | 
			
		||||
          'meta' => array(
 | 
			
		||||
            'tip' => $spec['tip'],
 | 
			
		||||
            'size' => 300,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        phutil_tag(
 | 
			
		||||
          'img',
 | 
			
		||||
          array(
 | 
			
		||||
            'height' => 50,
 | 
			
		||||
            'width' => 50,
 | 
			
		||||
            'src' => $spec['uri'],
 | 
			
		||||
          )));
 | 
			
		||||
 | 
			
		||||
      $button = array(
 | 
			
		||||
        phutil_tag(
 | 
			
		||||
          'input',
 | 
			
		||||
          array(
 | 
			
		||||
            'type'  => 'hidden',
 | 
			
		||||
            'name'  => 'phid',
 | 
			
		||||
            'value' => $phid,
 | 
			
		||||
          )),
 | 
			
		||||
        $button);
 | 
			
		||||
 | 
			
		||||
      $button = phabricator_form(
 | 
			
		||||
        $viewer,
 | 
			
		||||
        array(
 | 
			
		||||
          'class' => 'profile-image-form',
 | 
			
		||||
          'method' => 'POST',
 | 
			
		||||
        ),
 | 
			
		||||
        $button);
 | 
			
		||||
 | 
			
		||||
      $buttons[] = $button;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($has_current) {
 | 
			
		||||
      $form->appendChild(
 | 
			
		||||
        id(new AphrontFormMarkupControl())
 | 
			
		||||
          ->setLabel(pht('Current Picture'))
 | 
			
		||||
          ->setValue(array_shift($buttons)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $form->appendChild(
 | 
			
		||||
      id(new AphrontFormMarkupControl())
 | 
			
		||||
        ->setLabel(pht('Use Picture'))
 | 
			
		||||
        ->setValue($buttons));
 | 
			
		||||
 | 
			
		||||
    $launch_id = celerity_generate_unique_node_id();
 | 
			
		||||
    $input_id = celerity_generate_unique_node_id();
 | 
			
		||||
 | 
			
		||||
    Javelin::initBehavior(
 | 
			
		||||
      'launch-icon-composer',
 | 
			
		||||
      array(
 | 
			
		||||
        'launchID' => $launch_id,
 | 
			
		||||
        'inputID' => $input_id,
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    $compose_button = javelin_tag(
 | 
			
		||||
      'button',
 | 
			
		||||
      array(
 | 
			
		||||
        'class' => 'grey',
 | 
			
		||||
        'id' => $launch_id,
 | 
			
		||||
        'sigil' => 'icon-composer',
 | 
			
		||||
      ),
 | 
			
		||||
      pht('Choose Icon and Color...'));
 | 
			
		||||
 | 
			
		||||
    $compose_input = javelin_tag(
 | 
			
		||||
      'input',
 | 
			
		||||
      array(
 | 
			
		||||
        'type' => 'hidden',
 | 
			
		||||
        'id' => $input_id,
 | 
			
		||||
        'name' => 'phid',
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    $compose_form = phabricator_form(
 | 
			
		||||
      $viewer,
 | 
			
		||||
      array(
 | 
			
		||||
        'class' => 'profile-image-form',
 | 
			
		||||
        'method' => 'POST',
 | 
			
		||||
      ),
 | 
			
		||||
      array(
 | 
			
		||||
        $compose_input,
 | 
			
		||||
        $compose_button,
 | 
			
		||||
      ));
 | 
			
		||||
 | 
			
		||||
    $form->appendChild(
 | 
			
		||||
      id(new AphrontFormMarkupControl())
 | 
			
		||||
        ->setLabel(pht('Quick Create'))
 | 
			
		||||
        ->setValue($compose_form));
 | 
			
		||||
 | 
			
		||||
    $form_box = id(new PHUIObjectBoxView())
 | 
			
		||||
      ->setHeaderText($title)
 | 
			
		||||
      ->setFormError($errors)
 | 
			
		||||
      ->setForm($form);
 | 
			
		||||
 | 
			
		||||
    $upload_form = id(new AphrontFormView())
 | 
			
		||||
      ->setUser($viewer)
 | 
			
		||||
      ->setEncType('multipart/form-data')
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormFileControl())
 | 
			
		||||
          ->setName('picture')
 | 
			
		||||
          ->setLabel(pht('Upload Picture'))
 | 
			
		||||
          ->setError($e_file)
 | 
			
		||||
          ->setCaption(
 | 
			
		||||
            pht('Supported formats: %s', implode(', ', $supported_formats))))
 | 
			
		||||
      ->appendChild(
 | 
			
		||||
        id(new AphrontFormSubmitControl())
 | 
			
		||||
          ->addCancelButton($project_uri)
 | 
			
		||||
          ->setValue(pht('Upload Picture')));
 | 
			
		||||
 | 
			
		||||
    if ($errors) {
 | 
			
		||||
      $errors = id(new AphrontErrorView())->setErrors($errors);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $form_box = id(new PHUIObjectBoxView())
 | 
			
		||||
      ->setHeaderText($title)
 | 
			
		||||
      ->setFormError($errors)
 | 
			
		||||
      ->setForm($form);
 | 
			
		||||
 | 
			
		||||
    $upload_box = id(new PHUIObjectBoxView())
 | 
			
		||||
      ->setHeaderText(pht('Upload New Picture'))
 | 
			
		||||
      ->setForm($upload_form);
 | 
			
		||||
 | 
			
		||||
    return $this->buildApplicationPage(
 | 
			
		||||
      array(
 | 
			
		||||
        $crumbs,
 | 
			
		||||
        $form_box,
 | 
			
		||||
        $upload_box,
 | 
			
		||||
      ),
 | 
			
		||||
      array(
 | 
			
		||||
        'title' => $title,
 | 
			
		||||
        'device' => true,
 | 
			
		||||
      ));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -142,7 +142,7 @@ final class PhabricatorProjectQuery
 | 
			
		||||
            if (!$default) {
 | 
			
		||||
              $default = PhabricatorFile::loadBuiltin(
 | 
			
		||||
                $this->getViewer(),
 | 
			
		||||
                'profile.png');
 | 
			
		||||
                'project.png');
 | 
			
		||||
            }
 | 
			
		||||
            $file = $default;
 | 
			
		||||
          }
 | 
			
		||||
@@ -156,7 +156,7 @@ final class PhabricatorProjectQuery
 | 
			
		||||
          if (!$default) {
 | 
			
		||||
            $default = PhabricatorFile::loadBuiltin(
 | 
			
		||||
              $this->getViewer(),
 | 
			
		||||
              'profile.png');
 | 
			
		||||
              'project.png');
 | 
			
		||||
          }
 | 
			
		||||
          $profile = id(new PhabricatorProjectProfile())
 | 
			
		||||
            ->setProjectPHID($project->getPHID())
 | 
			
		||||
 
 | 
			
		||||
@@ -129,8 +129,8 @@ final class CelerityResourceTransformer {
 | 
			
		||||
      $data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function replaceCSSVariable($matches) {
 | 
			
		||||
    static $map = array(
 | 
			
		||||
  public static function getCSSVariableMap() {
 | 
			
		||||
    return array(
 | 
			
		||||
      // Base Colors
 | 
			
		||||
      'red'           => '#c0392b',
 | 
			
		||||
      'lightred'      => '#f4dddb',
 | 
			
		||||
@@ -148,6 +148,8 @@ final class CelerityResourceTransformer {
 | 
			
		||||
      'lightindigo'   => '#f5e2ef',
 | 
			
		||||
      'violet'        => '#8e44ad',
 | 
			
		||||
      'lightviolet'   => '#ecdff1',
 | 
			
		||||
      'charcoal'      => '#4b4d51',
 | 
			
		||||
      'backdrop'      => '#c4cde0',
 | 
			
		||||
 | 
			
		||||
      // Base Greys
 | 
			
		||||
      'lightgreyborder'     => '#C7CCD9',
 | 
			
		||||
@@ -170,6 +172,13 @@ final class CelerityResourceTransformer {
 | 
			
		||||
      'bluetext'            => '#6B748C',
 | 
			
		||||
      'darkbluetext'        => '#464C5C',
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function replaceCSSVariable($matches) {
 | 
			
		||||
    static $map;
 | 
			
		||||
    if (!$map) {
 | 
			
		||||
      $map = self::getCSSVariableMap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $var_name = $matches[1];
 | 
			
		||||
    if (empty($map[$var_name])) {
 | 
			
		||||
 
 | 
			
		||||
@@ -347,14 +347,14 @@ final class CeleritySpriteGenerator {
 | 
			
		||||
      ->setSourceSize(50, 50);
 | 
			
		||||
 | 
			
		||||
    $sprites = array();
 | 
			
		||||
    $prefix = 'projects_';
 | 
			
		||||
    $prefix = 'projects-';
 | 
			
		||||
    foreach ($icons as $icon) {
 | 
			
		||||
      $sprite = id(clone $template)
 | 
			
		||||
        ->setName($prefix.$icon)
 | 
			
		||||
        ->setTargetCSS('.'.$prefix.$icon);
 | 
			
		||||
 | 
			
		||||
      foreach ($scales as $scale_key => $scale) {
 | 
			
		||||
        $path = $this->getPath($prefix.$scale_key.'/'.$icon.'.png');
 | 
			
		||||
        $path = $this->getPath('projects_'.$scale_key.'/'.$icon.'.png');
 | 
			
		||||
        $sprite->setSourceFile($path, $scale);
 | 
			
		||||
      }
 | 
			
		||||
      $sprites[] = $sprite;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ final class PHUIIconView extends AphrontTagView {
 | 
			
		||||
  const SPRITE_ICONS = 'icons';
 | 
			
		||||
  const SPRITE_LOGIN = 'login';
 | 
			
		||||
  const SPRITE_STATUS = 'status';
 | 
			
		||||
  const SPRITE_PROJECTS = 'projects';
 | 
			
		||||
 | 
			
		||||
  const HEAD_SMALL = 'phuihead-small';
 | 
			
		||||
  const HEAD_MEDIUM = 'phuihead-medium';
 | 
			
		||||
 
 | 
			
		||||
@@ -11,3 +11,69 @@ button.profile-image-button {
 | 
			
		||||
  padding: 4px;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog button.profile-image-button-selected {
 | 
			
		||||
  background-image: none;
 | 
			
		||||
  background-color: {$lightblue};
 | 
			
		||||
  border-color: {$blueborder};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-header {
 | 
			
		||||
  color: {$bluetext};
 | 
			
		||||
  border-bottom: 1px solid {$lightblueborder};
 | 
			
		||||
  padding: 4px 0;
 | 
			
		||||
  margin: 0 0 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form.compose-dialog {
 | 
			
		||||
  width: 80%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .phui-icon-view {
 | 
			
		||||
  display: block;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 50px;
 | 
			
		||||
  height: 50px;
 | 
			
		||||
  background-color: {$darkgreytext};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-red {
 | 
			
		||||
  background-color: {$red};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-orange {
 | 
			
		||||
  background-color: {$orange};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-yellow {
 | 
			
		||||
  background-color: {$yellow};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-green {
 | 
			
		||||
  background-color: {$green};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-blue {
 | 
			
		||||
  background-color: {$blue};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-sky {
 | 
			
		||||
  background-color: {$sky};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-indigo {
 | 
			
		||||
  background-color: {$indigo};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-violet {
 | 
			
		||||
  background-color: {$violet};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-charcoal {
 | 
			
		||||
  background-color: {$charcoal};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.compose-dialog .compose-background-backdrop {
 | 
			
		||||
  background-color: {$backdrop};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.phui-workpanel-view .phui-workpanel-body {
 | 
			
		||||
  background: #c4cde0;
 | 
			
		||||
  background: {$backdrop};
 | 
			
		||||
  padding: 5px 5px 1px 5px;
 | 
			
		||||
  border-bottom-left-radius: 5px;
 | 
			
		||||
  border-bottom-right-radius: 5px;
 | 
			
		||||
 
 | 
			
		||||
@@ -18,226 +18,226 @@ only screen and (-webkit-min-device-pixel-ratio: 1.5) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.projects_8ball {
 | 
			
		||||
.projects-8ball {
 | 
			
		||||
  background-position: 0px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_alien {
 | 
			
		||||
.projects-alien {
 | 
			
		||||
  background-position: -51px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_annouce {
 | 
			
		||||
.projects-announce {
 | 
			
		||||
  background-position: -102px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_art {
 | 
			
		||||
.projects-art {
 | 
			
		||||
  background-position: -153px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_award {
 | 
			
		||||
.projects-award {
 | 
			
		||||
  background-position: -204px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_bacon {
 | 
			
		||||
.projects-bacon {
 | 
			
		||||
  background-position: -255px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_bandaid {
 | 
			
		||||
.projects-bandaid {
 | 
			
		||||
  background-position: -306px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_beer {
 | 
			
		||||
.projects-beer {
 | 
			
		||||
  background-position: 0px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_bomb {
 | 
			
		||||
.projects-bomb {
 | 
			
		||||
  background-position: -51px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_briefcase {
 | 
			
		||||
.projects-briefcase {
 | 
			
		||||
  background-position: -102px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_bug {
 | 
			
		||||
.projects-bug {
 | 
			
		||||
  background-position: -153px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_calendar {
 | 
			
		||||
.projects-calendar {
 | 
			
		||||
  background-position: -204px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_cloud {
 | 
			
		||||
.projects-cloud {
 | 
			
		||||
  background-position: -255px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_coffee {
 | 
			
		||||
.projects-coffee {
 | 
			
		||||
  background-position: -306px -51px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_creditcard {
 | 
			
		||||
.projects-creditcard {
 | 
			
		||||
  background-position: 0px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_death {
 | 
			
		||||
.projects-death {
 | 
			
		||||
  background-position: -51px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_desktop {
 | 
			
		||||
.projects-desktop {
 | 
			
		||||
  background-position: -102px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_dropbox {
 | 
			
		||||
.projects-dropbox {
 | 
			
		||||
  background-position: -153px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_education {
 | 
			
		||||
.projects-education {
 | 
			
		||||
  background-position: -204px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_experimental {
 | 
			
		||||
.projects-experimental {
 | 
			
		||||
  background-position: -255px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_facebook {
 | 
			
		||||
.projects-facebook {
 | 
			
		||||
  background-position: -306px -102px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_facility {
 | 
			
		||||
.projects-facility {
 | 
			
		||||
  background-position: 0px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_film {
 | 
			
		||||
.projects-film {
 | 
			
		||||
  background-position: -51px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_forked {
 | 
			
		||||
.projects-forked {
 | 
			
		||||
  background-position: -102px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_games {
 | 
			
		||||
.projects-games {
 | 
			
		||||
  background-position: -153px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_ghost {
 | 
			
		||||
.projects-ghost {
 | 
			
		||||
  background-position: -204px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_gift {
 | 
			
		||||
.projects-gift {
 | 
			
		||||
  background-position: -255px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_globe {
 | 
			
		||||
.projects-globe {
 | 
			
		||||
  background-position: -306px -153px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_golf {
 | 
			
		||||
.projects-golf {
 | 
			
		||||
  background-position: 0px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_heart {
 | 
			
		||||
.projects-heart {
 | 
			
		||||
  background-position: -51px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_intergalactic {
 | 
			
		||||
.projects-intergalactic {
 | 
			
		||||
  background-position: -102px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_lock {
 | 
			
		||||
.projects-lock {
 | 
			
		||||
  background-position: -153px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_mail {
 | 
			
		||||
.projects-mail {
 | 
			
		||||
  background-position: -204px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_martini {
 | 
			
		||||
.projects-martini {
 | 
			
		||||
  background-position: -255px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_medical {
 | 
			
		||||
.projects-medical {
 | 
			
		||||
  background-position: -306px -204px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_mobile {
 | 
			
		||||
.projects-mobile {
 | 
			
		||||
  background-position: 0px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_music {
 | 
			
		||||
.projects-music {
 | 
			
		||||
  background-position: -51px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_news {
 | 
			
		||||
.projects-news {
 | 
			
		||||
  background-position: -102px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_orgchart {
 | 
			
		||||
.projects-orgchart {
 | 
			
		||||
  background-position: -153px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_peoples {
 | 
			
		||||
.projects-peoples {
 | 
			
		||||
  background-position: -204px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_piechart {
 | 
			
		||||
.projects-piechart {
 | 
			
		||||
  background-position: -255px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_poison {
 | 
			
		||||
.projects-poison {
 | 
			
		||||
  background-position: -306px -255px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_putabirdonit {
 | 
			
		||||
.projects-putabirdonit {
 | 
			
		||||
  background-position: 0px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_radiate {
 | 
			
		||||
.projects-radiate {
 | 
			
		||||
  background-position: -51px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_savings {
 | 
			
		||||
.projects-savings {
 | 
			
		||||
  background-position: -102px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_search {
 | 
			
		||||
.projects-search {
 | 
			
		||||
  background-position: -153px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_shield {
 | 
			
		||||
.projects-shield {
 | 
			
		||||
  background-position: -204px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_speed {
 | 
			
		||||
.projects-speed {
 | 
			
		||||
  background-position: -255px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_sprint {
 | 
			
		||||
.projects-sprint {
 | 
			
		||||
  background-position: -306px -306px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_star {
 | 
			
		||||
.projects-star {
 | 
			
		||||
  background-position: 0px -357px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_storage {
 | 
			
		||||
.projects-storage {
 | 
			
		||||
  background-position: -51px -357px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_tablet {
 | 
			
		||||
.projects-tablet {
 | 
			
		||||
  background-position: -102px -357px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_travel {
 | 
			
		||||
.projects-travel {
 | 
			
		||||
  background-position: -153px -357px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_twitter {
 | 
			
		||||
.projects-twitter {
 | 
			
		||||
  background-position: -204px -357px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_warning {
 | 
			
		||||
.projects-warning {
 | 
			
		||||
  background-position: -255px -357px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.projects_whale {
 | 
			
		||||
.projects-whale {
 | 
			
		||||
  background-position: -306px -357px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										76
									
								
								webroot/rsrc/js/application/files/behavior-icon-composer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								webroot/rsrc/js/application/files/behavior-icon-composer.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @provides javelin-behavior-icon-composer
 | 
			
		||||
 * @requires javelin-behavior
 | 
			
		||||
 *           javelin-dom
 | 
			
		||||
 *           javelin-stratcom
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
JX.behavior('icon-composer', function(config) {
 | 
			
		||||
 | 
			
		||||
  var nodes = {
 | 
			
		||||
    root: JX.$(config.dialogID),
 | 
			
		||||
    colorInput: JX.$(config.colorInputID),
 | 
			
		||||
    iconInput: JX.$(config.iconInputID),
 | 
			
		||||
    preview: JX.$(config.previewID)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var selected = {
 | 
			
		||||
    color: config.defaultColor,
 | 
			
		||||
    icon: config.defaultIcon
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var redraw = function() {
 | 
			
		||||
    var ii;
 | 
			
		||||
 | 
			
		||||
    var colors = JX.DOM.scry(nodes.root, 'button', 'compose-select-color');
 | 
			
		||||
    for (ii = 0; ii < colors.length; ii++) {
 | 
			
		||||
      JX.DOM.alterClass(
 | 
			
		||||
        colors[ii],
 | 
			
		||||
        'profile-image-button-selected',
 | 
			
		||||
        (JX.Stratcom.getData(colors[ii]).color == selected.color));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var icons = JX.DOM.scry(nodes.root, 'button', 'compose-select-icon');
 | 
			
		||||
    for (ii = 0; ii < icons.length; ii++) {
 | 
			
		||||
      JX.DOM.alterClass(
 | 
			
		||||
        icons[ii],
 | 
			
		||||
        'profile-image-button-selected',
 | 
			
		||||
        (JX.Stratcom.getData(icons[ii]).icon == selected.icon));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nodes.colorInput.value = selected.color;
 | 
			
		||||
    nodes.iconInput.value = selected.icon;
 | 
			
		||||
 | 
			
		||||
    var classes = ['phui-icon-view', 'sprite-projects'];
 | 
			
		||||
    classes.push('compose-background-' + selected.color);
 | 
			
		||||
    classes.push('projects-' + selected.icon);
 | 
			
		||||
 | 
			
		||||
    nodes.preview.className = classes.join(' ');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  JX.DOM.listen(
 | 
			
		||||
    nodes.root,
 | 
			
		||||
    'click',
 | 
			
		||||
    'compose-select-color',
 | 
			
		||||
    function (e) {
 | 
			
		||||
      e.kill();
 | 
			
		||||
 | 
			
		||||
      selected.color = e.getNodeData('compose-select-color').color;
 | 
			
		||||
      redraw();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  JX.DOM.listen(
 | 
			
		||||
    nodes.root,
 | 
			
		||||
    'click',
 | 
			
		||||
    'compose-select-icon',
 | 
			
		||||
    function (e) {
 | 
			
		||||
      e.kill();
 | 
			
		||||
 | 
			
		||||
      selected.icon = e.getNodeData('compose-select-icon').icon;
 | 
			
		||||
      redraw();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  redraw();
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @provides javelin-behavior-launch-icon-composer
 | 
			
		||||
 * @requires javelin-behavior
 | 
			
		||||
 *           javelin-dom
 | 
			
		||||
 *           javelin-workflow
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
JX.behavior('launch-icon-composer', function(config) {
 | 
			
		||||
 | 
			
		||||
  JX.DOM.listen(
 | 
			
		||||
    JX.$(config.launchID),
 | 
			
		||||
    'click',
 | 
			
		||||
    null,
 | 
			
		||||
    function(e) {
 | 
			
		||||
      e.kill();
 | 
			
		||||
      new JX.Workflow('/file/compose/')
 | 
			
		||||
        .setHandler(function(r) {
 | 
			
		||||
          JX.$(config.inputID).value = r.phid;
 | 
			
		||||
          JX.DOM.findAbove(e.getTarget(), 'form').submit();
 | 
			
		||||
        })
 | 
			
		||||
        .start();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user