From cda2cccfd3c2f038df50bbe131409807a2d10d04 Mon Sep 17 00:00:00 2001 From: Mike Hatfield Date: Tue, 27 Apr 2010 17:08:09 +0000 Subject: [PATCH] Merged DEV/GDOCS to HEAD Google Doc Integration - The following configuration must be added to your alfresco-global.properties in order to enable the googleeditable repository behaviour: googledocs.username=myuser@bob.com googledocs.password=pwd123pwd googledocs.googleeditable.enabled=true Google Docs Share Integration - Enabled via share-config (see share-config-custom.xml.sample for details) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@20018 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/bootstrap-context.xml | 7 + config/alfresco/core-services-context.xml | 5 +- config/alfresco/mimetype/mimetype-map.xml | 4 +- .../googledocs/default/googledocs-context.xml | 57 ++ .../googledocs/default/googledocs-model.xml | 71 ++ .../googledocs/default/googledocs.properties | 16 + .../subsystems/googledocs/default/test.docx | Bin 0 -> 10060 bytes .../subsystems/googledocs/default/test.xlsx | Bin 0 -> 8276 bytes .../repo/coci/CheckOutCheckInServiceImpl.java | 415 ++++++--- .../coci/CheckOutCheckInServicePolicies.java | 130 +++ .../alfresco/repo/copy/CopyServiceImpl.java | 7 +- .../repo/googledocs/GoogleDocsModel.java | 37 + .../GoogleDocsPermissionContext.java | 15 + .../repo/googledocs/GoogleDocsService.java | 43 + .../googledocs/GoogleDocsServiceImpl.java | 814 ++++++++++++++++++ .../googledocs/GoogleDocumentServiceTest.java | 278 ++++++ .../repo/googledocs/GoogleEditableAspect.java | 206 +++++ .../alfresco/repo/site/SiteServiceImpl.java | 45 + .../service/cmr/site/SiteService.java | 7 + 19 files changed, 2040 insertions(+), 117 deletions(-) create mode 100755 config/alfresco/subsystems/googledocs/default/googledocs-context.xml create mode 100755 config/alfresco/subsystems/googledocs/default/googledocs-model.xml create mode 100755 config/alfresco/subsystems/googledocs/default/googledocs.properties create mode 100755 config/alfresco/subsystems/googledocs/default/test.docx create mode 100644 config/alfresco/subsystems/googledocs/default/test.xlsx create mode 100755 source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java create mode 100755 source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java create mode 100644 source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java create mode 100755 source/java/org/alfresco/repo/googledocs/GoogleDocsService.java create mode 100755 source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java create mode 100755 source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java create mode 100755 source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 7487048516..be6b6d27da 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -506,6 +506,13 @@ + + + + true + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 3211003767..89cea501d3 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -1152,7 +1152,7 @@ - + @@ -1177,6 +1177,9 @@ + + + diff --git a/config/alfresco/mimetype/mimetype-map.xml b/config/alfresco/mimetype/mimetype-map.xml index e8179c7db1..3bc9e2a841 100644 --- a/config/alfresco/mimetype/mimetype-map.xml +++ b/config/alfresco/mimetype/mimetype-map.xml @@ -256,7 +256,7 @@ ppm - + ppt pps pot @@ -338,7 +338,7 @@ xbm - + xls diff --git a/config/alfresco/subsystems/googledocs/default/googledocs-context.xml b/config/alfresco/subsystems/googledocs/default/googledocs-context.xml new file mode 100755 index 0000000000..589a57d690 --- /dev/null +++ b/config/alfresco/subsystems/googledocs/default/googledocs-context.xml @@ -0,0 +1,57 @@ + + + + + + + alfresco/subsystems/googledocs/default/googledocs-model.xml + + + + + + + ${googledocs.application.name} + + + + + + ${googledocs.spreadsheet.service.name} + + + ${googledocs.application.name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/subsystems/googledocs/default/googledocs-model.xml b/config/alfresco/subsystems/googledocs/default/googledocs-model.xml new file mode 100755 index 0000000000..d8b650348e --- /dev/null +++ b/config/alfresco/subsystems/googledocs/default/googledocs-model.xml @@ -0,0 +1,71 @@ + + + + + + + + Google Docs Model + unknown + 1.0 + + + + + + + + + + + + + + + + Google Editable + + + Property who's only purpose is to allow the marker aspect to be automatically added. + d:boolean + true + true + + + + + + Google Document + + + GoogleDocs URL + d:text + false + + + GoogleDocs id + d:text + false + + + GoogleDocs Type + d:text + false + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/subsystems/googledocs/default/googledocs.properties b/config/alfresco/subsystems/googledocs/default/googledocs.properties new file mode 100755 index 0000000000..f0289978b1 --- /dev/null +++ b/config/alfresco/subsystems/googledocs/default/googledocs.properties @@ -0,0 +1,16 @@ + +# Enables google editable functionality +googledocs.googleeditable.enabled=false + +# Google docs application name +googledocs.application.name=Alfresco ECM system + +# Google docs URL +googledocs.url=http://docs.google.com/feeds/default/private/full + +# System google docs authentication credentials +#googledocs.username= +#googledocs.password= + +# Google docs spreadsheet service name +googledocs.spreadsheet.service.name=wise \ No newline at end of file diff --git a/config/alfresco/subsystems/googledocs/default/test.docx b/config/alfresco/subsystems/googledocs/default/test.docx new file mode 100755 index 0000000000000000000000000000000000000000..e04a931e44464c0cb05a15104cefa28b0641c4c5 GIT binary patch literal 10060 zcmeHNWmH_rwr<=txVr^+4;oy8TX1Wvad!yrBqV6i1a}SY?(P~O1b63ka__v!WM;kf z{@;6k)Tv(G-|nhi+jl9;K|x~yU;*#|0Dv5@eE*Ke90C9!g8=}Z0pKBZ#X)w?W_Hd7 zY997xPOq8WZEeW1p&{uq0g&L||M&S1=0LUDpj`(mdXwhf3yBtu@T}0nLU^KE-dgEA zQp0@|tqZx!)7v{cNC|Z$QHHOW^x8*5`}Qey1W~WDVNK1a z$v6>Dp!6HwlZn6PQ!M}PMQNR_5f?4lrD-GsYjBfaf9hxi49Oc0v>*)y!p!^s0B#9@eI|F~C2-ZF%uzDIen%Ow9F#j0;tCs)4 zy7q&=NEmue`rYVB@Y7YV1V-KU!pXn`U%QVz5O&9Df@zs(YiVk^qt zs0tCK5~eWcD9_P*)7PT*C5}i2K{Ymhi0~q+=u1oQxh7Of@}~uD6rjbCNKLO6x4{hR zYaRk$MDwG$!Eo$8I3v(JvoV8>Opjx<75xPxfeq^@b$YyUF(UUGFnjt!;>jQ~<_#l4 z7DD*wnYT+Cl#V`{^L(~PZ2TYrY!$b6jP9ii!Ioj{cvM{C=CSImx1a0XFtUW!&aTF=)p&CT7u@hm;@fOg8LfY zij=!fHyY9dNLbrOSswAnr0t&R+2;*9-~yirh@IOKbW9n~K@Dys_MeQ_SZnImprH zU+%|H2Gmo8`&ozm?thjYB6n&c>eeDA zFok)>5nfGJ7F3)Bo%I5X)&iAW7b^hn*^9G#WX`>Y+g|GLd@=BkwXrHO*4(|voGX|!2pZIPuclZYSPpe?G{;)eMrmw(C;(m zfDu$&30Ti34uK*SuB4d*)lBl3FfuXkeIDoxDxD7&Y(xyDK8>tncywJ{pq0##q`kM~ z?zc&yMviI|OR-Ay_G(Hk#@vSDfzh-yOeAWfon4G2ow3!=wB-EI7S?g?tu)RLo%*=| zm&@|-vt_$-xx{CW%)BC%;pM9yMQE$RAnyd(5OL3Vvq;m-c7+MvL^P~tOmDi6YHc7Vf$bAOa)M&Sb8&+kj|e!RgPnPk2igqA-~j#zSHqitnD~Sy3JB& zX_VB|3WaR?T-GS;ZklEW`bvU`M;{aBL?t-N$1tv!LaE@~OZxDF8zeg{cee!0z4bva z>`rPr5AG&!VX}5QH9m1kG@W;Bsn=yrJefq5UCBqMQ)GK#=xnKtvQeKO@lcO zh+au6oy3b)T98({qZn+vxMKX)Do&A&i}O0&YY;}gm;ZR}GVa{U`Y>4vrP(-urfYD@ zo$doC<5XKlIPC#yMUzWRZb%Sy$AH5FdOecNX9v1&T6GnQfSpW`fw`xACYU^Z z!~*o^FwQ&oUMl>7XVqPZ6Uc_q@mWP_e$Blk67{ip{0rYaa!BcXjVnugr#qD(t2yB; zG#w~vJQSXAi*5>85=vT*OK+vGTwOh6IuowV)Spe(dv~D6h4j^HC}&7mufdG9>6mkB zE;dOZ(+(CQEVF-~L{Q7%MmY?RD;?fC7*)iFKR#q;h zlF+TCpTIpAQ6fx*d9!)KOpHlRue6~5;5aN!b>bnCSbnffa>-KSAj*Xl!MAI8eJcd91xjPeiRavo8$z&U;R&8?7RH$}&n(`w!@{{l$EnVn zK@uC!T>2}u#fEIPoT7KCv`Z`&C4a1|c5p!`vxxklQ(jwL3i6%*Vk2r_ZsQ(duf|$( zMOgA&7sdz5(28XC`^A@LPtj38`BBg(jU@KrmwqKH{lFxVONR{Cx|w1wJ@4?xfwZ9# z#m|#AW|dmmu%@JhPS-UHRwhpK?SygNJUhqHBj$CDduu3t8=5=E56?@cLn3)H!^XD^ zQA>yyjx45-`aV=0$X*k)&067mhXh*EWy@9Jok=i7)ZnddW}^X6Otr?L3xKjcbD?yH z8}3weGswsM?RoeDTZ0Yc!$>j)V`??cXsXEgY&lnZH&GKgPXf4g4q+=P3f zBQ{cKn-=_4ou&0*f3%l0=S7C^+37g$w7YiC(`_eh>;1h47y5(+%}6B6$NS6u{>U=n zhlfTf>|=!hDYNUi9*OMKE%<{oJS+|k2WnbMhZgKk_{r0VVXC*h?J06F4;c|e=fm{o z$!APyZz`1RCujHJ#Ol0A53I;^&hReBCg zytTR;BcJuQHp?KoaG=Rao!Chbv0dRQVxh{2VNE#Q`^O<39IEWixxBv(loQt2WtJqC zf3CouOG%qz9$5=S_D$CKhWzMiQ{@Hc!do=q8AVL0emc! zb=RUi9S%Bgh{vcHOkTvnp=aYAIH<#{ezgnPbvuSq#QAcEN*&XvR0dlI+zsPILKot| zSa`2on2ty^xpg$amZKbNi>#-|7Xo>`@x2#2{-PFVLt!qlklpA`;^uX*fVOj^9`yM^ zS%a>7!afH4*-iA$>wX_i$L1&R{?~6uEtsmS77)!euTW~~xtScB%Ed`kdtj{w`cQx= zrU-WuW1*tWBWb8I4#u*S?n8TDf{f1AC=0VigHryR%Ds9=fukuL{ZVXb&bqtsMF^kCLVV^%CZ zS$8?iTsFP4L>b?kw~PQ8XC@dHT67UoL;uZLgE;9&~ z6P}}Y`F3en*&BNxT^p`ey5u~dN1JlbE+zOY zbJNloMWt-ZruFPR`+9+kgq_JaB%4@G%r2v$+VHsIEtuTwD|Y5Poak4)JMJn!ZXy%b3eiGO+DF;XcN$sthar>1cpJsd61S2 zj1!`hqq$Wi*BHHImP;@TmX~`SG@)!cEhz(r~9fPKfJ3A z)JVE4qwnCW)6uQi=G3>_?g+r$;RY26jlMb z12+3M!B#MiInLD{Iy#ZKx;^?r`$pNPl-E zr>nV9?~JQWRzZ6ettwVX&y8pgZ;$r`gP+Srj@Q~QQ~l~kR%!*-b{^`0tP7$Kg8o}| zM8gx&|Ln+w`MgNF!GRPj*qQxrAmwD{>;X+qxVukycFw2x|>i9WbAv!)FP13tMcvm-02bqYEymnj|N9GzrRXhRJ3# z(4-1JI@Rf>^~Af7JztZZ5END>G)VKix1X^{;tw_F7DE7Xyz5PmPo`$VHpC6Y$SE#d z4y21O_+lQc_|g58)nkeOa}`x`TFl!|nA28f(7ax`@`+Iyvk}gLvhRy0oD=Z|+Kt+3 z%@$y}PFk(5)d(dnLDUtvf^!#x=S+k2%lgTC1P2gMOQke26M+yL`ShIVYKo- z6#?vc2`L(~v}s;Lt10_2bvIj{QgZfls0OX5VC=RVRBLqjw5a%k57V>NB*8aKAIvpK zB52vq7$UHr9`m;aq=#{H_=O5DVb6KA$%#nxN@o*E5)VVd{TE9a1K)Sidx*JI-Zt>) zvnC@=S)k&k-Xbph+lX}+=y@j-h}P5JpHAwI5u`@bm{kp#jHJvkJwWp{sT&@K%FP$k z*m2prpDo!#Y;cHw|F(wGL#d+>iZ6gVcM??sRhvRMG;=8fzfZqF>L$WEtUep&J18>7}90Gr|LoTYmwm=iA4vh1%3>}~IB>5<WS?~7hM!TnS4c8^uA;tftK;Gh8j%s+xRb8!7s&B)ls?1#`-Cm4VNS+TlT-|q{= zwJ0$>5@SR=ojIUI-T^cjCxUBp>jv&G8;}6C zq``=NHmAJcHd-zEI`{!*R|J&FszbF;7B~E9R4I{RB+39^tSey(V`70j&o@PcgO0E>BTGq$n3d=T#`qfGv=H!JMe0O~8rej7;jsr| zuKA?vm&^z*x)e`{H+s7Bc+{3b*>Wo+5y2;6EI0m#7t=eK5gt20 z>g;4!-qic%!GdC{CuGx>V)<*31Wsl?fqf1k7lcwa&8Zd-3GIm+5AV^F63;@y+BvwA z_V++rb}k~J5qw6_;Pb-z*Mvw5t;$eR`?YsJIZmTZI&papyU@6dyf}FM_I4VIB(t| zz+Jz5(|KL}O-}RJ^JUd-fr7fU0KOsHV7e0lj=X+dTKpb&6&2{>ZR(~+48$t0Hx-pr zW37)%U{Re;rrZ#spN@ybb4FDh)O;QBiQDhgqzF9=ee=cHNRdxi~JFytq+y4+taOv>BRS+=e(o_Z&!BvnZ$`iC# zhb5DU=xVx@Q-}_0)Lcv!gDX@xF}Cx}*?yxTW1L}x!UsbOoAVXxmsMQza3T5WueYm! z@fMbOstYrN-nuJFrDu#ZM(F%j9S5sghb2+K!QJ1DK)mc7E#qGxK91YgUO1TzgJ;KH%{nna2&_80Z^bSrbgzxQ~ zCne_T&PYr$!+RY=D5N~Oj<;VVg=swj#|&0c-mt|iYr`;-*O*!c?S&YkeG(9-sgQH< z9qCsu<>KkIA9Ky@$JOb*Zbrzy$3PK+AO_5W97&z|?Gp&Z-KT1=#&Nfvhw;O&-hUuO zx5;I#&tCVSmX|Q3h14j$=pf;}5!A@NBB^U}{?45Kk+QXaJPO3mMaW{hlUI)yE3nrV z!Y(TL80$$3NwO;%_>DO)-zIvmeg~9;WIy{PFPy4`q_Y}M%;))beejs&A4OH*UR~jb z4Lt#iD#ov(Y65cnX+r-it6;u?pQr={PzNix%$s;gTDT;qBTSJgGD<;aiWfb%u=K@Ai|n_+FW)e>C9dpGE^QR z2?(RA5=dEferevJ&{6Zoq6kIn?HZ(#3iGQ+VEq zKzdKJInoN1?!|b&h8C7&8Qzr1^^M7Gi@RruCQ4pm(}u`I_!@%j5Lx=z;P}U-$1@~e z?pfQlXG@Ucwv)K8VV}1#X(gGtobai7v;iU7No=sd-^!$aDNM!F z?{k}|k%`V)PPnTYC32?=)O_lWIlp{(;70L1(L+dP>fC%UWz<$B-9yp%I)rC-hvg9w zDo-1yO3mCGU)UbI#qVl|6m+Ec)>pa#F)BbleQS_)+|!LcIiGG__mlnr@#s)<^iZ9A z>fpUA1rH&{Ni6#U$m0|3WKPZv)eik6Y$;}is*%wFs>y-k_6hTQ49x-eMuqfdrf1sM zBlWn0yOgWtK85d^hssVjD(eGZ?66NV)C(9fU~Vk%+J)3(Cl9rznac)mqgu#*WN1fS zk>6K^JCcyqEcQXKfTvB$f8^lyt8m6w{A@Oue6c2@J5ae;8EwHL?3eFZiL5X=JiuKN zy=>NZid&>=Z3EfK_@oii=*8~t1XH@NWlA&h!H95J0iYmQJgzn;qJ(>Py3s4lgY|UY}VU^wFsBL_BI-D2fd+MGy9Bl^AIO*=>@D3>%>7g8XwW&gWvVKGN zM6!#mPDZj) zs0Ca_3`F*pEF{eiC|nGjkGd4GuaVJIFh};;5!;>=(W5+H;BnSa7NP?76TH(=cuuV9 z!)H4cL{3ptBfITcqTErV!v>>eH8z3@&9)NC_|d9(i0Q4pI4kq(q$q)&UYw4(;0ZN& z!k!79tThV}oE#jC!&_Lyh6UJ9S6RYV1?bGpihW+XJi^M{3{cU0o3a-1b)Va!k*fSn z&7tdit

DXeaEg^Bd!aiH0FNFN-cUi$>>0> z$PJ4d@PWMEQIr&NPU`w70%^G=nytrAGa>^>y59O+Z?qciW}Mida>Cp6W)x7?LQ6JEh2G7!TynNoRK>DSL)HrIxAfaf%K$2pXOw z%X|7Nvk?3vQ(Jp0&3kjT8KLrthp(>CCoh%zAgm&I0>xq!Q^Am9c zXm;x7Vm*%ky=Y##m2y14#vddBT-}5}7($b;zk|-`=nr1)3Ovr%pyA0&|F!s?Ksw4F z6n_r?FS0Lve3S^Dqk3@u5%hy{o@_u^#*Di=dGx$(6Wb# zBAR+EQu_&&mwn%L0jcYD-$?0_wp~dwl-Q-7rUNwmNY?8QP@>%xB#ZMIXKZL4S%um7 zk04#;^(wwIocE(d#7yRAXE0HH*N`-+iW1=`6zmpx{AqNx9e2wzuUFj|gZ4e80lAMmrBduZJ9(@Yhp2Ml1+R0i4!6;U#lHeO!QTG= z>K*@1>G$5@pR}I9cdP!^N&FrDd+X;6+|2?Pv6Tge|Z~Si=_U{z_nyCJX2LPtQS^V#5>+k5l<_^E2 u{Rw_S|B*@j4*xxl`;!0%@gLm#->6Pm4hAe9KQ7@Q0ouX2iVM|`PyYidh;ShQ literal 0 HcmV?d00001 diff --git a/config/alfresco/subsystems/googledocs/default/test.xlsx b/config/alfresco/subsystems/googledocs/default/test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..64d4a40495241a4468235908ace7b7852444b74f GIT binary patch literal 8276 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 literal 0 HcmV?d00001 diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index a4d4eb1e3f..d241102671 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -19,11 +19,23 @@ package org.alfresco.repo.coci; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCheckIn; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCancelCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut; +import org.alfresco.repo.policy.ClassPolicyDelegate; +import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.version.VersionableAspect; import org.alfresco.service.cmr.coci.CheckOutCheckInService; @@ -48,7 +60,7 @@ import org.alfresco.service.namespace.QName; import org.springframework.extensions.surf.util.I18NUtil; /** - * Version operations service implementation + * Check out check in service implementation * * @author Roy Wetherall */ @@ -65,6 +77,14 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService private static final String MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE = "coci_service.err_workingcopy_has_no_mimetype"; private static final String MSG_ALREADY_CHECKEDOUT = "coci_service.err_already_checkedout"; private static final String MSG_ERR_CANNOT_RENAME = "coci_service.err_cannot_rename"; + + /** Class policy delegate's */ + private ClassPolicyDelegate beforeCheckOut; + private ClassPolicyDelegate onCheckOut; + private ClassPolicyDelegate beforeCheckIn; + private ClassPolicyDelegate onCheckIn; + private ClassPolicyDelegate beforeCancelCheckOut; + private ClassPolicyDelegate onCancelCheckOut; /** * Extension character, used to recalculate the working copy names @@ -101,6 +121,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService */ private SearchService searchService; + /** Policy component */ + private PolicyComponent policyComponent; + /** * The authentication service */ @@ -147,8 +170,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService * * @param copyService the copy service */ - public void setCopyService( - CopyService copyService) + public void setCopyService(CopyService copyService) { this.copyService = copyService; } @@ -158,8 +180,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService * * @param authenticationService the authentication service */ - public void setAuthenticationService( - AuthenticationService authenticationService) + public void setAuthenticationService(AuthenticationService authenticationService) { this.authenticationService = authenticationService; } @@ -194,6 +215,174 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService this.versionableAspect = versionableAspect; } + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Initialise method + */ + public void init() + { + // Register the policies + beforeCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.BeforeCheckOut.class); + onCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.OnCheckOut.class); + beforeCheckIn = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.BeforeCheckIn.class); + onCheckIn = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.OnCheckIn.class); + beforeCancelCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.BeforeCancelCheckOut.class); + onCancelCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.OnCancelCheckOut.class); + } + + /** + * Returns all the classes of a node, including its type and aspects. + * + * @param nodeRef node reference + * @return List list of classes + */ + private List getInvokeClasses(NodeRef nodeRef) + { + List result = new ArrayList(10); + result.add(nodeService.getType(nodeRef)); + Set aspects = nodeService.getAspects(nodeRef); + for (QName aspect : aspects) + { + result.add(aspect); + } + return result; + } + + /** + * Invoke the before check out policy + * + * @param nodeRef + * @param destinationParentNodeRef + * @param destinationAssocTypeQName + * @param destinationAssocQName + */ + private void invokeBeforeCheckOut( + NodeRef nodeRef, + NodeRef destinationParentNodeRef, + QName destinationAssocTypeQName, + QName destinationAssocQName) + { + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = beforeCheckOut.getList(invokeClass); + for (BeforeCheckOut policy : policies) + { + policy.beforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); + } + + } + } + + /** + * Invoke on the on check out policy + * + * @param workingCopy + */ + private void invokeOnCheckOut(NodeRef workingCopy) + { + List classes = getInvokeClasses(workingCopy); + for (QName invokeClass : classes) + { + Collection policies = onCheckOut.getList(invokeClass); + for (OnCheckOut policy : policies) + { + policy.onCheckOut(workingCopy); + } + + } + } + + /** + * Invoke before check in policy + * + * @param workingCopyNodeRef + * @param versionProperties + * @param contentUrl + * @param keepCheckedOut + */ + private void invokeBeforeCheckIn( + NodeRef workingCopyNodeRef, + Map versionProperties, + String contentUrl, + boolean keepCheckedOut) + { + List classes = getInvokeClasses(workingCopyNodeRef); + for (QName invokeClass : classes) + { + Collection policies = beforeCheckIn.getList(invokeClass); + for (BeforeCheckIn policy : policies) + { + policy.beforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); + } + + } + } + + /** + * Invoke on check in policy + * + * @param nodeRef + */ + private void invokeOnCheckIn(NodeRef nodeRef) + { + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = onCheckIn.getList(invokeClass); + for (OnCheckIn policy : policies) + { + policy.onCheckIn(nodeRef); + } + + } + } + + /** + * Invoke before cancel check out + * + * @param workingCopy + */ + private void invokeBeforeCancelCheckOut(NodeRef workingCopy) + { + List classes = getInvokeClasses(workingCopy); + for (QName invokeClass : classes) + { + Collection policies = beforeCancelCheckOut.getList(invokeClass); + for (BeforeCancelCheckOut policy : policies) + { + policy.beforeCancelCheckOut(workingCopy); + } + + } + } + + /** + * Invoke on cancel check out + * + * @param nodeRef + */ + private void invokeOnCancelCheckOut(NodeRef nodeRef) + { + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = onCancelCheckOut.getList(invokeClass); + for (OnCancelCheckOut policy : policies) + { + policy.onCancelCheckOut(nodeRef); + } + + } + } + /** * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#checkout(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) */ @@ -221,6 +410,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null); } + // Invoke before check out policy + invokeBeforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); + // Rename the working copy String copyName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); copyName = createWorkingCopyName(copyName); @@ -251,9 +443,12 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_OWNER, userName); this.nodeService.addAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties); - // Lock the origional node + // Lock the original node this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + // Invoke on check out policy + invokeOnCheckOut(workingCopy); + // Return the working copy return workingCopy; } @@ -309,115 +504,111 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Check that the working node still has the copy aspect applied if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) { - // Disable versionable behaviours since we don't want the auto version policy behaviour to execute when we check-in - //this.versionableAspect.disableAutoVersion(); - //try - //{ - Map workingCopyProperties = nodeService.getProperties(workingCopyNodeRef); - // Try and get the original node reference - nodeRef = (NodeRef) workingCopyProperties.get(ContentModel.PROP_COPY_REFERENCE); - if(nodeRef == null) + // Invoke policy + invokeBeforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); + + Map workingCopyProperties = nodeService.getProperties(workingCopyNodeRef); + // Try and get the original node reference + nodeRef = (NodeRef) workingCopyProperties.get(ContentModel.PROP_COPY_REFERENCE); + if(nodeRef == null) + { + // Error since the original node can not be found + throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); + } + + try + { + // Release the lock + this.lockService.unlock(nodeRef); + } + catch (UnableToReleaseLockException exception) + { + throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); + } + + if (contentUrl != null) + { + ContentData contentData = (ContentData) workingCopyProperties.get(ContentModel.PROP_CONTENT); + if (contentData == null) { - // Error since the original node can not be found - throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); - } - - try - { - // Release the lock - this.lockService.unlock(nodeRef); - } - catch (UnableToReleaseLockException exception) - { - throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); - } - - if (contentUrl != null) - { - ContentData contentData = (ContentData) workingCopyProperties.get(ContentModel.PROP_CONTENT); - if (contentData == null) - { - throw new AlfrescoRuntimeException(MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[]{workingCopyNodeRef}); - } - else - { - contentData = new ContentData( - contentUrl, - contentData.getMimetype(), - contentData.getSize(), - contentData.getEncoding()); - } - // Set the content url value onto the working copy - this.nodeService.setProperty( - workingCopyNodeRef, - ContentModel.PROP_CONTENT, - contentData); - } - - // Copy the contents of the working copy onto the original - this.copyService.copy(workingCopyNodeRef, nodeRef); - - // Handle name change on working copy (only for folders/files) - if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) - { - String origName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - String name = (String)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); - if (hasWorkingCopyNameChanged(name, origName)) - { - // ensure working copy has working copy label in its name to avoid name clash - if (!name.contains(" " + getWorkingCopyLabel())) - { - try - { - fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); - } - catch (FileExistsException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); - } - catch (FileNotFoundException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); - } - } - try - { - // rename original to changed working name - fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); - } - catch (FileExistsException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); - } - catch (FileNotFoundException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); - } - } - } - - if (versionProperties != null && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) - { - // Create the new version - this.versionService.createVersion(nodeRef, versionProperties); - } - - if (keepCheckedOut == false) - { - // Delete the working copy - this.nodeService.deleteNode(workingCopyNodeRef); + throw new AlfrescoRuntimeException(MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[]{workingCopyNodeRef}); } else { - // Re-lock the original node - this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + contentData = new ContentData( + contentUrl, + contentData.getMimetype(), + contentData.getSize(), + contentData.getEncoding()); } - //} - //finally - //{ - // this.versionableAspect.enableAutoVersion(); - //} + // Set the content url value onto the working copy + this.nodeService.setProperty( + workingCopyNodeRef, + ContentModel.PROP_CONTENT, + contentData); + } + // Copy the contents of the working copy onto the original + this.copyService.copy(workingCopyNodeRef, nodeRef); + + // Handle name change on working copy (only for folders/files) + if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) + { + String origName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String name = (String)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); + if (hasWorkingCopyNameChanged(name, origName)) + { + // ensure working copy has working copy label in its name to avoid name clash + if (!name.contains(" " + getWorkingCopyLabel())) + { + try + { + fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); + } + catch (FileExistsException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + } + catch (FileNotFoundException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + } + } + try + { + // rename original to changed working name + fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); + } + catch (FileExistsException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); + } + catch (FileNotFoundException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); + } + } + } + + if (versionProperties != null && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) + { + // Create the new version + this.versionService.createVersion(nodeRef, versionProperties); + } + + if (keepCheckedOut == false) + { + // Delete the working copy + this.nodeService.deleteNode(workingCopyNodeRef); + } + else + { + // Re-lock the original node + this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + } + + // Invoke policy + invokeOnCheckIn(nodeRef); } else { @@ -466,6 +657,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Ensure that the node has the copy aspect if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) { + // Invoke policy + invokeBeforeCancelCheckOut(workingCopyNodeRef); + // Get the original node nodeRef = (NodeRef)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_COPY_REFERENCE); if (nodeRef == null) @@ -479,6 +673,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Delete the working copy this.nodeService.deleteNode(workingCopyNodeRef); + + // Invoke policy + invokeOnCancelCheckOut(nodeRef); } else { diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java new file mode 100755 index 0000000000..0297021500 --- /dev/null +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.coci; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.policy.ClassPolicy; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Policy interfaces for the check in/check out service + * + * @author Roy Wetherall + */ +public interface CheckOutCheckInServicePolicies +{ + /** + * + */ + public interface BeforeCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCheckOut"); + + /** + * + * @param nodeRef + */ + void beforeCheckOut( + NodeRef nodeRef, + NodeRef destinationParentNodeRef, + QName destinationAssocTypeQName, + QName destinationAssocQName); + } + + /** + * + */ + public interface OnCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onCheckOut"); + + /** + * + * @param nodeRef + */ + void onCheckOut(NodeRef workingCopy); + } + + /** + * + */ + public interface BeforeCheckIn extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCheckIn"); + + /** + * + * @param workingCopyNodeRef + * @param versionProperties + * @param contentUrl + * @param keepCheckedOut + */ + void beforeCheckIn( + NodeRef workingCopyNodeRef, + Map versionProperties, + String contentUrl, + boolean keepCheckedOut); + } + + /** + * + */ + public interface OnCheckIn extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onCheckIn"); + + /** + * + * @param nodeRef + */ + void onCheckIn(NodeRef nodeRef); + } + + /** + * + */ + public interface BeforeCancelCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCancelCheckOut"); + + /** + * + * @param nodeRef + */ + void beforeCancelCheckOut(NodeRef workingCopyNodeRef); + } + + /** + * + */ + public interface OnCancelCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onCancelCheckOut"); + + /** + * + * @param nodeRef + */ + void onCancelCheckOut(NodeRef nodeRef); + } +} diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index d87b3fc20c..48fdeaa56d 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -29,7 +29,6 @@ import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; -import org.springframework.extensions.surf.util.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ActionServiceImpl; import org.alfresco.repo.copy.CopyBehaviourCallback.ChildAssocCopyAction; @@ -38,7 +37,6 @@ import org.alfresco.repo.copy.CopyBehaviourCallback.CopyChildAssociationDetails; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; @@ -54,17 +52,16 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AccessPermission; -import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; -import org.springframework.extensions.surf.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.extensions.surf.util.ParameterCheck; /** * Node operations service implmentation. diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java new file mode 100755 index 0000000000..bd13306260 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java @@ -0,0 +1,37 @@ +/* +* Copyright (C) 2005-2010 Alfresco Software Limited. +* +* This file is part of Alfresco +* +* Alfresco is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Alfresco is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with Alfresco. If not, see . +*/ +package org.alfresco.repo.googledocs; + +import org.alfresco.service.namespace.QName; + +/** + * Google docs model constants + */ +public interface GoogleDocsModel +{ + static final String GOOGLE_DOCS_PREFIX = "gd"; + static final String GOOGLE_DOCS_MODEL_1_0_URI = "http://www.alfresco.org/model/googledocs/1.0"; + + static final QName ASPECT_GOOGLEEDITABLE = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "googleEditable"); + + static final QName ASPECT_GOOGLERESOURCE = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "googleResource"); + static final QName PROP_URL = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "url"); + static final QName PROP_RESOURCE_ID = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "resourceId"); + static final QName PROP_RESOURCE_TYPE = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "resourceType"); +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java new file mode 100644 index 0000000000..42c4edc344 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java @@ -0,0 +1,15 @@ +/** + * + */ +package org.alfresco.repo.googledocs; + +/** + * Google docs permission context + */ +public enum GoogleDocsPermissionContext +{ + PRIVATE, + SHARE_READ, + SHARE_WRITE, + SHARE_READWRITE +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsService.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsService.java new file mode 100755 index 0000000000..4e59c55e11 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsService.java @@ -0,0 +1,43 @@ +package org.alfresco.repo.googledocs; + +import java.io.InputStream; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Google docs integration service interface + */ +public interface GoogleDocsService +{ + /** + * Create a google doc from a given node. The content of the node will be used + * as a basis of the associated google doc. If the node has no content a new, empty google + * doc of the correct type will be created. + * + * The permission context provides information about how google sharing permissions should be + * set on the created google doc. + * + * @param nodeRef node reference + * @param permissionContext permission context + */ + void createGoogleDoc(NodeRef nodeRef, GoogleDocsPermissionContext permissionContext); + + /** + * Deletes the google resource associated with the node reference. This could be a folder or + * document. + * + * @param nodeRef node reference + */ + void deleteGoogleResource(NodeRef nodeRef); + + /** + * Gets the content as an input stream of google doc associated with the given node. The + * node must have the google resource aspect and the associated resource should not be a + * folder. + * + * @param nodeRef node reference + * @return InputStream the content of the associated google doc + */ + InputStream getGoogleDocContent(NodeRef nodeRef); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java new file mode 100755 index 0000000000..aee0e5d746 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java @@ -0,0 +1,814 @@ +/* +* Copyright (C) 2005-2010 Alfresco Software Limited. +* +* This file is part of Alfresco +* +* Alfresco is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Alfresco is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with Alfresco. If not, see . +*/ +package org.alfresco.repo.googledocs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.google.gdata.client.GoogleService; +import com.google.gdata.client.docs.DocsService; +import com.google.gdata.data.IEntry; +import com.google.gdata.data.MediaContent; +import com.google.gdata.data.PlainTextConstruct; +import com.google.gdata.data.acl.AclEntry; +import com.google.gdata.data.acl.AclFeed; +import com.google.gdata.data.acl.AclRole; +import com.google.gdata.data.acl.AclScope; +import com.google.gdata.data.docs.DocumentEntry; +import com.google.gdata.data.docs.DocumentListEntry; +import com.google.gdata.data.docs.FolderEntry; +import com.google.gdata.data.docs.PresentationEntry; +import com.google.gdata.data.media.MediaSource; +import com.google.gdata.data.media.MediaStreamSource; +import com.google.gdata.util.AuthenticationException; +import com.google.gdata.util.ContentType; +import com.google.gdata.util.ServiceException; + +/** + * + */ +public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(GoogleDocsServiceImpl.class); + + public static final String TYPE_DOCUMENT = "document"; + public static final String TYPE_SPREADSHEET = "spreadsheet"; + public static final String TYPE_PRESENTATION = "presentation"; + public static final String TYPE_PDF = "pdf"; + + /** Services */ + private DocsService googleDocumentService; + private GoogleService spreadsheetsService; + private NodeService nodeService; + private ContentService contentService; + private PersonService personService; + private MimetypeService mimetypeService; + private PermissionService permissionService; + private OwnableService ownableService; + private AuthorityService authorityService; + + /** GoogleDoc base feed url */ + private String url = "http://docs.google.com/feeds/default/private/full"; + + /** Authentication credentials */ + private boolean initialised = false; + private String username; + private String password; + + private Map permissionMap; + + /** + * @param googleDocumentService google document service + */ + public void setGoogleDocumentService(DocsService googleDocumentService) + { + this.googleDocumentService = googleDocumentService; + } + + /** + * @param spreadsheetsService spread sheets service + */ + public void setSpreadsheetsService(GoogleService spreadsheetsService) + { + this.spreadsheetsService = spreadsheetsService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @param personService person service + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * @param mimetypeService mime type service + */ + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + /** + * @param permissionService permission service + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /* + * @param ownableService ownable service + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } + + /** + * @param authorityService authority service + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * @param url root googleDoc URL + */ + public void setUrl(String url) + { + this.url = url; + } + + /** + * @param username google service user name + */ + public void setUsername(String username) + { + this.username = username; + } + + /** + * @param password google service password + */ + public void setPassword(String password) + { + this.password = password; + } + + /** + * @param permissionMap permission map + */ + public void setPermissionMap(Map permissionMap) + { + this.permissionMap = permissionMap; + } + + /** + * Initialise google docs services + */ + public void initialiseGoogleDocsService() + { + if (initialised == false) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Trying to initialise google docs service for user " + username); + } + + if (username == null ||username.length() == 0 || password == null) + { + throw new AlfrescoRuntimeException("No Goolge Docs credentials found. Please set the Google Docs authentication configuration."); + } + + try + { + googleDocumentService.setUserCredentials(username, password); + spreadsheetsService.setUserCredentials(username, password); + googleDocumentService.setChunkedMediaUpload(-1); + } + catch (AuthenticationException e) + { + throw new AlfrescoRuntimeException("Unable to connect to Google Docs. Please check the Google Docs authentication configuration.", e); + } + + initialised = true; + if (logger.isDebugEnabled() == true) + { + logger.debug("Successfully initialised google docs service for user " + username); + } + } + } + + /** + * @see org.alfresco.google.docs.GoogleDocsService#upload(org.alfresco.service.cmr.repository.NodeRef) + */ + public void createGoogleDoc(NodeRef nodeRef, GoogleDocsPermissionContext permissionContext) + { + // Check for mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Initialise google doc services + initialiseGoogleDocsService(); + + // Get property values + String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + + // TODO should be checking to make sure this doesn't already have an associated google doc + + // Get content reader + String mimetype = null; + InputStream is = null; + ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (contentReader == null) + { + // Determine the mimetype from the file extension + mimetype = mimetypeService.guessMimetype(name); + } + else + { + // Get the mime type and input stream from the content reader + mimetype = contentReader.getMimetype(); + is = contentReader.getContentInputStream(); + } + + // Get the parent folder id + DocumentListEntry parentFolder = getParentFolder(nodeRef); + + // Create the new google document + DocumentListEntry document = createGoogleDocument(name, mimetype, parentFolder, is); + + // Set permissions + setGoogleResourcePermissions(nodeRef, document, permissionContext); + + // Set the google document details + setResourceDetails(nodeRef, document); + } + + /** + * @see org.alfresco.google.docs.GoogleDocsService#deleteGoogleResource(org.alfresco.service.cmr.repository.NodeRef) + */ + public void deleteGoogleResource(NodeRef nodeRef) + { + // Check for mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Initialise google doc services + initialiseGoogleDocsService(); + + try + { + if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true) + { + // Get the entry + DocumentListEntry entry = getDocumentListEntry(nodeRef); + if (entry == null) + { + throw new AlfrescoRuntimeException("Unable to find google resource to delete for node " + nodeRef.toString()); + } + + // Delete the entry + entry.delete(); + + // Remove the aspect from the node + nodeService.removeAspect(nodeRef, ASPECT_GOOGLERESOURCE); + } + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to delete google resource for the node "+ nodeRef.toString()); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to delete google resource for the node "+ nodeRef.toString()); + } + } + + /** + * + * @param nodeRef + * @param resourceId + * @param permissionContext + */ + private void setGoogleResourcePermissions(NodeRef nodeRef, DocumentListEntry resource, GoogleDocsPermissionContext permissionContext) + { + // Set the owner of the document + String owner = ownableService.getOwner(nodeRef); + setGoogleResourcePermission(resource, AuthorityType.USER, owner, "owner"); + + if (GoogleDocsPermissionContext.PRIVATE.equals(permissionContext) == false) + { + Set accessPermissions = permissionService.getAllSetPermissions(nodeRef); + for (AccessPermission accessPermission : accessPermissions) + { + String authorityName = accessPermission.getAuthority(); + AuthorityType authorityType = accessPermission.getAuthorityType(); + String permission = accessPermission.getPermission(); + if (permissionMap.containsKey(permission) == true) + { + String aclRole = permissionMap.get(permission); + if (GoogleDocsPermissionContext.SHARE_READ.equals(permissionContext) == true && + ("reader".equals(aclRole) == true || "writer".equals(aclRole) == true)) + { + // Set the permission to read + setGoogleResourcePermission(resource, authorityType, authorityName, "reader"); + } + else if (GoogleDocsPermissionContext.SHARE_WRITE.equals(permissionContext) == true && + "writer".equals(aclRole) == true) + { + // Set the permission to write + setGoogleResourcePermission(resource, authorityType, authorityName, "writer"); + } + else if (GoogleDocsPermissionContext.SHARE_READWRITE.equals(permissionContext) == true && + ("reader".equals(aclRole) == true || "writer".equals(aclRole) == true)) + { + // Set the permission to the current acl + setGoogleResourcePermission(resource, authorityType, authorityName, aclRole); + } + + } + } + } + } + + /** + * + * @param resourceId + * @param authorityType + * @param authorityName + * @param role + */ + private void setGoogleResourcePermission(DocumentListEntry resource, AuthorityType authorityType, String authorityName, String role) + { + if (AuthorityType.USER.equals(authorityType) == true) + { + // Set the user permissions on the resource + String userEMail = getUserEMail(authorityName); + if (userEMail != null && userEMail.length() != 0) + { + setGoogleResourcePermission(resource, userEMail, role); + } + } + else if (AuthorityType.GROUP.equals(authorityType) == true) + { + Set childAuthorities = authorityService.getContainedAuthorities(AuthorityType.USER, authorityName, false); + for (String childAuthority : childAuthorities) + { + setGoogleResourcePermission(resource, AuthorityType.USER, childAuthority, role); + } + } + } + + /** + * + * @param userName + * @return + */ + private String getUserEMail(String userName) + { + String email = null; + NodeRef personNodeRef = personService.getPerson(userName); + if (personNodeRef != null) + { + email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL); + } + return email; + } + + /** + * + * @param nodeRef + * @return + */ + private DocumentListEntry getParentFolder(NodeRef nodeRef) + { + DocumentListEntry folder = null; + + NodeRef parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parentNodeRef != null) + { + if (nodeService.hasAspect(parentNodeRef, ASPECT_GOOGLERESOURCE) == true) + { + String resourceType = (String)nodeService.getProperty(parentNodeRef, PROP_RESOURCE_TYPE); + String resourceId = (String)nodeService.getProperty(parentNodeRef, PROP_RESOURCE_ID); + folder = getDocumentListEntry(resourceType + ":" + resourceId); + } + else + { + DocumentListEntry parentFolder = getParentFolder(parentNodeRef); + String name = (String)nodeService.getProperty(parentNodeRef, ContentModel.PROP_NAME); + folder = createGoogleFolder(name, parentFolder); + + setResourceDetails(parentNodeRef, folder); + } + } + + return folder; + } + + /** + * + * @param nodeRef + * @param folderId + */ + private void setResourceDetails(final NodeRef nodeRef, final DocumentListEntry documentListEntry) + { + AuthenticationUtil.RunAsWork runAsWork = new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Create a map of the property values + Map props = new HashMap(1); + props.put(GoogleDocsModel.PROP_RESOURCE_ID, documentListEntry.getDocId()); + props.put(GoogleDocsModel.PROP_RESOURCE_TYPE, documentListEntry.getType()); + props.put(GoogleDocsModel.PROP_URL, documentListEntry.getDocumentLink().getHref()); + + // Add the google resource aspect + nodeService.addAspect(nodeRef, GoogleDocsModel.ASPECT_GOOGLERESOURCE, props); + return null; + } + }; + + // Run as admin + AuthenticationUtil.runAs(runAsWork, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.google.docs.GoogleDocsService#getGoogleDocContent(org.alfresco.service.cmr.repository.NodeRef) + */ + public InputStream getGoogleDocContent(NodeRef nodeRef) + { + InputStream result = null; + + // Check for mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Initialise google doc services + initialiseGoogleDocsService(); + + try + { + if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true) + { + String downloadUrl = null; + DocumentListEntry document = getDocumentListEntry(nodeRef); + String docType = document.getType(); + + ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + String fileExtension = mimetypeService.getExtension(contentData.getMimetype()); + if (fileExtension.equals("docx")) + { + fileExtension = "doc"; + } + + if (docType.equals(TYPE_DOCUMENT) || docType.equals(TYPE_PRESENTATION)) + { + downloadUrl = ((MediaContent)document.getContent()).getUri() + "&exportFormat=" + fileExtension; + } + else if (docType.equals(TYPE_SPREADSHEET)) + { + downloadUrl = ((MediaContent)document.getContent()).getUri() + "&exportFormat=" + fileExtension; + + // If exporting to .csv or .tsv, add the gid parameter to specify which sheet to export + if (fileExtension.equals("csv") || fileExtension.equals("tsv")) + { + downloadUrl += "&gid=0"; // gid=0 will download only the first sheet + } + } + else if (docType.equals(TYPE_PDF)) + { + MediaContent mc = (MediaContent)document.getContent(); + downloadUrl = mc.getUri(); + } + else + { + throw new AlfrescoRuntimeException("Unsuported document type: " + docType); + } + + // TODO need to verify that download of a spreadsheet works before we delete this historical code ... + +// UserToken docsToken = null; +// if (isSpreadSheet) +// { +// docsToken = (UserToken) googleDocumentService.getAuthTokenFactory().getAuthToken(); +// UserToken spreadsheetsToken = (UserToken) spreadsheetsService.getAuthTokenFactory().getAuthToken(); +// googleDocumentService.setUserToken(spreadsheetsToken.getValue()); +// +// } + + MediaContent mc = new MediaContent(); + mc.setUri(downloadUrl); + MediaSource ms = googleDocumentService.getMedia(mc); + + // if (isSpreadSheet) + // { + // googleDocumentService.setUserToken(docsToken.getValue()); + // } + + result = ms.getInputStream(); + } + else + { + // error since we are trying to download a non-google resource + throw new AlfrescoRuntimeException("Can not download google doc content since no corresponsing google resource could be found"); + } + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to get google document stream.", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to get google document stream.", e); + } + + return result; + } + + /** + * + * @param docNodeRef + * @return + */ + private DocumentListEntry getDocumentListEntry(NodeRef docNodeRef) + { + String docType = (String)nodeService.getProperty(docNodeRef, PROP_RESOURCE_TYPE); + String docId = (String)nodeService.getProperty(docNodeRef, PROP_RESOURCE_ID); + return getDocumentListEntry(docType + ":" + docId); + } + + /** + * + * @param docResourceId + * @return + */ + private DocumentListEntry getDocumentListEntry(String docResourceId) + { + return getEntry(docResourceId, DocumentListEntry.class); + } + + /** + * + * @param + * @param resourceId + * @param entryClass + * @return + */ + private E getEntry(String resourceId, Class entryClass) + { + E result = null; + try + { + URL docEntryURL = new URL(url + "/" + resourceId); + result = googleDocumentService.getEntry(docEntryURL, entryClass); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to get document list entry for resource " + resourceId, e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to get document list entry for resource " + resourceId, e); + } + return result; + } + + /** + * + * @param name + * @param mimetype + * @param parentFolder + * @param is + * @return + */ + private DocumentListEntry createGoogleDocument(String name, String mimetype, DocumentListEntry parentFolder, InputStream is) + { + DocumentListEntry document = null; + + try + { + // Create the media content object + MediaContent mediaContent = new MediaContent(); + mediaContent.setMimeType(new ContentType(mimetype)); + + if (is != null) + { + mediaContent.setMediaSource(new MediaStreamSource(is, mimetype)); + } + + // Parent folder url + String parentFolderUrl = url; + if (parentFolder != null) + { + parentFolderUrl = ((MediaContent)parentFolder.getContent()).getUri(); + } + + // Create the document entry object + DocumentListEntry docEntry = null; + if (MimetypeMap.MIMETYPE_EXCEL.equals(mimetype) == true) + { + docEntry = new PresentationEntry(); + } + else + { + docEntry = new DocumentEntry(); + } + + docEntry.setContent(mediaContent); + docEntry.setTitle(new PlainTextConstruct(name)); + + // Upload the document into the parent folder + document = googleDocumentService.insert( + new URL(parentFolderUrl), + docEntry); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to create google document", e); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to create google document", e); + } + + return document; + } + + /** + * + * @param docResourceId + * @param mimeType + * @param is + */ + private void updateGoogleDocContent(DocumentListEntry document, String mimeType, InputStream is) + { + try + { + // Update the existing content + googleDocumentService.getRequestFactory().setHeader("If-Match", "*"); + document.setMediaSource(new MediaStreamSource(is, mimeType)); + document.updateMedia(false); + googleDocumentService.getRequestFactory().setHeader("If-Match", null); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to update documents content in google docs", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to update documents content in google docs", e); + } + } + + /** + * + * @param folderName + * @param parentFolderId + * @return + */ + private DocumentListEntry createGoogleFolder(String folderName, DocumentListEntry parentFolder) + { + DocumentListEntry folderEntry = null; + + try + { + // Parent folder url + String parentFolderUrl = url; + if (parentFolder != null) + { + parentFolderUrl = ((MediaContent)parentFolder.getContent()).getUri(); + } + + // Create the folder entry + FolderEntry folder = new FolderEntry(); + folder.setTitle(new PlainTextConstruct(folderName)); + + // Create the folder + folderEntry = googleDocumentService.insert( + new URL(parentFolderUrl), + folder); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to create Google Folder", e); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to create Google Folder", e); + } + + return folderEntry; + } + + /** + * Set permissions on a googleDoc resource + * + * @param resourceId + * @param email + * @param role + */ + private void setGoogleResourcePermission(DocumentListEntry resource, String email, String role) + { + ParameterCheck.mandatory("resource", resource); + ParameterCheck.mandatory("email", email); + ParameterCheck.mandatory("role", role); + + try + { + AclRole aclRole = new AclRole(role); + AclScope scope = new AclScope(AclScope.Type.USER, email); + + // Get the URL + URL aclFeedLinkURL = new URL(resource.getAclFeedLink().getHref()); + + // See if we have already set this permission or not + AclEntry aclEntry = null; + AclFeed aclFeed = googleDocumentService.getFeed(aclFeedLinkURL, AclFeed.class); + if (aclFeed != null) + { + List aclEntries = aclFeed.getEntries(); + for (AclEntry tempAclEntry : aclEntries) + { + AclScope tempScope = tempAclEntry.getScope(); + if (tempScope.equals(scope) == true) + { + // Existing ACL entry found + aclEntry = tempAclEntry; + break; + } + } + } + + if (aclEntry == null) + { + aclEntry = new AclEntry(); + aclEntry.setRole(aclRole); + aclEntry.setScope(scope); + googleDocumentService.insert(aclFeedLinkURL, aclEntry); + } + + // TODO for now we will not 'update' the permissions if they have already been set .... + // + //else + //{ + // AclRole currentAclRole = aclEntry.getRole(); + // if (currentAclRole.toString().equals(aclRole.toString()) == false) + // { + // aclEntry.setRole(aclRole); + // googleDocumentService.update(new URL(aclEntry.getEditLink().getHref()), aclEntry); + // } + //} + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to set premissions on google document", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to set premissions on google document", e); + } + } + + +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java b/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java new file mode 100755 index 0000000000..3d6c0aee33 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java @@ -0,0 +1,278 @@ +/* +* Copyright (C) 2005-2010 Alfresco Software Limited. +* +* This file is part of Alfresco +* +* Alfresco is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Alfresco is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with Alfresco. If not, see . +*/ +package org.alfresco.repo.googledocs; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.management.subsystems.ApplicationContextFactory; +import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteServiceImpl; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +import com.google.gdata.util.ServiceException; + +public class GoogleDocumentServiceTest extends TestCase implements GoogleDocsModel +{ + private NodeService nodeService; + private GoogleDocsService googleDocsService; + private SiteService siteService; + private TransactionService transactionService; + private FileFolderService fileFolderService; + private ContentService contentService; + private CheckOutCheckInService checkOutCheckInService; + private MutableAuthenticationService authenticationService; + private PersonService personService; + private ApplicationContextFactory subsystem; + + private static final String USER_ONE = "GoogleDocUserOne"; + private static final String USER_TWO = "GoogleDocUserTwo"; + private static final String USER_THREE = "GoogleDocUserThree"; + private static final String USER_FOUR = "GoogleDocUserFour"; + //private static final String EMAIL_DOMAIN = "@alfresco.com"; + + private NodeRef folder = null; + private NodeRef nodeRefDoc = null; + private NodeRef nodeRefSpread = null; + private NodeRef nodeRefPres = null; + private NodeRef nodeRefPdf = null; + private NodeRef nodeRef2 = null; + private UserTransaction userTransaction = null; + + @Override + protected void setUp() throws Exception + { + ApplicationContext appContext = ApplicationContextHelper.getApplicationContext(); + + nodeService = (NodeService)appContext.getBean("nodeService"); + siteService = (SiteService)appContext.getBean("siteService"); + transactionService = (TransactionService)appContext.getBean("transactionService"); + fileFolderService = (FileFolderService)appContext.getBean("fileFolderService"); + contentService = (ContentService)appContext.getBean("contentService"); + checkOutCheckInService = (CheckOutCheckInService)appContext.getBean("checkOutCheckInService"); + authenticationService = (MutableAuthenticationService)appContext.getBean("authenticationService"); + personService = (PersonService)appContext.getBean("personService"); + + // Start the user transaction + userTransaction = transactionService.getUserTransaction(); + userTransaction.begin(); + + // Get the sub-system and make sure the googleeditable feature is turned on + subsystem = (ApplicationContextFactory)appContext.getBean("googledocs"); + if (subsystem.getProperty("googledocs.googleeditable.enabled").equals("false") == true) + { + subsystem.stop(); + subsystem.setProperty("googledocs.googleeditable.enabled", "true"); + subsystem.start(); + } + + // Get the google docs service + ConfigurableApplicationContext childContext = (ConfigurableApplicationContext)subsystem.getApplicationContext(); + googleDocsService = (GoogleDocsService)childContext.getBean("googleDocsService"); + + // Create the test users + createUser(USER_ONE, "rwetherall@alfresco.com"); + createUser(USER_TWO, "rwetherall@activiti.com"); + createUser(USER_THREE, "roy.wetherall@alfresco.com"); + createUser(USER_FOUR, "roy.wetherall@activiti.com"); + + // Authenticate as user one + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + String id = GUID.generate(); + + // Create a site to use as holder for our test google documents + siteService.createSite("sitePreset", id, "My Title", "My Description", SiteVisibility.PUBLIC); + NodeRef container = siteService.createContainer(id, "testComponent", null, null); + + // Add some memberships to the site + siteService.setMembership(id, USER_TWO, SiteServiceImpl.SITE_COLLABORATOR); + siteService.setMembership(id, USER_THREE, SiteServiceImpl.SITE_CONTRIBUTOR); + siteService.setMembership(id, USER_FOUR, SiteServiceImpl.SITE_CONSUMER); + + // Create a folder in our site container + folder = fileFolderService.create(container, "myfolder", ContentModel.TYPE_FOLDER).getNodeRef(); + + // Create test documents + nodeRefDoc = createTestDocument("mydoc.docx", "alfresco/subsystems/googledocs/default/test.docx", MimetypeMap.MIMETYPE_WORD); + nodeRefSpread = createTestDocument("mydoc.xlsx", "alfresco/subsystems/googledocs/default/test.xlsx", MimetypeMap.MIMETYPE_EXCEL); + + // Create an empty content node (simulate creation of a new google doc in UI) + nodeRef2 = fileFolderService.create(folder, "mygoogledoc.doc", ContentModel.TYPE_CONTENT).getNodeRef(); + nodeService.addAspect(nodeRef2, ASPECT_GOOGLEEDITABLE, null); + } + + private NodeRef createTestDocument(String name, String contentPath, String mimetype) + { + NodeRef nodeRef = fileFolderService.create(folder, name, ContentModel.TYPE_CONTENT).getNodeRef(); + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setEncoding("UTF-8"); + writer.setMimetype(mimetype); + InputStream is = getClass().getClassLoader().getResourceAsStream(contentPath); + writer.putContent(is); + return nodeRef; + } + + private void createUser(String userName, String email) + { + if (authenticationService.authenticationExists(userName) == false) + { + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, email); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(ppOne); + } + } + + @Override + protected void tearDown() throws Exception + { + if (userTransaction != null) + { + userTransaction.rollback(); + } + } + + public void testGoogleDocUploadDownload() throws Exception + { + googleDocsService.createGoogleDoc(nodeRefDoc, GoogleDocsPermissionContext.SHARE_WRITE); + + assertTrue(nodeService.hasAspect(nodeRefDoc, ASPECT_GOOGLERESOURCE)); + assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_URL)); + assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_ID)); + assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_TYPE)); + + System.out.println("Google doc URL: " + nodeService.getProperty(nodeRefDoc, PROP_URL)); + System.out.println("Google doc type: " + nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_TYPE)); + System.out.println("Google doc id: " + nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_ID)); + String downloadFile = downloadFile(googleDocsService.getGoogleDocContent(nodeRefDoc), ".doc"); + System.out.println("Download file: " + downloadFile); + +// googleDocsService.upload(nodeRefSpread, GoogleDocsPermissionContext.SHARE_WRITE); +// +// assertTrue(nodeService.hasAspect(nodeRefSpread, ASPECT_GOOGLERESOURCE)); +// assertNotNull(nodeService.getProperty(nodeRefSpread, PROP_URL)); +// assertNotNull(nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_ID)); +// assertNotNull(nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_TYPE)); +// +// System.out.println("Google doc URL: " + nodeService.getProperty(nodeRefSpread, PROP_URL)); +// System.out.println("Google doc type: " + nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_TYPE)); +// System.out.println("Google doc id: " + nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_ID)); +// downloadFile = downloadFile(googleDocsService.download(nodeRefSpread), ".xls"); +// System.out.println("Download file: " + downloadFile); + + } + + public void testCheckOutCheckIn() throws Exception + { + ContentReader contentReader = contentService.getReader(nodeRef2, ContentModel.PROP_CONTENT); + assertNull(contentReader); + + // Check out the empty google document + NodeRef workingCopy = checkOutCheckInService.checkout(nodeRef2); + + assertTrue(nodeService.hasAspect(workingCopy, ASPECT_GOOGLERESOURCE)); + assertNotNull(nodeService.getProperty(workingCopy, PROP_URL)); + assertNotNull(nodeService.getProperty(workingCopy, PROP_RESOURCE_ID)); + assertNotNull(nodeService.getProperty(workingCopy, PROP_RESOURCE_TYPE)); + + System.out.println("Google doc URL: " + nodeService.getProperty(workingCopy, PROP_URL)); + System.out.println("Google doc type: " + nodeService.getProperty(workingCopy, PROP_RESOURCE_TYPE)); + System.out.println("Google doc id: " + nodeService.getProperty(workingCopy, PROP_RESOURCE_ID)); + + checkOutCheckInService.checkin(workingCopy, null); + + assertFalse(nodeService.hasAspect(nodeRefDoc, ASPECT_GOOGLERESOURCE)); + contentReader = contentService.getReader(nodeRef2, ContentModel.PROP_CONTENT); + assertNotNull(contentReader); + } + + /** + * Utility method to download input stream to a file for inspection + * + * @param inStream + * @param ext + * @return + * @throws IOException + * @throws MalformedURLException + * @throws ServiceException + */ + private String downloadFile(InputStream inStream, String ext) throws IOException, MalformedURLException, ServiceException + { + File file = File.createTempFile("googleDocTest", ext); + String filePath = file.getAbsolutePath(); + FileOutputStream outStream = null; + try + { + outStream = new FileOutputStream(filePath); + + int c; + while ((c = inStream.read()) != -1) + { + outStream.write(c); + } + } + finally + { + if (inStream != null) + { + inStream.close(); + } + if (outStream != null) + { + outStream.flush(); + outStream.close(); + } + } + + return filePath; + } +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java b/source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java new file mode 100755 index 0000000000..ecc69f5327 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java @@ -0,0 +1,206 @@ +/* +* Copyright (C) 2005-2010 Alfresco Software Limited. +* +* This file is part of Alfresco +* +* Alfresco is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Alfresco is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with Alfresco. If not, see . +*/ +package org.alfresco.repo.googledocs; + +import java.io.InputStream; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Behaviour associated with google editable documents + * + */ +public class GoogleEditableAspect implements NodeServicePolicies.OnAddAspectPolicy, + CheckOutCheckInServicePolicies.OnCheckOut, + CheckOutCheckInServicePolicies.OnCheckIn, + CheckOutCheckInServicePolicies.BeforeCancelCheckOut +{ + /** Indicates whether behaviour is enabled or not */ + boolean enabled = false; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Google docs service */ + private GoogleDocsService googleDocsService; + + /** Node service */ + private NodeService nodeService; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Content service */ + private ContentService contentService; + + /** + * @param enabled true if behaviour enabled, false otherwise + */ + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param googleDocsService google docs service + */ + public void setGoogleDocsService(GoogleDocsService googleDocsService) + { + this.googleDocsService = googleDocsService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Initialise method + */ + public void init() + { + if (enabled == true) + { + // Register behaviour with policy component + policyComponent.bindClassBehaviour(OnAddAspectPolicy.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE , + new JavaBehaviour(this, "onAddAspect", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour(OnCheckOut.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE, + new JavaBehaviour(this, "onCheckOut", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour(OnCheckIn.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE, + new JavaBehaviour(this, "onCheckIn", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour(BeforeCancelCheckOut.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE, + new JavaBehaviour(this, "beforeCancelCheckOut", NotificationFrequency.FIRST_EVENT)); + } + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true) + { + // Can only make cm:content descendant google editable + QName type = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == false) + { + // Prevent aspect from being applied + throw new AlfrescoRuntimeException("The node (" + nodeRef.toString() + ") can not be made google editable, because it is not a sub type of cm:content."); + } + } + } + + /** + * @see org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut#onCheckOut(org.alfresco.service.cmr.repository.NodeRef) + */ + public void onCheckOut(NodeRef workingCopy) + { + if (nodeService.exists(workingCopy) == true) + { + // Remove the google editable aspect from the working copy + nodeService.removeAspect(workingCopy, GoogleDocsModel.ASPECT_GOOGLEEDITABLE); + + // Upload the content of the working copy to google docs + googleDocsService.createGoogleDoc(workingCopy, GoogleDocsPermissionContext.SHARE_WRITE); + } + } + + /** + * @see org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn#onCheckIn(org.alfresco.service.cmr.repository.NodeRef) + */ + public void onCheckIn(NodeRef nodeRef) + { + if (nodeService.exists(nodeRef) == true && + nodeService.hasAspect(nodeRef, GoogleDocsModel.ASPECT_GOOGLERESOURCE) == true) + { + // Get input stream for the google doc + InputStream is = googleDocsService.getGoogleDocContent(nodeRef); + if (is == null) + { + throw new AlfrescoRuntimeException("Unable to complete check in, because the working copy content could not be retrieved from google docs."); + } + + // Write the google content into the node + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.putContent(is); + + // Delete the associated google resource + googleDocsService.deleteGoogleResource(nodeRef); + } + } + + /** + * @see org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut#beforeCancelCheckOut(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeCancelCheckOut(NodeRef workingCopyNodeRef) + { + if (nodeService.exists(workingCopyNodeRef) == true) + { + // Delete the associated google resource + googleDocsService.deleteGoogleResource(workingCopyNodeRef); + } + } +} diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 84191579cb..f91c163319 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -858,6 +858,12 @@ public class SiteServiceImpl implements SiteService, SiteModel } } + /** + * Get the site implementation given a short name + * + * @param shortName + * @return + */ private SiteInfo getSiteImpl(String shortName) { SiteInfo result = null; @@ -873,6 +879,45 @@ public class SiteServiceImpl implements SiteService, SiteModel // Return the site information return result; } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSite(org.alfresco.service.cmr.repository.NodeRef) + */ + public SiteInfo getSite(NodeRef nodeRef) + { + SiteInfo siteInfo = null; + NodeRef siteNodeRef = getSiteNodeRef(nodeRef); + if (siteNodeRef != null) + { + siteInfo = createSiteInfo(siteNodeRef); + } + return siteInfo; + } + + /** + * Gets the site node reference for a particular node reference + * + * @param nodeRef node reference + * @return NodeRef site node reference or null if node is not in a site + */ + private NodeRef getSiteNodeRef(NodeRef nodeRef) + { + NodeRef siteNodeRef = null; + QName nodeRefType = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(TYPE_SITE, nodeRefType) == true) + { + siteNodeRef = nodeRef; + } + else + { + ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef); + if (primaryParent != null && primaryParent.getParentRef() != null) + { + siteNodeRef = getSiteNodeRef(primaryParent.getParentRef()); + } + } + return siteNodeRef; + } /** * Gets the site's node reference based on its short name diff --git a/source/java/org/alfresco/service/cmr/site/SiteService.java b/source/java/org/alfresco/service/cmr/site/SiteService.java index f0ff598cc1..6ada2af125 100644 --- a/source/java/org/alfresco/service/cmr/site/SiteService.java +++ b/source/java/org/alfresco/service/cmr/site/SiteService.java @@ -98,6 +98,13 @@ public interface SiteService */ SiteInfo getSite(String shortName); + /** + * + * @param nodeRef + * @return + */ + SiteInfo getSite(NodeRef nodeRef); + /** * Update the site information. *