From b371ea0d5f4a8532116699c655de24badf8fa54c Mon Sep 17 00:00:00 2001 From: Thomas Williamson
&EVptr)syJkFbjzKwD+%d%x*zsnf`*NrxssTaDpuW0|pnte`nA)L|la)I5Hx zWwU(PQc}N9{oNj~Q_g<-sYy%|o?Vr7P=q$Df|BF0HJ(l|l~^v^G(R0S)&lhsMg;(| zzx9fVt!2A08w&}WjsrWaJETy^IS|f53IoHcB>2h_+mh-!1S^2a+20~TUCzS^k4?Dib%%f0IfHlBx0F3wWkdJP$*G8`BV?a`Wu;w6&+~&uIZTl{eNp=NyAY}cRp p|q5Tlfr_n!KVh#vx M+kx*Ekdft zGaMU!q#NI~WnM;;_bzk%$HQKER=
9e?{d3%rrflJO}()8UlI&&=G%j#28L7JVz+6< z%>7KI&4U`ho5m#wiPbVKe;GNe6EC1hXAos}m=)NO8 BKz`nG%uL-pDQIgzDzYxrU6M zW%`L|D0h5@I_xd82oVlNKS!=>-^6YVlK8Rl& Enw~M7}%zHAPwu0cuJs7A?~{f vV43=lvJy{sR?I&83(O-f#vD{)U?<0w`ar|JV#J0b}3 f=YhlZL~?sS zCf-Xcd}-;q`oKQf^Lsqt< VhbIu88RdI1n=MuIB*>+$lJ!$Ehl0bjRREGSK%3`A_C=3NAJ)gOAPTXDw zB^mi&Am)W&!Tn}lz#PpvyF7=+ bwgH>-hYNHEc9N)N zrWkcpIBQhd%e>?T-~vE0_$--ANklITjP>zc$odHNr(4CX$HG=6MhgYoUfI^Bx*HQ2 zqQ{O{ +` zB KHWb@Y$$?LgpAXQersQHEtAU$o95zrHs8sxF{r!*zwzz;&@UvKKNKZ#E zBhVN7^lP|e@+1>PuK*l<)Be|EyfQqmf9EddIjFnwsY)sSx^%x4JEI^z83W8`tV4ea zLXg}pEeq@M5UND3DP1|_EXDb@ym+Oi9J4&y=VySGySdU1TBJbp6GKJu&IGs3V-f{q z!DGrkt>QgAC5K6}gA^c+`Qbo%bLb9B2@{v4tll{%T4$$q6%B1;fhS3{Ume76U0z-S zz>kGNziUFe%?$#4(jy9cRJgwCfMILwiIe?!zGQ{Eu~}&=B_dT*A(OMLD^Za;OKn~R zhhYXAl u~#f1tVH z53@R)Gma8|hv;)B_KosCxTf&gn2OIS^wdI|5SVr3O5`FOUp<^@vmf(x@*;%9#iof3 zY1z=BU)~}Ju`rH-#jY9-&UL+ibqMiZx*%8~!JgoRS(ILVRxew{nQ 3oj`qF8lgdECFLn~cfllGw6i-0w!JLei#f z-gAoiO@G2_99nPw>SsMnq!jQ;S_p9j!-)t=#CO858lW4}*2b3+l&&^bjs}G&RLDw8 z{I@)AuEq8~OTlufjN#kgGbz7qFrzn5L#o*^C1SI9Ly{#d1bI_(xaGmNG(?@4#hKC~ zMBT<(zFZ09%u 6P5J(udR7q;MY nzj) z)7CJrotA-Jh_E}PE}iZ@|DR2+aPCvXI>&QpI9`p}x?Qxl$qS=KBOv*B;6!+hCS&t5 z*#ebDO2lQr^K6o1erwwSa5ZMl50VMworXep&4KIr3lAG@o%W8d_Mm+TetJ~Ys2vy| z p@8&sKrNo|u{*#H|1G%}n(hvKc8}JC) z|Mx^Vuc#I=pzpuy$^P%D4!*1Z&CkI}Q09rVZxA}ffQfVv;w+NRO{{! pp7jjY&BH>qzg584Vw(OREMi?`* gvt79MVqVMctFN1bzS3>&iH)7)SmYEIAYSPwe>iv; 9%#Gw9D69Df2OM zY*`&bDzE#JryEU4&w?%))yn8_*{mcc#=*{JcTUr$pzoNlk H5^+4tE+onkh|L%lRd# zk~s^pTX%^akBHDd)chA_YwV_5sUw-bR$D?FkFRjA1G%G>o7F4$d@h}T=z}9b&WcqQ z64=SA=>k$2^E|SQ4F2(>i08*wJYuD9i=~FPP!M5lqlSE35gLZg=sksEXXT*sF RKM>g^l}K%A;im6x?_PO?D)(5el-PaR!0~T A&5e63^K+P%e zZpLY`W!gv21{O22cIs7;`@hQxmXlgEQA9EYf3ham1(#R8! ckbDwheJbqSfF=rxT fUxay;4)I#aK4`|8XjUDxBOr$b;@0^dc8m;FM1HyiRf=8Nunr-k+&a=zh zzEzMc)^g^BcTddgGSy#w-=AbKW$}73cMZ@gjB8|@vYYN#DSD`9%N=nc+tp7W8S(Gz z+F6svpYm`N`jT6d$f~MeLpE$KE8>{cTm3VBdD1CvI3Vx-AQ38lC=?&HG<4&>W_D|! z=Y_Ag26b!fl5T;L)n-j{Op0O>JC4?|L7FUvlt&v_1l07`H9-7SdThC`L{o~(Q-RC) zJKYung$6-wQN1Xalas@-i@O1DJo#Ot@gzUz`OYo=a2U^wtmS^sbXgEgTHNYnW$V^Q zISd^8(nIe1?dN|FsB+)G8e0h_b=syTcW)|am=pj|wMQo%ikKbkh|8IX&et7@rviyb z6J3LqUnUdXi*^=WF5M-~gPskrXmUV^dTpg)P+S?V&Uz1~qyp+1PAwmN0mtIrgSSrW zSE14Mr_R%sJaxWDV#wd@vuWUJ*xWAFbI-?_t41;E>D}-~r)QT!5QyC7yeen zT2-q>ruZFh?Bn{-+9*nlC^nqwhDyd(->puJ~l;B0(Rn zklc0TjrW5_iz`qouWf3WykQUdVQOXqb`oyRYN2F9;CJxrmsh(AQPwq-Q<}hU1uqrU zBAe)S(YqHA{8)h-usJs|@jyOteI57cVdB&|bz!M3ITjlk6&r!uBWc+=?{7WjwG&HY zX162kd7@1+y<;4f!i699f<#U|ut~c^XpYCf1LN8{m~hE$CLMkCCkaldn%B!%Vb_x#dMQjR-m2GyCB1rIT zMS89MZRc0-&bM`lmIj+%40QYJxq2frG!(0uE>5o!?5xYe;dT6i6p29y*nx~f)PGc@ z{bjzzrZ@WWO6LqpX-&beaz->lNjV~GHDXWFlzAOC`&Wh7`x9ulgT^m`lqDsVRrsGi zv)Yu5s=7vjZetB1EAZC0DqMAdyN)E=X7N*e?3QHS;IskzT$LPkgoBDr2w7#`wu}ML zMU1`4V1Y}s23DyKW)~4`^ji@nHx1{wBe)A^SwyF6m$t1%NRL _IAr=h3a%w{ zbmUybnJx+umm3KR#W56<#755nb;_YS(DOc|n2AQ4kt5o%WjGNGjL_xd#Y_+gJm8QH zSaaQ_hB%sg{a~ccd4EQhBs!Egc`N&zSQ{$@C*2(H{~}&n*>{XQeF$f7zDukJNnO{t z)j(wh36>ma1P(N?bu$)2_!IYqV!*khb+BEQR_%)#BSobb1r&o-BL b z<7Egj%GAA<)J)KUBhUSRNR+UjCJtu8;&?Z^7%5vh!=;W4GL6PLbHlePv*`CsE|jOx zA^FkktY>Yr(ZK*!98JQ~At_N>W7lxY^4I8@6p2eYo~f_LiZLEi+6&jTcLmDMvsnNv z2K7;zTAch{m^Q@j*gtaxJc_3{$8_xu7co1o^G!hBkAPgh^Hm((9aQ^nkd@PF$1P)G zG3$SK0Duw_&xF20$Ga=SSG0dWvZSj2aJsYr7 ^WM@b?4HUiLvzG!Xy|{<- zu(L=Rhd&;0dhowma{V11E=wBHv1Kvn^gt_|R}Jr;A4Wwa)_`+3m}vx;yV w gzI8t!Ac!6?xItty^QDTf*){=mV(+Gef@h zI6LKlhC9BQQ7%o=i6{fGsS^OO!sYqi i7qn6mdBG@@rQa~~^6CVZBVP@Z^l%Rz*@*|=aZ?0r?`5;BInYHG!IVpx z|IyH8CjR3sjnFq!-rw4KR)mw)1D{Br4<7Hz1^vD}Kt!*8V<%$;`6xqjNg#CG#hwU2 zIDekD|Hda$ab&Z=(Tvjkk9raRUUj#Oen;v^N)=yTn$!*ROW)uZUmNYQ_cJ@+*+7G! z^C_vIWADvO!RFAt*UMB_U- k4f%2@>5y)!7u!2Q>GltHB8S9ph<)X&-d7Y7$dqwq5Z3wbvj?o f$p*&%h31qmdEGH@a6}0`b|UN=$B5NKH6m!4BWZ%xrJC^ z8$z`dd=KI#N;bw?ma1*k(=eC6&5~k@`kLY3*jRAOM_oo99tY|kJID-W+`;{t8mJsA z%hjD LE(% z;LZXo&NpqP1K7tSieSitUrUS;teQL@szWn=TBE70lx+Lmz)y01MyCs?(J2~P#QWx= z<3{Sehw^9P6e|Aw)8=wg^Y6Qxe-o$OTT74cUz}bS?1O|L&F)nk1PW+A^}ty7RS#WN zF3454*zfa4O$0&MtDa`G)xG;yeaBO96JCDV-d95WNNMD*(OGOt4%q}*WFlG#Ec(^B z &y!Y2$y`v#iBHL0W#?+Ny^-3)jC+qgUOIgFR7 zniKn2#!jD>|%^c0bRNtA&Y^h7MtwIar(#&!>EnECt&bvc}KRhuut^#iCv0d%B#w z{1+)kd ^TBj~T4g2VWvofcjCyDP^czy!gX%SybWfhylPyAuZQvr~5eu|a z%mE^G(X8o0N&8#Vfxd?cg02{4MBa1T45^Vb(H@(e{STe~Zd@12=^M!uk!fF2^fs~x z1gudeN@JM9z(zO1s7~Lq%06Erx;H!k-EUlkGW-?GF~aS?0^mv2M&{#WP~GpMOJmB+ zT8|0VU0y}6CSFKJJfA>Zy9jRx(2 <0Y4^Cwc$H z2GR$h8O2?cHpeD2mUvsvBLI V~(*WE<>2r^8`_l9@Aj+fiF#a*Hm3c|W$9+ALH`JfnrM`q- zF-<=z)DDXd%|iL;3+5a&GBBsCm?D0Jgf0>2(&@qd`KV+W^Ok_nK7OQ6tovHyuiM}1 zsLEPS!P+)d7gNlG@!fd1)-BHV#$kErtfZpH-N=LoOVWjSa=73n8i`6OP3pL9v>J}r zLed^MWLd2c!^Ah*VSbzB!J2V_DL|Dj=08IRbImuM!E&bBg- 6E}Kp>#U&bd-_of#^=BsNRE@dGYYAfb_0y zZzEdT!&=j*5?~oz)^fA}p_i-|Ucu{KSaxC@3QmdEcZ$UQ!SnY0YjSUdGkW8dWS44~ z0-s;fT_&gvCsL_SjK4`t4E#V?SD{1uN;_(?WwJSP7Cw%Ip#qM#u@?TIh=SNcaFyo$ ze <(Y+VB?RI&U93A`w6 qywH z 2T65r*oKQ1|Xg9fR$)!CPAPwQ8KI#z+8gxM| ;o%vGMSgrQ!ZXe&GCXkG z{kz^P+79*-jj_D2NLtZnBeU+Xy(3`-8U7I2vcl2xz|#r;*G~m$nVl&^^RMNfR9%uX z!e}!tYW1YOe#Zof!}@lZH%#)qkYNq=B|_=;xRfRcEYp~ZKil#R>hF+@AT+Va_ uoqz;9MaH+x7Y5Oa#Ll L)|h4@ zHw4M!;iyHsYhk?$jV>|+wQ{EIhwrv3gh(C$ild_qei++R78+I9P9os) *{1PjPm lgarIdj`|jSc&?&V z3RpsO?|8Rq{OUcH&F4RG+7+Xpb&BM=W`;|b1|0pq((HE95VTlR6`w-%9wv;WP`VNA z5)`? m9X=x_T)*9UcHWx zmic@HR@=bFjH#=Rc^lgb`wKy!xDff0-Ac`1N9Dqgcm;YwJi>*!BV*`eQY|Od(ypyc zFOec!uIt^mqc57h)y>HSL*?kgJphc?l!`cu{cU}!-@UBR?#FTxHSKn7&&l$n!PW)= z|K`_+Y*y9c(ZhA<2@f=8dHsK2*eJ{3s-z=gYq_BpN0LF~tAIzu-p)n)Al{OgS*;6A zGh13|X-aCAgzH{=7K`jbo*u;lz586|a?hNP15qAF$c^?)MOrB39vIz67Z<{Dx0+*p zvy~vYKWFbu>M1?LyaVAluKiLu3Oui~bFXjN<{SNer97xBUfVX|!RnWz&-WN}Akp|K z!N2c!!FbPHC%~%xyfa$LF;b*)!UDkVXRv#%zmQy3yM3FJ>WiFWen9=-PfTR-fAoQ? z>=ar)q6-#i_Ft`9s#H{2sjW~ZblrPuKBBbI1FBDl62pS>e2*o5F89;HaY=K{2VHPV z*;cjrlT(iV%DMGh8<-ICpRQ7Gzf1%lt20L-n%5@gZw~>$hJ}LlHSf={AqGxC%zm+P zW%ZztB>elkmZu<+an3z01`t!A*tErKQIJm6H!v|%k!Uig`OAWKROq9rzp;vk%5OFG z8@@ESumaTeALDxj+3lUXZOdnTj%38mw~3>M9d^0L-(1%`$VaB9ftp`zaO31NoObKr zh+~G|nA~l|ftX~O?rNyJ2R+E+c0v>rN48a_+>5(g;=Hi-WT5Y!?wc0kY}JaQq`@V# zvDMf|t~}pwbx{GLK5W>ReGJ%_Jq*J>0A+#`{A0S(i)~T&!Ry$miM4NzqSFF%8->WC znq tu&Nj&WLO{=dc znnm*1e}`%vSyDaS{e;4DXa<>K1-n9vRKoAm<7N;0>Rj>NBQl;;maLBfkU}}tWs2&K z%G0AG7|&L=enkj FSi BqKNUGUIxtdsxE3L8UYx z9Kknr=>(SYB`nRuvO)G12o27T6PZ!8!(SAyvRR$F(p+-+zO3Bfh;2GKllNKinPHT# z84v=BVnM%lNXsXzDa1KbJaSoG2dPeW`M!}wztd1WigOSxO U0$~hu1N+vd2Ss!lWt+e0V?>vkUi^IS6G|o zBeL#ViqNLzTuHK%7;m0FMBP08I*xPKVi)sP&?;Z23;#G%7gP_ix2Zqp-(80+47Nph zT6#B@f;^X0cn{l2LuvxNyHOvXj-`0{7srW1gfIYn)U8eu;(J$3=@_z*Qs{^X`#|c_ zwdXggL(HXn*Cq%K`X9&WWa<+&@cY$+qwn3pcMX6C{3T1obZY%`{G+)>(xhX@s=>Dv zA99;Qyy=I-1yhX7tibOpuGptLMRH$b(3g`jJ`++VnIZ^^8~eIbJu-72wt!77b3R9U z`7?@Ck@(M$f+5TD4%|zo))^tpaMt|_K&8;g}VY3EyZLiA&*Eo zv3`7~hkO=&-lbZwxs<#ntK7~fhL8E5YZC+ Wq_<82@dln58tS<2z%-Qq9}3 zHyYGgZ@yJYm7O*o>Eu*KDe~l8Wnf{*w@619Lf2rbRPoYj*?9TXWo{P{w^h%)FDIc5 zy&OCgDznEbiCB@?U&8=P`^+GYw?&tVYn>y=8ouY|e^DH_#~W3n84VGPsO)s$#o`JF z0ocdW6odxVZT#} LDe`n^AUyfZSnJR2zLvQQn&)Sp8yTCtA{XbSsR` zSG#@ykz~T`@z`fP6v6l-wgFUdSKG38`}%ClVofw94cnqu>p9%D%>d6Z{)0`L4<*M| zZ33cf#Dn`6Ux!NXT92Nn6TXKI0qo_mr}Sid|0f;f`o$eGs`}_cOS@LO0` d*1aQM|z>@CmXRB6Gyel)zJh}-x8x2i9Ppo8Wi z)~4llulWsK?Q GKC}Rx!yVeh#j`{Edi^5_4 |PQQlb66QV7Fuo`Is@7>jpXO|+`Zxk$Rp5M;8I&;7E`|t6gJRH^M z5eoc0N1X)s?foLJj=HyI{AIvgB?}5T8k`xGLybHX+DLGn-c$M$bU*2KwaBTr*&J)h zm# |F``c>^2d6h=;_Lv0Ci}n|N-Oryku8HWh+aCHz9Rg@$N6~Gd_%Epw9F3*4 zoFFPSNk{Efn%$fl4rq~?p}OZf{=|p{(a)K4zI*eF4>*ujq=VL86wg-J-m$TiPR)0O z%Z*O|^|2Y!Ou7Gfa-e5^^=@`3L=R$A4V8E-bk&bDaAcVAxqSK!3t^3}`eQLZuS3cE zF3Wbs%%@2vY>?~jRu&U>4@tjvO78p5blKSIOnCq%2l2JY??qVMZ`L9wT!j_$40cz$ z{?0udrN~KT=tz@_S5+_!H0vF$<~f}g%-PMXb>JbL }P&UJJ@CZ+h8VE_7M*Hoo)sYHad}^&3*Dbc K*)=2nK9wSl5NX!X?eBQd@2aM zU63!ob6|nzmWxCZVE;9l8(%Rt@?1c4^NUkaKPQ}l1%5iXmT646mp$Fa5b|0E7IP9} z$~zUN!c%ar;P{zGl{h%L^Rws!=gpQ4+52K*xBcg1Z7R5wv-&o#iA>D|P;1<}YW4m7 z-={5OrtKPpzvRkCI2xE5f<-LU{7rgSu+}oGMF<)Ch}B8g_fbF;JO#&f+QLSg%QH XaqnUK? zuj_m=AEDOHWHmt@v*ipovT`6xBd!Cychdy7dHp=lrY5RqpWgX?_%lB^v7;(LZ#aX# zmUr7IK2s;CXU#KTmtkaghfd+{fu_>xv}FS?N5QF0S>k{=nU*!u#U@_SLZF={uD4Ry z&d>Clb@P-+wKCE=w~&N_=nNLt{&v3TO)~3>B7I;EDS?#;Y}tZdwZ>l}4ozc#9p$JP zb0;mZEveD~)SUia)*~2GqpI&fs{Lfd*(SWda@rATd1!p+ztd^3HZXIs#JV#|LIvlg zHW`-S>&4mps(E2ZUq(U#w-TWL1NmT;ibHrBhU=p#$1F*skn)$kP|n2Bw(LKEVf`xw z)r1+>Y-MBsZtsVREE0CYrJL^Zs>J8OPX`W*zXiYoi!Ix&V-vb}iNJrU%r_Y`5o7o! zxhEjMiu-0SQoFG*s%|Gn&Yx8HcE`MYE=lt2(Ey2`rijmJ&1d`uNEb<50i>9tU22m- z>XQ}4fyW~37>=dc5=e=2%Vs%x7@p*$1$!~Zw$V*3=){f+Tkg&5SJ5Oywdho=_ ?AG7T=c$liG_H;V|1n^O8gqrXvB>{vq`I^%S91EL0@WMWi8#O_>L|5 z$zF@VX&sqW?$%GtY1;|k+A&Lc#-feA<9zonETH;E--X@mHg|o%$xr`C*{t0LCSBXH zE^$|USzzF6HgV+~&bX{?QOo{udYtNL*VjKXnAEv^g{KkTDQY^6BCw{p@)b28vxL|V z@c5e`RSE8HWV*+9S|pdZ&L^5P8Z0^J=F_hFxP>9k=+md;k{~G&1IEB<<%ChUM|;z> zx$w~u3(5pnLZi6laF^{$#C5TfmJcudZeZa$ -nElhl_~O)<$*BM$hB+jKJ=Z-hQAw5!vUd341?y4Z=wA z3SV;)1kO+p|Ndi(k^l+1f^?#GCjiLdDl9!Q_bv6xD1!h2X;neLgbm#aPTj5+2zO)d zcfkbLgvE0_w?KilfAueW>VQ^;Dq%P6h+za%NrZSSIDZj^JJr;cv)UWtn6gk^Ap@Qh z)%so(vf~4>x;rK>>6f(F)43(rL|wCP=hqXWYpvS#doU&1w|3So))Ai zFAN-+3_rF#0jvyQGxkOC1yeG+I%Z0b$H^Ti$y?`B?CH9^!pQcIs+dup*&yFlo1{Uz zt@8;B6|yT0D-_UoyUg*gTS?}3jC6Z XmDKO6Q=c!H%cnURyIWo`r-~J$?wug*ylW9!=kA0u+TwbxXtZ%dZh-Qe3 zQ3VDtWGg?7NZ~@Ii=%L@-7J#F|FKyZkYd19lYW1llyDCJ *u3-{kVv338#)SA0^s-xN z^%O0^tDZU)s0WowP5%KSN9WkuiB*f4%tbgBck4_mLf{8Z>7k!FXc$X+SL7V;-dAm+ z-pK03I;5V%EXLa*v63SJR#DR>KC0C3=TY8Mm*ingxgkYX^yvqCy?&PJqY$XPTDvU? z=P}KK?jUyFH%bO$ ~Mc9Y{=t zyXxNAk;|y+Go!>7x^HvEfijwZLk-7qgS8*_&fn~ws{`~uCTyB=!6VxewX*@T%m_f+ z{o NP#J58R)r&JChrQCmqgmm~pVugaV7 I2`N#8Hf;z1ZGT9DUyd-j|4QkG$GBC7>0z0Aa$%Hng zc)F8XM$%f};f!8RONBe}JIMd$laI;AXyI!$Mya=K(hDBe#Sc1+KiJcUq<+9=9q2DN zM3W|!W)A&^7w<%Hl`O_@X?#Z6@Q5QjPaJhN8|72+`#Kn7ir nx_5Uw5kW=%aYZq!Zsr zg{CW07DO#=LvC)e$(ofF L&Siise}Agn!OXzs zB}Gy1z2AqoWcq|H$##j>4_Jo0@*IVLUHkz*^6A(-QuA-f#Mtnbg8g6OIbd`}yHVS+ zJuOikN?qoN+5=mvMyy~nNoDAo%xs+Edy}Qb(tDp8&*YI=UQwZ=WEZ0f^`42~U=-hY z$H;imbXbV=VdGsl@3AM<)OjJ`r@Qg$DTZbwy$=OFh#p^|r!eJC6vu}ykk3hllS)=$ zK7lb+P7yCk(bfgpB|TGI-CP6X_4#x{+0pj |HqVZ)(7#3`Cfu341|ee-DidNX}k0i`A|GpOe6 zQxegvTM|924rygC+uHJvw3>sRa{(spo`I{hS5^Zjl!8(2B4z$E*F*T3_AQn4OuTwB zXe*fdssB~QCQJ)JCbae_P%GQa0q0v9$R?K9Q6e-=9AB?>G9;#tExL*6 9CziPb#yY1%sDZ& zU@wIx5Cb^YDFNM9vuMBf$ca%=L?GNaXGO~1O2cBw0t_h!IGFxtC2K6gm&Y%*3%oAC zaVz&nm^X*YLzBWuXc^|IQTHpgpGY%EfK302%1MA jIC4iiGr z(gjbWZaA%_ibJ{9b*8bQv rQU z-fp76(B#Pc(r=*Se2Yp}Sdb&%@^(cfYXs{FaVe;Z&aL7g1a`WxZ#Au4J;Sa2#{56k zodsJI+|$NI8kUfdr9rw`T2fd+KuWq{S?PXYX+&yimXMb2?vj#5y1Q%X?vU62$9T_2 zI5XE=b7t<}eTK>B*Z_lqJq3`_h7qWDPKpKja-D;t-InkiIP*l*u%Hv3o?pyp8j-Ae z$>8w=1pj2{5KFi17WpvVn7ZM7fHP; #g*%2os9kAk)mc0wP zl(xD(ybHeqD9C(pU}w}I^$0K=8T0Ow>`+&HJ>{O8INxdhy )FGgeD( ^ zMs-pINt~14Z~E$yq?%ma)nQ;^u8CbLTr$Og?x%s-K%sAh%t-gH`DN)Xm*)mIM;I_5 zE7C=mjuAAqwJ90RmXe~MY9Qbh1~K`fj~#s8HA>ksX_0j@6k);Cpmp#rV38EPEMUq5 z-)A93F+V9kFP|nU%(9!=*P6m15C|lrhfdOgS1iS`U)%k5e?uO}x;);VJOB2e{!Qv( zUC8hnTv07_?t+&ln~BST|6}5A`;Q7eW67v(XXE&O%kw1i7K@}7ZJNd33BMzos49EE z^c~%CcY5hXc `GKO5YfjKNC;p}O*g+eIv?K@IRNeP0j>vi!J=2!@;tSY+mRoyhI2Iy j&laydp6on1C```bksrhbeBSqjMf6YVp;vd(tALL|OTl5vYdAv{DjCOOZCHF}-j;#MwzuCu>)RY9 HV_+lp}8mPK@=bv18`nLhAW;Eb(>ZMZK5(;_+ej$C%%$5QR =J|H=&_ViAG+awyUe7ECpnLo|L~8daZrWpIdM zBMfBH=OQ50=0Pr2yMH&6*BUT8wePE*T8&3l$EHP7_Z8>BBG;aVoJHOI2eEEhLFz^( z$7YhM2SIMVG5yo4AEqA`em?kYf4@t49y#e>nO);^-K|)LzD-;&r<;(O@S}RHt$=7? zALAtHF8^`FuP6|Dy;|2`-*%t!PTA;;=SHU}S!jo_goK6BF4&+~zo{cm=A@Q3q}H52 zd8gZ>AkBQcK#W!mq^SE 9LE3LBMtB?@o=d)2Pzl2N7*l@(^wXXq6m0d zt{<2g^XMNzb?rXyN>X}SJ#%(!#78%FzKycSi2iTI;eV^ySpc4FgAKy^?dRujrUq1( zwtwPD*U%Kkh)kB81A{T$Q1PG^xx$PeJ0W-+>f`n|Cy|ZL6;1v2G;}y01*WLTT&VC7 zI8q|+aRO6p)*Fi?zU%|u#TT%l52q3IkWHmKMii}*UxtyMkA?VU)bn2YxO;W3!0@L! z<@fgv#JnNO(7y#p+LbsbHIQMTdUMxMk1W5+p*xC_W9K17esWzxz*!P+e-h*m`Y0Z2 z;M)pkF6u?95W(?+d5p1IL-k_Wu&5<~!rPla%bZaQ?o%VtGXb~);1S%4I0fiMBXTQ- zSZZmmiktZ6edKnlw&1P# {WG3?3YI4U*lFE$Kg8 zMr43F+xsoj1vVOTWzD9Ji56`yyHI4;E6df(e+2ZPFqCa#@sH~nl?5D %oglDa4dxRmwjF;B^++ac>*TMf#V= znHc!dwk0o-&L?TmMY{3~L3}S;p(CTt0Q^3g;|z=b3vd}8U-Xh&CVh#i14&k+y?q9< zL?_7JOIEv1WjBApB06lzqN;=A*vwPe_>~P*ql(Y4%4}ItlDtC3Rx|ztSN0OVE-H6F z?L}&tU=(;BQFS!mYo;B7ujXdZLBd44TnQ3qr_4@V$g8EN6jP8R{*p`@2L6NEZ_O0i zxWqr{)ScKAZfdtOI(e>3YI(UOs4HZb7Iwo I9jr)jkewRo)^aN~bCWet){JVbagE~pExF~?!H9=fZ t1xe-eePZxGqe` zi>91!jikTSMhMP+4!OTDSlV`)z$eEp5BLS$-RqgoCNHj{o`=e5%W17)0O%JR8X`uB zXQ}M+fuJ@F_PIX2ZgKOy(Gb1-H!k5COfJ^15Gl4L$j&WB;gN3RH3tKc{@tn_n=*fI znUs2z_*YE^*r#8x{pFN(q0?84L>jceOk#g3K*8&}KZ)6wWBTnfgai(o%92IW+}^UM zNC(6X2`RM-$^Bl|Nzm@B8no4M>LPT)@TRRGWZWH$_z#gi3M}m3wS!q~xh+ZbjZ-zQ zCNpI$oNv)z&R2V(3$0Gpa89kga6nue;+jVso#H#|$soZ~tk)ywy%4o)=8^HHk%7Sd zBT(NZg%x4#p*lUk6=liqdaj#Ky49AWOm6{Tl0u>BTJR8hN rcJV7r7_R$IH|?3D@OT5*wVZquFDrnYTH#i+nrLBnn}Ch3_|vj z)}LzLrlne#k?LFHbqSrel1gJDxj@ufzx&_+>8wj=nH?l8VM*fv4mQkSFSCcBAPmh% zzzl~v5h!FCTAqyq!cE{zQBAjylwvZ07rB$}eF+I=ZNb;#*fiI( jOgW!aPn!F7M9x9`rG3X1N&LrGET5e_n93>*jB>xRsv+wos47Qr zF*yQYfr_T5SrTX*1KJoIfAWe+T?3}MYobs!^%7+2-77fO-Wx*-3|+$69pC)CT%2&o zv(5gMGy{oTy(;76IbB)qxK{sBmJEl rWqqFEVd2SD}An;nk(_M10pX^#>9?KnGU|Wn{_+*q#T%xcX`~D)^se{~I z38*<&CT<)*_CE-Ja`2OR70bk6u|QEc{eG2MiK0G+vPqaMb%>7=fuNWWrWdfDD9bLi zK L1#A`rb{oeFxwnJ9FQ >U)Qw&Ivi zMyYQ->!-oF*E3>b$$a(Xv9i$ac_%$_2re^9egt`K_!I``i_mE7`KHz_^ ` zv#%z$aCcn)M>ir8;M{PTxxWxP6Aycp`Zy$W5a8J&Gf#gc^zTynNWX^{117rqY f&&o&{jTyk$nyvQ^Uu!hQmNRTo)8~LB|lt(_s$x)es8l#hB32WlT&~N9$|k0 zs6aZuNEDbg-b~5N>fupFx(jI{z(Z=@cA9J>qXoypJf+icXvV+u+}9(3Dq~M*vX)y- zNQ32azn-`Y 3sI2cWA?B#DWdzdQ zNcWmLlmK}(NO?AU($Y`U%(X_GhL;+?(#EtwK^*tn1+^@ylq~-|uVJ4*lX*L;rV2rK zyuC2KuyDfHIl=NNboha!_NL4ourF|IG?;&+$1#4mEW~?wW-+SQ(x0vHUghlx<<3Mg zR$|)YOxu2AYn8I}kzxA0-;D>hg{jx7Fka7eW40;>$|SI}8onpEFs_>vZPi`(C~vt7 zEviki+CbrAnv5-~b8RvrHT>B)R i|rzSG#xm5-%(;3%AjJFO6J&fJW@IX}Ub#xmfD84aCDuD6^m zjD7#u2Wyf=IbL6E{i_7*_j`Yk|NFfN@k}U3R?g)Q+Nl#o95IFNu&*m0m`@1JKZ}%F zXk$o?_u(MfE&StH8M(ESC^)5*LVP!zd#rKoah>5_^7C^p=W_x?6nx;zJl>$11*e30 zr?-tWl+s@Ij9!t*WDMD2WW4~Mazs%{i394c2&COW7D6IZm|liT0ZVXsp5qFk@I}A1 z%~OJY$SFRF>T>Z!q=qy*mSd7?NNMYa6jYRW&4`m*V)aZ*9nQhUN2T~D@PkB)8k9pj zTDjd(ZuJ4jlBWgS{FfT9p~n1HDe~H($o_vczaYX0)n5HDKY)E~F=8gY+5J{h+4r)2 z*mu#puFG4{O$n49Ka;jH`zxP2Ib@dnraqU~%RD3KTs ogxCuTKf6Qe?{kcI}-RGXLR@wQ$S55 ziA#=&SnwW*QJ9uBEua6s2wzL~>7md|4?RJ?GNePmF7++mh>tosa$=8F?$k1HH`x2X z-*(Cb_EC~DeE9in^K#_YJ~lnAV-Meur)%NGf%nfM!p`Drv-w1rAHh+9n$B`|KZ#|m z?qH!F_VV(TjBwxU&~#rW(V*!FwzLVnAz}vmF6dc-0RBhHFJpW2GF}H)>OXuuUzRXV zgKxEGFb9_~j+5)ERQU6_tR=G8)EpB|u5O(m_@EOhX+!%C_JWhTzq z>clM?7!F!~!QbpAeGX{Q%lkBmUCx4+Rv6J@{;#BXoeNqOM{o)Fpf67l(!5 VmUXQP*WGvdr#KdI&3U@*<70IBMx>6NDF%FVGQ9K|(DbS#$9SXlg9kNTnyM zDLO$^ZtgwLdq`Lue8SFKob^q6wIteOswa@CSm#jAYgBI(q6F>)-5ku%9UV0 AGT?lSsE)@ijrtZEumD}4H_jB!>!MLM&u5w%>=Gu zqTS{-41%?uix;a)(?e%*(X-O4zS*a1MB5WDV>VS@4?%^#QVA Q~;Q1*XR`DiCyo zU4x`ea;o~N{`mshUN<9NWC1ai-HDai?GL;{253RNb+5nnd7PxJVonUnh*@qQHS(zU zIJ1VLQj>}I3(|=F6^_cqsdl8ns-lmv!uzuKhKVJkyC)%A6Io8MoqexwEiIU*Mu7X( zhE^BYm|-?L^(ZSksz5)*?)qWt6Oj%U{)>of;jLBAcK(Y12eC ztZc~DDBi6JcrySAxKv8lAD45P#VgGl99(42hV34uJr?@@nnQ0=EuPZeU-o5cE@pgM z<`9bDjCCdY 3Vq;YHUhaQFD!|BsnYTk*eW+D=vKvCnAD&RG{jILO59BQ=5gC}E zIS~#I5YFE&C5yRY9Pm^pikB9n8ds)qrisY+{y{#gl;qhTL)(^{_21Y7V26sb$TuSm zvqoDUf3}3i(6h@e;a-vw{&KBw!i;V; &T?5y@P4>8pS%`erGlxDjuwSoH<)5L5UyJbWOpRDz)d^R?`M9=VD^ zHLhmgn$PxiT}*;iN3Zr%64 U8ND~h5fWDBjqc+MZY|eORjvSt0{|FO?Wt}f$p6{K#St9YZ!JP=RBaM%y_g?lg z8Iv_Go05erdCs(Zj`Ov^7b>F DW~L1PAM$&ywBoMr&97zdhq!$W&fIL_WfZf zEErmn%g!@f+y|11XR$B)PL`My_H_sLC%~Y_Z_8K2@^Kyq@@8wjK#}4o`+!K35E|9F zV5}G76caBK;CI|kQ|k-^Te4HC)8CaMweixOg}#nyWQGW6ZSmsezQ$j)Wp#>XhItL} zcOF}F@R>EMN&m7Bs#+3{_MWTJs4B-8@<5{qAsl}!k&(b$8lj3xf=({PT-!>J3s*M8 zj9ZJuOq`cTJd|MSN9&xS$(vTbS6B%)Nu_Uang09YQdlRw0Hp`a{OF-$fPcDBjH z(nD={t1B$m=+;s~5Kdg!b)~;1PU#)W&}3hqWnCtMw4j>~18kqyamc=Sl9LLXzubQF zOXzlk*f`;OY-mW4=l$WoaH0zAd?f0A9*!u^y9Y-)F>B=?w!D<9|0Yb`q*0CSY&B&K z-=IQ@gcC|&I;1cCFa$3pRnzh#lTbDxx)0v&y+DTe;*UObSuSir#gdHn>qj#--`YG9 ztF80!UAbT55i_P*Fc@8QYslY=CIvo>dxJ%fx!xo}v4$Q$$EKV>SyMI`IjCHV#(M34 zW-=Jne!~UKY2A%O!t%&{Xi3IMcaz9iiF-!oMl!qp`<3$-?K%Ww;1RM=I8cf83z$ye z1i0<=Y$b#UC8%J6ISD~XKC(Z(uOA3Jd7h4YYz7MFxHgz-w$kJ$d(|35eyGUsSVvwF z*P(cbp6eE%Xa3ARffP>1hA+N5WRR1fE8FBZWRa!LpU=oyecvlH(Zs$=6?h6z_@eql zTt+lh0s*0M%M0~-_a{RshmsGF;V&N1@GwPY1S#yB#ftElYy3Nt<+laqQ*`{33!%-p z5Zr6v*>XnNab@4Hhc{o*(>0@KXN4s9js(R&4Ck&Jl8cK)5r`;6xX3giH(oWsYl3{q z&8 {6S(iwmMcNe#7|m5o!OR Tn#(WEM1cWqX`>hz*z1l+fMNC5~4T-)_ddxvK$wjr{WxZKvl8t)Uv zyE&=o$|`cbK6v89(}H)$c>C4 -W3E=D~p*E?xo%zda{Hj%1iEZ2z z=N_So-)W@@p#Fx8MQoDtyk6U2imj;9Pv~8*?FX!$bj^~N1;$)KoR~hm-YHdf)U(0| zXfo+vTstNs4ytCmIqM##)eh~o5w#7bU4MS$AzJkuC)yXiaoVD$)M!-A1~SkdPvgm? z&dJ>2Wmt`}cIZ4lSCtrlR)xXACOq+*ZR$Nd;a3@-@>hf6D!ZHLIN<1At*scP&BhU) z?AX0OP>;amDA8b u`a{1=O1m +w(Xwo*+a>o7cXXt;+n)soGy2~V|TD67!gT&yL07_Zr&QOkHI9G5#> zkbN^gU&ONf*%P%L#WlrWsdLsyQ;%v~mC@dg%&kcjb?7Vn=*j$q^(0G!ggicST+i|x z571jNgms(QMdLDy0+XMx-zy_ptgg1)(>f%+4t*G?u51_+op_Mop8iC*iYB`nBfb>B zkA^0Mv7hfd--o9VhurC_ QfWDfz4#7HHN zzjXvZrYz`Hh8st>kx__sTla;z13)p63j9wK!r~W(eKjn%;f34Be@_!SuKZTx-Mx7J zR9*gv+@M!-X}aMR7{x;anqAaW1)S=|b{tV9rXT8&4Tsz(hz6~9Nso{)4%+4dT4~?` zE35%y|FH&MHHZs($+jaA;w-9&Zw46=G+8_n0mwHw+^Au(888ZGdODnWh4p;X K`kR`kN GSmHKt-0j`|Ex&Mhf?W`H z$K-rg1vm(77F!94R`9a$we!Fk_`4?e1Utn=Li*&Cx&~qS{QUW!E2D6XKlUVLY^2HU zGc=Rp?=)YPUZhD~XBu0lw?D4kY3!^#z4tg6+Z#s$g`{=q&an6x%5*;k&e&0jE`|Mw z54kWyQr{oE+~l_7ReX=HbKh|mC R{@T|E!e9Sm#?T&3!ehiFVI0DQG*w%#@Csu7HniBZB`RMGb7yS&41x1H=n z#Sl~7#Ix8~U59+|6=y7&jhN~wnAb`8hvgFSLnpO~P3& KA7 z*)MOc!O{qBb~b &ZoI2rLgW3&rBNwA=z?aXiBUP+gnMjst` zG~%3}b|4AS{@yVm2y+sfU<4+e`W_y@l*DT)HiUouONV^x3-jEAk5^*u;s?nGO}Zq; zFqpr8WdG!;>xU |_Mb{P~Bfk#Rz@UczC0^}fmeDuF`VXwLX#)$1nlY#!`PB~=+u zW|ce ksWb$u z%<|P1a=w$Z HF>M@^) zVEJNA-2Vu&*%d?k?2}SV3(_K7t2*^=cKX`)9#>e#o;pYgjhL^=t}t>iyjsjt02sw6 zbwxc~aux)8E&2E2H1=wO8oQQZKQYDCjAE6>YY0Ut6_ XQ5bM4?)AT zSX%#{CMWUJ01@6SnuN*2)sKo5GQu2((vIwK8zs|3y0bR9c7tYpdDSNOTprI?1j$pV z)UN;CYu5ZIUHD9X#UTIAZ~gW~(nSKH0j*Fyx9T5SdvqUc>73#V&rIwFv2z^+Hkn-w zoK-mel;D3KK||)PdpVlPlqvX%hikhATd3$eEv|s)p$?<7Y(;wN!Rwl0!^7ao^yMVW z3-1dtsY{ktN&iC0XUoB}zPTY@)a={GIF-*SO1S%13J>{_%+Rp)SpDbef8jX}KIpB$ z-qum-k2`X4c=I(`#?ojW&>xZ;GC4;3+5IVT`S1OTzU(*hb%`>Kb;`Asq_(Syqa?(3 z%JWAIYzZ2$ikp#g;rAcJ9%5%*I9GF4sJq^$r4iqZi0-Y7&BLGW+;W*XrYdgK;Dry2 zrS5sc?JPVUw{W(y5TA!7$#(&w`{;-dgpS=Co^+2A-}GEU=@ ;*WSP40LN+d(3HhEDokD=N6ON4o8`A?EO{(H?&{gyZ0 z*S*P&y()AZYQfaF@0a>}>TM)?cSifNaWMh(dNp%6)Q<9;_OZans!J#NHBI1bYQ1)I z?G0Wa>_eOEUB5XCQw4R-ZFpeu#~l=S3Biusf2<~VEq)7!ws?1n1}J`eV=w6!mySE? zM;)vT`wH+4{VdwrQaAyx+nubUVIJ3I?5~+lq3%i&-mj9s9I!ku!1QnQQ9HOmScP$b z4ZXHf9;wIYm&7?JZu_J6i`@NT$Nkd9RV~k8CoA!{nQZ }z;<4h`FQd-~52-4BU-k-qjId4J#Wl yH4;jEyeD4Ti-Y# z=F*18Nt{6K^V)r!(xsXN=eNP4pp)g3p2-Ayc6?lA07vqd(x8t3%JoH&_U^^cW}f@` qw|~a|24zC8&i-E?U98v_TBBZtgRSxjw|~FIyi$-=`B))i8t{Mdo_HYu literal 0 HcmV?d00001 diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminLocalFile.PermissionSetExt.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminLocalFile.PermissionSetExt.al new file mode 100644 index 0000000000..28c946483c --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminLocalFile.PermissionSetExt.al @@ -0,0 +1,11 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +permissionsetextension 4820 "File Storage - Admin - Local File" extends "File Storage - Admin" +{ + IncludedPermissionSets = "Local File - Edit"; +} diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileEdit.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileEdit.PermissionSet.al new file mode 100644 index 0000000000..5023b099dd --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileEdit.PermissionSet.al @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +permissionset 4822 "Local File - Edit" +{ + Assignable = false; + Access = Public; + Caption = 'Local File - Edit'; + + IncludedPermissionSets = "Local File - Read"; + + Permissions = + tabledata "Local File Account" = imd; +} diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileObjects.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileObjects.PermissionSet.al new file mode 100644 index 0000000000..3857e7962c --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileObjects.PermissionSet.al @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +permissionset 4820 "Local File - Objects" +{ + Assignable = false; + Access = Public; + Caption = 'Local File - Objects'; + + Permissions = + table "Local File Account" = X, + codeunit "Local File Connector Impl." = X, + page "Local File Account Wizard" = X, + page "Local File Account" = X; +} diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileRead.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileRead.PermissionSet.al new file mode 100644 index 0000000000..3b79d19669 --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileRead.PermissionSet.al @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +permissionset 4821 "Local File - Read" +{ + Assignable = false; + Access = Public; + Caption = 'Local File - Read'; + + IncludedPermissionSets = "Local File - Objects"; + + Permissions = + tabledata "Local File Account" = r; +} diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Page.al b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Page.al new file mode 100644 index 0000000000..ebec8b0799 --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Page.al @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +/// +/// Displays an account that was registered via the File Share connector. +/// +page 4820 "Local File Account" +{ + ApplicationArea = All; + SourceTable = "Local File Account"; + Caption = 'Local File Account'; + Permissions = tabledata "Local File Account" = rimd; + PageType = Card; + Extensible = false; + InsertAllowed = false; + DataCaptionExpression = Rec.Name; + UsageCategory = None; + + layout + { + area(Content) + { + field(NameField; Rec.Name) + { + Caption = 'Account Name'; + ToolTip = 'Specifies the name of the storage account connection.'; + ShowMandatory = true; + NotBlank = true; + } + field(BasePath; Rec."Base Path") + { + ApplicationArea = All; + Caption = 'Base Path'; + ToolTip = 'Specifies the a base path of the account like D:\share\.'; + ShowMandatory = true; + NotBlank = true; + } + } + } + + trigger OnOpenPage() + begin + Rec.SetCurrentKey(Name); + end; +} \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Table.al b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Table.al new file mode 100644 index 0000000000..e4896e003f --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Table.al @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +///+/// Holds the information for all file accounts that are registered via the File Share connector +/// +table 4820 "Local File Account" +{ + Access = Internal; + DataClassification = CustomerContent; + Caption = 'Local File Account'; + + fields + { + field(1; "Id"; Guid) + { + DataClassification = SystemMetadata; + Caption = 'Primary Key'; + } + field(2; Name; Text[250]) + { + Caption = 'Name of account'; + } + field(3; "Base Path"; Text[2048]) + { + Caption = 'Base Path'; + + trigger OnValidate() + begin + if "Base Path" = '' then + exit; + + if not "Base Path".EndsWith('\') then + "Base Path" += '\'; + end; + } + } + + keys + { + key(PK; Id) + { + Clustered = true; + } + } +} \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccountWizard.Page.al b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccountWizard.Page.al new file mode 100644 index 0000000000..fa1ca2db96 --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccountWizard.Page.al @@ -0,0 +1,134 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +Using System.Environment; + +///+/// Displays an account that is being registered via the File Share connector. +/// +page 4821 "Local File Account Wizard" +{ + Caption = 'Setup Local File Account'; + SourceTable = "Local File Account"; + SourceTableTemporary = true; + Permissions = tabledata "Local File Account" = rimd; + PageType = NavigatePage; + Extensible = false; + Editable = true; + + layout + { + area(Content) + { + group(TopBanner) + { + Editable = false; + ShowCaption = false; + Visible = TopBannerVisible; + field(NotDoneIcon; MediaResources."Media Reference") + { + ApplicationArea = All; + Editable = false; + ShowCaption = false; + ToolTip = ' '; + Caption = ' '; + } + } + + field(NameField; Rec.Name) + { + ApplicationArea = All; + Caption = 'Account Name'; + ToolTip = 'Specifies the name of the Azure File Share account.'; + ShowMandatory = true; + NotBlank = true; + + trigger OnValidate() + begin + IsNextEnabled := FileShareConnectorImpl.IsAccountValid(Rec); + end; + } + field(BasePath; Rec."Base Path") + { + ApplicationArea = All; + Caption = 'Base Path'; + ToolTip = 'Specifies the a base path of the account like D:\share\.'; + ShowMandatory = true; + NotBlank = true; + + trigger OnValidate() + begin + IsNextEnabled := FileShareConnectorImpl.IsAccountValid(Rec); + end; + } + } + } + + actions + { + area(processing) + { + action(Back) + { + ApplicationArea = All; + Caption = 'Back'; + ToolTip = 'Back'; + Image = Cancel; + InFooterBar = true; + + trigger OnAction() + begin + CurrPage.Close(); + end; + } + + action(Next) + { + ApplicationArea = All; + Caption = 'Next'; + Image = NextRecord; + Enabled = IsNextEnabled; + InFooterBar = true; + ToolTip = 'Next'; + + trigger OnAction() + begin + FileShareConnectorImpl.CreateAccount(Rec, LocalFileAccount); + CurrPage.Close(); + end; + } + } + } + + var + LocalFileAccount: Record "File Account"; + MediaResources: Record "Media Resources"; + FileShareConnectorImpl: Codeunit "Local File Connector Impl."; + IsNextEnabled: Boolean; + TopBannerVisible: Boolean; + + trigger OnOpenPage() + var + AssistedSetupLogoTok: Label 'ASSISTEDSETUP-NOTEXT-400PX.PNG', Locked = true; + begin + Rec.Init(); + Rec.Insert(); + + if MediaResources.Get(AssistedSetupLogoTok) and (CurrentClientType() = ClientType::Web) then + TopBannerVisible := MediaResources."Media Reference".HasValue(); + end; + + internal procedure GetAccount(var FileAccount: Record "File Account"): Boolean + begin + if IsNullGuid(LocalFileAccount."Account Id") then + exit(false); + + FileAccount := LocalFileAccount; + + exit(true); + end; +} \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnector.EnumExt.al b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnector.EnumExt.al new file mode 100644 index 0000000000..5f0c2297ce --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnector.EnumExt.al @@ -0,0 +1,21 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +///+/// Enum extension to register the File Share connector. +/// +enumextension 4820 "Local File Connector" extends "Ext. File Storage Connector" +{ + ///+ /// The File Share connector. + /// + value(4820; "Local File") + { + Caption = 'Local File'; + Implementation = "External File Storage Connector" = "Local File Connector Impl."; + } +} \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnectorImpl.Codeunit.al b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnectorImpl.Codeunit.al new file mode 100644 index 0000000000..ff880ee5ad --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnectorImpl.Codeunit.al @@ -0,0 +1,401 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +using System.Text; +using System.Utilities; +using System.Azure.Storage; +using System.Azure.Storage.Files; +using System.IO; +using System; + +codeunit 4820 "Local File Connector Impl." implements "External File Storage Connector" +{ + Access = Internal; + Permissions = tabledata "Local File Account" = rimd; + + var + ConnectorDescriptionTxt: Label 'Use Local File to store and retrieve files from the server file system.'; + NotRegisteredAccountErr: Label 'We could not find the account. Typically, this is because the account has been deleted.'; + + ///+ /// Gets a List of Files stored on the provided account. + /// + /// The file account ID which is used to get the file. + /// The file path to list. + /// Defines the pagination data. + /// A list with all files stored in the path. + procedure ListFiles(AccountId: Guid; Path: Text; FilePaginationData: Codeunit "File Pagination Data"; var FileAccountContent: Record "File Account Content" temporary) + var + LocalFileAccount: Record "Local File Account"; + LocalFile: Record File; + begin + LocalFileAccount.Get(AccountId); + FilePaginationData.SetEndOfListing(true); + SetLocalFileFilters(AccountId, Path, LocalFile, true); + if not LocalFile.FindSet() then + exit; + + repeat + FileAccountContent.Init(); + FileAccountContent.Name := LocalFile.Name; + FileAccountContent.Type := FileAccountContent.Type::"File"; + FileAccountContent."Parent Directory" := CopyStr(GetParentPath(LocalFileAccount, LocalFile.Path), 1, MaxStrLen(FileAccountContent."Parent Directory")); + FileAccountContent.Insert(); + until LocalFile.Next() = 0; + end; + + ///+ /// Gets a file from the provided account. + /// + /// The file account ID which is used to get the file. + /// The file path inside the file account. + /// The Stream were the file is read to. + procedure GetFile(AccountId: Guid; Path: Text; Stream: InStream) + var + LocalFile: File; + LocalPath: Text; + TempBlobOutStream: OutStream; + LocalFileInStream: InStream; + begin + Clear(TempBlob); + LocalPath := GetLocalPath(AccountId, Path); + LocalFile.Open(LocalPath); + LocalFile.CreateInStream(LocalFileInStream); + TempBlob.CreateOutStream(TempBlobOutStream); + CopyStream(TempBlobOutStream, LocalFileInStream); + LocalFile.Close(); + + TempBlob.CreateInStream(Stream); + end; + + ///+ /// Create a file in the provided account. + /// + /// The file account ID which is used to send out the file. + /// The file path inside the file account. + /// The Stream were the file is read from. + procedure CreateFile(AccountId: Guid; Path: Text; Stream: InStream) + var + LocalFile: File; + LocalPath: Text; + LocalFileStream: OutStream; + begin + LocalPath := GetLocalPath(AccountId, Path); + LocalFile.Create(LocalPath); + LocalFile.CreateOutStream(LocalFileStream); + CopyStream(LocalFileStream, Stream); + end; + + ///+ /// Copies as file inside the provided account. + /// + /// The file account ID which is used to send out the file. + /// The source file path. + /// The target file path. + procedure CopyFile(AccountId: Guid; SourcePath: Text; TargetPath: Text) + var + LocalSourcePath, LocalTargetPath : Text; + begin + LocalSourcePath := GetLocalPath(AccountId, SourcePath); + LocalTargetPath := GetLocalPath(AccountId, TargetPath); + + File.Copy(LocalSourcePath, LocalTargetPath); + end; + + ///+ /// Move as file inside the provided account. + /// + /// The file account ID which is used to send out the file. + /// The source file path. + /// The target file path. + procedure MoveFile(AccountId: Guid; SourcePath: Text; TargetPath: Text) + var + LocalSourcePath, LocalTargetPath : Text; + begin + LocalSourcePath := GetLocalPath(AccountId, SourcePath); + LocalTargetPath := GetLocalPath(AccountId, TargetPath); + + File.Rename(LocalSourcePath, LocalTargetPath); + end; + + ///+ /// Checks if a file exists on the provided account. + /// + /// The file account ID which is used to send out the file. + /// The file path inside the file account. + ///Returns true if the file exists + procedure FileExists(AccountId: Guid; Path: Text): Boolean + var + LocalPath: Text; + begin + LocalPath := GetLocalPath(AccountId, Path); + + exit(File.Exists(LocalPath)); + end; + + ///+ /// Deletes a file exists on the provided account. + /// + /// The file account ID which is used to send out the file. + /// The file path inside the file account. + procedure DeleteFile(AccountId: Guid; Path: Text) + var + LocalPath: Text; + begin + LocalPath := GetLocalPath(AccountId, Path); + + File.Erase(LocalPath); + end; + + ///+ /// Gets a List of Directories stored on the provided account. + /// + /// The file account ID which is used to get the file. + /// The file path to list. + /// Defines the pagination data. + /// A list with all directories stored in the path. + procedure ListDirectories(AccountId: Guid; Path: Text; FilePaginationData: Codeunit "File Pagination Data"; var FileAccountContent: Record "File Account Content" temporary) + var + LocalFileAccount: Record "Local File Account"; + LocalFile: Record File; + begin + FilePaginationData.SetEndOfListing(true); + LocalFileAccount.Get(AccountId); + SetLocalFileFilters(AccountId, Path, LocalFile, false); + if not LocalFile.FindSet() then + exit; + + repeat + FileAccountContent.Init(); + FileAccountContent.Name := LocalFile.Name; + FileAccountContent.Type := FileAccountContent.Type::Directory; + FileAccountContent."Parent Directory" := CopyStr(GetParentPath(LocalFileAccount, LocalFile.Path), 1, MaxStrLen(FileAccountContent."Parent Directory")); + if not (LocalFile.Name in ['..', '.']) then + FileAccountContent.Insert(); + until LocalFile.Next() = 0; + end; + + ///+ /// Creates a directory on the provided account. + /// + /// The file account ID which is used to send out the file. + /// The directory path inside the file account. + procedure CreateDirectory(AccountId: Guid; Path: Text) + var + LocalPath: Text; + begin + LocalPath := GetLocalPath(AccountId, Path); + ServerDirectoryHelper.CreateDirectory(LocalPath); + end; + + ///+ /// Checks if a directory exists on the provided account. + /// + /// The file account ID which is used to send out the file. + /// The directory path inside the file account. + ///Returns true if the directory exists + procedure DirectoryExists(AccountId: Guid; Path: Text): Boolean + var + LocalPath: Text; + begin + if Path = '' then + exit(true); + + LocalPath := GetLocalPath(AccountId, Path); + exit(ServerDirectoryHelper.Exists(LocalPath)); + end; + + ///+ /// Deletes a directory exists on the provided account. + /// + /// The file account ID which is used to send out the file. + /// The directory path inside the file account. + procedure DeleteDirectory(AccountId: Guid; Path: Text) + var + LocalPath: Text; + begin + if Path = '' then + exit; + + LocalPath := GetLocalPath(AccountId, Path); + ServerDirectoryHelper.Delete(LocalPath); + end; + + ///+ /// Gets the registered accounts for the File Share connector. + /// + /// Out parameter holding all the registered accounts for the File Share connector. + procedure GetAccounts(var TempAccounts: Record "File Account" temporary) + var + Account: Record "Local File Account"; + begin + if not Account.FindSet() then + exit; + + repeat + TempAccounts."Account Id" := Account.Id; + TempAccounts.Name := Account.Name; + TempAccounts.Connector := Enum::"Ext. File Storage Connector"::"Local File"; + TempAccounts.Insert(); + until Account.Next() = 0; + end; + + ///+ /// Shows accounts information. + /// + /// The ID of the account to show. + procedure ShowAccountInformation(AccountId: Guid) + var + FileShareAccountLocal: Record "Local File Account"; + begin + if not FileShareAccountLocal.Get(AccountId) then + Error(NotRegisteredAccountErr); + + FileShareAccountLocal.SetRecFilter(); + Page.Run(Page::"Local File Account", FileShareAccountLocal); + end; + + ///+ /// Register an file account for the File Share connector. + /// + /// Out parameter holding details of the registered account. + ///True if the registration was successful; false - otherwise. + procedure RegisterAccount(var TempAccount: Record "File Account" temporary): Boolean + var + FileShareAccountWizard: Page "Local File Account Wizard"; + begin + FileShareAccountWizard.RunModal(); + + exit(FileShareAccountWizard.GetAccount(TempAccount)); + end; + + ///+ /// Deletes an file account for the File Share connector. + /// + /// The ID of the File Share account + ///True if an account was deleted. + procedure DeleteAccount(AccountId: Guid): Boolean + var + FileShareAccountLocal: Record "Local File Account"; + begin + if FileShareAccountLocal.Get(AccountId) then + exit(FileShareAccountLocal.Delete()); + + exit(false); + end; + + ///+ /// Gets a description of the File Share connector. + /// + ///A short description of the File Share connector. + procedure GetDescription(): Text[250] + begin + exit(ConnectorDescriptionTxt); + end; + + ///+ /// Gets the File Share connector logo. + /// + ///A base64-formatted image to be used as logo. + procedure GetLogoAsBase64(): Text + var + Base64Convert: Codeunit "Base64 Convert"; + Stream: InStream; + begin + NavApp.GetResource('connector-logo.png', Stream); + exit(Base64Convert.ToBase64(Stream)); + end; + + internal procedure IsAccountValid(var Account: Record "Local File Account" temporary): Boolean + begin + if Account.Name = '' then + exit(false); + + if Account."Base Path" = '' then + exit(false); + + exit(true); + end; + + [NonDebuggable] + internal procedure CreateAccount(var AccountToCopy: Record "Local File Account"; var FileAccount: Record "File Account") + var + NewFileShareAccount: Record "Local File Account"; + begin + NewFileShareAccount.TransferFields(AccountToCopy); + NewFileShareAccount.Id := CreateGuid(); + NewFileShareAccount.Insert(); + + FileAccount."Account Id" := NewFileShareAccount.Id; + FileAccount.Name := NewFileShareAccount.Name; + FileAccount.Connector := Enum::"Ext. File Storage Connector"::"Local File"; + end; + + local procedure CheckPath(var Path: Text) + begin + if (Path <> '') and not Path.EndsWith(PathSeparator()) then + Path += PathSeparator(); + end; + + local procedure SetLocalFileFilters(var AccountId: Guid; var Path: Text; var LocalFile: Record File; Files: Boolean) + var + LocalPath: Text; + begin + CheckPath(Path); + LocalPath := GetLocalPath(AccountId, Path); + + LocalFile.SetRange(Path, LocalPath); + LocalFile.SetRange("Is a file", Files); + end; + + local procedure PathSeparator(): Text + begin + exit('/'); + end; + + local procedure GetLocalPath(AccountId: Guid; Path: Text) LocalPath: Text + var + LocalFileAccount: Record "Local File Account"; + begin + LocalFileAccount.Get(AccountId); + LocalFileAccount.TestField("Base Path"); + + LocalPath := LocalFileAccount."Base Path"; + if not LocalPath.EndsWith('\') then + LocalPath += '\'; + + LocalPath += Path; + + exit(ConvertPath(LocalPath)); + end; + + local procedure ConvertPath(Path: Text): Text + begin + exit(Path.Replace(PathSeparator(), '\')); + end; + + local procedure GetParentPath(LocalFileAccount: Record "Local File Account"; Path: Text): Text + var + LocalPath: Text; + begin + LocalPath := Path; + if Path.StartsWith(LocalFileAccount."Base Path".ToUpper()) then + LocalPath := Path.Substring(StrLen(LocalFileAccount."Base Path")); + + if LocalPath.StartsWith('\') then + LocalPath := LocalPath.Substring(2); + + if LocalPath = '\' then + LocalPath := ''; + + exit(LocalPath.Replace('\', PathSeparator())); + end; + + var + TempBlob: Codeunit "Temp Blob"; + ServerDirectoryHelper: DotNet Directory; +} \ No newline at end of file From 7c03005052e44e94859ae72ee1743bd4aa4f74d2 Mon Sep 17 00:00:00 2001 From: Thomas WilliamsonDate: Thu, 9 Jan 2025 13:17:25 +0100 Subject: [PATCH 2/4] Rename Local File Connector components to Ext. Local File Connector and update permissions accordingly --- .../ExtLocalFileConnector.Entitlement.al} | 8 +++-- ...t.al => ExtLocalFileEdit.PermissionSet.al} | 6 ++-- ...l => ExtLocalFileObjects.PermissionSet.al} | 9 +++--- ...t.al => ExtLocalFileRead.PermissionSet.al} | 6 ++-- ...orageAdminExtLocalFile.PermissionSetExt.al | 11 +++++++ ...nt.Page.al => ExtLocalFileAccount.Page.al} | 6 ++-- ....Table.al => ExtLocalFileAccount.Table.al} | 2 +- ...e.al => ExtLocalFileAccountWizard.Page.al} | 8 ++--- ...xt.al => ExtLocalFileConnector.EnumExt.al} | 4 +-- ... => ExtLocalFileConnectorImpl.Codeunit.al} | 32 +++++++++---------- 10 files changed, 52 insertions(+), 40 deletions(-) rename Apps/W1/External File Storage - Local File Connector/app/{permissions/FileStorageAdminLocalFile.PermissionSetExt.al => Entitlements/ExtLocalFileConnector.Entitlement.al} (73%) rename Apps/W1/External File Storage - Local File Connector/app/permissions/{LocalFileEdit.PermissionSet.al => ExtLocalFileEdit.PermissionSet.al} (76%) rename Apps/W1/External File Storage - Local File Connector/app/permissions/{LocalFileObjects.PermissionSet.al => ExtLocalFileObjects.PermissionSet.al} (69%) rename Apps/W1/External File Storage - Local File Connector/app/permissions/{LocalFileRead.PermissionSet.al => ExtLocalFileRead.PermissionSet.al} (76%) create mode 100644 Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminExtLocalFile.PermissionSetExt.al rename Apps/W1/External File Storage - Local File Connector/app/src/{LocalFileAccount.Page.al => ExtLocalFileAccount.Page.al} (90%) rename Apps/W1/External File Storage - Local File Connector/app/src/{LocalFileAccount.Table.al => ExtLocalFileAccount.Table.al} (97%) rename Apps/W1/External File Storage - Local File Connector/app/src/{LocalFileAccountWizard.Page.al => ExtLocalFileAccountWizard.Page.al} (94%) rename Apps/W1/External File Storage - Local File Connector/app/src/{LocalFileConnector.EnumExt.al => ExtLocalFileConnector.EnumExt.al} (77%) rename Apps/W1/External File Storage - Local File Connector/app/src/{LocalFileConnectorImpl.Codeunit.al => ExtLocalFileConnectorImpl.Codeunit.al} (92%) diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminLocalFile.PermissionSetExt.al b/Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al similarity index 73% rename from Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminLocalFile.PermissionSetExt.al rename to Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al index 28c946483c..efe285d6ff 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminLocalFile.PermissionSetExt.al +++ b/Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al @@ -5,7 +5,9 @@ namespace System.ExternalFileStorage; -permissionsetextension 4820 "File Storage - Admin - Local File" extends "File Storage - Admin" +entitlement "Ext. Local File Connector" { - IncludedPermissionSets = "Local File - Edit"; -} + Type = Implicit; + + ObjectEntitlements = "Ext. Local File - Objects"; +} \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileEdit.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al similarity index 76% rename from Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileEdit.PermissionSet.al rename to Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al index 5023b099dd..6c8a692665 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileEdit.PermissionSet.al +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al @@ -5,14 +5,14 @@ namespace System.ExternalFileStorage; -permissionset 4822 "Local File - Edit" +permissionset 4822 "Ext. Local File - Edit" { Assignable = false; Access = Public; Caption = 'Local File - Edit'; - IncludedPermissionSets = "Local File - Read"; + IncludedPermissionSets = "Ext. Local File - Read"; Permissions = - tabledata "Local File Account" = imd; + tabledata "Ext. Local File Account" = imd; } diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileObjects.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al similarity index 69% rename from Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileObjects.PermissionSet.al rename to Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al index 3857e7962c..f426c73464 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileObjects.PermissionSet.al +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al @@ -5,15 +5,14 @@ namespace System.ExternalFileStorage; -permissionset 4820 "Local File - Objects" +permissionset 4820 "Ext. Local File - Objects" { Assignable = false; Access = Public; Caption = 'Local File - Objects'; Permissions = - table "Local File Account" = X, - codeunit "Local File Connector Impl." = X, - page "Local File Account Wizard" = X, - page "Local File Account" = X; + table "Ext. Local File Account" = X, + page "Ext. Local File Account Wizard" = X, + page "Ext. Local File Account" = X; } diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileRead.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al similarity index 76% rename from Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileRead.PermissionSet.al rename to Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al index 3b79d19669..320b0dae00 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/LocalFileRead.PermissionSet.al +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al @@ -5,14 +5,14 @@ namespace System.ExternalFileStorage; -permissionset 4821 "Local File - Read" +permissionset 4821 "Ext. Local File - Read" { Assignable = false; Access = Public; Caption = 'Local File - Read'; - IncludedPermissionSets = "Local File - Objects"; + IncludedPermissionSets = "Ext. Local File - Objects"; Permissions = - tabledata "Local File Account" = r; + tabledata "Ext. Local File Account" = r; } diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminExtLocalFile.PermissionSetExt.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminExtLocalFile.PermissionSetExt.al new file mode 100644 index 0000000000..87eea5344c --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageAdminExtLocalFile.PermissionSetExt.al @@ -0,0 +1,11 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +permissionsetextension 4820 "File Storage - Admin - Ext. Local File" extends "File Storage - Admin" +{ + IncludedPermissionSets = "Ext. Local File - Edit"; +} diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Page.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al similarity index 90% rename from Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Page.al rename to Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al index ebec8b0799..3aac0d3cff 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Page.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al @@ -8,12 +8,12 @@ namespace System.ExternalFileStorage; /// /// Displays an account that was registered via the File Share connector. /// -page 4820 "Local File Account" +page 4820 "Ext. Local File Account" { ApplicationArea = All; - SourceTable = "Local File Account"; + SourceTable = "Ext. Local File Account"; Caption = 'Local File Account'; - Permissions = tabledata "Local File Account" = rimd; + Permissions = tabledata "Ext. Local File Account" = rimd; PageType = Card; Extensible = false; InsertAllowed = false; diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Table.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al similarity index 97% rename from Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Table.al rename to Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al index e4896e003f..76e7e7df16 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccount.Table.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al @@ -8,7 +8,7 @@ namespace System.ExternalFileStorage; ////// Holds the information for all file accounts that are registered via the File Share connector /// -table 4820 "Local File Account" +table 4820 "Ext. Local File Account" { Access = Internal; DataClassification = CustomerContent; diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccountWizard.Page.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al similarity index 94% rename from Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccountWizard.Page.al rename to Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al index fa1ca2db96..a30b9b981a 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileAccountWizard.Page.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al @@ -10,12 +10,12 @@ Using System.Environment; ////// Displays an account that is being registered via the File Share connector. /// -page 4821 "Local File Account Wizard" +page 4821 "Ext. Local File Account Wizard" { Caption = 'Setup Local File Account'; - SourceTable = "Local File Account"; + SourceTable = "Ext. Local File Account"; SourceTableTemporary = true; - Permissions = tabledata "Local File Account" = rimd; + Permissions = tabledata "Ext. Local File Account" = rimd; PageType = NavigatePage; Extensible = false; Editable = true; @@ -107,7 +107,7 @@ page 4821 "Local File Account Wizard" var LocalFileAccount: Record "File Account"; MediaResources: Record "Media Resources"; - FileShareConnectorImpl: Codeunit "Local File Connector Impl."; + FileShareConnectorImpl: Codeunit "Ext. Local File Connector Impl"; IsNextEnabled: Boolean; TopBannerVisible: Boolean; diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnector.EnumExt.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al similarity index 77% rename from Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnector.EnumExt.al rename to Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al index 5f0c2297ce..36d8db222e 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnector.EnumExt.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al @@ -8,7 +8,7 @@ namespace System.ExternalFileStorage; ////// Enum extension to register the File Share connector. /// -enumextension 4820 "Local File Connector" extends "Ext. File Storage Connector" +enumextension 4820 "Ext. Local File Connector" extends "Ext. File Storage Connector" { ////// The File Share connector. @@ -16,6 +16,6 @@ enumextension 4820 "Local File Connector" extends "Ext. File Storage Connector" value(4820; "Local File") { Caption = 'Local File'; - Implementation = "External File Storage Connector" = "Local File Connector Impl."; + Implementation = "External File Storage Connector" = "Ext. Local File Connector Impl"; } } \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnectorImpl.Codeunit.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al similarity index 92% rename from Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnectorImpl.Codeunit.al rename to Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al index ff880ee5ad..b7d286753f 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/LocalFileConnectorImpl.Codeunit.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al @@ -7,15 +7,15 @@ namespace System.ExternalFileStorage; using System.Text; using System.Utilities; -using System.Azure.Storage; -using System.Azure.Storage.Files; using System.IO; using System; -codeunit 4820 "Local File Connector Impl." implements "External File Storage Connector" +codeunit 4820 "Ext. Local File Connector Impl" implements "External File Storage Connector" { Access = Internal; - Permissions = tabledata "Local File Account" = rimd; + Permissions = tabledata "Ext. Local File Account" = rimd; + InherentPermissions = X; + InherentEntitlements = X; var ConnectorDescriptionTxt: Label 'Use Local File to store and retrieve files from the server file system.'; @@ -30,7 +30,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con /// A list with all files stored in the path. procedure ListFiles(AccountId: Guid; Path: Text; FilePaginationData: Codeunit "File Pagination Data"; var FileAccountContent: Record "File Account Content" temporary) var - LocalFileAccount: Record "Local File Account"; + LocalFileAccount: Record "Ext. Local File Account"; LocalFile: Record File; begin LocalFileAccount.Get(AccountId); @@ -160,7 +160,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con /// A list with all directories stored in the path. procedure ListDirectories(AccountId: Guid; Path: Text; FilePaginationData: Codeunit "File Pagination Data"; var FileAccountContent: Record "File Account Content" temporary) var - LocalFileAccount: Record "Local File Account"; + LocalFileAccount: Record "Ext. Local File Account"; LocalFile: Record File; begin FilePaginationData.SetEndOfListing(true); @@ -231,7 +231,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con /// Out parameter holding all the registered accounts for the File Share connector. procedure GetAccounts(var TempAccounts: Record "File Account" temporary) var - Account: Record "Local File Account"; + Account: Record "Ext. Local File Account"; begin if not Account.FindSet() then exit; @@ -250,13 +250,13 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con /// The ID of the account to show. procedure ShowAccountInformation(AccountId: Guid) var - FileShareAccountLocal: Record "Local File Account"; + FileShareAccountLocal: Record "Ext. Local File Account"; begin if not FileShareAccountLocal.Get(AccountId) then Error(NotRegisteredAccountErr); FileShareAccountLocal.SetRecFilter(); - Page.Run(Page::"Local File Account", FileShareAccountLocal); + Page.Run(Page::"Ext. Local File Account", FileShareAccountLocal); end; /// @@ -266,7 +266,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con /// True if the registration was successful; false - otherwise. procedure RegisterAccount(var TempAccount: Record "File Account" temporary): Boolean var - FileShareAccountWizard: Page "Local File Account Wizard"; + FileShareAccountWizard: Page "Ext. Local File Account Wizard"; begin FileShareAccountWizard.RunModal(); @@ -280,7 +280,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con ///True if an account was deleted. procedure DeleteAccount(AccountId: Guid): Boolean var - FileShareAccountLocal: Record "Local File Account"; + FileShareAccountLocal: Record "Ext. Local File Account"; begin if FileShareAccountLocal.Get(AccountId) then exit(FileShareAccountLocal.Delete()); @@ -310,7 +310,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con exit(Base64Convert.ToBase64(Stream)); end; - internal procedure IsAccountValid(var Account: Record "Local File Account" temporary): Boolean + internal procedure IsAccountValid(var Account: Record "Ext. Local File Account" temporary): Boolean begin if Account.Name = '' then exit(false); @@ -322,9 +322,9 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con end; [NonDebuggable] - internal procedure CreateAccount(var AccountToCopy: Record "Local File Account"; var FileAccount: Record "File Account") + internal procedure CreateAccount(var AccountToCopy: Record "Ext. Local File Account"; var FileAccount: Record "File Account") var - NewFileShareAccount: Record "Local File Account"; + NewFileShareAccount: Record "Ext. Local File Account"; begin NewFileShareAccount.TransferFields(AccountToCopy); NewFileShareAccount.Id := CreateGuid(); @@ -359,7 +359,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con local procedure GetLocalPath(AccountId: Guid; Path: Text) LocalPath: Text var - LocalFileAccount: Record "Local File Account"; + LocalFileAccount: Record "Ext. Local File Account"; begin LocalFileAccount.Get(AccountId); LocalFileAccount.TestField("Base Path"); @@ -378,7 +378,7 @@ codeunit 4820 "Local File Connector Impl." implements "External File Storage Con exit(Path.Replace(PathSeparator(), '\')); end; - local procedure GetParentPath(LocalFileAccount: Record "Local File Account"; Path: Text): Text + local procedure GetParentPath(LocalFileAccount: Record "Ext. Local File Account"; Path: Text): Text var LocalPath: Text; begin From fc24c3f8506da2c201dea093a5a965426a4a47d9 Mon Sep 17 00:00:00 2001 From: Thomas WilliamsonDate: Fri, 17 Jan 2025 09:57:24 +0100 Subject: [PATCH 3/4] Refactor Local File Connector entitlements and permissions; update documentation and fix tooltip messages --- .../ExtLocalFileConnector.Entitlement.al | 2 +- .../ExtLocalFileEdit.PermissionSet.al | 2 +- .../ExtLocalFileObjects.PermissionSet.al | 2 +- .../ExtLocalFileRead.PermissionSet.al | 2 +- ...torageEditExtLocalFile.PermissionSetExt.al | 11 +++++ .../app/src/ExtLocalFileAccount.Page.al | 18 ++++----- .../app/src/ExtLocalFileAccount.Table.al | 8 ++-- .../app/src/ExtLocalFileAccountWizard.Page.al | 31 ++++++-------- .../app/src/ExtLocalFileConnector.EnumExt.al | 4 +- .../src/ExtLocalFileConnectorImpl.Codeunit.al | 40 +++++++++---------- 10 files changed, 63 insertions(+), 57 deletions(-) create mode 100644 Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageEditExtLocalFile.PermissionSetExt.al diff --git a/Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al b/Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al index efe285d6ff..6985ad0afe 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al +++ b/Apps/W1/External File Storage - Local File Connector/app/Entitlements/ExtLocalFileConnector.Entitlement.al @@ -7,7 +7,7 @@ namespace System.ExternalFileStorage; entitlement "Ext. Local File Connector" { - Type = Implicit; ObjectEntitlements = "Ext. Local File - Objects"; + Type = Implicit; } \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al index 6c8a692665..182988d19a 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileEdit.PermissionSet.al @@ -7,8 +7,8 @@ namespace System.ExternalFileStorage; permissionset 4822 "Ext. Local File - Edit" { - Assignable = false; Access = Public; + Assignable = false; Caption = 'Local File - Edit'; IncludedPermissionSets = "Ext. Local File - Read"; diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al index f426c73464..94fdda3adc 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileObjects.PermissionSet.al @@ -7,8 +7,8 @@ namespace System.ExternalFileStorage; permissionset 4820 "Ext. Local File - Objects" { - Assignable = false; Access = Public; + Assignable = false; Caption = 'Local File - Objects'; Permissions = diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al index 320b0dae00..1ab7c7025d 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/ExtLocalFileRead.PermissionSet.al @@ -7,8 +7,8 @@ namespace System.ExternalFileStorage; permissionset 4821 "Ext. Local File - Read" { - Assignable = false; Access = Public; + Assignable = false; Caption = 'Local File - Read'; IncludedPermissionSets = "Ext. Local File - Objects"; diff --git a/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageEditExtLocalFile.PermissionSetExt.al b/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageEditExtLocalFile.PermissionSetExt.al new file mode 100644 index 0000000000..1c806ba9f5 --- /dev/null +++ b/Apps/W1/External File Storage - Local File Connector/app/permissions/FileStorageEditExtLocalFile.PermissionSetExt.al @@ -0,0 +1,11 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.ExternalFileStorage; + +permissionsetextension 4821 "File Storage - Edit - Ext. Local File" extends "File Storage - Edit" +{ + IncludedPermissionSets = "Ext. Local File - Read"; +} diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al index 3aac0d3cff..f973462748 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Page.al @@ -6,18 +6,18 @@ namespace System.ExternalFileStorage; /// -/// Displays an account that was registered via the File Share connector. +/// Displays an account that was registered via the Local File connector. /// page 4820 "Ext. Local File Account" { ApplicationArea = All; - SourceTable = "Ext. Local File Account"; Caption = 'Local File Account'; - Permissions = tabledata "Ext. Local File Account" = rimd; - PageType = Card; + DataCaptionExpression = Rec.Name; Extensible = false; InsertAllowed = false; - DataCaptionExpression = Rec.Name; + PageType = Card; + Permissions = tabledata "Ext. Local File Account" = rimd; + SourceTable = "Ext. Local File Account"; UsageCategory = None; layout @@ -27,17 +27,17 @@ page 4820 "Ext. Local File Account" field(NameField; Rec.Name) { Caption = 'Account Name'; - ToolTip = 'Specifies the name of the storage account connection.'; - ShowMandatory = true; NotBlank = true; + ShowMandatory = true; + ToolTip = 'Specifies the name of the storage account connection.'; } field(BasePath; Rec."Base Path") { ApplicationArea = All; Caption = 'Base Path'; - ToolTip = 'Specifies the a base path of the account like D:\share\.'; - ShowMandatory = true; NotBlank = true; + ShowMandatory = true; + ToolTip = 'Specifies the a base path of the account like D:\share\.'; } } } diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al index 76e7e7df16..de9f7cd6d3 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccount.Table.al @@ -6,24 +6,24 @@ namespace System.ExternalFileStorage; ///-/// Holds the information for all file accounts that are registered via the File Share connector +/// Holds the information for all file accounts that are registered via the Local File connector /// table 4820 "Ext. Local File Account" { Access = Internal; - DataClassification = CustomerContent; Caption = 'Local File Account'; + DataClassification = CustomerContent; fields { field(1; "Id"; Guid) { - DataClassification = SystemMetadata; Caption = 'Primary Key'; + DataClassification = SystemMetadata; } field(2; Name; Text[250]) { - Caption = 'Name of account'; + Caption = 'Account Name'; } field(3; "Base Path"; Text[2048]) { diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al index a30b9b981a..52b65d5d9e 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileAccountWizard.Page.al @@ -8,17 +8,17 @@ namespace System.ExternalFileStorage; Using System.Environment; ///-/// Displays an account that is being registered via the File Share connector. +/// Displays an account that is being registered via the Local File connector. /// page 4821 "Ext. Local File Account Wizard" { Caption = 'Setup Local File Account'; + Editable = true; + Extensible = false; + PageType = NavigatePage; + Permissions = tabledata "Ext. Local File Account" = rimd; SourceTable = "Ext. Local File Account"; SourceTableTemporary = true; - Permissions = tabledata "Ext. Local File Account" = rimd; - PageType = NavigatePage; - Extensible = false; - Editable = true; layout { @@ -34,18 +34,15 @@ page 4821 "Ext. Local File Account Wizard" ApplicationArea = All; Editable = false; ShowCaption = false; - ToolTip = ' '; - Caption = ' '; + ToolTip = ' ', Locked = true; } } - field(NameField; Rec.Name) { ApplicationArea = All; - Caption = 'Account Name'; - ToolTip = 'Specifies the name of the Azure File Share account.'; - ShowMandatory = true; NotBlank = true; + ShowMandatory = true; + ToolTip = 'Specifies the name of the Local File account.'; trigger OnValidate() begin @@ -55,10 +52,9 @@ page 4821 "Ext. Local File Account Wizard" field(BasePath; Rec."Base Path") { ApplicationArea = All; - Caption = 'Base Path'; - ToolTip = 'Specifies the a base path of the account like D:\share\.'; - ShowMandatory = true; NotBlank = true; + ShowMandatory = true; + ToolTip = 'Specifies the a base path of the account like D:\share\.'; trigger OnValidate() begin @@ -76,9 +72,9 @@ page 4821 "Ext. Local File Account Wizard" { ApplicationArea = All; Caption = 'Back'; - ToolTip = 'Back'; Image = Cancel; InFooterBar = true; + ToolTip = 'Move to the previous step.'; trigger OnAction() begin @@ -90,10 +86,10 @@ page 4821 "Ext. Local File Account Wizard" { ApplicationArea = All; Caption = 'Next'; - Image = NextRecord; Enabled = IsNextEnabled; + Image = NextRecord; InFooterBar = true; - ToolTip = 'Next'; + ToolTip = 'Move to the next step.'; trigger OnAction() begin @@ -128,7 +124,6 @@ page 4821 "Ext. Local File Account Wizard" exit(false); FileAccount := LocalFileAccount; - exit(true); end; } \ No newline at end of file diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al index 36d8db222e..bb81e6b233 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnector.EnumExt.al @@ -6,12 +6,12 @@ namespace System.ExternalFileStorage; ///-/// Enum extension to register the File Share connector. +/// Enum extension to register the Local File connector. /// enumextension 4820 "Ext. Local File Connector" extends "Ext. File Storage Connector" { ///- /// The File Share connector. + /// The Local File connector. /// value(4820; "Local File") { diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al index b7d286753f..daff75af87 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al @@ -13,9 +13,9 @@ using System; codeunit 4820 "Ext. Local File Connector Impl" implements "External File Storage Connector" { Access = Internal; - Permissions = tabledata "Ext. Local File Account" = rimd; - InherentPermissions = X; InherentEntitlements = X; + InherentPermissions = X; + Permissions = tabledata "Ext. Local File Account" = rimd; var ConnectorDescriptionTxt: Label 'Use Local File to store and retrieve files from the server file system.'; @@ -226,9 +226,9 @@ codeunit 4820 "Ext. Local File Connector Impl" implements "External File Storage end; ///- /// Gets the registered accounts for the File Share connector. + /// Gets the registered accounts for the Local File connector. /// - /// Out parameter holding all the registered accounts for the File Share connector. + /// Out parameter holding all the registered accounts for the Local File connector. procedure GetAccounts(var TempAccounts: Record "File Account" temporary) var Account: Record "Ext. Local File Account"; @@ -250,55 +250,55 @@ codeunit 4820 "Ext. Local File Connector Impl" implements "External File Storage /// The ID of the account to show. procedure ShowAccountInformation(AccountId: Guid) var - FileShareAccountLocal: Record "Ext. Local File Account"; + ExtLocalFileAccountLocal: Record "Ext. Local File Account"; begin - if not FileShareAccountLocal.Get(AccountId) then + if not ExtLocalFileAccountLocal.Get(AccountId) then Error(NotRegisteredAccountErr); - FileShareAccountLocal.SetRecFilter(); - Page.Run(Page::"Ext. Local File Account", FileShareAccountLocal); + ExtLocalFileAccountLocal.SetRecFilter(); + Page.Run(Page::"Ext. Local File Account", ExtLocalFileAccountLocal); end; ///- /// Register an file account for the File Share connector. + /// Register an file account for the Local File connector. /// /// Out parameter holding details of the registered account. ///True if the registration was successful; false - otherwise. procedure RegisterAccount(var TempAccount: Record "File Account" temporary): Boolean var - FileShareAccountWizard: Page "Ext. Local File Account Wizard"; + ExtLocalFileAccountWizard: Page "Ext. Local File Account Wizard"; begin - FileShareAccountWizard.RunModal(); + ExtLocalFileAccountWizard.RunModal(); - exit(FileShareAccountWizard.GetAccount(TempAccount)); + exit(ExtLocalFileAccountWizard.GetAccount(TempAccount)); end; ///- /// Deletes an file account for the File Share connector. + /// Deletes an file account for the Local File connector. /// - /// The ID of the File Share account + /// The ID of the Local File account ///True if an account was deleted. procedure DeleteAccount(AccountId: Guid): Boolean var - FileShareAccountLocal: Record "Ext. Local File Account"; + ExtLocalFileAccountLocal: Record "Ext. Local File Account"; begin - if FileShareAccountLocal.Get(AccountId) then - exit(FileShareAccountLocal.Delete()); + if ExtLocalFileAccountLocal.Get(AccountId) then + exit(ExtLocalFileAccountLocal.Delete()); exit(false); end; ///- /// Gets a description of the File Share connector. + /// Gets a description of the Local File connector. /// - ///A short description of the File Share connector. + ///A short description of the Local File connector. procedure GetDescription(): Text[250] begin exit(ConnectorDescriptionTxt); end; ///- /// Gets the File Share connector logo. + /// Gets the Local File connector logo. /// ///A base64-formatted image to be used as logo. procedure GetLogoAsBase64(): Text From f874f2353dcb95a8cb4aacc8605102964e5d3d2f Mon Sep 17 00:00:00 2001 From: Thomas WilliamsonDate: Tue, 28 Jan 2025 15:14:50 +0100 Subject: [PATCH 4/4] Rename parameter 'Files' to 'FileAccountContent' in ListFiles and ListDirectories procedures for clarity --- .../app/src/ExtLocalFileConnectorImpl.Codeunit.al | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al index daff75af87..4c61016570 100644 --- a/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al +++ b/Apps/W1/External File Storage - Local File Connector/app/src/ExtLocalFileConnectorImpl.Codeunit.al @@ -27,7 +27,7 @@ codeunit 4820 "Ext. Local File Connector Impl" implements "External File Storage /// The file account ID which is used to get the file. /// The file path to list. /// Defines the pagination data. - /// A list with all files stored in the path. + /// A list with all files stored in the path. procedure ListFiles(AccountId: Guid; Path: Text; FilePaginationData: Codeunit "File Pagination Data"; var FileAccountContent: Record "File Account Content" temporary) var LocalFileAccount: Record "Ext. Local File Account"; @@ -157,7 +157,7 @@ codeunit 4820 "Ext. Local File Connector Impl" implements "External File Storage /// The file account ID which is used to get the file. /// The file path to list. /// Defines the pagination data. - /// A list with all directories stored in the path. + /// A list with all directories stored in the path. procedure ListDirectories(AccountId: Guid; Path: Text; FilePaginationData: Codeunit "File Pagination Data"; var FileAccountContent: Record "File Account Content" temporary) var LocalFileAccount: Record "Ext. Local File Account";