From 4457aed5b41af159face672e8d89a903a0d3330c Mon Sep 17 00:00:00 2001 From: Amedeo Lepore Date: Fri, 27 May 2022 09:54:09 +0200 Subject: [PATCH] [AAE-6242] upload a new version of a file attached in a form (#7651) * [AAE-6242] Create upload new version dialog to handle the upload of the new file version * [AAE-6242] Create version manager service to open version manager dialog * [AAE-6242] Export service and dialog * [AAE-6242] add adf-upload button to the show the upload new file button * [AAE-6242] open upload new version dialog * [AAE-6242] Removed console log * [AAE-8798] display update option name to newVersion * [AAE-8799] Emit version manager data when new file version is uploaded * [AAE-8799] When a new file version is uploaded open new version dialog and update current file version with the new file version * [AAE-8799] Rename UploadNewVersionDialogComponent to VersionManagerDialogComponent and UploadNewVersionDialogData to VersionManagerDialogData * [AAE-8799] Use default root folder id * [AAE-8799] Add #uploadSingleFile ViewChild in order get the input reference * [AAE-8799] Trigger adf-upload-button by clicking on the button in order to open the file chooser and upload a new file version * [AAE-8799] Version manager dialog emits file upload error * [AAE-8799] Format version manager dialog code * [AAE-8799] Reject upload and permission errors * [AAE-8799] Catch upload new version errors * [AAE-8799] Update allowable operation type * [AAE-8799] Rename VersionManagerDialogComponent into NewVersionUploaderDialogComponent and VersionManagerService into NewVersionUploaderService, create specific folder for new version uploader component and service * Restore previous UploadButtonComponent version * [AAE-8799] Use [adf-upload] directive to upload new file version * [AAE-8799] Add mock file for new version uploader unit tests * [AAE-8799] Override mat dialog configuration * [AAE-8799] Add unit test related to event emitted from Dialog * [AAE-8799] Create model to handle New Version Uploader data * [AAE-8799] Return data on dialog close * [AAE-8799] Add showVersionsOnly property to dialog to show only file version list * [AAE-8799] Add dialogAction to emit dialog actions * [AAE-8799] Return observable instead of promise * [AAE-8799] Update new file version type * [AAE-8799] Subscribe dialog because return an Observable * [AAE-8799] Add license header * [AAE-8799] Add i18n new version uploader translations * [AAE-8799] If data.title is not provided, add a default title * [AAE-8799] Change panelClass for manage versions visualizations, add dialog styles * [AAE-8799] Add upload new version dialog unit test * [AAE-8799] Add upload new version dialog unit test related to manage versions section * [AAE-8799] Add onUploadNewFileVersion unit tests * [AAE-8799] Test new dialog panelClass * [AAE-8799] Create a method to set dialog title, if title isn't provided from parent component, a default title is set * [AAE-8799] Add doc to new-version-uploader-dilog component and service * [AAE-8799] Add new-version-uploader.dialog.service documentation --- .../new-version-uploader.dialog.service.md | 89 +++++++ ...f-new-version-uploader_manage-versions.png | Bin 0 -> 28006 bytes .../adf-new-version-uploader_upload.png | Bin 0 -> 26459 bytes lib/content-services/src/lib/i18n/en.json | 11 + .../mock/new-version-uploader.service.mock.ts | 182 +++++++++++++ .../src/lib/mock/public-api.ts | 1 + .../src/lib/new-version-uploader/index.ts | 18 ++ .../lib/new-version-uploader/models/index.ts | 17 ++ .../models/new-version-uploader.model.ts | 55 ++++ .../new-version-uploader.dialog.html | 33 +++ .../new-version-uploader.dialog.scss | 79 ++++++ .../new-version-uploader.dialog.spec.ts | 209 +++++++++++++++ .../new-version-uploader.dialog.ts | 83 ++++++ .../new-version-uploader.module.ts | 46 ++++ .../new-version-uploader.service.spec.ts | 242 ++++++++++++++++++ .../new-version-uploader.service.ts | 86 +++++++ .../lib/new-version-uploader/public-api.ts | 21 ++ lib/content-services/src/public-api.ts | 1 + .../attach-file-cloud-widget.component.html | 1 + ...attach-file-cloud-widget.component.spec.ts | 64 ++++- .../attach-file-cloud-widget.component.ts | 15 +- ...file-properties-table-cloud.component.html | 16 +- .../file-properties-table-cloud.component.ts | 12 + .../attach-file/upload-cloud.widget.ts | 8 + .../src/lib/form/form-cloud.module.ts | 5 +- 25 files changed, 1278 insertions(+), 16 deletions(-) create mode 100644 docs/content-services/services/new-version-uploader.dialog.service.md create mode 100644 docs/docassets/images/adf-new-version-uploader_manage-versions.png create mode 100644 docs/docassets/images/adf-new-version-uploader_upload.png create mode 100644 lib/content-services/src/lib/mock/new-version-uploader.service.mock.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/index.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/models/index.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/models/new-version-uploader.model.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.scss create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.spec.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.module.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts create mode 100644 lib/content-services/src/lib/new-version-uploader/public-api.ts diff --git a/docs/content-services/services/new-version-uploader.dialog.service.md b/docs/content-services/services/new-version-uploader.dialog.service.md new file mode 100644 index 0000000000..724e11caac --- /dev/null +++ b/docs/content-services/services/new-version-uploader.dialog.service.md @@ -0,0 +1,89 @@ +--- +Title: New Version Uploader service +Added: v1.0.0 +Status: Active +Last reviewed: 2022-05-26 +--- + +# [New Version Uploader service](../../../lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts "Defined in new-version-uploader.service.ts") + +Display a dialog that allows to upload new file version or to manage the current node versions. + +## Class members + +### Methods + +- **openUploadNewVersionDialog**(data: [NewVersionUploaderDialogData](../../../lib/content-services/src/lib/new-version-uploader/models/new-version-uploader.model.ts), config: `MatDialogConfig`): `Observable`
+ Opens a dialog to upload new file version or to manage current node versions + - _data:_ [NewVersionUploaderDialogData](../../../lib/content-services/src/lib/new-version-uploader/models/new-version-uploader.model.ts) - The data to pass to the dialog + - _config:_ `MatDialogConfig` - A configuration object that allows to override default dialog configuration + - **Returns** `Observable` - [`Observable`](http://reactivex.io/documentation/observable.html) which you can subscribe in order to get information about the dialog actions or error notification in case of error condition. +## Details + +You can open dialog in two different ways: + +- [Upload new file version](#upload-new-version) +- [Manage node versions](#manage-versions) + +### Upload New Version + +The dialog shows + +- a side by side comparison between the current target node (type, name, icon) and the new file that should update it's version +- the new version's minor/major changes +- the optional comment +- a button to upload a new file version + ![Upload new version image](../../docassets/images/adf-new-version-uploader_upload.png) + +Usage example: +```ts +import { NewVersionUploaderService } from '@alfresco/adf-content-services' + +constructor(private newVersionUploaderService: NewVersionUploaderService){} + +yourFunctionToOpenDialog(){ + const newVersionUploaderDialogData: NewVersionUploaderDialogData = { + file, + node + }; + this.newVersionUploaderService.openUploadNewVersionDialog(newVersionUploaderDialogData).subscribe( + (data: NewVersionUploaderData) => { + // place your action here on operation success! + }, + (error) => { + // place your action here on operation error! + }) +} +``` + +--- + +### Manage Versions + +Setting `showVersionsOnly` to `true` the dialog displays the version history of a node, with the ability to restore, delete and view version of the current node +![Manage versions image](../../docassets/images/adf-new-version-uploader_manage-versions.png) + +Usage example: +```ts +import { NewVersionUploaderService } from '@alfresco/adf-content-services' + +constructor(private newVersionUploaderService: NewVersionUploaderService){} + +yourFunctionToOpenDialog(){ + const newVersionUploaderDialogData: NewVersionUploaderDialogData = { + file, + node, + showVersionsOnly: true + }; + this.newVersionUploaderService.openUploadNewVersionDialog(newVersionUploaderDialogData).subscribe( + (data: NewVersionUploaderData) => { + // place your action here on operation success! + }) +} +``` + +## See Also + +- [Version list component](../components/docs/content-services/components/version-list.component.md) +- [Version Comparison Component](../components/docs/content-services/components/version-comparison.component.md) +- [Version Upload Component](../components/docs/content-services/components/version-upload.component.md) diff --git a/docs/docassets/images/adf-new-version-uploader_manage-versions.png b/docs/docassets/images/adf-new-version-uploader_manage-versions.png new file mode 100644 index 0000000000000000000000000000000000000000..43acaefbd8b5138fb01da487d05dff6f2cd8d30c GIT binary patch literal 28006 zcmcfp2RPUL|2~Yrl@OT;5ve4bhS5MqNXeG$5rwQmGLxN9(V}b_Wo2Y!ha?%eAbLyk5`eW1NrkJfH7SjdRMh)a=wG5{dS- zilP>Yv?+x|B1@;Dz$16BEUDu!0X7N>8mAQ$xSg&#TH9Q)B9Zv+dEb*(sXe)?&eSk$ zuQ1&Y?o)ddTwieujHK)G$p>l(7EGF5onSp;qoQTBYfJYn|J`A_l&qJGIgPf+&24_$ z)_A(@lKO0ng9nn^&6!w439cPj(9Ji&CX7SnF5tY?< zuD#zRKkg4+J8{_d;^E(KH7yc5>aMcpze;3MXKdJ^o9yNL(JJsa%cVktOFuk+u?A81 zy+71#L$@dJl#pBiX{W1>MqnU$7CS%hp@R7NxrSYpDk4vec8FaSN}$+rf0S~=|I0v3 zUE@|s{%G=BqD5;5H_gv%$_ldg-QDjtMNV~M^Lc{?Q@c%v<5MJ$8LU#V^-zdpACGxM zX*5IfS1-M&62Y7>AHS;An4e<={e6xN2qYVs(-Gx{b?aZspTz8t47!SV<&q z(rHCG9rwQJfg460t|PK@-5D<3|A=+(Ca#e55bEdYPbO?jE)2fNPVKaQQtGg1 z|EZub%cqRQo`d(V>aHZ@OnI_Wa%_Dwq;v$11AGf@b z?e?s9|63uGw6?PJA|X&Ubsi395t&D(Q7J0?JuS!j{ zuURb^81Sp_4LIbE=P^z^j5J`ZEvTQuFiyk7O0 zT%QUI2?^ebl|Hbi#G( z^S$@d8;Ab#CFF+_kg~DcXwI zXd2Cw@OCXp%8h)HWiL#+yEKeT$2e#bglSVfvNDRhA|$<|N$-bF<4K&-ABx&xK%!qc7K7PwM3>8hR0wQ1V2 z=ocK8-@Y*!A@`J6mcZ`sad9camy5o82|qdYMdwkuNLGqRRu+SO;<%>&_$ZZOi?+LV zhK|uPv5K6Y(b+5Y?Bg*W>t1IJ1zoomMDB~8EaZJBuDA2hlWpamOF{nrn|YYFR@&pv z6n~t|lJ|Q$eT9dYH>sdNXCGyzPNtHfl_9%oEY0Xh5moZ@=g-;1ZJD1weafbU_Ydh~ zlnx~~E_JC6xwp^U);3dn(M~|H=)2z;3*+k=89L!6cU4nwrKKJ4=R=5S9;biK#l;m~ zk)|Wi|8cOBX}m3?bia!+Yk7#uXi*~vF`d)L{PW>G5ZY^$Xo%_co`cyK`Ol9bIYh(!DIdqo@V`)>rg2M2~ zEc^XCrrIYn>|c^<|6O_mRch%+LEGkE8WzS{xkWeoVtCH}_l;QOUALwR+iU7(=roJ2 z==icQ|9cIiLWao7RHTBM0YRyl(-Lw|W&If4{cQbZ4x~s*b)VjVaKsPU&bs;_UF>wujred8BDR+O9YNpUK zW^!Ds(;{|b{IABw9+f@=+1S{x9hnEj3%~x zGk;Vze^W9p>O9~ek|%N9&rI;&VqV*=>xVR}Z2RQ$bkV;xnb|av9MJmkoWrIV z{l$!u?P5Xy!r)7_PTJXY1(Ll3nRj0H*5dUT9b(-(oNuLPU&s|ds`)u)^IV1+@mPxu zxA)sC>GqY+BT~EDTmK%ZN!Q9yG%#SF87QN?bm@}R(W7!aOyU>#pFDZ;?%lhaNH`;7 z<2@{4$3L)UynMNB>(;Gzo4$wIwUx zd-jah(a~`mhr~7y506z@65=DDNmW;156$?m-^p<=bcIvYleMxLYjR z)Tw;G>9Kg<`a3%hJFP7*&~@Jyyr;sjeY-#Lly_~(>6w|fue_9$C}jSA-R`xv(4C&0 z?eo%{nPh8c=lt#c4zGpI^TkmQ>l<3IEYg%@dXMb;j+BX26vfAPT~)u-5_d{TNhvBa z`~ACjL_Y~$(_&+1ule$2w}XQN)p1Xjl9CdGruW~jzJ2?aTS9_mpK?S%a`L_nJp}hb zGl|$OhNWT^=llxFi=9U)l9H1(jg1G!S2{a8i?4p8cKBS}W?red_Z}7TX4KU37ccUR zjEp2`@UaUUYG~Y?n{z5IDKVcLsunwR$YZ*@J45@xOT*W6uN}YCKkdy=%*kn~{ELg? zeneA-s(cgwuj zmQP}0s7s9Ku3x|Y`0-=zJ$uM>b#=$vk|{~t{QO%pGBR=t3Ib5qjEs!9@2MP#FxJrt zc>7lIqP2BRP0glqkA+))eq>tO+PH?u!E(>qo*p)mhPJkULBWxf=g*U{>)F}a<6T)C zSFc@j-dOjRI(E$Vj(1ji`bi6mz3rbqCF%&YWf@A&Pxop|nRuh>Qf=GzHru$2frW*_ z*Vor&=JSQ*JMxKD;T)1q(>(`2JUL8hT;>+2!gC9ur>DoFrKNTE!GkTz%F1|)?Td?x z3}JQH>h|>b%uIpLZ*FW~n*9=7St(oU_T%=*eI}j*`}Z6^yq(lqXm{Rac97HB+B!We z>y(xj6EzJD$DIqpf`STHuO7mM0Xhs#Prr1Irw-io+F_su5My|H`p&s$$M)>szooIU z5g#-@P$uayKTfuJ^XBl`%jeF~5m$5X9vKY{jmwX|9oS5LG zOYKK@kEe%~l@`|b!XfB$ZmP}uIjw0 zccAymqRkchZQF=+yAvM%Gtzf;GtrKaE_pY8Td(4Qw}CXQPGcWA6W=^|utR2TUYmT& z7CG#()NDh%*R3hM-LB(OQfz7J@$~^)x948{rpm^_u}@ZZw_(wh>epX8~()~{lT3t&fD#3dHFVZUcSsnO-((wRJll779^V#|pWC-Bj@0Er>IZ6K0L zaW5m0`{H(8+{ca?SXMIbQyN-Z`Vw4Lc05{jKN4qdIC!^3O~BS--whcNqv8ZjpWati zk7c}%omEpq;)Po_L=5Nnt_i+xY7&26KHq-EedWjN;mOJ6o`z;*f8oZ{zF&=MrQ`+= zRJTZLYk%Tx*wR6ta{J5Xbk&S2Zu>VHs+$@pUu$cz+b;+T2`TF9v%P%z(#+8@hcV># z?c2FlO)S&>ujw~aY^$=%DW|x0?V3@^)l*bKST3fTr=?RKV;`RpVO};{L1SrYsi>?x zx-whY(3NF4JTpVb%F3FOnmQaNT2ZxN*FE>EN!0SgFN2Hsh>(YZ+dY) zCkT7;z4^&zd|paQirJ^V`TY zJ*|+*8#Wmk84ZC4#V!*}@(K#~;^I(pmMFSo9zG26UH=^)5)n}^nDgdVLIQWXj=(M% zFSg3{Wx;LRw&hyXQ3Ezze%jyMOdIdB%s>6(hclww>vyx{pPvo8hkuN2uJqe{@9EP! zjun3?^z`)p4EfU!ef=76`!@A1X%FV}IVPvf&G(=a{q^06R$jV?F(&I-WUR@6OQvw) zXp;#ALse;w#{~3 zFXbwI*0@>1P$!3nhyA`aJsQQDlDOQa;$?lg4yebyrQGdx6FrW~xLev|{-%*g!^EdF zMkc2A%kQF8c;COUK#5rKk&f`}b+guvXd zpVevcUVEb#e*L(lMNYSy?)Mi%Zn7%eXl*ckaA;^K!<|p;u>-N%nZ&badWqcQ9e z6558!Io|(T^sU9gna?Hu>FIor*>>)cmgYpMrJ|=-20plY^(sc&yD>4h-ydsopyfQq zjHRuy)7IA3H#FRb__}xR-n*tIe@wE9d*vdzq>-ilL+nlO-!rhXQsPdw(QFGi%WGFa zmt$P^Jnw6FYobD+a}J67f$H$k5Xc!3ks!3#v~$nQE?o-F%@s4M@Y=b5|Nfe>L39u$ z!-s&N>3@S%?86_HPM?n-Og8C>qRU287$|kEeRpr4^WyY{JG*7M1_uXgwA7Q8!f(BJ z@uKxT)d{1eSGl{fLa2T@@I?xyGn1d{A+Yl+wQ=ipWX^PZUc{7vmTW@Brthh|LF zAn)aQxt`v9Dmv<0<>lqM9iQGm5Hz>5qd^J>6wcUSltGY?{_>dKK(6*iJ8;r*hNNRV-b%(9ncYWMwVL3Zg2Wjg_Fqr|!4@ z_;f3YP10c(@7c#BSy@?^$&S#ChCO@tWoyYP`*VX|_TgyGK2LuOOc4r$CkTlz<A%uiNVlKqbXa`x{;0;eG}*PSo7YyIzR%lS8GN%-Ibv7!y?x4J4xgpG{tO3? zx2Mu<%qn1lcrVt=^0x2XU{6c)18=R#kKD4Tfgj_f!=etT?*$M+hFoEgS(u+Uv$EPk zdiU|85^lDB1u>>3!u5q&K|ulgyZ88Uj=z8ZvR9wW(Af*DvUBH7`MFn!3*0D5aX^>4 z_`)kq+xq%?1dB5A6fI+De4Gj$DqI!-N$vnm7;mKzusho^7e1_-)1NtWqP;Xk?ruPs z4x@hGQz6H{c6N3^zzIb~EJ!5UkKew2B|m<=j3VoPxVH?3dHrIiJ?G+O!oT>e&^A31 zRYnB?77Tp<{yl|jPsz10>#5fIdL_%F2aIuv=DC%!df7&yK*~f5`jn>LfF)w!UkgzZiQ9w_RzSmqeC9wgB6S}Z32beqeKTvJ^G{A|* zCgn2DfJ_On69r0jcXxlm+Ab*}5%l`CBv2F4GQ#gZdGsiF`+;+pjb2AZZC_nob^7)7 z^p%0q9sK zu!I3z(%)ehrzYAAtEm@J|H8X2=IL+4>TY8bBdsjWHUM-UF$l+RoM%2C1>nwaY>_^2 zfD+A0{!EVIUO&JMFK_SR@85&&-@i|h)yB)q%kG-aWkN;*GH*b~AdyT=OoR=JO;fe@ zVB|P>@LCQ=@ zuUr29!{5Gb2~}+8HVdhd@ij!(;&Ck(>z|*H~vCsrmWZJ%+0M>}`3sZTiTKtloOwt;9w)4lnWK>AKDE{d~*<*`@xiyuC$aPlO{XL;fPiNPRh%FPe3d+5P=6FEgi z@y8a?m1r+=L8t&Ug_ zaPs=h=W2{U=K7*Ks;OGrQ1C^|=kIF-TguBPq-WJ(Ux^lE^Xa+yP?et=U3q#AiYNj0 zW!Hbw6L7_{{*D}w=e;LSLU;SFGGZezml=jdfa>eWyPDxJ6<$>Evg_pXOWyC#(=Xi( z4h%H^`tBZk{_xacNy!HEXw0Jw?CiI5D%TC%9WiK!gofI*gB3eA95J>2Kcx@d|cIx?RK|vt?*lF&0-8siJzW{)_BiMLtKgm&(;|qIVlF z_EdP6DLtxq_KXunmEWaIl69#3%^NZ64{V6oH*H-PQS-?uDMx{P>-q}q@_@sz`pWLU z?J1|h)rj{8+$$IK?AHGXufZyzqglw5dNW|Tis?pXXHt(KRtR) z?D~umz{_Q0wUhm_EJ}CS{8CbQb{%t}!noi0C%9U9w|4;9W(qNaFq>dg?HBmaiN(0gO`>INdaM-kF>#JmX9z194* z_rQe<7r?m?Lws0RSZ-^%NJqDcuI`pIXPS7+DtDi7zd1ke!py=VZ)&=dmt_tgJ{)LHj!9cy8c5x|L>N%XCG)VL zX}_nqxcIxq#%i>;z|yIauPHyvxdFI?f%icI1s>(ydrxJwu#_=#rp1*bE#U5jv^UFz zT}v*sDSfvlqNq|@V)22bR(;3qBw`HwI~G6MZR%Th{fl>SwLj%+cfF&4KbnT*S|N%I z-CI*!H(xTOZ09YTj!!*sL|RlwV8{Kas66Xt_BX4)nh5kHBqXE(x*}OQa)e-p9v`$E znw)$urbbAD?=CV&rC3Rcj*g3)D^{&?jb3&F|m^~ii%%4<>-g>YtcHHy4;&+C6Zd*tN%gQ>(PayCT0 z^SQF;-&?2M_9Ix+kT?f!E7lXldlf$Dx_IWlFRD99ET!VM6yneMD|e*m6f$&*Y&VF+JMMO8H?FwtntY^gt1~$T z#a>a-oW?^KLF#~(cKIwmWB`|9dyDd!P#%$$Mt7;L0`R;7Gaen?UfyFj6pv{3?L!spL0Wk6tH zBIr*aA0PMG@+AVdklr;n*TqSDI!$))U%Ys+3e)4Y?;qsQPX^sbX)&mgk%;L%Pyf#K z!5fr#AqbXEDRm4PzkdB{MfhcPU&VC1I#)|C<++Fq5CSkOR%$2#nA>su`pjE&Trd@p z!?nSd>#KjoBqdqjzinu+T$&k>y7qk=sj8)go*33tcxom)Uto^BMa-7pP^!W=Q_`7z zd;ef=e*P<~|9|d3ASr3@hd5~tASV5yD?7K*(&l-uxV>4Pd?9t?7Z35*E4_!X$Ch=s zcc8u#Bapte%C57yxw)}PdmO?3 z5L5h{m6@`Cdd0Ssc^=NASIKb&RJ62#=uD&p&!rh+;Kx%L0NH>l2zEmuXd4;H{l*RR zu9p|?V(wmAQkIh=BbkA-tzpwDL=2m^C7r67TKeL<8*H~h&VDemW!Gm}3@-Ttz5Mu* zr7417#?0FD=Eh+lLQ(?eQTJ~T&NJ-X8H(mUf>|A;O6|vI$M-x?B|C9tk1iiG=_ZE# zeIPU)%NJO>vW>&Q4_N;C+NhsvMpnuEFZSnoPAMyICRw+{4Gfik_;6MwRZGaQXd8lA z0c8ucT2P`wur2L_g>V2p+$K@L!PMnlY-G~W(HX+_IB4LiSmNU1-h2G0RaJK zeFZjuFpz}pKlAW1xvH!F%Zv=0Tt~}2nvaIk)4J!ojYS^OY<~p`j{8wlpeHYiV?%JW zY)bW~Pup+YxWOkR6aWrlt^x5A6cj|_DpD;iEww-v^c2~1015?zWFA>xTVby*Oi59c zngPjn_t~?7A+Jeq_D?ycvOEV4pw}tv6GPwB*jG?7S3?6xez(2?45=bU4=*n-aO4cT zcHO}%sg37tC%Zu&ztGE)N2n5{7r;5a%%23g8;BiS&-=2g!}Kp=1rp-gUM_zLqL}@= zy!?u{KM_}vj$pR*49?+#;=Q*lH}FZ z>3e&7d&;hhf)@~T96Z6s##RM_J~1(oX#csnH$lcla2}ug9_t7S2w;N%AZ2bpYEWzD z8V|%*4GeISh|muR3bFv40-Sw|8o{`I`{wiK&jTThyvV7jX^xddHRQfCTTob7_fXjI z?$f8+fCr4;xHHCk{n`q(G(cSa$MkgWwXt&-FI}qbZ@wAwUoXJNk91i5KP|rN{+Lj> z`S_^5crS7Kto~FcaILijB?>St@KhWYn06m-2TgFx-HbHfBe$5cnKUI^s9U||kl`uG-l5^*=g zSjL_|4SL1-?=LF?_$1xW$l!mj78`UrinsdRJF3}=6_GpOo4hgZ`~*{$fWnT_U41Fu zI|v}sY_vYSMjTsg0d|x~vagL%KZ9jg$uMx&rksu<3ceR675zMmKn591R*|k#UFKj| zX$?$~F6s86Zqq%9Oa^kg0v#I{*4{X$daTUm=nCv$rrrYR!y;nzF2;+FpP3q2ds!$? z=R6=JILofQCx}64I<(e(lGi`i>*(sr2|TbpD{pIGGC64*Qa3`e9ddgJRZoGW&rO?RqxarbEMjK>VH_Q)o(KtN!Z;TK($_wdo=a_IEs1Z4IMG z6#*8KF41hZ;ZRjksajr`V&vo$E)bIZPH=W_Zv56aCji2>6#J>)EwaiwI?-KAr}i@L z;^fTpS@R&7@ex>K6J*j1%+->{y%qhM?z{&YTyXxZ%r#0 z;1^wx_xg1M-el55$w5 zw70j%|nUnrgeqR{D7}Ay7s{#l(TFI8Kb)MBrc@4tg$5TX*+*F+X_l zKFq7F;RLadfpNG!^~}4Lmf(sCnK$L-!?UyW z$jZde0OHO+M^5qV-%mz@fQQQTd3r(T{Q1$(CD)35){YVLh>guwumFEnR%qhwfg%89ISqBUY;Bz zJGUQwTQYR0LEHiR$Zl*ygIm4PwwG@Q^)4wVQZOB7O-F_{8HsXn76b7X6W=vgj6{27 zWVk5lxdK3vgF19ArG($%1NaCOREtw$ciqmGM(pu6Ksn5H7&wOK%L}n6q-qfq9D$2e zRaL>u1>~k`@DX|)@X=`2>p}3Nf=J}&XP?|sm-$PMxe?&Z(N&P2^b|}#I1+YsXK0mm z08P6MIg5*n%>XFGBqS(E#EgayN0P`td6AcwTU4Z^q7vd{FDEC5CZdtQblhp^BnShY z%d}WOK`54fz%XPavgb=`ZNzybzdJl9UUEB z?)^0{40Nvi>p}zUnSYuv{{v!j7&6^hX02GB6vQY_0m4Yicl`ts>u3DNDka#CwjQt* z$I+V&{q2~;>g4af;J76$EF9Pktm+>bNe^4Ule5 zPhUY-v@Hc3X=oUBZR{ff=wVxMa&o%dn#sOn#}heLyAF6yf^Dr=|$5RsXdE zr_9RqVS;tQ_$*w}LMZ2HX&73$NHyP?`St+POeOZ3D= zM+d;u03Iw=t|_m`6SJ@dcj_gR8#j&;LNOL`m`I4IC~f~kSV~L|;L1Mf$Eb)rN`CWZ z7nm#BdhNjqZw|rp+05%#J{RXTP9a%>!GywEK}JG#sTr#F2Tm5x)&(Z?qjQ&*zaIvr7`~HI`tcFlxooFC`_5Zi2~_@ z0(lYyYg?tS@5fh;2c$AG!V4aCwLvT=s8*u3XX?Iag}AEZe+L9UMvTRofv}Mg3w?e4 zli)-_dJ_R39f_@{S3Yrzky{9w=8jgBFmZsSfr>38fOz6!7~yCU$HC_DH@2bn$dwAq_z+VxGdlxKEteh3YDgp7Z(hXF@_oY;p?=(-OiWkS9hW z+5?xylw-_IB@-6l?I4C%AViLBoN!*uEi4d)@E!sQZ%5RRt0hLsPotuu=5ooPpl3}5 z3PAEkQrXzpJQg$Ggvtu4UPDih_YNZ`rngu5`2=Cjyn}V$(W5(AMT~9&Py_#9SoVW5 zsuW46?3~Bls$gTNRlalb)TuxxuT-tA03BcqhoBtr@$<`pfs5dhz735M<_GGk+S9tS zYxCO)zI$dyWIBhg@gFwO^sHfNr%Qo^#76n|b>*7cXgM*w19P52V}oZz>~bqNoGv>F z*g#01gh7II6Kdj{r9sc!Ki*yLhQvS#V+!7y!&?^O34)f81F*<%-n>B|^l~$-tgKiP zD(=!!w@3DUcwKmSNF>a_Nk0dnVGko%M2t&^I?tQd!K?upk`8r*=#wDPNhFwc_G0m& zJJLqPLsVQ{^&(Zh{^FJVGtMI;4XpAo5=#ErB2pbdcKiE`U@k%sd?aehQWX36F)=50 zO>l*T9D7w3_u8~ySb`-ji~x=#bkrNazZdqptCkoICcKcr?n*j*t^(TLJ<&6nR}%UA@PWOP zAxIzI)zJ#yjrHjY$3(SQ2{_l}gzlYO>sMbZQ(arpMaL%GqPq*%jFy(1*Md@rNuy7x zua*t+cILrzc?SuZsAwk#hwOGs=EA@aNFNX>`wksqLT=c9YLs@p^!o*&1%X75^twU; zA7RoL--dkZz97ZDHf~Q6pCc>d$xI85Si$_AmQ0tHh*ktBAA|;!EzdSCkUU|C>uTit#}h$TIT-|v4CeO3Beb90s zU>aOTKu1n1i}sIWV{2<`H_yz>$nUi8z6LW3Gc&Wte#u|C zA_T>lL>L~b00;I&H4^LsIu0d~<%{Ptbfny-*-%9Zj)$C_oLgLc2YA6#YHGB^;4kuZ zk3x`HN4h53bx3&lDU8yP&u?{`c+w(GYe3v_7{wQ@VK8yVs0&x3<@;!UTv|04NoX61 z3o=+|a|eeAbkLnZNqEzqHyrgNSKJ(`;R+&jHc z#Z70ly5oWOn`be}T=K&^pN1_pV@ zqNs?7l8=uJ1uZ+dr>7?-J7QF*Spf3;n95k?t40Ab9*NCA$s z(d|VWS%$9zx=z4&2oACuGzct z-$Xie=#UvO4p^R)Ri6rA2;?^|EUUT%YMb-ypftoOG7@kl@iSCsVoErA^eA?YNVm)v zFU%bsML!2GAIt}8B-H=2XU}qPlt-ZB;-}WK>yA)FB%~ajPj8+1QYq>s0qJP&_wQPu z0Zk*LZFzr&H(22DBe-kiS~4OE))ST*$$vAyv{vhtE4<|%vB$R0Q$OeCsz7kz?8jYX9${t$R)l96t~Hf&=Yn8@ou6lq zh{yw(2J^wl=qTBV>Aun(*xWoE0u~ zeXv^5{QiArwe#e|9K%Ss65!-o*ba_DJYvreGq?bpr@DlZa>|yTFnS_{;Pni&2erp; z1%d)Y7X>Il!h?zdlsZtJDi64v^?hc87Yhpub#)quzBW7bC(`;7l?E;$JTJ3Zb2evC z5<*I~Yd5WFY5O-1!U~HPMc7!0^ffa}$Td|}r6zEQ^N3>B7-~Tc6kBR|T)-TdgHD#` zqaaC2N&@(ddo`m~%jcMs14;#<0FEH?F)-d%+IfwJCM!O?Zg!Ro;=zb18|0?F=$O?o zoe@=XtR-F<81nY*Fz1CyUA(c4OUY*XhEO;+2nRTv#*o#Yi#a#8wJ~aCFtdu9C_*ts zdj4Z_q4`%}2c zq=~ODW>nO`eE>HgSqRM?g$Z*N{4PrmH@-21#g=oyGDMhxF@id|x(4Ru9mK>1sEt}l zg)Eg|lQnn*+Z$*^!aU+K(SGXkDMVfnxI>H^L|PD>3O)qBjoV$8187G0D1_Us#Kb*> z<&<#lh;O2~c8#!R!LmAr=u{yP(%5gR%{Tr~!FhR1E-o|~2nN^XnZW8P! zo%zKOBe(3{y_<9s3_CPTV5TjAc7*>7wWAK!lK?SR=rja`AUFy1Jy-@cG&Hyi-h?Xd zMOPr$p<=fm?9b0VBX|x}E=46JxQr;YG79AEvJEU(kW_?Y&e_?SaLj?{*tB^woH0oY zY_7qa9cfJqu(j<&yr9%E6oHqdML&c2`FehQC}u8rdS0|q3$lbAgi=qC6|e-6&E(p(n^0lRIfd|xJGw~-3To=`f6ee48@_vRB-5yK~#Wprlp!}YI zn0jK!^te+xW@GESa6M!FU;3wOnYGf_|q+tdom{2qi;?Mv2-~T-Jf8bY15{cdS93=SpL*u1T zTcvjZ(!3<6(DGp#44t4P%sv-8heupP7Z8AvCKN+U_23{EoUjeh=mFnE zqC+Di8&C|Lzkk?Cx)~q8n?*>U1T+R*wvUI0{IK;0hW8+mI0)tgo=nW8jiMApv!BQp zg|B$3I|t?6C2G6dpy#aghgiwkCvSrPPs+@4fJW@n-I#$1LlP|N_d>QQK6p1ldr zkvUYJKhyawVj$zZR)03+=I7V`&ru287ka}3*S}b?{o6fzS(%O1Yw(2Ic$Y!~9wg_&Fu($)lcS8yJ9ORF@zyq+T@|hZZ zBoc&OWpKZP9#g_Ejmuad*)$*;^USI@`Q$;TVpHjlEX{A=*vAq^*z1N7W$xArHaJm2 zGJ`>bcCZPaCd@alteOaJ9F5Ejj%&i334y{zrU6am;I;2(-4WzCq4A=NSbaDSm}~e* zIBzuqbwttKU5a!vQDX=nE8krfgUJR~DI6IJf?aVG+#}q3r3^})li8c*;NsW^*EB9N zI44I`X8n(4NS)Ena3Y5bINPjf)hQ_{uk;tuA}HN?6>R=Da`X{!E$A;*c<(%q1=|nt zvb%vp>=*w40za0pXNSQem;VSeP>ey7Di2dfN5>KNY1iJap{DHrjUb)0NFaE)`pHNy z_4AVCTG*vMdscr3Y_=KDm>e0owJ_BUOUwL|5=xxg`TFf!BAn77v}<-FD{=eS1c3=C z9$>yK;ea5xW5Vcy?6)KD2ICWMIR>u_4qo7OMiwsjiX6ly)h14KRp~0O(=*Zw;1;w{e zxRqYRWJR2Fc4qvFp9jn4%{H`n3+ z5LyqV|3he93KRUl5?b{r;{NZV%b}@zHC%4qqyW^NzN6Y`i`L ztK7VhZ*7itz}}D314J`;D(%TqRlA^i>BD0`n9ojvh6Yj4fYUL%hcncPHbK-VsBh|0 z^^h(Jd+RFS3JV5b7XKwNT6AcNP!R>CHC_}cQ-2noj5rIB`#oxpq?3= zE%JW>In8mH3OTzOe)lR-DI9jL7FkiwxB0{iwk~8pQ^@2+wjp1IufWjgo?^ZV{VlG+ z9i6_E0vFl;>je-t-!cev}`bBnjkiL+EST*(|B<>vdx0L4+DN}6ii|_3$ME`-2+4o3w!Un4 zr6-pRwlRbFEjbuQ!3`0|y6g2o3;eAPGC^Rg8Rf=v^R}$VtQu4~i8F(8IxNvAXfn5CSCNOYXX0zg1*BGFBMp$C2P2L3>BE(9@8%q8%(_w@Hq z{ky9t9uLcxFS&go^fnnw0YSlopzVnxE*tAhm9gb%(4mQ=P6V-b^{Yy(NfXL6Z0V_O z$3R?${T^$PEn2w_zANcenAw#p+erABKuk|SegTKh11j3vhr_Zs+=tUZ5CjrrH_q&J_OIYfo|A;T7ZvFLr%^QSS|s0@XUROH$Rmi z8J|Vef~jtWOQs`HfpYXQ=J-Aw0svjq2?_INHW|JO#)boD2BPoZw^$8(nFyLeq{&iY zbY!ICwWBFcS^Tkoe!cQJc-Ygw+#S5Mk9sb?#984^$YquTfXN*d-ky;anRLMl;L<@p z-XCx{Z()%n#1aN6Ir`zl=P>%%PWR+ZcIU+BwS;az5DhM|Y^Jnn^jNcXKI7NV3Qmw& z!@mKj?i^s7cJpi={q*7c&A4OVrY3atUg!d=-?z0*$NJg^Xr7+z%8KR5$j#M2&EPWc zSlfL(JI#8_Ra#Q(Lt&%h*5pPm@^%R;bT*Imzhi}nLJHa=r_Y@EG#V-E(|hZhLQ}W+ z+;zTd znhsj$RK9wpg;O^5SFsEoVC>?pIO&4Xb?0H5B31+aQQO2M4m+O)z|7{?G2tR`_^>vF zKTUc0TRb+ae|~aB-YQ${vsX&hA2A&4JU*7Q8uDn>|bK z1?FwIo_K9=pcqVDJB*0)u%V4Vk9DB_`Cf{O0XT6o55%Z)ACM>+=Um-8 zHw>#wf_86t^w1%#pFe*pT@OWP<3D&X?<3=wM64RN`a{d$(7Lgn@*5{Y*_ zGCW)thZO&{c*S&u+vziGQ|Dhf&)W-5M| zwZ|5bxd>$&+}$gKEY-7Tbx_H&uBD}=O#y{zczBe4x%S}U!%5)t`zbKC-?$Qc%rz69 zXd*KQ;3Hq||FZrZT~L6BM-ltQF6r=;hyTCiHfs zIT3MurWL1IE}kuO?bJPQgK<^q==pAc;0jyJ;`PgLV@S`pDv%j(CXs|gZ2+;)br&2u zm!>^9YwZ1fSN4Le(D7&IdVjbbeRi%nUN-)CuRJt-94Y%?Ij9rWn1b`6SnU_Z#rlXo zP_M7_g`hD!#R*{37epqu@JEQaCb%7Y=5vV}vvq95uA@niX~i%2s=nwnc#*M=Z9L{Q zbkm{a+e0PJesRz0Z;xkaNH6Bfvr_oll@BqK%y?Pu~${HEl%r0*fTp8x&i|5q>S-!lB~ z@3kt8+d-~>*hy+q*z(X5`1Z4VE`50SPUG_B^z8CQgLN}vrP}&{Ba~3ZvhzP!MoK^T zoGpLdHkO&5uCcQC!zxdoz2FOu0VK+JFB6}{eK$$m1H_C~p+WdJ9BQ3}x4Fyy^?>!u zDFDLcmVBIaY>tyQB#s8-;C0DQVz+LyBV)Ll&`@=ZjGm&dcBPE*i->%;M&NJa7a4>< zKL=&k5Z{TRdEvqnqVOVbbU=6A=h?x+V%r=ez6@H&1?MVp=2`~^-!{-Xu1f>gr(j!4 z&>%)`cwjyIaITGlp6ewpvP;0S&|sm<&~ak-#-CuK1`@yk#`e;g z(y0^gy_YA_mhmkBM0F!-709+2Nyj(>K%n6@j2`a!;kGvByPj6DGIN~x=8DOlynVhd zBvLlFQm-W75}gft9c;L*@Ok`LDuh^J?MaK`T{;%$=>qja`sds2L}Ah~HqOE^I%8Dw ztH+*sjD#LQuj7?o|1-Lb>`1{yO`vl|4>)w^_$&yjJvnsh{t?*9rRSrN(LDQkd2O*&C{(KI>fP8Gp3^AmbN#{d(4F@_+>kVqh z$S;52rA2zFqi+!xB_JiGuYTfZ0E?)}OKkMS0olKyO5yCOu!7!fF@N1(2;VCjtv8qU z+5J2`?j8DEQchJ=MKhSw6VNRtAuB5RcB08A>ixMMK1WYW+X+v9CQ#`FJuVPOUerPE zjnVwOpath+&lVm|^ya7Oz0^-MT^(U?8+1$e`TAHOn5AF-qr?qzka$^86?D zLLz+revK+cyf5@vMv9LcNyG{#Tpj$KRI0xJTa2N&lvMFYOQLIJW~yqb?bZ*j-IM=> zOOVvc4p;NUlS^^;c!04rPM<#gIodkH^lQ^Dl>r4CWN94M&bl-0$eulNs104c`ImMu zGMc|R<}#iDyWpA3jA5LQ&6z1*GQqem57MVA*0CZ32ARQq#Sb4oTs{$uJpeIS2*NS4 zahaa;SPEt}%NsCj+)u$Nboc2>5THmmJ`h@Pp+)noE#&xSxpH;qK_nOVUH1Ynr!HE# z`fAX3!yEYhj;Ii+K&$1%?>mPMAAa!Ui4D2-5yyw|Ytxt1DVSb8mCa5s6c!hMaGZYv zLwU|`8x`yPUN)~!d6vq?zH1A8udKr01W&*Lgw&K2qpp?}6fS|IM=u}=B)b-7znJ1I zSR9O77DqfUSzAxuQ|8hHQisy++9dos9nch~Beeh$iK_uTy9^G`wmbV78tzLRmw9hn zn2A}QnvPD-&8>*bV|-6wP>?PB1h+#&+ivd=)G;u~#32%lsS^`v@p|UwiGUVIKvUxj zu24-Zmw(4^yaeR!y&_SLVVI~!3mw|({4z2oEMee-%ngdJJb|;1nTsnM-?*YgEW<9k zU@%$4OPSKkd<|H1wHNtmt2>3#qB6VL_6XDW(Gt?zNpP6?C7{S*wd?swEru3-i3 z;yz!NX!`olJY~0`u(7(^yvgFiU|m_=r}ACmDd#od*C>Qpo>&;2Hr33{?*toP~e4`iN{j|3D zy0Q}|GoJT5l&L|k)I=iVO2Q2~%xi;)UF!zQO9e473FV^;UFisP%jDC87J1`1RMLuC zSpZ{W^V6f+@acAfb8yAi(Qw4yK=of<^4+-ZjpHIQkj1T=V^XmUaBHX`o0H(Rm`Cl; zz>DsuAdc;DiaJKtw*p?HKYKTw6wG%TmPbqwI3Eh<*PsH@bEpMJ!woe4Au(|uy6)xO z4>3`qz&<^fh67S9&!Sz>10@A?xMaMFt0Vi~MB{e=z-#BiL7Jt)L{Q1k5FF*T+r*>k*UJl+wa=M4JFMFdJdGg`Yy<7S#^dH)y)KQpR_cS_B z%a3Ca8VTMihSXtoT4N`kEd(lr)iJduD{SUROP`dE=Yhxj=P7bhLSQ2MR*X6VIDA|> z8gbmEf%7_q_b?85ZhJ!mLt0=VVMbyC{a*m-WPcss%EMqeHMM{~O%f?47{@{5)psfW zK1FN8@y$s}M@uUSv%g1^o~|x2U=U>hUp!#YER6#i*}vr|w3Mh9oc3u z!k7$`_xXCa>;3)lUe|lQzw0U)&3yZQKg;vE@B6u*r}2id0y-EKHSG=zL}aR7ojC*t zCX?v+ixbq5r$S*^1?1K~jH#5vo{5EuJTzp`@Bo`TZbZAgKR~!8 z0mf&Pf;x_HwrF%vxZRy+%VK_KN5?&8AVNLK4Wp=G=jv-dk`nbN&AM7fihr%YQ=g@^ z1mb|Xc%CkT;I#)(^~FBe6+Fb42}}YB&$Yg-KA>xOQUmV~jxnSmP;kN2Y4Mx@3n+V1 z1Vw!FeHRM@fW^kb@->7sOg@zNLC>Ws=kgb&M<9y=OQv$VbtQh_`I&1_Mo_#W9$cvv z@|&Q(Vq|m_u9 z7de5M`8`5S%zn^M50XfHmg2l1<+3)wzynqsiT(oXadf_U(^M&T{bwbNu^5f1pjSaFG~~Pi8R&uATs_!^cZ2=e74{a&Ec#N3#T>+q z`+0dJD5z29L2N07Gcb<9F;S5_?~nN8PXG`BSqOlQ%!Un+;v^!70t!g0!Hv;o0l6$E z8=GGNZ0e=)(0Sd|f>HmceGCYRev4q`u{rQV!v_!WZMiI7H^24}-k2c5e=cQa=6rjsr07zbd zj5D1hA(<+IvM0l{e@HcBC6LS0Q@DVrq&8`7htC%1`>cr z=WDv&y<@{Vr$YK}J2XDY$jZs71f3Az)QdyxTo}owCbQJ-rtU776|8VCS0)E;1vtk{ zObX1SE^urB|1vOzMSJ|R%&*mBPT1Qspq$l{{-0zlvrgaa=-D7>kg{Ofg(Bq z?R8w?V1=P7hl|5ZIzWB!zO_~K3AYOu$|2~NfDCT;9x#cy>bfXMW^o};y7#W@sZ*8k zen8u0Z{K+VLI;rS3jl>N^Z8Y(dMMQs*~J(l+784_5H9Z_C<1DQjzHLQ{arW=d4A8Q z7gFK!exIcCx<}{K)`8q&lvm3VffgGGf(>o%t!e5ZN5Lx1*9*I}acIt0=g=WI_Deus zF)!BwK?0#$VDqc}o`oFcFq0{4fK#W(j6tXdM`Yw6O{ZXP=_vq1KcM|jG z51zj${?*oo>H_wkoR)Tgb^<_|BP>Mx-Gk5jf|0|z#+?LdmkUz-f2E)XtWhV&UJK$e z7Ho1l+S>QrT)j@axs}2gX=`bfyGx+<``-q6E2JqNZ1wE80z|NZZG!9s07%d?!u(mI z=s1nt1Y3vz+KuX}+y-#Z1@>HjYeFjsq5nf}n+Jr{xy95UQ@psWh8Q4p7zpxq*85!9(5RhSgR^T<(x#BfAc)YE&*zgi3$ zJ0wFrLPNA!yhcowup zorzh7qLx}u>TxjAp-e*`0<=gSHdT=mfS(a)L;s8L5>N|IdVAMEkPLt>HOVO{S#AcE zfsnsN^zW-zo1V0i(gM^$qP9A5Sna*hpN7REK#VDPXi1Q+*$j0gsD0;dLSm*8^io+h z3-8|EULq)S1@WSQW>B9q0!3j+3Ye?1x!1o?*W4TnA;F;hz^oDATL84k2?&}$tV*EB zl?u1%bh_ZBwAyZS^8tPX+#7^64ZHW?O)z_g$u~I(=1=ZG4O7a=P4&V4X;ofD`%QxPU3Vh;1-+s~dWuj=R% zz{0cX6AVCb4VinuDS|YeGP8M|XRiedRNKtV8{kS^Uoea&T67umb=4rlX=U0$_p7hQ z)ZP&phC>CNZ@&ikH66N80gZ3c;yDITJ%bFHYNnC)V8rR!^Ze8MO20t@ zoSt`{SK1$9l!r_ah#%egrqAu9g;2ZC@Ki#fstdo*SEO1P9Rdk#vu&>l^ssdRGC%`8 zB-L(If2VNs_fp)_Yaw&Rw6|+?yL}|Hl0{q}#B%%E;YSsTonfvF< zuIhRJ2QAQQF1)3`xO3xz&GupMGK|B;f%6X?CW2$|%_BY0hd#mc_k1#b=pb%&Y-Q5) z9eDH_gN)Me1BV5#F$6sS=BagF_Q#|B8>bsB+=Y&l!*g5zQeei8-q_Ngv)C+0@F0h7 z)}UEvAD4GXcI75I*fcyHVa5(ky%I$a3QauDyH@n5r#8@jD`++K@es(cCtOH0@y9_z z!LYbJ!;o<5!1prpyL?`F6HF5#m-J$`W<##WmrU|W|h~G+bj!W&F0@C zc~SiHyjSg%UU*~&ESKB`MAGveI~ADO^E$21Wg8f`sRJ<089 z3BNaDpL?=cL$UB<|1AdNvHL^HwuaV-`0Js{y;!wrZN{qvlbYdMCuwJKSw720n|?Z% zFlj)vxLBnZ4|0hh#PMOF1#o(@i|py zFv2p5TEUc`7+iM^nQS8UPmAq>_chT2GxI)s`j+!A%jn{p9v>$?SHcZhuk`F%O8Bw* zVrTXJCBpr7(%;vyLWkST4==H3(et?Z#cNM>exd5=P=3GS6eU#=VZ$5U38eD2z`N4dMTZyNSq{L4LGr)B7CUBAlmOTD$21(?RZ zCCkjEYQH6Q@%f9_rU}DEr9@)ap#4sb{xqh-(fx2m9(vdpK$r4l9)Y75=)9#2_!+0gm`0O&N?bzYtExJkMw91HI;OM#lk)G#3?xo=j}mS0!WE*uR*CW8 zV4Oz`J2q_-*ZnRE+4GO;H-=tKdnNj#(#_ylz{P^5h|@hu|CPaYy8@tqhMwmi~%d z*duGvWtR_pQ?HlWeVel+?QMM=4NFS>rjHD|56Jz0T)(Kpu+-*0vOp4Tk~-T~L+(&= zV1)X4O$)#hyzrfMpaQ^kUOdvsUVWQx=hc1MitrO$^&1?SpE%9CQw6U=Orw9u*ru#v zs~>qW5NeMkJj2(AT%SQ*QXh$2{pZ&CX5Lx7K)HObY~O{@&Mok%qN$r*<>%zSd{I@? zOmwJ4uDV?Voi4tw?%~~Mg~GWmkBnQw&k}V>PjV?8 zV;VbyVYrH__CFHFlg}GyAcK&-z5-})J>WFZL9gKT6F67 z4?F2@$LIvVwUtpZS6o#X7oL7?hv&q7MrBM8mrpNTE5)Y0t?@yMPrhx0JFCA0Wq|X_ zCSoR$o#SLqx*)a3oSWb&T7k4=U&)0JufY#Jt>q>k2H*40MA<4x$TlpWhyx}lT z6?ha8WvuhRxV+1#xGh;9~@~N6A`hcXOmuiesUvfn=phd zfC?ONM#}o|q-K&MWhTDZl0l45$uQxT^Uu*D znOvoPbuCp=Oojae*6DGw<+@HL7_Et*91bO%{pxbF zeRO}b0_6T+qAn|&P%~Qha(N@VMX^#?ieKzlswmJ}GWRLNV^5G1;lOLtPm4hzKNNzs`S$iT8}0snNg6QsOS3Un}gHXrAPk znb`FBCSPR7ZJbp?OZph)rl6-(Rk74+vXH!qE22cbm<=UUbfMwXsb^o#1VWb_C0Go( zhvv^MQ1rKoY6TIbiHKXSd|`(F!qe7PUL{38z z3;T(8*L1g_g|?2NWZxWSrCwaWQ`*DToW2is%&0P2*|p7gQ>|`_sQPP!e95fNmeC(lgi-V{|U$f9#Z z8_SfoxOt(7lWQb!re~=w>RWB;gjxxawLvpw6PKjL@b$P(yWkOsliMicU*MRqhIuhj zfWq^!Uflk{#;^c&i~xh}>ia2gdIN;L`cH{{ZtWz}1jlzS7E7)CzV?FpSa5QXTs1Ed zZFeignE4(=?_H3tMc48k5|GdwIMndrYrK{Z1Bfb%dX7IVG9P?eO?kqoC9L zgMQ49fq_;|%{lMXUi&Xn%L%hgOU7WNuVE><*Yhq>!9udEb)w+YG0xJ`?*jAv_0)L< zK~g{=ET_27=$?yR?CQRVxV5h8;-kF4>lD%`^+|*7YhSB*x#|{HjI?vw@84cwE`6Wj z`0f^P2AfkTqau+F^Z7#g)j&x7;!dBf(@xv=n&HGTe1yjo@by!O#PJRcUJzERU`v?t z9&{yMUFTeg5s@Q8_iv*gUZh1Q+{ocpZx+baVH9@fUHJf74ut!kdyD`p7aAYMUd2TE z`fd#wrxW#z9GQLo{AP~%q@iZkaZ5Layl%tH@udxg9BO%sUODwZhxBY&85~fFEHzt6 zone+HpLDh8=dZktC9*N4m92ZdJQfoJZJXtXC<_day|pKOGUx&g!W&&K|D0qhEN66)hFJ7kN>h?AgB~GDJf54vuI+ggD^;9Vsv@mVHF6&cy0|rKCBAm~;el(L58X5H4PAP9 zG^R&1f4rw}MEBlJ=cr}d`@#3VlcG*ZRM-`>b{>fA{A5?X{PnWh^F6(`y#k)RGAm{F zEd#>UY(A?Jf+jZvXTDy(7Tr){Lznh3dY9tPQd-qFZXVT!UNd`drfc0CbDgI1-r8Dz ztoatrL9cV%r|**(>{XP!y#7jLHY#4-cTJ{ejDyrRJ4J8o7O$MI>Ji6 zZxQBr_SZeW&#M9(XU8`tdYgN&v^k9YwON8fU8_{zbfaL@TVXM+mCf`&sd$sb!*Vz2 zjFUVSvv0^e+4E|MZ|~g&!KE09zq2Tpn%g^Dmqpx39~)Gcs6<5mIxqJuZ+vPzf4p-| zvVMGGld7;y`24FEo3JcY*W@lplh%pW-gaP4k4r8rGwdMPA8*-20 z6gImDPVg~$M?aY*PM9I~@0Ir0cK>r?j&I$RAq5-D_^JQ-eeY%+XU~u`AvnE^z*wXM49^Mn7Mi(E??5_MJO&xkp^yMx75qpOd>{c|}CY9wj1E*-DVfkTHpM@P+cZxIvBb2h}j;r-<| z4^3m^;^NFYK0khQ!M}HWTp{$B;Z}(h&rLf%UN5Ej@#Dwj>z(cCKx;isETThTQ(Vi9b^XE@( zo6*6VsGH;MSs!$=0v|o1c5`=69$7tvl}j^k`@G$+4|{?>PQsF*$H zV08qS6+=#k!lg^p6jan#tHOjF?ahM?=to=A{nl2O1r5IZr4T8h*=-ZTtJ@nJb^BW3 zoef{VeidPv6Ofe7st;@z}9r79)Lj*4EL670;cVol^~psh+S% z>HYessi|q!ksX}py71$(xpr<|o_+K=>Hf%P3tPUmN6}_qti^cB6{S|C6%?w?T_5fB zqxA6bX#I4ncz&#{CCm0^bZPj@mzCbz_f1YuzgA69d&Z}q|I@#wxY+Z15WDT~NdrBX zp?4d@Q_e|CTWoq~kwNjXZEU}o-GkNTg}$Di(;eCNqE3?smgmO~;T~M(hIj7SBki@7 zHYqFd!v}6$qx3Gtrn0iK!nU?>s_l$Q?=A+rRm8k~ORO3Vli2;79P#b@PP{d1`S`V@ zL3Cx6t}Z`r)Lu(UL`P(U>4n^Bs8Vfmii++5?IDFy*y zy*3L>EG#uGX*c7vGkA-NikzIBo?_)D1O$HC+H9twi7{!6r}16xF7iBl3-Sy(U!9<9}SuGxb7q*aKU=H^>Sa#jWpM1+ndI#Cy!g{h>D65D~$2g#wxOG z+_({zp)T3kZadj#SzwUX@?O8-hL)h!FFN zied@oQa^Iy#Pgp&HD11aY25IBC&m?vIpXAiPb#A>=3n1Cu$t8{=9F^5pV{#DWY1rJ z{nb4@T)xodF~Psy*s}mlv?#uHc;^IL; zLB%l52aG}w-L0)@D!hGs(r(osVLfz+LDJQPI%2E3wC}f1~y>ba!)0N}yP% z;463(6ttVgV@04oR`DjT?d0O3HfXfZ=A0O5PV5^=E->o- z>MODKyN@hjc;g1Y7k^(~%HRO2G- z0-I9$!i6-nHDhh=3l%F4<=(@jH)r<3!SIN=F0$rOLVi8|`Y z)187TacF<$4nJYOvAgAF)H~-H@=I~YCm7L8@eV1ad^czB9V~iRAFE%yxV1h;{`%05 zcgIbe9x+S0C`F&4{A||p1=p#{wC;Pak;-adU@+QM5H;0Tn#RZb#E0ef3mC?R$<>7} z;$$Lb!h(W9l-u_5V_~orevjy{Urf|W7r{oTT3W3_{tI~voy8p;9qnCVLZ*a$W2%cA z8-w8>1#wTkeSOYri-WhYunsv9zkV6#FOv7|+P!-V4NbMz0`s}2F)%ks?si~Nqp8o!?b#E>#&Nb_A*|H@X z=b}7etCgt6?9{y>OxUuzMr!?1bCT|@A8)tp_48U^8I)R?tyg?0Vtw!6S#PB*8^hNb zW*k4JKASX_kNVIuFvzN?>`6CiJmx=B8+}&#{Q2p2+nP|JyAfjcy>YRTU#hCA+@ku+ z%gg`7$+pktD|5*nhGkC=8co8#v9Y+%?wlc4UB@4|m9UZJ#lsLZ(l`;AKlDq z-D6+hp7tzTjEArU+ekmZZ9P3b4axdZ`W~ynm|J+K9qsMy#?AS8i>0C&N8)vJ9LV{$ zHZ~$|zxm<6V*pNYc-M!;6s^SLCJhhZIQ5!GD#qTfWfZJFo*t+WL|lSn53>B}=|{)+ zaj;NK_kcPPU}V z$^{zx4aZ__YkTOmt~jUK2aRr_)v=7O{gM04>t1`c?ATt|(!#*U$Cq|w68n==Kw7## z+fz+1<82I=xcIVnWNm_0x=DWiafJ3`;$NafZI5)Z6w8J1TF;Ma+O8_U+pqr`0T$3= z00Vs+6JvwV;msdj7$~Qyx&WhxiI#r~VEJu5NSaOJ1j2~2T0oN63gthmEyVNH$y`Nxcxv+ah?_@~4CJ{i|-1Ufol z_VW~e3qW+t$Cv(u8NHB6LpKn~xs#o_$@Q?{X#JP4F-e!%?#IHNSHC{k`G{WNUU&D^ zY{v-&m~<7nF4ydYc0kfC6O(ubM%x8UlD*&oO-fe zzLXsn_3P?FToGb~Si^~zC*8vu8dQ8vpGZqfs|sl6QjLE$vEu{YXo}m9{uDrB$TS}c z><7!3nw)%PGf=J)uj1R~u^s^=AbOmkb<6=cP1L&gFW^KC?!9UC3jjA|m6gZE4T=X8 z=Di;C>+f-1c647H2%(uF7Gv_pb%c;)u}7z2+fI{3n>6!WgtpP~9@js+x-=K36ul8a z4t{F7!?CLec89ICdHZ&rAG=)!Dneor^td%Y-pBM;pg0%Uy$@H!sAU#77h3Io(bkuO>&k$bm)-Kp8F3T z?9r~}OgR=W;;4lXf?4sdGP;gj2FqF4bL!*Aj~W>kOtyp7LT68R-q{0K+?sAGD<>zY z{}mWqBlQ}kg@wg&i*^zHASIxWTR&3{Mn*;wi4gpsZPd`v&NrA>0Nq0~(<_fF^FsV;P6p*g}hD02E5G1--V>eFW^p{TMmCOuiCJ z6CEA>K|ABgIlsL-ckMa@XyB(Z)7RfGt)%of@Zi8;ln1$9QL3b>>M=IDO_%#ZXmhvA zlMH@YS=kR->E)voM(J8p^YcfzxvAl{s-vW$k{zbH`g*xDV`Y)pG{O=N3Gdw?T@_oXPxn$4JT;=H*ACws5E0j64#k8z*__ z>F7$ocx_vsUCjRI(HxvE>9JU${{nexjpV|9e~IbMw{PFxFLnDRMs9`t?c`$rZossv zyQhl3e37|yiNMY&Dg3tgem(qdlGF;a$s+x)PDk zbz%ISX>$^8P(PhP8NtKID_Q%5NPKK8yJhYa6)=h0T%$U-)~7Oqe1HEPSnfC1OP?$* zE&N_ywq0Lc5cF7c!{#wom}tx3^H^@{vT}6fOK~noRD&fjBUG$S`Fl(PbBE3VjXC!{ z7Ovdc?e6aGJQ(5ld$M@P;^JUbRnhUh+}vM{33OezmlKOwW_fk9t)_a36Z-irKwAOOQFYJirP{oG+u0sSFMVqA>ZWtL6s1~SZtD8;F zXiFNx+7+ZVoa#wB#-($(uYwpb|l;et``rWU~HVzG)1_fAT)Iwgsg6SQM%`kEkM(a4fACyn5PL>U*ffs|5CNl(NB=`ACef$1>3p;yk zg+ax&td*I>t-8&JEPpW~stXk`U%)~A4%>8%elAU{Fa6?$VcTpw`VVcmF=L=lJ1Hqi zV!G^*$_I_hU5@NT68P+%iv@$Xwd?#J+AiygeAw|V35~PN=5z4|{gR zdEXw|Yat>X zzy9`@S`z^|bbY%2J3&*yr(Xsf(R1qYY8%!F;n6-1%FM1#T6(L1-7h5&LMq)7IUPy6 z50jq#;5f>#`^cq2xOq0Y=LCo$!E&pye?)Bh%j_*{_^G+~RtUw?eQs|g>u1}^(p{47 z=LFEH*+#MHqg%@{L~z?;!_VyDt;t z!A!|uRGLX6r{3*FBL64&Kj$7VIwv^a@8#vk76fhjWq{~@BhU@iz1alfmW(8lKqjC# z`+9r7VD%S~`q^(dC!M4OKN6!5Q4V~dnd@YIw*t%JO)wHhmGmHHr1M^l~~VKU1b$uJyK?7=1SaHf0C4Suk@EM zU*7BGiSe!{in{hy2@`2IOp72b;B`$J-k)}JlYm8nt&9XWMxm6XBMPul1F}is)F~3U z8NzPh6nck-d=Y6#h=~nJy4rL~(r-ySPX0bXgdX_Hs)fk_1A4caD&bES9gKb@U!u?Y z5SX^Et`2U`8|=)}z`!pE(4-gQj=V&0C7CvT*l=}iW%2&~`%1A2OcA5v&p_0Y^!4?1 zbaW7334W1JN!z{`0eHS-RsBav@%sXV;@slS@{}i<;VAB6NVgJlA@G7mx|5_5^r@faiPpA~qzi*XM&ba&Bct<#J zp8%}l$$rr@XU=Rqbm0lu+!&b=fcsSv!Q1wg`j7PbKG5+(rWxh2CW-(>{zL#e&|Adz zL`VWUG{9Wu&i;@y%Ib_hy9v>XUeJgd%sCbB!ra!))HnLe0wkus?taFluE4GlEtjMY zm>4Q-$qbmBi}?eSQkP+=kKGn}%=s7z+{((BrAMFVj+3uoO29+An3*k_PJlKxu(!#* zua+Rxl}{u?a4Rp66@#IB4OU^lxC6DLqvK4F=IN1v>6w`~pqO&!TTMTiHY?C6O7}A| z9ycyrU-nq9F=%z2$h&}ii=bnXY0Qqb(e2o=W2Q|8nFHb!=@qaQ*(J|qK6k|r5y8P;CD)2tS~Ll# zNyXrO8ex={OReF1rX1qd_YbvYt2q|wT8*(I4%X@6H74nL=|_fL+8w*xg>x+~Q?2aY z8IX{s!M#;y;iwSb!#@FoD5?DuA^!&Ok{M3>m zTGBO_DvzAF96M5D#j&j~lB>k?Zg%v!KZoShzm*L_u>vFywlYaALG{p&ibTU4lBHt7 z*SbBl4~V;(;DO&|6`tw+wLfArF|vg{b3@ssDh{6U!Q5;gC#b`V>ef%v?(w)371@6* zr{hUb(8+b*_aD@L1?E&1S4B9@)dgmahWCw9huRDk2&X4`75+@U?(6S=6VXK@aVb(1 z=X4Zy&)pNSYnu)-tG-eekN&K|$V2QGs_0n>ETYVPmn-C6$RD#IYNV69b^;CsXz$6~ zlPgU?o&LR)yp()9v)YD+N?a?G{_^w>(dTS(f7Y;D*k-x)qoG{D#^&H6IYps|fO^Pm&r>%7j&oHFV3KFs9p4NtmPG3Rj)IIUyC7G0H?Ea9d(1zFM>(cS3 z?Q-)p^v`bdFm%q!@1`^{*%REznLwNU_66oXKyh_qcJOnDNAAk5G<7<+HUPnWEY8(56LAW36W8-of&Mm~)o&9LJsRn&c7&YU0J4^)U0W=M(w; zv8u^`rTQ*PZJ~82-s>3~^B z>^{vsxv(o@%XWBf9e$;$)M#g1*r@2bsLoBh6lIN7XgW;ws zF3TGsA8WfGDqe7X(#jO0D_W2eYjAB>()#%|gPaK6EiEN3G)6QWiNg2VXS*qf``9=W zRMoibWSJ@LYb)DYj%6yWDV{g=NE^&?o=Lh=ThHGtUaOZR7zYm$l`Z|x3O?7CuksVt zJk4}GL4B#KM*ZY%*W)Eets-)nj&E~eJWnz9J&!TBq2cHsUv%f5xG~wClU!aMrrkS6 zdGg}R3{RueipA}56l1=BrYuW6&Uxxc#n`LR43AT3E(&BvJIZB|&S%!^?$3Ug+fO^x za=%X_&(VAe8HuA&U|c>o{;)e3N&fvUyOYG7s$K|Cu(P{&s%R#D)UuEamMFP8L3{n9 z3b)7Ndb!17fkp4axb#xX z7>y^&eD5{itzbHC>P+s+r)mvym9)y6F6Tn+VkNt2ABunzxie1`UcLYDeAk=oifc+r z_BK2L+#U+%`Tz-v%)zV|emyZGFVNibs~L*G8)PT1Zb=8GBY zH>q0Ng0D_aP0N)mEj8{qy@wb`xe1&p8&-QrzdkVex>2X%CCbFzzXqhUdDi7!4^fN_ z{Fld=*zo5XoCyOLhwD`@C5@W3Bzg(ymsWB~3Fu~3d)`%Wa_(p0BA@?fG?z8uK^@<5 ziEHaE((Z79v-!VbHXmpkv<*4Gt-(XwZ2{R*_6&hn>2m-#~1f9Q>#$M{y7GA zqWPyTX0J~#q|(JHRi6nGTX&^i^cnN= za1jl1$-1~)Zhej9!2aI<&QUAKbBF~A+^9Rg!^-{8HmO;o6QtmdXsve@!m~x!&*!IJ zlg3#81m*U>ZYuX&8m?OXx~KnU^etUKg`|JyiI+n`ene}QE%tRG8{+82JIuOIcU^w- z@njJPaRnko?c#6{;u<=yMoi3Q%|yf+9Ls7G6j$_{d8XJwJZINOKYJEIpJz?J2r+Q! zuWRisxt_$DNhcWsaDnqwzAGnfI46mH$*MdG4({9fYS+3!x)#ivh)i!qkh&zw6v^SR zaWxaY+)|0gBH8N&GQL!NcThWxK%wf-i5K=X+WD68?9iiZbRyJANV#PMHu2%REV}pO z%F-9Poi?C6E$H^!D&p^*mq8G4jCW(Gd`dHf>gXR7Rn~F3%5UL??iMN*z5*52<&yz7 z?FJqW*z*tohRg1|u9@Y%qR#t)t0unX7PYUQb84Q$7hT$RCkCE(eLb74Y0pX=*=4Iz zZ_l*!$(}VSsVI39bzlwSOHs$cJnsjaxFwjijtMK5OfNMY1~~ZR!FJl2U3+(Zcj+wz z`LAp(q7L7>3#~=$+6x_e=H0goacVm8D+vbyu>3I>#}}I@BBnd&Z=LY$@Y&uQI65kB zWHrtG^$4Sw=D#S);)pukwjEUD^=CH6OS+987R|3Z^!hWrlrXiLy_>!7NRa&Dn{K6^@}CF&H~9O%e*J#|WB)zV zf1dn*{#xhU`SVnuG1A6_UA5gHXR z+oj+(l>5ibf2Q0GICNq73y0nJM%0Ktg3X@MM~?a6(IdMTYN(a$Yjv=(IfnFhzo?Bg zyOwmn82Ev2+@7e3P!)nRsJeyx``dO)SEwd%iINpbA)#Q<47-_`tA`sCGz)G^q5@A% z&wr<~@{;Z&S0#>!d%f6v;4+0g_|%j3L!4su;(3Sxy$1Jl)~br{-#MnObawrk}{B$d*cm*XZf^mVY=@RM!&O`4)WaQ<|_L=t4(Pe$ODv|ioq2@=3%>_iT|Sq7rW zpGa6i3rW-IM2~b5gwI}XjY1G=xZmAKm<8L$9Va@gmM02oP!IYEB?xZBuqIN{ZoEAd zyfOv9NyE_?#V7?riS;?hWBs>&-LKR6GE0$`BF3}LfmoVWI0#zQ)mvcV=8UtKz04E#Mg|u zI0hvfbOcqYdpuUrxa;@i! z7rE;(YJ6AoqD&x5u69@2CgL*JnkR6Tt=yqpxD9Bun7q4L^%S6UI{gXbNAg%N3X#_c5~B{yv44%yIod)d6Vw-+^H^Sd)4+?oVKMK5M|lms&GCa58( z);1TqJruBPiG(^4`j>A|lvQ1if_nz@PI|OYxVn7}qz+MD+u=C;HK5psFdx`L2S0P?Q)kDCl`xHuT+HcKSP!|Wi z_!9DhTQEpLk81NLW{{(a@(81XIi7oeCF&~f-MH|c`jh7B_HA>g2lvEXXN8cK2tHCYd9Bd=%}N! zA9^>tqix~BmQOHlP_JLVecQEvzZq2$7bO+dk7RwR`I3@4{I!vS!oJ8-pb_d&R5>>M zy?=it>}IPD*^5rEoA?7azS76C-i^AKqf-NfemR8%FxSkqItZQIGma}%g&!Ca}N{{h*78| zY6(Cgi?YJi+uhH#&kE_Xy+!nx{Z;3QDxHAqycr?TOTE5{FK>1iv_b?QjpM=B#VNnr z3KEZ?bfKNNoTw!Pk%I>21Eio%u2TSRbq4no^z!AU8#fN~^77^)24emR)g-~c+jgBk zeH!&hL5ER|*Kgh+YHdS}vINCzlw`3-C~Vi@IEcy^p^3!4B2J0|U$<5&YnoBjH8`3_ zOybW08Hb2zOxDh@AmuKS!wESv!GiMT|Go#Mpe-trgu(^`{nsW!O68$-f)U z?vVVe@WIYQh*K;CTv%UfE^zAgWw%0@u2=&X5(E`15W^*?w=mm%Q0frT7j?)?wW>PP(|Qtqo?y55n`&pUm&nIS$WA8o&t&X`9ODkj*{3~Y zW9Ik0y%|wK5h6~2ssy9A0X<^du}GI;4%=n0w z5r@$iS)(urVr;fusL|bnG*{%AU)Jr#0lGKvSw@Qs;Mn&OKZ4lm@%#5j(<&_*-Um#z znkF+~uaL8e%v3XSpSJ;V!ghsD4`|p=K!8g{O@Ed6NXG*uOrsX&mS|5xqeM?o`YTW@ z&RCv(bN=z8o?@SUsBqc5=z`SRRthrLPuAUq8;EOvgWmEMuj|kcwmz)Yab}la2m8R= zy9l=5*v+nR{D^$m4MJ0U{D@+t*`-K{7F77-4FqhWJc9$N^WVOEcRBT1iQ5ba-b_^C z?>4qWDG6eKV?ux}ty*-qd2jvR?C!09KsF|qtPr=dR^e^S8 zKTQzYr;rcfzyEyw&(h>S|Ng)E`kyoX=Q;k{umAJwac)AB9=0~)_~EfHxv-96gN z&h$domF67Y9YSVBwu#Jjs=LuUA^rrsX`jOu?Wn}xbVGwRI`&!1oN7i&A+fZCim?Cl zameuzUDXn3{Yp08X*9``G|ARmB(r?z{IrY2!s3zbef|gU(feYmrW>C=eff3ir@rP# z`|THKN(FCMAHMeF>&(=Z8sgl%PbIZC|BQuAmuXyNuDV>QW)utNW z1ciuJiNrl>^{TI#hGN%OA{a7{&@j7K6;e)SJYfG_+FeKbuw=+q@3EGo@q`DM1I2QS zpeA)lY`#I1Kc@g9IqPh&@vkMbl7c%GLOKzQpZR0gxt2DG`Fn&?@kw3s3}K0x`5m&T zKD&dwQoUV!@}hqcOCyRUCTsd#QFSCL+DWL@Uc zBeuaGY&N{QtO5eh1=801g~{&Mc!fn?@9R7{CnMz%qhXQ*fzcO2>3sQ(cPCrP0Ti^L zHA1t?hEvF9kahH-Kw4syrkmz%4PT1NTo`uLs6$S1E=1VSh@EzE$yWG8lp7Ks`%=9! zHGV5Pre7yD+2L*Im6YaIe9VV2x!t~bEpC?8Y0FjmSqzgd#QCMN50`>eU0N*|GOLNPlKq94|l(KhDxyT^wpOQN($6l|d+ ztz1N9vm2oNwSK{EWM0!ygZGPngMFZZ<2(xWs7cL^w$vLC;$FM4R$BZNfU^YORyqSE zLKgB6`2&H`(83@OXU}r`cl6@Yo7b=DrEW`Lyr^mt_#FKvmF8yumCpD)VbV;wx&elJ z`0(Ku1}2bMlT9J(6JK6fpJ-Q%IvD{$O*ir$giokF_-0)Q)CFmF*-9SjZv^5&UPm{y zIs+%Uhdz@8oi)gfs}PlB7o(06&E?o#-)?Y_L_>E_Vn8>>LWs}DTvy)3MGWB+4GEr* z@zx^>o%d(1LXEfj)oITcf;VX(Kf8&Z0}{j`5X4r+Bp^-*{4aIv6K7jtY8n_nfy9vt zLWv#PndfQ?S)T1G%C(mZZoB8Nuemb2O`i7YOKTe=gboOZh+~9;33p8sO-Rn(q1%GE zFX-6$uYKKY+ler#`SHdURR0W{K4{)BG!(EUUjp8!YL>!32*Ek3uEfj&G#L__v(dH; ze_Xk5V4wumNrvNA2Y|}4Q+9?|Q4u*5F-W9v1}K*_B&hqtlr|EbGMygl?pVWbkQEV# z*L8lBW7Y*FP-Wof8Aq@cjV3(P%Y#}KO-Z`p7|jq3-Ohlv^Ks&f-N~^5RV5{qcYr(n zp|b48l>sW8MpH|PYVZcgwJ4<4R|JV#c*gB4n*pM!1>jVSw6?Z}uoH?J`VSwUoK|S}M_LqxISBJFp#5T87i2;BC%?T@mR}YOQHNaPYZ{7cT~wrd!;H z5Wv^hmoT{g&zG+W=?+ZftoTSB6#f2^+^0JcHZq`lq(>#N_;V5)T17BfBs7?8J%wRa zEiyARtB<)cF*2I0nWbvH*GlJvUh<`c^RDo!^DxoMMZ@xE(LkYC;F_0vckNRC_4)es z>l+~JKNnqb+_JNMB@1AkgvxAZyN8Cx3(>BYmeB6*ZntD9OBU$m;{%>Qrz(UHAD;Is z1QIW!99ID`(Pc!S$^`XfBbX1Npt%|8?EUQ79^#gP=Lnn*X#@3sk(63?shTLM9j5@P z{l->(clu zLl2`C>`+4Ifz_W#2P#j4;ibL#oj$Lc+&R#kg3)~8dw!)TgphlaD8~!lGoCOjgHw`` zU)8@d2BFUjOBN_=i8dOVbsPP@ZG5?fNR0_0CCD6!^_3Y)hxmobNfLkfNx!oIp1|49 zc(m0Vv*1_TqDP{=Lre{yo9SLxaLq0$mS~+%J=5&`X{=9zX+C9f`Nw)vTfTwD_eg1W zb|j+T3fAWMp7ZLKgT1I+QwsogAbs%AYkQQ9xb~nd-M|#YEC38 z1db-Qp<}0W>8PLT9zv&}r>94#x&CBLjq3V_)h|WKEB{;h0K5%Bd3?H6FQcFkgOu6H z#l;3$JY1kk_u};Qv<>~}9H1PKxi!=|B;Zq7W#z!d1I4K4mjd}#4Jby4ol{oc1=R~V z+c|@1NAo#(9X!&4*)Nx*#zXDusmBOlF;~h)o}H4^H;^u9o|@VAMJs+!lk&UCnQhm; z3MLEY4_1(EqSi8B9P(M{8zx_V%U1x}?Tgka`O60Bis}vx)lAg|{D90y5Nd#JkvJ71 z-WRFsyQ9fWKcFc?E3q~Jyk*nMJ>^yNm^O%S~r3xBu^vi z9gvWC2^^aWMH5t>x7t1@imsPyod>WBRD0c(C$c&?K`(W47ipbdA`FOU&#gRKfpCTs zbGoF?QNjCb=QitRJT#!k(&26$i`&Y+e?LfcFNA#~5VYOUF6`gM`eZ}MPlV3Maa4ubfFZ?XfR*w z@T*C8x&I_ljAF7i_7Iav?$((hf4{22##Gh8HcqRefz~%tyEAS0Dj(0E`>2&Dyg!qW z>7onl3~;!BfWV~)FG>`@p4t+ zoOM*Q9i`=Kti%ioxC=o#^{kFZGZ!Rh%^~)3Hsxacp$hkroqpPjIRh0HoC81 ztB#EW;B9`jXQh21`mo!dZ(U}5)%H5Z6i&db_0twWf0i&G?_GQ7WuQkT5$-tE+S;rw zJGj_q2l%)BPnabyChO(d83M=jKT+z9=CbTwlJjT&WL|%y>lh-@8#qk7K>!#2cy-O~ z_S)i2(mzSSp=ZVymN1miyQY)fMUa^Yg4aZ zAewl!KPH2`OttFyk~3Vj0KGIpCXvt*bO9Oad$iADd%gm$B;cx7QuwoH(r6VSM2(yfKw6u~aAR&=BGaT+`FncjYbPpOI5xsxYJKw7M=M|3eFKEnS!3rE4T+ z9(A4*CgLH4PEWv!L+wcDDv8hrW*f;S;e(KcZ_`--flGB2{G%J>-vm8^9k$_b^mRU@ z<$}zyS#~c0ZhYs4mOvAbjF}l-ires7j%2$9d_dIgusVL~nJu2Lt6c$el3caVg?hXx zz0#h%V3bq(fW#(M-$!x<2qKK)eC6RM23NW2p}&`{tuU4 ziUc4tB2G}7dPwd$x4>ZRyLST6fZn@z4@H@NWO{ZQ>i|s$j~&~EMOvC4s{lMQ@P2}R zQLhUu?$0Ljm!IGiINk{vi>!6%nX?*&&BTAb{HQV!`dz{d%Pz4J&>DghG8u zWM-gTLuUvh0wX^;H^;`!{b+~ON=ofXOR1Gf3OIP;@d%kDvlIJBK#VmRG_E?e7vPLSbXxwgDs;RtxUDP>aI-%BtIS)o?97?6NPfWrDXYlhr}qzk@Xgg9?EgD* zb+KA-X0?CWEo$OY*l}4btEmLNAx%dO6XxPB57%MU+9R%b{;|5KzO0RsETobOf~_B zM@~$U%fkkQ#|TD7lMV>BYV?iTzj_9aO&MP1lX=?-wj)P|OX&2EpY4@eo7w^HQgMgG z+)&M0*|_wX&ixFz)KqEN@}K3jJoK0f|O z)cVRzR5wgPxpE#oT8qA%eeG+Y5syPWj@gq7v_mhz2&}y!XCgl#YIXD`c?}6ZtG<30 zm39_Ubbzy!SCp8#|JLvNb6uBhGiX9+nz+3nE~`AznTH<0V{kh}8&Ew=Iv65%g6Pz7 z=*oA$sHj-rw081j;9JP6O)6gq1cDVia_f5_n^vw>0Q_%e!L&UzG7x(<~c*I zjz|E^$7m9Y7KMa_fGXpKJ518U6pwk|zyD&8hE!yBfh6o?aJ%d zZ>kFW^lsw$6XX@l{)>p@D_5@2($TS7T3Xt!A%WZ10f_j~*{_0!msbID|Lx=JHqOq@ z#N-yNwiO6v`?Ki&Sg+!zrlh0{B_0j&0S#gP1N+1}(X~3WfP%-&J`y5QWeRyO1H(}2 zIM|ES84n66U_`vouEdc1jTX# zoZD=Zwj~EQ_lF{T^o^V3&xh#m00qIE%vUT+B5C<)H~pXmzCn!P5M?`u7_OfF_}K)7 zypz*!RhA3anetudhTnnoK85xiql9wglZ?bOIxgicqB};VVPUm8><8rfEn^OF=ZYxV z7}up{`!7td3ftvAHh+VbtiZQKHd{}zL*jreI<%7|o^RQ9&CW zs;a7?5#!soZbjG5z>NDg77{6=mB)0*M*WHYSes*Gn^m&wV!e5DG584;Rn>Q>7A{;w zH6D3FEJ*pZx&ih}FJ7T)2<`AiBnyOF011EGy7wzN@9({SXoY@76csEzmczGh+m?=| zVM16{rR9NLgsk@m!W5ob6OCQ}+4$&VbZrs4DooTi2Kjt4ig=CtJk8K2LEQVN8{c4$V!s7p)VKRiV6_TJ-iELs}*Zdo8CV^D#XMJsiA zb+wvFhXS6?CG!m?QwWV=~Tulc*HIIJ24zhb?Ev+z2?_5a1N(r@M z;4ElRQ)mFR(u^MCp${4~zPUsyTC{UEIff7PP;a!S3 zI8fJ1$sazbpsJJX;=vmtM@vilcz)faJbkY0*YPYLM^RFD z4B_}&GW4 zKj#fp|60KT2n7fG`}<$$4n#{c=ogzSN!v-Ju#x9G7G%!W2I28wFkxbtn}$t^N0?MX2Mh9DF$)y z*RWIEfc=#IWKJloozq9{0Zx7gQ_|I?uw{K7V z+6d78KM($|w|8nONu>XSx1{K8BvQA-R$V;Ppb2>Z$5h*+`XLo_a~{eKIPvAbGB1}` zR1i*TU|`@fJ&AON?%bVyB|TPU12&icCXr-5rpI_%i$#dJ{Z4uK@L|7|$c?m?JxolA zu(5g_JTyoek>!e}W+?YwKNP_m@fZZ0=oVoCIgZKuX?(`B2ATcM>1b&I@6Qf*6%?Sy zR#i_g3H>6!&?{q;rGtkTF^X8d!jpI!eoev0a2z^x7ET(~F4S$53=E=CWE8ql_Vj$S zxOMM+XsJ}MTsc(C-#<7Qi~4@LWtU#yGsML9cAdtirgEdSIFyqgK7Nd6K?S}AR<7u= zdXI2bURn99xaVm`Mg~$1RVAfYc)EjbrqyY*&nT;?Jj2+NZ%N9E*^(bAycCIk_;4$O z_dz^&0Qr{;0)Eh80)xP(#$(&wzK!R5#G>QWu36WSgP&gs`?wxtOyvxo|V& zbMpq0g-rv9U(O%-VW9hRUG{lo+q0saG2 zG%AQLEIbt<<)H_?Jae0(ynJwRj}lJJ$;E~3_ZH(LycafmK>bidq@*k3n8z%9)eu5? zd_uw%NPFx{nH-Pu@W{V=_YO$HWHuMYlNfOEvVow`8z8t*RZ%%AD0n5%4$>w(1L!>@ zK~Q5oPfkt-ut8A0Vetmw68NCJtgIHjuyM#m8XFpZ;eiAE#g}jeRW-F3Gg(@Uk5~eDuJ9Q-KZW zB_e<**}-LgG!9GpE+%I9iTL=_!x&sVE(H?u81-3p#YilEEi8~gXR^Z}79j0OM5za} zm{ZimS5iXBzS}~8cT!^4e$6VY zjOPmtSK%=xW{i_!)=a1x=b6M_+?C7zS^gq4G!DKuJVRjv=@i#CU4_^y zfydE=wz#z9Qi-l6=8EHZ;L}Ca%YemD8(Mu96_pI+i-;(j{j;czpL+FosZoa(S&0$&Oro!v@ zQZ(RCRrK_DEiETp2I>R5hxD2{Y^X4t(U{M6q7e#!f7zY0urCe?2^~BpK?!yVSZ0St zdO#8ds4kSRUVR0R(}+?WteeQRRPf9l!aYIaLbX7dMm zE<0NbW~X9o{2ovG+p}xe>)c$4v6i$8;P7LS+@gHRibsGjbN?ApU(PTTPCZS3}ySaz)>RFX0kDoQoGC}xT1 zFsmcmnyp4tI!L1fNqsg%Q7K8J(^NW4Wkx4l>0?A?ry-?C$Jq*aU}?KIC&(FKm4(fgd%h|Qgz$76%~Qw97v+bYe3hAspUL$u zJpuuk!Zlz=ZGHW7NZ_};7UkQUa1I0nlpxe7r?@g0E0gOE*Gd0_eFZ_l%p_tx0$PTJ z@HRuTM{#>rf?!X3tbg6| z>K!SO4X^cmN?xU9e=BO9nJ}s5(eo-iUd;#jDKJED0( z3Hr!>`O}wwi-cr2sl~c9xiAh&gH0pQN_-O9w3TT7t$lHyQ7E@Ww<>_D{wdb^1dKl2 zm+ZA4k7W>W-JTss?R= zdZ-2vEKbu!GA`d^V=ZyhqAh)`-)&hTUcF$B6V83~mowc$el1_h%w0$SPS;1hE8PjcX)VYGoM_^gIiL|#KeI( zt1^0o^o1+Q+DBgo@p}rXOKZc^z{{#oQ0L8oFQCqw^XB z=0Lf#`RS*0B0gehM51&SktoK8x(Qt;T2#2)zmYk!X&+uGp?aMN*?Aa3Xdi^J*mO7@ z`cAF9qCbXd0Bt>R7uX<-4^)h~5J{zLj1_{?B&R1WY8Y-z76d@@ence&5;UJdX&HqU zdL8b|y9!PTBnsPyh%QfLf?>nco=YwW9wziw(~)sJi`Z)Ztv>>h65;}(7`@pq-G{nm zj==}MpE%L-5e5R6h z#HbF}*kLFmn=wO=C|g&t-Z~Kb;ILln9fj?>2M>HVK-Tcfql3MWG;T~m1H0YOF*G`A z4`bET5N+Cq$Om9>Unwi+V2~wU@Vgwe;GH5!pS(~h$Lo2K1<(=EKQyu-qj97o;<|z& zWO+;iV4$L7@9dnC9z7(h_vnRg=n)!a2Q?RY}L9NInN*m-+`kq6F3X3Y08W2Zd z4~Ye{D#7(xrJ6hAJU4>&dsnizo>ggp4rYK-U7l-|3s6iWYQW<>IUh%z)vIkG3ozVNbfVzp2trlB_VzKzcw9PKF8w6KE0&l`l58l2z;j>bkcYI_P zrVlY6)dhL^VO^bJrt?IEr;kqomZ`Qzsl5~e`zj2>86FW`q;#s{&%Nwc0X-H^McjT;kB*qp09Tv{ zC1a=ry<2G$=V8bs8KGk6@y49&>@|*6y4?~9*W+o12dqjETCjENQMA;iLoROs=rsr& zXwjU$<}1O>Ckz0Rt4pQ1k~v#2iDyD%pt_VlbK@gOhG5VhbcMKtgai#Dov*4lPD;zzohGRiPeAH^dD2+}5+~g=FMVZ2A}THMJnM z!i2U5I3LXiH$ZKyE^lZkuSP!O{a7OpCBq$l5Y;7}wWk-U<56{AIl2=DYYqr?U%)=^ zQ>e$m6wm`hXA_F7>8Rg(Lceh+*ms=&2)`xoA1*!uIXgiQrmCF-ZS z2-|LiVy>Gut`&-sX#OtK{j*k&89%wSZYxURwKj)2|Jhc^?}_IVcihMMQQNe2&RZ( zg~%I&<}9x!^2{d?n{sfn*K?7)N-{8C;-?Y^iPWI%Zpja;IheY(VDVxuN(vsBqC&zT z0thPG*Z!NTrbVI1DUtsppt`!6bLTZiKs?>7WR{-$G!tDG-r0zDU9O7uTv7QpbTMWj z4SQmwrUTjp;Ig_3HVDoh<%vw-!1uj@F}!J1$1;xTf_lg?dFa!HUL#9$sD;L;?)uGe ztfd7K_%PZzxL|r57XxAf`g>aaWI15|yPK0qxnuz9mN7txH4%}qGyhnB~8kKf|hrNIIt^r0jYwvN0 zJ1m6#34E|2G-~N9rLcO0Hc|^2x~x1QnM3g@OeTZY34aCl)LOp$yBM?4M0t66UhwlG zvfd&ZLM!PwKpq?Uy(5N&xwy>*hpwZ3n4FueJfYytRba4`B~=%(G0(%oRk4smN|UH! zpq29tP9K6-jQKXkYxcE@=5gwCh9B4)qKmWG%ZP!y!!rpI@{A93_xHEvu6IEI#8~|_ z`D&%J!p>Oyh3*H{2;uoF6_w!DF{o+UG+LDDwwW*@Xy{znnX_KDX1sceg>naU;WNj_Wad?nwM+y7an%r=pf3elw8% z2(m_RWawkTpk+y^pqJ?$mnMPnPK3@e$MTmbFh4qO0zUA$bDnK*a5)fCnDtn~3`%uf zgXW|^_>|wT!|~``7#^8H;nCM~cZro2g$VLBSGl~8SO(7ESiQ^=dz|n+A7jBoUY(4L z$RzS_H@o+H9z9?wBO|kBSK4NbJ8a%N58}D|i914l1&CI=zo~;F$BbVrBSIT}(DMLB z;Uv0sv1~C2Q*7*)F^7uTa+c|{!Dk||hg^-2 zsQ8n*T#+_v<@fX-`5c1fhwfhxSSH_m& z2i|C`au&63b^@iz+Lao(q2&2NsNKW8(E1q*%UhP;6$*t4P73O;7Ts;3I({A986Wf1 zLbf=w^#Z#&PR7Tar#8yipVqdkUKhS1|J)?Up4qG>dp`eP<$qj#a9=YfHXy4_VbA=7 z`o z)787rx~DID@p^4{=U-$WKmU`S;FUaSq%M2iEIF#|ovg@4`cu`!=ThW^-^cs=@{jww p>h+p+jNG!%Gl8kkqR6yqr5SdK+muZ=y})-Vbc>x^@;B`Z{~z&8*4zL9 literal 0 HcmV?d00001 diff --git a/lib/content-services/src/lib/i18n/en.json b/lib/content-services/src/lib/i18n/en.json index 12c1ed7a8d..f64214f76d 100644 --- a/lib/content-services/src/lib/i18n/en.json +++ b/lib/content-services/src/lib/i18n/en.json @@ -22,6 +22,17 @@ "NO_LABEL": "No" } }, + "ADF-NEW-VERSION-UPLOADER": { + "DIALOG_LIST": { + "TITLE": "Manage Versions", + "CLOSE": "Close" + }, + "DIALOG_UPLOAD": { + "TITLE": "Upload New Version", + "CANCEL": "Cancel", + "UPLOAD": "Upload" + } + }, "ADF_VERSION_COMPARISON": { "CURRENT_VERSION": "Current", "NEW_VERSION": "New", diff --git a/lib/content-services/src/lib/mock/new-version-uploader.service.mock.ts b/lib/content-services/src/lib/mock/new-version-uploader.service.mock.ts new file mode 100644 index 0000000000..7bce9cf8a9 --- /dev/null +++ b/lib/content-services/src/lib/mock/new-version-uploader.service.mock.ts @@ -0,0 +1,182 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const mockNode: any = ({ + isFile: true, + createdByUser: { id: 'admin', displayName: 'Administrator' }, + modifiedAt: '2017-05-24T15:08:55.640Z', + nodeType: 'cm:content', + content: { + mimeType: 'application/rtf', + mimeTypeName: 'Rich Text Format', + sizeInBytes: 14530, + encoding: 'UTF-8' + }, + parentId: 'd124de26-6ba0-4f40-8d98-4907da2d337a', + createdAt: '2017-05-24T15:08:55.640Z', + path: { + name: '/Company Home/Guest Home', + isComplete: true, + elements: [{ + id: '94acfc73-7014-4475-9bd9-93a2162f0f8c', + name: 'Company Home' + }, { id: 'd124de26-6ba0-4f40-8d98-4907da2d337a', name: 'Guest Home' }] + }, + isFolder: false, + modifiedByUser: { id: 'admin', displayName: 'Administrator' }, + name: 'b_txt_file.rtf', + id: '70e1cc6a-6918-468a-b84a-1048093b06fd', + properties: { 'cm:versionLabel': '1.0', 'cm:versionType': 'MAJOR' }, + allowableOperations: ['delete', 'update'] +}); + +export const mockFile = new File(['fakefake'], 'file-fake.png', { type: 'image/png' }); + +export const mockNewVersionUploaderData: any = { + action: 'upload', + newVersion: { + value: { + entry: { + isFile: true, + createdByUser: { + id: 'hruser', + displayName: 'hruser' + }, + modifiedAt: '2022-05-24T10:19:43.544Z', + nodeType: 'cm:content', + content: { + mimeType: + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + mimeTypeName: 'Microsoft Word 2007', + sizeInBytes: 11887, + encoding: 'UTF-8' + }, + parentId: '422538ca-ea4b-4086-83f9-b36e4521ec7f', + aspectNames: [ + 'rn:renditioned', + 'cm:versionable', + 'cm:titled', + 'cm:auditable', + 'cm:author', + 'cm:thumbnailModification' + ], + createdAt: '2022-05-24T07:26:44.429Z', + isFolder: false, + modifiedByUser: { + id: 'hruser', + displayName: 'hruser' + }, + name: 'Test3.docx', + id: '42ddb84d-fc96-4b45-aa3c-f24ca997d602', + properties: { + 'cm:versionType': 'MINOR', + 'cm:versionLabel': '1.1', + 'cm:author': 'Amedeo Lepore', + 'cm:lastThumbnailModification': ['doclib:1653377205499'] + }, + allowableOperations: ['delete', 'update', 'updatePermissions'] + } + } + }, + currentVersion: { + isFile: true, + createdByUser: { + id: 'hruser', + displayName: 'hruser' + }, + modifiedAt: '2022-05-24T07:26:45.337Z', + nodeType: 'cm:content', + content: { + mimeType: + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + mimeTypeName: 'Microsoft Word 2007', + sizeInBytes: 11949, + encoding: 'UTF-8' + }, + parentId: '422538ca-ea4b-4086-83f9-b36e4521ec7f', + aspectNames: [ + 'rn:renditioned', + 'cm:versionable', + 'cm:titled', + 'cm:auditable', + 'cm:author', + 'cm:thumbnailModification' + ], + createdAt: '2022-05-24T07:26:44.429Z', + path: { + name: '/Company Home/User Homes/hruser', + isComplete: true, + elements: [ + { + id: '4e2284fd-9457-4914-a612-ea844e87f53f', + name: 'Company Home', + nodeType: 'cm:folder', + aspectNames: ['cm:titled', 'cm:auditable', 'app:uifacets'] + }, + { + id: '75a5d2d2-6edb-40b6-822e-499f5e8beffb', + name: 'User Homes', + nodeType: 'cm:folder', + aspectNames: ['cm:titled', 'cm:auditable', 'app:uifacets'] + }, + { + id: '422538ca-ea4b-4086-83f9-b36e4521ec7f', + name: 'hruser', + nodeType: 'cm:folder', + aspectNames: ['cm:ownable', 'cm:auditable'] + } + ] + }, + isFolder: false, + permissions: { + inherited: [ + { + authorityId: 'ROLE_OWNER', + name: 'All', + accessStatus: 'ALLOWED' + }, + { + authorityId: 'hruser', + name: 'All', + accessStatus: 'ALLOWED' + } + ], + settable: [ + 'Contributor', + 'Collaborator', + 'Coordinator', + 'Editor', + 'Consumer' + ], + isInheritanceEnabled: true + }, + modifiedByUser: { + id: 'hruser', + displayName: 'hruser' + }, + name: 'Test2.docx', + id: '42ddb84d-fc96-4b45-aa3c-f24ca997d602', + properties: { + 'cm:versionType': 'MAJOR', + 'cm:versionLabel': '1.0', + 'cm:author': 'Amedeo Lepore', + 'cm:lastThumbnailModification': ['doclib:1653377205499'] + }, + allowableOperations: ['delete', 'update', 'updatePermissions'], + isExternal: true + } +}; diff --git a/lib/content-services/src/lib/mock/public-api.ts b/lib/content-services/src/lib/mock/public-api.ts index ac5c588d4f..93938de2ec 100644 --- a/lib/content-services/src/lib/mock/public-api.ts +++ b/lib/content-services/src/lib/mock/public-api.ts @@ -22,3 +22,4 @@ export * from './search.service.mock'; export * from './search-filter-mock'; export * from './sites-dropdown.component.mock'; export * from './search-query.mock'; +export * from './new-version-uploader.service.mock'; diff --git a/lib/content-services/src/lib/new-version-uploader/index.ts b/lib/content-services/src/lib/new-version-uploader/index.ts new file mode 100644 index 0000000000..a7e30cc675 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/index.ts @@ -0,0 +1,18 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './public-api'; diff --git a/lib/content-services/src/lib/new-version-uploader/models/index.ts b/lib/content-services/src/lib/new-version-uploader/models/index.ts new file mode 100644 index 0000000000..cbf55165cc --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/models/index.ts @@ -0,0 +1,17 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './new-version-uploader.model'; diff --git a/lib/content-services/src/lib/new-version-uploader/models/new-version-uploader.model.ts b/lib/content-services/src/lib/new-version-uploader/models/new-version-uploader.model.ts new file mode 100644 index 0000000000..bbea817f34 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/models/new-version-uploader.model.ts @@ -0,0 +1,55 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { MinimalNodeEntryEntity, Version, NodeChildAssociation, Node } from '@alfresco/js-api'; +import { NodeEntityEvent } from '../../document-list'; + +export interface NewVersionUploaderDialogData { + title?: string; + node: MinimalNodeEntryEntity; + file?: File; + currentVersion?: Version; + showVersionsOnly?: boolean; +} + +export type NewVersionUploaderData = VersionManagerUploadData | ViewVersion | RefreshData; + +// eslint-disable-next-line no-shadow +export enum NewVersionUploaderDataAction { + refresh = 'refresh', + upload = 'upload', + view = 'view' +} + +interface BaseData { + action: NewVersionUploaderDataAction; +} + +export interface VersionManagerUploadData extends BaseData { + action: NewVersionUploaderDataAction.upload; + newVersion: NodeEntityEvent; + currentVersion: NodeChildAssociation; +} + +export interface ViewVersion extends BaseData { + action: NewVersionUploaderDataAction.view; + versionId: string; +} + +export interface RefreshData extends BaseData { + action: NewVersionUploaderDataAction.refresh; + node: Node; +} diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html new file mode 100644 index 0000000000..17eada95a3 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.html @@ -0,0 +1,33 @@ +
{{ title | translate }}
+
+ + + +
+ +
+
+
+ +
+
+
+
+ +
+
diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.scss b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.scss new file mode 100644 index 0000000000..ffc32eec5d --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.scss @@ -0,0 +1,79 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.adf-new-version-uploader-dialog { + &-list { + height: 400px; + } + + &-upload { + height: 500px; + } + + .mat-dialog { + &-title { + flex: 0 0 auto; + font-size: 20px; + font-weight: 600; + font-style: normal; + font-stretch: normal; + line-height: 1.6; + margin: 0; + letter-spacing: -0.5px; + color: var(--theme-text-bold-color); + } + + &-content { + flex: 1 1 auto; + position: relative; + overflow-y: auto; + max-height: 100vh; + overflow: hidden; + padding: 2px 26px; + } + + &-actions { + /* stylelint-disable-next-line shorthand-property-no-redundant-values */ + padding: 8px 8px 24px 8px; + display: flex; + justify-content: flex-end; + color: var(--theme-text-color); + + button { + text-transform: uppercase; + font-weight: normal; + } + } + } + + .mat-list-item-content { + padding: 0; + margin: 0 16px; + } + + .adf-version-list-container { + .adf-version-list { + height: 250px; + overflow: hidden; + padding: 0; + } + + .mat-list.adf-version-list { + overflow: auto; + } + } +} diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.spec.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.spec.ts new file mode 100644 index 0000000000..2c5b1b8ea3 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.spec.ts @@ -0,0 +1,209 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { TranslateModule } from '@ngx-translate/core'; +import { setupTestBed } from 'core'; +import { mockFile, mockNode } from '../mock'; +import { ContentTestingModule } from '../testing/content.testing.module'; +import { UploadVersionButtonComponent } from '../upload'; +import { VersionComparisonComponent, VersionListComponent, VersionUploadComponent } from '../version-manager'; +import { NewVersionUploaderDataAction } from './models'; +import { NewVersionUploaderDialogComponent } from './new-version-uploader.dialog'; + +describe('NewVersionUploaderDialog', () => { + let component: NewVersionUploaderDialogComponent; + let fixture: ComponentFixture; + let nativeElement; + + const cssSelectors = { + adfVersionUploadButton: '#adf-version-upload-button', + adfVersionComparison: '#adf-version-comparison', + adfVersionList: '.adf-version-list', + matDialogTitle: '.mat-dialog-title' + }; + + const mockDialogRef = { + close: jasmine.createSpy('close'), + open: jasmine.createSpy('open') + }; + const showVersionsOnly = true; + + setupTestBed({ + imports: [ + TranslateModule.forRoot(), + ContentTestingModule + ], + declarations: [ + NewVersionUploaderDialogComponent, + VersionListComponent, + VersionUploadComponent, + UploadVersionButtonComponent, + VersionComparisonComponent + ], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: { node: mockNode, showVersionsOnly, file: mockFile } }, + { + provide: MatDialogRef, useValue: mockDialogRef + } + ] + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NewVersionUploaderDialogComponent); + component = fixture.componentInstance; + nativeElement = fixture.debugElement.nativeElement; + }); + + afterEach(() => { + fixture.destroy(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('Upload New Version', () => { + + const expectedUploadNewVersionTitle = 'ADF-NEW-VERSION-UPLOADER.DIALOG_UPLOAD.TITLE'; + + it('should display adf version upload button if showVersionsOnly is passed as false from parent component', () => { + component.data.showVersionsOnly = false; + fixture.detectChanges(); + const adfVersionComponent = nativeElement.querySelector(cssSelectors.adfVersionUploadButton); + expect(adfVersionComponent).not.toEqual(null); + }); + + it('should display adf version comparison if showVersionsOnly is passed as false from parent component', () => { + component.data.showVersionsOnly = false; + fixture.detectChanges(); + const adfVersionComparisonComponent = nativeElement.querySelector(cssSelectors.adfVersionComparison); + expect(adfVersionComparisonComponent).not.toEqual(null); + }); + + it('should not display adf version list if showVersionsOnly is passed as false from parent component', () => { + component.data.showVersionsOnly = false; + fixture.detectChanges(); + const adfVersionComparisonComponent = nativeElement.querySelector(cssSelectors.adfVersionList); + expect(adfVersionComparisonComponent).toEqual(null); + }); + + it('should show default title if title is not provided from parent component', () => { + component.data.showVersionsOnly = false; + fixture.detectChanges(); + const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + expect(matDialogTitle.innerHTML).toEqual(expectedUploadNewVersionTitle); + }); + + it('should show default title if title is provided as empty from parent component', () => { + component.data.showVersionsOnly = false; + component.data.title = ''; + fixture.detectChanges(); + const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + expect(matDialogTitle.innerHTML).toEqual(expectedUploadNewVersionTitle); + }); + + it('should not show Upload New Version default title if title is provided from parent component', () => { + component.data.showVersionsOnly = false; + component.data.title = 'TEST_TITLE'; + fixture.detectChanges(); + const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + expect(matDialogTitle.innerHTML).toEqual('TEST_TITLE'); + }); + + it('should emit dialog action when upload a new file', () => { + const spyOnDialogAction = spyOn(component.dialogAction, 'emit'); + component.data.showVersionsOnly = false; + fixture.detectChanges(); + component.handleUpload(mockNode); + const expectedEmittedValue = { + action: NewVersionUploaderDataAction.upload, + currentVersion: component.data.node, + newVersion: mockNode + }; + expect(spyOnDialogAction).toHaveBeenCalledWith(expectedEmittedValue); + }); + + it('should close dialog after file is uploaded', () => { + component.data.showVersionsOnly = false; + fixture.detectChanges(); + component.handleUpload(mockFile); + expect(mockDialogRef.close).toHaveBeenCalled(); + }); + + it('should close dialog after click on dialog cancel', () => { + component.data.showVersionsOnly = false; + fixture.detectChanges(); + component.handleCancel(); + expect(mockDialogRef.close).toHaveBeenCalled(); + }); + + }); + + describe('Manage Versions', () => { + + const expectedManageVersionsTitle = 'ADF-NEW-VERSION-UPLOADER.DIALOG_LIST.TITLE'; + + it('should display adf version list if showVersionsOnly is passed as true from parent component', () => { + component.data.showVersionsOnly = true; + fixture.detectChanges(); + const adfVersionListComponent = document.querySelector(cssSelectors.adfVersionList); + expect(adfVersionListComponent).not.toEqual(null); + }); + + it('should not display adf version upload button if showVersionsOnly is passed as true from parent component', () => { + component.data.showVersionsOnly = true; + fixture.detectChanges(); + const adfVersionComponent = nativeElement.querySelector(cssSelectors.adfVersionUploadButton); + expect(adfVersionComponent).toEqual(null); + }); + + it('should not display adf version comparison if showVersionsOnly is passed as true from parent component', () => { + component.data.showVersionsOnly = true; + fixture.detectChanges(); + const adfVersionComponent = nativeElement.querySelector(cssSelectors.adfVersionComparison); + expect(adfVersionComponent).toEqual(null); + }); + + it('should show Manage Versions default title if title is not provided from parent component', () => { + component.data.showVersionsOnly = true; + component.data.title = undefined; + fixture.detectChanges(); + const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + expect(matDialogTitle.innerHTML).toEqual(expectedManageVersionsTitle); + }); + + it('should show Manage Versions default title if title is provided as empty from parent component', () => { + component.data.showVersionsOnly = true; + component.data.title = ''; + fixture.detectChanges(); + const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + expect(matDialogTitle.innerHTML).toEqual(expectedManageVersionsTitle); + }); + + it('should not show Manage Versions default title if title is provided from parent component', () => { + component.data.showVersionsOnly = true; + component.data.title = 'TEST_TITLE'; + fixture.detectChanges(); + const matDialogTitle = nativeElement.querySelector(cssSelectors.matDialogTitle); + expect(matDialogTitle.innerHTML).toEqual('TEST_TITLE'); + }); + + }); + +}); diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts new file mode 100644 index 0000000000..e03c667f69 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.dialog.ts @@ -0,0 +1,83 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Node } from '@alfresco/js-api'; +import { Component, EventEmitter, Inject, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { NewVersionUploaderDialogData, NewVersionUploaderData, NewVersionUploaderDataAction } from './models'; + +@Component({ + selector: 'adf-new-version-uploader-dialog', + templateUrl: './new-version-uploader.dialog.html', + styleUrls: ['./new-version-uploader.dialog.scss'], + encapsulation: ViewEncapsulation.None +}) +export class NewVersionUploaderDialogComponent implements OnInit { + + /** + * Dialog title to show into the header. + * If data.title is not provided, a default title is set + * */ + title: string; + + /** Emitted when an action is done. */ + @Output() + dialogAction = new EventEmitter(); + + /** Emitted when an error occurs. */ + @Output() + uploadError = new EventEmitter(); + + constructor( + @Inject(MAT_DIALOG_DATA) public data: NewVersionUploaderDialogData, + private dialogRef: MatDialogRef + ) { } + + ngOnInit(): void { + this.setDialogTitle(); + } + + private setDialogTitle() { + if (!this.data.title) { + this.title = this.data.showVersionsOnly ? 'ADF-NEW-VERSION-UPLOADER.DIALOG_LIST.TITLE' : 'ADF-NEW-VERSION-UPLOADER.DIALOG_UPLOAD.TITLE'; + } else { + this.title = this.data.title; + } + } + + handleUpload(newFileVersion) { + this.dialogAction.emit({ action: NewVersionUploaderDataAction.upload, newVersion: newFileVersion, currentVersion: this.data.node }); + this.dialogRef.close(); + } + + handleCancel() { + this.dialogRef.close(); + } + + onUploadError(error) { + this.uploadError.emit(error); + } + + onViewingVersion(versionId: string) { + this.dialogAction.emit({ action: NewVersionUploaderDataAction.view, versionId }); + } + + refresh(node: Node) { + this.dialogAction.emit({ action: NewVersionUploaderDataAction.refresh, node }); + } + +} diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.module.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.module.ts new file mode 100644 index 0000000000..b1360aa736 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.module.ts @@ -0,0 +1,46 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { MaterialModule } from '../material.module'; + +import { UploadModule } from '../upload/upload.module'; +import { CoreModule } from '@alfresco/adf-core'; +import { VersionManagerModule } from '../version-manager'; +import { NewVersionUploaderDialogComponent } from './new-version-uploader.dialog'; + + +@NgModule({ + imports: [ + CommonModule, + MaterialModule, + CoreModule, + UploadModule, + FormsModule, + VersionManagerModule + ], + declarations: [ + NewVersionUploaderDialogComponent + ], + exports: [ + NewVersionUploaderDialogComponent, + FormsModule + ] +}) +export class NewVersionUploaderModule { } diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts new file mode 100644 index 0000000000..8a0be24b00 --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.spec.ts @@ -0,0 +1,242 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ContentService } from '@alfresco/adf-core'; +import { Component, EventEmitter, Output } from '@angular/core'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; +import { TranslateModule } from '@ngx-translate/core'; +import { BehaviorSubject, of, Subject } from 'rxjs'; +import { mockFile, mockNewVersionUploaderData, mockNode } from '../mock'; +import { ContentTestingModule } from '../testing/content.testing.module'; +import { NewVersionUploaderData, NewVersionUploaderDataAction, NewVersionUploaderDialogData, RefreshData, VersionManagerUploadData, ViewVersion } from './models'; +import { NewVersionUploaderDialogComponent } from './new-version-uploader.dialog'; +import { NewVersionUploaderService } from './new-version-uploader.service'; + +@Component({ + template: '' +}) +class TestDialogComponent { + @Output() + dialogAction = new EventEmitter(); + + @Output() + uploadError = new EventEmitter(); + + afterClosed = () => of({ action: 'refresh', node: mockNode }); + +} + +describe('NewVersionUploaderService', () => { + let fixture: ComponentFixture; + let service: NewVersionUploaderService; + let contentService: ContentService; + let dialog: MatDialog; + let spyOnDialogOpen: jasmine.Spy; + let dialogRefSpyObj; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ContentTestingModule + ], + declarations: [TestDialogComponent] + }); + }); + + beforeEach(() => { + service = TestBed.inject(NewVersionUploaderService); + contentService = TestBed.inject(ContentService); + dialog = TestBed.inject(MatDialog); + fixture = TestBed.createComponent(TestDialogComponent); + + dialogRefSpyObj = jasmine.createSpyObj({ afterClosed: null }); + dialogRefSpyObj.componentInstance = fixture.componentInstance; + dialogRefSpyObj.afterClosed = fixture.componentInstance.afterClosed; + spyOnDialogOpen = spyOn(dialog, 'open').and.returnValue(dialogRefSpyObj); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('openUploadNewVersionDialog', () => { + it('Should not open dialog if update operation is not allowed', () => { + spyOn(contentService, 'hasAllowableOperations').and.returnValue(false); + expect(spyOnDialogOpen).not.toHaveBeenCalled(); + }); + + it('Should return error if update operation is not allowed', async () => { + spyOn(contentService, 'hasAllowableOperations').and.returnValue(false); + const mockNewVersionUploaderDialogData: NewVersionUploaderDialogData = { + node: mockNode, + file: mockFile + }; + try { + await service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).toPromise(); + fail('An error should have been thrown'); + } catch (error) { + expect(error).toEqual({ value: 'OPERATION.ERROR.PERMISSION' }); + } + }); + + describe('Mat Dialog configuration', () => { + let mockNewVersionUploaderDialogData: NewVersionUploaderDialogData; + beforeEach(() => { + spyOn(contentService, 'hasAllowableOperations').and.returnValue(true); + spyOn(service.versionsApi, 'listVersionHistory').and.returnValue(Promise.resolve({ + list: { entries: [{ entry: '2' }] } + })); + mockNewVersionUploaderDialogData = { + node: mockNode, + file: mockFile + }; + }); + + it('Should open dialog with default configuration', fakeAsync(() => { + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).toPromise(); + tick(); + expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, { + data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, + panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-upload'], + width: '630px' + }); + })); + + it('Should override default dialog panelClass', fakeAsync(() => { + const mockDialogConfiguration: MatDialogConfig = { + panelClass: 'adf-custom-class', + width: '500px' + }; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration).toPromise(); + tick(); + expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, { + data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, + panelClass: 'adf-custom-class', + width: '500px' + }); + })); + + it('Should set dialog height', fakeAsync(() => { + const mockDialogConfiguration: MatDialogConfig = { + height: '600px' + }; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration).toPromise(); + tick(); + expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, { + data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, + panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-upload'], + width: '630px', + height: '600px' + }); + })); + + it('Should not override dialog configuration, if dialog configuration is empty', fakeAsync(() => { + const mockDialogConfiguration: MatDialogConfig = {}; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData, mockDialogConfiguration).toPromise(); + tick(); + expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, { + data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: undefined }, + panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-upload'], + width: '630px' + }); + })); + + it('Should dialog add list css class if showVersionsOnly is true', fakeAsync(() => { + const mockNewVersionUploaderDialogDataWithVersionsOnly = { + node: mockNode, + file: mockFile, + showVersionsOnly: true + }; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogDataWithVersionsOnly).toPromise(); + tick(); + expect(spyOnDialogOpen).toHaveBeenCalledWith(NewVersionUploaderDialogComponent, { + data: { file: mockFile, node: mockNode, currentVersion: '2', showComments: true, allowDownload: true, showVersionsOnly: true }, + panelClass: ['adf-new-version-uploader-dialog', 'adf-new-version-uploader-dialog-list'], + width: '630px' + }); + })); + + }); + + describe('Subscribe events from Dialog', () => { + let mockNewVersionUploaderDialogData: NewVersionUploaderDialogData; + + beforeEach(() => { + spyOn(contentService, 'hasAllowableOperations').and.returnValue(true); + spyOn(service.versionsApi, 'listVersionHistory').and.returnValue(Promise.resolve({ + list: { entries: [{ entry: '2' }] } + })); + mockNewVersionUploaderDialogData = { + node: mockNode, + file: mockFile + }; + }); + + it('Should return Refresh action', (done) => { + dialogRefSpyObj.componentInstance = { + dialogAction: new BehaviorSubject({ action: NewVersionUploaderDataAction.refresh, node: mockNode }), + uploadError: new Subject() + }; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).subscribe((res) => { + expect(res).toEqual({ action: NewVersionUploaderDataAction.refresh, node: mockNode }); + done(); + }); + }); + + it('Should return Upload action', (done) => { + dialogRefSpyObj.componentInstance = { + dialogAction: new BehaviorSubject(mockNewVersionUploaderData), + uploadError: new Subject() + }; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).subscribe((res) => { + expect(res).toEqual(mockNewVersionUploaderData); + done(); + }); + }); + + it('Should return View Version action', (done) => { + dialogRefSpyObj.componentInstance = { + dialogAction: new BehaviorSubject({ action: NewVersionUploaderDataAction.view, versionId: '2' }), + uploadError: new Subject() + }; + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).subscribe((res) => { + expect(res).toEqual({ action: NewVersionUploaderDataAction.view, versionId: '2' }); + done(); + }); + }); + + it('Should return upload error', (done) => { + dialogRefSpyObj.componentInstance = { + dialogAction: new Subject(), + uploadError: new BehaviorSubject({ value: 'Upload error' }) + }; + spyOnDialogOpen.and.returnValue(dialogRefSpyObj); + service.openUploadNewVersionDialog(mockNewVersionUploaderDialogData).subscribe(() => { + fail('An error should have been thrown'); + }, + error => { + expect(error).toEqual({ value: 'Upload error' }); + done(); + }); + }); + + }); + + }); +}); diff --git a/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts new file mode 100644 index 0000000000..620290061f --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/new-version-uploader.service.ts @@ -0,0 +1,86 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; +import { AlfrescoApiService, ContentService } from '@alfresco/adf-core'; + +import { NewVersionUploaderDialogComponent } from './new-version-uploader.dialog'; +import { VersionPaging, VersionsApi } from '@alfresco/js-api'; +import { NewVersionUploaderData, NewVersionUploaderDialogData } from './models'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class NewVersionUploaderService { + + _versionsApi: VersionsApi; + get versionsApi(): VersionsApi { + this._versionsApi = this._versionsApi ?? new VersionsApi(this.apiService.getInstance()); + return this._versionsApi; + } + + constructor( + private contentService: ContentService, + private apiService: AlfrescoApiService, + private dialog: MatDialog + ) { } + + /** + * Open a dialog NewVersionUploaderDialogComponent to display: + * - a side by side comparison between the current target node (type, name, icon) and the new file that should update it's version + * - the new version's minor/major changes and the optional comment of a node and the ability to upload a new file version + * - if data.showVersionsOnly is set to true, displays the version history of a node, with the ability to restore, delete and view version of the current node + * @param data data to pass to MatDialog + * @param config allow to override default MatDialogConfig + * @returns an Observable represents the triggered dialog action or an error in case of an error condition + */ + openUploadNewVersionDialog(data: NewVersionUploaderDialogData, config?: MatDialogConfig) { + const { file, node, showVersionsOnly } = data; + const showComments = true; + const allowDownload = true; + + return new Observable((observer) => { + if (this.contentService.hasAllowableOperations(node, 'update')) { + this.versionsApi.listVersionHistory(node.id).then((versionPaging: VersionPaging) => { + const dialogRef = this.dialog.open(NewVersionUploaderDialogComponent, { + data: { file, node, currentVersion: versionPaging.list.entries[0].entry, showComments, allowDownload, showVersionsOnly }, + panelClass: this.composePanelClass(showVersionsOnly), + width: '630px', + ...(config && Object.keys(config).length > 0 && config) + }); + dialogRef.componentInstance.dialogAction.asObservable() + .subscribe((newVersionUploaderData: NewVersionUploaderData) => { + observer.next(newVersionUploaderData); + }); + dialogRef.componentInstance.uploadError.asObservable().subscribe(error => { + observer.error(error); + }); + }); + } else { + observer.error({ value: 'OPERATION.ERROR.PERMISSION' }); + } + }); + + } + + private composePanelClass(showVersionsOnly: boolean): string | string[] { + const dialogCssClass = 'adf-new-version-uploader-dialog'; + return [dialogCssClass, `${dialogCssClass}-${showVersionsOnly ? 'list' : 'upload'}`]; + } +} diff --git a/lib/content-services/src/lib/new-version-uploader/public-api.ts b/lib/content-services/src/lib/new-version-uploader/public-api.ts new file mode 100644 index 0000000000..6ba0d0007f --- /dev/null +++ b/lib/content-services/src/lib/new-version-uploader/public-api.ts @@ -0,0 +1,21 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './new-version-uploader.dialog'; +export * from './new-version-uploader.module'; +export * from './new-version-uploader.service'; +export * from './models'; diff --git a/lib/content-services/src/public-api.ts b/lib/content-services/src/public-api.ts index 04291a44c1..a0ddc7abe5 100644 --- a/lib/content-services/src/public-api.ts +++ b/lib/content-services/src/public-api.ts @@ -35,5 +35,6 @@ export * from './lib/tree-view/index'; export * from './lib/group/index'; export * from './lib/aspect-list/index'; export * from './lib/content-type/index'; +export * from './lib/new-version-uploader'; export * from './lib/content.module'; diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.html b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.html index 00c714492f..c95f6db234 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.html +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.html @@ -26,6 +26,7 @@ (rowClick)="onRowClicked($event)" (attachFileClick)="onAttachFileClicked($event)" (downloadFile)="downloadContent($event)" + (uploadNewFileVersion)="onUploadNewFileVersion($event)" (contentModelFileHandler)="contentModelFormFileHandler($event)" (removeAttachFile)="onRemoveAttachFile($event)" > diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts index e2bc6d734c..5c94784cab 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.spec.ts @@ -29,7 +29,8 @@ import { DownloadService, AppConfigService, UploadWidgetContentLinkModel, - LocalizedDatePipe + LocalizedDatePipe, + NotificationService } from '@alfresco/adf-core'; import { allSourceParams, @@ -60,11 +61,12 @@ import { } from '../../../mocks/attach-file-cloud-widget.mock'; import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ContentModule, ContentNodeSelectorPanelService } from '@alfresco/adf-content-services'; +import { ContentModule, ContentNodeSelectorPanelService, NewVersionUploaderDataAction, NewVersionUploaderService } from '@alfresco/adf-content-services'; import { By } from '@angular/platform-browser'; -import { of } from 'rxjs'; +import { of, throwError } from 'rxjs'; import { FormCloudModule } from '../../../form-cloud.module'; import { TranslateModule } from '@ngx-translate/core'; +import { mockNode } from 'content-services/src/lib/mock'; describe('AttachFileCloudWidgetComponent', () => { let widget: AttachFileCloudWidgetComponent; @@ -82,6 +84,8 @@ describe('AttachFileCloudWidgetComponent', () => { let contentClickedSpy: jasmine.Spy; let openUploadFileDialogSpy: jasmine.Spy; let localizedDataPipe: LocalizedDatePipe; + let newVersionUploaderService: NewVersionUploaderService; + let notificationService: NotificationService; const createUploadWidgetField = (form: FormModel, fieldId: string, value?: any, params?: any, multiple?: boolean, name?: string, readOnly?: boolean) => { widget.field = new FormFieldModel(form, { @@ -204,7 +208,7 @@ describe('AttachFileCloudWidgetComponent', () => { describe('when is required', () => { it('should be able to display label with asterisk', async () => { - widget.field = new FormFieldModel( new FormModel({ taskId: '' }), { + widget.field = new FormFieldModel(new FormModel({ taskId: '' }), { type: FormFieldTypes.UPLOAD, required: true }); @@ -298,7 +302,7 @@ describe('AttachFileCloudWidgetComponent', () => { it('should be able to use mapped string variable value if the destinationFolderPath set to string type variable', async () => { const getNodeIdFromPathSpy = spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(mockNodeIdBasedOnStringVariableValue); - const form = new FormModel({ formVariables, processVariables}); + const form = new FormModel({ formVariables, processVariables }); createUploadWidgetField(form, 'attach-file-alfresco', [], mockAllFileSourceWithStringVariablePathType); fixture.detectChanges(); await fixture.whenStable(); @@ -383,9 +387,9 @@ describe('AttachFileCloudWidgetComponent', () => { appConfigService.config = Object.assign(appConfigService.config, { 'alfresco-deployed-apps': [ { - name: 'fakeapp' + name: 'fakeapp' } - ] + ] }); expect(widget.replaceAppNameAliasWithValue('/myfiles/-appname-/folder')).toBe('/myfiles/fakeapp/folder'); @@ -504,7 +508,7 @@ describe('AttachFileCloudWidgetComponent', () => { describe('when a file is uploaded', () => { beforeEach(async () => { - apiServiceSpy = spyOn(widget['nodesApi'], 'getNode').and.returnValue(new Promise(resolve => resolve({entry: fakeNodeWithProperties}))); + apiServiceSpy = spyOn(widget['nodesApi'], 'getNode').and.returnValue(new Promise(resolve => resolve({ entry: fakeNodeWithProperties }))); spyOn(contentCloudNodeSelectorService, 'getNodeIdFromPath').and.returnValue(new Promise(resolve => resolve('fake-properties'))); openUploadFileDialogSpy.and.returnValue(of([fakeNodeWithProperties])); widget.field = new FormFieldModel(new FormModel(), { @@ -846,4 +850,48 @@ describe('AttachFileCloudWidgetComponent', () => { expect(getProcessVariableValueSpy).not.toHaveBeenCalled(); }); }); + + describe('onUploadNewFileVersion', () => { + let spyOnOpenUploadNewVersionDialog: jasmine.Spy; + let spyOnReplaceOldFileVersionWithNew: jasmine.Spy; + let spyOnShowError: jasmine.Spy; + + beforeEach(() => { + notificationService = TestBed.inject(NotificationService); + newVersionUploaderService = TestBed.inject(NewVersionUploaderService); + spyOnOpenUploadNewVersionDialog = spyOn(newVersionUploaderService, 'openUploadNewVersionDialog') + .and.returnValue(of({ action: NewVersionUploaderDataAction.refresh })); + spyOnReplaceOldFileVersionWithNew = spyOn(widget, 'replaceOldFileVersionWithNew'); + spyOnShowError = spyOn(notificationService, 'showError'); + }); + + it('Should open new version uploader dialog', async () => { + await fixture.whenStable(); + widget.onUploadNewFileVersion(mockNode); + expect(spyOnOpenUploadNewVersionDialog).toHaveBeenCalledWith(mockNode); + }); + + it('Should not replace old file version with the new one if dialog returned action is not upload', async () => { + await fixture.whenStable(); + widget.onUploadNewFileVersion(mockNode); + expect(spyOnReplaceOldFileVersionWithNew).not.toHaveBeenCalled(); + }); + + it('Should replace old file version with the new one if dialog returned action is upload', async () => { + spyOnOpenUploadNewVersionDialog.and.returnValue(of({ action: NewVersionUploaderDataAction.upload })); + await fixture.whenStable(); + widget.onUploadNewFileVersion(mockNode); + expect(spyOnReplaceOldFileVersionWithNew).toHaveBeenCalledTimes(1); + }); + + it('Should show notification error if new version uploader dialog return error', async () => { + const mockError = {value: 'Upload error'}; + spyOnOpenUploadNewVersionDialog.and.returnValue(throwError(mockError)); + await fixture.whenStable(); + widget.onUploadNewFileVersion(mockNode); + expect(spyOnReplaceOldFileVersionWithNew).not.toHaveBeenCalled(); + expect(spyOnShowError).toHaveBeenCalledWith(mockError.value); + }); + + }); }); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts index 6f32e4f4ef..323e2da8e7 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/attach-file-cloud-widget.component.ts @@ -35,7 +35,7 @@ import { ContentCloudNodeSelectorService } from '../../../services/content-cloud import { ProcessCloudContentService } from '../../../services/process-cloud-content.service'; import { UploadCloudWidgetComponent } from './upload-cloud.widget'; import { DestinationFolderPathModel, DestinationFolderPathType } from '../../../models/form-cloud-representation.model'; -import { ContentNodeSelectorPanelService } from '@alfresco/adf-content-services'; +import { ContentNodeSelectorPanelService, NewVersionUploaderData, NewVersionUploaderDataAction, NewVersionUploaderDialogData, NewVersionUploaderService, VersionManagerUploadData } from '@alfresco/adf-content-services'; export const RETRIEVE_METADATA_OPTION = 'retrieveMetadata'; export const ALIAS_ROOT_FOLDER = '-root-'; @@ -81,7 +81,8 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i private contentNodeSelectorService: ContentCloudNodeSelectorService, private appConfigService: AppConfigService, private apiService: AlfrescoApiService, - private contentNodeSelectorPanelService: ContentNodeSelectorPanelService + private contentNodeSelectorPanelService: ContentNodeSelectorPanelService, + private newVersionUploaderService: NewVersionUploaderService ) { super(formService, thumbnails, processCloudContentService, notificationService, logger); } @@ -211,6 +212,16 @@ export class AttachFileCloudWidgetComponent extends UploadCloudWidgetComponent i this.processCloudContentService.downloadFile(file.id); } + onUploadNewFileVersion(node: NewVersionUploaderDialogData): void { + this.newVersionUploaderService.openUploadNewVersionDialog(node).subscribe((newVersionUploaderData: NewVersionUploaderData) => { + if (newVersionUploaderData.action === NewVersionUploaderDataAction.upload) { + this.replaceOldFileVersionWithNew(newVersionUploaderData as VersionManagerUploadData); + } + }, + error => this.notificationService.showError(error.value) + ); + } + onAttachFileClicked(nodeSelector: any) { nodeSelector.nodeId = nodeSelector.id; this.fileClicked(new ContentLinkModel(nodeSelector)); diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.html b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.html index 7d0514ebee..dadb7faace 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.html +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.html @@ -14,7 +14,7 @@ [alt]="mimeTypeIcon" role="button" tabindex="0" /> - + {{ 'FORM.FIELD.FILE_NAME' | translate }} @@ -22,7 +22,7 @@ (click)="onRowClicked(element)">{{element.name}} - + {{ columnName.title ? columnName.title : columnName.name | titlecase }} @@ -31,7 +31,7 @@ (click)="onRowClicked(row)">{{ getColumnValue(row, columnName) }} - + @@ -62,11 +62,19 @@ highlight_off {{ 'FORM.FIELD.REMOVE_FILE' | translate }} +
+ +
- + diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.ts index 4fba9f160f..f0a64deb11 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/file-properties-table-cloud.component.ts @@ -20,6 +20,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { LocalizedDatePipe, ThumbnailService } from '@alfresco/adf-core'; import { Node } from '@alfresco/js-api'; +import { NewVersionUploaderDialogData } from '@alfresco/adf-content-services'; export const RETRIEVE_METADATA_OPTION = 'retrieveMetadata'; @@ -56,6 +57,9 @@ export class FilePropertiesTableCloudComponent { @Output() downloadFile: EventEmitter = new EventEmitter(); + @Output() + uploadNewFileVersion: EventEmitter = new EventEmitter(); + @Output() contentModelFileHandler: EventEmitter = new EventEmitter(); @@ -76,6 +80,14 @@ export class FilePropertiesTableCloudComponent { this.downloadFile.emit(file); } + onUploadNewFileVersion(customEvent: any, node: Node){ + const newVersionUploaderDialogData: NewVersionUploaderDialogData = { + file: customEvent.detail.files[0].file, + node + }; + this.uploadNewFileVersion.emit(newVersionUploaderDialogData); + } + contentModelFormFileHandler(file?: any) { this.contentModelFileHandler.emit(file); } diff --git a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/upload-cloud.widget.ts b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/upload-cloud.widget.ts index d1de313e60..6bfb5ba066 100644 --- a/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/upload-cloud.widget.ts +++ b/lib/process-services-cloud/src/lib/form/components/widgets/attach-file/upload-cloud.widget.ts @@ -24,6 +24,7 @@ import { mergeMap } from 'rxjs/operators'; import { WidgetComponent, LogService, FormService, ThumbnailService, NotificationService } from '@alfresco/adf-core'; import { ProcessCloudContentService } from '../../../services/process-cloud-content.service'; import { FileSourceTypes, DestinationFolderPathType } from '../../../models/form-cloud-representation.model'; +import { VersionManagerUploadData } from '@alfresco/adf-content-services'; @Component({ selector: 'upload-cloud-widget', @@ -78,6 +79,13 @@ export class UploadCloudWidgetComponent extends WidgetComponent implements OnIni } } + replaceOldFileVersionWithNew(versionManagerData: VersionManagerUploadData) { + const currentUploadedFileIndex = this.uploadedFiles.findIndex(file => file.name === versionManagerData.currentVersion.name); + this.uploadedFiles[currentUploadedFileIndex] = { ...versionManagerData.newVersion.value.entry}; + this.field.value = [...this.uploadedFiles]; + this.field.form.values[this.field.id] = [...this.uploadedFiles]; + } + onFileChanged(event: any) { const files: File[] = []; const filesSaved: Node[] = []; diff --git a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts index 6dfc1bed6f..69f7248cfe 100644 --- a/lib/process-services-cloud/src/lib/form/form-cloud.module.ts +++ b/lib/process-services-cloud/src/lib/form/form-cloud.module.ts @@ -24,7 +24,7 @@ import { MaterialModule } from '../material.module'; import { FormCloudComponent } from './components/form-cloud.component'; import { FormDefinitionSelectorCloudComponent } from './components/form-definition-selector-cloud.component'; import { FormCustomOutcomesComponent } from './components/form-cloud-custom-outcomes.component'; -import { ContentMetadataModule, ContentNodeSelectorModule } from '@alfresco/adf-content-services'; +import { ContentMetadataModule, ContentNodeSelectorModule, UploadModule } from '@alfresco/adf-content-services'; import { DateCloudWidgetComponent } from './components/widgets/date/date-cloud.widget'; import { DropdownCloudWidgetComponent } from './components/widgets/dropdown/dropdown-cloud.widget'; @@ -51,7 +51,8 @@ import { FilePropertiesTableCloudComponent } from './components/widgets/attach-f ContentNodeSelectorModule, PeopleCloudModule, GroupCloudModule, - ContentMetadataModule + ContentMetadataModule, + UploadModule ], declarations: [ FormCloudComponent,