From e549d2fb32844ceb91bcec730eb16c1dbdb78f7e Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 21:33:47 +0200 Subject: [PATCH 1/9] Add data properties for device interface table Preparatory work for factoring row styling decisions out of Python code. --- netbox/dcim/tables/devices.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 34dbcbf30..fc8a3af73 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -69,6 +69,36 @@ def get_interface_state_attribute(record): return "disabled" +def get_interface_virtual_attribute(record): + """ + Get interface virtual state as string to attach to DOM element. + """ + if record.is_virtual: + return "true" + else: + return "false" + + +def get_interface_mark_connected_attribute(record): + """ + Get interface enabled state as string to attach to DOM element. + """ + if record.mark_connected: + return "true" + else: + return "false" + + +def get_interface_cable_status_attribute(record): + """ + Get interface enabled state as string to attach to DOM element. + """ + if record.cable: + return record.cable.status + else: + return "" + + # # Device roles # @@ -673,6 +703,9 @@ class DeviceInterfaceTable(InterfaceTable): 'class': get_interface_row_class, 'data-name': lambda record: record.name, 'data-enabled': get_interface_state_attribute, + 'data-virtual': get_interface_virtual_attribute, + 'data-mark-connected': get_interface_mark_connected_attribute, + 'data-cable-status': get_interface_cable_status_attribute, 'data-type': lambda record: record.type, } From df34e10ee59242b73fc47c28d894811ab28e1a44 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 21:43:32 +0200 Subject: [PATCH 2/9] Add --nbx-color-* variables for theme colors Preparatory work for moving row styling to CSS --- netbox/project-static/dist/netbox-dark.css | Bin 375591 -> 376198 bytes netbox/project-static/dist/netbox-light.css | Bin 232798 -> 233371 bytes netbox/project-static/dist/netbox-print.css | Bin 727883 -> 728556 bytes netbox/project-static/styles/netbox.scss | 7 +++++++ 4 files changed, 7 insertions(+) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index 2d7142bc6b6da7f32935de02c63becb00dc33c22..f9d1b619bcf5d7ce3693baa5ed80987964b0a350 100644 GIT binary patch delta 548 zcmZvYy-LJD6opw$thBI6Cx}*p192sP+W0DWGIz3r<0MS7tSK}W!56SdYbRR@!akMQ z+1NOf1#w)<;X7x}x##6Gc>D?;`bodlZ};At|GFIZ-o4$Uyp*LTl@>Rk+l#~F7$N0& zNreH$j)A^5B49M4%n?yJ$1(SBdDDoA!ghl5B;<+Xs^+?q)(2UN%&k-j zWwS6&BO0w;-FgqAky)}?wbb?w!#L7uGIg?Al(Jk{wt0S`5tTvz4B8+_OESju(=+GC qyaj8DabxR3Lpv^ZKqch%j4%ux?_wqz9Kt*AWVmAf&T6&vtNm}BAh&k_ delta 25 hcmZqsEw=odSVIeA3sVbo3(FSPjVrectYIw^1OSbd3HJa1 diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index ffdd83285e235e4f723f8f46937a2c0ab5033570..66829e602dd097a0d7a9fff33bb7b047ba68c82c 100644 GIT binary patch delta 589 zcmZvZO-{ow5JpwHMDz?utRp#f>@?*dtex>VwQB6hNmVKqp;B+a4i>;2kl1k)&VsG* zXVLZZ%s1cj$HVOLX?Ay*98O-RFZ*HXo$GJ2%+@yJYUlbq5;+S;Rx;Q#5)hhFzrle6L1{SJ+B+wlH~hQ=e>fR32$QJ6sH1ga`^2RjjG phy?NDe3a055&u}onzA~TP#XcU4I!zw?MnBtzf$V$c|UzW{RBeQzuEu* delta 26 icmbO|pYPr*zJ?aY7N#xC3mc~&sAp!|{-l|CJ2wEAwhHwC diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index b492e4d1dbd0bfe6538e68d291f6d78972bb52ea..5368b2956d88f1f8c109c0dd4b7fccd8706310dd 100644 GIT binary patch delta 576 zcmZY6y-ve06a`=vXrWy}VnyoONKW$)&%)r?*Qtf&ASYGG6b4Uhh@k^g-+%?7GB7do zDhzC0v4No`qxH$x=N^A-W}k1fTRqTwdS4&tp&sc&eWb@Pll6A>csITrl2VlF{_CWZ z`HlB;@G2msq*%4*XVaJ@Q9>^~Z-*!*wa zZxIjggX5hhuDP`1X&eNpyQczbp&zzX*lc2q(FA<=QVT+qjkSIQg#wU<{H*JhS_2k_ z=7?mO+Z$=Iy@V*i|LQIjw*QA|9_8--xxmD3g^=4=v*5K2zIskar?2bnF7M2#)7Pc1l7LFFqEnM@yZ@2%&wN@Db|HTdO diff --git a/netbox/project-static/styles/netbox.scss b/netbox/project-static/styles/netbox.scss index 94fddc32c..f7d3d7069 100644 --- a/netbox/project-static/styles/netbox.scss +++ b/netbox/project-static/styles/netbox.scss @@ -835,6 +835,13 @@ table tbody { } } +// Expose theme colors as variables. (Useful for dynamic styling of choices etc.) +:root { + @each $color, $value in $theme-colors { + --nbx-color-#{$color}: #{$value}; + } +} + // Style objects with statuses/roles within a table. As of implementation, used for IP addresses // assigned to interfaces. table .table-badge-group { From 3b2a23c3a88c1cd609168b5548dd2b0058e088a5 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 23:01:08 +0200 Subject: [PATCH 3/9] Add 15% alpha variants of --nbx-color Preparatory work for factoring row styling out of Python --- netbox/project-static/dist/netbox-dark.css | Bin 376198 -> 377206 bytes netbox/project-static/dist/netbox-light.css | Bin 233371 -> 234355 bytes netbox/project-static/dist/netbox-print.css | Bin 728556 -> 729540 bytes netbox/project-static/styles/netbox.scss | 2 ++ 4 files changed, 2 insertions(+) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index f9d1b619bcf5d7ce3693baa5ed80987964b0a350..3032fe467ea47d59c64340686dd42ea368d0ac99 100644 GIT binary patch delta 850 zcmZvaKTE?v7{>9>iG!dn>L8L@qgER6u1Q;UaC33>3tZA`4928fTWbeFqzFzO9J0AM zHE|IHCj~!%lV8Bq(M1p!--{_+Xd&DXe)qi3`#g8`JNJD#f6*D~jCRI4ZAK1%B@tW zXEQF71u%`{LkH#9z8RwRtn1iE4H+chjSx^H1@IAr=iqV@c03l`|BkmIQkG4($)u~p zB64w=L=-eALQlh@poVsRTlU4UZA82l8G)f_K2lav!%#RZ{S z#tAnTq16m$6=^OR62f8xars>2C6l6KToS?Je%ob1wwdettrR&}!C?f!+MF>@0x=$} z-;ZFI2BWUb5!fKEA8+@Yx)z+(rfI#wXL2Vvn^XDWk|BjYM@T&|U24f8g53WnV&2s> n&pgvC-}SlHI&vCpN6A*J)xrP5`c1dH5WMb^6{4^QK*t_n|u z8aaY_2bKz2qU^D%#W=^(bgR}b^D6n{7@TG4Cvm$#^Q##s<-f*Y+r5GB%U;hA;2!q6 zSeo^s|IZk9N@3hd42phoMx|aWu(cFE*+Ze?Fb)iodnCAzX7&oOkwRs$E#g2b;_~0K zunJ`KD21{eg~3VR?02!1yA3M)g~DkHI~zF7SA+9#X0KVb8WY=>2$fpVKpGH3EymC> z_a^DwpquEo=14|SGMobc&-zOSI1W5Hqm oJtY3wapKmQ<0ny~oZ(5>mj{;J>2%(I*B-AAVeR21gP%)(0kTillK=n! delta 125 zcmeyojBoaQzJ?aY7N#xC7aFHCHZdzq4{c(WnZB@zS!(*rCT7{`X3aouV>7e#^fS#s zF`*V95!C_|Th;=U{oKMVIo+z2S#oj^qtNt&t;}lE#oB;sxs TJ2Ut6_zq^Z?YG;RXK(`mA51Je diff --git a/netbox/project-static/dist/netbox-print.css b/netbox/project-static/dist/netbox-print.css index 5368b2956d88f1f8c109c0dd4b7fccd8706310dd..3c58d79c006a25c5beea279a242142bc7c8fcde4 100644 GIT binary patch delta 807 zcmZXSzfK!L5XJ>|g1u=-;TE5LC>V5yylbETfKZeoU3dYywS7KLIqs~L!Er~iU67EV zxRw{7L!zJri6_Vd&__zUN5#xy!xE&^>a_apH{X0Se{Y<-Tjy9V$VHixdHF$pluNQ8 zi&tbtt&T)^z{S+%)Z4N|(Bn0aIu>ONj!wg|HmSFsepbXy%Ze28$fe(Z7b|I75j)*c zWa;_t_@r3MA-k){S|yB!i6Pl(Iu@gNH0v|TK#>i5!bKd$fk86QgTh@X+@66_k@f0; z$AK?Qj)9N|SgSkZE)=QRQ5fuxO{@V3Yqao~gEwHXJmBJ|!FkZG*8w%~s5}0HUG$K1UiVa=^&P8S>D8B8waqpMgPohM)ll^e)Z&~(mIDCJvJ)SS;OF4L#R;-26_18yo GX+Htfc;iz5 delta 148 zcmX?dSm(`dorV_17N!>F7M2#)7Pc1l7LFFqEnIRxrsw_O;+!7wgG+q+;vZZx)8GH# zlA3P*lS_7b%TFNp{7)|F=~BOdVhJE(#V?@PmtR0x$KPC%(>p-K+224h$v;3M Date: Sat, 23 Sep 2023 23:07:16 +0200 Subject: [PATCH 4/9] Move DeviceInterfaceTable coloring logic into CSS Preparatory work for simplifying toggle button code for cable status. --- netbox/dcim/tables/devices.py | 11 ++--------- netbox/templates/dcim/device/interfaces.html | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index fc8a3af73..063e05215 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -6,6 +6,7 @@ from dcim import models from netbox.tables import NetBoxTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from .template_code import * +from dcim.choices import LinkStatusChoices __all__ = ( 'BaseInterfaceTable', @@ -51,14 +52,6 @@ def get_cabletermination_row_class(record): return '' -def get_interface_row_class(record): - if not record.enabled: - return 'danger' - elif record.is_virtual: - return 'primary' - return get_cabletermination_row_class(record) - - def get_interface_state_attribute(record): """ Get interface enabled state as string to attach to DOM element. @@ -700,7 +693,6 @@ class DeviceInterfaceTable(InterfaceTable): 'cable', 'connection', ) row_attrs = { - 'class': get_interface_row_class, 'data-name': lambda record: record.name, 'data-enabled': get_interface_state_attribute, 'data-virtual': get_interface_virtual_attribute, @@ -708,6 +700,7 @@ class DeviceInterfaceTable(InterfaceTable): 'data-cable-status': get_interface_cable_status_attribute, 'data-type': lambda record: record.type, } + cable_status_styles = [(slug, color) for slug, _, color in LinkStatusChoices.CHOICES] class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable): diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 8b3fe3097..54682439c 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -30,3 +30,23 @@ {% endif %} {% endblock bulk_extra_controls %} + +{% block head %} + {{ block.super }} + +{% endblock %} From 32035d2f287c27783a9573366066de08a1552124 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sat, 23 Sep 2023 23:45:08 +0200 Subject: [PATCH 5/9] Simplify mark connected/installed implementation Fixes: #13712 and #13806. --- netbox/project-static/dist/netbox.js | Bin 530368 -> 529923 bytes netbox/project-static/dist/netbox.js.map | Bin 450659 -> 450364 bytes .../src/buttons/connectionToggle.ts | 36 +++++------------- netbox/templates/dcim/device/interfaces.html | 9 ++++- .../dcim/inc/cable_toggle_buttons.html | 15 +++----- 5 files changed, 24 insertions(+), 36 deletions(-) diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 84bfecae34920e732ecf52670208390ce6712040..d457ae229f71cfb090301054275a1d36b6ddfb16 100644 GIT binary patch delta 15474 zcmZ{L349yH)%b7LyISY=nZ!99TTvW2Udu{M2-w*;vgBKbEy*`Q(P_&%y^<`;2UoeF zg|IC{ITP-NgaB#Sq(Epst`7<&q4Wa^6eu?>l+bcCQ24*um1CFh`~QB4X6Nnf%zJO% zJ>%aVDErC2vISagCBJQQ%q^6)?pjbyHM(|{7qdZsf@^m1V%`_U;`Wc`)7->the>c6 zxwm4MRG!lfk4xSy-u1Z#^@zpjbE?NB{tm4XE2w2`qf46+xCzljB~Y(;m}*BYVor_? zX+%d(Cu$b&%&A`5;?hQjwSpT)mh;h6INZ?c(sm#GFz1Hd^LkCZHK?uRTExrmTvOg} z;(JYlLS)C(REtR~M7YW6%KBhL=O2&9gk*y67ytXtRj5OJ|IUlxclfS9pgwWy-PQ0r za<_V3yNT~ez+7G8FYa!d*JaXq-QlpdU3~ZMb?ktN*K>ZGxa6LGX24VhPmy~zp`^I` zo-q6_y7#Oi!Nxn?+RA$0>~g}v{(B!pC@3!2TT2CP_~b@ZC_47Wi=sBZCk10&vty&; zD|^k>giRqQX5&Z0fXaA;ACE%ow()JG?}*4Ba$y_a3lnfAHy^WU)#*?|UCG(pe8Q%4 zWFnqenDg5BusC|(O4@C!cZ%qNV$t?>uDJg`JMvz)uXbU|#&;0D`D}bjY}mH~`NV;J z%_t!5*;hyfZ1}_#Ore;K--hDi zU-xg$kJ$JK;o*>tZzl7Pi5u@PWFxkUit?fVm~{yn6I1uMBaiso``4;GHrU)sF@CH< zTyj8Pno)SkH7R7)TW9&yWNQ$2Jz0d`Tn)Uu_`p(>Ie627Cs9!vI23^eU0K4$ZM;iN zJm^5<;xi8(MQQQUhcx*P8{bFzQDva4_%r<_A@W=u@B3Sg!QfCC}_Iyw%*4)f4HQRInf|N&qk=l(MOjtQxmwi1? z+;B*TLSp|R7yKSMWGu30H_2+_2MALA5#BE5J=}n-V&}uQcvl22-y#L`m)FwXuaC1qgjraRtqLJ*xtu|%%8^uy-*}P^OKS3mNLfj^;U#_w7 zBQ{;H-yO2JHKaRH{?$y{X%n0sOzy!gEkW@)Ro{z1g&bY|-XRNt z){zvS`psdmN9R4FM(yIBM^>RO@#rI!1--V)Nte)&5e7ZN4n88(^^5;{WOqTIO`9AJ zCMtD_SPEvEn(7emdsKr4#N&@v!|(TxZqoS;d@DhwC(IjT&?XIv777MFY0#>ZF=c6| z8$^%`iswGI2}Q+Sk8P#=2Jywm`ZQ64)+6vX&xqffBnra`hRRBwpAp*zz$GYl98IoD zWEYAV_$cgQeR$}!4NQoyAKidr2UU+BqvoXye2lygh=yauV*FGt9Smqw2TvYrMOvRh zQQ?3=L3Y@{tJS(hJRD5c56{l$6Wvc(3jzj$51^t@pl%ozk3CVpIBwuA?&Pq}>ko#x zh&cbrMml1si;JC4s^<-ncff=(aq`I;dd#3567PR;>U(y zXj7J7X#?LR?tJQJc^Sfx%8ClY7LPEP7QcVWj9g;J)79{sdAg>2+`zku5IYRKnJngn z8OZjV4Ez9TZQ`>}@6tF8S|@PREbtD4)&Ng-fgd+$O#){VxY|n2Du7;}_e=@09lYV0 z=g>TZ!WuBFgl!i*y9OD=-Osu<8x4Ff?5_?)smbjf=FA2@OjftR>af3AFzB0UmVXxU zjOWTJqXF+uFlUL^JXfR|HSp!-TKxA9*j3_F&()(*5&h0n-e%xKLtsWNLyBLT+*r41 zn%h`eZyy4)(`IPEWp^+|;tjtu<#%R@9Wn6zKxDlY&|D@SxQC@X4cZZL$@8l>wi*;W z-Du!_1jA;aNLFX~lt`w=pqRF9gQ7+~;*RHQKy!ck{K|p}ndxAUc#T0eb3Nj3o?o}L zn}B62l{KEt25otx_~rAp#l6|BZpn&6#68aSi~I}KWxcZ8%$VDVuvOpCLME+(sq~eg zSy#TG$?wP_)tBA0cJc8S&RW%>py3kuK7%$b=)(T!P;wZ2rogw0g}?u}yes?J0MG`~ zqYFlT{&8D?^Xqv7@zfL9-CGEH-SFZ%%C8p#FE)d!?tk&=qNJXW6UGR7zJ)L*sOJUo zvg1xRsV8Ix#lIZy;-Y%l=W_`?-%kd`^n8ST*4U3(BSXvg*^LDq+=T5r$$!Ts#?pY1aww@+lZB>KTUiyc|T91M$ppwxmcDG5- zcM-)(!v};VE z>v}#T>$;xz5Vm%NdG(;W%BHVU?@)6FK-53fG#=Ng&sA4-gvmek2C%tAs;oApYVX35SR0^-L&!oYDvIAKg#rZ3E!^}4uQNch_zO0w(u znsW!Ms&_Q#2m8i%)Noc&|LVpZtG)sM`8sA99nxzp;`y(xMt1R*S2xaU%gSG;_{yub zY?~}}onroLwUr~;T~7P>CPGeYR_Ge_Sycv(6LKfS$ZG~Pa`3=wO$Z|CFaM;T*Gh!O zA@Gf2+3QQugt+DP{j0jO)Ahh-Bx38;^CM*ATJ(I+b#HLKNvNJPIL455}da**Ss zsC`onI@j^$_9DTkgwR8?Ukl=GZ`N%LW~YxD`3Uft^NcAN#RSsOPhx*dV(2*-wuO58MNT%C5C(*|izywr@{{qC{uQ2TXy9(z)ZaT{WFkBeC7Ss$SHpLzIm&`$XftiU0W89P9c_zB{Ok9 zXUsC#41^JcEk@q&g*F5P5LaYBji|N8Xw({v++@^0o~$z)m7sW)^*@pllmH*-U|(-1)b!mo*x-5IwXB z5IqdG2|Ft5{o#batW%7BItZ%w+^1_G`2FP5l?%Fo{xMwwBGDc(@3T^X)_himy2Z}V zmch8;&#F+*!JmJoM=N@Pg(QY)F)H?~AEcdNEOCyuQW11Iw-zgf(j+)|h{EB@Z?kv$mfrXW6XG69;fzg_AL}qMdGYHgWJNst`8IngnYN zE30x|GoSJRB`LF#=K9Qhfb`=YBB{XypE7IJ5r5JR(i;#T`-cH^`-gw5E)1Bfs<<>{ z9VT#7b(7POAc$}Ovjn-twtxPTikroqM~cL6{%NBkW^vCw#h|RNFDsS|!R&J^g81_< zp8@?p=d1epUb8MR4Otd=w>@7KR}GoDfIE@w@Mr3V%_I{c368u}+|0$I&4S-Q;jfFB zr|VoF@PBV$%1x5>kNY6&hR{+5vHcn=D{X0^R;G` z%PdFh+<5kTM|M7^nRkdszTUdhX@<%M%OU(O5J&d#5x8Ake(DKi5#Km<9=N>0Z~MU? z9s0Jd*k}gdTv<6uOt(#dVu(@T&0^tqJ5cE0=y%;{p*4%F&BI#{zVz=Dg1EKy2LXN` z`Qe=YwyaEanibDE>XE|%ZbXqxuGP#3iPATk`EH_Z&1Sxbv@JeGcKUrvXft8vEd))C zSuqmbzlfs*>iorf$b?3uN`yW_s1+CFpa7b{nH+R=akE+5NrDOvA|W|@oDleKyn;sM zs1a|a(NWZfzok(b(%@AL3YGL^8P$ia?XJ+iiT$WN#9Q?AdXU*h#30m;V=TJ2tkqFu$cP5E# ztRs=8udWMk$wj581^48l4X6WOn2YqNM|vn1xvBgCSc`DVZ&5gy#F_gPSEegPA;ZKK50o=75HKJj>CJ)u+#w~onGBq`f-FaxWK4Qt{ z!MgZqPcmfTBNp9E7R`-Wl;o3S8y1qA7&quuFXfe2zl`0 zl_)Lc6{6oG)P&zELU+Qri;K}kD2;coMm5NVkFQ4RrD+TC$rTkt79|mr^WGSaEk$R- zkh_Fl?FF3t2{~b(jl`#>QnTO&A6~o$Ek$8`^)j>?R^Ph}twQ5? z=NhyezqJgpA;ag&#W0)h;eRyX%dT3+c92R>6d@7?Gb|-A2GV%Ad2D>Ytc&V)u2r%f&Wy2R^x*j#KZWL8nl!bLiIvy7C}-%F3=)0ux-qKY|flygz~wTvGU^^?-{H7i!UZ3YfB03pib_1q6~>RD=)0Qvpm! zlzn<8UMP#>K`ko5FKAIdir}}jr~zhJQwe+tgzAHHQ6#tw{|H8KiT?;jaEV}{64ip< z-(86~+fazCISG}x@_2?kPEC1aP^D5>Dc>kt7ASe8W$@HgzpNn*U6#6T%Z$W(gsj!U ziWE>Rp^UWaqqy^7hjmApMA-VX-Swc!`6bdOd zIy=tQp)=N+Wz1w!p~hngksy>5cS0ep3!Wg2wM#A?NY5H;NU2bchUDO)zH?TEts(6w zK2ihXJAuEh0Xin|Dm{91p+*_XX)?S9zo$pd*gmg-2puT5z#%}!i zMpT4)F|!Hv6}4o)>d$`F5#syt=q4D@qKxanH*G@Gd|yb3+1p`!2Ha{#hVLeAS4cVN z83-v^rr$1?r{%(Q($4piF#_(cMP+o*&UfKVE&6m>)Xt9wrluV5zb`X2m9$Sy3HJIZ zo<0MuUEsINH8}|W`|y2dpxvllGH*sT6bj<0E$BkfwePo}On$Nx zigfc*c4b$5c9G$c=f&1wJ0Lpe=qejgoY%3FI7&Jk6HPNCfIe4m2V+!7wiCOGgiKxv`zT@ zebhFHRF3YWenKOc)c7EkVL-nheV8(Vwjzo82O5{Yl&CW)c+35U+LZ(Pdh{_$2OoX< z7*(GGuPcvHn-D1VIsZl7hMJ}C{)=kPlmuP8H|$O%Ca3G0y*iW0;*Pq9`~n}82Dj#X zOrfaM)R6NLv{=`eb1O72H0FShc-WZpWG;$HPj%#6it=NwtkZP6h|_HG;+>yR75E}c z&e{bwUt}JvZ=kB|bVIfg;OTwD@U; zTAUi6qY*wlFhswP-w7(Sl;G4b-45da+hMv0(E%8WKN+Db@b^LbFhupz2)z}5BGCD` zbC_NyRfg!LEJWzA)Lzh$zY6q!gL3>jL2o6>aWY9eN!vU|Uo{`3{iO?NEv~v5+faFqZc4YIhBE=2^lLa9Jk`~mo(!+Ow%Jt*7 z?xL?m9n#3%^r0Nohs*ZT%eMBBj|PVSWBztn4ubbs+UySiOS@*e_TbPk^asZI&3d>( z;=qZ$utQ0FbT7Ssso>xz2iwPY)D642!7gZ<6VqG}Uw9u~YL7bjop3zwi>0HQNnaFJ zOaLSjOC|hp>%uQ&fw382*zX>L?U6^i0ho-Xyu)y+E+n(SFg|%7U3k8C<{gNC_Du>v z@mP?|I%hfGEHo9*jtl_IC%A{^&N~dSaLlbtrkNZV22|q!^M++{0EDu@h=W&q@lW>A z#brbAmjTOf3WtD&qFsRqRGiG6fYhrEiK%lxkqqyVhun*J9&F8IgPTpHk6 za;m{~M`5T7FWXPA$M*en!AdpffB}%a&sno97#UJ?P8`}#S8p`^=j*61=q07|gqpL# zn?YiBt38QoU@ZjsfIfeaXbP5&JH=c~V_&DeGbjF|oqy#{P_=OOxL3S7vi61^5|PX1wfJqqBn4}+~q z;qwpE>P0>Wsq1cQ5=h8{A9#^2h4RD;hiNE!$^*cZ&p$$|@p*@77S|r3yTEx|a)eHR z$@|DACA&j6Qg_e zZ|NU$z$Rb)B>gEJcL2fDPtog_4?8&dW*FpRW#mvLC|AF{m2wMCy2khLYci^we{-mDs?t7wMH!&GR%xAv@mmd-_vkl>l}&#f&;C!5O)~pbPQn z3HksaS^g66xKkQ>iLT3mI>haNq<5i`R+t{Fiw}})q8DmzI3e4YR&0KSwy7E&{3fm$ z?|Fq*Z=X0#z;FWS)65}226O{JpIZe}m=` z>XvM8(itkJ%TbAcs9|dGski9Ip&t30w`n7S#OV8X=pHm7b^nDPMMxt(^De!R!l&M+ z7vp!{r%Q_a9F^Hbwk+YSC$a-IGQ9HxW+`6x0o`5H?;z=DL(*C2cfvt+L(o|#;Gca! z*FuQ(UmwsR&<{!XF?|U_jnc0_q4hZszrOz|O;e~v`sD9)qY6}@`YYH?FHU?#PZXz| zygCx|xx>{Sw>L!Cs^)wW^)H3ORn!#55t#p{p1Q;RhEmtMHYD%o6D^ zVy=Lk1UJ%51B5vj(F{jHG4z0nd5Wf+oR!nqkPBmfzJOUx4LNc7ix2=FU%=dyLuZ_j zW9?bNtieB9!kmEoY-b*`ywXXiumKfwe69&7RXjr%A=DNpL-3wFNSzJ%Lb|k=F;gmo6LKyA@62KJQtoo5 z1~GOgH|oUh7_%N|e4b@0z))UQ!W1AnPUxIa35h z$m`3Qbx?$Sv>ce&D}7eZJc+=@99+xnK>bp_hKZwP6HYE3i$f977>fpiLv`eW?KIaR z-LQ^XiXh-QP{C9#Y=?DZk%hoUdar^BB77;B1YG(CXmwBvBGNI}QuIoDwaf$aL171L znGawVsc|#&vI>@7wv~AfbxD8O%4}!BWUMtY$0#r;?>95Ap}GNB1#pRt`2{uSH^~}z zy5Fdf<~M!sJ1S|yA2c)FxXs4=b&Xp= zE#Vbn;cy}jDrgSN2Ek2`3u8wovl*<<6`jnsGg2mgryFh`jKS?ja9d#*-~*8IZ59v! zfTAYwUNafS-*z%<$+1Tlvl2A4z6)rJV0RazsThK#=lG+sx!vkyY-lJ95tT=}uM0#C z3|g&&Ify*chYsc>1-9(99%eVaWXuE^giem;;Z$M>Up~lm!ZNQ7GJUYjrgNAel``Q6 zR1m^EbPm&w@&hKt&(ApcVZ7*EMtgn+7Eii8V5<~<$j>gf%!ww#iL{IziI(J4!Ube1 zKHfz_fN`ZO`*<1PAbgQS1i9b$FO*h%}^Uz06LewVB|6lsGh~S3>!9hQkIE-1LTPOf&4W zNL_xgfV9cPnek--2G`oN42;2PGk9-B((Qyi>kQGeCU1Zk%2C`k#1x+22CwCeAeMqC zUw+dGpa5LWP6UB*GGt^1GCQahK+0CgBN~;iycIH_834*w$lZx-cVn%e2~^vj4&a3!c@uO(ipP>9~fcYgtHrX zxf0(S0{7G?eHLP(IkeTp_28Xga`Dj^IGDR*%yRrCd3uvPaq%n+^2Ntv5T#h-j2^9a zXK%ZP4a$`auYvc%owItkVQMOjZyIGvTfGKuG8Ij@1OB>leYR8(GAL)!b_1UvC#YT@ zZzEULtZ*9_?p4UQlng^%K^^$E1k*FvVdO^uU!8Z@Ef|tqU-mjk)T7*NZa4Cygq(zD<~DBG z%%vN!-bOyE9A6K}PmsXC`ps~tpMW#qcHEhSb-Hjo$t(lge|ZuXAHe&Pj1v||DW(PD z#kLd^g|gD&6mv;#p(_LqFX@>AlbwVtDO|&iOP%L2izz6ixGn&143WhJ%x$@pBP5^y z;WMvb?uX3g{VSN;)Xvb{ix}C%jq)+?G-nF&n>On?n{@Y;fB|I);Rmk+v3dV0=34N$ zV^@R!a^jb-X5IyteE6r(Ks<5tHH;B*u(z)P-wkcvF6QxEXIn_wqRtTC2DNm!BpOI^ zBO&ElZYvz?ksC*?A%27u+8dz_Y1M+?m-3RU=*=O%yfsu&(VE$|~Pk>#qrHIGiS{kvn!YGJowF;$dhAierQkw@1%~=ZCVhRP8ECrz~1$H|hCY5O`6#NNAqfpx?Hwi-le$-Cb zDVOtspKaMDpD<63d}Rwk@!wDaXQyL=}NpS+dv!oGLk z29C;t@4k&mA%nDPHv+#Ni)49^%B2^y)HtyF2DhUFraG`1~38-K|v2<6FDhDmn*tu>y*JI~e>|C#Os95y^ zMfKb9?RIj!SC) zkrk>v5ZCNjsj3D=ylSQD5{RktSE)W{z&227m6qJ3($zgD#qrV-Yv91tZvy;cRqWa;l~Ro5a2lP*}Nx)KUq3o2Cnl7y9DG?~4@9mTyB zDv~2kRH#-ij?FpYh*`z+$CgS*;Pp*V6@BBFYCfIou*!5a;S*MnjlAmm6P33t=i{~!r6tIQ#V0T4qBI>^OGm| z`wWONK;aY`r{!CD5xnyvRdK18TzaWiriU;J4u0nx{bq34SE|(uBW@v74H6xV`hECh zKD!2YE>qEY3W@6M3w0$vzJOi1SRNdV!i?eY+~FnJ;ocZb<4yW~Q01MLZ^TNOuT(d{ z1r({^8`U!u)OP-KN>u^X8uYDdE7;U+->R;``QNF|UZkEqsdc9DNm^Bmwcn|JQtAZS z>KyRHji(8>2o!RIyi4#x&N=%Uyfub+(Tf+%qV4E9l`U#jc|$vpl{C@1xlVKu32lYzM?e zzhc;Rq!RO@iY)`^=PI@sdMcK+fQO5$Enr)y z#x#x;s@CDF3)qom6KQ@ds%?ff!Axgw4wh+Tf|p@UAzKJ0!81Vn08NJwhRqWUXgu zq;in6t8uAsE8EO~C%etSUO|JkscdF{sX|Wa2{U^&1&r^sv2QH~2dF=n-Ma=9`1NV_ zOqi?SJoYxYEG9j4GYe;SJ<`p$vDXzAbh@|^*J;J>bXAQ=+Yhp*$k9Xn!|bOyW>+Q7 zzl1Hu+a)$YS>UeIA-24P3%O3;q0;I(i>qc+b*oUbsS+`7d95xeZ+XW1?q%!>U5wju{8&KxIfGD$DI%$`ZnHka0dr(b8+OS)HBlZrC9 za7<)Nq@TXYst{##;q8yWcJBW(ds!|N?l*tPVsdQv!pCebY5(W1EF9qU;Wz%qW~lb8 zl!rfM7a*!DjZdtG+a2e<$Eu_oK4aljunRx-cXk{y)-yk6|B9%AG=BUPTP7L5V9mLd z--UObV#^l}5c8(!Z&EsXiv7bPN&w1;O5U5ByQCmV{>W8%A9oQ^8kU~T&0UCcBZOkG zTX=M-ssw+Ro4Wv``MJe#QoL+_ZiIr$>m>_w@1}}|NWHbmHD_HHL77|vRf@i4vB03y%eAJt#x!SY;tcT5%XNSjdO1y+Yfp22 r7bm#5h>II@aUK_FcS%uh7%^m?I(&2_XC*GXnl6Ai@o;f&F7^KaI?2)* delta 15773 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 Iygcgv0oR~A9smFU diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 7f2400ed2610973ed909703e534ec9b73c19c8ae..51042c43167bdeace2fa7ed21428254800856a7e 100644 GIT binary patch delta 181 zcmaESKzh$U>4p}@7N!>F7M3ln%-3B7Smnf> z&A~FhAVq;XiH?qmI+2d9S<@rmu^KWv7dTI!{EpRJ(%n(V)6vlrBA@T%=ve5a(yTx>4GnRCy4Gw&b6pU>e_xlwMGPT6}^PJJ@cbOc;8G1wiOI-5nwxLRVk4IvC6}h3si>{C2id->YA7Q-uZq diff --git a/netbox/project-static/src/buttons/connectionToggle.ts b/netbox/project-static/src/buttons/connectionToggle.ts index 74b32dc3a..ed119f738 100644 --- a/netbox/project-static/src/buttons/connectionToggle.ts +++ b/netbox/project-static/src/buttons/connectionToggle.ts @@ -7,10 +7,10 @@ import { isTruthy, apiPatch, hasError, getElements } from '../util'; * * @param element Connection Toggle Button Element */ -function toggleConnection(element: HTMLButtonElement): void { +function setConnectionStatus(element: HTMLButtonElement, status: string): void { + // Get the button's row to change its data-cable-status attribute + const row = element.parentElement?.parentElement as HTMLTableRowElement; const url = element.getAttribute('data-url'); - const connected = element.classList.contains('connected'); - const status = connected ? 'planned' : 'connected'; if (isTruthy(url)) { apiPatch(url, { status }).then(res => { @@ -19,34 +19,18 @@ function toggleConnection(element: HTMLButtonElement): void { createToast('danger', 'Error', res.error).show(); return; } else { - // Get the button's row to change its styles. - const row = element.parentElement?.parentElement as HTMLTableRowElement; - // Get the button's icon to change its CSS class. - const icon = element.querySelector('i.mdi, span.mdi') as HTMLSpanElement; - if (connected) { - row.classList.remove('success'); - row.classList.add('info'); - element.classList.remove('connected', 'btn-warning'); - element.classList.add('btn-info'); - element.title = 'Mark Installed'; - icon.classList.remove('mdi-lan-disconnect'); - icon.classList.add('mdi-lan-connect'); - } else { - row.classList.remove('info'); - row.classList.add('success'); - element.classList.remove('btn-success'); - element.classList.add('connected', 'btn-warning'); - element.title = 'Mark Installed'; - icon.classList.remove('mdi-lan-connect'); - icon.classList.add('mdi-lan-disconnect'); - } + // Update cable status in DOM + row.setAttribute('data-cable-status', status); } }); } } export function initConnectionToggle(): void { - for (const element of getElements('button.cable-toggle')) { - element.addEventListener('click', () => toggleConnection(element)); + for (const element of getElements('button.mark-planned')) { + element.addEventListener('click', () => setConnectionStatus(element, 'planned')); + } + for (const element of getElements('button.mark-installed')) { + element.addEventListener('click', () => setConnectionStatus(element, 'connected')); } } diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 54682439c..c4aee9784 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -48,5 +48,12 @@ tr[data-enabled=disabled] { background-color: var(--nbx-color-danger-a15); } - + tr[data-cable-status=connected] button.mark-installed { + display: none; + } + tr:not([data-cable-status=connected]) button.mark-planned { + display: none; + } + + {% endblock %} diff --git a/netbox/templates/dcim/inc/cable_toggle_buttons.html b/netbox/templates/dcim/inc/cable_toggle_buttons.html index 4d8d995c4..1c5427337 100644 --- a/netbox/templates/dcim/inc/cable_toggle_buttons.html +++ b/netbox/templates/dcim/inc/cable_toggle_buttons.html @@ -1,12 +1,9 @@ {% load i18n %} {% if perms.dcim.change_cable %} - {% if cable.status == 'connected' %} - - {% else %} - - {% endif %} + + {% endif %} From 6a6e44dca9796536aaea2d6cfaaf12671f300601 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Sun, 24 Sep 2023 00:08:39 +0200 Subject: [PATCH 6/9] Fix formatting --- netbox/templates/dcim/device/interfaces.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index c4aee9784..8669789c7 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -35,9 +35,9 @@ {{ block.super }} + {% endblock %} From cd523f531104784b5bbd3f988daf377950399f3a Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Tue, 23 Jan 2024 20:49:10 +0100 Subject: [PATCH 7/9] Refactor noisy getter methods into neat lambdas --- netbox/dcim/tables/devices.py | 48 +++-------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 063e05215..fcacd886a 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -52,46 +52,6 @@ def get_cabletermination_row_class(record): return '' -def get_interface_state_attribute(record): - """ - Get interface enabled state as string to attach to DOM element. - """ - if record.enabled: - return "enabled" - else: - return "disabled" - - -def get_interface_virtual_attribute(record): - """ - Get interface virtual state as string to attach to DOM element. - """ - if record.is_virtual: - return "true" - else: - return "false" - - -def get_interface_mark_connected_attribute(record): - """ - Get interface enabled state as string to attach to DOM element. - """ - if record.mark_connected: - return "true" - else: - return "false" - - -def get_interface_cable_status_attribute(record): - """ - Get interface enabled state as string to attach to DOM element. - """ - if record.cable: - return record.cable.status - else: - return "" - - # # Device roles # @@ -694,10 +654,10 @@ class DeviceInterfaceTable(InterfaceTable): ) row_attrs = { 'data-name': lambda record: record.name, - 'data-enabled': get_interface_state_attribute, - 'data-virtual': get_interface_virtual_attribute, - 'data-mark-connected': get_interface_mark_connected_attribute, - 'data-cable-status': get_interface_cable_status_attribute, + 'data-enabled': lambda record: "enabled" if record.enabled else "disabled", + 'data-virtual': lambda record: "true" if record.is_virtual else "false", + 'data-mark-connected': lambda record: "true" if record.mark_connected else "false", + 'data-cable-status': lambda record: record.cable.status if record.cable else "", 'data-type': lambda record: record.type, } cable_status_styles = [(slug, color) for slug, _, color in LinkStatusChoices.CHOICES] From cf6fd306d9c2921c28da2894c84769f0f11c90f2 Mon Sep 17 00:00:00 2001 From: Per von Zweigbergk Date: Tue, 23 Jan 2024 20:58:10 +0100 Subject: [PATCH 8/9] Hardcode cable status colours --- netbox/dcim/tables/devices.py | 2 -- netbox/templates/dcim/device/interfaces.html | 14 +++++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index fcacd886a..d15cfe64d 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -6,7 +6,6 @@ from dcim import models from netbox.tables import NetBoxTable, columns from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from .template_code import * -from dcim.choices import LinkStatusChoices __all__ = ( 'BaseInterfaceTable', @@ -660,7 +659,6 @@ class DeviceInterfaceTable(InterfaceTable): 'data-cable-status': lambda record: record.cable.status if record.cable else "", 'data-type': lambda record: record.type, } - cable_status_styles = [(slug, color) for slug, _, color in LinkStatusChoices.CHOICES] class FrontPortTable(ModularDeviceComponentTable, CableTerminationTable): diff --git a/netbox/templates/dcim/device/interfaces.html b/netbox/templates/dcim/device/interfaces.html index 8669789c7..9860d74ef 100644 --- a/netbox/templates/dcim/device/interfaces.html +++ b/netbox/templates/dcim/device/interfaces.html @@ -34,11 +34,15 @@ {% block head %} {{ block.super }} -{% endblock %}