From 84df1bb4670b369baa24ef719babfbfe07e74d36 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 29 Dec 2014 13:39:21 -0600 Subject: [PATCH] Custom header logo --- CHANGELOG-EE | 3 ++ .../stylesheets/sections/appearances.scss | 8 ++++++ .../admin/appearances_controller.rb | 13 ++++++++- app/helpers/appearances_helper.rb | 15 ++++++++++ app/models/appearance.rb | 10 +++++++ app/views/admin/appearances/_form.html.haml | 27 ++++++++++++++++++ app/views/admin/appearances/show.html.haml | 4 +-- app/views/layouts/_head_panel.html.haml | 1 + config/routes.rb | 1 + ...3212220_add_header_logos_to_appearances.rb | 6 ++++ db/schema.rb | 4 ++- features/admin/appearance.feature | 9 ++++++ features/steps/admin/appearance.rb | 20 +++++++++++++ public/header_logo_dark.png | Bin 0 -> 2797 bytes public/header_logo_light.png | Bin 0 -> 7501 bytes 15 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20141213212220_add_header_logos_to_appearances.rb create mode 100644 public/header_logo_dark.png create mode 100644 public/header_logo_light.png diff --git a/CHANGELOG-EE b/CHANGELOG-EE index 1a1492a34e..f306d7d9d8 100644 --- a/CHANGELOG-EE +++ b/CHANGELOG-EE @@ -1,3 +1,6 @@ +v 7.7.0 + - Added custom header logo support (Drew Blessing) + v 7.6.2 - Fix failing migrations for MySQL, LDAP diff --git a/app/assets/stylesheets/sections/appearances.scss b/app/assets/stylesheets/sections/appearances.scss index addf79cfb1..dd4a1b99ab 100644 --- a/app/assets/stylesheets/sections/appearances.scss +++ b/app/assets/stylesheets/sections/appearances.scss @@ -2,3 +2,11 @@ max-width: 400px; margin-bottom: 20px; } + +.appearance-dark-logo-preview { + background-color: #F1F1F1; +} + +.appearance-light-logo-preview { + background-color: #373737; +} diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb index 98cc2faf1a..51884ea6ec 100644 --- a/app/controllers/admin/appearances_controller.rb +++ b/app/controllers/admin/appearances_controller.rb @@ -34,6 +34,16 @@ class Admin::AppearancesController < Admin::ApplicationController redirect_to admin_appearances_path, notice: 'Logo was succesfully removed.' end + def header_logos + appearance = Appearance.last + appearance.remove_light_logo! + appearance.remove_dark_logo! + + appearance.save + + redirect_to admin_appearances_path, notice: 'Header logos were succesfully removed.' + end + private # Use callbacks to share common setup or constraints between actions. @@ -43,6 +53,7 @@ class Admin::AppearancesController < Admin::ApplicationController # Only allow a trusted parameter "white list" through. def appearance_params - params.require(:appearance).permit(:title, :description, :logo, :updated_by) + params.require(:appearance).permit(:title, :description, :logo, + :dark_logo, :light_logo, :updated_by) end end diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index fef4d7c035..502c9c7cd0 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -15,6 +15,21 @@ module AppearancesHelper end end + def brand_header_logo + if brand_item.header_logos? + haml_tag(:style) do + # Dark theme/light logo + haml_concat ".dark_theme .app_logo a h1 {" \ + "background: url('#{brand_item.light_logo}') " \ + "no-repeat center center !important; }" + # Light theme/dark logo + haml_concat ".light_theme .app_logo a h1 {" \ + "background: url('#{brand_item.dark_logo}') " \ + "no-repeat center center !important; }" + end + end + end + def brand_text markdown(brand_item.description) end diff --git a/app/models/appearance.rb b/app/models/appearance.rb index 5d8edf4990..0d9156f3ae 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -2,6 +2,16 @@ class Appearance < ActiveRecord::Base validates :title, presence: true validates :description, presence: true validates :logo, file_size: { maximum: 1000.kilobytes.to_i } + validates :dark_logo, file_size: { maximum: 1000.kilobytes.to_i }, + presence: true, if: :light_logo? + validates :light_logo, file_size: { maximum: 1000.kilobytes.to_i }, + presence: true, if: :dark_logo? mount_uploader :logo, AttachmentUploader + mount_uploader :dark_logo, AttachmentUploader + mount_uploader :light_logo, AttachmentUploader + + def header_logos? + dark_logo? && light_logo? + end end diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 238fe5926e..8d2f8e93de 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -4,6 +4,9 @@ - @appearance.errors.full_messages.each do |msg| %p= msg + %fieldset.sign-in + %legend + Sign in/Sign up pages: .form-group = f.label :title, class: 'control-label' .col-sm-10 @@ -25,6 +28,30 @@ = f.file_field :logo, class: "" .hint Maximum logo size is 1MB, page optimized for logo size 640x360px + + %fieldset.app_logo + %legend + Navigation bar: + .form-group + = f.label :dark_logo, class: 'control-label' + .col-sm-10 + - if @appearance.dark_logo? + = image_tag @appearance.dark_logo, class: 'appearance-dark-logo-preview' + = f.file_field :dark_logo, class: "" + .hint + Maximum size is 1MB, page optimized for logo size 40x40px + = f.label :light_logo, class: 'control-label' + .col-sm-10 + - if @appearance.light_logo? + = image_tag @appearance.light_logo, class: 'appearance-light-logo-preview' + = f.file_field :light_logo, class: "" + .hint + Maximum size is 1MB, page optimized for logo size 41x41px + -if @appearance.light_logo? || @appearance.dark_logo? + %br + = link_to 'Remove header logos', header_logos_admin_appearances_path, data: { confirm: "Header logos will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-logo" + + .form-actions = f.submit 'Save', class: 'btn btn-save' = link_to 'Preview', preview_admin_appearances_path, class: 'btn', target: '_blank' diff --git a/app/views/admin/appearances/show.html.haml b/app/views/admin/appearances/show.html.haml index c3512908c6..52bbbf0d52 100644 --- a/app/views/admin/appearances/show.html.haml +++ b/app/views/admin/appearances/show.html.haml @@ -1,8 +1,6 @@ %h3.page-title Appearance settings %p.light - You can modify look of sign-in and sign-up pages here - -%hr + You can modify the look and feel of GitLab here = render 'form' diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index eda37f8237..e0e6f7f7fb 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -2,6 +2,7 @@ .navbar-inner .container %div.app_logo + - brand_header_logo if brand_item = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do %h1 GITLAB %h1.title= title diff --git a/config/routes.rb b/config/routes.rb index 98250653f7..c24b28dde8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -114,6 +114,7 @@ Gitlab::Application.routes.draw do member do get :preview delete :logo + delete :header_logos end end diff --git a/db/migrate/20141213212220_add_header_logos_to_appearances.rb b/db/migrate/20141213212220_add_header_logos_to_appearances.rb new file mode 100644 index 0000000000..9f3b787727 --- /dev/null +++ b/db/migrate/20141213212220_add_header_logos_to_appearances.rb @@ -0,0 +1,6 @@ +class AddHeaderLogosToAppearances < ActiveRecord::Migration + def change + add_column :appearances, :dark_logo, :string + add_column :appearances, :light_logo, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 6bc9f1b697..8f35fcca88 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -23,6 +23,8 @@ ActiveRecord::Schema.define(version: 20141217125223) do t.integer "updated_by" t.datetime "created_at" t.datetime "updated_at" + t.string "dark_logo" + t.string "light_logo" end create_table "audit_events", force: true do |t| @@ -369,8 +371,8 @@ ActiveRecord::Schema.define(version: 20141217125223) do t.boolean "archived", default: false, null: false t.string "import_status" t.float "repository_size", default: 0.0 - t.text "merge_requests_template" t.integer "star_count", default: 0, null: false + t.text "merge_requests_template" t.boolean "merge_requests_rebase_enabled", default: false end diff --git a/features/admin/appearance.feature b/features/admin/appearance.feature index ccdaa745e1..5c1dd7531c 100644 --- a/features/admin/appearance.feature +++ b/features/admin/appearance.feature @@ -26,3 +26,12 @@ Feature: Admin Appearance Then I should see a logo And I remove the logo Then I should see logo removed + + Scenario: Header logos + Given application has custom appearance + And I sign in as an admin + And I visit admin appearance page + When I attach header logos + Then I should see header logos + And I remove the header logos + Then I should see header logos removed diff --git a/features/steps/admin/appearance.rb b/features/steps/admin/appearance.rb index 7703b32ccb..78b20e587a 100644 --- a/features/steps/admin/appearance.rb +++ b/features/steps/admin/appearance.rb @@ -37,18 +37,38 @@ class Spinach::Features::AdminAppearance < Spinach::FeatureSteps click_button 'Save' end + step 'I attach header logos' do + attach_file(:appearance_light_logo, File.join(Rails.root, 'public', 'header_logo_light.png')) + attach_file(:appearance_dark_logo, File.join(Rails.root, 'public', 'header_logo_dark.png')) + click_button 'Save' + end + step 'I should see a logo' do page.should have_xpath('//img[@src="/uploads/appearance/logo/1/gitlab_logo.png"]') end + step 'I should see header logos' do + page.should have_xpath('//img[@src="/uploads/appearance/light_logo/1/header_logo_light.png"]') + page.should have_xpath('//img[@src="/uploads/appearance/dark_logo/1/header_logo_dark.png"]') + end + step 'I remove the logo' do click_link 'Remove logo' end + step 'I remove the header logos' do + click_link 'Remove header logos' + end + step 'I should see logo removed' do page.should_not have_xpath('//img[@src="/uploads/appearance/logo/1/gitlab_logo.png"]') end + step 'I should see header logos removed' do + page.should_not have_xpath('//img[@src="/uploads/appearance/light_logo/1/header_logo_light.png"]') + page.should_not have_xpath('//img[@src="/uploads/appearance/dark_logo/1/header_logo_dark.png"]') + end + def appearance Appearance.last end diff --git a/public/header_logo_dark.png b/public/header_logo_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..4a96572d570108366da2cf5aa8213f69b591a2a3 GIT binary patch literal 2797 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 literal 0 HcmV?d00001 diff --git a/public/header_logo_light.png b/public/header_logo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2ef601a538d69ef99d5bdafa605e63f902e8e4 GIT binary patch literal 7501 zcmV-T9kSwyP)e-PV>S4|;ohGaKq_-8TRBi;1WZ zpPn#@U7Zi?$lr`7FE=})2sZknsL;5(^o;>7i6}n3ogEYhb=8S~ z@p2=piR~mzjr2^eTs-4pVtC!QtFyDZ_fyFW4)Sx-n4@YZCPLZ(3Hn|fhP8qN(77pTAg8k2MByt}FQ4-P!{p+|?Z=_Bv!g^u z`+@}^z_7=UJl}(M3qBJSYSSle4i9^q)6`i1ZjyrJWmjj%*XZOi3dI9keeLR{KqYzU z^O6$;CXIkXpgAT8DPYmOX}moJ`kX(hKrS(uqX3e_0U)BF|k5Rkc#A9}t84O&f%4MJA_xY(|_sWA!Lp--Fi zX@HpMh}hMumbp$483$zu_YjlqV`24~${RH1&ahd(ZsoIwUhX+SsApn8;WrM)8K13& zI0r&oMn`*V<^xan++WuI^hABOiq-tND(lhlNrO&~2a%PL6g+bN?1=!-M-@TTG4OL~ zqqN3u_~mD#2VU-(bUHozvZ6dzZlaj+J)65wezg-i#gOfj zv0Ysq`I?9K`c9M*+AS{3Ex|RAT1m=El1c!?%k$?>clPphPXTK$9|j?2AP!a-L!k`D z%JlSbOPi-Y-EsD;spnLbq?8C^ic^q;%=vNId^?~w5;iYYNbnVZ(FS?-1Rksai_%Y z9e-TR$ar4_8v57@$j{CoiHTUlvgD-l619~Wnb2Y_|!A+s@=AHH#mjrE;Z zfZA?2F`Cyrf}aVSYG~N^xK|rKTi3 zl9uGR8pnxTn?Fz0JU%Wu6oSk|nSBc&Spk-km=F^@TXm8<-#8?A&#r9_?QLzzP=OOc z#oE&jAHt@sC z7aW)^-ri3vxY&^UljKC6>FZv0pmulFerAlUow; zl!|+{-rVeF;M&zIpMYn|;Dzwekc4Ts0V**YsL z6Dq789GJ~sUhen!co6T2l0vR&smT$jnMD-j-&-52MV7 z4?OL-x!7L+vi9eI#)f*x;*{@@Dp{r0*6QlY@MTLExGh<{;64B~5p#7Rby`^53f;AH zD|9{kI5+uS!kfwRV*6kWgcU$#XmcO%a_h|^Kf%|1bp{C z!m6*UjsJ7&CR1j*>kIPpid1JzP1v|$?X&jwwzB^fFqu$lpUc_Y z)KvNVreE%zICdDu7CO4SyNfPgJZled`RPl-Saa5|`| zAy}P3h?@>J!EZs`xQNq`VD%2eZFtsi^7r$3#fBi;e~=U%_a%TXJgRv>2bd9QLkv#T z^v$4VQ>k6)9qoPXU}PqhS=4EaDVD+H$fnN=0yTxfpnrh#q0ze2!S5U_c!Lil55;g0 zgQG_d=#186!j3C8`T2UoqmWh>*qlM9)3ZWDL;SDlTzT>9#Y znwX92e>#p@J|iw`t?%CUz%=_XrTYBYlQ6hki<>k9SMiI+{cf+tXMUrD3sQWQ^yY(gSPWnwmWz4_^)AIZA~6=cLpnG&m4ztSuv3k zgoK2`PM$oObmPX2)CCI`gn`Ai6XY2Dw_8qD+6KrcUlu_fFrCtp!f@Fiq&!i8%0Ugr z%S?WL{@R?J9OJgOwxooF1nBiR66Tq%t*w27g$>01Z1aBpIE0%G*}_UXknC?8*XX9D zCchg_v*UO4x}G*n>B}OrnXay`5vJA8pFjVYot?cIw~&Ozix)?rN~2p=R_Y#wLXN;I ztD%%2q|Exd+CU&LpTIH2#Kcn0o;~YhYHE5E{O;d4VPWC6jEs!Fbe`7M)-rHSi!jd| zPfs^X7AUlT?3R}1jD35y8GV-EFd&(SgvPoB))Va zy8-I71g2D9WNP>B-B4+@;GVGq3k>r1_I?IfF&0wnhF?}Y^z`(==jqJ3qW>y`Mx$mZ z$jRJ=WAX9vWv8a5LeO$6D=U-0wR~)x2@@tXgK^+`=!J!aa2#Jeg-T0HW;N7TKVs3G zz&4tan({7f)356cKp#JR2Bz5|giS;MS;WhYn1i2J0s~%};*LOADLKoQEP|}oMA-e? z_4W0AN+&Qd(2qnS^;3?zy1GXPDDYlVQqpa1Zfw;w-#{3(`@ z0oy)&`0y2w+pkgTEI)GOh%b&h8N9sXiXV7+K&PdaMe}!nat51TVRYlFrwBj$AD`t- ziHq{Cxpwu^(~gdI%!sFTcD4mjsoihUqvKvy{9RD846a{`g8T}dD)4$13Abx^wQ!y-**X-H@iAJ3JKvk#!%-mpftopM<(ysu^r0P40JKAQ>G|4 zu2``mf5nO)%cP}c3JJjA!Rg7(%`wB26CTj`*f8^9yfLgoN1_q^n+nae1N--;rlqCi zVWu1lVB)!tT3uE4Iwtbf-8M>V5qgu_r;)QWi+KZgV5kV3MbfK32{No~h00KOV z?!X42(`h|vDGB$YBSWrrcXw9|6Pfuea}8`nskis@51N{XlUA?(xptzABoqa4C{;ou z`xWXc77;BO+uuv! z=H`G}y9WiRv8=S@Ix`9_$j>psfMf%8s>+lsPY<_T&`;!eO()Z66(b(^Y)5TPWuT?G{<)Ik0%$Q%u{Aa~HC9B6alpTKEUUA#&Orj$Hly1)hK7tQ5nE#O^pr7Hg|6tg0;i=DXl-0S6f?o?dnBW zj*n+eqC>f#@~YG1uDp2@1U-)?Y?RvCnp#`y+pbw3GGN$^I-2MZ^9A+)Ocxdwi0U)? zXHC@xRsnV3Q3WR3>D4k)BDdgke7u}q;8M*X;fB#>MfXpZOm436ba!-%iVO?J>4zSG zMahM8C*euxMpj?%QB%<*k3`b!{8e!wwSo=01DogwGhC!hQ;vx3|@~IN8`d@^W?pCR``>Ktp{!1k(Wp zb42fKsV$m6^JHmBaSCqIX@VkVW zKac)~a<%lx;k~v2{ysrtojBH~OuM?O+}#|l&CQIg+uJB5{YL>U&1Jeemm!#&M*9NX zD!Y4@&d}Au4NQv&e;cTIc(+AmWf@HL@A}fj2aLj6$<5h-rPDw!J$Nz9>BEglHrQH+(eOs>1ipk1lOy} z!T#>eix*DcYieq29)tX*&ziFkzUv*Mys7~k6&D*5yLHP(gNpLfOgvYlq`Zrjmf%0c zG`cjxvPA2`Nef`i77)y$eDhm|b~jB7e8}V$${5Hp1}oZlus1Zu08DC1a#H5LJ%74n zXZHn6DA-L3So`_2$1GUQSRVEbGf=k!c?A_zC*G)qS$Nu)&%52Rxb+nH%;Yc45E*GZ zsGz<8&jCJ2R#s;6jz70}#>PfN^QawnufyN(MGU}T2i{-*(jv7$W2RnuT5@LZr=))L z-JQm~wX0VcTi(891+-xkD~EbR6<|C^@7uele&4=*wfpz)uVGgA?%msP<u zjBe-}!u`beSzcC3IecKJGt8C!Dld?e_}JI$>0WUK=0hFvNFX!geavJ98PmA97#Q+v z9}TG3BEdrPmMod?M2N0qHTe0{$DzQ7t{ANd#W;uzEcau79rQeNO4ABRu~NJ^Yi@38 z(7kragP+8{;j4bUgqs7|X=7#XPNnt-OE52{Ej}(bdF78wo@9NUa9ou@^9|i5IG1ZKby@Oq_VQ`%I*- z{&^67ZcgN2V1QpD7{8mqcI~f%{nOU3`zf%bxClA~-6Ki0!C4SkB!}|w;e+UDDwER= z9XgOwTwGXzr6rCOO#Eg6Sx+%HGxgfLdwXyTAVvJxzz(MQ`+6tC1l!?W>Ke-;pf+9c zdPI0=5rGo>=4)+r+hyUxxwgRPO~QLqEb=GOd}_?k&#P%|C0DbMEjMhC25b`fo{go& zteMkntE(#u31fAKg}y17IbBizTY}kts5f)QWaH?lh(dy9>^mm{a>;(teAPS6&5h70 zXd8t$HJGtKYA~TI{`jH$4Fx#~$K0F`F=!~RA56FwuqLCCq}hyh5mTEs$s{H^G6$v9 z{#O^fIy-9j?%MWX^QMiaot>SfpW=v&Q5lP)wQq4LJLJTKI9G9TVcVp{_%xiY3Ga1A zMMZp2n=uK-TE+hEZ&XRlQk`u4=2cKI?wp2!W@C9N~ARBG238~$Hnxaa3( z$Ei$Fcoq>6R)C&L^v65`{9itwtu_UoRY@SPEf0=DpP)t5wmx&w}uCA!<{xVSj~@T)*cfpnB)PTT*8cLlkQ*F z)4qiT#y_5y(d%yAG_v63MvlR`Bj0He6dKQdP*>-Ib5~bqJr2`Oz;e-|LOhOdLjoUR z39dtZjsl)T0gJ{bEbO%la5JxHU(vc875UbU+THa8^W%rH5V||s+wx~jleao??4S(; zG(IZfu(fXKwJg@U#NB93Vj7FyeTeIuPkpn(^c5id+{`66VHcD$t zg68499wc7m4=%PZ`tH;>Ej*$ET&vWkD;ZT)mM7qpKzm9`a>|@p(_WXB6z2_=x=5$d zDoRUB%d@gRw0!vRp*1TjivlbBJ3BkO3CLwE^Msku1!!bW_Rx05R7LOly1FzXsEiRL8Zr9` z4EHXh8+slK=FNoprd8NRWxP+#nXDiKW17my-~YAAFDjC%)0NDf9BtuIN(;I`va!+i zGfD~*9e~fC*nd1rkf}_MGMT)ohkJ>u)B3K(9Z_MDep6#(C~9Li+1pv!0UPt?*9gJ= zT}41hfP3HRQ%C)1G%BWP>#k~Fyf}4=j3u2$?f3gfc;JV}x9xSXzl(?Y35+%$y=zx|)KWF;T9hfHoso}?swr=TDoij9`Dr;omoojBnPg8Xw# zqA?QzGLnMJRi?-~0&^w@crLWVhxR(VxjN|ge*URR#sDu01+%NOGhvFNq;WxhZe~S! zY1VWVc_(Q}!KMGpEY6AukeH$*ZF|SU#1??sb>z^_=OM3Oo<>2W5kbYgAW#$L%~sUP z$Vkt-WqQL(dD28%)F1!%bMY|YN^%14uGwl+tZJ$&BTpYc=viEpe-out8sXh`7~Ck^ z_TLTXo<4SWou({zS4y0;3;FurWd6q$_^|^eB?T#G6T_>9|J}4&rXIHs3;Hom@BW8IKQS4KgW%Es**<-40-|@CF(=e(caQj zOc3!I&x;&@&x(%c`uD%uV1-)wc#tWAB*a!_k~j>zL31eH)WpDwpTy}2z?=%|zf?Jr z!pDoS3G*Yo+#JX@1v#<%it^&N6NGuU!dpDNZ2wZ_s7aLz;pAaQWOz9H{(Ft@KB)13 XBYOdNmDsD+00000NkvXXu0mjf1LwCf literal 0 HcmV?d00001