From 4f0573617562a0ebbbf88de99000a0a223f8a6e1 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 17 Oct 2013 09:32:34 -0700 Subject: [PATCH] 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 --- resources/builtin/project.png | Bin 0 -> 3049 bytes resources/sprite/manifest/projects.json | 338 +++++++++--------- .../projects_1x/{annouce.png => announce.png} | Bin .../projects_2x/{annouce.png => announce.png} | Bin src/__celerity_resource_map__.php | 28 +- src/__phutil_library_map__.php | 4 + .../PhabricatorApplicationFiles.php | 1 + .../PhabricatorFileComposeController.php | 249 +++++++++++++ .../PhabricatorApplicationProject.php | 2 + .../PhabricatorProjectProfileController.php | 8 + ...habricatorProjectProfileEditController.php | 53 --- ...ricatorProjectProfilePictureController.php | 274 ++++++++++++++ .../project/query/PhabricatorProjectQuery.php | 4 +- .../celerity/CelerityResourceTransformer.php | 13 +- .../celerity/CeleritySpriteGenerator.php | 4 +- src/view/phui/PHUIIconView.php | 1 + .../css/application/people/people-profile.css | 66 ++++ webroot/rsrc/css/phui/phui-workpanel-view.css | 2 +- webroot/rsrc/css/sprite-projects.css | 112 +++--- .../files/behavior-icon-composer.js | 76 ++++ .../files/behavior-launch-icon-composer.js | 25 ++ 21 files changed, 973 insertions(+), 287 deletions(-) create mode 100644 resources/builtin/project.png rename resources/sprite/projects_1x/{annouce.png => announce.png} (100%) rename resources/sprite/projects_2x/{annouce.png => announce.png} (100%) create mode 100644 src/applications/files/controller/PhabricatorFileComposeController.php create mode 100644 src/applications/project/controller/PhabricatorProjectProfilePictureController.php create mode 100644 webroot/rsrc/js/application/files/behavior-icon-composer.js create mode 100644 webroot/rsrc/js/application/files/behavior-launch-icon-composer.js diff --git a/resources/builtin/project.png b/resources/builtin/project.png new file mode 100644 index 0000000000000000000000000000000000000000..59cf6cf1ad44d5ebbfbb3c2a53f384124b47c90a GIT binary patch literal 3049 zcmVKLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0000~P)t-s#LeKx(BjS2<<8aR&erA6*5=dN=-1uq-Qw-uFo3A?ey#J z^zQKX@A3BV^Y`)d_wn=i^7Q!h_WAbs`uO?#{QUj={r>*`{{R2~pVU%q0002ANklPP zym}Epj=)t<-GEa7>0sxu2tM}zq^`yq|GkDbHA8ZtRH}WV zQ>8@CkqVGYB7tg$JR`EY*CW~c{n{8Ki4?}GAR@;Hy+I6rFtbNWYqKTEtSwpcIN^fO rh9d*rSmzT*n}H!rk^G3nVgcO&$&E?%F67Ha00000NkvXXu0mjfrEkpc literal 0 HcmV?d00001 diff --git a/resources/sprite/manifest/projects.json b/resources/sprite/manifest/projects.json index b170ad16e5..203444c4a4 100644 --- a/resources/sprite/manifest/projects.json +++ b/resources/sprite/manifest/projects.json @@ -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" } }, diff --git a/resources/sprite/projects_1x/annouce.png b/resources/sprite/projects_1x/announce.png similarity index 100% rename from resources/sprite/projects_1x/annouce.png rename to resources/sprite/projects_1x/announce.png diff --git a/resources/sprite/projects_2x/annouce.png b/resources/sprite/projects_2x/announce.png similarity index 100% rename from resources/sprite/projects_2x/annouce.png rename to resources/sprite/projects_2x/announce.png diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 18caa1c6ad..9c63744e58 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -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( diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 10a442415b..41633c43ca 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', diff --git a/src/applications/files/application/PhabricatorApplicationFiles.php b/src/applications/files/application/PhabricatorApplicationFiles.php index 05590852b2..cac7c4435f 100644 --- a/src/applications/files/application/PhabricatorApplicationFiles.php +++ b/src/applications/files/application/PhabricatorApplicationFiles.php @@ -51,6 +51,7 @@ final class PhabricatorApplicationFiles extends PhabricatorApplication { '(query/(?P[^/]+)/)?' => 'PhabricatorFileListController', 'upload/' => 'PhabricatorFileUploadController', 'dropupload/' => 'PhabricatorFileDropUploadController', + 'compose/' => 'PhabricatorFileComposeController', 'comment/(?P[1-9]\d*)/' => 'PhabricatorFileCommentController', 'delete/(?P[1-9]\d*)/' => 'PhabricatorFileDeleteController', 'info/(?P[^/]+)/' => 'PhabricatorFileInfoController', diff --git a/src/applications/files/controller/PhabricatorFileComposeController.php b/src/applications/files/controller/PhabricatorFileComposeController.php new file mode 100644 index 0000000000..ec27ea836a --- /dev/null +++ b/src/applications/files/controller/PhabricatorFileComposeController.php @@ -0,0 +1,249 @@ +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'); + } + +} diff --git a/src/applications/project/application/PhabricatorApplicationProject.php b/src/applications/project/application/PhabricatorApplicationProject.php index 22d8faa7f1..629588a9cf 100644 --- a/src/applications/project/application/PhabricatorApplicationProject.php +++ b/src/applications/project/application/PhabricatorApplicationProject.php @@ -42,6 +42,8 @@ final class PhabricatorApplicationProject extends PhabricatorApplication { => 'PhabricatorProjectMembersEditController', 'view/(?P[1-9]\d*)/(?:(?P\w+)/)?' => 'PhabricatorProjectProfileController', + 'picture/(?P[1-9]\d*)/' => + 'PhabricatorProjectProfilePictureController', 'create/' => 'PhabricatorProjectCreateController', 'update/(?P[1-9]\d*)/(?P[^/]+)/' => 'PhabricatorProjectUpdateController', diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 6174b6e795..ef2c6ad4fe 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -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())) { diff --git a/src/applications/project/controller/PhabricatorProjectProfileEditController.php b/src/applications/project/controller/PhabricatorProjectProfileEditController.php index 5f7aa0824f..ce43d1373f 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileEditController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileEditController.php @@ -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().'/') diff --git a/src/applications/project/controller/PhabricatorProjectProfilePictureController.php b/src/applications/project/controller/PhabricatorProjectProfilePictureController.php new file mode 100644 index 0000000000..6188f58014 --- /dev/null +++ b/src/applications/project/controller/PhabricatorProjectProfilePictureController.php @@ -0,0 +1,274 @@ +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, + )); + } +} diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index e379a48377..70bfa9d573 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -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()) diff --git a/src/infrastructure/celerity/CelerityResourceTransformer.php b/src/infrastructure/celerity/CelerityResourceTransformer.php index 5f2fd1f200..7b87dbb5bd 100644 --- a/src/infrastructure/celerity/CelerityResourceTransformer.php +++ b/src/infrastructure/celerity/CelerityResourceTransformer.php @@ -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])) { diff --git a/src/infrastructure/celerity/CeleritySpriteGenerator.php b/src/infrastructure/celerity/CeleritySpriteGenerator.php index 4936c38f0c..0e3e1e6d59 100644 --- a/src/infrastructure/celerity/CeleritySpriteGenerator.php +++ b/src/infrastructure/celerity/CeleritySpriteGenerator.php @@ -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; diff --git a/src/view/phui/PHUIIconView.php b/src/view/phui/PHUIIconView.php index 4fb475bcd2..e996aaef27 100644 --- a/src/view/phui/PHUIIconView.php +++ b/src/view/phui/PHUIIconView.php @@ -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'; diff --git a/webroot/rsrc/css/application/people/people-profile.css b/webroot/rsrc/css/application/people/people-profile.css index 9a6ea0e22d..655bcb0c99 100644 --- a/webroot/rsrc/css/application/people/people-profile.css +++ b/webroot/rsrc/css/application/people/people-profile.css @@ -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}; +} + diff --git a/webroot/rsrc/css/phui/phui-workpanel-view.css b/webroot/rsrc/css/phui/phui-workpanel-view.css index 343d8682ae..c414445219 100644 --- a/webroot/rsrc/css/phui/phui-workpanel-view.css +++ b/webroot/rsrc/css/phui/phui-workpanel-view.css @@ -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; diff --git a/webroot/rsrc/css/sprite-projects.css b/webroot/rsrc/css/sprite-projects.css index 111e0ba1d5..1b7519b1c0 100644 --- a/webroot/rsrc/css/sprite-projects.css +++ b/webroot/rsrc/css/sprite-projects.css @@ -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; } diff --git a/webroot/rsrc/js/application/files/behavior-icon-composer.js b/webroot/rsrc/js/application/files/behavior-icon-composer.js new file mode 100644 index 0000000000..2967f49e2f --- /dev/null +++ b/webroot/rsrc/js/application/files/behavior-icon-composer.js @@ -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(); + +}); + diff --git a/webroot/rsrc/js/application/files/behavior-launch-icon-composer.js b/webroot/rsrc/js/application/files/behavior-launch-icon-composer.js new file mode 100644 index 0000000000..327a7efc54 --- /dev/null +++ b/webroot/rsrc/js/application/files/behavior-launch-icon-composer.js @@ -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(); + }); + +}); +